TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-agent$(EXEEXT) scp$(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 mac.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 ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.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 mac.o hostfile.o key.o kex.o kexdh.o kexgex.o kexgss.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 ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.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 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
+SSHDOBJS= sshd.o auth.o auth1.o auth2.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 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
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
/* Define if libc defines __progname */
#undef HAVE___PROGNAME
+/* Define this is you want GSSAPI support in the version 2 protocol */
+#undef GSSAPI
+
/* Define if you want Kerberos 5 support */
#undef KRB5
/* 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
#endif /* HAVE_PAM_GETENVLIST */
}
+/* 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;
+
+ compound=xmalloc(strlen(name)+strlen(value)+2);
+ if (compound) {
+ sprintf(compound,"%s=%s",name,value);
+ ret=pam_putenv(__pamh,compound);
+ xfree(compound);
+ }
+ return(ret);
+}
+
/* Print any messages that have been generated during authentication */
/* or account checking to stderr */
void print_pam_messages(void)
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 */
krb5_principal krb5_user;
char *krb5_ticket_file;
#endif
+ void *methoddata;
};
/*
#include "canohost.h"
#include "match.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
/* import */
extern ServerOptions options;
extern u_char *session_id2;
static int userauth_hostbased(Authctxt *);
static int userauth_kbdint(Authctxt *);
+#ifdef GSSAPI
+int userauth_external(Authctxt *authctxt);
+int userauth_gssapi(Authctxt *authctxt);
+#endif
+
Authmethod authmethods[] = {
{"none",
userauth_none,
&one},
+#ifdef GSSAPI
+ {"external-keyx",
+ userauth_external,
+ &options.gss_authentication},
+ {"gssapi",
+ userauth_gssapi,
+ &options.gss_authentication},
+#endif
{"publickey",
userauth_pubkey,
&options.pubkey_authentication},
}
/* reset state */
dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, &protocol_error);
+
+#ifdef GSSAPI
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &protocol_error);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, &protocol_error);
+#endif
+
authctxt->postponed = 0;
#ifdef BSD_AUTH
if (authctxt->as) {
]
)
+# Check whether the user wants GSI (Globus) support
+gsi_path="no"
+AC_ARG_WITH(gsi,
+ [ --with-gsi=PATH Enable GSI/Globus authentication support],
+ [
+ gsi_path="$withval"
+ ]
+)
+
+AC_ARG_WITH(globus,
+ [ --with-globus=PATH Enable GSI/Globus authentication support],
+ [
+ gsi_path="$withval"
+ ]
+)
+
+if test "x$gsi_path" != "xno" ; then
+ # Globus GSSAPI configuration
+ AC_DEFINE(GSSAPI)
+ AC_DEFINE(GSI)
+
+ # Find GLOBUS/GSI installation Directory
+ AC_MSG_CHECKING(for Globus/GSI installation directory)
+
+ globus_install_dir=$gsi_path
+
+ if test "x$globus_install_dir" = "xyes" ; then
+ if test -n "$GLOBUS_INSTALL_PATH" ; then
+ globus_install_dir=$GLOBUS_INSTALL_PATH
+ elif test -n "$GSI_INSTALL_PATH" ; then
+ globus_install_dir=$GSI_INSTALL_PATH
+ elif test -d /usr/local/globus ; then
+ globus_install_dir="/usr/local/globus"
+ elif test -d /usr/local/gsi ; then
+ globus_install_dir="/usr/local/gsi"
+ else
+ AC_MSG_ERROR(Cannot find Globus/GSI installation directory)
+ fi
+ fi
+ AC_MSG_RESULT($globus_install_dir)
+
+ # Find GLOBUS/GSI development directory
+ AC_MSG_CHECKING(for Globus/GSI development directory)
+
+ if test -d ${globus_install_dir}/lib ; then
+ # Looks like a flat directory structure from configure/make
+ # and not globus-install or gsi-install
+ globus_dev_dir=$globus_install_dir
+
+ else
+ # Assume a true globus installation with architecture
+ # directories and run globus-development-path to find
+ # the development directory
+
+ # Set GLOBUS_INSTALL_PATH
+ GLOBUS_INSTALL_PATH=$globus_install_dir
+ export GLOBUS_INSTALL_PATH
+
+ dev_path_program=${globus_install_dir}/bin/globus-development-path
+
+ if test ! -x ${dev_path_program} ; then
+ AC_MSG_ERROR(Cannot find Globus/GSI installation directory: program ${dev_path_program} does not exist or is not executable)
+ fi
+
+ globus_dev_dir=`${dev_path_program}`
+
+ if test -z "$globus_dev_dir" -o "X$globus_dev_dir" = "X<not found>" ; then
+ AC_MSG_ERROR(Cannot find Globus/GSI development directory)
+ fi
+
+ if test ! -d "$globus_dev_dir" ; then
+ AC_MSG_ERROR(Cannot find Globus/GSI development directory: $globus_dev_dir does not exist)
+ fi
+ fi
+ AC_MSG_RESULT($globus_dev_dir)
+
+ GSI_LIBS="-lglobus_gss_assist -lglobus_gss -lglobus_gaa -lssl -lcrypto"
+ GSI_LDFLAGS="-L${globus_dev_dir}/lib"
+ GSI_CFLAGS="-I${globus_dev_dir}/include"
+
+ LIBS="$LIBS $GSI_LIBS"
+ LDFLAGS="$LDFLAGS $GSI_LDFLAGS"
+ CFLAGS="$CFLAGS $GSI_CFLAGS"
+# End Globus/GSI section
+fi
+
# Check whether user wants S/Key support
SKEY_MSG="no"
AC_ARG_WITH(skey,
fi
AC_CHECK_LIB(resolv, dn_expand, , )
+ AC_CHECK_LIB(gssapi,gss_init_sec_context,
+ [ AC_DEFINE(GSSAPI)
+ K5LIBS="-lgssapi $K5LIBS" ],
+ [ AC_CHECK_LIB(gssapi_krb5,gss_init_sec_context,
+ [ AC_DEFINE(GSSAPI)
+ K5LIBS="-lgssapi_krb5 $K5LIBS" ],
+ AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail]),
+ $K5LIBS)
+ ],
+ $K5LIBS)
+
+ AC_CHECK_HEADER(gssapi.h, ,
+ [ unset ac_cv_header_gssapi_h
+ CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi"
+ AC_CHECK_HEADERS(gssapi.h, ,
+ AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail])
+ )
+ ]
+ )
+
+ oldCPP="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi"
+ AC_CHECK_HEADER(gssapi_krb5.h, ,
+ [ CPPFLAGS="$oldCPP" ])
+
KRB5=yes
fi
]
--- /dev/null
+/*
+ * Copyright (c) 2001 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 <openssl/evp.h>
+#include "cipher.h"
+#include "kex.h"
+#include "log.h"
+
+#include <netdb.h>
+
+#include "ssh-gss.h"
+
+/* Assorted globals for tracking the clients identity once they've
+ * authenticated */
+
+gss_buffer_desc gssapi_client_name = {0,NULL}; /* Name of our client */
+gss_cred_id_t gssapi_client_creds = GSS_C_NO_CREDENTIAL; /* Their credentials */
+enum ssh_gss_id gssapi_client_type = GSS_LAST_ENTRY;
+
+/* The mechanism name used in the list below is defined in the internet
+ * draft as the Base 64 encoding of the MD5 hash of the ASN.1 DER encoding
+ * of the underlying GSSAPI mechanism's OID.
+ *
+ * Also from the draft, before considering adding SPNEGO, bear in mind that
+ * "mechanisms ... MUST NOT use SPNEGO as the underlying GSSAPI mechanism"
+ */
+
+/* These must be in the same order as ssh_gss_id, in ssh-gss.h */
+
+ssh_gssapi_mech supported_mechs[]= {
+#ifdef KRB5
+ /* Official OID - 1.2.850.113554.1.2.2 */
+ {"Se3H81ismmOC3OE+FwYCiQ==","Kerberos",
+ {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}},
+#endif
+#ifdef GSI
+ /* gssapi_ssleay 1.3.6.1.4.1.3536.1.1 */
+ {"N3+k7/4wGxHyuP8Yxi4RhA==",
+ "GSI",
+ {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"}
+ },
+#endif /* GSI */
+ {NULL,NULL,{0,0}}
+};
+
+char gssprefix[]=KEX_GSS_SHA1;
+
+/* Return a list of the gss-group1-sha1-x mechanisms supported by this
+ * program.
+ *
+ * We only support the mechanisms that we've indicated in the list above,
+ * but we check that they're supported by the GSSAPI mechanism on the
+ * machine. We also check, before including them in the list, that
+ * we have the necesary information in order to carry out the key exchange
+ * (that is, that the user has credentials, the server's creds are accessible,
+ * etc)
+ *
+ * The way that this is done is fairly nasty, as we do a lot of work that
+ * is then thrown away. This should possibly be implemented with a cache
+ * that stores the results (in an expanded Gssctxt structure), which are
+ * then used by the first calls if that key exchange mechanism is chosen.
+ */
+
+char *
+ssh_gssapi_mechanisms(int server,char *host) {
+ gss_OID_set supported;
+ OM_uint32 maj_status, min_status;
+ Buffer buf;
+ int i = 0;
+ int present;
+ char * mechs;
+ Gssctxt ctx;
+ gss_buffer_desc token;
+
+
+ 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) {
+ ssh_gssapi_build_ctx(&ctx);
+ ssh_gssapi_set_oid(&ctx,&supported_mechs[i].oid);
+ if (server) {
+ if (ssh_gssapi_acquire_cred(&ctx)) {
+ ssh_gssapi_delete_ctx(&ctx);
+ continue;
+ }
+ }
+ else { /* client */
+ if (ssh_gssapi_import_name(&ctx,host))
+ continue;
+ maj_status=ssh_gssapi_init_ctx(&ctx, 0,
+ GSS_C_NO_BUFFER,
+ &token,
+ NULL);
+ ssh_gssapi_delete_ctx(&ctx);
+ if (GSS_ERROR(maj_status)) {
+ continue;
+ }
+ }
+
+ /* 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. */
+int ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
+ enum ssh_gss_id i=0;
+
+ if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) {
+ return(1);
+ }
+
+ 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 (1);
+
+ ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid);
+
+ return 0;
+}
+
+
+/* All this effort to report an error ... */
+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->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;
+
+ 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);
+}
+
+/* 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 mechanism thats being used is
+ * the one that we asked for (in ctx->oid) */
+
+ ctx->status=maj_status;
+
+ return(maj_status);
+}
+
+/* Create a service name for the given host */
+OM_uint32
+ssh_gssapi_import_name(Gssctxt *ctx,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 {
+ host = hostinfo->h_name;
+ }
+ xfree(xhost);
+
+ gssbuf.length = sizeof("host@")+strlen(host);
+
+ gssbuf.value = xmalloc(gssbuf.length);
+ if (gssbuf.value == NULL) {
+ return(-1);
+ }
+ snprintf(gssbuf.value,gssbuf.length,"host@%s",host);
+ if ((maj_status=gss_import_name(&min_status,
+ &gssbuf,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ &ctx->name))) {
+ ssh_gssapi_error(maj_status,min_status);
+ }
+
+ 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
+ */
+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);
+}
+
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 2001 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 <openssl/evp.h>
+#include "cipher.h"
+#include "kex.h"
+#include "auth.h"
+#include "log.h"
+#include "session.h"
+#include "dispatch.h"
+#include "servconf.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 <krb5.h>
+#else
+#include <gssapi_krb5.h>
+#define krb5_get_err_text(context,code) error_message(code)
+#endif
+
+static krb5_context krb_context = NULL;
+
+/* Initialise the krb5 library, so we can use it for those bits that
+ * GSSAPI won't do */
+
+int ssh_gssapi_krb5_init() {
+ krb5_error_code problem;
+
+ if (krb_context !=NULL)
+ return 1;
+
+ problem = krb5_init_context(&krb_context);
+ if (problem) {
+ log("Cannot initialize krb5 context");
+ return 0;
+ }
+ krb5_init_ets(krb_context);
+
+ return 1;
+}
+
+/* Check if this user is OK to login. This only works with krb5 - other
+ * GSSAPI mechanisms will need their own.
+ * Returns true if the user is OK to log in, otherwise returns 0
+ */
+
+int
+ssh_gssapi_krb5_userok(char *name) {
+ krb5_principal princ;
+ int retval;
+
+ if (ssh_gssapi_krb5_init() == 0)
+ return 0;
+
+ if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value,
+ &princ))) {
+ log("krb5_parse_name(): %.100s",
+ krb5_get_err_text(krb_context,retval));
+ return 0;
+ }
+ if (krb5_kuserok(krb_context, princ, name)) {
+ retval = 1;
+ log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
+ (char *)gssapi_client_name.value);
+ }
+ else
+ retval = 0;
+
+ krb5_free_principal(krb_context, princ);
+ return retval;
+}
+
+/* Make sure that this is called _after_ we've setuid to the user */
+
+/* This writes out any forwarded credentials. Its specific to the Kerberos
+ * GSSAPI mechanism
+ *
+ * We assume that our caller has made sure that the user has selected
+ * delegated credentials, and that the client_creds structure is correctly
+ * populated.
+ */
+
+void
+ssh_gssapi_krb5_storecreds() {
+ krb5_ccache ccache;
+ krb5_error_code problem;
+ krb5_principal princ;
+ char ccname[35];
+ static char name[40];
+ int tmpfd;
+ OM_uint32 maj_status,min_status;
+
+
+ if (gssapi_client_creds==NULL) {
+ debug("No credentials stored");
+ return;
+ }
+
+ if (ssh_gssapi_krb5_init() == 0)
+ return;
+
+ if (options.gss_use_session_ccache) {
+ snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
+
+ if ((tmpfd = mkstemp(ccname))==-1) {
+ log("mkstemp(): %.100s", strerror(errno));
+ return;
+ }
+ if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
+ log("fchmod(): %.100s", strerror(errno));
+ close(tmpfd);
+ return;
+ }
+ } else {
+ snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
+ tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ if (tmpfd == -1) {
+ log("open(): %.100s", strerror(errno));
+ return;
+ }
+ }
+
+ close(tmpfd);
+ snprintf(name, sizeof(name), "FILE:%s",ccname);
+
+ if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
+ log("krb5_cc_default(): %.100s",
+ krb5_get_err_text(krb_context,problem));
+ return;
+ }
+
+ if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value,
+ &princ))) {
+ log("krb5_parse_name(): %.100s",
+ krb5_get_err_text(krb_context,problem));
+ krb5_cc_destroy(krb_context,ccache);
+ return;
+ }
+
+ if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
+ log("krb5_cc_initialize(): %.100s",
+ krb5_get_err_text(krb_context,problem));
+ krb5_free_principal(krb_context,princ);
+ krb5_cc_destroy(krb_context,ccache);
+ return;
+ }
+
+ krb5_free_principal(krb_context,princ);
+
+ #ifdef HEIMDAL
+ if ((problem = krb5_cc_copy_cache(krb_context,
+ gssapi_client_creds->ccache,
+ ccache))) {
+ log("krb5_cc_copy_cache(): %.100s",
+ krb5_get_err_text(krb_context,problem));
+ krb5_cc_destroy(krb_context,ccache);
+ return;
+ }
+ #else
+ if ((maj_status = gss_krb5_copy_ccache(&min_status,
+ gssapi_client_creds,
+ ccache))) {
+ log("gss_krb5_copy_ccache() failed");
+ ssh_gssapi_error(maj_status,min_status);
+ krb5_cc_destroy(krb_context,ccache);
+ return;
+ }
+ #endif
+
+ krb5_cc_close(krb_context,ccache);
+
+
+#ifdef USE_PAM
+ do_pam_putenv("KRB5CCNAME",name);
+#endif
+
+ gssapi_cred_store.filename=strdup(ccname);
+ gssapi_cred_store.envvar="KRB5CCNAME";
+ gssapi_cred_store.envval=strdup(name);
+
+ return;
+}
+
+#endif /* KRB5 */
+
+#ifdef GSI
+#include <globus_gss_assist.h>
+
+/*
+ * 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");
+
+ 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);
+}
+
+int
+userauth_external(Authctxt *authctxt)
+{
+ packet_done();
+
+ return(ssh_gssapi_userok(authctxt->user));
+}
+
+void input_gssapi_token(int type, int plen, void *ctxt);
+void input_gssapi_exchange_complete(int type, int 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
+ */
+int
+userauth_gssapi(Authctxt *authctxt)
+{
+ gss_OID_desc oid= {0,NULL};
+ Gssctxt *ctxt;
+ int mechs;
+ gss_OID_set supported;
+ int present;
+ OM_uint32 ms;
+
+ if (!authctxt->valid || authctxt->user == NULL)
+ 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(&oid.length);
+ gss_test_oid_set_member(&ms, &oid, supported, &present);
+ mechs--;
+ } while (mechs>0 && !present);
+
+ if (!present) {
+ xfree(oid.elements);
+ return(0);
+ }
+
+ ctxt=xmalloc(sizeof(Gssctxt));
+ authctxt->methoddata=(void *)ctxt;
+
+ ssh_gssapi_build_ctx(ctxt);
+ ssh_gssapi_set_oid(ctxt,&oid);
+
+ if (ssh_gssapi_acquire_cred(ctxt))
+ return 0;
+
+ /* 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;
+}
+
+void
+input_gssapi_token(int type, int 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)
+ fatal("No authentication or GSSAPI context");
+
+ gssctxt=authctxt->methoddata;
+
+ recv_tok.value=packet_get_string(&recv_tok.length);
+
+ maj_status=ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL);
+ packet_done();
+
+ if (GSS_ERROR(maj_status)) {
+ /* Failure <sniff> */
+ 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.
+ */
+
+void
+input_gssapi_exchange_complete(int type, int plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ int authenticated;
+
+ if (authctxt == NULL || authctxt->methoddata == NULL)
+ fatal("No authentication or GSSAPI context");
+
+ gssctxt=authctxt->methoddata;
+
+ /* This should never happen, but better safe than sorry. */
+ if (gssctxt->status != GSS_S_COMPLETE) {
+ packet_disconnect("Context negotiation is not complete");
+ }
+
+ if (ssh_gssapi_getclient(gssctxt,&gssapi_client_type,
+ &gssapi_client_name,
+ &gssapi_client_creds)) {
+ fatal("Couldn't convert client name");
+ }
+
+ authenticated = 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");
+}
+
+#endif /* GSSAPI */
#include "match.h"
#include "dispatch.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
#define KEX_COOKIE_LEN 16
/* prototype */
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);
}
{
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);
}
enum kex_exchange {
DH_GRP1_SHA1,
- DH_GEX_SHA1
+ DH_GEX_SHA1,
+ GSS_GRP1_SHA1
};
#define KEX_INIT_SENT 0x0001
Mac mac;
Comp comp;
};
+
+struct KexOptions {
+ int gss_deleg_creds;
+};
+
struct Kex {
u_char *session_id;
int session_id_len;
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);
+ struct KexOptions options;
};
Kex *kex_setup(char *[PROPOSAL_MAX]);
--- /dev/null
+/*
+ * Copyright (c) 2001 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#include "ssh-gss.h"
+
+/* 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;
+ int plen,dlen;
+ 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)) {
+ 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(&plen);
+ 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, &dlen);
+ 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;
+ int plen;
+ int dlen=0;
+ unsigned int klen, kout;
+ unsigned char *kbuf;
+ unsigned char *hash;
+ DH *dh;
+ BIGNUM *shared_secret = 0;
+ BIGNUM *dh_client_pub = 0;
+ int type =0;
+
+ /* Initialise GSSAPI */
+
+ ssh_gssapi_build_ctx(&ctxt);
+ if (ssh_gssapi_id_kex(&ctxt,kex->name))
+ fatal("Unknown gssapi mechanism");
+ if (ssh_gssapi_acquire_cred(&ctxt))
+ fatal("Unable to acquire credentials for the server");
+
+ /* Initialise some bignums */
+ dh_client_pub = BN_new();
+ if (dh_client_pub == NULL)
+ fatal("dh_client_pub == NULL");
+
+ do {
+ debug("Wait SSH2_MSG_GSSAPI_INIT");
+ type = packet_read(&plen);
+ switch(type) {
+ case SSH2_MSG_KEXGSS_INIT:
+ if (dlen!=0)
+ fatal("Received KEXGSS_INIT after initialising");
+ recv_tok.value=packet_get_string(&recv_tok.length);
+ packet_get_bignum2(dh_client_pub, &dlen);
+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
+ break;
+ case SSH2_MSG_KEXGSS_CONTINUE:
+ if (dlen==0)
+ 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=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 ((maj_status=gss_get_mic(&min_status,
+ ctxt.context,
+ GSS_C_QOP_DEFAULT,
+ &gssbuf,
+ &msg_tok))) {
+ ssh_gssapi_error(maj_status,min_status);
+ 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();
+
+ /* Store the client name, and the delegated credentials for later
+ * use */
+ if (ssh_gssapi_getclient(&ctxt,&gssapi_client_type,
+ &gssapi_client_name,
+ &gssapi_client_creds)) {
+ fatal("Couldn't convert client name");
+ }
+
+ gss_release_buffer(&min_status, &send_tok);
+ 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 */
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;
KEY_RSA1,
KEY_RSA,
KEY_DSA,
+ KEY_NULL,
KEY_UNSPEC
};
enum fp_type {
--- /dev/null
+#!/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;
+}
+
+
#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
#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
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;
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
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;
#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
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;
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 = (access(KEYFILE, R_OK) == 0);
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
{ "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 },
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;
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. */
#include "canohost.h"
#include "session.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
#ifdef WITH_IRIX_PROJECT
#include <proj.h>
#endif /* WITH_IRIX_PROJECT */
session_proctitle(s);
+#if defined(GSSAPI)
+ temporarily_use_uid(s->pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+#endif
+
#if defined(USE_PAM)
do_pam_session(s->pw->pw_name, NULL);
do_pam_setcred(1);
ptyfd = s->ptyfd;
ttyfd = s->ttyfd;
+#if defined(GSSAPI)
+ temporarily_use_uid(s->pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+#endif
+
#if defined(USE_PAM)
do_pam_session(s->pw->pw_name, s->tty);
do_pam_setcred(1);
* 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)
{
exit(1);
}
endgrent();
+
# ifdef USE_PAM
/*
* PAM credentials may take the form of
copy_environment(&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);
do_authenticated2(Authctxt *authctxt)
{
server_loop2(authctxt);
+#if defined(GSSAPI)
+ ssh_gssapi_cleanup_creds(NULL);
+#endif
}
void session_close_by_pid(pid_t, int);
void session_close_by_channel(int, void *);
void session_destroy_all(void);
-
+void child_set_env(char ***envp, u_int *envsizep, const char *name,
+ const char *value);
#endif
--- /dev/null
+/*
+ * Copyright (c) 2001 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.
+ */
+
+#ifdef GSSAPI
+
+#include "kex.h"
+#include "buffer.h"
+
+#include <gssapi.h>
+
+#ifdef KRB5
+#ifndef HEIMDAL
+#include <gssapi_generic.h>
+
+/* 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-01 */
+#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 KEX_GSS_SHA1 "gss-group1-sha1-"
+
+/* draft-galb-secsh-gssapi-01 */
+#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60
+#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61
+#define SSH2_MSG_USERAUTH_GSSAPI_HASH 62
+#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63
+
+enum ssh_gss_id {
+#ifdef KRB5
+ GSS_KERBEROS,
+#endif
+#ifdef GSI
+ GSS_GSI,
+#endif /* GSI */
+ GSS_LAST_ENTRY
+};
+
+typedef struct ssh_gss_mech_struct {
+ char *enc_name;
+ char *name;
+ gss_OID_desc oid;
+} ssh_gssapi_mech;
+
+typedef struct {
+ OM_uint32 status; /* both */
+ gss_ctx_id_t context; /* both */
+ gss_name_t name; /* both */
+ gss_OID oid; /* client */
+ gss_cred_id_t creds; /* server */
+ gss_name_t client; /* server */
+ gss_cred_id_t client_creds; /* server */
+} Gssctxt;
+
+extern ssh_gssapi_mech supported_mechs[];
+extern gss_buffer_desc gssapi_client_name;
+extern gss_cred_id_t gssapi_client_creds;
+extern enum ssh_gss_id gssapi_client_type;
+
+char *ssh_gssapi_mechanisms(int server, char *host);
+int 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, 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);
+
+/* In the client */
+void ssh_gssapi_client(Kex *kex, char *host, struct sockaddr *hostaddr,
+ Buffer *client_kexinit, Buffer *server_kexinit);
+
+/* In the server */
+void ssh_gssapi_server(Kex *kex, Buffer *client_kexinit,
+ Buffer *server_kexinit);
+void ssh_gssapi_do_child(char ***envp, u_int *envsizep);
+void ssh_gssapi_cleanup_creds(void *ignored);
+void ssh_gssapi_storecreds();
+#endif /* GSSAPI */
#include "dispatch.h"
#include "canohost.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
/* import */
extern char *client_version_string;
extern char *server_version_string;
xxx_host = host;
xxx_hostaddr = hostaddr;
+#ifdef GSSAPI
+ /* This is a bit of a nasty kludge. This adds the GSSAPI included
+ * key exchange methods to the top of the list, allowing the GSSAPI
+ * code to decide whether each one should be included or not.
+ */
+ {
+ char *orig, *gss;
+ int len;
+ 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);
+ /* If we've got GSSAPI algorithms, then we also support the
+ * 'null' hostkey, as a last resort */
+ orig=myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
+ len = strlen(orig)+sizeof(",null");
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]=xmalloc(len);
+ snprintf(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],len,"%s,null",orig);
+ }
+ }
+#endif
+
if (options.ciphers == (char *)-1) {
log("No valid ciphers for protocol version 2 given, using defaults.");
options.ciphers = NULL;
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;
int nkeys;
/* kbd-interactive */
int info_req_seen;
+ /* generic */
+ void *methoddata;
};
struct Authmethod {
char *name; /* string to compare against server's list */
int userauth_kbdint(Authctxt *authctxt);
int userauth_hostbased(Authctxt *authctxt);
+#ifdef GSSAPI
+int userauth_external(Authctxt *authctxt);
+int userauth_gssapi(Authctxt *authctxt);
+void input_gssapi_response(int type, int plen, void *ctxt);
+void input_gssapi_token(int type, int plen, void *ctxt);
+void input_gssapi_hash(int type, int plen, void *ctxt);
+
+int gss_host_key_ok=0;
+#endif
+
void userauth(Authctxt *authctxt, char *authlist);
static int sign_and_send_pubkey(Authctxt *, Key *, sign_cb_fn *);
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,
authctxt.success = 0;
authctxt.method = authmethod_lookup("none");
authctxt.authlist = NULL;
+ authctxt.methoddata = NULL;
authctxt.keys = keys;
authctxt.nkeys = nkeys;
authctxt.info_req_seen = 0;
void
userauth(Authctxt *authctxt, char *authlist)
{
+ if (authctxt->methoddata!=NULL) {
+ xfree(authctxt->methoddata);
+ authctxt->methoddata=NULL;
+ }
+
if (authlist == NULL) {
authlist = authctxt->authlist;
} else {
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 */
}
}
+#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;
+
+ gssctxt=xmalloc(sizeof(Gssctxt));
+
+ /* 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;i<GSS_LAST_ENTRY;i++) {
+ packet_put_string(supported_mechs[i].oid.elements,
+ supported_mechs[i].oid.length);
+ }
+ packet_send();
+ packet_write_wait();
+
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,&input_gssapi_response);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,&input_gssapi_token);
+
+ return 1;
+}
+
+void
+input_gssapi_response(int type, int plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ OM_uint32 status,ms;
+ int oidlen;
+ char *oidv;
+ gss_buffer_desc send_tok;
+
+ if (authctxt == NULL)
+ fatal("input_gssapi_response: no authentication context");
+ gssctxt = authctxt->methoddata;
+
+ /* Setup our OID */
+ oidv=packet_get_string(&oidlen);
+ ssh_gssapi_set_oid_data(gssctxt,oidv,oidlen);
+
+ packet_done();
+
+ 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, int 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_done();
+
+ 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)
{
packet_put_cstring(authctxt->method->name);
packet_send();
return 1;
+
}
int
and applies to protocol version 2 only.
The default is
.Dq no .
+.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 HostKey
Specifies the file containing the private host keys (default
.Pa /etc/ssh_host_key )
#include "dispatch.h"
#include "channels.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
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);
}
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);
kex->server = 1;