diff -u -N --exclude configure --exclude config.h.in src.old/Makefile.in src/Makefile.in --- src.old/Makefile.in Sat Jan 11 17:08:23 2003 +++ src/Makefile.in Sat Jan 11 17:08:17 2003 @@ -60,11 +60,11 @@ TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} $(SFTP_PROGS) -LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dh.o dispatch.o fatal.o mac.o msg.o hostfile.o key.o kex.o kexdh.o kexgex.o log.o match.o misc.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o scard.o scard-opensc.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o monitor_wrap.o monitor_fdpass.o +LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dh.o dispatch.o fatal.o mac.o msg.o hostfile.o key.o kex.o kexdh.o kexgex.o log.o match.o misc.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o scard.o scard-opensc.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o monitor_wrap.o monitor_fdpass.o kexgss.o gss-genr.o SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o sshtty.o readconf.o clientloop.o -SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth2-hostbased.o auth2-kbdint.o auth2-none.o auth2-passwd.o auth2-pubkey.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-krb5.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o auth-sia.o sshpty.o sshlogin.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o auth-skey.o auth-bsdauth.o monitor_mm.o monitor.o +SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth2-hostbased.o auth2-kbdint.o auth2-none.o auth2-passwd.o auth2-pubkey.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-krb5.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o auth-sia.o sshpty.o sshlogin.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o auth-skey.o auth-bsdauth.o monitor_mm.o monitor.o auth2-gss.o gss-serv.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 MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5 diff -u -N --exclude configure --exclude config.h.in src.old/acconfig.h src/acconfig.h --- src.old/acconfig.h Sat Jan 11 17:08:21 2003 +++ src/acconfig.h Sat Jan 11 17:08:13 2003 @@ -201,6 +201,9 @@ /* Define if compiler implements __func__ */ #undef HAVE___func__ +/* Define this is you want GSSAPI support in the version 2 protocol */ +#undef GSSAPI + /* Define if you want Kerberos 5 support */ #undef KRB5 @@ -212,6 +215,9 @@ /* Define if you want AFS support */ #undef AFS + +/* Define if you want GSI/Globus authentication support */ +#undef GSI /* Define if you want S/Key support */ #undef SKEY diff -u -N --exclude configure --exclude config.h.in src.old/auth-pam.c src/auth-pam.c --- src.old/auth-pam.c Sat Jan 11 17:08:21 2003 +++ src/auth-pam.c Sat Jan 11 17:08:14 2003 @@ -431,6 +431,26 @@ } } +/* Set a PAM environment string. We need to do this so that the session + * modules can handle things like Kerberos/GSI credentials that appear + * during the ssh authentication process. + */ + +int do_pam_putenv(char *name, char *value) { + char *compound; + int ret=1; + +#ifdef HAVE_PAM_PUTENV + compound=xmalloc(strlen(name)+strlen(value)+2); + if (compound) { + sprintf(compound,"%s=%s",name,value); + ret=pam_putenv(__pamh,compound); + xfree(compound); + } +#endif + return(ret); +} + /* Print any messages that have been generated during authentication */ /* or account checking to stderr */ void print_pam_messages(void) diff -u -N --exclude configure --exclude config.h.in src.old/auth-pam.h src/auth-pam.h --- src.old/auth-pam.h Sat Jan 11 17:08:21 2003 +++ src/auth-pam.h Sat Jan 11 17:08:14 2003 @@ -44,6 +44,7 @@ int is_pam_password_change_required(void); void do_pam_chauthtok(void); void do_pam_set_conv(struct pam_conv *); +int do_pam_putenv(char *, char *); void message_cat(char **p, const char *a); #endif /* USE_PAM */ diff -u -N --exclude configure --exclude config.h.in src.old/auth.h src/auth.h --- src.old/auth.h Sat Jan 11 17:08:21 2003 +++ src/auth.h Sat Jan 11 17:08:14 2003 @@ -70,6 +70,7 @@ krb5_principal krb5_user; char *krb5_ticket_file; #endif + void *methoddata; }; struct Authmethod { diff -u -N --exclude configure --exclude config.h.in src.old/auth2-gss.c src/auth2-gss.c --- src.old/auth2-gss.c Wed Dec 31 18:00:00 1969 +++ src/auth2-gss.c Sat Jan 11 17:08:14 2003 @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2001,2002 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 "auth.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "log.h" +#include "dispatch.h" +#include "servconf.h" +#include "compat.h" +#include "packet.h" +#include "monitor_wrap.h" + +#include "ssh-gss.h" + +extern ServerOptions options; + +static int +userauth_external(Authctxt *authctxt) +{ + packet_check_eom(); + + return(PRIVSEP(ssh_gssapi_userok(authctxt->user))); +} + +static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); +static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + +/* We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like + */ +static int +userauth_gssapi(Authctxt *authctxt) +{ + gss_OID_desc oid= {0,NULL}; + Gssctxt *ctxt = NULL; + int mechs; + gss_OID_set supported; + int present; + OM_uint32 ms; + u_int len; + + if (!authctxt->valid || authctxt->user == NULL) + return 0; + + if (datafellows & SSH_OLD_GSSAPI) { + debug("Early drafts of GSSAPI userauth not supported"); + return 0; + } + + mechs=packet_get_int(); + if (mechs==0) { + debug("Mechanism negotiation is not supported"); + return 0; + } + + ssh_gssapi_supported_oids(&supported); + do { + if (oid.elements) + xfree(oid.elements); + oid.elements = packet_get_string(&len); + oid.length = len; + gss_test_oid_set_member(&ms, &oid, supported, &present); + mechs--; + } while (mechs>0 && !present); + + if (!present) { + xfree(oid.elements); + return(0); + } + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid)))) + return(0); + + authctxt->methoddata=(void *)ctxt; + + /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */ + + packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); + packet_put_string(oid.elements,oid.length); + packet_send(); + packet_write_wait(); + xfree(oid.elements); + + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, + &input_gssapi_token); + authctxt->postponed = 1; + + return 0; +} + +static void +input_gssapi_token(int type, u_int32_t plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok,recv_tok; + OM_uint32 maj_status, min_status; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt=authctxt->methoddata; + recv_tok.value=packet_get_string(&recv_tok.length); + + maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, + &send_tok, NULL)); + packet_check_eom(); + + if (GSS_ERROR(maj_status)) { + /* Failure */ + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + userauth_finish(authctxt, 0, "gssapi"); + } + + if (send_tok.length != 0) { + /* Send a packet back to the client */ + packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + packet_put_string(send_tok.value,send_tok.length); + packet_send(); + packet_write_wait(); + gss_release_buffer(&min_status, &send_tok); + } + + if (maj_status == GSS_S_COMPLETE) { + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, + &input_gssapi_exchange_complete); + } +} + +/* This is called when the client thinks we've completed authentication. + * It should only be enabled in the dispatch handler by the function above, + * which only enables it once the GSSAPI exchange is complete. + */ + +static void +input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + int authenticated; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt=authctxt->methoddata; + + /* We don't need to check the status, because the stored credentials + * which userok uses are only populated once the context init step + * has returned complete. + */ + + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(authctxt, authenticated, "gssapi"); +} + +Authmethod method_external = { + "external-keyx", + userauth_external, + &options.gss_authentication +}; + +Authmethod method_gssapi = { + "gssapi", + userauth_gssapi, + &options.gss_authentication +}; + +#endif /* GSSAPI */ diff -u -N --exclude configure --exclude config.h.in src.old/auth2.c src/auth2.c --- src.old/auth2.c Sat Jan 11 17:08:22 2003 +++ src/auth2.c Sat Jan 11 17:08:14 2003 @@ -36,6 +36,10 @@ #include "pathnames.h" #include "monitor_wrap.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + /* import */ extern ServerOptions options; extern u_char *session_id2; @@ -50,9 +54,15 @@ extern Authmethod method_passwd; extern Authmethod method_kbdint; extern Authmethod method_hostbased; +extern Authmethod method_external; +extern Authmethod method_gssapi; Authmethod *authmethods[] = { &method_none, +#ifdef GSSAPI + &method_external, + &method_gssapi, +#endif &method_pubkey, &method_passwd, &method_kbdint, @@ -180,6 +190,12 @@ } /* reset state */ auth2_challenge_stop(authctxt); + +#ifdef GSSAPI + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); +#endif + authctxt->postponed = 0; /* try to authenticate user */ diff -u -N --exclude configure --exclude config.h.in src.old/compat.c src/compat.c --- src.old/compat.c Sat Jan 11 17:08:22 2003 +++ src/compat.c Sat Jan 11 17:08:15 2003 @@ -71,12 +71,14 @@ { "OpenSSH_2.5.0p1*," "OpenSSH_2.5.1p1*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| - SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, + SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| + SSH_OLD_GSSAPI}, { "OpenSSH_2.5.0*," "OpenSSH_2.5.1*," "OpenSSH_2.5.2*", SSH_OLD_DHGEX|SSH_BUG_NOREKEY| SSH_BUG_EXTEOF}, { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, + { "OpenSSH_2.9p*", SSH_BUG_EXTEOF|SSH_OLD_GSSAPI}, { "OpenSSH_2.*," "OpenSSH_3.0*," "OpenSSH_3.1*", SSH_BUG_EXTEOF}, diff -u -N --exclude configure --exclude config.h.in src.old/compat.h src/compat.h --- src.old/compat.h Sat Jan 11 17:08:22 2003 +++ src/compat.h Sat Jan 11 17:08:15 2003 @@ -55,6 +55,7 @@ #define SSH_BUG_EXTEOF 0x00200000 #define SSH_BUG_K5USER 0x00400000 #define SSH_BUG_PROBE 0x00800000 +#define SSH_OLD_GSSAPI 0x01000000 void enable_compat13(void); void enable_compat20(void); diff -u -N --exclude configure --exclude config.h.in src.old/configure.ac src/configure.ac --- src.old/configure.ac Sat Jan 11 17:08:22 2003 +++ src/configure.ac Sat Jan 11 17:08:15 2003 @@ -695,6 +695,7 @@ AC_CHECK_LIB(dl, dlopen, , ) AC_CHECK_LIB(pam, pam_set_item, , AC_MSG_ERROR([*** libpam missing])) AC_CHECK_FUNCS(pam_getenvlist) + AC_CHECK_FUNCS(pam_putenv) disable_shadow=yes PAM_MSG="yes" @@ -705,6 +706,7 @@ else LIBPAM="-lpam" fi + AC_SUBST(LIBPAM) fi ] @@ -735,6 +737,47 @@ AC_CHECK_LIB(crypt, crypt) fi +# Start of GSI/Globus 2.0 mods +# Check whether the user wants GSI (Globus 2.0) support +# if we are using GSI, we will also use the +# OPenSSL that is built by Globus. This is called +# -lcrypto_FLAVOR +# and it will be in the GSI path. +# The includes will be in the include/FLAVOR/openssl +# therfore we will not process the -with-ssl parameter. + +gsi_path="no" +AC_ARG_WITH(gsi, + [ --with-gsi=PATH Enable GSI/Globus GSSAPI support], + [ + gsi_path="$withval" + ] +) + +gsi_flavor=gcc32dbg +AC_ARG_WITH(gsi-flavor, + [ --with-gsi-flavor=FLAVOR Globus build flavor ], + [ + gsi_flavor="$withval" + ] +) + +if test "x$gsi_path" != "xno" ; then + # Globus GSSAPI configuration + AC_DEFINE(GSSAPI) + AC_DEFINE(GSI) + AC_DEFINE(HAVE_OPENSSL) + LDFLAGS="$LDFLAGS -L${gsi_path}/lib" + if test ! -z "$need_dash_r" ; then + LDFLAGS="$LDFLAGS -R${gsi_path}/lib" + fi + if test ! -z "$blibpath" ; then + blibpath="$blibpath:${gsi_path}/lib" + fi + LIBS="$LIBS -lcrypto_${gsi_flavor} -lglobus_gss_assist_${gsi_flavor} -lglobus_gssapi_gsi_${gsi_flavor}" + CPPFLAGS="$CPPFLAGS -I${gsi_path}/include -I${gsi_path}/include/${gsi_flavor}" +else + # Search for OpenSSL saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" @@ -781,6 +824,9 @@ ] ) +fi +#end of GSI/Globus 2.0 mods + # Determine OpenSSL header version AC_MSG_CHECKING([OpenSSL header version]) AC_TRY_RUN( @@ -1786,6 +1832,31 @@ blibpath="$blibpath:${KRB5ROOT}/lib" fi AC_CHECK_LIB(resolv, dn_expand, , ) + + AC_CHECK_LIB(gssapi,gss_init_sec_context, + [ AC_DEFINE(GSSAPI) + K5LIBS="-lgssapi $K5LIBS" ], + [ AC_CHECK_LIB(gssapi_krb5,gss_init_sec_context, + [ AC_DEFINE(GSSAPI) + K5LIBS="-lgssapi_krb5 $K5LIBS" ], + AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail]), + $K5LIBS) + ], + $K5LIBS) + + AC_CHECK_HEADER(gssapi.h, , + [ unset ac_cv_header_gssapi_h + CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" + AC_CHECK_HEADERS(gssapi.h, , + AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail]) + ) + ] + ) + + oldCPP="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" + AC_CHECK_HEADER(gssapi_krb5.h, , + [ CPPFLAGS="$oldCPP" ]) KRB5=yes fi diff -u -N --exclude configure --exclude config.h.in src.old/gss-genr.c src/gss-genr.c --- src.old/gss-genr.c Wed Dec 31 18:00:00 1969 +++ src/gss-genr.c Sat Jan 11 17:08:15 2003 @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2001,2002 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 "ssh.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "packet.h" +#include "compat.h" +#include +#include "cipher.h" +#include "kex.h" +#include "log.h" +#include "compat.h" +#include "monitor_wrap.h" + +#include + +#include "ssh-gss.h" + +/* Assorted globals for tracking the clients identity once they've + * authenticated */ + +gss_buffer_desc gssapi_client_name = {0,NULL}; /* Name of our client */ +gss_cred_id_t gssapi_client_creds = GSS_C_NO_CREDENTIAL; /* Their credentials */ +enum ssh_gss_id gssapi_client_type = GSS_LAST_ENTRY; + +/* The mechanism name used in the list below is defined in the internet + * draft as the Base 64 encoding of the MD5 hash of the ASN.1 DER encoding + * of the underlying GSSAPI mechanism's OID. + * + * Also from the draft, before considering adding SPNEGO, bear in mind that + * "mechanisms ... MUST NOT use SPNEGO as the underlying GSSAPI mechanism" + */ + +/* These must be in the same order as ssh_gss_id, in ssh-gss.h */ + +ssh_gssapi_mech supported_mechs[]= { +#ifdef KRB5 + /* Official OID - 1.2.850.113554.1.2.2 */ + {"Se3H81ismmOC3OE+FwYCiQ==","Kerberos", + {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}}, +#endif +#ifdef GSI + /* gssapi_ssleay 1.3.6.1.4.1.3536.1.1 */ + {"N3+k7/4wGxHyuP8Yxi4RhA==", + "GSI", + {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"} + }, +#endif /* GSI */ + {NULL,NULL,{0,0}} +}; + +char gssprefix[]=KEX_GSS_SHA1; + +/* 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. + */ + +char * +ssh_gssapi_mechanisms(int server,char *host) { + gss_OID_set supported; + OM_uint32 maj_status, min_status; + Buffer buf; + int i = 0; + int present; + char * mechs; + Gssctxt * ctx = NULL; + + if (datafellows & SSH_OLD_GSSAPI) return NULL; + + gss_indicate_mechs(&min_status, &supported); + + buffer_init(&buf); + + do { + if ((maj_status=gss_test_oid_set_member(&min_status, + &supported_mechs[i].oid, + supported, + &present))) { + present=0; + } + if (present) { + if ((server && + !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, + &supported_mechs[i].oid)))) + || (!server && + !GSS_ERROR(ssh_gssapi_client_ctx(&ctx, + &supported_mechs[i].oid, + host)))) { + /* Append gss_group1_sha1_x to our list */ + buffer_append(&buf, gssprefix, + strlen(gssprefix)); + buffer_append(&buf, + supported_mechs[i].enc_name, + strlen(supported_mechs[i].enc_name)); + } + } + } while (supported_mechs[++i].name != 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); +} + +void ssh_gssapi_supported_oids(gss_OID_set *oidset) { + enum ssh_gss_id i =0; + OM_uint32 maj_status,min_status; + int present; + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status,oidset); + gss_indicate_mechs(&min_status, &supported); + + 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) { + gss_add_oid_set_member(&min_status, + &supported_mechs[i].oid, + oidset); + } + i++; + } +} + +/* Set the contexts OID from a data stream */ +void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) { + if (ctx->oid != GSS_C_NO_OID) { + xfree(ctx->oid->elements); + xfree(ctx->oid); + } + ctx->oid=xmalloc(sizeof(gss_OID_desc)); + ctx->oid->length=len; + ctx->oid->elements=xmalloc(len); + memcpy(ctx->oid->elements,data,len); +} + +/* Set the contexts OID */ +void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) { + ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length); +} + +/* Find out which GSS type (out of the list we define in ssh-gss.h) a + * particular connection is using + */ +enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt) { + enum ssh_gss_id i=0; + + while(supported_mechs[i].name!=NULL && + supported_mechs[i].oid.length != ctxt->oid->length && + (memcmp(supported_mechs[i].oid.elements, + ctxt->oid->elements,ctxt->oid->length) !=0)) { + i++; + } + return(i); +} + +/* Set the GSS context's OID to the oid indicated by the given key exchange + * name. */ +gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name) { + enum ssh_gss_id i=0; + + if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) { + return(NULL); + } + + name+=strlen(gssprefix); /* 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); + + if (ctx) ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid); + + return &supported_mechs[i].oid; +} + + +/* All this effort to report an error ... + * ... and with privsep, how on earth do we get it back to the client? */ + +void +ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status) { + OM_uint32 lmaj, lmin; + gss_buffer_desc msg; + OM_uint32 ctx; + + ctx = 0; + /* The GSSAPI error */ + do { + lmaj = gss_display_status(&lmin, major_status, + GSS_C_GSS_CODE, + GSS_C_NULL_OID, + &ctx, &msg); + if (lmaj == GSS_S_COMPLETE) { + debug((char *)msg.value); + (void) gss_release_buffer(&lmin, &msg); + } + } while (ctx!=0); + + /* The mechanism specific error */ + do { + lmaj = gss_display_status(&lmin, minor_status, + GSS_C_MECH_CODE, + GSS_C_NULL_OID, + &ctx, &msg); + if (lmaj == GSS_S_COMPLETE) { + debug((char *)msg.value); + (void) gss_release_buffer(&lmin, &msg); + } + } while (ctx!=0); +} + +/* Initialise our GSSAPI context. We use this opaque structure to contain all + * of the data which both the client and server need to persist across + * {accept,init}_sec_context calls, so that when we do it from the userauth + * stuff life is a little easier + */ +void +ssh_gssapi_build_ctx(Gssctxt **ctx) +{ + *ctx=xmalloc(sizeof (Gssctxt)); + (*ctx)->context=GSS_C_NO_CONTEXT; + (*ctx)->name=GSS_C_NO_NAME; + (*ctx)->oid=GSS_C_NO_OID; + (*ctx)->creds=GSS_C_NO_CREDENTIAL; + (*ctx)->client=GSS_C_NO_NAME; + (*ctx)->client_creds=GSS_C_NO_CREDENTIAL; +} + +/* Delete our context, providing it has been built correctly */ +void +ssh_gssapi_delete_ctx(Gssctxt **ctx) +{ + OM_uint32 ms; + + /* Return if there's no context */ + if ((*ctx)==NULL) + return; + + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ms,&(*ctx)->context,GSS_C_NO_BUFFER); + if ((*ctx)->name != GSS_C_NO_NAME) + gss_release_name(&ms,&(*ctx)->name); + if ((*ctx)->oid != GSS_C_NO_OID) { + xfree((*ctx)->oid->elements); + xfree((*ctx)->oid); + (*ctx)->oid = GSS_C_NO_OID; + } + if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms,&(*ctx)->creds); + if ((*ctx)->client != GSS_C_NO_NAME) + gss_release_name(&ms,&(*ctx)->client); + if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms,&(*ctx)->client_creds); + + xfree(*ctx); + *ctx=NULL; +} + +/* Wrapper to init_sec_context + * Requires that the context contains: + * oid + * server name (from ssh_gssapi_import_name) + */ +OM_uint32 +ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, + gss_buffer_desc* send_tok, OM_uint32 *flags) +{ + OM_uint32 maj_status, min_status; + int deleg_flag = 0; + + if (deleg_creds) { + deleg_flag=GSS_C_DELEG_FLAG; + debug("Delegating credentials"); + } + + maj_status=gss_init_sec_context(&min_status, + GSS_C_NO_CREDENTIAL, /* def. cred */ + &ctx->context, + ctx->name, + ctx->oid, + GSS_C_MUTUAL_FLAG | + GSS_C_INTEG_FLAG | + deleg_flag, + 0, /* default lifetime */ + NULL, /* no channel bindings */ + recv_tok, + NULL, + send_tok, + flags, + NULL); + ctx->status=maj_status; + if (GSS_ERROR(maj_status)) { + ssh_gssapi_error(maj_status,min_status); + } + return(maj_status); +} + +/* Wrapper arround accept_sec_context + * Requires that the context contains: + * oid + * credentials (from ssh_gssapi_acquire_cred) + */ +OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok, + gss_buffer_desc *send_tok, OM_uint32 *flags) +{ + OM_uint32 maj_status, min_status; + gss_OID mech; + + maj_status=gss_accept_sec_context(&min_status, + &ctx->context, + ctx->creds, + recv_tok, + GSS_C_NO_CHANNEL_BINDINGS, + &ctx->client, + &mech, + send_tok, + flags, + NULL, + &ctx->client_creds); + if (GSS_ERROR(maj_status)) { + ssh_gssapi_error(maj_status,min_status); + } + + if (ctx->client_creds) { + debug("Received some client credentials"); + } else { + debug("Got no client credentials"); + } + + /* FIXME: We should check that the me + * the one that we asked for (in ctx->oid) */ + + ctx->status=maj_status; + + /* Now, if we're complete and we have the right flags, then + * we flag the user as also having been authenticated + */ + + if (((flags==NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && + (*flags & GSS_C_INTEG_FLAG))) && + (maj_status == GSS_S_COMPLETE)) { + if (ssh_gssapi_getclient(ctx,&gssapi_client_type, + &gssapi_client_name, + &gssapi_client_creds)) + fatal("Couldn't convert client name"); + } + + return(maj_status); +} + +/* Create a service name for the given host */ +OM_uint32 +ssh_gssapi_import_name(Gssctxt *ctx, const char *host) { + gss_buffer_desc gssbuf; + OM_uint32 maj_status, min_status; + struct hostent *hostinfo = NULL; + char *xhost; + + /* Make a copy of the host name, in case it was returned by a + * previous call to gethostbyname(). */ + xhost = xstrdup(host); + + /* Make sure we have the FQDN. Some GSSAPI implementations don't do + * this for us themselves */ + + hostinfo = gethostbyname(xhost); + + if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) { + debug("Unable to get FQDN for \"%s\"", xhost); + } else { + xfree(xhost); + xhost = xstrdup(hostinfo->h_name); + } + + gssbuf.length = sizeof("host@")+strlen(xhost); + + gssbuf.value = xmalloc(gssbuf.length); + if (gssbuf.value == NULL) { + xfree(xhost); + return(-1); + } + snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost); + if ((maj_status=gss_import_name(&min_status, + &gssbuf, + GSS_C_NT_HOSTBASED_SERVICE, + &ctx->name))) { + ssh_gssapi_error(maj_status,min_status); + } + + xfree(xhost); + xfree(gssbuf.value); + return(maj_status); +} + +/* Acquire credentials for a server running on the current host. + * Requires that the context structure contains a valid OID + */ + +/* Returns a GSSAPI error code */ +OM_uint32 +ssh_gssapi_acquire_cred(Gssctxt *ctx) { + OM_uint32 maj_status, min_status; + char lname[MAXHOSTNAMELEN]; + gss_OID_set oidset; + + gss_create_empty_oid_set(&min_status,&oidset); + gss_add_oid_set_member(&min_status,ctx->oid,&oidset); + + if (gethostname(lname, MAXHOSTNAMELEN)) { + return(-1); + } + + if ((maj_status=ssh_gssapi_import_name(ctx,lname))) { + return(maj_status); + } + if ((maj_status=gss_acquire_cred(&min_status, + ctx->name, + 0, + oidset, + GSS_C_ACCEPT, + &ctx->creds, + NULL, + NULL))) { + ssh_gssapi_error(maj_status,min_status); + } + + gss_release_oid_set(&min_status, &oidset); + return(maj_status); +} + +/* Extract the client details from a given context. This can only reliably + * be called once for a context */ + +OM_uint32 +ssh_gssapi_getclient(Gssctxt *ctx, enum ssh_gss_id *type, + gss_buffer_desc *name, gss_cred_id_t *creds) { + + OM_uint32 maj_status,min_status; + + *type=ssh_gssapi_get_ctype(ctx); + if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) { + ssh_gssapi_error(maj_status,min_status); + } + + /* This is icky. There appears to be no way to copy this structure, + * rather than the pointer to it, so we simply copy the pointer and + * mark the originator as empty so we don't destroy it. + */ + *creds=ctx->client_creds; + ctx->client_creds=GSS_C_NO_CREDENTIAL; + return(maj_status); +} + +OM_uint32 +ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) { + OM_uint32 maj_status,min_status; + + if ((maj_status=gss_get_mic(&min_status,ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) { + ssh_gssapi_error(maj_status,min_status); + } + + return(maj_status); +} + +OM_uint32 +ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid) { + if (*ctx) ssh_gssapi_delete_ctx(ctx); + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx,oid); + return(ssh_gssapi_acquire_cred(*ctx)); +} + +OM_uint32 +ssh_gssapi_client_ctx(Gssctxt **ctx,gss_OID oid, char *host) { + gss_buffer_desc token; + OM_uint32 major,minor; + + if (*ctx) ssh_gssapi_delete_ctx(ctx); + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx,oid); + ssh_gssapi_import_name(*ctx,host); + major=ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, NULL); + gss_release_buffer(&minor,&token); + return(major); +} + +#endif /* GSSAPI */ diff -u -N --exclude configure --exclude config.h.in src.old/gss-serv.c src/gss-serv.c --- src.old/gss-serv.c Wed Dec 31 18:00:00 1969 +++ src/gss-serv.c Sat Jan 11 17:08:15 2003 @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2001,2002 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 "ssh.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "packet.h" +#include "compat.h" +#include +#include "cipher.h" +#include "kex.h" +#include "auth.h" +#include "log.h" +#include "channels.h" +#include "session.h" +#include "dispatch.h" +#include "servconf.h" +#include "compat.h" +#include "monitor_wrap.h" + +#include "ssh-gss.h" + +extern ServerOptions options; +extern u_char *session_id2; +extern int session_id2_len; + +typedef struct ssh_gssapi_cred_cache { + char *filename; + char *envvar; + char *envval; + void *data; +} ssh_gssapi_cred_cache; + +static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL}; + +#ifdef KRB5 + +#ifdef HEIMDAL +#include +#else +#include +#define krb5_get_err_text(context,code) error_message(code) +#endif + +static krb5_context krb_context = NULL; + +/* Initialise the krb5 library, so we can use it for those bits that + * GSSAPI won't do */ + +int ssh_gssapi_krb5_init() { + krb5_error_code problem; + + if (krb_context !=NULL) + return 1; + + problem = krb5_init_context(&krb_context); + if (problem) { + log("Cannot initialize krb5 context"); + return 0; + } + krb5_init_ets(krb_context); + + return 1; +} + +/* Check if this user is OK to login. This only works with krb5 - other + * GSSAPI mechanisms will need their own. + * Returns true if the user is OK to log in, otherwise returns 0 + */ + +int +ssh_gssapi_krb5_userok(char *name) { + krb5_principal princ; + int retval; + + if (ssh_gssapi_krb5_init() == 0) + return 0; + + if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value, + &princ))) { + log("krb5_parse_name(): %.100s", + krb5_get_err_text(krb_context,retval)); + return 0; + } + if (krb5_kuserok(krb_context, princ, name)) { + retval = 1; + log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name, + (char *)gssapi_client_name.value); + } + else + retval = 0; + + krb5_free_principal(krb_context, princ); + return retval; +} + +/* Make sure that this is called _after_ we've setuid to the user */ + +/* This writes out any forwarded credentials. Its specific to the Kerberos + * GSSAPI mechanism + * + * We assume that our caller has made sure that the user has selected + * delegated credentials, and that the client_creds structure is correctly + * populated. + */ + +void +ssh_gssapi_krb5_storecreds() { + krb5_ccache ccache; + krb5_error_code problem; + krb5_principal princ; + char ccname[35]; + static char name[40]; + int tmpfd; + OM_uint32 maj_status,min_status; + + + if (gssapi_client_creds==NULL) { + debug("No credentials stored"); + return; + } + + if (ssh_gssapi_krb5_init() == 0) + return; + + if (options.gss_use_session_ccache) { + snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid()); + + if ((tmpfd = mkstemp(ccname))==-1) { + log("mkstemp(): %.100s", strerror(errno)); + return; + } + if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) { + log("fchmod(): %.100s", strerror(errno)); + close(tmpfd); + return; + } + } else { + snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid()); + tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (tmpfd == -1) { + log("open(): %.100s", strerror(errno)); + return; + } + } + + close(tmpfd); + snprintf(name, sizeof(name), "FILE:%s",ccname); + + if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) { + log("krb5_cc_default(): %.100s", + krb5_get_err_text(krb_context,problem)); + return; + } + + if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value, + &princ))) { + log("krb5_parse_name(): %.100s", + krb5_get_err_text(krb_context,problem)); + krb5_cc_destroy(krb_context,ccache); + return; + } + + if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { + log("krb5_cc_initialize(): %.100s", + krb5_get_err_text(krb_context,problem)); + krb5_free_principal(krb_context,princ); + krb5_cc_destroy(krb_context,ccache); + return; + } + + krb5_free_principal(krb_context,princ); + + #ifdef HEIMDAL + if ((problem = krb5_cc_copy_cache(krb_context, + gssapi_client_creds->ccache, + ccache))) { + log("krb5_cc_copy_cache(): %.100s", + krb5_get_err_text(krb_context,problem)); + krb5_cc_destroy(krb_context,ccache); + return; + } + #else + if ((maj_status = gss_krb5_copy_ccache(&min_status, + gssapi_client_creds, + ccache))) { + log("gss_krb5_copy_ccache() failed"); + ssh_gssapi_error(maj_status,min_status); + krb5_cc_destroy(krb_context,ccache); + return; + } + #endif + + krb5_cc_close(krb_context,ccache); + + +#ifdef USE_PAM + do_pam_putenv("KRB5CCNAME",name); +#endif + + gssapi_cred_store.filename=strdup(ccname); + gssapi_cred_store.envvar="KRB5CCNAME"; + gssapi_cred_store.envval=strdup(name); + + return; +} + +#endif /* KRB5 */ + +#ifdef GSI +#include + +/* + * Check if this user is OK to login under GSI. User has been authenticated + * as identity in global 'client_name.value' and is trying to log in as passed + * username in 'name'. + * + * Returns non-zero if user is authorized, 0 otherwise. + */ +int +ssh_gssapi_gsi_userok(char *name) +{ + int authorized = 0; + + /* This returns 0 on success */ + authorized = (globus_gss_assist_userok(gssapi_client_name.value, + name) == 0); + + debug("GSI user %s is%s authorized as target user %s", + (char *) gssapi_client_name.value, + (authorized ? "" : " not"), + name); + + return authorized; +} + +/* + * Handle setting up child environment for GSI. + * + * Make sure that this is called _after_ we've setuid to the user. + */ +void +ssh_gssapi_gsi_storecreds() +{ + OM_uint32 major_status; + OM_uint32 minor_status; + + + if (gssapi_client_creds != NULL) + { + char *creds_env = NULL; + + /* + * This is the current hack with the GSI gssapi library to + * export credentials to disk. + */ + + debug("Exporting delegated credentials"); + + minor_status = 0xdee0; /* Magic value */ + major_status = + gss_inquire_cred(&minor_status, + gssapi_client_creds, + (gss_name_t *) &creds_env, + NULL, + NULL, + NULL); + + if ((major_status == GSS_S_COMPLETE) && + (minor_status == 0xdee1) && + (creds_env != NULL)) + { + char *value; + + /* + * String is of the form: + * X509_USER_DELEG_PROXY=filename + * so we parse out the filename + * and then set X509_USER_PROXY + * to point at it. + */ + value = strchr(creds_env, '='); + + if (value != NULL) + { + *value = '\0'; + value++; +#ifdef USE_PAM + do_pam_putenv("X509_USER_PROXY",value); +#endif + gssapi_cred_store.filename=NULL; + gssapi_cred_store.envvar="X509_USER_PROXY"; + gssapi_cred_store.envval=strdup(value); + + return; + } + else + { + log("Failed to parse delegated credentials string '%s'", + creds_env); + } + } + else + { + log("Failed to export delegated credentials (error %ld)", + major_status); + } + } +} + +#endif /* GSI */ + +void +ssh_gssapi_cleanup_creds(void *ignored) +{ + if (gssapi_cred_store.filename!=NULL) { + /* Unlink probably isn't sufficient */ + debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename); + unlink(gssapi_cred_store.filename); + } +} + +void +ssh_gssapi_storecreds() +{ + switch (gssapi_client_type) { +#ifdef KRB5 + case GSS_KERBEROS: + ssh_gssapi_krb5_storecreds(); + break; +#endif +#ifdef GSI + case GSS_GSI: + ssh_gssapi_gsi_storecreds(); + break; +#endif /* GSI */ + case GSS_LAST_ENTRY: + /* GSSAPI not used in this authentication */ + debug("No GSSAPI credentials stored"); + break; + default: + log("ssh_gssapi_do_child: Unknown mechanism"); + + } + + if (options.gss_cleanup_creds) { + fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL); + } + +} + +/* This allows GSSAPI methods to do things to the childs environment based + * on the passed authentication process and credentials. + * + * Question: If we didn't use userauth_external for some reason, should we + * still delegate credentials? + */ +void +ssh_gssapi_do_child(char ***envp, u_int *envsizep) +{ + + if (gssapi_cred_store.envvar!=NULL && + gssapi_cred_store.envval!=NULL) { + + debug("Setting %s to %s", gssapi_cred_store.envvar, + gssapi_cred_store.envval); + child_set_env(envp, envsizep, gssapi_cred_store.envvar, + gssapi_cred_store.envval); + } + + switch(gssapi_client_type) { +#ifdef KRB5 + case GSS_KERBEROS: break; +#endif +#ifdef GSI + case GSS_GSI: break; +#endif + case GSS_LAST_ENTRY: + debug("No GSSAPI credentials stored"); + break; + default: + log("ssh_gssapi_do_child: Unknown mechanism"); + } +} + +int +ssh_gssapi_userok(char *user) +{ + if (gssapi_client_name.length==0 || + gssapi_client_name.value==NULL) { + debug("No suitable client data"); + return 0; + } + switch (gssapi_client_type) { +#ifdef KRB5 + case GSS_KERBEROS: + return(ssh_gssapi_krb5_userok(user)); + break; /* Not reached */ +#endif +#ifdef GSI + case GSS_GSI: + return(ssh_gssapi_gsi_userok(user)); + break; /* Not reached */ +#endif /* GSI */ + case GSS_LAST_ENTRY: + debug("Client not GSSAPI"); + break; + default: + debug("Unknown client authentication type"); + } + return(0); +} + +/* Stuff to play nicely with privsep */ + +#if 0 +extern struct monitor *pmonitor; + +OM_uint32 +mm_ssh_gssapi_server_ctxt(Gssctxt **ctx, gss_OID oid) { + Buffer m; + + /* Client doesn't get to see the context */ + *ctx=NULL; + + buffer_init(&m) + buffer_put_string(&m,oid->elements,oid->length); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m); + + debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m); + major=buffer_get_int(&m); + + return(major); +} + +int +mm_answer_gss_server_ctxt(int socket, Buffer *m) { + gss_OID_desc oid; + OM_uint32 major; + + oid.elements=buffer_get_string(m,&oid.length); + + major=ssh_gssapi_server_ctxt(&gsscontext,&oid); + + xfree(oid.elements); + + buffer_clear(m); + buffer_put_int(m,result); + + mm_request_send(socket,MONITOR_ANS_GSSSIGN,m); + + return(0); +} + +OM_uint32 +mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in, + gss_buffer_desc *out, OM_uint32 *flags) { + + Buffer m; + OM_uint32, major; + + buffer_init(&m); + buffer_put_string(&m, in->value, in->length); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m); + + debug3("%s: waiting for MONITOR_ANS_GSSSTEP", &m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m); + + major=buffer_get_int(&m); + *out->value=buffer->get_string(&m,&out->length); + flags=buffer_get_int(&m); + + return(major); +} + +int +mm_answer_gss_accept_ctxt(int socket, Buffer *m) { + gss_buffer_desc in,out; + OM_uint32 major; + OM_uint32 flags = 0; /* GSI needs this */ + + in.value = buffer_get_string(m,&in.length); + major=ssh_gssapi_accept_ctxt(gsscontext,&in,&out,&flags); + xfree(in.value); + + buffer_clear(m); + buffer_put_int(m, major); + buffer_put_string(m, out.value, out.length); + buffer_put_int(m, flags); + mm_request_send(socket,MONITOR_ANS_STEP,m); + + gss_release_buffer(out); + + return(0); +} + +int +mm_ssh_gssapi_userok(char *user) { + Buffer m; + + buffer_init(&m); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m); + + debug3("%s: waiting for MONTIOR_ANS_GSSUSEROK", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK), + &m); + + authenticated = buffer_get_int(&m); + + buffer_free(&m); + debug3("%s: user %sauthetnicated",__func__, authenticated ? "" : "not "); + return(authenticated); +} + +int +mm_answer_gss_userok(int socket, Buffer *m) { + authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); + + buffer_clear(m); + buffer_put_int(m, authenticated); + + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(socket, MONITOR_ANS_GSSUSEROK, m); + + /* Monitor loop will terminate if authenticated */ + return(authenticated); +} + +OM_uint32 +mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) { + Buffer m; + OM_uint32 major, minor; + + buffer_init(&m); + buffer_put_string(&m, data->value, data->length); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); + + debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); + major=buffer_get_int(&m); + *hash->value = buffer_get_string(&m, &hash->length); + + return(major); +} + +int +mm_answer_gss_sign(int socket, Buffer *m) { + gss_buffer_desc data,hash; + OM_uint32 major; + + data.value = buffer_get_string(m,&data.length); + if (data.length != 20) + fatal("%s: data length incorrect: %d", __func__, datlen); + + /* 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(hash); + + return(0); +} +#endif +#endif /* GSSAPI */ diff -u -N --exclude configure --exclude config.h.in src.old/kex.c src/kex.c --- src.old/kex.c Sat Jan 11 17:08:22 2003 +++ src/kex.c Sat Jan 11 17:08:16 2003 @@ -42,6 +42,10 @@ #include "dispatch.h" #include "monitor.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + #define KEX_COOKIE_LEN 16 /* Use privilege separation for sshd */ @@ -242,6 +246,11 @@ case DH_GEX_SHA1: kexgex(kex); break; +#ifdef GSSAPI + case GSS_GRP1_SHA1: + kexgss(kex); + break; +#endif default: fatal("Unsupported key exchange %d", kex->kex_type); } @@ -297,11 +306,15 @@ { k->name = match_list(client, server, NULL); if (k->name == NULL) - fatal("no kex alg"); + fatal("No key exchange algorithm"); if (strcmp(k->name, KEX_DH1) == 0) { k->kex_type = DH_GRP1_SHA1; } else if (strcmp(k->name, KEX_DHGEX) == 0) { k->kex_type = DH_GEX_SHA1; +#ifdef GSSAPI + } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) { + k->kex_type = GSS_GRP1_SHA1; +#endif } else fatal("bad kex alg %s", k->name); } diff -u -N --exclude configure --exclude config.h.in src.old/kex.h src/kex.h --- src.old/kex.h Sat Jan 11 17:08:23 2003 +++ src/kex.h Sat Jan 11 17:08:16 2003 @@ -56,7 +56,8 @@ enum kex_exchange { DH_GRP1_SHA1, - DH_GEX_SHA1 + DH_GEX_SHA1, + GSS_GRP1_SHA1 }; #define KEX_INIT_SENT 0x0001 @@ -94,6 +95,11 @@ Mac mac; Comp comp; }; + +struct KexOptions { + int gss_deleg_creds; +}; + struct Kex { u_char *session_id; u_int session_id_len; @@ -107,11 +113,13 @@ Buffer peer; int done; int flags; + char *host; char *client_version_string; char *server_version_string; int (*verify_host_key)(Key *); Key *(*load_host_key)(int); int (*host_key_index)(Key *); + struct KexOptions options; }; Kex *kex_setup(char *[PROPOSAL_MAX]); @@ -123,6 +131,9 @@ void kexdh(Kex *); void kexgex(Kex *); +#ifdef GSSAPI +void kexgss(Kex *); +#endif Newkeys *kex_get_newkeys(int); diff -u -N --exclude configure --exclude config.h.in src.old/kexgss.c src/kexgss.c --- src.old/kexgss.c Wed Dec 31 18:00:00 1969 +++ src/kexgss.c Sat Jan 11 17:08:16 2003 @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2001,2002 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 +#include + +#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" + +/* This is now the same as the DH hash ... */ + +u_char * +kex_gssapi_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + u_char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static u_char digest[EVP_MAX_MD_SIZE]; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + + buffer_init(&b); + buffer_put_string(&b, client_version_string, strlen(client_version_string)); + buffer_put_string(&b, server_version_string, strlen(server_version_string)); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEX + buffer_dump(&b); +#endif + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEX + dump_digest("hash", digest, evp_md->md_size); +#endif + return digest; +} + +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; + int type = 0; + int first = 1; + int slen = 0; + + /* Initialise our GSSAPI world */ + ssh_gssapi_build_ctx(&ctxt); + if (ssh_gssapi_id_kex(ctxt,kex->name)==NULL) { + fatal("Couldn't identify host exchange"); + } + if (ssh_gssapi_import_name(ctxt,kex->host)) { + 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)) { + 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. */ + + type = packet_read(); + switch (type) { + case SSH2_MSG_KEXGSS_HOSTKEY: + debug("Received KEXGSS_HOSTKEY"); + serverhostkey=packet_get_string(&slen); + break; + case SSH2_MSG_KEXGSS_CONTINUE: + debug("Received GSSAPI_CONTINUE"); + if (maj_status == GSS_S_COMPLETE) + fatal("GSSAPI Continue received from server when complete"); + recv_tok.value=packet_get_string(&recv_tok.length); + break; + case SSH2_MSG_KEXGSS_COMPLETE: + debug("Received GSSAPI_COMPLETE"); + packet_get_bignum2(dh_server_pub); + msg_tok.value= + packet_get_string(&msg_tok.length); + + /* Is there a token included? */ + if (packet_get_char()) { + recv_tok.value= + packet_get_string(&recv_tok.length); + /* 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; + default: + packet_disconnect("Protocol error: didn't expect packet type %d", + type); + } + token_ptr=&recv_tok; + } + + } 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); + + hash = kex_gssapi_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); +} + + + + +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; + gss_OID oid; + + /* Initialise GSSAPI */ + + debug2("%s: Identifying %s",__func__,kex->name); + oid=ssh_gssapi_id_kex(ctxt,kex->name); + if (oid==NULL) { + fatal("Unknown gssapi mechanism"); + } + + debug2("%s: Acquiring credentials",__func__); + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid)))) + 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(&recv_tok.length); + + 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: + if (dh_client_pub == NULL) + fatal("Received KEXGSS_CONTINUE without initialising"); + recv_tok.value=packet_get_string(&recv_tok.length); + 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_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)) + fatal("gss_accept_context 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); + + hash = kex_gssapi_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)))) + 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); +} + +void +kexgss(Kex *kex) +{ + if (kex->server) + kexgss_server(kex); + else + kexgss_client(kex); +} + +#endif /* GSSAPI */ diff -u -N --exclude configure --exclude config.h.in src.old/key.c src/key.c --- src.old/key.c Sat Jan 11 17:08:23 2003 +++ src/key.c Sat Jan 11 17:08:16 2003 @@ -648,6 +648,8 @@ 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 -N --exclude configure --exclude config.h.in src.old/key.h src/key.h --- src.old/key.h Sat Jan 11 17:08:23 2003 +++ src/key.h Sat Jan 11 17:08:16 2003 @@ -34,6 +34,7 @@ KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_NULL, KEY_UNSPEC }; enum fp_type { diff -u -N --exclude configure --exclude config.h.in src.old/log.c src/log.c --- src.old/log.c Sat Jan 11 17:08:23 2003 +++ src/log.c Sat Jan 11 17:08:17 2003 @@ -387,7 +387,7 @@ vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); } if (log_on_stderr) { - fprintf(stderr, "%s\r\n", msgbuf); + fprintf(stderr, "%d: %s\r\n", getpid(), msgbuf); } else { openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); syslog(pri, "%.500s", msgbuf); diff -u -N --exclude configure --exclude config.h.in src.old/makegssname.pl src/makegssname.pl --- src.old/makegssname.pl Wed Dec 31 18:00:00 1969 +++ src/makegssname.pl Sat Jan 11 17:08:17 2003 @@ -0,0 +1,46 @@ +#!/usr/bin/perl + +use Convert::ASN1 qw(:tag); +use Digest::MD5 qw(md5); +use MIME::Base64; + +$oid=shift; +$encoded=encode_object_id($oid); + +@entries=unpack("C*",$encoded); +shift @entries; # Get rid of the NULL + +print "DER representation: "; +foreach $entry (@entries) { + print "\\x"; + printf "%02X",$entry; +} +print "\n"; + +$digest = md5($encoded); +# We only want the first 10 characters; +# Conversations with the authors suggest that we want to use all of the +# characters of the digest. +#$digest = substr($digest,0,10); +print "gsskeyex representation: ",encode_base64($digest),"\n"; + +sub encode_object_id { + $string=""; + + my @data = ($_[0] =~ /(\d+)/g); + + if(@data < 2) { + @data = (0); + } + else { + my $first = $data[1] + ($data[0] * 40); + splice(@data,0,2,$first); + } + +# my $l = length $string; + $string .= pack("cw*", 0, @data); +# substr($string,$l,1) = asn_encode_length(length($string) - $l - 1); + return $string; +} + + diff -u -N --exclude configure --exclude config.h.in src.old/monitor.c src/monitor.c --- src.old/monitor.c Sat Jan 11 17:08:24 2003 +++ src/monitor.c Sat Jan 11 17:08:17 2003 @@ -59,6 +59,11 @@ #include "ssh2.h" #include "mpaux.h" +#ifdef GSSAPI +#include "ssh-gss.h" +static Gssctxt *gsscontext = NULL; +#endif + /* Imports */ extern ServerOptions options; extern u_int utmp_len; @@ -127,6 +132,13 @@ int mm_answer_krb5(int, Buffer *); #endif +#ifdef GSSAPI +int mm_answer_gss_setup_ctx(int, Buffer *); +int mm_answer_gss_accept_ctx(int, Buffer *); +int mm_answer_gss_userok(int, Buffer *); +int mm_answer_gss_sign(int, Buffer *); +#endif + static Authctxt *authctxt; static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */ @@ -172,12 +184,23 @@ {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, #endif +#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_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, +#endif {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, {0, 0, NULL} }; 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}, +#endif {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, {MONITOR_REQ_SIGN, 0, mm_answer_sign}, {MONITOR_REQ_PTY, 0, mm_answer_pty}, @@ -267,6 +290,12 @@ /* 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_GSSSTEP, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); +#endif } else { mon_dispatch = mon_dispatch_proto15; @@ -1638,3 +1667,96 @@ mon->m_recvfd = pair[0]; mon->m_sendfd = pair[1]; } + +#ifdef GSSAPI + +int +mm_answer_gss_setup_ctx(int socket, Buffer *m) { + gss_OID_desc oid; + OM_uint32 major; + + oid.elements=buffer_get_string(m,&oid.length); + + major=ssh_gssapi_server_ctx(&gsscontext,&oid); + + xfree(oid.elements); + + buffer_clear(m); + buffer_put_int(m,major); + + mm_request_send(socket,MONITOR_ANS_GSSSETUP,m); + + return(0); +} + +int +mm_answer_gss_accept_ctx(int socket, Buffer *m) { + gss_buffer_desc in,out; + OM_uint32 major,minor; + OM_uint32 flags = 0; /* GSI needs this */ + + in.value = buffer_get_string(m,&in.length); + major=ssh_gssapi_accept_ctx(gsscontext,&in,&out,&flags); + xfree(in.value); + + buffer_clear(m); + buffer_put_int(m, major); + buffer_put_string(m, out.value, out.length); + buffer_put_int(m, flags); + mm_request_send(socket,MONITOR_ANS_GSSSTEP,m); + + gss_release_buffer(&minor, &out); + + return(0); +} +int +mm_answer_gss_userok(int socket, Buffer *m) { + int authenticated; + + authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); + + buffer_clear(m); + buffer_put_int(m, authenticated); + + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(socket, MONITOR_ANS_GSSUSEROK, m); + + auth_method="gssapi"; + + /* Monitor loop will terminate if authenticated */ + return(authenticated); +} +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); +} + +#endif /* GSSAPI */ diff -u -N --exclude configure --exclude config.h.in src.old/monitor.h src/monitor.h --- src.old/monitor.h Sat Jan 11 17:08:24 2003 +++ src/monitor.h Sat Jan 11 17:08:17 2003 @@ -39,6 +39,10 @@ MONITOR_REQ_BSDAUTHRESPOND, MONITOR_ANS_BSDAUTHRESPOND, MONITOR_REQ_SKEYQUERY, MONITOR_ANS_SKEYQUERY, MONITOR_REQ_SKEYRESPOND, MONITOR_ANS_SKEYRESPOND, + MONITOR_REQ_GSSSETUP,MONITOR_ANS_GSSSETUP, + MONITOR_REQ_GSSSTEP,MONITOR_ANS_GSSSTEP, + MONITOR_REQ_GSSSIGN,MONITOR_ANS_GSSSIGN, + MONITOR_REQ_GSSUSEROK,MONITOR_ANS_GSSUSEROK, MONITOR_REQ_KEYALLOWED, MONITOR_ANS_KEYALLOWED, MONITOR_REQ_KEYVERIFY, MONITOR_ANS_KEYVERIFY, MONITOR_REQ_KEYEXPORT, diff -u -N --exclude configure --exclude config.h.in src.old/monitor_wrap.c src/monitor_wrap.c --- src.old/monitor_wrap.c Sat Jan 11 17:08:24 2003 +++ src/monitor_wrap.c Sat Jan 11 17:08:18 2003 @@ -51,6 +51,10 @@ #include "channels.h" #include "session.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + /* Imports */ extern int compat20; extern Newkeys *newkeys[]; @@ -1007,3 +1011,82 @@ return (success); } #endif +#ifdef GSSAPI +OM_uint32 +mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) { + Buffer m; + OM_uint32 major; + + /* Client doesn't get to see the context */ + *ctx=NULL; + + buffer_init(&m); + buffer_put_string(&m,oid->elements,oid->length); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m); + + debug3("%s: waiting for MONITOR_ANS_GSSSETUP",__func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSETUP, &m); + major=buffer_get_int(&m); + + return(major); +} + +OM_uint32 +mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in, + gss_buffer_desc *out, OM_uint32 *flags) { + + Buffer m; + OM_uint32 major; + + buffer_init(&m); + buffer_put_string(&m, in->value, in->length); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m); + + debug3("%s: waiting for MONITOR_ANS_GSSSTEP", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m); + + major=buffer_get_int(&m); + out->value=buffer_get_string(&m,&out->length); + if (flags) *flags=buffer_get_int(&m); + + return(major); +} + +int +mm_ssh_gssapi_userok(char *user) { + Buffer m; + int authenticated = 0; + + buffer_init(&m); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m); + + debug3("%s: waiting for MONTIOR_ANS_GSSUSEROK", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK, + &m); + + authenticated = buffer_get_int(&m); + + buffer_free(&m); + 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); + + debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); + major=buffer_get_int(&m); + hash->value = buffer_get_string(&m, &hash->length); + + return(major); +} +#endif /* GSSAPI */ diff -u -N --exclude configure --exclude config.h.in src.old/monitor_wrap.h src/monitor_wrap.h --- src.old/monitor_wrap.h Sat Jan 11 17:08:24 2003 +++ src/monitor_wrap.h Sat Jan 11 17:08:18 2003 @@ -59,6 +59,16 @@ void mm_start_pam(char *); #endif +#ifdef GSSAPI +#include "ssh-gss.h" +OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **ctxt, gss_OID oid); +OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *ctxt, gss_buffer_desc *recv, + gss_buffer_desc *send, OM_uint32 *flags); +OM_uint32 mm_ssh_gssapi_sign(Gssctxt *ctxt, gss_buffer_desc *buffer, + gss_buffer_desc *hash); +int mm_ssh_gssapi_userok(char *user); +#endif + void mm_terminate(void); int mm_pty_allocate(int *, int *, char *, int); void mm_session_pty_cleanup2(void *); diff -u -N --exclude configure --exclude config.h.in src.old/readconf.c src/readconf.c --- src.old/readconf.c Sat Jan 11 17:08:25 2003 +++ src/readconf.c Sat Jan 11 17:08:18 2003 @@ -97,6 +97,12 @@ #if defined(KRB4) || defined(KRB5) oKerberosAuthentication, #endif +#ifdef GSSAPI + oGssAuthentication, oGssDelegateCreds, +#ifdef GSI + oGssGlobusDelegateLimitedCreds, +#endif /* GSI */ +#endif /* GSSAPI */ #if defined(AFS) || defined(KRB5) oKerberosTgtPassing, #endif @@ -143,6 +149,15 @@ #if defined(KRB4) || defined(KRB5) { "kerberosauthentication", oKerberosAuthentication }, #endif +#ifdef GSSAPI + { "gssapiauthentication", oGssAuthentication }, + { "gssapidelegatecredentials", oGssDelegateCreds }, +#ifdef GSI + /* For backwards compatability with old 1.2.27 client code */ + { "forwardgssapiglobusproxy", oGssDelegateCreds }, /* alias */ + { "forwardgssapiglobuslimitedproxy", oGssGlobusDelegateLimitedCreds }, +#endif /* GSI */ +#endif /* GSSAPI */ #if defined(AFS) || defined(KRB5) { "kerberostgtpassing", oKerberosTgtPassing }, #endif @@ -362,6 +377,19 @@ intptr = &options->kerberos_authentication; goto parse_flag; #endif +#ifdef GSSAPI + case oGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; +#ifdef GSI + case oGssGlobusDelegateLimitedCreds: + intptr = &options->gss_globus_deleg_limited_proxy; + goto parse_flag; +#endif /* GSI */ +#endif /* GSSAPI */ #if defined(AFS) || defined(KRB5) case oKerberosTgtPassing: intptr = &options->kerberos_tgt_passing; @@ -747,6 +775,13 @@ options->rsa_authentication = -1; options->pubkey_authentication = -1; options->challenge_response_authentication = -1; +#ifdef GSSAPI + options->gss_authentication = -1; + options->gss_deleg_creds = -1; +#ifdef GSI + options->gss_globus_deleg_limited_proxy = -1; +#endif /* GSI */ +#endif /* GSSAPI */ #if defined(KRB4) || defined(KRB5) options->kerberos_authentication = -1; #endif @@ -823,6 +858,16 @@ options->pubkey_authentication = 1; if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; +#ifdef GSSAPI + if (options->gss_authentication == -1) + options->gss_authentication = 1; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 1; +#ifdef GSI + if (options->gss_globus_deleg_limited_proxy == -1) + options->gss_globus_deleg_limited_proxy = 0; +#endif /* GSI */ +#endif /* GSSAPI */ #if defined(KRB4) || defined(KRB5) if (options->kerberos_authentication == -1) options->kerberos_authentication = 1; diff -u -N --exclude configure --exclude config.h.in src.old/readconf.h src/readconf.h --- src.old/readconf.h Sat Jan 11 17:08:25 2003 +++ src/readconf.h Sat Jan 11 17:08:18 2003 @@ -47,6 +47,15 @@ #if defined(AFS) || defined(KRB5) int kerberos_tgt_passing; /* Try Kerberos TGT passing. */ #endif + +#ifdef GSSAPI + int gss_authentication; + int gss_deleg_creds; +#ifdef GSI + int gss_globus_deleg_limited_proxy; +#endif /* GSI */ +#endif /* GSSAPI */ + #ifdef AFS int afs_token_passing; /* Try AFS token passing. */ #endif diff -u -N --exclude configure --exclude config.h.in src.old/servconf.c src/servconf.c --- src.old/servconf.c Sat Jan 11 17:08:25 2003 +++ src/servconf.c Sat Jan 11 17:08:18 2003 @@ -86,6 +86,12 @@ options->hostbased_uses_name_from_packet_only = -1; options->rsa_authentication = -1; options->pubkey_authentication = -1; +#ifdef GSSAPI + options->gss_authentication=-1; + options->gss_keyex=-1; + options->gss_use_session_ccache = -1; + options->gss_cleanup_creds = -1; +#endif #if defined(KRB4) || defined(KRB5) options->kerberos_authentication = -1; options->kerberos_or_local_passwd = -1; @@ -200,6 +206,16 @@ options->rsa_authentication = 1; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; +#ifdef GSSAPI + if (options->gss_authentication == -1) + options->gss_authentication = 1; + 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; +#endif #if defined(KRB4) || defined(KRB5) if (options->kerberos_authentication == -1) options->kerberos_authentication = 0; @@ -280,6 +296,9 @@ sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, sPermitRootLogin, sLogFacility, sLogLevel, sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, +#ifdef GSSAPI + sGssAuthentication, sGssKeyEx, sGssUseSessionCredCache, sGssCleanupCreds, +#endif #if defined(KRB4) || defined(KRB5) sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, #endif @@ -330,6 +349,13 @@ { "rsaauthentication", sRSAAuthentication }, { "pubkeyauthentication", sPubkeyAuthentication }, { "dsaauthentication", sPubkeyAuthentication }, /* alias */ +#ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication }, + { "gssapikeyexchange", sGssKeyEx }, + { "gssusesessionccache", sGssUseSessionCredCache }, + { "gssapiusesessioncredcache", sGssUseSessionCredCache }, + { "gssapicleanupcreds", sGssCleanupCreds }, +#endif #if defined(KRB4) || defined(KRB5) { "kerberosauthentication", sKerberosAuthentication }, { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, @@ -645,6 +671,20 @@ case sPubkeyAuthentication: intptr = &options->pubkey_authentication; goto parse_flag; +#ifdef GSSAPI + case sGssAuthentication: + intptr = &options->gss_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; +#endif #if defined(KRB4) || defined(KRB5) case sKerberosAuthentication: intptr = &options->kerberos_authentication; diff -u -N --exclude configure --exclude config.h.in src.old/servconf.h src/servconf.h --- src.old/servconf.h Sat Jan 11 17:08:25 2003 +++ src/servconf.h Sat Jan 11 17:08:19 2003 @@ -73,6 +73,13 @@ int hostbased_uses_name_from_packet_only; /* experimental */ int rsa_authentication; /* If true, permit RSA authentication. */ int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ +#ifdef GSSAPI + int gss_authentication; + 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 */ +#endif #if defined(KRB4) || defined(KRB5) int kerberos_authentication; /* If true, permit Kerberos * authentication. */ diff -u -N --exclude configure --exclude config.h.in src.old/session.c src/session.c --- src.old/session.c Sat Jan 11 17:08:25 2003 +++ src/session.c Sat Jan 11 17:08:19 2003 @@ -58,6 +58,10 @@ #include "session.h" #include "monitor_wrap.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + #ifdef HAVE_CYGWIN #include #include @@ -453,6 +457,12 @@ session_proctitle(s); +#if defined(GSSAPI) + temporarily_use_uid(s->pw); + ssh_gssapi_storecreds(); + restore_uid(); +#endif + #if defined(USE_PAM) do_pam_session(s->pw->pw_name, NULL); do_pam_setcred(1); @@ -580,6 +590,12 @@ ptyfd = s->ptyfd; ttyfd = s->ttyfd; +#if defined(GSSAPI) + temporarily_use_uid(s->pw); + ssh_gssapi_storecreds(); + restore_uid(); +#endif + #if defined(USE_PAM) do_pam_session(s->pw->pw_name, s->tty); do_pam_setcred(1); @@ -838,7 +854,7 @@ * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. */ -static void +void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { @@ -965,6 +981,13 @@ copy_environment(environ, &env, &envsize); #endif +#ifdef GSSAPI + /* Allow any GSSAPI methods that we've used to alter + * the childs environment as they see fit + */ + ssh_gssapi_do_child(&env,&envsize); +#endif + if (!options.use_login) { /* Set basic environment. */ child_set_env(&env, &envsize, "USER", pw->pw_name); @@ -2092,4 +2115,7 @@ do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); +#if defined(GSSAPI) + ssh_gssapi_cleanup_creds(NULL); +#endif } diff -u -N --exclude configure --exclude config.h.in src.old/session.h src/session.h --- src.old/session.h Sat Jan 11 17:08:25 2003 +++ src/session.h Sat Jan 11 17:08:19 2003 @@ -68,4 +68,7 @@ Session *session_by_tty(char *); void session_close(Session *); void do_setusercontext(struct passwd *); + +void child_set_env(char ***envp, u_int *envsizep, const char *name, + const char *value); #endif diff -u -N --exclude configure --exclude config.h.in src.old/ssh-gss.h src/ssh-gss.h --- src.old/ssh-gss.h Wed Dec 31 18:00:00 1969 +++ src/ssh-gss.h Sat Jan 11 17:08:19 2003 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2001,2002 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. + */ + +#ifndef _SSH_GSS_H +#define _SSH_GSS_H + +#ifdef GSSAPI + +#include "kex.h" +#include "buffer.h" + +#include + +#ifdef KRB5 +#ifndef HEIMDAL +#include + +/* MIT Kerberos doesn't seem to define GSS_NT_HOSTBASED_SERVICE */ + +#ifndef GSS_C_NT_HOSTBASED_SERVICE +#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +#endif /* GSS_C_NT_... */ +#endif /* !HEIMDAL */ +#endif /* KRB5 */ + +/* draft-ietf-secsh-gsskeyex-03 */ +#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 +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 + +#define KEX_GSS_SHA1 "gss-group1-sha1-" + +enum ssh_gss_id { +#ifdef KRB5 + GSS_KERBEROS, +#endif +#ifdef GSI + GSS_GSI, +#endif /* GSI */ + GSS_LAST_ENTRY +}; + +typedef struct ssh_gss_mech_struct { + char *enc_name; + char *name; + gss_OID_desc oid; +} ssh_gssapi_mech; + +typedef struct { + OM_uint32 status; /* both */ + gss_ctx_id_t context; /* both */ + gss_name_t name; /* both */ + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ + gss_cred_id_t client_creds; /* server */ +} Gssctxt; + +extern ssh_gssapi_mech supported_mechs[]; +extern gss_buffer_desc gssapi_client_name; +extern gss_cred_id_t gssapi_client_creds; +extern enum ssh_gss_id gssapi_client_type; + +char *ssh_gssapi_mechanisms(int server, char *host); +gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name); +void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len); +void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid); +void ssh_gssapi_supported_oids(gss_OID_set *oidset); +enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt); + +OM_uint32 ssh_gssapi_import_name(Gssctxt *ctx, const char *host); +OM_uint32 ssh_gssapi_acquire_cred(Gssctxt *ctx); +OM_uint32 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, + gss_buffer_desc *recv_tok, + gss_buffer_desc *send_tok, OM_uint32 *flags); +OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx, + gss_buffer_desc *recv_tok, + gss_buffer_desc *send_tok, + OM_uint32 *flags); +OM_uint32 ssh_gssapi_getclient(Gssctxt *ctx, + enum ssh_gss_id *type, + gss_buffer_desc *name, + gss_cred_id_t *creds); +void ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status); +void ssh_gssapi_build_ctx(Gssctxt **ctx); +void ssh_gssapi_delete_ctx(Gssctxt **ctx); +OM_uint32 ssh_gssapi_client_ctx(Gssctxt **ctx,gss_OID oid,char *host); +OM_uint32 ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid); + +/* In the client */ +void ssh_gssapi_client(Kex *kex, char *host, struct sockaddr *hostaddr, + Buffer *client_kexinit, Buffer *server_kexinit); + +/* In the server */ +int ssh_gssapi_userok(char *name); +void ssh_gssapi_server(Kex *kex, Buffer *client_kexinit, + Buffer *server_kexinit); + +OM_uint32 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, + gss_buffer_desc *hash); + +void ssh_gssapi_do_child(char ***envp, u_int *envsizep); +void ssh_gssapi_cleanup_creds(void *ignored); +void ssh_gssapi_storecreds(); +#endif /* GSSAPI */ + +#endif /* _SSH_GSS_H */ diff -u -N --exclude configure --exclude config.h.in src.old/sshconnect2.c src/sshconnect2.c --- src.old/sshconnect2.c Sat Jan 11 17:08:26 2003 +++ src/sshconnect2.c Sat Jan 11 17:08:19 2003 @@ -48,6 +48,10 @@ #include "msg.h" #include "pathnames.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + /* import */ extern char *client_version_string; extern char *server_version_string; @@ -77,10 +81,26 @@ 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_mechanisms(0,host); + if (gss) { + len = strlen(orig)+strlen(gss)+2; + myproposal[PROPOSAL_KEX_ALGS]=xmalloc(len); + snprintf(myproposal[PROPOSAL_KEX_ALGS],len,"%s,%s",gss,orig); + } +#endif + if (options.ciphers == (char *)-1) { log("No valid ciphers for protocol version 2 given, using defaults."); options.ciphers = NULL; @@ -108,11 +128,27 @@ 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 + /* start key exchange */ kex = kex_setup(myproposal); kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; + kex->host=host; + +#ifdef GSSAPI + kex->options.gss_deleg_creds=options.gss_deleg_creds; +#endif xxx_kex = kex; @@ -159,6 +195,8 @@ Sensitive *sensitive; /* kbd-interactive */ int info_req_seen; + /* generic */ + void *methoddata; }; struct Authmethod { char *name; /* string to compare against server's list */ @@ -181,6 +219,14 @@ int userauth_kbdint(Authctxt *); int userauth_hostbased(Authctxt *); +#ifdef GSSAPI +int userauth_external(Authctxt *authctxt); +int userauth_gssapi(Authctxt *authctxt); +void input_gssapi_response(int type, u_int32_t plen, void *ctxt); +void input_gssapi_token(int type, u_int32_t plen, void *ctxt); +void input_gssapi_hash(int type, u_int32_t plen, void *ctxt); +#endif + void userauth(Authctxt *, char *); static int sign_and_send_pubkey(Authctxt *, Key *, sign_cb_fn *); @@ -191,6 +237,16 @@ static char *authmethods_get(void); Authmethod authmethods[] = { +#ifdef GSSAPI + {"external-keyx", + userauth_external, + &options.gss_authentication, + NULL}, + {"gssapi", + userauth_gssapi, + &options.gss_authentication, + NULL}, +#endif {"hostbased", userauth_hostbased, &options.hostbased_authentication, @@ -256,6 +312,7 @@ authctxt.success = 0; authctxt.method = authmethod_lookup("none"); authctxt.authlist = NULL; + authctxt.methoddata = NULL; authctxt.sensitive = sensitive; authctxt.info_req_seen = 0; if (authctxt.method == NULL) @@ -278,6 +335,11 @@ void userauth(Authctxt *authctxt, char *authlist) { + if (authctxt->methoddata!=NULL) { + xfree(authctxt->methoddata); + authctxt->methoddata=NULL; + } + if (authlist == NULL) { authlist = authctxt->authlist; } else { @@ -327,6 +389,8 @@ fatal("input_userauth_success: no authentication context"); if (authctxt->authlist) xfree(authctxt->authlist); + if (authctxt->methoddata) + xfree(authctxt->methoddata); clear_auth_state(authctxt); authctxt->success = 1; /* break out */ } @@ -427,6 +491,153 @@ userauth(authctxt, NULL); } + +#ifdef GSSAPI +int +userauth_gssapi(Authctxt *authctxt) +{ + int i; + Gssctxt *gssctxt; + static int tries=0; + + /* For now, we only make one attempt at this. We could try offering + * the server different GSSAPI OIDs until we get bored, I suppose. + */ + if (tries++>0) return 0; + + if (datafellows & SSH_OLD_GSSAPI) return 0; + + /* Initialise as much of our context as we can, so failures can be + * trapped before sending any packets. + */ + ssh_gssapi_build_ctx(&gssctxt); + + if (ssh_gssapi_import_name(gssctxt,authctxt->host)) { + return(0); + } + + authctxt->methoddata=(void *)gssctxt; + + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + + /* FIXME: This assumes that our current GSSAPI implementation + * supports all of the mechanisms listed in supported_mechs. + * This may not be the case - we should use something along + * the lines of the code in gss_genr to remove the ones that + * aren't supported */ + packet_put_int(GSS_LAST_ENTRY); + for (i=0;imethoddata; + + /* Setup our OID */ + oidv=packet_get_string(&oidlen); + ssh_gssapi_set_oid_data(gssctxt,oidv,oidlen); + + packet_check_eom(); + + status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, + GSS_C_NO_BUFFER, &send_tok, + NULL); + if (GSS_ERROR(status)) { + /* Start again with next method on list */ + debug("Trying to start again"); + userauth(authctxt,NULL); + return; + } + + /* We must have data to send */ + packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + packet_put_string(send_tok.value,send_tok.length); + packet_send(); + packet_write_wait(); + gss_release_buffer(&ms, &send_tok); +} + +void +input_gssapi_token(int type, u_int32_t plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok,recv_tok; + OM_uint32 status; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); + gssctxt = authctxt->methoddata; + + recv_tok.value=packet_get_string(&recv_tok.length); + + status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, + &recv_tok, &send_tok, NULL); + + packet_check_eom(); + + if (GSS_ERROR(status)) { + /* Start again with the next method in the list */ + userauth(authctxt,NULL); + return; + } + + if (send_tok.length>0) { + packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + packet_put_string(send_tok.value,send_tok.length); + packet_send(); + packet_write_wait(); + } + + if (status == GSS_S_COMPLETE) { + /* If that succeeded, send a exchange complete message */ + packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); + packet_send(); + packet_write_wait(); + } +} + +int +userauth_external(Authctxt *authctxt) +{ + static int attempt =0; + + if (attempt++ >= 1) + return 0; + + debug2("userauth_external"); + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + packet_send(); + packet_write_wait(); + return 1; +} +#endif /* GSSAPI */ int userauth_none(Authctxt *authctxt) diff -u -N --exclude configure --exclude config.h.in src.old/sshd.c src/sshd.c --- src.old/sshd.c Sat Jan 11 17:08:26 2003 +++ src/sshd.c Sat Jan 11 17:08:20 2003 @@ -85,6 +85,10 @@ #include "monitor_wrap.h" #include "monitor_fdpass.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + #ifdef LIBWRAP #include #include @@ -1005,10 +1009,13 @@ log("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) { log("Disabling protocol version 2. Could not load host key"); options.protocol &= ~SSH_PROTO_2; } +#endif if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { log("sshd: no hostkeys available -- exiting."); exit(1); @@ -1803,6 +1810,45 @@ myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } 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_mechanisms(1,NULL); + 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); diff -u -N --exclude configure --exclude config.h.in src.old/sshd_config.5 src/sshd_config.5 --- src.old/sshd_config.5 Sat Jan 11 17:08:26 2003 +++ src/sshd_config.5 Sat Jan 11 17:08:20 2003 @@ -253,6 +253,25 @@ 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