X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/007914b3f401941ad12a4bfeaa78814a43a94619..22ce3a3becb01f9487f7fa12b859100c162e231c:/openssh/auth2-gss.c diff --git a/openssh/auth2-gss.c b/openssh/auth2-gss.c index 597b614..08fcf72 100644 --- a/openssh/auth2-gss.c +++ b/openssh/auth2-gss.c @@ -1,5 +1,7 @@ +/* $OpenBSD: auth2-gss.c,v 1.16 2007/10/29 00:52:45 dtucker Exp $ */ + /* - * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved. + * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,241 +27,393 @@ #include "includes.h" #ifdef GSSAPI + +#include + +#include + +#include "xmalloc.h" +#include "key.h" +#include "hostfile.h" #include "auth.h" #include "ssh2.h" -#include "ssh1.h" -#include "xmalloc.h" #include "log.h" #include "dispatch.h" +#include "buffer.h" #include "servconf.h" -#include "compat.h" #include "packet.h" -#include "monitor_wrap.h" - #include "ssh-gss.h" +#include "monitor_wrap.h" extern ServerOptions options; -extern unsigned char ssh1_key_digest[16]; +static void ssh_gssapi_userauth_error(Gssctxt *ctxt); +static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); +static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); +static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); +static void input_gssapi_errtok(int, u_int32_t, void *); + +/* + * The 'gssapi_keyex' userauth mechanism. + */ static int -userauth_external(Authctxt *authctxt) +userauth_gsskeyex(Authctxt *authctxt) { - packet_check_eom(); + int authenticated = 0; + Buffer b, b2; + gss_buffer_desc mic, gssbuf, gssbuf2; + u_int len; - return(PRIVSEP(ssh_gssapi_userok(authctxt->user))); -} + mic.value = packet_get_string(&len); + mic.length = len; -static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); -static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + packet_check_eom(); + + ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, + "gssapi-keyex"); + + gssbuf.value = buffer_ptr(&b); + gssbuf.length = buffer_len(&b); + + /* client may have used empty username to determine target + name from GSSAPI context */ + ssh_gssapi_buildmic(&b2, "", authctxt->service, "gssapi-keyex"); -/* We only support those mechanisms that we know about (ie ones that we know - * how to check local user kuserok and the like + gssbuf2.value = buffer_ptr(&b2); + gssbuf2.length = buffer_len(&b2); + + /* gss_kex_context is NULL with privsep, so we can't check it here */ + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, + &gssbuf, &mic))) || + !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, + &gssbuf2, &mic)))) { + if (authctxt->valid && authctxt->user && authctxt->user[0]) { + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, + authctxt->pw)); + } + } + + buffer_free(&b); + buffer_free(&b2); + xfree(mic.value); + + return (authenticated); +} + +/* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) */ static int userauth_gssapi(Authctxt *authctxt) { - gss_OID_desc oid= {0,NULL}; - Gssctxt *ctxt = NULL; - int mechs; - gss_OID_set supported; - int present; - OM_uint32 ms; - u_int len; - - if (!authctxt->valid || authctxt->user == NULL) - return 0; - - if (datafellows & SSH_OLD_GSSAPI) { - debug("Early drafts of GSSAPI userauth not supported"); - return 0; - } - - mechs=packet_get_int(); - if (mechs==0) { - debug("Mechanism negotiation is not supported"); - return 0; - } + gss_OID_desc goid = {0, NULL}; + Gssctxt *ctxt = NULL; + int mechs; + gss_OID_set supported; + int present; + OM_uint32 ms; + u_int len; + u_char *doid = NULL; - ssh_gssapi_supported_oids(&supported); - do { - if (oid.elements) - xfree(oid.elements); - oid.elements = packet_get_string(&len); - oid.length = len; - gss_test_oid_set_member(&ms, &oid, supported, &present); - mechs--; - } while (mechs>0 && !present); - - if (!present) { - xfree(oid.elements); - return(0); - } - - if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid)))) - return(0); - - authctxt->methoddata=(void *)ctxt; + /* authctxt->valid may be 0 if we haven't yet determined + username from gssapi context. */ - /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */ + if (authctxt->user == NULL) + return (0); + + mechs = packet_get_int(); + if (mechs == 0) { + debug("Mechanism negotiation is not supported"); + return (0); + } + + ssh_gssapi_supported_oids(&supported); + do { + mechs--; + + if (doid) + xfree(doid); + + present = 0; + doid = packet_get_string(&len); + + if (len > 2 && doid[0] == SSH_GSS_OIDTYPE && + doid[1] == len - 2) { + goid.elements = doid + 2; + goid.length = len - 2; + gss_test_oid_set_member(&ms, &goid, supported, + &present); + } else { + logit("Badly formed OID received"); + } + } while (mechs > 0 && !present); + + gss_release_oid_set(&ms, &supported); + + if (!present) { + xfree(doid); + authctxt->server_caused_failure = 1; + return (0); + } + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { + if (ctxt != NULL) + ssh_gssapi_delete_ctx(&ctxt); + xfree(doid); + authctxt->server_caused_failure = 1; + return (0); + } + + authctxt->methoddata = (void *)ctxt; - if (!compat20) - packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE); - else packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); - packet_put_string(oid.elements,oid.length); - packet_send(); - packet_write_wait(); - xfree(oid.elements); - - if (!compat20) - dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, - &input_gssapi_token); - else - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, - &input_gssapi_token); - authctxt->postponed = 1; - - return 0; + + /* Return the OID that we received */ + packet_put_string(doid, len); + + packet_send(); + xfree(doid); + + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); + authctxt->postponed = 1; + + return (0); } static void input_gssapi_token(int type, u_int32_t plen, void *ctxt) { - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; - gss_buffer_desc send_tok,recv_tok; - OM_uint32 maj_status, min_status; - int len; - - if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) - fatal("No authentication or GSSAPI context"); - - gssctxt=authctxt->methoddata; - recv_tok.value=packet_get_string(&len); - recv_tok.length=len; /* int vs. size_t */ - - maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, - &send_tok, NULL)); - packet_check_eom(); - - if (GSS_ERROR(maj_status)) { - /* Failure */ - ssh_gssapi_send_error(maj_status,min_status); - authctxt->postponed = 0; - dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - userauth_finish(authctxt, 0, "gssapi"); - } - - if (send_tok.length != 0) { - /* Send a packet back to the client */ - if (!compat20) - packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN); - else - packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); - packet_put_string(send_tok.value,send_tok.length); - packet_send(); - packet_write_wait(); - gss_release_buffer(&min_status, &send_tok); - } - - if (maj_status == GSS_S_COMPLETE) { - dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL); - /* ssh1 does not have an extra message here */ - if (!compat20) - input_gssapi_exchange_complete(0, 0, ctxt); - else - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, - &input_gssapi_exchange_complete); + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok; + OM_uint32 maj_status, min_status, flags=0; + u_int len; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt = authctxt->methoddata; + recv_tok.value = packet_get_string(&len); + recv_tok.length = len; /* u_int vs. size_t */ + + packet_check_eom(); + + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, + &send_tok, &flags)); + + xfree(recv_tok.value); + + if (GSS_ERROR(maj_status)) { + ssh_gssapi_userauth_error(gssctxt); + if (send_tok.length != 0) { + packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); + packet_put_string(send_tok.value, send_tok.length); + packet_send(); + } + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + userauth_finish(authctxt, 0, "gssapi-with-mic"); + } else { + if (send_tok.length != 0) { + packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + packet_put_string(send_tok.value, send_tok.length); + packet_send(); + } + if (maj_status == GSS_S_COMPLETE) { + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + if (flags & GSS_C_INTEG_FLAG) + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, + &input_gssapi_mic); + else + dispatch_set( + SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, + &input_gssapi_exchange_complete); + } + } + + gss_release_buffer(&min_status, &send_tok); +} + +static void +input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok; + OM_uint32 maj_status; + u_int len; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt = authctxt->methoddata; + recv_tok.value = packet_get_string(&len); + recv_tok.length = len; + + packet_check_eom(); + + /* Push the error token into GSSAPI to see what it says */ + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, + &send_tok, NULL)); + + xfree(recv_tok.value); + + /* We can't return anything to the client, even if we wanted to */ + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + + /* The client will have already moved on to the next auth */ + + gss_release_buffer(&maj_status, &send_tok); +} + +static void +gssapi_set_username(Authctxt *authctxt) +{ + char *lname = NULL; + + if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) { + PRIVSEP(ssh_gssapi_localname(&lname)); + if (lname && lname[0] != '\0') { + if (authctxt->user) xfree(authctxt->user); + authctxt->user = lname; + debug("set username to %s from gssapi context", lname); + authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user)); + if (authctxt->pw) { + authctxt->valid = 1; +#ifdef USE_PAM + if (options.use_pam) + PRIVSEP(start_pam(authctxt)); +#endif + } + } else { + debug("failed to set username from gssapi context"); + packet_send_debug("failed to set username from gssapi context"); } + } } -/* This is called when the client thinks we've completed authentication. +/* + * This is called when the client thinks we've completed authentication. * It should only be enabled in the dispatch handler by the function above, * which only enables it once the GSSAPI exchange is complete. */ - + static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) { - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; - int authenticated; - + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + int authenticated; + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) - fatal("No authentication or GSSAPI context"); - - if ((strcmp(authctxt->user, "") == 0) && (authctxt->pw == NULL)) { - char *lname = NULL; - PRIVSEP(ssh_gssapi_localname(&lname)); - if (lname && lname[0] != '\0') { - xfree(authctxt->user); - authctxt->user = lname; - debug("set username to %s from gssapi context", lname); - authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user)); - } else { - debug("failed to set username from gssapi context"); - } - } - if (authctxt->pw) { -#ifdef USE_PAM - PRIVSEP(start_pam(authctxt->pw->pw_name)); -#endif + fatal("No authentication or GSSAPI context"); + + gssapi_set_username(authctxt); + + gssctxt = authctxt->methoddata; + + /* + * We don't need to check the status, because we're only enabled in + * the dispatcher once the exchange is complete + */ + + packet_check_eom(); + + /* user should be set if valid but we double-check here */ + if (authctxt->valid && authctxt->user && authctxt->user[0]) { + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, + authctxt->pw)); } else { - authctxt->valid = 0; authenticated = 0; - goto finish; } - gssctxt=authctxt->methoddata; + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(authctxt, authenticated, "gssapi-with-mic"); +} - /* ssh1 needs to exchange the hash of the keys */ - if (!compat20) { +static void +input_gssapi_mic(int type, u_int32_t plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + int authenticated = 0; + Buffer b; + gss_buffer_desc mic, gssbuf; + u_int len; - OM_uint32 min_status; - gss_buffer_desc dummy, msg_tok; + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); - /* ssh1 wraps the keys, in the monitor */ + gssctxt = authctxt->methoddata; - dummy.value=malloc(sizeof(ssh1_key_digest)); - memcpy(dummy.value,ssh1_key_digest,sizeof(ssh1_key_digest)); - dummy.length=sizeof(ssh1_key_digest); - if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(gssctxt,&dummy,&msg_tok)))) - fatal("Couldn't wrap keys"); - - packet_start(SSH_SMSG_AUTH_GSSAPI_HASH); - packet_put_string((char *)msg_tok.value,msg_tok.length); - packet_send(); - packet_write_wait(); - gss_release_buffer(&min_status,&msg_tok); - } + mic.value = packet_get_string(&len); + mic.length = len; - - /* We don't need to check the status, because the stored credentials - * which userok uses are only populated once the context init step - * has returned complete. - */ + ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, + "gssapi-with-mic"); + + gssbuf.value = buffer_ptr(&b); + gssbuf.length = buffer_len(&b); - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); + gssapi_set_username(authctxt); -finish: - authctxt->postponed = 0; - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); - userauth_finish(authctxt, authenticated, "gssapi"); + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) + if (authctxt->valid && authctxt->user && authctxt->user[0]) { + authenticated = + PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); + } else { + authenticated = 0; + } + else + logit("GSSAPI MIC check failed"); + + buffer_free(&b); + xfree(mic.value); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(authctxt, authenticated, "gssapi-with-mic"); +} + +static void ssh_gssapi_userauth_error(Gssctxt *ctxt) { + char *errstr; + OM_uint32 maj,min; + + errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min)); + if (errstr) { + packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); + packet_put_int(maj); + packet_put_int(min); + packet_put_cstring(errstr); + packet_put_cstring(""); + packet_send(); + packet_write_wait(); + xfree(errstr); + } } -Authmethod method_external = { - "external-keyx", - userauth_external, +Authmethod method_gsskeyex = { + "gssapi-keyex", + userauth_gsskeyex, &options.gss_authentication }; - + Authmethod method_gssapi = { - "gssapi", - userauth_gssapi, - &options.gss_authentication + "gssapi-with-mic", + userauth_gssapi, + &options.gss_authentication }; #endif /* GSSAPI */