From 2a3e00306c9b3b4db71a777a7c3ccb70e470c675 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 12 May 2006 09:46:51 +0000 Subject: * Update to current GSSAPI patch from http://www.sxw.org.uk/computing/patches/openssh-4.3p2-gsskex-20060223.patch (closes: #352042). --- auth-krb5.c | 17 +++++- auth.h | 1 + auth2-gss.c | 4 +- auth2.c | 5 +- config.h.in | 9 ++++ configure | 117 ++++++++++++++++++++++++++++++++++++++++ configure.ac | 24 +++++++++ debian/changelog | 3 ++ gss-genr.c | 47 ++++++++++------ gss-serv.c | 15 ++---- kex.c | 8 ++- kex.h | 5 +- kexgssc.c | 159 ++++++++++++++++++++++++++++++++++++++----------------- kexgsss.c | 110 ++++++++++++++++++++++++++++---------- monitor.c | 8 ++- monitor_wrap.c | 4 +- readconf.c | 10 ++++ readconf.h | 1 + servconf.c | 12 ++++- servconf.h | 1 + ssh-gss.h | 13 +++-- ssh_config.5 | 10 ++++ sshconnect2.c | 31 ++++++++--- sshd.c | 65 ++++++++++++++++++++++- sshd_config.5 | 6 +++ 25 files changed, 554 insertions(+), 131 deletions(-) diff --git a/auth-krb5.c b/auth-krb5.c index 64d613543..bc37675a2 100644 --- a/auth-krb5.c +++ b/auth-krb5.c @@ -156,8 +156,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) @@ -209,15 +214,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()); + cctemplate, geteuid()); if (ret < 0 || (size_t)ret >= sizeof(ccname)) return ENOMEM; +#ifndef USE_CCAPI old_umask = umask(0177); tmpfd = mkstemp(ccname + strlen("FILE:")); umask(old_umask); @@ -232,6 +244,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/auth.h b/auth.h index f3a31c446..267e7b022 100644 --- a/auth.h +++ b/auth.h @@ -53,6 +53,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/auth2-gss.c b/auth2-gss.c index a6a9c05cd..539654ee0 100644 --- a/auth2-gss.c +++ b/auth2-gss.c @@ -129,11 +129,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); } @@ -318,7 +320,7 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) } Authmethod method_gsskeyex = { - "gssapi-keyx", + "gssapi-keyex", userauth_gsskeyex, &options.gss_authentication }; diff --git a/auth2.c b/auth2.c index f12440815..a67449db5 100644 --- a/auth2.c +++ b/auth2.c @@ -196,6 +196,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); @@ -266,7 +267,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/config.h.in b/config.h.in index 05e17adc8..e841f33c9 100644 --- a/config.h.in +++ b/config.h.in @@ -1216,6 +1216,9 @@ /* Use btmp to log bad logins */ #undef USE_BTMP +/* platform uses an in-memory credentials cache */ +#undef USE_CCAPI + /* Use libedit for sftp */ #undef USE_LIBEDIT @@ -1231,6 +1234,9 @@ /* Define if you want smartcard support using sectok */ #undef USE_SECTOK +/* platform has the Security Authorization Session API */ +#undef USE_SECURITY_SESSION_API + /* Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp */ #undef WITH_ABBREV_NO_TTY @@ -1250,6 +1256,9 @@ /* Define if you want IRIX project management */ #undef WITH_IRIX_PROJECT +/* Define if you want SELinux support. */ +#undef WITH_SELINUX + /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #undef WORDS_BIGENDIAN diff --git a/configure b/configure index 552acba68..271904359 100755 --- a/configure +++ b/configure @@ -5257,6 +5257,123 @@ cat >>confdefs.h <<_ACEOF #define BIND_8_COMPAT 1 _ACEOF + echo "$as_me:$LINENO: checking if we have the Security Authorization Session API" >&5 +echo $ECHO_N "checking if we have the Security Authorization Session API... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +SessionCreate(0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_use_security_session_api="yes" + +cat >>confdefs.h <<\_ACEOF +#define USE_SECURITY_SESSION_API 1 +_ACEOF + + LIBS="$LIBS -framework Security" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_use_security_session_api="no" + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + echo "$as_me:$LINENO: checking if we have an in-memory credentials cache" >&5 +echo $ECHO_N "checking if we have an in-memory credentials cache... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +cc_context_t c; + (void) cc_initialize (&c, 0, NULL, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +cat >>confdefs.h <<\_ACEOF +#define USE_CCAPI 1 +_ACEOF + + LIBS="$LIBS -framework Security" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + if test "x$ac_cv_use_security_session_api" = "xno"; then + { { echo "$as_me:$LINENO: error: *** Need a security framework to use the credentials cache API ***" >&5 +echo "$as_me: error: *** Need a security framework to use the credentials cache API ***" >&2;} + { (exit 1); exit 1; }; } + fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ;; *-*-hpux*) # first we define all of the options common to all HP-UX releases diff --git a/configure.ac b/configure.ac index 9ff199451..7b723799e 100644 --- a/configure.ac +++ b/configure.ac @@ -231,6 +231,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) AC_DEFINE(BROKEN_SETREGID) AC_DEFINE_UNQUOTED(BIND_8_COMPAT, 1, [Define if your resolver libs need this for getrrsetbyname]) + 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, 1, + [platform has the Security Authorization 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, 1, + [platform uses an in-memory credentials cache]) + 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/debian/changelog b/debian/changelog index 98e6ed73a..2c4dc3b01 100644 --- a/debian/changelog +++ b/debian/changelog @@ -40,6 +40,9 @@ openssh (1:4.3p2-1) UNRELEASED; urgency=low - Many manual page improvements. - Lots of cleanups, including fixes to memory leaks on error paths and possible crashes. + * Update to current GSSAPI patch from + http://www.sxw.org.uk/computing/patches/openssh-4.3p2-gsskex-20060223.patch + (closes: #352042). * Rename KeepAlive to TCPKeepAlive in default sshd_config (closes: #349896). * debconf template translations: diff --git a/gss-genr.c b/gss-genr.c index 2a905f5e9..dfaa708ea 100644 --- a/gss-genr.c +++ b/gss-genr.c @@ -1,7 +1,7 @@ /* $OpenBSD: gss-genr.c,v 1.6 2005/10/13 22:24:31 stevesk 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 @@ -53,6 +53,11 @@ 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 * @@ -62,7 +67,7 @@ static ssh_gss_kex_mapping *gss_enc2oid = NULL; char * -ssh_gssapi_client_mechanisms(char *host) { +ssh_gssapi_client_mechanisms(const char *host) { gss_OID_set gss_supported; OM_uint32 min_status; @@ -83,8 +88,6 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, const EVP_MD *evp_md = EVP_md5(); EVP_MD_CTX md; - evp_md = EVP_md5(); - if (gss_enc2oid != NULL) { for (i=0;gss_enc2oid[i].encoded!=NULL;i++) xfree(gss_enc2oid[i].encoded); @@ -97,12 +100,13 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, buffer_init(&buf); oidpos = 0; - for (i=0;icount;i++) { - if (gss_supported->elements[i].length<128 && + 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, @@ -115,10 +119,14 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, encoded, EVP_MD_size(evp_md)*2); if (oidpos != 0) - buffer_put_char(&buf,','); + buffer_put_char(&buf, ','); - buffer_append(&buf, KEX_GSS_SHA1, - sizeof(KEX_GSS_SHA1)-1); + 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]); @@ -129,7 +137,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, gss_enc2oid[oidpos].oid = NULL; gss_enc2oid[oidpos].encoded = NULL; - buffer_put_char(&buf,'\0'); + buffer_put_char(&buf, '\0'); mechs = xmalloc(buffer_len(&buf)); buffer_get(&buf, mechs, buffer_len(&buf)); @@ -144,21 +152,28 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, } gss_OID -ssh_gssapi_id_kex(Gssctxt *ctx, char *name) { +ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int *gex) { int i = 0; - if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) != 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; - - name+=sizeof(KEX_GSS_SHA1)-1; /* Skip ID string */ + } while (gss_enc2oid[i].encoded != NULL && - strcmp(name,gss_enc2oid[i].encoded)!=0) { + 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); + ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); return gss_enc2oid[i].oid; } diff --git a/gss-serv.c b/gss-serv.c index 9682fc3c3..190f56fc0 100644 --- a/gss-serv.c +++ b/gss-serv.c @@ -36,6 +36,7 @@ #include "servconf.h" #include "xmalloc.h" #include "getput.h" +#include "monitor_wrap.h" #include "ssh-gss.h" @@ -70,9 +71,9 @@ ssh_gssapi_server_mechanisms() { /* Unprivileged */ int ssh_gssapi_server_check_mech(gss_OID oid, void *data) { - Gssctxt * ctx = NULL; + Gssctxt * ctx = NULL; int res; - + res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ssh_gssapi_delete_ctx(&ctx); @@ -315,14 +316,4 @@ ssh_gssapi_userok(char *user) return (0); } -/* Privileged */ -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/kex.c b/kex.c index e0b9d5872..47983f8d9 100644 --- a/kex.c +++ b/kex.c @@ -306,8 +306,12 @@ choose_kex(Kex *k, char *client, char *server) k->kex_type = KEX_DH_GEX_SHA1; k->evp_md = EVP_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; + k->evp_md = EVP_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; k->evp_md = EVP_sha1(); #endif diff --git a/kex.h b/kex.h index 370e3e873..1c4d1a718 100644 --- a/kex.h +++ b/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 }; @@ -117,7 +118,9 @@ struct Kex { int flags; const EVP_MD *evp_md; #ifdef GSSAPI - int gss_deleg_creds; + int gss_deleg_creds; + int gss_trust_dns; + char *gss_host; #endif char *client_version_string; char *server_version_string; diff --git a/kexgssc.c b/kexgssc.c index eee96dd23..9830ad384 100644 --- a/kexgssc.c +++ b/kexgssc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2004 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 @@ -42,34 +42,65 @@ void kexgss_client(Kex *kex) { - gss_buffer_desc gssbuf, send_tok, recv_tok, msg_tok, *token_ptr; + 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; + u_int klen, kout, slen = 0, hashlen, strlen; DH *dh; - BIGNUM *dh_server_pub = 0; - BIGNUM *shared_secret = 0; - unsigned char *kbuf; - unsigned char *hash; - unsigned char *serverhostkey; + BIGNUM *dh_server_pub = NULL; + BIGNUM *shared_secret = NULL; + BIGNUM *p = NULL; + BIGNUM *g = NULL; + u_char *kbuf, *hash; + u_char *serverhostkey = NULL; char *msg; char *lang; int type = 0; int first = 1; - int slen = 0; - u_int strlen; - + int gex = 0; + int nbits, min, max; + + /* Initialise our GSSAPI world */ ssh_gssapi_build_ctx(&ctxt); - if (ssh_gssapi_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 */ @@ -97,7 +128,7 @@ kexgss_client(Kex *kex) { /* 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 */ @@ -126,15 +157,21 @@ kexgss_client(Kex *kex) { send_tok.length); } packet_send(); + gss_release_buffer(&min_status, &send_tok); /* If we've sent them data, they should reply */ - - type = packet_read(); + 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); + 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) @@ -144,8 +181,8 @@ kexgss_client(Kex *kex) { break; case SSH2_MSG_KEXGSS_COMPLETE: debug("Received GSSAPI_COMPLETE"); - packet_get_bignum2(dh_server_pub); - msg_tok.value = packet_get_string(&strlen); + packet_get_bignum2(dh_server_pub); + msg_tok.value = packet_get_string(&strlen); msg_tok.length = strlen; /* Is there a token included? */ @@ -156,10 +193,10 @@ kexgss_client(Kex *kex) { /* 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: @@ -168,7 +205,7 @@ kexgss_client(Kex *kex) { min_status = packet_get_int(); msg = packet_get_string(NULL); lang = packet_get_string(NULL); - fprintf(stderr,"GSSAPI Error: \n%s",msg); + fatal("GSSAPI Error: \n%s",msg); default: packet_disconnect("Protocol error: didn't expect packet type %d", type); @@ -181,12 +218,12 @@ kexgss_client(Kex *kex) { } } 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 + * server, which will have set dh_server_pub and msg_tok */ - if (type!=SSH2_MSG_KEXGSS_COMPLETE) + 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] */ @@ -203,28 +240,52 @@ kexgss_client(Kex *kex) { 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 */ - ); - + if (gex) { + kexgex_hash( + kex->evp_md, + 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, + &hash, &hashlen + ); + } else { + /* The GSS hash is identical to the DH one */ + 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 */ + &hash, &hashlen + ); + } + gssbuf.value = hash; - gssbuf.length = 20; + gssbuf.length = hashlen; /* 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_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } @@ -234,7 +295,7 @@ kexgss_client(Kex *kex) { else ssh_gssapi_delete_ctx(&ctxt); - kex_derive_keys(kex, hash, shared_secret); + kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); } diff --git a/kexgsss.c b/kexgsss.c index 80c133ac9..6447dc97b 100644 --- a/kexgsss.c +++ b/kexgsss.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2004 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 @@ -53,21 +53,30 @@ kexgss_server(Kex *kex) */ 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, *hash; + 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; - u_int slen; + int type = 0; + int gex; gss_OID oid; /* Initialise GSSAPI */ + /* 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); + oid = ssh_gssapi_id_kex(NULL, kex->name, &gex); if (oid == NULL) fatal("Unknown gssapi mechanism"); @@ -76,6 +85,34 @@ kexgss_server(Kex *kex) if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) 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(); @@ -86,10 +123,9 @@ kexgss_server(Kex *kex) recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; - dh_client_pub = BN_new(); - - if (dh_client_pub == NULL) + 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 */ @@ -107,8 +143,8 @@ kexgss_server(Kex *kex) maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, &send_tok, &ret_flags)); - gss_release_buffer(&min_status, &recv_tok); - + xfree(recv_tok.value); + if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) fatal("Zero length token output when incomplete"); @@ -125,7 +161,7 @@ kexgss_server(Kex *kex) } while (maj_status & GSS_S_CONTINUE_NEEDED); 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); packet_send(); @@ -139,9 +175,6 @@ kexgss_server(Kex *kex) if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity flag wasn't set"); - dh = dh_new_group1(); - dh_gen_key(dh, kex->we_need * 8); - if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); @@ -154,24 +187,42 @@ kexgss_server(Kex *kex) 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) { + 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 + ); + } + else { + /* The GSSAPI hash is identical to the Diffie Helman one */ + 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 + ); + } BN_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 */ + gssbuf.length = hashlen; if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) fatal("Couldn't get MIC"); @@ -180,7 +231,7 @@ kexgss_server(Kex *kex) 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); } else { @@ -188,7 +239,8 @@ kexgss_server(Kex *kex) } packet_send(); - gss_release_buffer(&min_status, &send_tok); + gss_release_buffer(&min_status, &send_tok); + gss_release_buffer(&min_status, &msg_tok); if (gss_kex_context == NULL) gss_kex_context = ctxt; @@ -197,7 +249,7 @@ kexgss_server(Kex *kex) 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); } diff --git a/monitor.c b/monitor.c index e9693ef63..fbb15312e 100644 --- a/monitor.c +++ b/monitor.c @@ -1644,6 +1644,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); @@ -1942,10 +1943,13 @@ mm_answer_gss_userok(int sock, Buffer *m) int mm_answer_gss_sign(int socket, Buffer *m) { - gss_buffer_desc data, hash; + 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, &data.length); + data.value = buffer_get_string(m, &len); + data.length = len; if (data.length != 20) fatal("%s: data length incorrect: %d", __func__, data.length); diff --git a/monitor_wrap.c b/monitor_wrap.c index 23b0cbd59..6749d3f93 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1222,6 +1222,7 @@ 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); @@ -1230,7 +1231,8 @@ mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); major = buffer_get_int(&m); - hash->value = buffer_get_string(&m, &hash->length); + hash->value = buffer_get_string(&m, &len); + hash->length = len; buffer_free(&m); diff --git a/readconf.c b/readconf.c index 7933c5289..b3e14b9d2 100644 --- a/readconf.c +++ b/readconf.c @@ -109,6 +109,7 @@ typedef enum { oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, + oGssTrustDns, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, @@ -146,9 +147,11 @@ static struct { #if defined(GSSAPI) { "gssapiauthentication", oGssAuthentication }, { "gssapidelegatecredentials", oGssDelegateCreds }, + { "gssapitrustdns", oGssTrustDns }, #else { "gssapiauthentication", oUnsupported }, { "gssapidelegatecredentials", oUnsupported }, + { "gssapitrustdns", oUnsupported }, #endif { "fallbacktorsh", oDeprecated }, { "usersh", oDeprecated }, @@ -423,6 +426,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; @@ -998,6 +1005,7 @@ initialize_options(Options * options) options->challenge_response_authentication = -1; options->gss_authentication = -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; @@ -1087,6 +1095,8 @@ fill_default_options(Options * options) options->gss_authentication = 0; if (options->gss_deleg_creds == -1) options->gss_deleg_creds = 0; + if (options->gss_trust_dns == -1) + options->gss_trust_dns = 0; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) diff --git a/readconf.h b/readconf.h index 630895ee4..4639a74a4 100644 --- a/readconf.h +++ b/readconf.h @@ -46,6 +46,7 @@ typedef struct { /* Try S/Key or TIS, authentication. */ int gss_authentication; /* Try GSS authentication */ 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/servconf.c b/servconf.c index 81953bb80..219a0300f 100644 --- a/servconf.c +++ b/servconf.c @@ -72,6 +72,7 @@ initialize_server_options(ServerOptions *options) options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; options->gss_authentication=-1; + options->gss_keyex = -1; options->gss_cleanup_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; @@ -187,6 +188,8 @@ fill_default_server_options(ServerOptions *options) options->kerberos_get_afs_token = 0; if (options->gss_authentication == -1) options->gss_authentication = 0; + if (options->gss_keyex == -1) + options->gss_keyex = 0; if (options->gss_cleanup_creds == -1) options->gss_cleanup_creds = 1; if (options->password_authentication == -1) @@ -273,7 +276,8 @@ typedef enum { sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, - sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, + sGssAuthentication, sGssKeyEx, sGssCleanupCreds, + sAcceptEnv, sPermitTunnel, sUsePrivilegeSeparation, sDeprecated, sUnsupported } ServerOpCodes; @@ -327,9 +331,11 @@ static struct { { "afstokenpassing", sUnsupported }, #ifdef GSSAPI { "gssapiauthentication", sGssAuthentication }, + { "gssapikeyexchange", sGssKeyEx }, { "gssapicleanupcredentials", sGssCleanupCreds }, #else { "gssapiauthentication", sUnsupported }, + { "gssapikeyexchange", sUnsupported }, { "gssapicleanupcredentials", sUnsupported }, #endif { "passwordauthentication", sPasswordAuthentication }, @@ -673,6 +679,10 @@ parse_flag: intptr = &options->gss_authentication; goto parse_flag; + case sGssKeyEx: + intptr = &options->gss_keyex; + goto parse_flag; + case sGssCleanupCreds: intptr = &options->gss_cleanup_creds; goto parse_flag; diff --git a/servconf.h b/servconf.h index ab82c8f57..0ef05bcd9 100644 --- a/servconf.h +++ b/servconf.h @@ -88,6 +88,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_cleanup_creds; /* If true, destroy cred cache on logout */ int password_authentication; /* If true, permit password * authentication. */ diff --git a/ssh-gss.h b/ssh-gss.h index e5699baf3..213930103 100644 --- a/ssh-gss.h +++ b/ssh-gss.h @@ -67,7 +67,10 @@ #define SSH2_MSG_KEXGSS_COMPLETE 32 #define SSH2_MSG_KEXGSS_HOSTKEY 33 #define SSH2_MSG_KEXGSS_ERROR 34 -#define KEX_GSS_SHA1 "gss-group1-sha1-" +#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; @@ -130,19 +133,19 @@ OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); typedef int ssh_gssapi_check_fn(gss_OID, void *); -char *ssh_gssapi_client_mechanisms(char *host); +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 *); +gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int *); -char *ssh_gssapi_server_mechanisms(void); 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(); #endif /* GSSAPI */ #endif /* _SSH_GSS_H */ diff --git a/ssh_config.5 b/ssh_config.5 index 889def626..979f9282f 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -479,6 +479,16 @@ Forward (delegate) credentials to the server. The default is .Dq no . 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 no . +This option only applies to protocol version 2 connections using GSSAPI +key exchange. .It Cm HashKnownHosts Indicates that .Nm ssh diff --git a/sshconnect2.c b/sshconnect2.c index 7ee71763a..3fb5df233 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -87,6 +87,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) #ifdef GSSAPI char *orig, *gss = NULL; int len; + char *gss_host; #endif xxx_host = host; @@ -94,10 +95,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) #ifdef GSSAPI if (options.gss_authentication) { + /* 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(get_canonical_hostname(1)); - debug("Offering GSSAPI proposal: %s",gss); + 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) { + 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", @@ -134,6 +142,8 @@ 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 (gss) { orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; len = strlen(orig) + sizeof(",null"); @@ -152,8 +162,10 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; #ifdef GSSAPI - if (options.gss_authentication) + if (options.gss_authentication) { 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 +173,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) #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; @@ -245,7 +259,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_gsskeyx(Authctxt *authctxt); +int userauth_gsskeyex(Authctxt *authctxt); #endif void userauth(Authctxt *, char *); @@ -261,8 +275,8 @@ static char *authmethods_get(void); Authmethod authmethods[] = { #ifdef GSSAPI - {"gssapi-keyx", - userauth_gsskeyx, + {"gssapi-keyex", + userauth_gsskeyex, &options.gss_authentication, NULL}, {"gssapi-with-mic", @@ -775,10 +789,11 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) } int -userauth_gsskeyx(Authctxt *authctxt) +userauth_gsskeyex(Authctxt *authctxt) { Buffer b; - gss_buffer_desc gssbuf, mic; + gss_buffer_desc gssbuf; + gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; OM_uint32 ms; static int attempt = 0; diff --git a/sshd.c b/sshd.c index df6d1e374..85b679d5e 100644 --- a/sshd.c +++ b/sshd.c @@ -86,6 +86,10 @@ RCSID("$OpenBSD: sshd.c,v 1.318 2005/12/24 02:27:41 djm Exp $"); #include "monitor_wrap.h" #include "monitor_fdpass.h" +#ifdef USE_SECURITY_SESSION_API +#include +#endif + #ifdef LIBWRAP #include #include @@ -1129,6 +1133,7 @@ main(int ac, char **av) options.protocol &= ~SSH_PROTO_1; } #ifndef GSSAPI + /* The GSSAPI key exchange can run without a host key */ if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { logit("Disabling protocol version 2. Could not load host key"); options.protocol &= ~SSH_PROTO_2; @@ -1681,6 +1686,60 @@ 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 are %.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 Attributes 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 @@ -2051,7 +2110,10 @@ do_ssh2_kex(void) if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) orig = NULL; - gss = ssh_gssapi_server_mechanisms(); + if (options.gss_keyex) + gss = ssh_gssapi_server_mechanisms(); + else + gss = NULL; if (gss && orig) { int len = strlen(orig) + strlen(gss) + 2; @@ -2084,6 +2146,7 @@ do_ssh2_kex(void) 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; diff --git a/sshd_config.5 b/sshd_config.5 index 71a293ffb..841cb29d3 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -277,6 +277,12 @@ Specifies whether user authentication based on GSSAPI is allowed. The default is .Dq no . Note that this option applies to protocol version 2 only. +.It Cm GSSAPIKeyExchange +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 no . +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. -- cgit v1.2.3