X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/58b60d163a3c1a5735240ec1697d437b7aaf524a..HEAD:/openssh/kexgsss.c diff --git a/openssh/kexgsss.c b/openssh/kexgsss.c index b4e0f2c..3e5205c 100644 --- a/openssh/kexgsss.c +++ b/openssh/kexgsss.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,101 +26,145 @@ #ifdef GSSAPI +#include + #include #include #include "xmalloc.h" #include "buffer.h" -#include "bufaux.h" +#include "ssh2.h" +#include "key.h" +#include "cipher.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" -#include "ssh2.h" #include "ssh-gss.h" #include "monitor_wrap.h" +#include "servconf.h" static void kex_gss_send_error(Gssctxt *ctxt); +extern ServerOptions options; void kexgss_server(Kex *kex) { OM_uint32 maj_status, min_status; - /* Some GSSAPI implementations use the input value of ret_flags (an + /* + * Some GSSAPI implementations use the input value of ret_flags (an * output variable) as a means of triggering mechanism specific * features. Initializing it to zero avoids inadvertently - * activating this non-standard behaviour.*/ + * activating this non-standard behaviour. + */ OM_uint32 ret_flags = 0; - gss_buffer_desc gssbuf,send_tok,recv_tok,msg_tok; + gss_buffer_desc gssbuf, recv_tok, msg_tok; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; Gssctxt *ctxt = NULL; - unsigned int klen, kout; - unsigned char *kbuf; - unsigned char *hash; - DH *dh; - BIGNUM *shared_secret = NULL; - BIGNUM *dh_client_pub = NULL; - int type =0; - u_int slen; + u_int slen, klen, kout, hashlen; + u_char *kbuf, *hash; + DH *dh; + int min = -1, max = -1, nbits = -1; + BIGNUM *shared_secret = NULL; + BIGNUM *dh_client_pub = NULL; + int type = 0; gss_OID oid; - + char *mechs; + /* Initialise GSSAPI */ - debug2("%s: Identifying %s",__func__,kex->name); - oid=ssh_gssapi_server_id_kex(kex->name); - if (oid==NULL) { - packet_disconnect("Unknown gssapi mechanism"); - } - - debug2("%s: Acquiring credentials",__func__); - - if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid)))) { + /* If we're rekeying, privsep means that some of the private structures + * in the GSSAPI code are no longer available. This kludges them back + * into life + */ + if (!ssh_gssapi_oid_table_ok()) + if ((mechs = ssh_gssapi_server_mechanisms())) + xfree(mechs); + + debug2("%s: Identifying %s", __func__, kex->name); + oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); + if (oid == GSS_C_NO_OID) + fatal("Unknown gssapi mechanism"); + + debug2("%s: Acquiring credentials", __func__); + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) { kex_gss_send_error(ctxt); - packet_disconnect("Unable to acquire credentials for the server"); - } - + fatal("Unable to acquire credentials for the server"); + } + + switch (kex->kex_type) { + case KEX_GSS_GRP1_SHA1: + dh = dh_new_group1(); + break; + case KEX_GSS_GRP14_SHA1: + dh = dh_new_group14(); + break; + case KEX_GSS_GEX_SHA1: + debug("Doing group exchange"); + packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); + min = packet_get_int(); + nbits = packet_get_int(); + max = packet_get_int(); + min = MAX(DH_GRP_MIN, min); + max = MIN(DH_GRP_MAX, max); + packet_check_eom(); + if (max < min || nbits < min || max < nbits) + fatal("GSS_GEX, bad parameters: %d !< %d !< %d", + min, nbits, max); + dh = PRIVSEP(choose_dh(min, nbits, max)); + if (dh == NULL) + packet_disconnect("Protocol error: no matching group found"); + + packet_start(SSH2_MSG_KEXGSS_GROUP); + packet_put_bignum2(dh->p); + packet_put_bignum2(dh->g); + packet_send(); + + packet_write_wait(); + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + dh_gen_key(dh, kex->we_need * 8); + do { debug("Wait SSH2_MSG_GSSAPI_INIT"); type = packet_read(); switch(type) { case SSH2_MSG_KEXGSS_INIT: - if (dh_client_pub!=NULL) - packet_disconnect("Received KEXGSS_INIT after initialising"); - recv_tok.value=packet_get_string(&slen); - recv_tok.length=slen; /* int vs. size_t */ - - dh_client_pub = BN_new(); - - if (dh_client_pub == NULL) - packet_disconnect("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub); - - /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ + if (dh_client_pub != NULL) + fatal("Received KEXGSS_INIT after initialising"); + recv_tok.value = packet_get_string(&slen); + recv_tok.length = slen; + + if ((dh_client_pub = BN_new()) == NULL) + fatal("dh_client_pub == NULL"); + + packet_get_bignum2(dh_client_pub); + + /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ break; case SSH2_MSG_KEXGSS_CONTINUE: - recv_tok.value=packet_get_string(&slen); - recv_tok.length=slen; /* int vs. size_t */ + recv_tok.value = packet_get_string(&slen); + recv_tok.length = slen; break; default: - packet_disconnect("Protocol error: didn't expect packet type %d", - type); + packet_disconnect( + "Protocol error: didn't expect packet type %d", + type); } - - maj_status=PRIVSEP(ssh_gssapi_accept_ctx(ctxt,&recv_tok, - &send_tok, &ret_flags)); - gss_release_buffer(&min_status,&recv_tok); - -#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG - if (ret_flags & GSS_C_GLOBUS_LIMITED_PROXY_FLAG) { - packet_disconnect("Limited proxy is not allowed in gssapi key exchange."); - } -#endif + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, + &send_tok, &ret_flags)); + + xfree(recv_tok.value); - if (maj_status!=GSS_S_COMPLETE && send_tok.length==0) { + if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) fatal("Zero length token output when incomplete"); - } if (dh_client_pub == NULL) fatal("No client public key"); @@ -128,102 +172,122 @@ kexgss_server(Kex *kex) if (maj_status & GSS_S_CONTINUE_NEEDED) { debug("Sending GSSAPI_CONTINUE"); packet_start(SSH2_MSG_KEXGSS_CONTINUE); - packet_put_string(send_tok.value,send_tok.length); + packet_put_string((char *)send_tok.value, send_tok.length); packet_send(); - packet_write_wait(); gss_release_buffer(&min_status, &send_tok); } } while (maj_status & GSS_S_CONTINUE_NEEDED); if (GSS_ERROR(maj_status)) { kex_gss_send_error(ctxt); - if (send_tok.length>0) { + if (send_tok.length > 0) { packet_start(SSH2_MSG_KEXGSS_CONTINUE); - packet_put_string(send_tok.value,send_tok.length); + packet_put_string((char *)send_tok.value, send_tok.length); packet_send(); - packet_write_wait(); - } - packet_disconnect("gssapi key exchange handshake failed"); + } + packet_disconnect("GSSAPI Key Exchange handshake failed"); } - - debug("gss_complete"); + if (!(ret_flags & GSS_C_MUTUAL_FLAG)) - packet_disconnect("gssapi_mutual authentication failed"); - + fatal("Mutual Authentication flag wasn't set"); + if (!(ret_flags & GSS_C_INTEG_FLAG)) - packet_disconnect("gssapi channel integrity not established"); - - dh = dh_new_group1(); - dh_gen_key(dh, kex->we_need * 8); + fatal("Integrity flag wasn't set"); - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); + if (!dh_pub_is_valid(dh, dh_client_pub)) + packet_disconnect("bad client public DH value"); - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_client_pub, dh); + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_client_pub, dh); + if (kout < 0) + fatal("DH_compute_key: failed"); shared_secret = BN_new(); - BN_bin2bn(kbuf, kout, shared_secret); + if (shared_secret == NULL) + fatal("kexgss_server: BN_new failed"); + + if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) + fatal("kexgss_server: BN_bin2bn failed"); + memset(kbuf, 0, klen); xfree(kbuf); - - /* The GSSAPI hash is identical to the Diffie Helman one */ - hash = kex_dh_hash( - kex->client_version_string, - kex->server_version_string, - buffer_ptr(&kex->peer), buffer_len(&kex->peer), - buffer_ptr(&kex->my), buffer_len(&kex->my), - NULL, 0, /* Change this if we start sending host keys */ - dh_client_pub, - dh->pub_key, - shared_secret - ); - BN_free(dh_client_pub); - + + switch (kex->kex_type) { + case KEX_GSS_GRP1_SHA1: + case KEX_GSS_GRP14_SHA1: + kex_dh_hash( + kex->client_version_string, kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + NULL, 0, /* Change this if we start sending host keys */ + dh_client_pub, dh->pub_key, shared_secret, + &hash, &hashlen + ); + break; + case KEX_GSS_GEX_SHA1: + kexgex_hash( + kex->evp_md, + kex->client_version_string, kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + NULL, 0, + min, nbits, max, + dh->p, dh->g, + dh_client_pub, + dh->pub_key, + shared_secret, + &hash, &hashlen + ); + break; + default: + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + } + + BN_clear_free(dh_client_pub); + if (kex->session_id == NULL) { - kex->session_id_len = 20; + kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } - + gssbuf.value = hash; - gssbuf.length = 20; /* Hashlen appears to always be 20 */ - - if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) { - kex_gss_send_error(ctxt); + gssbuf.length = hashlen; + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) fatal("Couldn't get MIC"); - } - + packet_start(SSH2_MSG_KEXGSS_COMPLETE); packet_put_bignum2(dh->pub_key); packet_put_string((char *)msg_tok.value,msg_tok.length); - if (send_tok.length!=0) { + if (send_tok.length != 0) { packet_put_char(1); /* true */ - packet_put_string((char *)send_tok.value,send_tok.length); + packet_put_string((char *)send_tok.value, send_tok.length); } else { packet_put_char(0); /* false */ } - packet_send(); - packet_write_wait(); - - /* We used to store the client name and credentials here for later - * use. With privsep, its easier to do this as a by product of the - * call to accept_context, which stores delegated information when - * the context is complete */ - - gss_release_buffer(&min_status, &send_tok); - - /* If we've got a context, delete it. It may be NULL if we've been - * using privsep */ - ssh_gssapi_delete_ctx(&ctxt); - + packet_send(); + + gss_release_buffer(&min_status, &send_tok); + gss_release_buffer(&min_status, &msg_tok); + + if (gss_kex_context == NULL) + gss_kex_context = ctxt; + else + ssh_gssapi_delete_ctx(&ctxt); + DH_free(dh); - kex_derive_keys(kex, hash, shared_secret); + kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); + + /* If this was a rekey, then save out any delegated credentials we + * just exchanged. */ + if (options.gss_store_rekey) + ssh_gssapi_rekey_creds(); } static void