key.o dispatch.o kex.o mac.o uuencode.o misc.o \
rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o kexgex.o \
kexdhc.o kexgexc.o scard.o msg.o progressmeter.o \
- entropy.o
+ entropy.o kexgssc.o gss-genr.o
SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
sshconnect.o sshconnect1.o sshconnect2.o
auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
auth2-none.o auth2-passwd.o auth2-pubkey.o \
monitor_mm.o monitor.o monitor_wrap.o monitor_fdpass.o \
- kexdhs.o kexgexs.o \
- auth-krb5.o auth-krb4.o \
+ kexdhs.o kexgexs.o kexgsss.o \
+ auth-krb5.o auth-krb4.o auth2-gss.o \
+ gss-serv.o gss-serv-krb5.o gss-serv-gsi.o \
loginrec.o auth-pam.o auth2-pam.o auth-sia.o md5crypt.o
MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
/* 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
/* 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
}
}
+/* 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)
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 */
authmsg,
method,
authctxt->valid ? "" : "illegal user ",
- authctxt->user,
+ (authctxt->user[0]) ? authctxt->user : "<implicit>",
get_remote_ipaddr(),
get_remote_port(),
info);
krb5_principal krb5_user;
char *krb5_ticket_file;
#endif
+ void *methoddata;
};
struct Authmethod {
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#include "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 ssh_gssapi_userauth_error(Gssctxt *ctxt);
+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);
+static void input_gssapi_errtok(int, u_int32_t, void *);
+
+/* 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;
+ char * doid = NULL;
+
+ 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 {
+ mechs--;
+
+ if (doid)
+ xfree(doid);
+
+ debug("Trying to get OID string");
+ doid = packet_get_string(&len);
+ debug("Got string");
+
+ if (doid[0]!=0x06 || doid[1]!=len-2) {
+ log("Mechanism OID received using the old encoding form");
+ oid.elements = doid;
+ oid.length = len;
+ } else {
+ oid.elements = doid + 2;
+ oid.length = len - 2;
+ }
+ gss_test_oid_set_member(&ms, &oid, supported, &present);
+ } while (mechs>0 && !present);
+
+ if (!present) {
+ xfree(doid);
+ return(0);
+ }
+
+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid)))) {
+ ssh_gssapi_userauth_error(ctxt);
+ return(0);
+ }
+
+ authctxt->methoddata=(void *)ctxt;
+
+ /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
+
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
+
+ /* Just return whatever they sent */
+ packet_put_string(doid,len);
+
+ packet_send();
+ packet_write_wait();
+ xfree(doid);
+
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ &input_gssapi_token);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,
+ &input_gssapi_errtok);
+ 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;
+ u_int len;
+
+ if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+ fatal("No authentication or GSSAPI context");
+
+ gssctxt=authctxt->methoddata;
+ recv_tok.value=packet_get_string(&len);
+ recv_tok.length=len; /* u_int vs. size_t */
+
+ maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+ &send_tok, NULL));
+ packet_check_eom();
+
+ if (GSS_ERROR(maj_status)) {
+ ssh_gssapi_userauth_error(gssctxt);
+ if (send_tok.length != 0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+ packet_put_string(send_tok.value,send_tok.length);
+ packet_send();
+ packet_write_wait();
+ }
+ authctxt->postponed = 0;
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+ userauth_finish(authctxt, 0, "gssapi");
+ } else {
+ 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 (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);
+ }
+ }
+
+ gss_release_buffer(&min_status, &send_tok);
+}
+
+static void
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ gss_buffer_desc send_tok,recv_tok;
+ OM_uint32 maj_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);
+
+ /* Push the error token into GSSAPI to see what it says */
+ maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+ &send_tok, NULL));
+ packet_check_eom();
+
+ /* We can't return anything to the client, even if we wanted to */
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,NULL);
+
+ /* The client will have already moved on to the next auth */
+
+}
+
+/* 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_ERRTOK, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+ userauth_finish(authctxt, authenticated, "gssapi");
+}
+
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
+ char *errstr;
+ OM_uint32 maj,min;
+
+ errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
+ if (errstr) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
+ packet_put_int(maj);
+ packet_put_int(min);
+ packet_put_cstring(errstr);
+ packet_put_cstring("");
+ packet_send();
+ packet_write_wait();
+ xfree(errstr);
+ }
+}
+
+Authmethod method_external = {
+ "external-keyx",
+ userauth_external,
+ &options.gss_authentication
+};
+
+Authmethod method_gssapi = {
+ "gssapi",
+ userauth_gssapi,
+ &options.gss_authentication
+};
+
+#endif /* GSSAPI */
#include "pathnames.h"
#include "monitor_wrap.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
/* import */
extern ServerOptions options;
extern u_char *session_id2;
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,
}
/* 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 */
{ "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},
+ "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_BUG_GSSAPI_BER},
+ { "OpenSSH_3.2*,"
+ "OpenSSH_3.3*,"
+ "OpenSSH_3.4*,"
+ "OpenSSH_3.5*", SSH_BUG_GSSAPI_BER},
{ "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
{ "OpenSSH*", 0 },
{ "*MindTerm*", 0 },
#define SSH_BUG_K5USER 0x00400000
#define SSH_BUG_PROBE 0x00800000
#define SSH_BUG_FIRSTKEX 0x01000000
+#define SSH_OLD_GSSAPI 0x02000000
+#define SSH_BUG_GSSAPI_BER 0x04000000
void enable_compat13(void);
void enable_compat20(void);
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"
else
LIBPAM="-lpam"
fi
+
AC_SUBST(LIBPAM)
fi
]
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"
AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
fi
+fi
+#end of GSI/Globus 2.0 mods
### Configure cryptographic random number support
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-2003 Simon Wilkinson. All rights reserved. *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include "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 "compat.h"
+#include "monitor_wrap.h"
+
+#include <netdb.h>
+
+#include "ssh-gss.h"
+
+typedef struct {
+ char *encoded;
+ gss_OID oid;
+} ssh_gss_kex_mapping;
+
+static ssh_gss_kex_mapping *gss_enc2oid;
+
+/* Return a list of the gss-group1-sha1-x mechanisms supported by this
+ * program.
+ *
+ * On the client side, we don't need to worry about whether we 'know'
+ * about the mechanism or not - we assume that any mechanism that we've been
+ * linked against is suitable for inclusion.
+ *
+ * XXX - We might want to make this configurable in the future, so as to
+ * XXX - allow the user control over which mechanisms to use.
+ */
+
+char *
+ssh_gssapi_client_mechanisms(char *host) {
+ gss_OID_set supported;
+ OM_uint32 min_status;
+ Buffer buf;
+ int i = 0;
+ char *mechs;
+ char *encoded;
+ int enclen;
+ char digest[EVP_MAX_MD_SIZE];
+ char deroid[2];
+ const EVP_MD *evp_md = EVP_md5();
+ EVP_MD_CTX md;
+ int oidpos=0;
+
+ if (datafellows & SSH_OLD_GSSAPI) return NULL;
+
+ gss_indicate_mechs(&min_status,&supported);
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping)
+ *((supported->count*2)+1));
+ } else {
+ gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping)
+ *(supported->count+1));
+ }
+
+ buffer_init(&buf);
+
+
+ for (i=0;i<supported->count;i++) {
+
+ gss_enc2oid[oidpos].encoded=NULL;
+
+ if (supported->elements[i].length<128 &&
+ ssh_gssapi_check_mechanism(&(supported->elements[i]),host)) {
+
+ /* Earlier versions of this code interpreted the
+ * spec incorrectly with regard to OID encoding. They
+ * also mis-encoded the krb5 OID. The following
+ * _temporary_ code interfaces with these broken
+ * servers */
+
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ char *bodge=NULL;
+ gss_OID_desc krb5oid={9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
+ gss_OID_desc gsioid={9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"};
+
+ if (supported->elements[i].length==krb5oid.length &&
+ memcmp(supported->elements[i].elements,
+ krb5oid.elements, krb5oid.length)==0) {
+ bodge="Se3H81ismmOC3OE+FwYCiQ==";
+ }
+
+ if (supported->elements[i].length==gsioid.length &&
+ memcmp(supported->elements[i].elements,
+ gsioid.elements, gsioid.length)==0) {
+ bodge="N3+k7/4wGxHyuP8Yxi4RhA==";
+ }
+
+ if (bodge) {
+ if (oidpos!=0) {
+ buffer_put_char(&buf,',');
+ }
+
+ buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
+ buffer_append(&buf, bodge, strlen(bodge));
+
+ gss_enc2oid[oidpos].oid=&(supported->elements[i]);
+ gss_enc2oid[oidpos].encoded=bodge;
+
+ oidpos++;
+ }
+ }
+
+ /* Add the required DER encoding octets and MD5 hash */
+ deroid[0]=0x06; /* Object Identifier */
+ deroid[1]=supported->elements[i].length;
+
+ EVP_DigestInit(&md, evp_md);
+ EVP_DigestUpdate(&md,deroid,2);
+ EVP_DigestUpdate(&md,
+ supported->elements[i].elements,
+ supported->elements[i].length);
+ EVP_DigestFinal(&md, digest, NULL);
+
+ /* Base64 encode it */
+ encoded=xmalloc(EVP_MD_size(evp_md)*2);
+ enclen=__b64_ntop(digest, EVP_MD_size(evp_md),
+ encoded,EVP_MD_size(evp_md)*2);
+ if (oidpos!=0) {
+ buffer_put_char(&buf,',');
+ }
+ buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
+ buffer_append(&buf, encoded, enclen);
+
+ debug("Mechanism encoded as %s",encoded);
+
+ gss_enc2oid[oidpos].oid=&(supported->elements[i]);
+ gss_enc2oid[oidpos].encoded=encoded;
+ oidpos++;
+ }
+ }
+ gss_enc2oid[oidpos].oid=NULL;
+ gss_enc2oid[oidpos].encoded=NULL;
+
+ buffer_put_char(&buf,'\0');
+
+ mechs=xmalloc(buffer_len(&buf));
+ buffer_get(&buf,mechs,buffer_len(&buf));
+ buffer_free(&buf);
+ if (strlen(mechs)==0)
+ return(NULL);
+ else
+ return(mechs);
+}
+
+gss_OID
+ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name) {
+ int i=0;
+
+ if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) {
+ return(NULL);
+ }
+
+ name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the ID string */
+
+ while (gss_enc2oid[i].encoded!=NULL &&
+ strcmp(name,gss_enc2oid[i].encoded)!=0) {
+ i++;
+ }
+
+ if (gss_enc2oid[i].oid!=NULL) {
+ ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid);
+ }
+
+ return gss_enc2oid[i].oid;
+}
+
+/* Check that the OID in a data stream matches that in the context */
+int ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) {
+
+ return (ctx!=NULL && ctx->oid != GSS_C_NO_OID &&
+ ctx->oid->length == len &&
+ memcmp(ctx->oid->elements,data,len)==0);
+}
+
+/* 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);
+}
+
+/* All this effort to report an error ... */
+
+void
+ssh_gssapi_error(Gssctxt *ctxt) {
+
+ debug(ssh_gssapi_last_error(ctxt,NULL,NULL));
+}
+
+char *
+ssh_gssapi_last_error(Gssctxt *ctxt,
+ OM_uint32 *major_status, OM_uint32 *minor_status) {
+ OM_uint32 lmin;
+ gss_buffer_desc msg;
+ OM_uint32 ctx;
+ Buffer b;
+ char *ret;
+
+ buffer_init(&b);
+
+ if (major_status!=NULL) *major_status=ctxt->major;
+ if (minor_status!=NULL) *minor_status=ctxt->minor;
+
+ ctx = 0;
+ /* The GSSAPI error */
+ do {
+ gss_display_status(&lmin, ctxt->major,
+ GSS_C_GSS_CODE, GSS_C_NULL_OID,
+ &ctx, &msg);
+
+ buffer_append(&b,msg.value,msg.length);
+ buffer_put_char(&b,'\n');
+
+ gss_release_buffer(&lmin, &msg);
+ } while (ctx!=0);
+
+ /* The mechanism specific error */
+ do {
+ gss_display_status(&lmin, ctxt->minor,
+ GSS_C_MECH_CODE, GSS_C_NULL_OID,
+ &ctx, &msg);
+
+ buffer_append(&b,msg.value,msg.length);
+ buffer_put_char(&b,'\n');
+
+ gss_release_buffer(&lmin, &msg);
+ } while (ctx!=0);
+
+ buffer_put_char(&b,'\0');
+ ret=xmalloc(buffer_len(&b));
+ buffer_get(&b,ret,buffer_len(&b));
+ buffer_free(&b);
+ return(ret);
+}
+
+/* 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)->major=0;
+ (*ctx)->minor=0;
+ (*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)
+{
+ int deleg_flag = 0;
+
+ if (deleg_creds) {
+ deleg_flag=GSS_C_DELEG_FLAG;
+ debug("Delegating credentials");
+ }
+
+ ctx->major=gss_init_sec_context(&ctx->minor,
+ 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);
+ if (GSS_ERROR(ctx->major)) {
+ ssh_gssapi_error(ctx);
+ }
+ return(ctx->major);
+}
+
+/* Create a service name for the given host */
+OM_uint32
+ssh_gssapi_import_name(Gssctxt *ctx, const char *host) {
+ gss_buffer_desc gssbuf;
+
+ 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 ((ctx->major=gss_import_name(&ctx->minor,
+ &gssbuf,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ &ctx->name))) {
+ ssh_gssapi_error(ctx);
+ }
+
+ xfree(gssbuf.value);
+ return(ctx->major);
+}
+
+/* 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 status;
+ char lname[MAXHOSTNAMELEN];
+ gss_OID_set oidset;
+
+ gss_create_empty_oid_set(&status,&oidset);
+ gss_add_oid_set_member(&status,ctx->oid,&oidset);
+
+ if (gethostname(lname, MAXHOSTNAMELEN)) {
+ return(-1);
+ }
+
+ if (GSS_ERROR(ssh_gssapi_import_name(ctx,lname))) {
+ return(ctx->major);
+ }
+
+ if ((ctx->major=gss_acquire_cred(&ctx->minor,
+ ctx->name,
+ 0,
+ oidset,
+ GSS_C_ACCEPT,
+ &ctx->creds,
+ NULL,
+ NULL))) {
+ ssh_gssapi_error(ctx);
+ }
+
+ gss_release_oid_set(&status, &oidset);
+ return(ctx->major);
+}
+
+OM_uint32
+ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
+
+ if ((ctx->major=gss_get_mic(&ctx->minor,ctx->context,
+ GSS_C_QOP_DEFAULT, buffer, hash))) {
+ ssh_gssapi_error(ctx);
+ }
+
+ return(ctx->major);
+}
+
+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));
+}
+
+int
+ssh_gssapi_check_mechanism(gss_OID oid, char *host) {
+ Gssctxt * ctx = NULL;
+ gss_buffer_desc token;
+ OM_uint32 major,minor;
+
+ ssh_gssapi_build_ctx(&ctx);
+ ssh_gssapi_set_oid(ctx,oid);
+ ssh_gssapi_import_name(ctx,host);
+ major=ssh_gssapi_init_ctx(ctx,0, GSS_C_NO_BUFFER, &token, NULL);
+ gss_release_buffer(&minor,&token);
+ ssh_gssapi_delete_ctx(&ctx);
+ return(!GSS_ERROR(major));
+}
+
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#ifdef GSI
+
+#include "auth.h"
+#include "auth-pam.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "servconf.h"
+
+#include "ssh-gss.h"
+
+#include <globus_gss_assist.h>
+
+static int ssh_gssapi_gsi_userok(ssh_gssapi_client *client, char *name);
+static int ssh_gssapi_gsi_localname(ssh_gssapi_client *client, char **user);
+static void ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client);
+
+ssh_gssapi_mech gssapi_gsi_mech_old = {
+ "N3+k7/4wGxHyuP8Yxi4RhA==",
+ "GSI",
+ {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"},
+ NULL,
+ &ssh_gssapi_gsi_userok,
+ &ssh_gssapi_gsi_localname,
+ &ssh_gssapi_gsi_storecreds
+};
+
+ssh_gssapi_mech gssapi_gsi_mech = {
+ "dZuIebMjgUqaxvbF7hDbAw==",
+ "GSI",
+ {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"},
+ NULL,
+ &ssh_gssapi_gsi_userok,
+ &ssh_gssapi_gsi_localname,
+ &ssh_gssapi_gsi_storecreds
+};
+
+/*
+ * 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.
+ */
+static int
+ssh_gssapi_gsi_userok(ssh_gssapi_client *client, char *name)
+{
+ int authorized = 0;
+
+#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
+ if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
+ return 0;
+ }
+#endif
+
+ /* This returns 0 on success */
+ authorized = (globus_gss_assist_userok(client->name.value,
+ name) == 0);
+
+ log("GSI user %s is%s authorized as target user %s",
+ (char *) client->name.value, (authorized ? "" : " not"), name);
+
+ return authorized;
+}
+
+/*
+ * Return the local username associated with the GSI credentials.
+ */
+int
+ssh_gssapi_gsi_localname(ssh_gssapi_client *client, char **user)
+{
+#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
+ if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
+ return 0;
+ }
+#endif
+ return(globus_gss_assist_gridmap(client->name.value, user) == 0);
+}
+
+/*
+ * Export GSI credentials to disk.
+ */
+static void
+ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client)
+{
+ OM_uint32 major_status;
+ OM_uint32 minor_status;
+ gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER;
+ char * p;
+
+ if (!client || !client->creds) {
+ return;
+ }
+
+ major_status = gss_export_cred(&minor_status,
+ client->creds,
+ GSS_C_NO_OID,
+ 1,
+ &export_cred);
+ if (GSS_ERROR(major_status) && major_status != GSS_S_UNAVAILABLE) {
+ Gssctxt *ctx;
+ ssh_gssapi_build_ctx(&ctx);
+ ctx->major = major_status;
+ ctx->minor = minor_status;
+ ssh_gssapi_set_oid(ctx, &gssapi_gsi_mech.oid);
+ ssh_gssapi_error(ctx);
+ ssh_gssapi_delete_ctx(&ctx);
+ return;
+ }
+
+ p = strchr((char *) export_cred.value, '=');
+ if (p == NULL) {
+ log("Failed to parse exported credentials string '%.100s'",
+ (char *)export_cred.value);
+ gss_release_buffer(&minor_status, &export_cred);
+ return;
+ }
+ *p++ = '\0';
+ if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0) {
+ client->store.envvar = strdup("X509_USER_PROXY");
+ } else {
+ client->store.envvar = strdup((char *)export_cred.value);
+ }
+ client->store.envval = strdup(p);
+#ifdef USE_PAM
+ do_pam_putenv(client->store.envvar, client->store.envval);
+#endif
+ if (strncmp(p, "FILE:", 5) == 0) {
+ p += 5;
+ }
+ if (access(p, R_OK) == 0) {
+ client->store.filename = strdup(p);
+ }
+ gss_release_buffer(&minor_status, &export_cred);
+}
+
+#endif /* GSI */
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#ifdef KRB5
+
+#include "auth.h"
+#include "auth-pam.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "servconf.h"
+
+#include "ssh-gss.h"
+
+extern ServerOptions options;
+
+#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 */
+
+static 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
+ */
+
+static int
+ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) {
+ krb5_principal princ;
+ int retval;
+
+ if (ssh_gssapi_krb5_init() == 0)
+ return 0;
+
+ if ((retval=krb5_parse_name(krb_context, 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 *)client->name.value);
+ }
+ else
+ retval = 0;
+
+ krb5_free_principal(krb_context, princ);
+ return retval;
+}
+
+/* Retrieve the local username associated with a set of Kerberos
+ * credentials. Hopefully we can use this for the 'empty' username
+ * logins discussed in the draft */
+static int
+ssh_gssapi_krb5_localname(ssh_gssapi_client *client, char **user) {
+ krb5_principal princ;
+ int retval;
+
+ if (ssh_gssapi_krb5_init() == 0)
+ return 0;
+
+ if ((retval=krb5_parse_name(krb_context, client->name.value,
+ &princ))) {
+ log("krb5_parse_name(): %.100s",
+ krb5_get_err_text(krb_context,retval));
+ return 0;
+ }
+
+ /* We've got to return a malloc'd string */
+ *user = (char *)xmalloc(256);
+ if (krb5_aname_to_localname(krb_context, princ, 256, *user)) {
+ xfree(*user);
+ *user = NULL;
+ return(0);
+ }
+
+ return(1);
+}
+
+/* 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.
+ */
+
+static void
+ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) {
+ 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 (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, 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,
+ 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,
+ client->creds,
+ ccache))) {
+ log("gss_krb5_copy_ccache() failed");
+ krb5_cc_destroy(krb_context,ccache);
+ return;
+ }
+ #endif
+
+ krb5_cc_close(krb_context,ccache);
+
+#ifdef USE_PAM
+ do_pam_putenv("KRB5CCNAME",name);
+#endif
+
+ client->store.filename=strdup(ccname);
+ client->store.envvar="KRB5CCNAME";
+ client->store.envval=strdup(name);
+
+ return;
+}
+
+/* We've been using a wrongly encoded mechanism ID for yonks */
+
+ssh_gssapi_mech gssapi_kerberos_mech_old = {
+ "Se3H81ismmOC3OE+FwYCiQ==",
+ "Kerberos",
+ {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"},
+ &ssh_gssapi_krb5_init,
+ &ssh_gssapi_krb5_userok,
+ &ssh_gssapi_krb5_localname,
+ &ssh_gssapi_krb5_storecreds
+};
+
+ssh_gssapi_mech gssapi_kerberos_mech = {
+ "toWM5Slw5Ew8Mqkay+al2g==",
+ "Kerberos",
+ {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"},
+ NULL,
+ &ssh_gssapi_krb5_userok,
+ &ssh_gssapi_krb5_localname,
+ &ssh_gssapi_krb5_storecreds
+};
+
+#endif /* KRB5 */
+
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include "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 "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;
+
+static ssh_gssapi_client gssapi_client =
+ { {0,NULL}, GSS_C_NO_CREDENTIAL, NULL, {NULL,NULL,NULL}};
+
+ssh_gssapi_mech gssapi_null_mech
+ = {NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
+
+#ifdef KRB5
+extern ssh_gssapi_mech gssapi_kerberos_mech;
+extern ssh_gssapi_mech gssapi_kerberos_mech_old;
+#endif
+#ifdef GSI
+extern ssh_gssapi_mech gssapi_gsi_mech;
+extern ssh_gssapi_mech gssapi_gsi_mech_old;
+#endif
+
+ssh_gssapi_mech* supported_mechs[]= {
+#ifdef KRB5
+ &gssapi_kerberos_mech,
+ &gssapi_kerberos_mech_old, /* Support for legacy clients */
+#endif
+#ifdef GSI
+ &gssapi_gsi_mech,
+ &gssapi_gsi_mech_old, /* Support for legacy clients */
+#endif
+ &gssapi_null_mech,
+};
+
+/* Return a list of the gss-group1-sha1-x mechanisms supported by this
+ * program.
+ *
+ * We only support the mechanisms that we've indicated in the list above,
+ * but we check that they're supported by the GSSAPI mechanism on the
+ * machine. We also check, before including them in the list, that
+ * we have the necesary information in order to carry out the key exchange
+ * (that is, that the user has credentials, the server's creds are accessible,
+ * etc)
+ *
+ * The way that this is done is fairly nasty, as we do a lot of work that
+ * is then thrown away. This should possibly be implemented with a cache
+ * that stores the results (in an expanded Gssctxt structure), which are
+ * then used by the first calls if that key exchange mechanism is chosen.
+ */
+
+/* Unpriviledged */
+char *
+ssh_gssapi_server_mechanisms() {
+ gss_OID_set supported;
+ Gssctxt *ctx = NULL;
+ OM_uint32 maj_status, min_status;
+ Buffer buf;
+ int i = 0;
+ int first = 0;
+ int present;
+ char * mechs;
+
+ if (datafellows & SSH_OLD_GSSAPI) return NULL;
+
+ ssh_gssapi_supported_oids(&supported);
+
+ buffer_init(&buf);
+
+ while(supported_mechs[i]->name != NULL) {
+ if ((maj_status=gss_test_oid_set_member(&min_status,
+ &supported_mechs[i]->oid,
+ supported,
+ &present))) {
+ present=0;
+ }
+
+ if (present) {
+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx,
+ &supported_mechs[i]->oid)))) {
+ /* Append gss_group1_sha1_x to our list */
+ if (first++!=0)
+ buffer_put_char(&buf,',');
+ buffer_append(&buf, KEX_GSS_SHA1,
+ sizeof(KEX_GSS_SHA1)-1);
+ buffer_append(&buf,
+ supported_mechs[i]->enc_name,
+ strlen(supported_mechs[i]->enc_name));
+ debug("GSSAPI mechanism %s (%s%s) supported",
+ supported_mechs[i]->name, KEX_GSS_SHA1,
+ supported_mechs[i]->enc_name);
+ } else {
+ debug("no credentials for GSSAPI mechanism %s",
+ supported_mechs[i]->name);
+ }
+ } else {
+ debug("GSSAPI mechanism %s not supported",
+ supported_mechs[i]->name);
+ }
+ ssh_gssapi_delete_ctx(&ctx);
+ i++;
+ }
+
+ buffer_put_char(&buf,'\0');
+
+ mechs=xmalloc(buffer_len(&buf));
+ buffer_get(&buf,mechs,buffer_len(&buf));
+ buffer_free(&buf);
+ if (strlen(mechs)==0)
+ return(NULL);
+ else
+ return(mechs);
+}
+
+/* Unpriviledged */
+void ssh_gssapi_supported_oids(gss_OID_set *oidset) {
+ int i =0;
+ OM_uint32 maj_status,min_status;
+ int present;
+ gss_OID_set supported;
+
+ gss_create_empty_oid_set(&min_status,oidset);
+ PRIVSEP(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++;
+ }
+}
+
+/* Find out which GSS type (out of the list we define in ssh-gss.h) a
+ * particular connection is using
+ */
+
+/* Priviledged (called ssh_gssapi_accept_ctx -> ssh_gssapi_getclient ->) */
+ssh_gssapi_mech *
+ssh_gssapi_get_ctype(Gssctxt *ctxt) {
+ int i=0;
+
+ while(supported_mechs[i]->name!=NULL) {
+ if (supported_mechs[i]->oid.length == ctxt->oid->length &&
+ (memcmp(supported_mechs[i]->oid.elements,
+ ctxt->oid->elements,ctxt->oid->length)==0)) {
+ return supported_mechs[i];
+ }
+ i++;
+ }
+ return NULL;
+}
+
+/* Return the OID that corresponds to the given context name */
+
+/* Unpriviledged */
+gss_OID
+ssh_gssapi_server_id_kex(char *name) {
+ int i=0;
+
+ if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) {
+ return(NULL);
+ }
+
+ name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the MIME string */
+
+ while (supported_mechs[i]->name!=NULL &&
+ strcmp(name,supported_mechs[i]->enc_name)!=0) {
+ i++;
+ }
+
+ if (supported_mechs[i]->name==NULL)
+ return (NULL);
+
+ debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i]->name,
+ KEX_GSS_SHA1, supported_mechs[i]->enc_name);
+
+ return &supported_mechs[i]->oid;
+}
+
+/* Wrapper around accept_sec_context
+ * Requires that the context contains:
+ * oid
+ * credentials (from ssh_gssapi_acquire_cred)
+ */
+/* Priviledged */
+OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
+ gss_buffer_desc *send_tok, OM_uint32 *flags)
+{
+ OM_uint32 status;
+ gss_OID mech;
+
+ ctx->major=gss_accept_sec_context(&ctx->minor,
+ &ctx->context,
+ ctx->creds,
+ recv_tok,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &ctx->client,
+ &mech, /* read-only pointer */
+ send_tok,
+ flags,
+ NULL,
+ &ctx->client_creds);
+ if (GSS_ERROR(ctx->major)) {
+ ssh_gssapi_error(ctx);
+ }
+
+ 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) */
+
+ status=ctx->major;
+
+ /* 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))) &&
+ (ctx->major == GSS_S_COMPLETE)) {
+ if (ssh_gssapi_getclient(ctx,&gssapi_client.mech,
+ &gssapi_client.name,
+ &gssapi_client.creds))
+ fatal("Couldn't convert client name");
+ }
+
+ /* Make sure that the getclient call hasn't stamped on this */
+ return(status);
+}
+
+/* Extract the client details from a given context. This can only reliably
+ * be called once for a context */
+
+/* Priviledged (called from accept_secure_ctx) */
+OM_uint32
+ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_mech **type,
+ gss_buffer_desc *name, gss_cred_id_t *creds) {
+
+ *type=ssh_gssapi_get_ctype(ctx);
+ if ((ctx->major=gss_display_name(&ctx->minor,ctx->client,name,NULL))) {
+ ssh_gssapi_error(ctx);
+ return(ctx->major);
+ }
+
+ /* 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(ctx->major);
+}
+
+/* As user - called through fatal cleanup hook */
+void
+ssh_gssapi_cleanup_creds(void *ignored)
+{
+ if (gssapi_client.store.filename!=NULL) {
+ /* Unlink probably isn't sufficient */
+ debug("removing gssapi cred file\"%s\"",gssapi_client.store.filename);
+ unlink(gssapi_client.store.filename);
+ }
+}
+
+/* As user */
+void
+ssh_gssapi_storecreds()
+{
+ if (gssapi_client.mech && gssapi_client.mech->storecreds) {
+ (*gssapi_client.mech->storecreds)(&gssapi_client);
+ if (options.gss_cleanup_creds) {
+ fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
+ }
+ } else {
+ debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
+ }
+}
+
+/* This allows GSSAPI methods to do things to the childs environment based
+ * on the passed authentication process and credentials.
+ */
+/* As user */
+void
+ssh_gssapi_do_child(char ***envp, u_int *envsizep)
+{
+
+ if (gssapi_client.store.envvar!=NULL &&
+ gssapi_client.store.envval!=NULL) {
+
+ debug("Setting %s to %s", gssapi_client.store.envvar,
+ gssapi_client.store.envval);
+ child_set_env(envp, envsizep, gssapi_client.store.envvar,
+ gssapi_client.store.envval);
+ }
+}
+
+/* Priviledged */
+int
+ssh_gssapi_userok(char *user)
+{
+ if (gssapi_client.name.length==0 ||
+ gssapi_client.name.value==NULL) {
+ debug("No suitable client data");
+ return 0;
+ }
+ if (gssapi_client.mech && gssapi_client.mech->userok) {
+ return((*gssapi_client.mech->userok)(&gssapi_client,user));
+ } else {
+ debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
+ }
+ return(0);
+}
+
+/* Priviledged */
+int
+ssh_gssapi_localname(char **user)
+{
+ *user = NULL;
+ if (gssapi_client.name.length==0 ||
+ gssapi_client.name.value==NULL) {
+ debug("No suitable client data");
+ return(0);;
+ }
+ if (gssapi_client.mech && gssapi_client.mech->localname) {
+ return((*gssapi_client.mech->localname)(&gssapi_client,user));
+ } else {
+ debug("Unknown client authentication type");
+ }
+ return(0);
+}
+#endif
#include "dispatch.h"
#include "monitor.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
#define KEX_COOKIE_LEN 16
/* prototype */
k->kex_type = KEX_DH_GRP1_SHA1;
} else if (strcmp(k->name, KEX_DHGEX) == 0) {
k->kex_type = KEX_DH_GEX_SHA1;
+#ifdef GSSAPI
+ } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) {
+ k->kex_type = KEX_GSS_GRP1_SHA1;
+#endif
} else
fatal("bad kex alg %s", k->name);
}
enum kex_exchange {
KEX_DH_GRP1_SHA1,
KEX_DH_GEX_SHA1,
+ KEX_GSS_GRP1_SHA1,
KEX_MAX
};
Mac mac;
Comp comp;
};
+
+struct KexOptions {
+ int gss_deleg_creds;
+};
+
struct Kex {
u_char *session_id;
u_int session_id_len;
int flags;
char *client_version_string;
char *server_version_string;
+ struct KexOptions options;
int (*verify_host_key)(Key *);
Key *(*load_host_key)(int);
int (*host_key_index)(Key *);
void kexdh_server(Kex *);
void kexgex_client(Kex *);
void kexgex_server(Kex *);
+#ifdef GSSAPI
+void kexgss_client(Kex *);
+void kexgss_server(Kex *);
+#endif
u_char *
kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "canohost.h"
+#include "ssh2.h"
+#include "ssh-gss.h"
+
+void
+kexgss_client(Kex *kex)
+{
+ gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok, *token_ptr;
+ Gssctxt *ctxt;
+ OM_uint32 maj_status, min_status, ret_flags;
+ unsigned int klen, kout;
+ DH *dh;
+ BIGNUM *dh_server_pub = 0;
+ BIGNUM *shared_secret = 0;
+ unsigned char *kbuf;
+ unsigned char *hash;
+ unsigned char *serverhostkey;
+ char *msg;
+ char *lang;
+ int type = 0;
+ int first = 1;
+ int slen = 0;
+ u_int strlen;
+
+ /* Initialise our GSSAPI world */
+ ssh_gssapi_build_ctx(&ctxt);
+ if (ssh_gssapi_client_id_kex(ctxt,kex->name)==NULL) {
+ fatal("Couldn't identify host exchange");
+ }
+
+ if (ssh_gssapi_import_name(ctxt,get_canonical_hostname(1))) {
+ fatal("Couldn't import hostname ");
+ }
+
+ /* This code should match that in ssh_dh1_client */
+
+ /* Step 1 - e is dh->pub_key */
+ dh = dh_new_group1();
+ dh_gen_key(dh, kex->we_need * 8);
+
+ /* This is f, we initialise it now to make life easier */
+ dh_server_pub = BN_new();
+ if (dh_server_pub == NULL) {
+ fatal("dh_server_pub == NULL");
+ }
+
+ token_ptr = GSS_C_NO_BUFFER;
+
+ do {
+ debug("Calling gss_init_sec_context");
+
+ maj_status=ssh_gssapi_init_ctx(ctxt,
+ kex->options.gss_deleg_creds,
+ token_ptr,&send_tok,
+ &ret_flags);
+
+ if (GSS_ERROR(maj_status)) {
+ if (send_tok.length!=0) {
+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+ packet_put_string(send_tok.value,
+ send_tok.length);
+ }
+ fatal("gss_init_context failed");
+ }
+
+ /* If we've got an old receive buffer get rid of it */
+ if (token_ptr != GSS_C_NO_BUFFER)
+ (void) gss_release_buffer(&min_status, &recv_tok);
+
+
+ if (maj_status == GSS_S_COMPLETE) {
+ /* If mutual state flag is not true, kex fails */
+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) {
+ fatal("Mutual authentication failed");
+ }
+ /* If integ avail flag is not true kex fails */
+ if (!(ret_flags & GSS_C_INTEG_FLAG)) {
+ fatal("Integrity check failed");
+ }
+ }
+
+ /* If we have data to send, then the last message that we
+ * received cannot have been a 'complete'. */
+ if (send_tok.length !=0) {
+ if (first) {
+ packet_start(SSH2_MSG_KEXGSS_INIT);
+ packet_put_string(send_tok.value,
+ send_tok.length);
+ packet_put_bignum2(dh->pub_key);
+ first=0;
+ } else {
+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+ packet_put_string(send_tok.value,
+ send_tok.length);
+ }
+ packet_send();
+ packet_write_wait();
+
+
+ /* If we've sent them data, they'd better be polite
+ * and reply. */
+
+ 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(&strlen);
+ recv_tok.length=strlen; /* u_int vs. size_t */
+ break;
+ case SSH2_MSG_KEXGSS_COMPLETE:
+ debug("Received GSSAPI_COMPLETE");
+ packet_get_bignum2(dh_server_pub);
+ msg_tok.value=packet_get_string(&strlen);
+ msg_tok.length=strlen; /* u_int vs. size_t */
+
+ /* Is there a token included? */
+ if (packet_get_char()) {
+ recv_tok.value=
+ packet_get_string(&strlen);
+ recv_tok.length=strlen; /*u_int/size_t*/
+ /* If we're already complete - protocol error */
+ if (maj_status == GSS_S_COMPLETE)
+ packet_disconnect("Protocol error: received token when complete");
+ } else {
+ /* No token included */
+ if (maj_status != GSS_S_COMPLETE)
+ packet_disconnect("Protocol error: did not receive final token");
+ }
+ break;
+ case SSH2_MSG_KEXGSS_ERROR:
+ debug("Received Error");
+ maj_status=packet_get_int();
+ min_status=packet_get_int();
+ msg=packet_get_string(NULL);
+ lang=packet_get_string(NULL);
+ fprintf(stderr,"GSSAPI Error: \n%s",msg);
+ default:
+ packet_disconnect("Protocol error: didn't expect packet type %d",
+ type);
+ }
+ token_ptr=&recv_tok;
+ } else {
+ /* No data, and not complete */
+ if (maj_status!=GSS_S_COMPLETE) {
+ fatal("Not complete, and no token output");
+ }
+ }
+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
+
+ /* We _must_ have received a COMPLETE message in reply from the
+ * server, which will have set dh_server_pub and msg_tok */
+
+ if (type!=SSH2_MSG_KEXGSS_COMPLETE)
+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
+
+ /* Check f in range [1, p-1] */
+ if (!dh_pub_is_valid(dh, dh_server_pub))
+ packet_disconnect("bad server public DH value");
+
+ /* compute K=f^x mod p */
+ klen = DH_size(dh);
+ kbuf = xmalloc(klen);
+ kout = DH_compute_key(kbuf, dh_server_pub, dh);
+
+ shared_secret = BN_new();
+ BN_bin2bn(kbuf,kout, shared_secret);
+ memset(kbuf, 0, klen);
+ xfree(kbuf);
+
+ /* The GSS hash is identical to the DH one */
+ hash = kex_dh_hash(
+ kex->client_version_string,
+ kex->server_version_string,
+ buffer_ptr(&kex->my), buffer_len(&kex->my),
+ buffer_ptr(&kex->peer), buffer_len(&kex->peer),
+ serverhostkey, slen, /* server host key */
+ dh->pub_key, /* e */
+ dh_server_pub, /* f */
+ shared_secret /* K */
+ );
+
+ gssbuf.value=hash;
+ gssbuf.length=20;
+
+ /* Verify that H matches the token we just got. */
+ if ((maj_status = gss_verify_mic(&min_status,
+ ctxt->context,
+ &gssbuf,
+ &msg_tok,
+ NULL))) {
+
+ packet_disconnect("Hash's MIC didn't verify");
+ }
+
+ DH_free(dh);
+ ssh_gssapi_delete_ctx(&ctxt);
+ /* save session id */
+ if (kex->session_id == NULL) {
+ kex->session_id_len = 20;
+ kex->session_id = xmalloc(kex->session_id_len);
+ memcpy(kex->session_id, hash, kex->session_id_len);
+ }
+
+ kex_derive_keys(kex, hash, shared_secret);
+ BN_clear_free(shared_secret);
+ kex_finish(kex);
+}
+
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#include "ssh-gss.h"
+#include "monitor_wrap.h"
+
+static void kex_gss_send_error(Gssctxt *ctxt);
+
+void
+kexgss_server(Kex *kex)
+{
+ OM_uint32 maj_status, min_status;
+
+ /* Some GSSAPI implementations use the input value of ret_flags (an
+ * output variable) as a means of triggering mechanism specific
+ * features. Initializing it to zero avoids inadvertently
+ * activating this non-standard behaviour.*/
+
+ OM_uint32 ret_flags = 0;
+ gss_buffer_desc gssbuf,send_tok,recv_tok,msg_tok;
+ Gssctxt *ctxt = NULL;
+ unsigned int klen, kout;
+ unsigned char *kbuf;
+ unsigned char *hash;
+ DH *dh;
+ BIGNUM *shared_secret = NULL;
+ BIGNUM *dh_client_pub = NULL;
+ int type =0;
+ u_int slen;
+ gss_OID oid;
+
+ /* Initialise GSSAPI */
+
+ debug2("%s: Identifying %s",__func__,kex->name);
+ oid=ssh_gssapi_server_id_kex(kex->name);
+ if (oid==NULL) {
+ fatal("Unknown gssapi mechanism");
+ }
+
+ debug2("%s: Acquiring credentials",__func__);
+
+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid)))) {
+ kex_gss_send_error(ctxt);
+ fatal("Unable to acquire credentials for the server");
+ }
+
+ do {
+ debug("Wait SSH2_MSG_GSSAPI_INIT");
+ type = packet_read();
+ switch(type) {
+ case SSH2_MSG_KEXGSS_INIT:
+ if (dh_client_pub!=NULL)
+ fatal("Received KEXGSS_INIT after initialising");
+ recv_tok.value=packet_get_string(&slen);
+ recv_tok.length=slen; /* int vs. size_t */
+
+ dh_client_pub = BN_new();
+
+ if (dh_client_pub == NULL)
+ fatal("dh_client_pub == NULL");
+ packet_get_bignum2(dh_client_pub);
+
+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
+ break;
+ case SSH2_MSG_KEXGSS_CONTINUE:
+ recv_tok.value=packet_get_string(&slen);
+ recv_tok.length=slen; /* int vs. size_t */
+ break;
+ default:
+ packet_disconnect("Protocol error: didn't expect packet type %d",
+ type);
+ }
+
+ maj_status=PRIVSEP(ssh_gssapi_accept_ctx(ctxt,&recv_tok,
+ &send_tok, &ret_flags));
+
+ gss_release_buffer(&min_status,&recv_tok);
+
+ if (maj_status!=GSS_S_COMPLETE && send_tok.length==0) {
+ fatal("Zero length token output when incomplete");
+ }
+
+ if (dh_client_pub == NULL)
+ fatal("No client public key");
+
+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
+ debug("Sending GSSAPI_CONTINUE");
+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+ packet_put_string(send_tok.value,send_tok.length);
+ packet_send();
+ packet_write_wait();
+ gss_release_buffer(&min_status, &send_tok);
+ }
+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
+
+ if (GSS_ERROR(maj_status)) {
+ kex_gss_send_error(ctxt);
+ if (send_tok.length>0) {
+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+ packet_put_string(send_tok.value,send_tok.length);
+ packet_send();
+ packet_write_wait();
+ }
+ fatal("accept_ctx died");
+ }
+
+ debug("gss_complete");
+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
+ fatal("mutual authentication flag wasn't set");
+
+ if (!(ret_flags & GSS_C_INTEG_FLAG))
+ fatal("Integrity flag wasn't set");
+
+ dh = dh_new_group1();
+ dh_gen_key(dh, kex->we_need * 8);
+
+ if (!dh_pub_is_valid(dh, dh_client_pub))
+ packet_disconnect("bad client public DH value");
+
+ klen = DH_size(dh);
+ kbuf = xmalloc(klen);
+ kout = DH_compute_key(kbuf, dh_client_pub, dh);
+
+ shared_secret = BN_new();
+ BN_bin2bn(kbuf, kout, shared_secret);
+ memset(kbuf, 0, klen);
+ xfree(kbuf);
+
+ /* The GSSAPI hash is identical to the Diffie Helman one */
+ hash = kex_dh_hash(
+ kex->client_version_string,
+ kex->server_version_string,
+ buffer_ptr(&kex->peer), buffer_len(&kex->peer),
+ buffer_ptr(&kex->my), buffer_len(&kex->my),
+ NULL, 0, /* Change this if we start sending host keys */
+ dh_client_pub,
+ dh->pub_key,
+ shared_secret
+ );
+ BN_free(dh_client_pub);
+
+ if (kex->session_id == NULL) {
+ kex->session_id_len = 20;
+ kex->session_id = xmalloc(kex->session_id_len);
+ memcpy(kex->session_id, hash, kex->session_id_len);
+ }
+
+ gssbuf.value = hash;
+ gssbuf.length = 20; /* Hashlen appears to always be 20 */
+
+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) {
+ kex_gss_send_error(ctxt);
+ fatal("Couldn't get MIC");
+ }
+
+ packet_start(SSH2_MSG_KEXGSS_COMPLETE);
+ packet_put_bignum2(dh->pub_key);
+ packet_put_string((char *)msg_tok.value,msg_tok.length);
+
+ if (send_tok.length!=0) {
+ packet_put_char(1); /* true */
+ packet_put_string((char *)send_tok.value,send_tok.length);
+ } else {
+ packet_put_char(0); /* false */
+ }
+ packet_send();
+ packet_write_wait();
+
+ /* We used to store the client name and credentials here for later
+ * use. With privsep, its easier to do this as a by product of the
+ * call to accept_context, which stores delegated information when
+ * the context is complete */
+
+ gss_release_buffer(&min_status, &send_tok);
+
+ /* If we've got a context, delete it. It may be NULL if we've been
+ * using privsep */
+ ssh_gssapi_delete_ctx(&ctxt);
+
+ DH_free(dh);
+
+ kex_derive_keys(kex, hash, shared_secret);
+ BN_clear_free(shared_secret);
+ kex_finish(kex);
+}
+
+static void
+kex_gss_send_error(Gssctxt *ctxt) {
+ char *errstr;
+ OM_uint32 maj,min;
+
+ errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
+ if (errstr) {
+ packet_start(SSH2_MSG_KEXGSS_ERROR);
+ packet_put_int(maj);
+ packet_put_int(min);
+ packet_put_cstring(errstr);
+ packet_put_cstring("");
+ packet_send();
+ packet_write_wait();
+ /* XXX - We should probably log the error locally here */
+ xfree(errstr);
+ }
+}
+#endif /* GSSAPI */
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;
+use Data::Dumper;
+
+$oid=shift;
+my $asn=Convert::ASN1->new;
+$asn->prepare("oid OBJECT IDENTIFIER");
+$encoded=$asn->encode(oid => $oid);
+Convert::ASN1::asn_dump($encoded);
+print Dumper($asn->decode($encoded));
+
+@entries=unpack("C*",$encoded);
+
+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;
+}
+
+
#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;
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 *);
+int mm_answer_gss_error(int, Buffer *);
+int mm_answer_gss_indicate_mechs(int, Buffer *);
+int mm_answer_gss_localname(int, Buffer *);
+#endif
+
static Authctxt *authctxt;
static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */
#ifdef SKEY
{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_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error},
+ {MONITOR_REQ_GSSMECHS, MON_ISAUTH, mm_answer_gss_indicate_mechs},
+ {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
+/* Turn this off until we use it */
+#if 0
+ {MONITOR_REQ_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname},
+#endif
#endif
{MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed},
{MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify},
};
struct mon_table mon_dispatch_postauth20[] = {
+#ifdef GSSAPI
+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
+ {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error},
+ {MONITOR_REQ_GSSMECHS, 0, mm_answer_gss_indicate_mechs},
+#endif
{MONITOR_REQ_MODULI, 0, mm_answer_moduli},
{MONITOR_REQ_SIGN, 0, mm_answer_sign},
{MONITOR_REQ_PTY, 0, mm_answer_pty},
/* Permit requests for moduli and signatures */
monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+#ifdef GSSAPI
+ /* and for the GSSAPI key exchange */
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1);
+#endif
} else {
mon_dispatch = mon_dispatch_proto15;
monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
+#ifdef GSSAPI
+ /* and for the GSSAPI key exchange */
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS,1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP,1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR,1);
+#endif
+
} else {
mon_dispatch = mon_dispatch_postauth15;
monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
kex->we_need = buffer_get_int(m);
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+#ifdef GSSAPI
+ kex->kex[KEX_GSS_GRP1_SHA1] =kexgss_server;
+#endif
kex->server = 1;
kex->hostkey_type = buffer_get_int(m);
kex->kex_type = buffer_get_int(m);
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;
+ u_int len;
+
+ oid.elements=buffer_get_string(m,&len);
+ oid.length=len;
+
+ 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);
+
+ /* Now we have a context, enable the step and sign */
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP,1);
+
+ 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);
+
+ /* Complete - now we can do signing */
+ if (major==GSS_S_COMPLETE) {
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP,0);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN,1);
+ }
+ 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);
+
+ /* XXX - auth method could also be 'external' */
+ 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);
+}
+
+int
+mm_answer_gss_error(int socket, Buffer *m) {
+ OM_uint32 major,minor;
+ char *msg;
+
+ msg=ssh_gssapi_last_error(gsscontext,&major,&minor);
+ buffer_clear(m);
+ buffer_put_int(m,major);
+ buffer_put_int(m,minor);
+ buffer_put_cstring(m,msg);
+
+ mm_request_send(socket,MONITOR_ANS_GSSERR,m);
+
+ xfree(msg);
+
+ return(0);
+}
+
+int
+mm_answer_gss_indicate_mechs(int socket, Buffer *m) {
+ OM_uint32 major,minor;
+ gss_OID_set mech_set;
+ int i;
+
+ major=gss_indicate_mechs(&minor, &mech_set);
+
+ buffer_clear(m);
+ buffer_put_int(m, major);
+ buffer_put_int(m, mech_set->count);
+ for (i=0; i < mech_set->count; i++) {
+ buffer_put_string(m, mech_set->elements[i].elements,
+ mech_set->elements[i].length);
+ }
+
+ gss_release_oid_set(&minor,&mech_set);
+
+ mm_request_send(socket,MONITOR_ANS_GSSMECHS,m);
+
+ return(0);
+}
+
+int
+mm_answer_gss_localname(int socket, Buffer *m) {
+ char *name;
+
+ ssh_gssapi_localname(&name);
+
+ buffer_clear(m);
+ if (name) {
+ buffer_put_cstring(m, name);
+ debug3("%s: sending result %s", __func__, name);
+ xfree(name);
+ } else {
+ buffer_put_cstring(m, "");
+ debug3("%s: sending result \"\"", __func__);
+ }
+
+ mm_request_send(socket, MONITOR_ANS_GSSLOCALNAME, m);
+
+ return(0);
+}
+#endif /* GSSAPI */
MONITOR_REQ_BSDAUTHRESPOND, MONITOR_ANS_BSDAUTHRESPOND,
MONITOR_REQ_SKEYQUERY, MONITOR_ANS_SKEYQUERY,
MONITOR_REQ_SKEYRESPOND, MONITOR_ANS_SKEYRESPOND,
+#ifdef GSSAPI
+ 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_GSSMECHS,MONITOR_ANS_GSSMECHS,
+ MONITOR_REQ_GSSLOCALNAME,MONITOR_ANS_GSSLOCALNAME,
+ MONITOR_REQ_GSSERR,MONITOR_ANS_GSSERR,
+#endif
MONITOR_REQ_KEYALLOWED, MONITOR_ANS_KEYALLOWED,
MONITOR_REQ_KEYVERIFY, MONITOR_ANS_KEYVERIFY,
MONITOR_REQ_KEYEXPORT,
#include "channels.h"
#include "session.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
/* Imports */
extern int compat20;
extern Newkeys *newkeys[];
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);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSETUP, &m);
+
+ major=buffer_get_int(&m);
+
+ buffer_free(&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);
+ 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);
+
+ buffer_free(&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);
+ 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);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
+
+ major=buffer_get_int(&m);
+ hash->value = buffer_get_string(&m, &hash->length);
+
+ buffer_free(&m);
+
+ return(major);
+}
+
+char *
+mm_ssh_gssapi_last_error(Gssctxt *ctx, OM_uint32 *major, OM_uint32 *minor) {
+ Buffer m;
+ OM_uint32 maj,min;
+ char *errstr;
+
+ buffer_init(&m);
+
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSERR, &m);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSERR, &m);
+
+ maj = buffer_get_int(&m);
+ min = buffer_get_int(&m);
+
+ if (major) *major=maj;
+ if (minor) *minor=min;
+
+ errstr=buffer_get_string(&m,NULL);
+
+ buffer_free(&m);
+
+ return(errstr);
+}
+
+OM_uint32
+mm_gss_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set)
+{
+ Buffer m;
+ OM_uint32 major,minor;
+ int count;
+ gss_OID_desc oid;
+ u_int length;
+
+ buffer_init(&m);
+
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSMECHS, &m);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSMECHS,
+ &m);
+ major=buffer_get_int(&m);
+ count=buffer_get_int(&m);
+
+ gss_create_empty_oid_set(&minor,mech_set);
+ while(count-->0) {
+ oid.elements=buffer_get_string(&m,&length);
+ oid.length=length;
+ gss_add_oid_set_member(&minor,&oid,mech_set);
+ }
+
+ buffer_free(&m);
+
+ return(major);
+}
+
+int
+mm_ssh_gssapi_localname(char **lname)
+{
+ Buffer m;
+
+ buffer_init(&m);
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSLOCALNAME, &m);
+
+ debug3("%s: waiting for MONITOR_ANS_GSSLOCALNAME", __func__);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSLOCALNAME,
+ &m);
+
+ *lname = buffer_get_string(&m, NULL);
+
+ buffer_free(&m);
+ if (lname[0] == '\0') {
+ debug3("%s: gssapi identity mapping failed", __func__);
+ } else {
+ debug3("%s: gssapi identity mapped to %s", __func__, *lname);
+ }
+
+ return(0);
+}
+#endif /* GSSAPI */
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);
+char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
+
+#endif
+
void mm_terminate(void);
int mm_pty_allocate(int *, int *, char *, int);
void mm_session_pty_cleanup2(void *);
#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 = 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 "session.h"
#include "monitor_wrap.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
#ifdef HAVE_CYGWIN
#include <windows.h>
#include <sys/cygwin.h>
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)
{
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);
do_authenticated2(Authctxt *authctxt)
{
server_loop2(authctxt);
+#if defined(GSSAPI)
+ ssh_gssapi_cleanup_creds(NULL);
+#endif
}
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
--- /dev/null
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SSH_GSS_H
+#define _SSH_GSS_H
+
+#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-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 SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65
+
+#define KEX_GSS_SHA1 "gss-group1-sha1-"
+
+typedef struct {
+ char *filename;
+ char *envvar;
+ char *envval;
+ void *data;
+} ssh_gssapi_ccache;
+
+typedef struct {
+ gss_buffer_desc name;
+ gss_cred_id_t creds;
+ struct ssh_gssapi_mech_struct *mech;
+ ssh_gssapi_ccache store;
+} ssh_gssapi_client;
+
+typedef struct ssh_gssapi_mech_struct {
+ char *enc_name;
+ char *name;
+ gss_OID_desc oid;
+ int (*dochild) (ssh_gssapi_client *);
+ int (*userok) (ssh_gssapi_client *, char *);
+ int (*localname) (ssh_gssapi_client *, char **);
+ void (*storecreds) (ssh_gssapi_client *);
+} ssh_gssapi_mech;
+
+
+
+typedef struct {
+ OM_uint32 major; /* both */
+ OM_uint32 minor; /* 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[];
+
+char *ssh_gssapi_mechanisms(char *host);
+char *ssh_gssapi_client_mechanisms(char *host);
+gss_OID ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name);
+int ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len);
+void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len);
+void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid);
+void ssh_gssapi_supported_oids(gss_OID_set *oidset);
+ssh_gssapi_mech *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,
+ ssh_gssapi_mech **mech,
+ gss_buffer_desc *name,
+ gss_cred_id_t *creds);
+void ssh_gssapi_error(Gssctxt *ctx);
+char *ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
+void ssh_gssapi_build_ctx(Gssctxt **ctx);
+void ssh_gssapi_delete_ctx(Gssctxt **ctx);
+OM_uint32 ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid);
+
+int ssh_gssapi_check_mechanism(gss_OID oid, char *host);
+
+/* In the server */
+gss_OID ssh_gssapi_server_id_kex(char *name);
+int ssh_gssapi_userok(char *name);
+int ssh_gssapi_localname(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();
+char *ssh_gssapi_server_mechanisms();
+#endif /* GSSAPI */
+
+#endif /* _SSH_GSS_H */
#include "msg.h"
#include "pathnames.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
/* import */
extern char *client_version_string;
extern char *server_version_string;
ssh_kex2(char *host, struct sockaddr *hostaddr)
{
Kex *kex;
+#ifdef GSSAPI
+ char *orig, *gss;
+ int len;
+#endif
xxx_host = host;
xxx_hostaddr = hostaddr;
+#ifdef GSSAPI
+ /* Add the GSSAPI mechanisms currently supported on this client to
+ * the key exchange algorithm proposal */
+ orig = myproposal[PROPOSAL_KEX_ALGS];
+ gss = ssh_gssapi_client_mechanisms(get_canonical_hostname(1));
+ if (gss) {
+ len = strlen(orig)+strlen(gss)+2;
+ myproposal[PROPOSAL_KEX_ALGS]=xmalloc(len);
+ snprintf(myproposal[PROPOSAL_KEX_ALGS],len,"%s,%s",gss,orig);
+ }
+#endif
+
if (options.ciphers == (char *)-1) {
log("No valid ciphers for protocol version 2 given, using defaults.");
options.ciphers = NULL;
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->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
+#ifdef GSSAPI
+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
+#endif
kex->client_version_string=client_version_string;
kex->server_version_string=server_version_string;
kex->verify_host_key=&verify_host_key_callback;
-
+#ifdef GSSAPI
+ kex->options.gss_deleg_creds=options.gss_deleg_creds;
+#endif
xxx_kex = kex;
dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
Sensitive *sensitive;
/* kbd-interactive */
int info_req_seen;
+ /* generic */
+ void *methoddata;
};
struct Authmethod {
char *name; /* string to compare against server's list */
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, void *);
+void input_gssapi_token(int type, u_int32_t, void *);
+void input_gssapi_hash(int type, u_int32_t, void *);
+void input_gssapi_error(int, u_int32_t, void *);
+void input_gssapi_errtok(int, u_int32_t, void *);
+#endif
+
void userauth(Authctxt *, char *);
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.sensitive = sensitive;
authctxt.info_req_seen = 0;
if (authctxt.method == NULL)
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)
+{
+ Gssctxt *gssctxt = NULL;
+ static gss_OID_set supported = NULL;
+ static int mech=0;
+ OM_uint32 min;
+ int ok=0;
+
+ /* Things work better if we send one mechanism at a time, rather
+ * than them all at once. This means that if we fail at some point
+ * in the middle of a negotiation, we can come back and try something
+ * different. */
+
+ if (datafellows & SSH_OLD_GSSAPI) return 0;
+
+ /* Before we offer a mechanism, check that we can support it. Don't
+ * bother trying to get credentials - as the standard fallback will
+ * deal with that kind of failure.
+ */
+
+ if (supported==NULL) gss_indicate_mechs(&min, &supported);
+
+ while (mech<supported->count && !ok) {
+ if (gssctxt) ssh_gssapi_delete_ctx(&gssctxt);
+ ssh_gssapi_build_ctx(&gssctxt);
+ ssh_gssapi_set_oid(gssctxt,&supported->elements[mech]);
+
+ /* The DER encoding below only works for lengths<128,
+ * so check this here
+ */
+ if (supported->elements[mech].length<128 &&
+ !GSS_ERROR(ssh_gssapi_import_name(gssctxt,
+ authctxt->host))) {
+ ok = 1; /* Mechanism works */
+ } else {
+ mech++;
+ }
+ }
+
+ if (!ok) 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);
+
+ packet_put_int(1);
+
+ /* The newest gsskeyex draft stipulates that OIDs should
+ * be DER encoded, so we need to add the object type and
+ * length information back on */
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ packet_put_string(supported->elements[mech].elements,
+ supported->elements[mech].length);
+ } else {
+ packet_put_int((supported->elements[mech].length)+2);
+ packet_put_char(0x06);
+ packet_put_char(supported->elements[mech].length);
+ packet_put_raw(supported->elements[mech].elements,
+ supported->elements[mech].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);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR,&input_gssapi_error);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,&input_gssapi_errtok);
+
+ mech++; /* Move along to next candidate */
+
+ return 1;
+}
+
+void
+input_gssapi_response(int type, u_int32_t 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);
+
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ if (!ssh_gssapi_check_oid(gssctxt,oidv,oidlen)) {
+ fatal("Server returned different OID than expected");
+ }
+ ssh_gssapi_set_oid_data(gssctxt,oidv,oidlen);
+ } else {
+ if(oidv[0]!=0x06 || oidv[1]!=oidlen-2) {
+ debug("Badly encoded mechanism OID received");
+ clear_auth_state(authctxt);
+ userauth(authctxt,NULL);
+ return;
+ }
+ if (!ssh_gssapi_check_oid(gssctxt,oidv+2,oidlen-2)) {
+ fatal("Server returned different OID than expected");
+ }
+ ssh_gssapi_set_oid_data(gssctxt,oidv+2,oidlen-2);
+ }
+
+ packet_check_eom();
+
+ status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ GSS_C_NO_BUFFER, &send_tok,
+ NULL);
+ if (GSS_ERROR(status)) {
+ if (send_tok.length>0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+ packet_put_string(send_tok.value,send_tok.length);
+ packet_send();
+ packet_write_wait();
+ }
+ /* Start again with next method on list */
+ debug("Trying to start again");
+ clear_auth_state(authctxt);
+ 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;
+ u_int slen;
+
+ if (authctxt == NULL)
+ fatal("input_gssapi_response: no authentication context");
+ gssctxt = authctxt->methoddata;
+
+ recv_tok.value=packet_get_string(&slen);
+ recv_tok.length=slen; /* safe typecast */
+
+ status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ &recv_tok, &send_tok, NULL);
+
+ packet_check_eom();
+
+ if (GSS_ERROR(status)) {
+ if (send_tok.length>0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+ packet_put_string(send_tok.value,send_tok.length);
+ packet_send();
+ packet_write_wait();
+ }
+ /* Start again with the next method in the list */
+ clear_auth_state(authctxt);
+ 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();
+ }
+}
+
+void
+input_gssapi_errtok(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);
+
+ /* Stick it into GSSAPI and see what it says */
+ status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ &recv_tok, &send_tok, NULL);
+
+ packet_check_eom();
+
+ /* We can't send a packet to the server */
+
+ /* The draft says that we should wait for the server to fail
+ * before starting the next authentication. So, we clear the
+ * state, but don't do anything else */
+ clear_auth_state(authctxt);
+ return;
+}
+
+void
+input_gssapi_error(int type, u_int32_t plen, void *ctxt)
+{
+ OM_uint32 maj,min;
+ char *msg;
+ char *lang;
+
+ maj=packet_get_int();
+ min=packet_get_int();
+ msg=packet_get_string(NULL);
+ lang=packet_get_string(NULL);
+
+ packet_check_eom();
+
+ fprintf(stderr, "Server GSSAPI Error:\n%s\n", msg);
+ xfree(msg);
+ xfree(lang);
+}
+
+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
{
/* XXX clear authentication state */
dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, NULL);
-
+#ifdef GSSAPI
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR,NULL);
+#endif
+
if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) {
debug3("clear_auth_state: key_free %p", authctxt->last_key);
key_free(authctxt->last_key);
#include "monitor_wrap.h"
#include "monitor_fdpass.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_server_mechanisms();
+ else
+ gss = NULL;
+
+ if (gss && orig) {
+ int len = strlen(orig) + strlen(gss) +2;
+ newstr=xmalloc(len);
+ snprintf(newstr,len,"%s,%s",gss,orig);
+ } else if (gss) {
+ newstr=gss;
+ } else if (orig) {
+ newstr=orig;
+ }
+ /* If we've got GSSAPI mechanisms, then we've also got the 'null'
+ host key algorithm, but we're not allowed to advertise it, unless
+ its the only host key algorithm we're supporting */
+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) {
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]="null";
+ }
+ if (newstr)
+ myproposal[PROPOSAL_KEX_ALGS]=newstr;
+ else
+ fatal("No supported key exchange algorithms");
+ }
+#endif
+
/* start key exchange */
kex = kex_setup(myproposal);
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+#ifdef GSSAPI
+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+#endif
kex->server = 1;
kex->client_version_string=client_version_string;
kex->server_version_string=server_version_string;
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