diff -u -r -N --exclude configure --exclude config.h.in src.old/AuthSession.h src/AuthSession.h --- src.old/AuthSession.h Wed Dec 31 18:00:00 1969 +++ src/AuthSession.h Sat Jan 11 17:48:12 2003 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please obtain + * a copy of the License at http://www.apple.com/publicsource and read it before + * using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT + * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the + * specific language governing rights and limitations under the License. + */ + + +/* + * AuthSession.h + * AuthSession - APIs for managing login, authorization, and security Sessions. + */ +#if !defined(__AuthSession__) +#define __AuthSession__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + + +/*! + @header AuthSession + + The Session API provides specialized applications access to Session management and inquiry + functions. This is a specialized API that should not be of interest to most people. + + If you do not know what "Session" means in the context of MacOS Authorization and security, + please check with your documentation and come back when you have figured it out - we won't + explain it here. + + This API is tentative, preliminary, incomplete, internal, and subject to change. + You have been warned. +*/ + + +/*! + @typedef SecuritySessionId + These are externally visible identifiers for authorization sessions. + Different sessions have different identifiers; beyond that, you can't + tell anything from these values. + SessionIds can be compared for equality as you'd expect, but you should be careful + to use attribute bits wherever appropriate. For example, don't rely on there being + "the" graphical login session - some day, we may have more than one... +*/ +typedef UInt32 SecuritySessionId; + + +/*! + @enum SecuritySessionId + Here are some special values for SecuritySessionId. You may specify those + on input to SessionAPI functions. They will never be returned from such + functions. +*/ +enum { + noSecuritySession = 0, /* definitely not a valid SecuritySessionId */ + callerSecuritySession = -1 /* the Session I (the caller) am in */ +}; + + +/*! + @enum SessionAttributeBits + Each Session has a set of attribute bits. You can get those from the + SessionGetInfo API function. + */ +typedef UInt32 SessionAttributeBits; + +enum { + sessionIsRoot = 0x0001, /* is the root session (startup/system programs) */ + sessionHasGraphicAccess = 0x0010, /* graphic subsystem (CoreGraphics et al) available */ + sessionHasTTY = 0x0020, /* /dev/tty is available */ + sessionIsRemote = 0x1000, /* session was established over the network */ + + sessionWasInitialized = 0x8000 /* session has been set up by its leader */ +}; + + +/*! + @enum SessionCreationFlags + These flags control how a new session is created by SessionCreate. + They have no permanent meaning beyond that. + */ +typedef UInt32 SessionCreationFlags; + +enum { + sessionKeepCurrentBootstrap = 0x8000 /* caller has allocated sub-bootstrap (expert use only) */ +}; + + +/*! + @enum SessionStatus + Error codes returned by AuthSession API. + Note that the AuthSession APIs can also return Authorization API error codes. +*/ +enum { + errSessionSuccess = 0, /* all is well */ + errSessionInvalidId = -60500, /* invalid session id specified */ + errSessionInvalidAttributes = -60501, /* invalid set of requested attribute bits */ + errSessionAuthorizationDenied = -60502, /* you are not allowed to do this */ + + errSessionInternal = errAuthorizationInternal, /* internal error */ + errSessionInvalidFlags = errAuthorizationInvalidFlags /* invalid flags/options */ +}; + + +/*! + @function SessionGetInfo + Obtain information about a session. + + @param session (input) The Session you are asking about. Can be one of the + special constants defined above. + + @param sessionId (output/optional) The actual SecuritySessionId for the session you asked about. + Will never be one of those constants. + + @param attributes (output/optional) Receives the attribute bits for the session. + + @result An OSStatus indicating success (noErr) or an error cause. + + errSessionInvalidId -60500 Invalid session id specified + +*/ +OSStatus SessionGetInfo(SecuritySessionId session, + SecuritySessionId *sessionId, + SessionAttributeBits *attributes); + + +/*! + @function SessionCreate + This (very specialized) function creates and/or initializes a security session. + It always sets up the session that the calling process belongs to - you cannot + create a session for someone else. + By default, a new bootstrap subset port is created for the calling process. The process + acquires this new port as its bootstrap port, which all its children will inherit. + If you happen to have created the subset port on your own, you can pass the + sessionKeepCurrentBootstrap flag, and SessionCreate will use it. Note however that + you cannot supersede a prior SessionCreate call that way; only a single SessionCreate + call is allowed for each Session (however made). + + @param flags Flags controlling how the session is created. + + @param attributes The set of attribute bits to set for the new session. + Not all bits can be set this way. + + @result An OSStatus indicating success (noErr) or an error cause. + + errSessionInvalidAttributes -60501 Attempt to set invalid attribute bits + errSessionAuthorizationDenied -60502 Attempt to re-initialize a session + errSessionInvalidFlags -60011 Attempt to specify unsupported flag bits + +*/ +OSStatus SessionCreate(SessionCreationFlags flags, + SessionAttributeBits attributes); + + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __AuthSession__ */ diff -u -r -N --exclude configure --exclude config.h.in src.old/Makefile.in src/Makefile.in --- src.old/Makefile.in Sat Jan 11 17:48:23 2003 +++ src/Makefile.in Sat Jan 11 17:48:14 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 -r -N --exclude configure --exclude config.h.in src.old/acconfig.h src/acconfig.h --- src.old/acconfig.h Sat Jan 11 17:48:20 2003 +++ src/acconfig.h Sat Jan 11 17:48:10 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 @@ -213,6 +216,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 @@ -297,6 +303,21 @@ /* Use IPv4 for connection by default, IPv6 can still if explicity asked */ #undef IPV4_DEFAULT + +/* Don't use krb5_init_ets() -- it's not present (MIT only) */ +#undef DONT_KRB5_INIT_ETS + +/* Our Kerberos 5 credentials cache uses the Ccache API (MIT only) */ +#undef HAVE_API_CCACHE + +/* OS X workaround */ +#undef HAVE_SECURITYSERVER + +/* OS X workaround */ +#undef _POSIX_SAVED_IDS + +/* OS X workaround */ +#undef HAVE_ONLY_MKSTEMP /* getaddrinfo is broken (if present) */ #undef BROKEN_GETADDRINFO diff -u -r -N --exclude configure --exclude config.h.in src.old/auth-krb5.c src/auth-krb5.c --- src.old/auth-krb5.c Sat Jan 11 17:48:20 2003 +++ src/auth-krb5.c Sat Jan 11 17:48:10 2003 @@ -42,9 +42,14 @@ #ifdef KRB5 #include #ifndef HEIMDAL +#include #define krb5_get_err_text(context,code) error_message(code) #endif /* !HEIMDAL */ +#if defined(HAVE_API_CCACHE) +#include "ssh-gss.h" +#endif + extern ServerOptions options; static int @@ -181,12 +186,12 @@ #ifdef HEIMDAL problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache); -#else +#elif !defined(HAVE_API_CCACHE) { char ccname[40]; int tmpfd; - snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); + snprintf(ccname,sizeof(ccname)-1,"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { log("mkstemp(): %.100s", strerror(errno)); @@ -203,6 +208,7 @@ problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache); } #endif +#if !defined(HAVE_API_CCACHE) if (problem) goto fail; @@ -210,6 +216,7 @@ authctxt->krb5_user); if (problem) goto fail; +#endif #ifdef HEIMDAL problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, @@ -221,15 +228,31 @@ tgt, &creds, NULL); if (problem) goto fail; +#if defined(HAVE_API_CCACHE) + memcpy(&gssapi_client_krb5_creds,*creds,sizeof(krb5_creds)); + problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, + (char **)(&gssapi_client_name.value)); + if (problem) + goto fail; + gssapi_client_name.length = strlen(gssapi_client_name.value); + snprintf(gssapi_client_krb5_ccname,sizeof(gssapi_client_krb5_ccname)-1, + "Credentials from passed TGT"); + //strncpy(gssapi_client_krb5_ccname,API_CCACHE_DFLT_NAME, + // sizeof(gssapi_client_krb5_ccname)-1); + gssapi_client_type = GSS_KERBEROS; +#else problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds); if (problem) goto fail; #endif +#endif +#if !defined(HAVE_API_CCACHE) authctxt->krb5_fwd_ccache = ccache; ccache = NULL; authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); +#endif problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, &pname); @@ -260,9 +283,11 @@ #ifndef HEIMDAL krb5_creds creds; krb5_principal server; +#if !defined(HAVE_API_CCACHE) char ccname[40]; int tmpfd; -#endif +#endif +#endif krb5_error_code problem; if (authctxt->pw == NULL) @@ -280,7 +305,7 @@ goto out; #ifdef HEIMDAL - problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, + problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &authctxt->krb5_fwd_ccache); if (problem) goto out; @@ -293,6 +318,14 @@ restore_uid(); problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->krb5_fwd_ccache, password, 1, NULL); + if (!problem) { + authctxt->krb5_ticket_file = (char *) krb5_cc_get_name(authctxt->krb5_ctx, + authctxt->krb5_fwd_ccache); + /* krb5_verify_user() changes the ownership of authctxt-> + krb5_fwd_ccache, so we need to change it back */ + problem = chown(authctxt->krb5_ticket_file, + authctxt->pw->pw_uid,authctxt->pw->pw_gid); + } temporarily_use_uid(authctxt->pw); if (problem) @@ -323,7 +356,20 @@ goto out; } - snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); +#if defined(HAVE_API_CCACHE) + memcpy(&gssapi_client_krb5_creds,&creds,sizeof(krb5_creds)); + problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, + (char **)(&gssapi_client_name.value)); + if (problem) + goto out; + gssapi_client_name.length = strlen(gssapi_client_name.value); + snprintf(gssapi_client_krb5_ccname,sizeof(gssapi_client_krb5_ccname)-1, + "Credentials from password"); + //strncpy(gssapi_client_krb5_ccname,API_CCACHE_DFLT_NAME, + // sizeof(gssapi_client_krb5_ccname)-1); + gssapi_client_type = GSS_KERBEROS; +#else + snprintf(ccname,sizeof(ccname)-1,"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { log("mkstemp(): %.100s", strerror(errno)); @@ -352,9 +398,11 @@ &creds); if (problem) goto out; -#endif authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); +#endif /* #if defined(HAVE_API_CCACHE) */ + +#endif /* #ifdef HEIMDAL */ out: restore_uid(); diff -u -r -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:48:20 2003 +++ src/auth-pam.c Sat Jan 11 17:48:11 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 -r -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:48:20 2003 +++ src/auth-pam.h Sat Jan 11 17:48:11 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 -r -N --exclude configure --exclude config.h.in src.old/auth.h src/auth.h --- src.old/auth.h Sat Jan 11 17:48:20 2003 +++ src/auth.h Sat Jan 11 17:48:11 2003 @@ -70,6 +70,7 @@ krb5_principal krb5_user; char *krb5_ticket_file; #endif + void *methoddata; }; struct Authmethod { diff -u -r -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:48:11 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 -r -N --exclude configure --exclude config.h.in src.old/auth2.c src/auth2.c --- src.old/auth2.c Sat Jan 11 17:48:21 2003 +++ src/auth2.c Sat Jan 11 17:48:11 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 -r -N --exclude configure --exclude config.h.in src.old/compat.c src/compat.c --- src.old/compat.c Sat Jan 11 17:48:21 2003 +++ src/compat.c Sat Jan 11 17:48:12 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 -r -N --exclude configure --exclude config.h.in src.old/compat.h src/compat.h --- src.old/compat.h Sat Jan 11 17:48:21 2003 +++ src/compat.h Sat Jan 11 17:48:12 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 -r -N --exclude configure --exclude config.h.in src.old/configure.ac src/configure.ac --- src.old/configure.ac Sat Jan 11 17:48:21 2003 +++ src/configure.ac Sat Jan 11 21:02:22 2003 @@ -108,6 +108,13 @@ [AC_MSG_RESULT(buggy) AC_DEFINE(BROKEN_GETADDRINFO)], [AC_MSG_RESULT(assume it is working)]) + LDFLAGS="$LDFLAGS -framework Security -bind_at_load" + AC_DEFINE(HAVE_SETLOGIN) + AC_DEFINE(DONT_KRB5_INIT_ETS) # MIT only + AC_DEFINE(HAVE_API_CCACHE) # MIT only + AC_DEFINE(HAVE_SECURITYSERVER) + AC_DEFINE(_POSIX_SAVED_IDS) + AC_DEFINE(HAVE_ONLY_MKSTEMP) ;; *-*-hpux10.26) if test -z "$GCC"; then @@ -695,6 +702,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 +713,7 @@ else LIBPAM="-lpam" fi + AC_SUBST(LIBPAM) fi ] @@ -735,6 +744,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 +831,9 @@ ] ) +fi +#end of GSI/Globus 2.0 mods + # Determine OpenSSL header version AC_MSG_CHECKING([OpenSSL header version]) AC_TRY_RUN( @@ -1764,28 +1817,53 @@ else KRB5ROOT=${withval} fi - CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include" - LDFLAGS="$LDFLAGS -L${KRB5ROOT}/lib" + CPPFLAGS="-I${KRB5ROOT}/include $CPPFLAGS" + LDFLAGS="-L${KRB5ROOT}/lib $LDFLAGS" AC_DEFINE(KRB5) KRB5_MSG="yes" AC_MSG_CHECKING(whether we are using Heimdal) - AC_TRY_COMPILE([ #include ], + AC_TRY_COMPILE([ #include "${KRB5ROOT}/include/krb5.h" ], [ char *tmp = heimdal_version; ], [ AC_MSG_RESULT(yes) AC_DEFINE(HEIMDAL) - K5LIBS="-lkrb5 -ldes -lcom_err -lasn1 -lroken" + K5LIBS="${KRB5ROOT}/lib/libkrb5.a ${KRB5ROOT}/lib/libcom_err.a -lasn1 -lroken" ], [ AC_MSG_RESULT(no) K5LIBS="-lkrb5 -lk5crypto -lcom_err" ] ) if test ! -z "$need_dash_r" ; then - LDFLAGS="$LDFLAGS -R${KRB5ROOT}/lib" + LDFLAGS="-R${KRB5ROOT}/lib $LDFLAGS" fi if test ! -z "$blibpath" ; then - blibpath="$blibpath:${KRB5ROOT}/lib" + blibpath="${KRB5ROOT}/lib:$blibpath" 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="-I${KRB5ROOT}/include/gssapi $CPPFLAGS" + AC_CHECK_HEADERS(gssapi.h, , + AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail]) + ) + ] + ) + + oldCPP="$CPPFLAGS" + CPPFLAGS="-I${KRB5ROOT}/include/gssapi $CPPFLAGS" + AC_CHECK_HEADER(gssapi_krb5.h, , + [ CPPFLAGS="$oldCPP" ]) KRB5=yes fi diff -u -r -N --exclude configure --exclude config.h.in src.old/defines.h src/defines.h --- src.old/defines.h Sat Jan 11 17:48:21 2003 +++ src/defines.h Sat Jan 11 17:48:12 2003 @@ -3,6 +3,10 @@ /* $Id: defines.h,v 1.96 2002/09/26 00:38:48 tim Exp $ */ +#if defined(HEIMDAL) +# undef DONT_KRB5_INIT_ETS +# undef HAVE_API_CCACHE +#endif /* Constants */ @@ -438,6 +442,10 @@ # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) #else # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) +#endif + +#if defined(DONT_KRB5_INIT_ETS) +# define krb5_init_ets(table) #endif #if defined(BROKEN_GETADDRINFO) && defined(HAVE_GETADDRINFO) diff -u -r -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:48:13 2003 @@ -0,0 +1,546 @@ +/* + * 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; +#if defined(HAVE_API_CCACHE) +char gssapi_client_krb5_ccname[40] = {0}; +krb5_creds gssapi_client_krb5_creds = {0}; +#endif + +/* 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 -r -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:48:13 2003 @@ -0,0 +1,709 @@ +/* + * 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 +#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]; +#if defined(HAVE_API_CCACHE) + cc_context_t cc_context = NULL; + cc_ccache_t cc_cache = NULL; + cc_ccache_iterator_t ccacheIterator = NULL; + cc_string_t cc_name = NULL; + cc_int32 cc_err; +#else + int tmpfd; +#endif + OM_uint32 maj_status,min_status; + + if (ssh_gssapi_krb5_init() == 0) + return; + +#if defined(HAVE_API_CCACHE) + cc_err = cc_initialize(&cc_context, ccapi_version_4, NULL, NULL); + if (cc_err != ccNoError) { + log("cc_initialize returned error = %d", cc_err); + return; + } + cc_err = cc_context_new_ccache_iterator(cc_context, &ccacheIterator); + if (cc_err != ccNoError) { + log("cc_ccache_new_ccache_iterator returned error = %d", cc_err); + return; + } + for (;;) { + cc_err = cc_ccache_iterator_next(ccacheIterator, &cc_cache); + if ((cc_err != ccNoError) && (cc_err != ccIteratorEnd)) { + log("cc_ccache_iterator_next returned error = %d", cc_err); + return; + } else if (cc_err == ccIteratorEnd) { + break; + } + cc_err = cc_ccache_get_name(cc_cache, &cc_name); + if (cc_err == ccNoError) { + log("destroying previous credentials cache in \"%s\"", cc_name->data); + } + cc_string_release(cc_name); + cc_err = cc_ccache_destroy(cc_cache); + if (cc_err != ccNoError) { + log("cc_ccache_destroy returned error = %d", cc_err); + return; + } + } + cc_ccache_iterator_release(ccacheIterator); + cc_context_release(cc_context); +#endif + +#if defined(HAVE_API_CCACHE) && !defined(HEIMDAL) + if ((gssapi_client_creds==NULL)&&(gssapi_client_krb5_creds.client==NULL)) { +#else + if (gssapi_client_creds==NULL) { +#endif + debug("No credentials stored"); + return; + } + +#if defined(HAVE_API_CCACHE) && !defined(HEIMDAL) + if (gssapi_client_krb5_ccname[0] == 0) { + snprintf(ccname,sizeof(ccname)-1,"Forwarded credentials"); + //strncpy(ccname,API_CCACHE_DFLT_NAME,sizeof(ccname)-1); + } + else strncpy(ccname,gssapi_client_krb5_ccname,sizeof(ccname)-1); +#else + if (options.gss_use_session_ccache) { + snprintf(ccname,sizeof(ccname)-1,"/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)-1,"/tmp/krb5cc_%d",geteuid()); + tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (tmpfd == -1) { + log("open(): %.100s", strerror(errno)); + return; + } + } +#endif + +#if defined(HAVE_API_CCACHE) + cc_err = cc_initialize(&cc_context, ccapi_version_4, NULL, NULL); + if (cc_err != ccNoError) { + log("cc_initialize returned error = %d", cc_err); + return; + } + cc_err = cc_context_create_ccache(cc_context, ccname, cc_credentials_v5, + gssapi_client_name.value, &cc_cache); + if (cc_err != ccNoError) { + log("cc_context_create_ccache returned error = %d", cc_err); + return; + } + cc_err = cc_ccache_set_default(cc_cache); + if (cc_err != ccNoError) { + log("cc_ccache_set_default returned error = %d", cc_err); + return; + } + cc_context_release(cc_context); + cc_ccache_release(cc_cache); +#endif + +#if defined(HAVE_API_CCACHE) && !defined(HEIMDAL) + snprintf(name, sizeof(name)-1, "API:%s", ccname); +#else + close(tmpfd); + snprintf(name, sizeof(name)-1, "FILE:%s",ccname); +#endif + + if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) { + log("krb5_cc_resolve(): %.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 + #ifndef HAVE_API_CCACHE + 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; + } + #else + if (gssapi_client_creds != NULL) { + 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; + } + } else if (gssapi_client_krb5_creds.client != NULL) { + if ((problem = krb5_cc_store_cred(krb_context, ccache, + &gssapi_client_krb5_creds))) + { + log("krb5_cc_store_cred(): %.100s", + krb5_get_err_text(krb_context,problem)); + krb5_cc_destroy(krb_context,ccache); + return; + } + } + #endif + #endif + + krb5_cc_close(krb_context,ccache); + +#ifdef USE_PAM + do_pam_putenv("KRB5CCNAME",name); +#endif + +#if !defined(HAVE_API_CCACHE) + gssapi_cred_store.filename=strdup(ccname); +#endif + 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 -r -N --exclude configure --exclude config.h.in src.old/kex.c src/kex.c --- src.old/kex.c Sat Jan 11 17:48:22 2003 +++ src/kex.c Sat Jan 11 17:48:13 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 -r -N --exclude configure --exclude config.h.in src.old/kex.h src/kex.h --- src.old/kex.h Sat Jan 11 17:48:22 2003 +++ src/kex.h Sat Jan 11 17:48:13 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 -r -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:48:13 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 -r -N --exclude configure --exclude config.h.in src.old/key.c src/key.c --- src.old/key.c Sat Jan 11 17:48:22 2003 +++ src/key.c Sat Jan 11 17:48:14 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 -r -N --exclude configure --exclude config.h.in src.old/key.h src/key.h --- src.old/key.h Sat Jan 11 17:48:22 2003 +++ src/key.h Sat Jan 11 17:48:14 2003 @@ -34,6 +34,7 @@ KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_NULL, KEY_UNSPEC }; enum fp_type { diff -u -r -N --exclude configure --exclude config.h.in src.old/log.c src/log.c --- src.old/log.c Sat Jan 11 17:48:22 2003 +++ src/log.c Sat Jan 11 17:48:14 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 -r -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:48:14 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 -r -N --exclude configure --exclude config.h.in src.old/monitor.c src/monitor.c --- src.old/monitor.c Sat Jan 11 17:48:23 2003 +++ src/monitor.c Sat Jan 11 17:48:15 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 -r -N --exclude configure --exclude config.h.in src.old/monitor.h src/monitor.h --- src.old/monitor.h Sat Jan 11 17:48:23 2003 +++ src/monitor.h Sat Jan 11 17:48:15 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 -r -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:48:23 2003 +++ src/monitor_wrap.c Sat Jan 11 17:48:15 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 -r -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:48:23 2003 +++ src/monitor_wrap.h Sat Jan 11 17:48:15 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 -r -N --exclude configure --exclude config.h.in src.old/openbsd-compat/bsd-misc.h src/openbsd-compat/bsd-misc.h --- src.old/openbsd-compat/bsd-misc.h Sat Jan 11 17:48:19 2003 +++ src/openbsd-compat/bsd-misc.h Sat Jan 11 17:48:09 2003 @@ -29,6 +29,11 @@ #include "config.h" +/* Resolve name conflict with libroken */ +#ifdef HEIMDAL +#define get_progname get_progname_x +#endif + char *get_progname(char *argv0); #ifndef HAVE_SETSID diff -u -r -N --exclude configure --exclude config.h.in src.old/openbsd-compat/mktemp.h src/openbsd-compat/mktemp.h --- src.old/openbsd-compat/mktemp.h Sat Jan 11 17:48:19 2003 +++ src/openbsd-compat/mktemp.h Sat Jan 11 17:48:10 2003 @@ -5,6 +5,11 @@ #include "config.h" #ifndef HAVE_MKDTEMP +/* Resolve name conflict in OS X, which has mkstemp() but + not the other two. */ +#if defined(HAVE_ONLY_MKSTEMP) +# define mkstemp mkstemp_x +#endif int mkstemps(char *path, int slen); int mkstemp(char *path); char *mkdtemp(char *path); diff -u -r -N --exclude configure --exclude config.h.in src.old/readconf.c src/readconf.c --- src.old/readconf.c Sat Jan 11 17:48:24 2003 +++ src/readconf.c Sat Jan 11 17:48:16 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 -r -N --exclude configure --exclude config.h.in src.old/readconf.h src/readconf.h --- src.old/readconf.h Sat Jan 11 17:48:24 2003 +++ src/readconf.h Sat Jan 11 17:48:16 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 -r -N --exclude configure --exclude config.h.in src.old/servconf.c src/servconf.c --- src.old/servconf.c Sat Jan 11 17:48:24 2003 +++ src/servconf.c Sat Jan 11 17:48:16 2003 @@ -16,14 +16,10 @@ #include #endif #if defined(KRB5) -#ifdef HEIMDAL -#include -#else /* Bodge - but then, so is using the kerberos IV KEYFILE to get a Kerberos V * keytab */ #define KEYFILE "/etc/krb5.keytab" #endif -#endif #ifdef AFS #include #endif @@ -86,6 +82,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 +202,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 +292,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 +345,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 +667,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 -r -N --exclude configure --exclude config.h.in src.old/servconf.h src/servconf.h --- src.old/servconf.h Sat Jan 11 17:48:24 2003 +++ src/servconf.h Sat Jan 11 17:48:16 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 -r -N --exclude configure --exclude config.h.in src.old/session.c src/session.c --- src.old/session.c Sat Jan 11 17:48:25 2003 +++ src/session.c Sat Jan 11 17:48:16 2003 @@ -58,6 +58,17 @@ #include "session.h" #include "monitor_wrap.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + +#if defined(HAVE_SECURITYSERVER) +#include +#include +#include +#include "AuthSession.h" +#endif + #ifdef HAVE_CYGWIN #include #include @@ -453,6 +464,12 @@ session_proctitle(s); +#if defined(GSSAPI) && !defined(HAVE_API_CCACHE) + 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 +597,12 @@ ptyfd = s->ptyfd; ttyfd = s->ttyfd; +#if defined(GSSAPI) && !defined(HAVE_API_CCACHE) + 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 +861,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 +988,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); @@ -1324,6 +1354,35 @@ shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); #endif +#if defined(HAVE_SECURITYSERVER) + { + OSStatus err; + SecuritySessionId sec_session_id = 0; + SessionAttributeBits sec_session_attr = 0; + static char AuthSession = 0; + if (AuthSession == 0 ) { + err = SessionCreate(0,sessionHasTTY|sessionIsRemote); + if (err != 0) { + log("SessionCreate() failed with error %.8X",(unsigned)err); + } else { + log("SessionCreate() succeeded"); + AuthSession = 1; + } + } + err = SessionGetInfo(callerSecuritySession,&sec_session_id, + &sec_session_attr); + log("SessionGetInfo() returned %.8X",(unsigned)err); + log("sec_session_id is %.8X",(unsigned)sec_session_id); + log("sec_session_attr is %.8X",(unsigned)sec_session_attr); + } +#endif + +#if defined(GSSAPI) && defined(HAVE_API_CCACHE) + if (gssapi_client_type == GSS_LAST_ENTRY) + gssapi_client_type = GSS_KERBEROS; + ssh_gssapi_storecreds(); +#endif + env = do_setup_env(s, shell); /* we have to stash the hostname before we close our socket. */ @@ -2092,4 +2151,7 @@ do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); +#if defined(GSSAPI) + ssh_gssapi_cleanup_creds(NULL); +#endif } diff -u -r -N --exclude configure --exclude config.h.in src.old/session.h src/session.h --- src.old/session.h Sat Jan 11 17:48:25 2003 +++ src/session.h Sat Jan 11 17:48:16 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 -r -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:48:17 2003 @@ -0,0 +1,143 @@ +/* + * 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 +#if defined(HAVE_API_CCACHE) +#include +#include +#endif +#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; +#if defined(HAVE_API_CCACHE) +extern char gssapi_client_krb5_ccname[40]; +extern krb5_creds gssapi_client_krb5_creds; +#define API_CCACHE_DFLT_NAME "Initial default ccache" +#endif + +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 -r -N --exclude configure --exclude config.h.in src.old/sshconnect2.c src/sshconnect2.c --- src.old/sshconnect2.c Sat Jan 11 17:48:25 2003 +++ src/sshconnect2.c Sat Jan 11 17:48:17 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 -r -N --exclude configure --exclude config.h.in src.old/sshd.c src/sshd.c --- src.old/sshd.c Sat Jan 11 17:48:25 2003 +++ src/sshd.c Sat Jan 11 17:48:17 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 -r -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:48:25 2003 +++ src/sshd_config.5 Sat Jan 11 17:48:17 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