|
diff -u -udbNpr Makefile.in Makefile.in --- Makefile.in 2004-02-17 19:35:11.000000000 -0800 +++ Makefile.in 2004-10-26 14:45:50.000000000 -0700 @@ -70,7 +70,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o b atomicio.o key.o dispatch.o kex.o mac.o uuencode.o misc.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 \ - 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,6 +85,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw kexdhs.o kexgexs.o \ auth-krb5.o \ auth2-gss.o gss-serv.o gss-serv-krb5.o \ + kexgsss.o \ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out diff -u -udbNpr auth-krb5.c auth-krb5.c --- auth-krb5.c 2004-04-16 05:47:55.000000000 -0700 +++ auth-krb5.c 2004-10-26 14:45:50.000000000 -0700 @@ -42,6 +42,10 @@ RCSID("$OpenBSD: auth-krb5.c,v 1.15 2003 #ifdef KRB5 #include <krb5.h> +#ifndef HEIMDAL +#include <com_err.h> +#endif + extern ServerOptions options; static int @@ -68,7 +72,6 @@ auth_krb5_password(Authctxt *authctxt, c krb5_creds creds; krb5_principal server; char ccname[40]; - int tmpfd; #endif krb5_error_code problem; krb5_ccache ccache = NULL; @@ -145,8 +148,14 @@ auth_krb5_password(Authctxt *authctxt, c goto out; } +#ifdef USE_CCAPI + snprintf(ccname,sizeof(ccname),"API:krb5cc_%d",geteuid()); +#else snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); + { + int tmpfd; + if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { logit("mkstemp(): %.100s", strerror(errno)); problem = errno; @@ -160,6 +169,8 @@ auth_krb5_password(Authctxt *authctxt, c goto out; } close(tmpfd); + } +#endif problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache); if (problem) @@ -180,8 +191,11 @@ auth_krb5_password(Authctxt *authctxt, c len = strlen(authctxt->krb5_ticket_file) + 6; authctxt->krb5_ccname = xmalloc(len); - snprintf(authctxt->krb5_ccname, len, "FILE:%s", - authctxt->krb5_ticket_file); +#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 out: restore_uid(); diff -u -udbNpr auth.c auth.c --- auth.c 2004-10-26 14:40:11.000000000 -0700 +++ auth.c 2004-10-26 14:45:50.000000000 -0700 @@ -256,7 +256,7 @@ auth_log(Authctxt *authctxt, int authent authmsg, method, authctxt->valid ? "" : "illegal user ", - authctxt->user, + (authctxt->user[0]) ? authctxt->user : "<implicit>", get_remote_ipaddr(), get_remote_port(), info); diff -u -udbNpr auth.h auth.h --- auth.h 2004-04-16 05:47:55.000000000 -0700 +++ auth.h 2004-10-26 14:45:50.000000000 -0700 @@ -52,6 +52,8 @@ struct Authctxt { int valid; /* user exists and is allowed to login */ int attempt; int failures; + int server_caused_failure; + Authmethod *method; int force_pwchange; char *user; /* username sent by the client */ char *service; diff -u -udbNpr auth2-gss.c auth2-gss.c --- auth2-gss.c 2003-11-21 04:56:47.000000000 -0800 +++ auth2-gss.c 2004-10-26 14:45:50.000000000 -0700 @@ -63,6 +63,7 @@ userauth_gssapi(Authctxt *authctxt) u_int len; char *doid = NULL; + if (!authctxt->valid || authctxt->user == NULL) return (0); @@ -98,11 +99,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, &oid)))) { xfree(doid); + authctxt->server_caused_failure = 1; return (0); } @@ -132,6 +135,8 @@ input_gssapi_token(int type, u_int32_t p gss_buffer_desc recv_tok; OM_uint32 maj_status, min_status, flags; u_int len; + int old_gssapi_mechanism = !strcmp(authctxt->method->name, "gssapi"); + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); @@ -154,8 +159,9 @@ input_gssapi_token(int type, u_int32_t p packet_send(); } authctxt->postponed = 0; + authctxt->server_caused_failure = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - userauth_finish(authctxt, 0, "gssapi-with-mic"); + userauth_finish(authctxt, 0, authctxt->method->name); } else { if (send_tok.length != 0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); @@ -164,7 +170,7 @@ input_gssapi_token(int type, u_int32_t p } if (maj_status == GSS_S_COMPLETE) { dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - if (flags & GSS_C_INTEG_FLAG) + if (!old_gssapi_mechanism &&flags & GSS_C_INTEG_FLAG) dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, &input_gssapi_mic); else @@ -239,11 +245,12 @@ input_gssapi_exchange_complete(int type, authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); authctxt->postponed = 0; + authctxt->server_caused_failure = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); - userauth_finish(authctxt, authenticated, "gssapi-with-mic"); + userauth_finish(authctxt, authenticated, authctxt->method->name); } static void @@ -292,4 +299,10 @@ Authmethod method_gssapi = { &options.gss_authentication }; +Authmethod method_gssapi_nomic = { + "gssapi", + userauth_gssapi, + &options.gss_nomic_authentication +}; + #endif /* GSSAPI */ diff -u -udbNpr auth2.c auth2.c --- auth2.c 2004-10-26 14:40:11.000000000 -0700 +++ auth2.c 2004-10-26 14:48:35.000000000 -0700 @@ -54,14 +54,16 @@ extern Authmethod method_kbdint; extern Authmethod method_hostbased; #ifdef GSSAPI extern Authmethod method_gssapi; +extern Authmethod method_gssapi_nomic; #endif Authmethod *authmethods[] = { &method_none, - &method_pubkey, #ifdef GSSAPI &method_gssapi, #endif + &method_pubkey, + &method_gssapi_nomic, &method_passwd, &method_kbdint, &method_hostbased, @@ -190,11 +192,13 @@ input_userauth_request(int type, u_int32 #endif authctxt->postponed = 0; + authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(method); if (m != NULL) { debug2("input_userauth_request: try method %s", method); + authctxt->method = m; authenticated = m->userauth(authctxt); } userauth_finish(authctxt, authenticated, method); @@ -250,12 +254,15 @@ userauth_finish(Authctxt *authctxt, int /* now we can break out */ authctxt->success = 1; } else { + /* Do not count server configuration problems against the client */ + if (!authctxt->server_caused_failure) { if (authctxt->failures++ > AUTH_FAIL_MAX) { #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) PRIVSEP(solaris_audit_maxtrys()); #endif /* BSM */ packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } + } #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) PRIVSEP(solaris_audit_bad_pw("authorization")); #endif /* BSM */ diff -u -udbNpr auth2.c.rej auth2.c.rej --- auth2.c.rej 1969-12-31 16:00:00.000000000 -0800 +++ auth2.c.rej 2004-10-26 14:45:50.000000000 -0700 @@ -0,0 +1,22 @@ +*************** +*** 246,253 **** + /* now we can break out */ + authctxt->success = 1; + } else { +- if (authctxt->failures++ > AUTH_FAIL_MAX) +- packet_disconnect(AUTH_FAIL_MSG, authctxt->user); + methods = authmethods_get(); + packet_start(SSH2_MSG_USERAUTH_FAILURE); + packet_put_cstring(methods); +--- 250,260 ---- + /* now we can break out */ + authctxt->success = 1; + } else { ++ /* Do not count server configuration problems against the client */ ++ if (!authctxt->server_caused_failure) { ++ if (authctxt->failures++ > AUTH_FAIL_MAX) ++ packet_disconnect(AUTH_FAIL_MSG, authctxt->user); ++ } + methods = authmethods_get(); + packet_start(SSH2_MSG_USERAUTH_FAILURE); + packet_put_cstring(methods); diff -u -udbNpr compat.c compat.c --- compat.c 2003-11-03 01:09:03.000000000 -0800 +++ compat.c 2004-10-26 14:45:50.000000000 -0700 @@ -79,7 +79,11 @@ compat_datafellows(const char *version) { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, { "OpenSSH_2.*," "OpenSSH_3.0*," - "OpenSSH_3.1*", SSH_BUG_EXTEOF}, + "OpenSSH_3.1*", SSH_BUG_EXTEOF | SSH_BUG_GSSAPI_BER}, + { "OpenSSH_3.2*," + "OpenSSH_3.3*," + "OpenSSH_3.4*," + "OpenSSH_3.5*", SSH_BUG_GSSAPI_BER}, { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, { "OpenSSH*", 0 }, { "*MindTerm*", 0 }, diff -u -udbNpr compat.h compat.h --- compat.h 2003-11-03 01:09:03.000000000 -0800 +++ compat.h 2004-10-26 14:45:50.000000000 -0700 @@ -55,6 +55,7 @@ #define SSH_BUG_EXTEOF 0x00200000 #define SSH_BUG_PROBE 0x00400000 #define SSH_BUG_FIRSTKEX 0x00800000 +#define SSH_BUG_GSSAPI_BER 0x01000000 void enable_compat13(void); void enable_compat20(void); diff -u -udbNpr config.h.in config.h.in --- config.h.in 2004-10-26 14:44:19.000000000 -0700 +++ config.h.in 2004-10-26 14:45:50.000000000 -0700 @@ -353,6 +353,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 -u -udbNpr configure.ac configure.ac --- configure.ac 2004-10-26 14:40:11.000000000 -0700 +++ configure.ac 2004-10-26 14:45:50.000000000 -0700 @@ -168,6 +168,28 @@ main() { if (NSVersionOfRunTimeLibrary(" 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 <Security/AuthSession.h>], + [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 <Kerberos/Kerberos.h>], + [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)] + ) ;; *-*-hpux10.26) if test -z "$GCC"; then diff -u -udbNpr gss-genr.c gss-genr.c --- gss-genr.c 2003-11-21 04:56:47.000000000 -0800 +++ gss-genr.c 2004-10-26 14:45:50.000000000 -0700 @@ -28,15 +28,179 @@ #ifdef GSSAPI +#include "ssh.h" #include "xmalloc.h" #include "bufaux.h" +#include "buffer.h" +#include "packet.h" #include "compat.h" +#include <openssl/evp.h> +#include "cipher.h" +#include "kex.h" #include "log.h" +#include "compat.h" #include "monitor_wrap.h" + +#include <netdb.h> #include "ssh2.h" #include "ssh-gss.h" +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 - We might want to make this configurable in the future, so as to + * XXX - allow the user control over which mechanisms to use. + */ + +char * +ssh_gssapi_client_mechanisms(const char *host) { + gss_OID_set supported; + OM_uint32 min_status; + Buffer buf; + int i = 0; + char *mechs; + char *encoded; + int enclen; + 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); + if (datafellows & SSH_BUG_GSSAPI_BER) { + gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping) + *((supported->count*2)+1)); + } else { + gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping) + *(supported->count+1)); + } + + buffer_init(&buf); + + + for (i=0;i<supported->count;i++) { + + gss_enc2oid[oidpos].encoded=NULL; + + if (supported->elements[i].length<128 && + ssh_gssapi_check_mechanism(&(supported->elements[i]),host)) { + + /* Earlier versions of this code interpreted the + * spec incorrectly with regard to OID encoding. They + * also mis-encoded the krb5 OID. The following + * _temporary_ code interfaces with these broken + * servers */ + + if (datafellows & SSH_BUG_GSSAPI_BER) { + char *bodge=NULL; + gss_OID_desc krb5oid={9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}; + gss_OID_desc gsioid={9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"}; + + if (supported->elements[i].length==krb5oid.length && + memcmp(supported->elements[i].elements, + krb5oid.elements, krb5oid.length)==0) { + bodge="Se3H81ismmOC3OE+FwYCiQ=="; + } + + if (supported->elements[i].length==gsioid.length && + memcmp(supported->elements[i].elements, + gsioid.elements, gsioid.length)==0) { + bodge="N3+k7/4wGxHyuP8Yxi4RhA=="; + } + + if (bodge) { + if (oidpos!=0) { + buffer_put_char(&buf,','); + } + + buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1); + buffer_append(&buf, bodge, strlen(bodge)); + + gss_enc2oid[oidpos].oid=&(supported->elements[i]); + gss_enc2oid[oidpos].encoded=bodge; + + oidpos++; + } + } + + /* Add the required DER encoding octets and MD5 hash */ + deroid[0]=0x06; /* Object Identifier */ + deroid[1]=supported->elements[i].length; + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md,deroid,2); + EVP_DigestUpdate(&md, + supported->elements[i].elements, + 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); + + gss_enc2oid[oidpos].oid=&(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)); + 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); + } + + 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++; + } + + if (gss_enc2oid[i].oid!=NULL) { + ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid); + } + + return gss_enc2oid[i].oid; +} + extern u_char *session_id2; extern u_int session_id2_len; @@ -269,6 +433,22 @@ ssh_gssapi_buildmic(Buffer *b, const cha buffer_put_cstring(b, context); } + +int +ssh_gssapi_check_mechanism(gss_OID oid, const char *host) +{ + Gssctxt * ctx = NULL; + gss_buffer_desc token; + OM_uint32 major,minor; + + ssh_gssapi_build_ctx(&ctx); + ssh_gssapi_set_oid(ctx,oid); + ssh_gssapi_import_name(ctx, (char *) 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)); +} OM_uint32 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) { if (*ctx) diff -u -udbNpr gss-serv-krb5.c gss-serv-krb5.c --- gss-serv-krb5.c 2004-04-06 21:16:11.000000000 -0700 +++ gss-serv-krb5.c 2004-10-26 14:45:50.000000000 -0700 @@ -41,9 +41,10 @@ extern ServerOptions options; #ifdef HEIMDAL # include <krb5.h> #else -# ifdef HAVE_GSSAPI_KRB5 +# include <com_err.h> +# ifdef HAVE_GSSAPI_KRB5_h # include <gssapi_krb5.h> -# elif HAVE_GSSAPI_GSSAPI_KRB5 +# elif HAVE_GSSAPI_GSSAPI_KRB5_H # include <gssapi/gssapi_krb5.h> # endif #endif @@ -132,9 +133,11 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl } #else { - int tmpfd; char ccname[40]; +#ifndef USE_CCAPI + int tmpfd; + snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d_XXXXXX", geteuid()); @@ -150,6 +153,10 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl return; } close(tmpfd); +#else + snprintf(ccname, sizeof(ccname), "API:krb5cc_%d", geteuid()); + debug ("Using ccache '%s'", ccname); +#endif if ((problem = krb5_cc_resolve(krb_context, ccname, &ccache))) { logit("krb5_cc_resolve(): %.100s", krb5_get_err_text(krb_context, problem)); @@ -183,11 +190,20 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl return; } - client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); + { + const char *new_ccname = krb5_cc_get_name(krb_context, ccache); + client->store.envvar = "KRB5CCNAME"; - len = strlen(client->store.filename) + 6; + len = strlen(new_ccname) + 6; client->store.envval = xmalloc(len); - snprintf(client->store.envval, len, "FILE:%s", client->store.filename); +#ifdef USE_CCAPI + snprintf(client->store.envval, len, "API:%s", new_ccname); + client->store.filename = NULL; /* don't unlink -- not a file */ +#else + snprintf(client->store.envval, len, "FILE:%s", new_ccname); + client->store.filename = xstrdup(new_ccname); +#endif + } #ifdef USE_PAM if (options.use_pam) diff -u -udbNpr gss-serv.c gss-serv.c --- gss-serv.c 2003-11-17 03:18:22.000000000 -0800 +++ gss-serv.c 2004-10-26 14:45:50.000000000 -0700 @@ -42,6 +42,8 @@ #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, @@ -61,6 +63,84 @@ ssh_gssapi_mech* supported_mechs[]= { &gssapi_null_mech, }; +/* 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) + return(NULL); + else + return(mechs); +} + /* Unpriviledged */ void ssh_gssapi_supported_oids(gss_OID_set *oidset) @@ -84,6 +164,32 @@ ssh_gssapi_supported_oids(gss_OID_set *o } } +/* 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; +} /* Wrapper around accept_sec_context * Requires that the context contains: @@ -208,7 +314,6 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g if (client->mech == NULL) return GSS_S_FAILURE; - if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, &client->displayname, NULL))) { ssh_gssapi_error(ctx); diff -u -udbNpr kex.c kex.c --- kex.c 2003-11-21 04:48:55.000000000 -0800 +++ kex.c 2004-10-26 14:45:50.000000000 -0700 @@ -42,6 +42,10 @@ RCSID("$OpenBSD: kex.c,v 1.56 2003/11/21 #include "dispatch.h" #include "monitor.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + #define KEX_COOKIE_LEN 16 /* prototype */ @@ -295,6 +299,10 @@ choose_kex(Kex *k, char *client, char *s k->kex_type = KEX_DH_GRP1_SHA1; } 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) { + k->kex_type = KEX_GSS_GRP1_SHA1; +#endif } else fatal("bad kex alg %s", k->name); } diff -u -udbNpr kex.h kex.h --- kex.h 2003-02-23 17:03:03.000000000 -0800 +++ kex.h 2004-10-26 14:45:50.000000000 -0700 @@ -57,6 +57,7 @@ enum kex_modes { enum kex_exchange { KEX_DH_GRP1_SHA1, KEX_DH_GEX_SHA1, + KEX_GSS_GRP1_SHA1, KEX_MAX }; @@ -95,6 +96,11 @@ struct Newkeys { Mac mac; Comp comp; }; + +struct KexOptions { + int gss_deleg_creds; +}; + struct Kex { u_char *session_id; u_int session_id_len; @@ -110,6 +116,7 @@ struct Kex { int flags; char *client_version_string; char *server_version_string; + struct KexOptions options; int (*verify_host_key)(Key *); Key *(*load_host_key)(int); int (*host_key_index)(Key *); @@ -129,6 +136,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 *); +#endif u_char * kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, diff -u -udbNpr kexgssc.c kexgssc.c --- kexgssc.c 1969-12-31 16:00:00.000000000 -0800 +++ kexgssc.c 2004-10-26 14:45:50.000000000 -0700 @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef GSSAPI + +#include <openssl/crypto.h> +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "canohost.h" +#include "ssh2.h" +#include "ssh-gss.h" + +void +kexgss_client(Kex *kex) +{ + gss_buffer_desc gssbuf,send_tok,recv_tok, 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; + unsigned char *kbuf; + unsigned char *hash; + unsigned char *serverhostkey = NULL; + char *msg; + char *lang; + int type = 0; + int first = 1; + int slen = 0; + u_int strlen; + + /* Initialise our GSSAPI world */ + ssh_gssapi_build_ctx(&ctxt); + if (ssh_gssapi_client_id_kex(ctxt,kex->name)==NULL) { + fatal("Couldn't identify host exchange"); + } + + if (ssh_gssapi_import_name(ctxt,get_canonical_hostname(1))) { + fatal("Couldn't import hostname "); + } + + /* 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"); + } + + 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); + + if (GSS_ERROR(maj_status)) { + if (send_tok.length!=0) { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value, + 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); + + + if (maj_status == GSS_S_COMPLETE) { + /* If mutual state flag is not true, kex fails */ + 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)) { + 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 (first) { + packet_start(SSH2_MSG_KEXGSS_INIT); + packet_put_string(send_tok.value, + send_tok.length); + packet_put_bignum2(dh->pub_key); + first=0; + } else { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value, + send_tok.length); + } + packet_send(); + packet_write_wait(); + + + /* If we've sent them data, they'd better be polite + * and reply. */ + + read_loop: type = packet_read(); + switch (type) { + case SSH2_MSG_KEXGSS_HOSTKEY: + debug("Received KEXGSS_HOSTKEY"); + if (serverhostkey) + fatal("Server host key received more than once"); + serverhostkey=packet_get_string(&slen); + goto read_loop; + 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 */ + 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 */ + + /* Is there a token included? */ + if (packet_get_char()) { + recv_tok.value= + packet_get_string(&strlen); + recv_tok.length=strlen; /*u_int/size_t*/ + /* 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"); + } + 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); + debug("GSSAPI Error: \n%s",msg); + default: + packet_disconnect("Protocol error: didn't expect packet type %d", + type); + } + token_ptr=&recv_tok; + } else { + /* No data, and not 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"); + + /* 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))) { + + 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); + } + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + kex_finish(kex); +} + +#endif /* GSSAPI */ diff -u -udbNpr kexgsss.c kexgsss.c --- kexgsss.c 1969-12-31 16:00:00.000000000 -0800 +++ kexgsss.c 2004-10-26 14:45:50.000000000 -0700 @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef GSSAPI + +#include <openssl/crypto.h> +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#include "ssh-gss.h" +#include "monitor_wrap.h" + +static void kex_gss_send_error(Gssctxt *ctxt); + +void +kexgss_server(Kex *kex) +{ + OM_uint32 maj_status, min_status; + + /* 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.*/ + + OM_uint32 ret_flags = 0; + gss_buffer_desc gssbuf,send_tok,recv_tok,msg_tok; + Gssctxt *ctxt = NULL; + unsigned int klen, kout; + unsigned char *kbuf; + unsigned char *hash; + DH *dh; + BIGNUM *shared_secret = NULL; + BIGNUM *dh_client_pub = NULL; + int type =0; + u_int slen; + gss_OID oid; + + /* Initialise GSSAPI */ + + debug2("%s: Identifying %s",__func__,kex->name); + oid=ssh_gssapi_server_id_kex(kex->name); + 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); + fatal("Unable to acquire credentials for the server"); + } + + do { + debug("Wait SSH2_MSG_GSSAPI_INIT"); + type = packet_read(); + switch(type) { + case SSH2_MSG_KEXGSS_INIT: + if (dh_client_pub!=NULL) + fatal("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) + 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 */ + break; + default: + 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); + + 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_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) { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); + packet_put_string(send_tok.value,send_tok.length); + packet_send(); + packet_write_wait(); + } + fatal("accept_ctx died"); + } + + debug("gss_complete"); + if (!(ret_flags & GSS_C_MUTUAL_FLAG)) + fatal("mutual authentication flag wasn't set"); + + 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"); + + 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 + ); + 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); + 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) { + packet_put_char(1); /* true */ + 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); + + DH_free(dh); + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + kex_finish(kex); +} + +static void +kex_gss_send_error(Gssctxt *ctxt) { + char *errstr; + OM_uint32 maj,min; + + errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min)); + if (errstr) { + packet_start(SSH2_MSG_KEXGSS_ERROR); + packet_put_int(maj); + packet_put_int(min); + packet_put_cstring(errstr); + packet_put_cstring(""); + packet_send(); + packet_write_wait(); + /* XXX - We should probably log the error locally here */ + xfree(errstr); + } +} +#endif /* GSSAPI */ diff -u -udbNpr key.c key.c --- key.c 2003-11-17 02:18:23.000000000 -0800 +++ key.c 2004-10-26 14:45:50.000000000 -0700 @@ -650,6 +650,8 @@ 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){ + return KEY_NULL; } debug2("key_type_from_name: unknown key type '%s'", name); return KEY_UNSPEC; diff -u -udbNpr key.h key.h --- key.h 2003-11-17 02:18:23.000000000 -0800 +++ key.h 2004-10-26 14:45:50.000000000 -0700 @@ -34,6 +34,7 @@ enum types { KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_NULL, KEY_UNSPEC }; enum fp_type { diff -u -udbNpr monitor.c monitor.c --- monitor.c 2004-10-26 14:40:11.000000000 -0700 +++ monitor.c 2004-10-26 14:45:50.000000000 -0700 @@ -141,6 +141,9 @@ 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 *); +int mm_answer_gss_sign(int, Buffer *); +int mm_answer_gss_error(int, Buffer *); +int mm_answer_gss_indicate_mechs(int, Buffer *); #endif #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) @@ -205,6 +208,9 @@ struct mon_table mon_dispatch_proto20[] #ifdef GSSAPI {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, + {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_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, #endif @@ -217,6 +223,13 @@ struct mon_table mon_dispatch_proto20[] }; struct mon_table mon_dispatch_postauth20[] = { +#ifdef GSSAPI + {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, + {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error}, + {MONITOR_REQ_GSSMECHS, 0, mm_answer_gss_indicate_mechs}, +#endif {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, {MONITOR_REQ_SIGN, 0, mm_answer_sign}, {MONITOR_REQ_PTY, 0, mm_answer_pty}, @@ -313,6 +326,12 @@ monitor_child_preauth(Authctxt *_authctx /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); +#ifdef GSSAPI + /* and for the GSSAPI key exchange */ + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1); +#endif } else { mon_dispatch = mon_dispatch_proto15; @@ -386,6 +405,12 @@ monitor_child_postauth(struct monitor *p monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); +#ifdef GSSAPI + /* and for the GSSAPI key exchange */ + monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS,1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP,1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR,1); +#endif } else { mon_dispatch = mon_dispatch_postauth15; monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); @@ -1608,6 +1633,9 @@ mm_get_kex(Buffer *m) kex->we_need = buffer_get_int(m); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; +#ifdef GSSAPI + kex->kex[KEX_GSS_GRP1_SHA1] =kexgss_server; +#endif kex->server = 1; kex->hostkey_type = buffer_get_int(m); kex->kex_type = buffer_get_int(m); @@ -1799,6 +1827,84 @@ monitor_reinit(struct monitor *mon) } #ifdef GSSAPI + +int +mm_answer_gss_sign(int socket, Buffer *m) { + gss_buffer_desc data,hash; + OM_uint32 major,minor; + + data.value = buffer_get_string(m,&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); + + xfree(data.value); + + buffer_clear(m); + buffer_put_int(m, major); + buffer_put_string(m, hash.value, hash.length); + + mm_request_send(socket,MONITOR_ANS_GSSSIGN,m); + + gss_release_buffer(&minor,&hash); + + /* Turn on permissions for getpwnam */ + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); + + return(0); +} + +int +mm_answer_gss_error(int socket, Buffer *m) { + OM_uint32 major,minor; + char *msg; + + msg=ssh_gssapi_last_error(gsscontext,&major,&minor); + buffer_clear(m); + buffer_put_int(m,major); + buffer_put_int(m,minor); + buffer_put_cstring(m,msg); + + mm_request_send(socket,MONITOR_ANS_GSSERR,m); + + xfree(msg); + + return(0); +} + +int +mm_answer_gss_indicate_mechs(int socket, Buffer *m) { + OM_uint32 major,minor; + gss_OID_set mech_set; + int i; + + major=gss_indicate_mechs(&minor, &mech_set); + + buffer_clear(m); + buffer_put_int(m, major); + buffer_put_int(m, mech_set->count); + for (i=0; i < mech_set->count; i++) { + buffer_put_string(m, mech_set->elements[i].elements, + mech_set->elements[i].length); + } + + gss_release_oid_set(&minor,&mech_set); + + mm_request_send(socket,MONITOR_ANS_GSSMECHS,m); + + return(0); +} + +#endif /* GSSAPI */ + +#ifdef GSSAPI int mm_answer_gss_setup_ctx(int socket, Buffer *m) { @@ -1850,6 +1956,7 @@ mm_answer_gss_accept_ctx(int socket, Buf monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); } return (0); } diff -u -udbNpr monitor.h monitor.h --- monitor.h 2004-10-26 14:40:11.000000000 -0700 +++ monitor.h 2004-10-26 14:45:50.000000000 -0700 @@ -56,6 +56,9 @@ enum monitor_reqtype { MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, + MONITOR_REQ_GSSSIGN,MONITOR_ANS_GSSSIGN, + MONITOR_REQ_GSSERR,MONITOR_ANS_GSSERR, + MONITOR_REQ_GSSMECHS,MONITOR_ANS_GSSMECHS, MONITOR_REQ_PAM_START, MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, diff -u -udbNpr monitor_wrap.c monitor_wrap.c --- monitor_wrap.c 2004-10-26 14:40:11.000000000 -0700 +++ monitor_wrap.c 2004-10-26 14:45:50.000000000 -0700 @@ -1175,6 +1175,79 @@ mm_ssh_gssapi_userok(char *user) debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); return (authenticated); } + +OM_uint32 +mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) { + Buffer m; + OM_uint32 major; + + 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); + + major=buffer_get_int(&m); + hash->value = buffer_get_string(&m, &hash->length); + + buffer_free(&m); + + return(major); +} + +char * +mm_ssh_gssapi_last_error(Gssctxt *ctx, OM_uint32 *major, OM_uint32 *minor) { + Buffer m; + OM_uint32 maj,min; + char *errstr; + + buffer_init(&m); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSERR, &m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSERR, &m); + + maj = buffer_get_int(&m); + min = buffer_get_int(&m); + + if (major) *major=maj; + if (minor) *minor=min; + + errstr=buffer_get_string(&m,NULL); + + buffer_free(&m); + + return(errstr); +} + +OM_uint32 +mm_gss_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set) +{ + Buffer m; + OM_uint32 major,minor; + int count; + gss_OID_desc oid; + u_int length; + + buffer_init(&m); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSMECHS, &m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSMECHS, + &m); + major=buffer_get_int(&m); + count=buffer_get_int(&m); + + gss_create_empty_oid_set(&minor,mech_set); + while(count-->0) { + oid.elements=buffer_get_string(&m,&length); + oid.length=length; + gss_add_oid_set_member(&minor,&oid,mech_set); + } + + buffer_free(&m); + + return(major); +} + #endif /* GSSAPI */ #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) diff -u -udbNpr monitor_wrap.h monitor_wrap.h --- monitor_wrap.h 2004-10-26 14:40:11.000000000 -0700 +++ monitor_wrap.h 2004-10-26 14:45:50.000000000 -0700 @@ -63,6 +63,10 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssct gss_buffer_desc *recv, gss_buffer_desc *send, OM_uint32 *flags); 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); +char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min); + #endif #ifdef USE_PAM @@ -74,6 +78,7 @@ int mm_sshpam_respond(void *, u_int, cha void mm_sshpam_free_ctx(void *); #endif + struct Session; void mm_terminate(void); int mm_pty_allocate(int *, int *, char *, int); diff -u -udbNpr readconf.c readconf.c --- readconf.c 2004-03-08 04:12:36.000000000 -0800 +++ readconf.c 2004-10-26 14:45:50.000000000 -0700 @@ -910,9 +910,9 @@ fill_default_options(Options * options) if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; if (options->gss_authentication == -1) - options->gss_authentication = 0; + options->gss_authentication = 1; if (options->gss_deleg_creds == -1) - options->gss_deleg_creds = 0; + options->gss_deleg_creds = 1; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) diff -u -udbNpr servconf.c servconf.c --- servconf.c 2004-01-23 03:03:10.000000000 -0800 +++ servconf.c 2004-10-26 14:45:50.000000000 -0700 @@ -74,6 +74,9 @@ initialize_server_options(ServerOptions options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; options->gss_authentication=-1; + options->gss_nomic_authentication = -1; + options->gss_keyex = -1; + options->gss_use_session_ccache = -1; options->gss_cleanup_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; @@ -111,7 +114,7 @@ fill_default_server_options(ServerOption { /* Portable-specific options */ if (options->use_pam == -1) - options->use_pam = 0; + options->use_pam = 1; /* Standard Options */ if (options->protocol == SSH_PROTO_UNKNOWN) @@ -185,7 +188,13 @@ fill_default_server_options(ServerOption if (options->kerberos_get_afs_token == -1) options->kerberos_get_afs_token = 0; if (options->gss_authentication == -1) - options->gss_authentication = 0; + options->gss_authentication = 1; + if (options->gss_nomic_authentication == -1) + options->gss_nomic_authentication = options->gss_authentication; + if (options->gss_keyex == -1) + options->gss_keyex =1; + if (options->gss_use_session_ccache == -1) + options->gss_use_session_ccache = 1; if (options->gss_cleanup_creds == -1) options->gss_cleanup_creds = 1; if (options->password_authentication == -1) @@ -266,7 +275,8 @@ typedef enum { sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, - sGssAuthentication, sGssCleanupCreds, + sGssAuthentication, sGssKeyEx, sGssNoMICAuthentication, + sGssUseSessionCredCache, sGssCleanupCreds, sUsePrivilegeSeparation, sDeprecated, sUnsupported } ServerOpCodes; @@ -320,9 +330,14 @@ static struct { { "afstokenpassing", sUnsupported }, #ifdef GSSAPI { "gssapiauthentication", sGssAuthentication }, + {"gssapinomicauthentication", sGssNoMICAuthentication}, + { "gssapikeyexchange", sGssKeyEx }, + { "gssusesessionccache", sGssUseSessionCredCache }, + { "gssapiusesessioncredcache", sGssUseSessionCredCache }, { "gssapicleanupcredentials", sGssCleanupCreds }, #else { "gssapiauthentication", sUnsupported }, + {"gssapinomicauthentication", sUnsupported }, { "gssapicleanupcredentials", sUnsupported }, #endif { "passwordauthentication", sPasswordAuthentication }, @@ -648,6 +663,18 @@ parse_flag: intptr = &options->gss_authentication; goto parse_flag; + case sGssNoMICAuthentication: + intptr = &options->gss_nomic_authentication; + goto parse_flag; + + case sGssKeyEx: + intptr = &options->gss_keyex; + goto parse_flag; + + case sGssUseSessionCredCache: + intptr = &options->gss_use_session_ccache; + goto parse_flag; + case sGssCleanupCreds: intptr = &options->gss_cleanup_creds; goto parse_flag; diff -u -udbNpr servconf.h servconf.h --- servconf.h 2003-12-30 16:37:34.000000000 -0800 +++ servconf.h 2004-10-26 14:45:50.000000000 -0700 @@ -83,6 +83,10 @@ 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_nomic_authentication; /* Add option for old gssapi mechanism*/ + int gss_keyex; + int gss_use_session_ccache; /* If true, delegated credentials are + * stored in a session specific cache */ int gss_cleanup_creds; /* If true, destroy cred cache on logout */ int password_authentication; /* If true, permit password * authentication. */ diff -u -udbNpr session.c session.c --- session.c 2004-10-26 14:40:11.000000000 -0700 +++ session.c 2004-10-26 14:45:50.000000000 -0700 @@ -398,6 +398,12 @@ do_exec_no_pty(Session *s, const char *c session_proctitle(s); +#if defined(GSSAPI) + temporarily_use_uid(s->pw); + ssh_gssapi_storecreds(); + restore_uid(); +#endif + #if defined(USE_PAM) if (options.use_pam && !use_privsep) do_pam_setcred(1); @@ -529,6 +535,12 @@ do_exec_pty(Session *s, const char *comm ptyfd = s->ptyfd; ttyfd = s->ttyfd; +#if defined(GSSAPI) + temporarily_use_uid(s->pw); + ssh_gssapi_storecreds(); + restore_uid(); +#endif + #if defined(USE_PAM) if (options.use_pam) { do_pam_set_tty(s->tty); @@ -2221,6 +2233,9 @@ static void do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); +#if defined(GSSAPI) + ssh_gssapi_cleanup_creds(); +#endif } void diff -u -udbNpr ssh-gss.h ssh-gss.h --- ssh-gss.h 2004-02-23 15:37:33.000000000 -0800 +++ ssh-gss.h 2004-10-26 14:45:50.000000000 -0700 @@ -28,6 +28,7 @@ #ifdef GSSAPI +#include "kex.h" #include "buffer.h" #ifdef HAVE_GSSAPI_H @@ -53,6 +54,11 @@ #endif /* KRB5 */ /* 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 @@ -60,6 +66,7 @@ #define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 #define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 +#define KEX_GSS_SHA1 "gss-group1-sha1-" #define SSH_GSS_OIDTYPE 0x06 typedef struct { @@ -100,6 +107,9 @@ typedef struct { extern ssh_gssapi_mech *supported_mechs[]; +char *ssh_gssapi_mechanisms(char *host); +char *ssh_gssapi_client_mechanisms(const char *host); +gss_OID ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name); int ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len); void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len); void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid); @@ -117,16 +127,22 @@ void ssh_gssapi_error(Gssctxt *ctx); char *ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min); void ssh_gssapi_build_ctx(Gssctxt **ctx); void ssh_gssapi_delete_ctx(Gssctxt **ctx); +int ssh_gssapi_check_mechanism(gss_OID oid, const char *host); OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); OM_uint32 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid); void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); /* In the server */ +gss_OID ssh_gssapi_server_id_kex(char *name); int ssh_gssapi_userok(char *name); +void ssh_gssapi_server(Kex *kex, Buffer *client_kexinit, + Buffer *server_kexinit); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); void ssh_gssapi_do_child(char ***envp, u_int *envsizep); void ssh_gssapi_cleanup_creds(void); void ssh_gssapi_storecreds(void); +char *ssh_gssapi_server_mechanisms(void); #endif /* GSSAPI */ diff -u -udbNpr sshconnect2.c sshconnect2.c --- sshconnect2.c 2004-03-08 04:12:36.000000000 -0800 +++ sshconnect2.c 2004-10-26 14:45:50.000000000 -0700 @@ -83,10 +83,26 @@ void ssh_kex2(char *host, struct sockaddr *hostaddr) { Kex *kex; +#ifdef GSSAPI + char *orig, *gss; + int len; +#endif xxx_host = host; xxx_hostaddr = hostaddr; +#ifdef GSSAPI + /* 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)); + if (gss) { + len = strlen(orig)+strlen(gss)+2; + myproposal[PROPOSAL_KEX_ALGS]=xmalloc(len); + snprintf(myproposal[PROPOSAL_KEX_ALGS],len,"%s,%s",gss,orig); + } +#endif + if (options.ciphers == (char *)-1) { logit("No valid ciphers for protocol version 2 given, using defaults."); options.ciphers = NULL; @@ -114,6 +130,16 @@ ssh_kex2(char *host, struct sockaddr *ho myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = 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"); + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]=xmalloc(len); + snprintf(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],len,"%s,null",orig); + } +#endif if (options.rekey_limit) packet_set_rekey_limit(options.rekey_limit); @@ -121,10 +147,15 @@ ssh_kex2(char *host, struct sockaddr *ho kex = kex_setup(myproposal); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; +#ifdef GSSAPI + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; +#endif kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; - +#ifdef GSSAPI + kex->options.gss_deleg_creds=options.gss_deleg_creds; +#endif xxx_kex = kex; dispatch_run(DISPATCH_BLOCK, &kex->done, kex); @@ -226,6 +257,10 @@ Authmethod authmethods[] = { userauth_gssapi, &options.gss_authentication, NULL}, + {"gssapi", + userauth_gssapi, + &options.gss_authentication, + NULL}, #endif {"hostbased", userauth_hostbased, @@ -484,6 +519,7 @@ userauth_gssapi(Authctxt *authctxt) static int mech = 0; OM_uint32 min; int ok = 0; + int old_gssapi_method; /* Try one GSSAPI method at a time, rather than sending them all at * once. */ @@ -517,13 +553,24 @@ userauth_gssapi(Authctxt *authctxt) packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); - packet_put_int(1); + old_gssapi_method = !strcmp( authctxt->method->name, "gssapi"); + + /* Version of Debian ssh-krb5 prior to 3.8.1p1-1 don't expect + * tagged OIDs. As such we include both tagged and untagged oids for the old gssapi method. + * We only include tagged oids for the new gssapi-with-mic method. + */ + packet_put_int(old_gssapi_method?2:1); packet_put_int((gss_supported->elements[mech].length) + 2); packet_put_char(SSH_GSS_OIDTYPE); packet_put_char(gss_supported->elements[mech].length); packet_put_raw(gss_supported->elements[mech].elements, gss_supported->elements[mech].length); + if (old_gssapi_method) { + packet_put_int((gss_supported->elements[mech].length) ); + packet_put_raw(gss_supported->elements[mech].elements, + gss_supported->elements[mech].length); + } packet_send(); @@ -563,7 +610,9 @@ process_gssapi_token(void *ctxt, gss_buf if (status == GSS_S_COMPLETE) { /* send either complete or MIC, depending on mechanism */ - if (!(flags & GSS_C_INTEG_FLAG)) { + int old_gssapi_method = !strcmp(authctxt->method->name, "gssapi"); + + if (old_gssapi_method || !(flags & GSS_C_INTEG_FLAG)) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); packet_send(); } else { @@ -596,30 +645,35 @@ input_gssapi_response(int type, u_int32_ Authctxt *authctxt = ctxt; Gssctxt *gssctxt; int oidlen; - char *oidv; + char *oidv, *oidv_free; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; /* Setup our OID */ - oidv = packet_get_string(&oidlen); + oidv = oidv_free = packet_get_string(&oidlen); if (oidlen <= 2 || oidv[0] != SSH_GSS_OIDTYPE || oidv[1] != oidlen - 2) { - xfree(oidv); debug("Badly encoded mechanism OID received"); + if (!(oidlen >= 2)) { + xfree(oidv_free); userauth(authctxt, NULL); return; } + } else { + oidlen -= 2; + oidv += 2; + } - if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) + if (!ssh_gssapi_check_oid(gssctxt, oidv , oidlen)) fatal("Server returned different OID than expected"); packet_check_eom(); - xfree(oidv); + xfree(oidv_free); if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) { /* Start again with next method on list */ diff -u -udbNpr sshd.c sshd.c --- sshd.c 2004-10-26 14:40:11.000000000 -0700 +++ sshd.c 2004-10-26 14:45:50.000000000 -0700 @@ -85,6 +85,14 @@ RCSID("$OpenBSD: sshd.c,v 1.290 2004/03/ #include "monitor_wrap.h" #include "monitor_fdpass.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + +#ifdef USE_SECURITY_SESSION_API +#include <Security/AuthSession.h> +#endif + #ifdef LIBWRAP #include <tcpd.h> #include <syslog.h> @@ -994,10 +1002,13 @@ main(int ac, char **av) logit("Disabling protocol version 1. Could not load host key"); 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; } +#endif if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { logit("sshd: no hostkeys available -- exiting."); exit(1); @@ -1447,6 +1458,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 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 @@ -1779,10 +1846,52 @@ do_ssh2_kex(void) } myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); +#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; + + if (gss && orig) { + int len = strlen(orig) + strlen(gss) +2; + newstr=xmalloc(len); + snprintf(newstr,len,"%s,%s",gss,orig); + } else if (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"; + } + if (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; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; +#ifdef GSSAPI + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; +#endif kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; diff -u -udbNpr sshd_config.5 sshd_config.5 --- sshd_config.5 2004-04-13 20:04:36.000000000 -0700 +++ sshd_config.5 2004-10-26 14:45:50.000000000 -0700 @@ -265,6 +265,26 @@ keys are used for version 1 and or .Dq rsa are used for version 2 of the SSH protocol. +.It Cm GssapiAuthentication +Specifies whether authentication based on GSSAPI may be used, either using +the result of a successful key exchange, or using GSSAPI user +authentication. +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. +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. +The default is +.Dq yes . +Note that this option applies to protocol version 2 only. + .It Cm IgnoreRhosts Specifies that .Pa .rhosts syntax highlighted by Code2HTML, v. 0.9.1 |