From fe4ad27384e843fd03f01a4e4a0d457a90f312be Mon Sep 17 00:00:00 2001 From: jbasney Date: Mon, 19 Dec 2005 22:53:37 +0000 Subject: [PATCH] merged OPENSSH_4_2P1_SIMON-20050926-2 to trunk --- openssh/Makefile.in | 6 +- openssh/acconfig.h | 6 + openssh/auth-krb5.c | 19 ++- openssh/auth.h | 1 + openssh/auth2-gss.c | 54 ++++++++ openssh/auth2.c | 10 +- openssh/configure.ac | 22 +++ openssh/gss-genr.c | 227 ++++++++++++++++++------------ openssh/gss-serv-krb5.c | 6 +- openssh/gss-serv.c | 140 +++---------------- openssh/kex.c | 6 +- openssh/kex.h | 11 +- openssh/kexgssc.c | 297 +++++++++++++++++++++++----------------- openssh/kexgsss.c | 239 ++++++++++++++++++-------------- openssh/key.c | 2 +- openssh/monitor.c | 60 ++++---- openssh/monitor.h | 2 +- openssh/monitor_wrap.c | 24 ++-- openssh/monitor_wrap.h | 3 +- openssh/readconf.c | 10 ++ openssh/readconf.h | 1 + openssh/servconf.c | 5 +- openssh/servconf.h | 2 +- openssh/session.c | 8 -- openssh/ssh-gss.h | 33 +++-- openssh/ssh_config.5 | 12 +- openssh/sshconnect2.c | 141 +++++++++++++++---- openssh/sshd.c | 137 ++++++++++++------ openssh/sshd_config.5 | 16 +-- 29 files changed, 903 insertions(+), 597 deletions(-) diff --git a/openssh/Makefile.in b/openssh/Makefile.in index 5046720..84e2c77 100644 --- a/openssh/Makefile.in +++ b/openssh/Makefile.in @@ -72,8 +72,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o buffer.o \ atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ - kexgssc.o \ - entropy.o scard-opensc.o gss-genr.o + entropy.o scard-opensc.o gss-genr.o kexgssc.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ sshconnect.o sshconnect1.o sshconnect2.o @@ -85,9 +84,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ auth2-none.o auth2-passwd.o auth2-pubkey.o \ monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ - kexgsss.o gss-serv-gsi.o \ auth-krb5.o \ - auth2-gss.o gss-serv.o gss-serv-krb5.o \ + auth2-gss.o gss-serv.o gss-serv-krb5.o gss-serv-gsi.o kexgsss.o \ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ audit.o audit-bsm.o diff --git a/openssh/acconfig.h b/openssh/acconfig.h index b2e21dd..9adbe8b 100644 --- a/openssh/acconfig.h +++ b/openssh/acconfig.h @@ -360,6 +360,12 @@ /* getaddrinfo is broken (if present) */ #undef BROKEN_GETADDRINFO +/* platform uses an in-memory credentials cache */ +#undef USE_CCAPI + +/* platform has a Security Authorization Session API */ +#undef USE_SECURITY_SESSION_API + /* updwtmpx is broken (if present) */ #undef BROKEN_UPDWTMPX diff --git a/openssh/auth-krb5.c b/openssh/auth-krb5.c index c7367b4..5f554a6 100644 --- a/openssh/auth-krb5.c +++ b/openssh/auth-krb5.c @@ -159,8 +159,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password) len = strlen(authctxt->krb5_ticket_file) + 6; authctxt->krb5_ccname = xmalloc(len); +#ifdef USE_CCAPI + snprintf(authctxt->krb5_ccname, len, "API:%s", + authctxt->krb5_ticket_file); +#else snprintf(authctxt->krb5_ccname, len, "FILE:%s", authctxt->krb5_ticket_file); +#endif #ifdef USE_PAM if (options.use_pam) @@ -212,15 +217,22 @@ krb5_cleanup_proc(Authctxt *authctxt) #ifndef HEIMDAL krb5_error_code ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { - int tmpfd, ret; + int ret; char ccname[40]; mode_t old_umask; +#ifdef USE_CCAPI + char cctemplate[] = "API:krb5cc_%d"; +#else + char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; + int tmpfd; +#endif ret = snprintf(ccname, sizeof(ccname), - "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); - if (ret == -1 || ret >= sizeof(ccname)) + cctemplate, geteuid()); + if (ret == -1 || ret >= (int) sizeof(ccname)) return ENOMEM; +#ifndef USE_CCAPI old_umask = umask(0177); tmpfd = mkstemp(ccname + strlen("FILE:")); umask(old_umask); @@ -235,6 +247,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { return errno; } close(tmpfd); +#endif return (krb5_cc_resolve(ctx, ccname, ccache)); } diff --git a/openssh/auth.h b/openssh/auth.h index 5cd4ea3..4114fa3 100644 --- a/openssh/auth.h +++ b/openssh/auth.h @@ -56,6 +56,7 @@ struct Authctxt { int valid; /* user exists and is allowed to login */ int attempt; int failures; + int server_caused_failure; int force_pwchange; char *user; /* username sent by the client */ char *service; diff --git a/openssh/auth2-gss.c b/openssh/auth2-gss.c index 1445e4f..8622002 100644 --- a/openssh/auth2-gss.c +++ b/openssh/auth2-gss.c @@ -62,6 +62,52 @@ userauth_external(Authctxt *authctxt) return 0; } +/* + * The 'gssapi_keyex' userauth mechanism. + */ +static int +userauth_gsskeyex(Authctxt *authctxt) +{ + int authenticated = 0; + Buffer b, b2; + gss_buffer_desc mic, gssbuf, gssbuf2; + u_int len; + + mic.value = packet_get_string(&len); + mic.length = len; + + 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"); + + 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)); + } + } + + 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 @@ -115,11 +161,13 @@ userauth_gssapi(Authctxt *authctxt) if (!present) { xfree(doid); + authctxt->server_caused_failure = 1; return (0); } if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { xfree(doid); + authctxt->server_caused_failure = 1; return (0); } @@ -383,6 +431,12 @@ Authmethod method_external = { &options.gss_authentication }; +Authmethod method_gsskeyex = { + "gssapi-keyex", + userauth_gsskeyex, + &options.gss_authentication +}; + Authmethod method_gssapi = { "gssapi-with-mic", userauth_gssapi_with_mic, diff --git a/openssh/auth2.c b/openssh/auth2.c index ac0b0d7..319303c 100644 --- a/openssh/auth2.c +++ b/openssh/auth2.c @@ -56,6 +56,7 @@ extern Authmethod method_kbdint; extern Authmethod method_hostbased; #ifdef GSSAPI extern Authmethod method_external; +extern Authmethod method_gsskeyex; extern Authmethod method_gssapi; extern Authmethod method_gssapi_compat; #endif @@ -64,6 +65,7 @@ Authmethod *authmethods[] = { &method_none, &method_pubkey, #ifdef GSSAPI + &method_gsskeyex, &method_external, &method_gssapi, &method_gssapi_compat, @@ -151,7 +153,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) #ifdef GSSAPI if (user[0] == '\0') { debug("received empty username for %s", method); - if (strcmp(method, "external-keyx") == 0) { + if (strcmp(method, "external-keyx") == 0 || + strcmp(method, "gssapi-keyex") == 0) { char *lname = NULL; PRIVSEP(ssh_gssapi_localname(&lname)); if (lname && lname[0] != '\0') { @@ -243,6 +246,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) #endif authctxt->postponed = 0; + authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(method); @@ -313,7 +317,9 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method) /* now we can break out */ authctxt->success = 1; } else { - if (authctxt->failures++ > options.max_authtries) { + /* Dont count server configuration issues against the client */ + if (!authctxt->server_caused_failure && + authctxt->failures++ > options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif diff --git a/openssh/configure.ac b/openssh/configure.ac index 759a0b0..a7fc06e 100644 --- a/openssh/configure.ac +++ b/openssh/configure.ac @@ -257,6 +257,28 @@ case "$host" in AC_DEFINE(BROKEN_SETREUID) AC_DEFINE(BROKEN_SETREGID) AC_DEFINE_UNQUOTED(BIND_8_COMPAT, 1) + AC_MSG_CHECKING(if we have the Security Authorization Session API) + AC_TRY_COMPILE([#include ], + [SessionCreate(0, 0);], + [ac_cv_use_security_session_api="yes" + AC_DEFINE(USE_SECURITY_SESSION_API) + LIBS="$LIBS -framework Security" + AC_MSG_RESULT(yes)], + [ac_cv_use_security_session_api="no" + AC_MSG_RESULT(no)]) + AC_MSG_CHECKING(if we have an in-memory credentials cache) + AC_TRY_COMPILE( + [#include ], + [cc_context_t c; + (void) cc_initialize (&c, 0, NULL, NULL);], + [AC_DEFINE(USE_CCAPI) + LIBS="$LIBS -framework Security" + AC_MSG_RESULT(yes) + if test "x$ac_cv_use_security_session_api" = "xno"; then + AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) + fi], + [AC_MSG_RESULT(no)] + ) ;; *-*-hpux*) # first we define all of the options common to all HP-UX releases diff --git a/openssh/gss-genr.c b/openssh/gss-genr.c index 3d6a4e2..26e20b9 100644 --- a/openssh/gss-genr.c +++ b/openssh/gss-genr.c @@ -1,7 +1,7 @@ /* $OpenBSD: gss-genr.c,v 1.4 2005/07/17 07:17:55 djm Exp $ */ /* - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * Copyright (c) 2001-2005 Simon Wilkinson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,6 +38,7 @@ #include "monitor_wrap.h" #include "canohost.h" #include "ssh2.h" +#include #include "ssh-gss.h" @@ -48,109 +49,139 @@ typedef struct { char *encoded; gss_OID oid; } ssh_gss_kex_mapping; - -static ssh_gss_kex_mapping *gss_enc2oid; -/* Return a list of the gss-group1-sha1-x mechanisms supported by this - * program. - * - * On the client side, we don't need to worry about whether we 'know' - * about the mechanism or not - we assume that any mechanism that we've been - * linked against is suitable for inclusion. +/* + * XXX - It would be nice to find a more elegant way of handling the + * XXX passing of the key exchange context to the userauth routines + */ + +Gssctxt *gss_kex_context = NULL; + +static ssh_gss_kex_mapping *gss_enc2oid = NULL; + +int +ssh_gssapi_oid_table_ok() { + return (gss_enc2oid != NULL); +} + +/* + * Return a list of the gss-group1-sha1 mechanisms supported by this program * - * XXX - We might want to make this configurable in the future, so as to - * XXX - allow the user control over which mechanisms to use. + * We test mechanisms to ensure that we can use them, to avoid starting + * a key exchange with a bad mechanism */ - -char * -ssh_gssapi_client_mechanisms(char *host) { - gss_OID_set supported; - OM_uint32 min_status; - Buffer buf; - size_t i = 0; - char *mechs; - char *encoded; - int enclen; - unsigned char digest[EVP_MAX_MD_SIZE]; - char deroid[2]; - const EVP_MD *evp_md = EVP_md5(); - EVP_MD_CTX md; - int oidpos=0; - - gss_indicate_mechs(&min_status,&supported); - gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping) - *(supported->count+1)); - - buffer_init(&buf); - for (i=0;icount;i++) { - gss_enc2oid[oidpos].encoded=NULL; - - if (supported->elements[i].length<128 && - ssh_gssapi_check_mechanism(&(supported->elements[i]),host)) { +char * +ssh_gssapi_client_mechanisms(const char *host) { + gss_OID_set gss_supported; + OM_uint32 min_status; + + gss_indicate_mechs(&min_status, &gss_supported); - /* Add the required DER encoding octets and MD5 hash */ - deroid[0]=0x06; /* Object Identifier */ - deroid[1]=supported->elements[i].length; + return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, + (void *)host)); +} + +char * +ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, + void *data) { + Buffer buf; + size_t i; + int oidpos, enclen; + char *mechs, *encoded; + unsigned char digest[EVP_MAX_MD_SIZE]; + char deroid[2]; + const EVP_MD *evp_md = EVP_md5(); + EVP_MD_CTX md; + + if (gss_enc2oid != NULL) { + for (i=0;gss_enc2oid[i].encoded!=NULL;i++) + xfree(gss_enc2oid[i].encoded); + xfree(gss_enc2oid); + } + + gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping)* + (gss_supported->count+1)); + + buffer_init(&buf); + + oidpos = 0; + for (i = 0;i < gss_supported->count;i++) { + if (gss_supported->elements[i].length < 128 && + (*check)(&(gss_supported->elements[i]), data)) { + + deroid[0] = SSH_GSS_OIDTYPE; + deroid[1] = gss_supported->elements[i].length; EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md,deroid,2); + EVP_DigestUpdate(&md, deroid, 2); EVP_DigestUpdate(&md, - supported->elements[i].elements, - supported->elements[i].length); + gss_supported->elements[i].elements, + gss_supported->elements[i].length); EVP_DigestFinal(&md, digest, NULL); - - /* Base64 encode it */ - encoded=xmalloc(EVP_MD_size(evp_md)*2); - enclen=__b64_ntop(digest, EVP_MD_size(evp_md), - encoded,EVP_MD_size(evp_md)*2); - if (oidpos!=0) { - buffer_put_char(&buf,','); - } - buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1); - buffer_append(&buf, encoded, enclen); - debug("Mechanism encoded as %s",encoded); + encoded = xmalloc(EVP_MD_size(evp_md)*2); + enclen = __b64_ntop(digest, EVP_MD_size(evp_md), + encoded, EVP_MD_size(evp_md)*2); - gss_enc2oid[oidpos].oid=&(supported->elements[i]); - gss_enc2oid[oidpos].encoded=encoded; + if (oidpos != 0) + buffer_put_char(&buf, ','); + + buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, + sizeof(KEX_GSS_GEX_SHA1_ID)-1); + buffer_append(&buf, encoded, enclen); + buffer_put_char(&buf,','); + buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, + sizeof(KEX_GSS_GRP1_SHA1_ID)-1); + buffer_append(&buf, encoded, enclen); + + gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); + gss_enc2oid[oidpos].encoded = encoded; oidpos++; } } - gss_enc2oid[oidpos].oid=NULL; - gss_enc2oid[oidpos].encoded=NULL; - - buffer_put_char(&buf,'\0'); - - mechs=xmalloc(buffer_len(&buf)); - buffer_get(&buf,mechs,buffer_len(&buf)); + gss_enc2oid[oidpos].oid = NULL; + gss_enc2oid[oidpos].encoded = NULL; + + buffer_put_char(&buf, '\0'); + + mechs = xmalloc(buffer_len(&buf)); + buffer_get(&buf, mechs, buffer_len(&buf)); buffer_free(&buf); - if (strlen(mechs)==0) - return(NULL); - else - return(mechs); -} -gss_OID -ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name) { - int i=0; - - if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) { - return(NULL); + if (strlen(mechs) == 0) { + xfree(mechs); + mechs = NULL; } - name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the ID string */ - - while (gss_enc2oid[i].encoded!=NULL && - strcmp(name,gss_enc2oid[i].encoded)!=0) { - i++; + return (mechs); +} + +gss_OID +ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int *gex) { + int i = 0; + + if (strncmp(name, KEX_GSS_GRP1_SHA1_ID, + sizeof(KEX_GSS_GRP1_SHA1_ID)-1) == 0) { + name+=sizeof(KEX_GSS_GRP1_SHA1_ID)-1; + *gex = 0; + } else if (strncmp(name, KEX_GSS_GEX_SHA1_ID, + sizeof(KEX_GSS_GEX_SHA1_ID)-1) == 0) { + name+=sizeof(KEX_GSS_GEX_SHA1_ID)-1; + *gex = 1; + } else { + return NULL; } - - if (gss_enc2oid[i].oid!=NULL) { - ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid); + + while (gss_enc2oid[i].encoded != NULL && + strcmp(name, gss_enc2oid[i].encoded) != 0) { + i++; } + if (gss_enc2oid[i].oid != NULL && ctx != NULL) + ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); + return gss_enc2oid[i].oid; } @@ -378,6 +409,9 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx) OM_uint32 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) { + if (ctx == NULL) + return -1; + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, GSS_C_QOP_DEFAULT, buffer, hash))) ssh_gssapi_error(ctx); @@ -385,6 +419,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) return (ctx->major); } +/* Priviledged when used by server */ +OM_uint32 +ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +{ + if (ctx == NULL) + return -1; + + ctx->major = gss_verify_mic(&ctx->minor, ctx->context, + gssbuf, gssmic, NULL); + + return (ctx->major); +} + void ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, const char *context) @@ -407,18 +454,18 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) { } int -ssh_gssapi_check_mechanism(gss_OID oid, const char *host) { +ssh_gssapi_check_mechanism(gss_OID oid, void *host) { Gssctxt * ctx = NULL; - gss_buffer_desc token; - OM_uint32 major,minor; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; ssh_gssapi_build_ctx(&ctx); - ssh_gssapi_set_oid(ctx,oid); - ssh_gssapi_import_name(ctx,host); - major=ssh_gssapi_init_ctx(ctx,0, GSS_C_NO_BUFFER, &token, NULL); - gss_release_buffer(&minor,&token); + ssh_gssapi_set_oid(ctx, oid); + ssh_gssapi_import_name(ctx, host); + major = ssh_gssapi_init_ctx(ctx, 0, GSS_C_NO_BUFFER, &token, NULL); + gss_release_buffer(&minor, &token); ssh_gssapi_delete_ctx(&ctx); - return(!GSS_ERROR(major)); + return (!GSS_ERROR(major)); } #endif /* GSSAPI */ diff --git a/openssh/gss-serv-krb5.c b/openssh/gss-serv-krb5.c index 8b3a4a7..a1e87ac 100644 --- a/openssh/gss-serv-krb5.c +++ b/openssh/gss-serv-krb5.c @@ -40,10 +40,10 @@ extern ServerOptions options; #ifdef HEIMDAL # include -#else -# ifdef HAVE_GSSAPI_KRB5 +#elsif !defined(MECHGLUE) +# ifdef HAVE_GSSAPI_KRB5_H # include -# elif HAVE_GSSAPI_GSSAPI_KRB5 +# elif HAVE_GSSAPI_GSSAPI_KRB5_H # include # endif #endif diff --git a/openssh/gss-serv.c b/openssh/gss-serv.c index 9b6b4c2..0c73250 100644 --- a/openssh/gss-serv.c +++ b/openssh/gss-serv.c @@ -45,8 +45,6 @@ #include "ssh-gss.h" extern ServerOptions options; -extern u_char *session_id2; -extern int session_id2_len; static ssh_gssapi_client gssapi_client = { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, @@ -76,6 +74,28 @@ ssh_gssapi_mech* supported_mechs[]= { static int limited = 0; #endif +/* Unpriviledged */ +char * +ssh_gssapi_server_mechanisms() { + gss_OID_set supported; + + ssh_gssapi_supported_oids(&supported); + return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, + NULL)); +} + +/* Unpriviledged */ +int +ssh_gssapi_server_check_mech(gss_OID oid, void *data) { + Gssctxt * ctx = NULL; + int res; + + res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); + ssh_gssapi_delete_ctx(&ctx); + + return (res); +} + /* Unpriviledged */ void ssh_gssapi_supported_oids(gss_OID_set *oidset) @@ -335,112 +355,6 @@ ssh_gssapi_userok(char *user) return (0); } -/* Return a list of the gss-group1-sha1-x mechanisms supported by this - * program. - * - * We only support the mechanisms that we've indicated in the list above, - * but we check that they're supported by the GSSAPI mechanism on the - * machine. We also check, before including them in the list, that - * we have the necesary information in order to carry out the key exchange - * (that is, that the user has credentials, the server's creds are accessible, - * etc) - * - * The way that this is done is fairly nasty, as we do a lot of work that - * is then thrown away. This should possibly be implemented with a cache - * that stores the results (in an expanded Gssctxt structure), which are - * then used by the first calls if that key exchange mechanism is chosen. - */ - -/* Unpriviledged */ -char * -ssh_gssapi_server_mechanisms() { - gss_OID_set supported; - Gssctxt *ctx = NULL; - OM_uint32 maj_status, min_status; - Buffer buf; - int i = 0; - int first = 0; - int present; - char * mechs; - - ssh_gssapi_supported_oids(&supported); - - buffer_init(&buf); - - while(supported_mechs[i]->name != NULL) { - if ((maj_status=gss_test_oid_set_member(&min_status, - &supported_mechs[i]->oid, - supported, - &present))) { - present=0; - } - - if (present) { - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, - &supported_mechs[i]->oid)))) { - /* Append gss_group1_sha1_x to our list */ - if (first++!=0) - buffer_put_char(&buf,','); - buffer_append(&buf, KEX_GSS_SHA1, - sizeof(KEX_GSS_SHA1)-1); - buffer_append(&buf, - supported_mechs[i]->enc_name, - strlen(supported_mechs[i]->enc_name)); - debug("GSSAPI mechanism %s (%s%s) supported", - supported_mechs[i]->name, KEX_GSS_SHA1, - supported_mechs[i]->enc_name); - } else { - debug("no credentials for GSSAPI mechanism %s", - supported_mechs[i]->name); - } - } else { - debug("GSSAPI mechanism %s not supported", - supported_mechs[i]->name); - } - ssh_gssapi_delete_ctx(&ctx); - i++; - } - - buffer_put_char(&buf,'\0'); - - mechs=xmalloc(buffer_len(&buf)); - buffer_get(&buf,mechs,buffer_len(&buf)); - buffer_free(&buf); - if (strlen(mechs)==0) { - options.gss_authentication = 0; /* no mechs. skip gss auth. */ - return(NULL); - } else { - return(mechs); - } -} - -/* Return the OID that corresponds to the given context name */ - -/* Unpriviledged */ -gss_OID -ssh_gssapi_server_id_kex(char *name) { - int i=0; - - if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) { - return(NULL); - } - - name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the MIME string */ - - while (supported_mechs[i]->name!=NULL && - strcmp(name,supported_mechs[i]->enc_name)!=0) { - i++; - } - - if (supported_mechs[i]->name==NULL) - return (NULL); - - debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i]->name, - KEX_GSS_SHA1, supported_mechs[i]->enc_name); - - return &supported_mechs[i]->oid; -} - /* Priviledged */ int ssh_gssapi_localname(char **user) @@ -459,14 +373,4 @@ ssh_gssapi_localname(char **user) return(0); } -/* Priviledged */ -OM_uint32 -ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -{ - ctx->major = gss_verify_mic(&ctx->minor, ctx->context, - gssbuf, gssmic, NULL); - - return (ctx->major); -} - #endif diff --git a/openssh/kex.c b/openssh/kex.c index 7d81421..8cd851d 100644 --- a/openssh/kex.c +++ b/openssh/kex.c @@ -303,7 +303,11 @@ choose_kex(Kex *k, char *client, char *server) } else if (strcmp(k->name, KEX_DHGEX) == 0) { k->kex_type = KEX_DH_GEX_SHA1; #ifdef GSSAPI - } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) { + } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID, + sizeof(KEX_GSS_GEX_SHA1_ID)-1) == 0) { + k->kex_type = KEX_GSS_GEX_SHA1; + } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID, + sizeof(KEX_GSS_GRP1_SHA1_ID)-1) == 0) { k->kex_type = KEX_GSS_GRP1_SHA1; #endif } else diff --git a/openssh/kex.h b/openssh/kex.h index 1cedd99..ce9d334 100644 --- a/openssh/kex.h +++ b/openssh/kex.h @@ -64,6 +64,7 @@ enum kex_exchange { KEX_DH_GRP14_SHA1, KEX_DH_GEX_SHA1, KEX_GSS_GRP1_SHA1, + KEX_GSS_GEX_SHA1, KEX_MAX }; @@ -120,6 +121,11 @@ struct Kex { Buffer peer; int done; int flags; +#ifdef GSSAPI + int gss_deleg_creds; + int gss_trust_dns; + char *gss_host; +#endif char *client_version_string; char *server_version_string; struct KexOptions options; @@ -142,9 +148,10 @@ void kexdh_client(Kex *); void kexdh_server(Kex *); void kexgex_client(Kex *); void kexgex_server(Kex *); + #ifdef GSSAPI -void kexgss_client(Kex *); -void kexgss_server(Kex *); +void kexgss_client(Kex *); +void kexgss_server(Kex *); #endif u_char * diff --git a/openssh/kexgssc.c b/openssh/kexgssc.c index 627f14e..acd2045 100644 --- a/openssh/kexgssc.c +++ b/openssh/kexgssc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * Copyright (c) 2001-2005 Simon Wilkinson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,213 +41,262 @@ #include "ssh-gss.h" void -kexgss_client(Kex *kex) -{ - gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok, *token_ptr; +kexgss_client(Kex *kex) { + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; Gssctxt *ctxt; OM_uint32 maj_status, min_status, ret_flags; unsigned int klen, kout; DH *dh; - BIGNUM *dh_server_pub = 0; - BIGNUM *shared_secret = 0; + BIGNUM *dh_server_pub = NULL; + BIGNUM *shared_secret = NULL; + BIGNUM *p = NULL; + BIGNUM *g = NULL; unsigned char *kbuf; unsigned char *hash; - unsigned char *serverhostkey = 0; + unsigned char *serverhostkey = NULL; char *msg; char *lang; int type = 0; int first = 1; - u_int slen = 0; + unsigned int slen = 0; + int gex = 0; + int nbits = -1, min = -1, max = -1; u_int strlen; - - /* Initialise our GSSAPI world */ + + /* Initialise our GSSAPI world */ ssh_gssapi_build_ctx(&ctxt); - if (ssh_gssapi_client_id_kex(ctxt,kex->name)==NULL) { + if (ssh_gssapi_id_kex(ctxt, kex->name, &gex) == NULL) fatal("Couldn't identify host exchange"); - } - if (ssh_gssapi_import_name(ctxt,get_canonical_hostname(1))) { - fatal("Couldn't import hostname "); + if (ssh_gssapi_import_name(ctxt, kex->gss_host)) + fatal("Couldn't import hostname"); + + if (gex) { + debug("Doing group exchange\n"); + nbits = dh_estimate(kex->we_need * 8); + min = DH_GRP_MIN; + max = DH_GRP_MAX; + packet_start(SSH2_MSG_KEXGSS_GROUPREQ); + packet_put_int(min); + packet_put_int(nbits); + packet_put_int(max); + + packet_send(); + + packet_read_expect(SSH2_MSG_KEXGSS_GROUP); + + if ((p = BN_new()) == NULL) + fatal("BN_new() failed"); + packet_get_bignum2(p); + if ((g = BN_new()) == NULL) + fatal("BN_new() failed"); + packet_get_bignum2(g); + packet_check_eom(); + + if (BN_num_bits(p) < min || BN_num_bits(p) > max) + fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", + min, BN_num_bits(p), max); + + dh = dh_new_group(g, p); + } else { + dh = dh_new_group1(); } - /* This code should match that in ssh_dh1_client */ - /* Step 1 - e is dh->pub_key */ - dh = dh_new_group1(); dh_gen_key(dh, kex->we_need * 8); /* This is f, we initialise it now to make life easier */ - dh_server_pub = BN_new(); - if (dh_server_pub == NULL) { - fatal("dh_server_pub == NULL"); - } - + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + token_ptr = GSS_C_NO_BUFFER; do { debug("Calling gss_init_sec_context"); - maj_status=ssh_gssapi_init_ctx(ctxt, - kex->options.gss_deleg_creds, - token_ptr,&send_tok, - &ret_flags); + maj_status = ssh_gssapi_init_ctx(ctxt, + kex->gss_deleg_creds, token_ptr, &send_tok, + &ret_flags); if (GSS_ERROR(maj_status)) { - 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); - } + send_tok.length); + } fatal("gss_init_context failed"); } /* If we've got an old receive buffer get rid of it */ if (token_ptr != GSS_C_NO_BUFFER) - (void) gss_release_buffer(&min_status, &recv_tok); - - + xfree(recv_tok.value); + if (maj_status == GSS_S_COMPLETE) { /* If mutual state flag is not true, kex fails */ - if (!(ret_flags & GSS_C_MUTUAL_FLAG)) { + if (!(ret_flags & GSS_C_MUTUAL_FLAG)) fatal("Mutual authentication failed"); - } + /* If integ avail flag is not true kex fails */ - if (!(ret_flags & GSS_C_INTEG_FLAG)) { + if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity check failed"); - } } - - /* If we have data to send, then the last message that we - * received cannot have been a 'complete'. */ - if (send_tok.length !=0) { + + /* + * If we have data to send, then the last message that we + * received cannot have been a 'complete'. + */ + if (send_tok.length != 0) { if (first) { packet_start(SSH2_MSG_KEXGSS_INIT); packet_put_string(send_tok.value, - send_tok.length); + send_tok.length); packet_put_bignum2(dh->pub_key); - first=0; + first = 0; } else { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, - send_tok.length); + send_tok.length); } packet_send(); - packet_write_wait(); + gss_release_buffer(&min_status, &send_tok); + + /* If we've sent them data, they should reply */ + do { + type = packet_read(); + if (type == SSH2_MSG_KEXGSS_HOSTKEY) { + debug("Received KEXGSS_HOSTKEY"); + if (serverhostkey) + fatal("Server host key received more than once"); + serverhostkey = + packet_get_string(&slen); + } + } while (type == SSH2_MSG_KEXGSS_HOSTKEY); - - /* If we've sent them data, they'd better be polite - * and reply. */ - - type = packet_read(); switch (type) { - case SSH2_MSG_KEXGSS_HOSTKEY: - debug("Received KEXGSS_HOSTKEY"); - serverhostkey=packet_get_string(&slen); - break; case SSH2_MSG_KEXGSS_CONTINUE: debug("Received GSSAPI_CONTINUE"); if (maj_status == GSS_S_COMPLETE) fatal("GSSAPI Continue received from server when complete"); - recv_tok.value=packet_get_string(&strlen); - recv_tok.length=strlen; /* u_int vs. size_t */ + recv_tok.value = packet_get_string(&strlen); + recv_tok.length = strlen; break; case SSH2_MSG_KEXGSS_COMPLETE: debug("Received GSSAPI_COMPLETE"); - packet_get_bignum2(dh_server_pub); - msg_tok.value=packet_get_string(&strlen); - msg_tok.length=strlen; /* u_int vs. size_t */ + packet_get_bignum2(dh_server_pub); + msg_tok.value = packet_get_string(&strlen); + msg_tok.length = strlen; /* Is there a token included? */ if (packet_get_char()) { recv_tok.value= packet_get_string(&strlen); - recv_tok.length=strlen; /*u_int/size_t*/ + recv_tok.length = strlen; /* If we're already complete - protocol error */ if (maj_status == GSS_S_COMPLETE) packet_disconnect("Protocol error: received token when complete"); - } else { - /* No token included */ - if (maj_status != GSS_S_COMPLETE) - packet_disconnect("Protocol error: did not receive final token"); + } else { + /* No token included */ + if (maj_status != GSS_S_COMPLETE) + packet_disconnect("Protocol error: did not receive final token"); } break; case SSH2_MSG_KEXGSS_ERROR: debug("Received Error"); - maj_status=packet_get_int(); - min_status=packet_get_int(); - msg=packet_get_string(NULL); - lang=packet_get_string(NULL); + maj_status = packet_get_int(); + min_status = packet_get_int(); + msg = packet_get_string(NULL); + lang = packet_get_string(NULL); fatal("GSSAPI Key Exchange Error: \n%s",msg); default: packet_disconnect("Protocol error: didn't expect packet type %d", type); } - token_ptr=&recv_tok; + token_ptr = &recv_tok; } else { /* No data, and not complete */ - if (maj_status!=GSS_S_COMPLETE) { + if (maj_status != GSS_S_COMPLETE) fatal("Not complete, and no token output"); - } } - } while (maj_status & GSS_S_CONTINUE_NEEDED); - - /* We _must_ have received a COMPLETE message in reply from the - * server, which will have set dh_server_pub and msg_tok */ - - if (type!=SSH2_MSG_KEXGSS_COMPLETE) - fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); - + } while (maj_status & GSS_S_CONTINUE_NEEDED); + + /* + * We _must_ have received a COMPLETE message in reply from the + * server, which will have set dh_server_pub and msg_tok + */ + + if (type != SSH2_MSG_KEXGSS_COMPLETE) + fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); + /* Check f in range [1, p-1] */ - if (!dh_pub_is_valid(dh, dh_server_pub)) - packet_disconnect("bad server public DH value"); - - /* compute K=f^x mod p */ - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_server_pub, dh); - - shared_secret = BN_new(); - BN_bin2bn(kbuf,kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - /* The GSS hash is identical to the DH one */ - hash = kex_dh_hash( - kex->client_version_string, - kex->server_version_string, - buffer_ptr(&kex->my), buffer_len(&kex->my), - buffer_ptr(&kex->peer), buffer_len(&kex->peer), - serverhostkey, slen, /* server host key */ - dh->pub_key, /* e */ - dh_server_pub, /* f */ - shared_secret /* K */ - ); - - gssbuf.value=hash; - gssbuf.length=20; - - /* Verify that H matches the token we just got. */ - if ((maj_status = gss_verify_mic(&min_status, - ctxt->context, - &gssbuf, - &msg_tok, - NULL))) { + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); - packet_disconnect("Hash's MIC didn't verify"); - } - - DH_free(dh); - ssh_gssapi_delete_ctx(&ctxt); - /* save session id */ - if (kex->session_id == NULL) { - kex->session_id_len = 20; - kex->session_id = xmalloc(kex->session_id_len); - memcpy(kex->session_id, hash, kex->session_id_len); + /* compute K=f^x mod p */ + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, dh); + + shared_secret = BN_new(); + BN_bin2bn(kbuf,kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + if (gex) { + hash = kexgex_hash( kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + serverhostkey, slen, + min, nbits, max, + dh->p, dh->g, + dh->pub_key, + dh_server_pub, + shared_secret + ); + } else { + /* The GSS hash is identical to the DH one */ + hash = kex_dh_hash( kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + serverhostkey, slen, /* server host key */ + dh->pub_key, /* e */ + dh_server_pub, /* f */ + shared_secret /* K */ + ); } - + + gssbuf.value = hash; + gssbuf.length = 20; + + /* Verify that the hash matches the MIC we just got. */ + if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) + packet_disconnect("Hash's MIC didn't verify"); + + xfree(msg_tok.value); + + DH_free(dh); + if (serverhostkey) + xfree(serverhostkey); + BN_clear_free(dh_server_pub); + + /* save session id */ + if (kex->session_id == NULL) { + kex->session_id_len = 20; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + if (gss_kex_context == NULL) + gss_kex_context = ctxt; + else + ssh_gssapi_delete_ctx(&ctxt); + kex_derive_keys(kex, hash, shared_secret); BN_clear_free(shared_secret); - kex_finish(kex); + kex_finish(kex); } #endif /* GSSAPI */ diff --git a/openssh/kexgsss.c b/openssh/kexgsss.c index c4e0e9d..52e46e8 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-2005 Simon Wilkinson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,168 +47,209 @@ 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; + unsigned int klen, kout; + unsigned char *kbuf, *hash; + DH *dh; + int min = -1, max = -1, nbits = -1; + BIGNUM *shared_secret = NULL; + BIGNUM *dh_client_pub = NULL; + int type = 0; + int gex; u_int slen; gss_OID oid; /* 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()) + ssh_gssapi_server_mechanisms(); + + debug2("%s: Identifying %s", __func__, kex->name); + oid = ssh_gssapi_id_kex(NULL, kex->name, &gex); + if (oid == NULL) + 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"); + } + + if (gex) { + 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(); + + } else { + dh = dh_new_group1(); + } + 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); - + 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) + fatal("Zero length token output when incomplete"); + if (dh_client_pub == NULL) fatal("No client public key"); 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(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(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); shared_secret = BN_new(); BN_bin2bn(kbuf, kout, shared_secret); 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 - ); + + if (gex) { + hash = kexgex_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, + min, nbits, max, + dh->p, dh->g, + dh_client_pub, + dh->pub_key, + shared_secret + ); + } + else { + /* 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); - + if (kex->session_id == NULL) { kex->session_id_len = 20; 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); + + 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); diff --git a/openssh/key.c b/openssh/key.c index ea87112..239a359 100644 --- a/openssh/key.c +++ b/openssh/key.c @@ -650,7 +650,7 @@ key_type_from_name(char *name) return KEY_RSA; } else if (strcmp(name, "ssh-dss") == 0) { return KEY_DSA; - } else if (strcmp(name, "null") == 0){ + } else if (strcmp(name, "null") == 0) { return KEY_NULL; } debug2("key_type_from_name: unknown key type '%s'", name); diff --git a/openssh/monitor.c b/openssh/monitor.c index 7a6e463..346bbd2 100644 --- a/openssh/monitor.c +++ b/openssh/monitor.c @@ -141,12 +141,6 @@ int mm_answer_gss_setup_ctx(int, Buffer *); int mm_answer_gss_accept_ctx(int, Buffer *); int mm_answer_gss_userok(int, Buffer *); int mm_answer_gss_checkmic(int, Buffer *); -#endif - -#ifdef GSSAPI -int mm_answer_gss_setup_ctx(int, Buffer *); -int mm_answer_gss_accept_ctx(int, Buffer *); -int mm_answer_gss_userok(int, Buffer *); int mm_answer_gss_sign(int, Buffer *); int mm_answer_gss_error(int, Buffer *); int mm_answer_gss_indicate_mechs(int, Buffer *); @@ -218,11 +212,11 @@ struct mon_table mon_dispatch_proto20[] = { {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, {MONITOR_REQ_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error}, {MONITOR_REQ_GSSMECHS, MON_ISAUTH, mm_answer_gss_indicate_mechs}, {MONITOR_REQ_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname}, - {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, #endif {0, 0, NULL} }; @@ -1668,6 +1662,7 @@ mm_get_kex(Buffer *m) kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; #ifdef GSSAPI kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; #endif kex->server = 1; kex->hostkey_type = buffer_get_int(m); @@ -1912,6 +1907,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); } return (0); } @@ -1963,40 +1959,41 @@ mm_answer_gss_userok(int sock, Buffer *m) return (authenticated); } -int -mm_answer_gss_sign(int socket, Buffer *m) { - gss_buffer_desc data,hash; - OM_uint32 major,minor; +int +mm_answer_gss_sign(int socket, Buffer *m) +{ + gss_buffer_desc data; + gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; u_int len; - data.value = buffer_get_string(m, &len); + data.value = buffer_get_string(m, &len); data.length = len; - if (data.length != 20) - fatal("%s: data length incorrect: %d", __func__, - (int)data.length); + if (data.length != 20) + fatal("%s: data length incorrect: %d", __func__, data.length); - /* Save the session ID - only first time round */ - if (session_id2_len == 0) { - session_id2_len=data.length; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, data.value, session_id2_len); - } - major=ssh_gssapi_sign(gsscontext, &data, &hash); + /* Save the session ID on the first time around */ + if (session_id2_len == 0) { + session_id2_len = data.length; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, data.value, session_id2_len); + } + major = ssh_gssapi_sign(gsscontext, &data, &hash); - xfree(data.value); + xfree(data.value); - buffer_clear(m); - buffer_put_int(m, major); - buffer_put_string(m, hash.value, hash.length); + buffer_clear(m); + buffer_put_int(m, major); + buffer_put_string(m, hash.value, hash.length); - mm_request_send(socket,MONITOR_ANS_GSSSIGN,m); + mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); - gss_release_buffer(&minor,&hash); + gss_release_buffer(&minor, &hash); - /* Turn on permissions for getpwnam */ + /* Turn on getpwnam permissions */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); - - return(0); + + return (0); } int @@ -2062,4 +2059,5 @@ mm_answer_gss_localname(int socket, Buffer *m) { return(0); } + #endif /* GSSAPI */ diff --git a/openssh/monitor.h b/openssh/monitor.h index cfce700..cb92f14 100644 --- a/openssh/monitor.h +++ b/openssh/monitor.h @@ -52,11 +52,11 @@ enum monitor_reqtype { MONITOR_REQ_GSSSETUP, MONITOR_ANS_GSSSETUP, MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, - MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN, MONITOR_REQ_GSSMECHS, MONITOR_ANS_GSSMECHS, MONITOR_REQ_GSSLOCALNAME, MONITOR_ANS_GSSLOCALNAME, MONITOR_REQ_GSSERR, MONITOR_ANS_GSSERR, MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, + MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN, MONITOR_REQ_PAM_START, MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, diff --git a/openssh/monitor_wrap.c b/openssh/monitor_wrap.c index 6768a67..83f7b2c 100644 --- a/openssh/monitor_wrap.c +++ b/openssh/monitor_wrap.c @@ -1218,24 +1218,25 @@ mm_ssh_gssapi_userok(char *user) } OM_uint32 -mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) { - Buffer m; - OM_uint32 major; +mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) +{ + Buffer m; + OM_uint32 major; u_int len; - buffer_init(&m); - buffer_put_string(&m, data->value, data->length); + buffer_init(&m); + buffer_put_string(&m, data->value, data->length); - mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); - mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); - major=buffer_get_int(&m); - hash->value = buffer_get_string(&m, &len); + major = buffer_get_int(&m); + hash->value = buffer_get_string(&m, &len); hash->length = len; buffer_free(&m); - - return(major); + + return(major); } char * @@ -1314,4 +1315,5 @@ mm_ssh_gssapi_localname(char **lname) return(0); } + #endif /* GSSAPI */ diff --git a/openssh/monitor_wrap.h b/openssh/monitor_wrap.h index dfc995f..ab05333 100644 --- a/openssh/monitor_wrap.h +++ b/openssh/monitor_wrap.h @@ -63,8 +63,7 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); int mm_ssh_gssapi_userok(char *user); OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); -OM_uint32 mm_ssh_gssapi_sign(Gssctxt *ctxt, gss_buffer_desc *buffer, - gss_buffer_desc *hash); +OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); int mm_ssh_gssapi_localname(char **user); OM_uint32 mm_gss_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set); diff --git a/openssh/readconf.c b/openssh/readconf.c index c4b542d..a926561 100644 --- a/openssh/readconf.c +++ b/openssh/readconf.c @@ -105,6 +105,7 @@ typedef enum { oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssKeyEx, oGssDelegateCreds, + oGssTrustDns, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, oDeprecated, oUnsupported @@ -141,10 +142,12 @@ static struct { { "gssapiauthentication", oGssAuthentication }, { "gssapikeyexchange", oGssKeyEx }, { "gssapidelegatecredentials", oGssDelegateCreds }, + { "gssapitrustdns", oGssTrustDns }, #else { "gssapiauthentication", oUnsupported }, { "gssapikeyexchange", oUnsupported }, { "gssapidelegatecredentials", oUnsupported }, + { "gssapitrustdns", oUnsupported }, #endif { "fallbacktorsh", oDeprecated }, { "usersh", oDeprecated }, @@ -416,6 +419,10 @@ parse_flag: intptr = &options->gss_deleg_creds; goto parse_flag; + case oGssTrustDns: + intptr = &options->gss_trust_dns; + goto parse_flag; + case oBatchMode: intptr = &options->batch_mode; goto parse_flag; @@ -924,6 +931,7 @@ initialize_options(Options * options) options->gss_authentication = -1; options->gss_keyex = -1; options->gss_deleg_creds = -1; + options->gss_trust_dns = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->kbd_interactive_devices = NULL; @@ -1009,6 +1017,8 @@ fill_default_options(Options * options) options->gss_keyex = 1; if (options->gss_deleg_creds == -1) options->gss_deleg_creds = 1; + if (options->gss_trust_dns == -1) + options->gss_trust_dns = 1; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) diff --git a/openssh/readconf.h b/openssh/readconf.h index ee981ab..991e9b5 100644 --- a/openssh/readconf.h +++ b/openssh/readconf.h @@ -47,6 +47,7 @@ typedef struct { int gss_authentication; /* Try GSS authentication */ int gss_keyex; int gss_deleg_creds; /* Delegate GSS credentials */ + int gss_trust_dns; /* Trust DNS for GSS canonicalization */ int password_authentication; /* Try password * authentication. */ int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ diff --git a/openssh/servconf.c b/openssh/servconf.c index 59bb58b..ece97dc 100644 --- a/openssh/servconf.c +++ b/openssh/servconf.c @@ -77,7 +77,7 @@ initialize_server_options(ServerOptions *options) #endif options->kerberos_get_afs_token = -1; options->gss_authentication=-1; - options->gss_keyex=-1; + options->gss_keyex = -1; options->gss_cleanup_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; @@ -281,8 +281,7 @@ typedef enum { sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, - sGssAuthentication, sGssCleanupCreds, sAcceptEnv, - sGssKeyEx, + sGssAuthentication, sGssKeyEx, sGssCleanupCreds, sAcceptEnv, sUsePrivilegeSeparation, sDeprecated, sUnsupported } ServerOpCodes; diff --git a/openssh/servconf.h b/openssh/servconf.h index 0a57e1c..1750622 100644 --- a/openssh/servconf.h +++ b/openssh/servconf.h @@ -93,7 +93,7 @@ typedef struct { int kerberos_get_afs_token; /* If true, try to get AFS token if * authenticated with Kerberos. */ int gss_authentication; /* If true, permit GSSAPI authentication */ - int gss_keyex; /* If true, permit GSSAPI key exchange. */ + int gss_keyex; /* If true, permit GSSAPI key exchange */ int gss_cleanup_creds; /* If true, destroy cred cache on logout */ int password_authentication; /* If true, permit password * authentication. */ diff --git a/openssh/session.c b/openssh/session.c index 7ae4a8e..9de2097 100644 --- a/openssh/session.c +++ b/openssh/session.c @@ -706,14 +706,6 @@ do_exec(Session *s, const char *command) } #endif -#ifdef GSSAPI - if (options.gss_authentication) { - temporarily_use_uid(s->pw); - ssh_gssapi_storecreds(); - restore_uid(); - } -#endif - #ifdef SSH_AUDIT_EVENTS if (command != NULL) PRIVSEP(audit_run_command(command)); diff --git a/openssh/ssh-gss.h b/openssh/ssh-gss.h index 165e733..12f40c5 100644 --- a/openssh/ssh-gss.h +++ b/openssh/ssh-gss.h @@ -56,11 +56,6 @@ #endif /* !MECHGLUE */ /* draft-ietf-secsh-gsskeyex-06 */ -#define SSH2_MSG_KEXGSS_INIT 30 -#define SSH2_MSG_KEXGSS_CONTINUE 31 -#define SSH2_MSG_KEXGSS_COMPLETE 32 -#define SSH2_MSG_KEXGSS_HOSTKEY 33 -#define SSH2_MSG_KEXGSS_ERROR 34 #define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 #define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 #define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 @@ -70,7 +65,15 @@ #define SSH_GSS_OIDTYPE 0x06 -#define KEX_GSS_SHA1 "gss-group1-sha1-" +#define SSH2_MSG_KEXGSS_INIT 30 +#define SSH2_MSG_KEXGSS_CONTINUE 31 +#define SSH2_MSG_KEXGSS_COMPLETE 32 +#define SSH2_MSG_KEXGSS_HOSTKEY 33 +#define SSH2_MSG_KEXGSS_ERROR 34 +#define SSH2_MSG_KEXGSS_GROUPREQ 40 +#define SSH2_MSG_KEXGSS_GROUP 41 +#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" +#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" typedef struct { char *filename; @@ -109,9 +112,7 @@ typedef struct { } Gssctxt; extern ssh_gssapi_mech *supported_mechs[]; - -char *ssh_gssapi_client_mechanisms(char *); -gss_OID ssh_gssapi_client_id_kex(Gssctxt *, char *); +extern Gssctxt *gss_kex_context; int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); @@ -134,18 +135,22 @@ OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); -int ssh_gssapi_check_mechanism(gss_OID oid, const char *host); - -/* In the server */ -char *ssh_gssapi_server_mechanisms(); -gss_OID ssh_gssapi_server_id_kex(char *name); int ssh_gssapi_localname(char **name); +typedef int ssh_gssapi_check_fn(gss_OID, void *); +char *ssh_gssapi_client_mechanisms(const char *host); +char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, void *); +int ssh_gssapi_check_mechanism(gss_OID, void *); +gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int *); + +int ssh_gssapi_server_check_mech(gss_OID, void *); int ssh_gssapi_userok(char *name); OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); void ssh_gssapi_do_child(char ***, u_int *); void ssh_gssapi_cleanup_creds(void); void ssh_gssapi_storecreds(void); +char * ssh_gssapi_server_mechanisms(void); +int ssh_gssapi_oid_table_ok(); #ifdef MECHGLUE gss_cred_id_t __gss_get_mechanism_cred diff --git a/openssh/ssh_config.5 b/openssh/ssh_config.5 index 5719873..9d37d58 100644 --- a/openssh/ssh_config.5 +++ b/openssh/ssh_config.5 @@ -459,6 +459,16 @@ Forward (delegate) credentials to the server. The default is .Dq yes . Note that this option applies to protocol version 2 only. +.It Cm GSSAPITrustDns +Set to +.Dq yes to indicate that the DNS is trusted to securely canonicalize +the name of the host being connected to. If +.Dq no, the hostname entered on the +command line will be passed untouched to the GSSAPI library. +The default is +.Dq yes . +This option only applies to protocol version 2 connections using GSSAPI +key exchange. .It Cm HashKnownHosts Indicates that .Nm ssh @@ -624,7 +634,7 @@ This allows a client to prefer one method (e.g.\& over another method (e.g.\& .Cm password ) The default for this option is: -.Dq hostbased,external-keyx,gssapi-with-mic,gssapi,publickey,keyboard-interactive,password . +.Dq publickey,gssapi-keyex,external-keyx,gssapi-with-mic,gssapi,password,keyboard-interactive . .It Cm Protocol Specifies the protocol versions .Nm ssh diff --git a/openssh/sshconnect2.c b/openssh/sshconnect2.c index 2268506..a0d88eb 100644 --- a/openssh/sshconnect2.c +++ b/openssh/sshconnect2.c @@ -83,9 +83,11 @@ void ssh_kex2(char *host, struct sockaddr *hostaddr) { Kex *kex; + #ifdef GSSAPI - char *orig=NULL, *gss=NULL; + char *orig = NULL, *gss = NULL; int len; + char *gss_host = NULL; #endif xxx_host = host; @@ -96,11 +98,18 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) /* Add the GSSAPI mechanisms currently supported on this client to * the key exchange algorithm proposal */ orig = myproposal[PROPOSAL_KEX_ALGS]; - gss = ssh_gssapi_client_mechanisms((char *)get_canonical_hostname(1)); + if (options.gss_trust_dns) + gss_host = (char *)get_canonical_hostname(1); + else + gss_host = host; + + gss = ssh_gssapi_client_mechanisms(gss_host); if (gss) { - len = strlen(orig)+strlen(gss)+2; - myproposal[PROPOSAL_KEX_ALGS]=xmalloc(len); - snprintf(myproposal[PROPOSAL_KEX_ALGS],len,"%s,%s",gss,orig); + debug("Offering GSSAPI proposal: %s", gss); + len = strlen(orig) + strlen(gss) + 2; + myproposal[PROPOSAL_KEX_ALGS] = xmalloc(len); + snprintf(myproposal[PROPOSAL_KEX_ALGS], len, "%s,%s", gss, + orig); } } #endif @@ -133,13 +142,14 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) options.hostkeyalgorithms; #ifdef GSSAPI - /* If we've got GSSAPI algorithms, then we also support the - * 'null' hostkey, as a last resort */ + /* If we've got GSSAPI algorithms, then we also support the + * 'null' hostkey, as a last resort */ if (options.gss_keyex && gss) { - orig=myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; - len = strlen(orig)+sizeof(",null"); - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]=xmalloc(len); - snprintf(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],len,"%s,null",orig); + orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; + len = strlen(orig) + sizeof(",null"); + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xmalloc(len); + snprintf(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], len, + "%s,null", orig); } #endif @@ -153,6 +163,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; #ifdef GSSAPI kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; #endif kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; @@ -161,6 +172,12 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) kex->options.gss_deleg_creds=options.gss_deleg_creds; #endif +#ifdef GSSAPI + kex->gss_deleg_creds = options.gss_deleg_creds; + kex->gss_trust_dns = options.gss_trust_dns; + kex->gss_host = gss_host; +#endif + xxx_kex = kex; dispatch_run(DISPATCH_BLOCK, &kex->done, kex); @@ -236,15 +253,6 @@ int userauth_kbdint(Authctxt *); int userauth_hostbased(Authctxt *); int userauth_kerberos(Authctxt *); -#ifdef GSSAPI -int userauth_gssapi(Authctxt *authctxt); -void input_gssapi_response(int type, u_int32_t, void *); -void input_gssapi_token(int type, u_int32_t, void *); -void input_gssapi_hash(int type, u_int32_t, void *); -void input_gssapi_error(int, u_int32_t, void *); -void input_gssapi_errtok(int, u_int32_t, void *); -#endif - #ifdef GSSAPI int userauth_external(Authctxt *authctxt); int userauth_gssapi(Authctxt *authctxt); @@ -255,6 +263,7 @@ void input_gssapi_token(int type, u_int32_t, void *); void input_gssapi_hash(int type, u_int32_t, void *); void input_gssapi_error(int, u_int32_t, void *); void input_gssapi_errtok(int, u_int32_t, void *); +int userauth_gsskeyex(Authctxt *authctxt); #endif void userauth(Authctxt *, char *); @@ -270,6 +279,10 @@ static char *authmethods_get(void); Authmethod authmethods[] = { #ifdef GSSAPI + {"gssapi-keyex", + userauth_gsskeyex, + &options.gss_authentication, + NULL}, {"external-keyx", userauth_external, &options.gss_authentication, @@ -540,12 +553,18 @@ userauth_gssapi(Authctxt *authctxt) static u_int mech = 0; OM_uint32 min; int ok = 0; + char *gss_host = NULL; if (!options.gss_authentication) { verbose("GSSAPI authentication disabled."); return 0; } + if (options.gss_trust_dns) + gss_host = (char *)get_canonical_hostname(1); + else + gss_host = (char *)authctxt->host; + /* Try one GSSAPI method at a time, rather than sending them all at * once. */ @@ -562,9 +581,9 @@ userauth_gssapi(Authctxt *authctxt) /* My DER encoding requires length<128 */ if (gss_supported->elements[mech].length < 128 && ssh_gssapi_check_mechanism(&gss_supported->elements[mech], - get_canonical_hostname(1)) && + gss_host) && !GSS_ERROR(ssh_gssapi_import_name(gssctxt, - get_canonical_hostname(1)))) { + gss_host))) { ok = 1; /* Mechanism works */ } else { mech++; @@ -661,8 +680,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; - int oidlen; - char *oidv; + unsigned int oidlen; + unsigned char *oidv; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); @@ -770,6 +789,15 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) xfree(lang); } +#ifdef GSI +extern +const gss_OID_desc * const gss_mech_globus_gssapi_openssl; +#define is_gsi_oid(oid) \ + (oid->length == gss_mech_globus_gssapi_openssl->length && \ + (memcmp(oid->elements, gss_mech_globus_gssapi_openssl->elements, \ + oid->length) == 0)) +#endif + int userauth_external(Authctxt *authctxt) { @@ -781,7 +809,7 @@ userauth_external(Authctxt *authctxt) /* The client MUST NOT try this method if initial key exchange was not performed using a GSSAPI-based key exchange method. */ - if (xxx_kex->kex_type != KEX_GSS_GRP1_SHA1) { + if (gss_kex_context == NULL) { debug2("gsskex not performed, skipping external-keyx"); return 0; } @@ -789,11 +817,11 @@ userauth_external(Authctxt *authctxt) debug2("userauth_external"); packet_start(SSH2_MSG_USERAUTH_REQUEST); #ifdef GSI - if(options.implicit) { - packet_put_cstring(""); + if (options.implicit && is_gsi_oid(gss_kex_context->oid)) { + packet_put_cstring(""); } else { #endif - packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->server_user); #ifdef GSI } #endif @@ -803,6 +831,63 @@ userauth_external(Authctxt *authctxt) packet_write_wait(); return 1; } +int +userauth_gsskeyex(Authctxt *authctxt) +{ + Buffer b; + gss_buffer_desc gssbuf; + gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; + OM_uint32 ms; + + static int attempt = 0; + if (attempt++ >= 1) + return (0); + + if (gss_kex_context == NULL) { + debug("No valid Key exchange context"); + return (0); + } + +#ifdef GSI + if (options.implicit && is_gsi_oid(gss_kex_context->oid)) { + ssh_gssapi_buildmic(&b, "", authctxt->service, "gssapi-keyex"); + } else { +#endif + ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, + "gssapi-keyex"); +#ifdef GSI + } +#endif + + gssbuf.value = buffer_ptr(&b); + gssbuf.length = buffer_len(&b); + + if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { + buffer_free(&b); + return (0); + } + + packet_start(SSH2_MSG_USERAUTH_REQUEST); +#ifdef GSI + if (options.implicit && is_gsi_oid(gss_kex_context->oid)) { + packet_put_cstring(""); + } else { +#endif + packet_put_cstring(authctxt->server_user); +#ifdef GSI + } +#endif + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + packet_put_string(mic.value, mic.length); + packet_send(); + + buffer_free(&b); + gss_release_buffer(&ms, &mic); + + return (1); +} + #endif /* GSSAPI */ int diff --git a/openssh/sshd.c b/openssh/sshd.c index 9003c31..5fd4b20 100644 --- a/openssh/sshd.c +++ b/openssh/sshd.c @@ -86,15 +86,10 @@ RCSID("$OpenBSD: sshd.c,v 1.312 2005/07/25 11:59:40 markus Exp $"); #include "monitor_wrap.h" #include "monitor_fdpass.h" -#ifdef GSSAPI -#include "ssh-gss.h" +#ifdef USE_SECURITY_SESSION_API +#include #endif -#ifdef GSSAPI -#include -#include "bufaux.h" -#endif /* GSSAPI */ - #ifdef LIBWRAP #include #include @@ -1675,6 +1670,62 @@ main(int ac, char **av) /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); +#ifdef USE_SECURITY_SESSION_API + /* + * Create a new security session for use by the new user login if + * the current session is the root session or we are not launched + * by inetd (eg: debugging mode or server mode). We do not + * necessarily need to create a session if we are launched from + * inetd because Panther xinetd will create a session for us. + * + * The only case where this logic will fail is if there is an + * inetd running in a non-root session which is not creating + * new sessions for us. Then all the users will end up in the + * same session (bad). + * + * When the client exits, the session will be destroyed for us + * automatically. + * + * We must create the session before any credentials are stored + * (including AFS pags, which happens a few lines below). + */ + { + OSStatus err = 0; + SecuritySessionId sid = 0; + SessionAttributeBits sattrs = 0; + + err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); + if (err) + error("SessionGetInfo() failed with error %.8X", + (unsigned) err); + else + debug("Current Session ID is %.8X / Session Attributes a +re %.8X", + (unsigned) sid, (unsigned) sattrs); + + if (inetd_flag && !(sattrs & sessionIsRoot)) + debug("Running in inetd mode in a non-root session... " + "assuming inetd created the session for us."); + else { + debug("Creating new security session..."); + err = SessionCreate(0, sessionHasTTY | sessionIsRemote); + if (err) + error("SessionCreate() failed with error %.8X", + (unsigned) err); + + err = SessionGetInfo(callerSecuritySession, &sid, + &sattrs); + if (err) + error("SessionGetInfo() failed with error %.8X", + (unsigned) err); + else + debug("New Session ID is %.8X / Session Attribut +es are %.8X", + (unsigned) sid, (unsigned) sattrs); + } + } +#endif + /* * We don\'t want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is @@ -2024,56 +2075,64 @@ do_ssh2_kex(void) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); + /* start key exchange */ + #ifdef GSSAPI - { + { char *orig; char *gss = NULL; char *newstr = NULL; - orig = myproposal[PROPOSAL_KEX_ALGS]; - - /* If we don't have a host key, then all of the algorithms - * currently in myproposal are useless */ - if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])==0) - orig= NULL; - - if (options.gss_keyex) - gss = ssh_gssapi_server_mechanisms(); - else - gss = NULL; - + orig = myproposal[PROPOSAL_KEX_ALGS]; + + /* + * If we don't have a host key, then there's no point advertising + * the other key exchange algorithms + */ + + if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) + orig = NULL; + + if (options.gss_keyex) + gss = ssh_gssapi_server_mechanisms(); + else + gss = NULL; + if (gss && orig) { - int len = strlen(orig) + strlen(gss) +2; - newstr=xmalloc(len); - snprintf(newstr,len,"%s,%s",gss,orig); + int len = strlen(orig) + strlen(gss) + 2; + newstr = xmalloc(len); + snprintf(newstr, len, "%s,%s", gss, orig); } else if (gss) { - newstr=gss; + newstr = gss; } else if (orig) { - newstr=orig; - } - /* If we've got GSSAPI mechanisms, then we've also got the 'null' - host key algorithm, but we're not allowed to advertise it, unless - its the only host key algorithm we're supporting */ - if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) { - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]="null"; + newstr = orig; } + /* + * If we've got GSSAPI mechanisms, then we've got the 'null' host + * key alg, but we can't tell people about it unless its the only + * host key algorithm we support + */ + if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; + if (newstr) - myproposal[PROPOSAL_KEX_ALGS]=newstr; + myproposal[PROPOSAL_KEX_ALGS] = newstr; else fatal("No supported key exchange algorithms"); - } + } #endif - /* start key exchange */ - kex = kex_setup(myproposal); - kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; + /* start key exchange */ + kex = kex_setup(myproposal); + kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; #ifdef GSSAPI kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; #endif - kex->server = 1; - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; kex->load_host_key=&get_hostkey_by_type; kex->host_key_index=&get_hostkey_index; diff --git a/openssh/sshd_config.5 b/openssh/sshd_config.5 index da0da2d..a9c59f4 100644 --- a/openssh/sshd_config.5 +++ b/openssh/sshd_config.5 @@ -277,21 +277,15 @@ Specifies whether user authentication based on GSSAPI is allowed. The default is .Dq yes . Note that this option applies to protocol version 2 only. -.It Cm GSSAPICleanupCredentials -Specifies whether to automatically destroy the user's credentials cache -on logout. -The default is -.Dq yes . -Note that this option applies to protocol version 2 only. .It Cm GSSAPIKeyExchange -Specifies whether key exchange based on GSSAPI may be used. When using -GSSAPI key exchange the server need not have a host key. +Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange +doesn't rely on ssh keys to verify host identity. The default is .Dq yes . Note that this option applies to protocol version 2 only. -.It Cm GSSAPIUseSessionCredCache -Specifies whether a unique credentials cache name should be generated per -session for storing delegated credentials. +.It Cm GSSAPICleanupCredentials +Specifies whether to automatically destroy the user's credentials cache +on logout. The default is .Dq yes . Note that this option applies to protocol version 2 only. -- 2.45.2