2 * Copyright (c) 2001 Simon Wilkinson. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include <openssl/evp.h>
51 extern ServerOptions options;
52 extern u_char *session_id2;
53 extern int session_id2_len;
55 void userauth_reply(Authctxt *authctxt, int authenticated);
56 static void gssapi_unsetenv(const char *var);
58 typedef struct ssh_gssapi_cred_cache {
63 } ssh_gssapi_cred_cache;
65 static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
66 unsigned char ssh1_key_digest[16]; /* used for ssh1 gssapi */
69 * Environment variables pointing to delegated credentials
71 static char *delegation_env[] = {
72 "X509_USER_PROXY", /* GSSAPI/SSLeay */
73 "KRB5CCNAME", /* Krb5 and possibly SSLeay */
77 Authmethod method_external = {
80 &options.gss_authentication
83 Authmethod method_gssapi = {
86 &options.gss_authentication
94 #include <gssapi_krb5.h>
95 #define krb5_get_err_text(context,code) error_message(code)
98 static krb5_context krb_context = NULL;
100 /* Initialise the krb5 library, so we can use it for those bits that
103 int ssh_gssapi_krb5_init() {
104 krb5_error_code problem;
106 if (krb_context !=NULL)
109 problem = krb5_init_context(&krb_context);
111 log("Cannot initialize krb5 context");
114 krb5_init_ets(krb_context);
119 /* Check if this user is OK to login. This only works with krb5 - other
120 * GSSAPI mechanisms will need their own.
121 * Returns true if the user is OK to log in, otherwise returns 0
125 ssh_gssapi_krb5_userok(char *name) {
126 krb5_principal princ;
129 if (ssh_gssapi_krb5_init() == 0)
132 if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value,
134 log("krb5_parse_name(): %.100s",
135 krb5_get_err_text(krb_context,retval));
138 if (krb5_kuserok(krb_context, princ, name)) {
140 log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
141 (char *)gssapi_client_name.value);
146 krb5_free_principal(krb_context, princ);
150 /* Make sure that this is called _after_ we've setuid to the user */
152 /* This writes out any forwarded credentials. Its specific to the Kerberos
155 * We assume that our caller has made sure that the user has selected
156 * delegated credentials, and that the client_creds structure is correctly
161 ssh_gssapi_krb5_storecreds() {
163 krb5_error_code problem;
164 krb5_principal princ;
166 static char name[40];
168 OM_uint32 maj_status,min_status;
171 if (gssapi_client_creds==NULL) {
172 debug("No credentials stored");
176 if (ssh_gssapi_krb5_init() == 0)
179 if (options.gss_use_session_ccache) {
180 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
182 if ((tmpfd = mkstemp(ccname))==-1) {
183 log("mkstemp(): %.100s", strerror(errno));
186 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
187 log("fchmod(): %.100s", strerror(errno));
192 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
193 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
195 log("open(): %.100s", strerror(errno));
201 snprintf(name, sizeof(name), "FILE:%s",ccname);
203 if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
204 log("krb5_cc_default(): %.100s",
205 krb5_get_err_text(krb_context,problem));
209 if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value,
211 log("krb5_parse_name(): %.100s",
212 krb5_get_err_text(krb_context,problem));
213 krb5_cc_destroy(krb_context,ccache);
217 if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
218 log("krb5_cc_initialize(): %.100s",
219 krb5_get_err_text(krb_context,problem));
220 krb5_free_principal(krb_context,princ);
221 krb5_cc_destroy(krb_context,ccache);
225 krb5_free_principal(krb_context,princ);
228 if ((problem = krb5_cc_copy_cache(krb_context,
229 gssapi_client_creds->ccache,
231 log("krb5_cc_copy_cache(): %.100s",
232 krb5_get_err_text(krb_context,problem));
233 krb5_cc_destroy(krb_context,ccache);
237 if ((maj_status = gss_krb5_copy_ccache(&min_status,
240 log("gss_krb5_copy_ccache() failed");
241 ssh_gssapi_error(maj_status,min_status);
242 krb5_cc_destroy(krb_context,ccache);
247 krb5_cc_close(krb_context,ccache);
251 do_pam_putenv("KRB5CCNAME",name);
254 gssapi_cred_store.filename=strdup(ccname);
255 gssapi_cred_store.envvar="KRB5CCNAME";
256 gssapi_cred_store.envval=strdup(name);
264 #include <globus_gss_assist.h>
267 * Check if this user is OK to login under GSI. User has been authenticated
268 * as identity in global 'client_name.value' and is trying to log in as passed
269 * username in 'name'.
271 * Returns non-zero if user is authorized, 0 otherwise.
274 ssh_gssapi_gsi_userok(char *name)
278 /* This returns 0 on success */
279 authorized = (globus_gss_assist_userok(gssapi_client_name.value,
282 log("GSI user %s is%s authorized as target user %s",
283 (char *) gssapi_client_name.value, (authorized ? "" : " not"), name);
289 * Handle setting up child environment for GSI.
291 * Make sure that this is called _after_ we've setuid to the user.
294 ssh_gssapi_gsi_storecreds()
296 OM_uint32 major_status;
297 OM_uint32 minor_status;
300 if (gssapi_client_creds != NULL)
302 char *creds_env = NULL;
305 * This is the current hack with the GSI gssapi library to
306 * export credentials to disk.
309 debug("Exporting delegated credentials");
311 minor_status = 0xdee0; /* Magic value */
313 gss_inquire_cred(&minor_status,
315 (gss_name_t *) &creds_env,
320 if ((major_status == GSS_S_COMPLETE) &&
321 (minor_status == 0xdee1) &&
327 * String is of the form:
328 * X509_USER_DELEG_PROXY=filename
329 * so we parse out the filename
330 * and then set X509_USER_PROXY
333 value = strchr(creds_env, '=');
340 do_pam_putenv("X509_USER_PROXY",value);
342 gssapi_cred_store.filename=NULL;
343 gssapi_cred_store.envvar="X509_USER_PROXY";
344 gssapi_cred_store.envval=strdup(value);
350 log("Failed to parse delegated credentials string '%s'",
356 log("Failed to export delegated credentials (error %ld)",
365 ssh_gssapi_cleanup_creds(void *ignored)
367 if (gssapi_cred_store.filename!=NULL) {
368 /* Unlink probably isn't sufficient */
369 debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
370 unlink(gssapi_cred_store.filename);
375 ssh_gssapi_storecreds()
377 switch (gssapi_client_type) {
380 ssh_gssapi_krb5_storecreds();
385 ssh_gssapi_gsi_storecreds();
389 /* GSSAPI not used in this authentication */
390 debug("No GSSAPI credentials stored");
393 log("ssh_gssapi_do_child: Unknown mechanism");
397 if (options.gss_cleanup_creds) {
398 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
403 /* This allows GSSAPI methods to do things to the childs environment based
404 * on the passed authentication process and credentials.
406 * Question: If we didn't use userauth_external for some reason, should we
407 * still delegate credentials?
410 ssh_gssapi_do_child(char ***envp, u_int *envsizep)
413 if (gssapi_cred_store.envvar!=NULL &&
414 gssapi_cred_store.envval!=NULL) {
416 debug("Setting %s to %s", gssapi_cred_store.envvar,
417 gssapi_cred_store.envval);
418 child_set_env(envp, envsizep, gssapi_cred_store.envvar,
419 gssapi_cred_store.envval);
422 switch(gssapi_client_type) {
424 case GSS_KERBEROS: break;
430 debug("No GSSAPI credentials stored");
433 log("ssh_gssapi_do_child: Unknown mechanism");
438 ssh_gssapi_userok(char *user)
440 if (gssapi_client_name.length==0 ||
441 gssapi_client_name.value==NULL) {
442 debug("No suitable client data");
445 switch (gssapi_client_type) {
448 return(ssh_gssapi_krb5_userok(user));
449 break; /* Not reached */
453 return(ssh_gssapi_gsi_userok(user));
454 break; /* Not reached */
457 debug("Client not GSSAPI");
460 debug("Unknown client authentication type");
466 userauth_external(Authctxt *authctxt)
470 return(ssh_gssapi_userok(authctxt->user));
473 void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
474 void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
476 /* We only support those mechanisms that we know about (ie ones that we know
477 * how to check local user kuserok and the like
480 userauth_gssapi(Authctxt *authctxt)
482 gss_OID_desc oid= {0,NULL};
485 gss_OID_set supported;
490 if (!authctxt->valid || authctxt->user == NULL)
493 if (datafellows & SSH_OLD_GSSAPI) {
494 debug("Early drafts of GSSAPI userauth not supported");
498 mechs=packet_get_int();
500 debug("Mechanism negotiation is not supported");
504 ssh_gssapi_supported_oids(&supported);
508 oid.elements = packet_get_string(&len);
510 gss_test_oid_set_member(&ms, &oid, supported, &present);
512 } while (mechs>0 && !present);
519 ctxt=xmalloc(sizeof(Gssctxt));
520 authctxt->methoddata=(void *)ctxt;
522 ssh_gssapi_build_ctx(ctxt);
523 ssh_gssapi_set_oid(ctxt,&oid);
525 if (ssh_gssapi_acquire_cred(ctxt))
528 /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
531 packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE);
533 packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
534 packet_put_string(oid.elements,oid.length);
540 dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN,
541 &input_gssapi_token);
543 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
544 &input_gssapi_token);
545 authctxt->postponed = 1;
551 input_gssapi_token(int type, u_int32_t plen, void *ctxt)
553 Authctxt *authctxt = ctxt;
555 gss_buffer_desc send_tok,recv_tok;
556 OM_uint32 maj_status, min_status;
558 if (authctxt == NULL || authctxt->methoddata == NULL)
559 fatal("No authentication or GSSAPI context");
561 gssctxt=authctxt->methoddata;
563 recv_tok.value=packet_get_string(&recv_tok.length);
565 maj_status=ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL);
568 if (GSS_ERROR(maj_status)) {
569 /* Failure <sniff> */
570 ssh_gssapi_send_error(maj_status,min_status);
571 authctxt->postponed = 0;
572 dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
573 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
574 userauth_finish(authctxt, 0, "gssapi");
577 if (send_tok.length != 0) {
578 /* Send a packet back to the client */
580 packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN);
582 packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
583 packet_put_string(send_tok.value,send_tok.length);
586 gss_release_buffer(&min_status, &send_tok);
589 if (maj_status == GSS_S_COMPLETE) {
590 dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
591 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
592 /* ssh1 does not have an extra message here */
594 input_gssapi_exchange_complete(0, 0, ctxt);
596 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
597 &input_gssapi_exchange_complete);
601 /* This is called when the client thinks we've completed authentication.
602 * It should only be enabled in the dispatch handler by the function above,
603 * which only enables it once the GSSAPI exchange is complete.
607 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
609 Authctxt *authctxt = ctxt;
613 if(strcmp(authctxt->user,"") == 0) {
615 char *gridmapped_name = NULL;
616 struct passwd *pw = NULL;
617 if(globus_gss_assist_gridmap(gssapi_client_name.value,
618 &gridmapped_name) == 0) {
619 user = gridmapped_name;
620 debug("I gridmapped and got %s", user);
622 if (pw && allowed_user(pw)) {
623 authctxt->user = user;
624 authctxt->pw = pwcopy(pw);
631 if (authctxt == NULL || authctxt->methoddata == NULL)
632 fatal("No authentication or GSSAPI context");
634 gssctxt=authctxt->methoddata;
636 /* This should never happen, but better safe than sorry. */
637 if (gssctxt->status != GSS_S_COMPLETE) {
638 packet_disconnect("Context negotiation is not complete");
641 if (ssh_gssapi_getclient(gssctxt,&gssapi_client_type,
643 &gssapi_client_creds)) {
644 fatal("Couldn't convert client name");
647 authenticated = ssh_gssapi_userok(authctxt->user);
649 /* ssh1 needs to exchange the hash of the keys */
653 OM_uint32 maj_status, min_status;
654 gss_buffer_desc gssbuf,msg_tok;
657 gssbuf.value=ssh1_key_digest;
658 gssbuf.length=sizeof(ssh1_key_digest);
659 if ((maj_status=gss_wrap(&min_status,
666 ssh_gssapi_error(maj_status,min_status);
667 fatal("Couldn't wrap keys");
669 packet_start(SSH_SMSG_AUTH_GSSAPI_HASH);
670 packet_put_string((char *)msg_tok.value,msg_tok.length);
673 gss_release_buffer(&min_status,&msg_tok);
675 packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
681 authctxt->postponed = 0;
682 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
683 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
684 userauth_finish(authctxt, authenticated, "gssapi");
688 * Clean our environment on startup. This means removing any environment
689 * strings that might inadvertantly been in root's environment and
690 * could cause serious security problems if we think we set them.
693 ssh_gssapi_clean_env(void)
699 for (envstr_index = 0;
700 (envstr = delegation_env[envstr_index]) != NULL;
703 if (getenv(envstr)) {
704 debug("Clearing environment variable %s", envstr);
705 gssapi_unsetenv(envstr);
711 * Wrapper around unsetenv.
714 gssapi_unsetenv(const char *var)
719 #else /* !HAVE_UNSETENV */
720 extern char **environ;
721 char **p1 = environ; /* New array list */
722 char **p2 = environ; /* Current array list */
723 int len = strlen(var);
726 * Walk through current environ array (p2) copying each pointer
727 * to new environ array (p1) unless the pointer is to the item
728 * we want to delete. Copy happens in place.
731 if ((strncmp(*p2, var, len) == 0) &&
732 ((*p2)[len] == '=')) {
734 * *p2 points at item to be deleted, just skip over it
739 * *p2 points at item we want to save, so copy it
747 /* And make sure new array is NULL terminated */
749 #endif /* HAVE_UNSETENV */