atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
- kexgssc.o \
- entropy.o scard-opensc.o gss-genr.o
+ entropy.o scard-opensc.o gss-genr.o kexgssc.o
SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
sshconnect.o sshconnect1.o sshconnect2.o
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 kexdhs.o kexgexs.o \
- kexgsss.o gss-serv-gsi.o \
auth-krb5.o \
- auth2-gss.o gss-serv.o gss-serv-krb5.o \
+ auth2-gss.o gss-serv.o gss-serv-krb5.o gss-serv-gsi.o kexgsss.o \
loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
audit.o audit-bsm.o
/* getaddrinfo is broken (if present) */
#undef BROKEN_GETADDRINFO
+/* platform uses an in-memory credentials cache */
+#undef USE_CCAPI
+
+/* platform has a Security Authorization Session API */
+#undef USE_SECURITY_SESSION_API
+
/* updwtmpx is broken (if present) */
#undef BROKEN_UPDWTMPX
len = strlen(authctxt->krb5_ticket_file) + 6;
authctxt->krb5_ccname = xmalloc(len);
+#ifdef USE_CCAPI
+ snprintf(authctxt->krb5_ccname, len, "API:%s",
+ authctxt->krb5_ticket_file);
+#else
snprintf(authctxt->krb5_ccname, len, "FILE:%s",
authctxt->krb5_ticket_file);
+#endif
#ifdef USE_PAM
if (options.use_pam)
#ifndef HEIMDAL
krb5_error_code
ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
- int tmpfd, ret;
+ int ret;
char ccname[40];
mode_t old_umask;
+#ifdef USE_CCAPI
+ char cctemplate[] = "API:krb5cc_%d";
+#else
+ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
+ int tmpfd;
+#endif
ret = snprintf(ccname, sizeof(ccname),
- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid());
- if (ret == -1 || ret >= sizeof(ccname))
+ cctemplate, geteuid());
+ if (ret == -1 || ret >= (int) sizeof(ccname))
return ENOMEM;
+#ifndef USE_CCAPI
old_umask = umask(0177);
tmpfd = mkstemp(ccname + strlen("FILE:"));
umask(old_umask);
return errno;
}
close(tmpfd);
+#endif
return (krb5_cc_resolve(ctx, ccname, ccache));
}
int valid; /* user exists and is allowed to login */
int attempt;
int failures;
+ int server_caused_failure;
int force_pwchange;
char *user; /* username sent by the client */
char *service;
return 0;
}
+/*
+ * The 'gssapi_keyex' userauth mechanism.
+ */
+static int
+userauth_gsskeyex(Authctxt *authctxt)
+{
+ int authenticated = 0;
+ Buffer b, b2;
+ gss_buffer_desc mic, gssbuf, gssbuf2;
+ u_int len;
+
+ mic.value = packet_get_string(&len);
+ mic.length = len;
+
+ packet_check_eom();
+
+ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+ "gssapi-keyex");
+
+ gssbuf.value = buffer_ptr(&b);
+ gssbuf.length = buffer_len(&b);
+
+ /* client may have used empty username to determine target
+ name from GSSAPI context */
+ ssh_gssapi_buildmic(&b2, "", authctxt->service, "gssapi-keyex");
+
+ gssbuf2.value = buffer_ptr(&b2);
+ gssbuf2.length = buffer_len(&b2);
+
+ /* gss_kex_context is NULL with privsep, so we can't check it here */
+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
+ &gssbuf, &mic))) ||
+ !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
+ &gssbuf2, &mic)))) {
+ if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+ }
+ }
+
+ buffer_free(&b);
+ buffer_free(&b2);
+ xfree(mic.value);
+
+ return (authenticated);
+}
+
/*
* We only support those mechanisms that we know about (ie ones that we know
* how to check local user kuserok and the like
if (!present) {
xfree(doid);
+ authctxt->server_caused_failure = 1;
return (0);
}
if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
xfree(doid);
+ authctxt->server_caused_failure = 1;
return (0);
}
&options.gss_authentication
};
+Authmethod method_gsskeyex = {
+ "gssapi-keyex",
+ userauth_gsskeyex,
+ &options.gss_authentication
+};
+
Authmethod method_gssapi = {
"gssapi-with-mic",
userauth_gssapi_with_mic,
extern Authmethod method_hostbased;
#ifdef GSSAPI
extern Authmethod method_external;
+extern Authmethod method_gsskeyex;
extern Authmethod method_gssapi;
extern Authmethod method_gssapi_compat;
#endif
&method_none,
&method_pubkey,
#ifdef GSSAPI
+ &method_gsskeyex,
&method_external,
&method_gssapi,
&method_gssapi_compat,
#ifdef GSSAPI
if (user[0] == '\0') {
debug("received empty username for %s", method);
- if (strcmp(method, "external-keyx") == 0) {
+ if (strcmp(method, "external-keyx") == 0 ||
+ strcmp(method, "gssapi-keyex") == 0) {
char *lname = NULL;
PRIVSEP(ssh_gssapi_localname(&lname));
if (lname && lname[0] != '\0') {
#endif
authctxt->postponed = 0;
+ authctxt->server_caused_failure = 0;
/* try to authenticate user */
m = authmethod_lookup(method);
/* now we can break out */
authctxt->success = 1;
} else {
- if (authctxt->failures++ > options.max_authtries) {
+ /* Dont count server configuration issues against the client */
+ if (!authctxt->server_caused_failure &&
+ authctxt->failures++ > options.max_authtries) {
#ifdef SSH_AUDIT_EVENTS
PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
#endif
AC_DEFINE(BROKEN_SETREUID)
AC_DEFINE(BROKEN_SETREGID)
AC_DEFINE_UNQUOTED(BIND_8_COMPAT, 1)
+ AC_MSG_CHECKING(if we have the Security Authorization Session API)
+ AC_TRY_COMPILE([#include <Security/AuthSession.h>],
+ [SessionCreate(0, 0);],
+ [ac_cv_use_security_session_api="yes"
+ AC_DEFINE(USE_SECURITY_SESSION_API)
+ LIBS="$LIBS -framework Security"
+ AC_MSG_RESULT(yes)],
+ [ac_cv_use_security_session_api="no"
+ AC_MSG_RESULT(no)])
+ AC_MSG_CHECKING(if we have an in-memory credentials cache)
+ AC_TRY_COMPILE(
+ [#include <Kerberos/Kerberos.h>],
+ [cc_context_t c;
+ (void) cc_initialize (&c, 0, NULL, NULL);],
+ [AC_DEFINE(USE_CCAPI)
+ LIBS="$LIBS -framework Security"
+ AC_MSG_RESULT(yes)
+ if test "x$ac_cv_use_security_session_api" = "xno"; then
+ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***)
+ fi],
+ [AC_MSG_RESULT(no)]
+ )
;;
*-*-hpux*)
# first we define all of the options common to all HP-UX releases
/* $OpenBSD: gss-genr.c,v 1.4 2005/07/17 07:17:55 djm Exp $ */
/*
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2005 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "monitor_wrap.h"
#include "canohost.h"
#include "ssh2.h"
+#include <openssl/evp.h>
#include "ssh-gss.h"
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 - It would be nice to find a more elegant way of handling the
+ * XXX passing of the key exchange context to the userauth routines
+ */
+
+Gssctxt *gss_kex_context = NULL;
+
+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
+
+int
+ssh_gssapi_oid_table_ok() {
+ return (gss_enc2oid != NULL);
+}
+
+/*
+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
*
- * XXX - We might want to make this configurable in the future, so as to
- * XXX - allow the user control over which mechanisms to use.
+ * We test mechanisms to ensure that we can use them, to avoid starting
+ * a key exchange with a bad mechanism
*/
-
-char *
-ssh_gssapi_client_mechanisms(char *host) {
- gss_OID_set supported;
- OM_uint32 min_status;
- Buffer buf;
- size_t i = 0;
- char *mechs;
- char *encoded;
- int enclen;
- unsigned char digest[EVP_MAX_MD_SIZE];
- char deroid[2];
- const EVP_MD *evp_md = EVP_md5();
- EVP_MD_CTX md;
- int oidpos=0;
-
- gss_indicate_mechs(&min_status,&supported);
- 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)) {
+char *
+ssh_gssapi_client_mechanisms(const char *host) {
+ gss_OID_set gss_supported;
+ OM_uint32 min_status;
+
+ gss_indicate_mechs(&min_status, &gss_supported);
- /* Add the required DER encoding octets and MD5 hash */
- deroid[0]=0x06; /* Object Identifier */
- deroid[1]=supported->elements[i].length;
+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
+ (void *)host));
+}
+
+char *
+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
+ void *data) {
+ Buffer buf;
+ size_t i;
+ int oidpos, enclen;
+ char *mechs, *encoded;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ char deroid[2];
+ const EVP_MD *evp_md = EVP_md5();
+ EVP_MD_CTX md;
+
+ if (gss_enc2oid != NULL) {
+ for (i=0;gss_enc2oid[i].encoded!=NULL;i++)
+ xfree(gss_enc2oid[i].encoded);
+ xfree(gss_enc2oid);
+ }
+
+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping)*
+ (gss_supported->count+1));
+
+ buffer_init(&buf);
+
+ oidpos = 0;
+ for (i = 0;i < gss_supported->count;i++) {
+ if (gss_supported->elements[i].length < 128 &&
+ (*check)(&(gss_supported->elements[i]), data)) {
+
+ deroid[0] = SSH_GSS_OIDTYPE;
+ deroid[1] = gss_supported->elements[i].length;
EVP_DigestInit(&md, evp_md);
- EVP_DigestUpdate(&md,deroid,2);
+ EVP_DigestUpdate(&md, deroid, 2);
EVP_DigestUpdate(&md,
- supported->elements[i].elements,
- supported->elements[i].length);
+ gss_supported->elements[i].elements,
+ gss_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);
+ encoded = xmalloc(EVP_MD_size(evp_md)*2);
+ enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
+ encoded, EVP_MD_size(evp_md)*2);
- gss_enc2oid[oidpos].oid=&(supported->elements[i]);
- gss_enc2oid[oidpos].encoded=encoded;
+ if (oidpos != 0)
+ buffer_put_char(&buf, ',');
+
+ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
+ sizeof(KEX_GSS_GEX_SHA1_ID)-1);
+ buffer_append(&buf, encoded, enclen);
+ buffer_put_char(&buf,',');
+ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
+ sizeof(KEX_GSS_GRP1_SHA1_ID)-1);
+ buffer_append(&buf, encoded, enclen);
+
+ gss_enc2oid[oidpos].oid = &(gss_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));
+ 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);
+ if (strlen(mechs) == 0) {
+ xfree(mechs);
+ mechs = 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++;
+ return (mechs);
+}
+
+gss_OID
+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int *gex) {
+ int i = 0;
+
+ if (strncmp(name, KEX_GSS_GRP1_SHA1_ID,
+ sizeof(KEX_GSS_GRP1_SHA1_ID)-1) == 0) {
+ name+=sizeof(KEX_GSS_GRP1_SHA1_ID)-1;
+ *gex = 0;
+ } else if (strncmp(name, KEX_GSS_GEX_SHA1_ID,
+ sizeof(KEX_GSS_GEX_SHA1_ID)-1) == 0) {
+ name+=sizeof(KEX_GSS_GEX_SHA1_ID)-1;
+ *gex = 1;
+ } else {
+ return NULL;
}
-
- if (gss_enc2oid[i].oid!=NULL) {
- ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid);
+
+ while (gss_enc2oid[i].encoded != NULL &&
+ strcmp(name, gss_enc2oid[i].encoded) != 0) {
+ i++;
}
+ if (gss_enc2oid[i].oid != NULL && ctx != NULL)
+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
+
return gss_enc2oid[i].oid;
}
OM_uint32
ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
{
+ if (ctx == NULL)
+ return -1;
+
if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
GSS_C_QOP_DEFAULT, buffer, hash)))
ssh_gssapi_error(ctx);
return (ctx->major);
}
+/* Priviledged when used by server */
+OM_uint32
+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
+{
+ if (ctx == NULL)
+ return -1;
+
+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
+ gssbuf, gssmic, NULL);
+
+ return (ctx->major);
+}
+
void
ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
const char *context)
}
int
-ssh_gssapi_check_mechanism(gss_OID oid, const char *host) {
+ssh_gssapi_check_mechanism(gss_OID oid, void *host) {
Gssctxt * ctx = NULL;
- gss_buffer_desc token;
- OM_uint32 major,minor;
+ gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+ 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_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));
+ return (!GSS_ERROR(major));
}
#endif /* GSSAPI */
#ifdef HEIMDAL
# include <krb5.h>
-#else
-# ifdef HAVE_GSSAPI_KRB5
+#elsif !defined(MECHGLUE)
+# ifdef HAVE_GSSAPI_KRB5_H
# include <gssapi_krb5.h>
-# elif HAVE_GSSAPI_GSSAPI_KRB5
+# elif HAVE_GSSAPI_GSSAPI_KRB5_H
# include <gssapi/gssapi_krb5.h>
# endif
#endif
#include "ssh-gss.h"
extern ServerOptions options;
-extern u_char *session_id2;
-extern int session_id2_len;
static ssh_gssapi_client gssapi_client =
{ GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
static int limited = 0;
#endif
+/* Unpriviledged */
+char *
+ssh_gssapi_server_mechanisms() {
+ gss_OID_set supported;
+
+ ssh_gssapi_supported_oids(&supported);
+ return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
+ NULL));
+}
+
+/* Unpriviledged */
+int
+ssh_gssapi_server_check_mech(gss_OID oid, void *data) {
+ Gssctxt * ctx = NULL;
+ int res;
+
+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
+ ssh_gssapi_delete_ctx(&ctx);
+
+ return (res);
+}
+
/* Unpriviledged */
void
ssh_gssapi_supported_oids(gss_OID_set *oidset)
return (0);
}
-/* Return a list of the gss-group1-sha1-x mechanisms supported by this
- * program.
- *
- * We only support the mechanisms that we've indicated in the list above,
- * but we check that they're supported by the GSSAPI mechanism on the
- * machine. We also check, before including them in the list, that
- * we have the necesary information in order to carry out the key exchange
- * (that is, that the user has credentials, the server's creds are accessible,
- * etc)
- *
- * The way that this is done is fairly nasty, as we do a lot of work that
- * is then thrown away. This should possibly be implemented with a cache
- * that stores the results (in an expanded Gssctxt structure), which are
- * then used by the first calls if that key exchange mechanism is chosen.
- */
-
-/* Unpriviledged */
-char *
-ssh_gssapi_server_mechanisms() {
- gss_OID_set supported;
- Gssctxt *ctx = NULL;
- OM_uint32 maj_status, min_status;
- Buffer buf;
- int i = 0;
- int first = 0;
- int present;
- char * mechs;
-
- ssh_gssapi_supported_oids(&supported);
-
- buffer_init(&buf);
-
- while(supported_mechs[i]->name != NULL) {
- if ((maj_status=gss_test_oid_set_member(&min_status,
- &supported_mechs[i]->oid,
- supported,
- &present))) {
- present=0;
- }
-
- if (present) {
- if (!GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx,
- &supported_mechs[i]->oid)))) {
- /* Append gss_group1_sha1_x to our list */
- if (first++!=0)
- buffer_put_char(&buf,',');
- buffer_append(&buf, KEX_GSS_SHA1,
- sizeof(KEX_GSS_SHA1)-1);
- buffer_append(&buf,
- supported_mechs[i]->enc_name,
- strlen(supported_mechs[i]->enc_name));
- debug("GSSAPI mechanism %s (%s%s) supported",
- supported_mechs[i]->name, KEX_GSS_SHA1,
- supported_mechs[i]->enc_name);
- } else {
- debug("no credentials for GSSAPI mechanism %s",
- supported_mechs[i]->name);
- }
- } else {
- debug("GSSAPI mechanism %s not supported",
- supported_mechs[i]->name);
- }
- ssh_gssapi_delete_ctx(&ctx);
- i++;
- }
-
- buffer_put_char(&buf,'\0');
-
- mechs=xmalloc(buffer_len(&buf));
- buffer_get(&buf,mechs,buffer_len(&buf));
- buffer_free(&buf);
- if (strlen(mechs)==0) {
- options.gss_authentication = 0; /* no mechs. skip gss auth. */
- return(NULL);
- } else {
- return(mechs);
- }
-}
-
-/* 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;
-}
-
/* Priviledged */
int
ssh_gssapi_localname(char **user)
return(0);
}
-/* Priviledged */
-OM_uint32
-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
-{
- ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
- gssbuf, gssmic, NULL);
-
- return (ctx->major);
-}
-
#endif
} 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) {
+ } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID,
+ sizeof(KEX_GSS_GEX_SHA1_ID)-1) == 0) {
+ k->kex_type = KEX_GSS_GEX_SHA1;
+ } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID,
+ sizeof(KEX_GSS_GRP1_SHA1_ID)-1) == 0) {
k->kex_type = KEX_GSS_GRP1_SHA1;
#endif
} else
KEX_DH_GRP14_SHA1,
KEX_DH_GEX_SHA1,
KEX_GSS_GRP1_SHA1,
+ KEX_GSS_GEX_SHA1,
KEX_MAX
};
Buffer peer;
int done;
int flags;
+#ifdef GSSAPI
+ int gss_deleg_creds;
+ int gss_trust_dns;
+ char *gss_host;
+#endif
char *client_version_string;
char *server_version_string;
struct KexOptions options;
void kexdh_server(Kex *);
void kexgex_client(Kex *);
void kexgex_server(Kex *);
+
#ifdef GSSAPI
-void kexgss_client(Kex *);
-void kexgss_server(Kex *);
+void kexgss_client(Kex *);
+void kexgss_server(Kex *);
#endif
u_char *
/*
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2005 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "ssh-gss.h"
void
-kexgss_client(Kex *kex)
-{
- gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok, *token_ptr;
+kexgss_client(Kex *kex) {
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc recv_tok, gssbuf, 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;
+ BIGNUM *dh_server_pub = NULL;
+ BIGNUM *shared_secret = NULL;
+ BIGNUM *p = NULL;
+ BIGNUM *g = NULL;
unsigned char *kbuf;
unsigned char *hash;
- unsigned char *serverhostkey = 0;
+ unsigned char *serverhostkey = NULL;
char *msg;
char *lang;
int type = 0;
int first = 1;
- u_int slen = 0;
+ unsigned int slen = 0;
+ int gex = 0;
+ int nbits = -1, min = -1, max = -1;
u_int strlen;
-
- /* Initialise our GSSAPI world */
+
+ /* Initialise our GSSAPI world */
ssh_gssapi_build_ctx(&ctxt);
- if (ssh_gssapi_client_id_kex(ctxt,kex->name)==NULL) {
+ if (ssh_gssapi_id_kex(ctxt, kex->name, &gex) == NULL)
fatal("Couldn't identify host exchange");
- }
- if (ssh_gssapi_import_name(ctxt,get_canonical_hostname(1))) {
- fatal("Couldn't import hostname ");
+ if (ssh_gssapi_import_name(ctxt, kex->gss_host))
+ fatal("Couldn't import hostname");
+
+ if (gex) {
+ debug("Doing group exchange\n");
+ nbits = dh_estimate(kex->we_need * 8);
+ min = DH_GRP_MIN;
+ max = DH_GRP_MAX;
+ packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
+ packet_put_int(min);
+ packet_put_int(nbits);
+ packet_put_int(max);
+
+ packet_send();
+
+ packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
+
+ if ((p = BN_new()) == NULL)
+ fatal("BN_new() failed");
+ packet_get_bignum2(p);
+ if ((g = BN_new()) == NULL)
+ fatal("BN_new() failed");
+ packet_get_bignum2(g);
+ packet_check_eom();
+
+ if (BN_num_bits(p) < min || BN_num_bits(p) > max)
+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
+ min, BN_num_bits(p), max);
+
+ dh = dh_new_group(g, p);
+ } else {
+ dh = dh_new_group1();
}
- /* 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");
- }
-
+ 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);
+ maj_status = ssh_gssapi_init_ctx(ctxt,
+ kex->gss_deleg_creds, token_ptr, &send_tok,
+ &ret_flags);
if (GSS_ERROR(maj_status)) {
- if (send_tok.length!=0) {
+ if (send_tok.length != 0) {
packet_start(SSH2_MSG_KEXGSS_CONTINUE);
packet_put_string(send_tok.value,
- send_tok.length);
- }
+ 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);
-
-
+ xfree(recv_tok.value);
+
if (maj_status == GSS_S_COMPLETE) {
/* If mutual state flag is not true, kex fails */
- if (!(ret_flags & GSS_C_MUTUAL_FLAG)) {
+ 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)) {
+ 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 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);
+ send_tok.length);
packet_put_bignum2(dh->pub_key);
- first=0;
+ first = 0;
} else {
packet_start(SSH2_MSG_KEXGSS_CONTINUE);
packet_put_string(send_tok.value,
- send_tok.length);
+ send_tok.length);
}
packet_send();
- packet_write_wait();
+ gss_release_buffer(&min_status, &send_tok);
+
+ /* If we've sent them data, they should reply */
+ do {
+ type = packet_read();
+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
+ debug("Received KEXGSS_HOSTKEY");
+ if (serverhostkey)
+ fatal("Server host key received more than once");
+ serverhostkey =
+ packet_get_string(&slen);
+ }
+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
-
- /* 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 */
+ recv_tok.value = packet_get_string(&strlen);
+ recv_tok.length = strlen;
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 */
+ packet_get_bignum2(dh_server_pub);
+ msg_tok.value = packet_get_string(&strlen);
+ msg_tok.length = strlen;
/* Is there a token included? */
if (packet_get_char()) {
recv_tok.value=
packet_get_string(&strlen);
- recv_tok.length=strlen; /*u_int/size_t*/
+ recv_tok.length = strlen;
/* 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");
+ } 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);
+ maj_status = packet_get_int();
+ min_status = packet_get_int();
+ msg = packet_get_string(NULL);
+ lang = packet_get_string(NULL);
fatal("GSSAPI Key Exchange Error: \n%s",msg);
default:
packet_disconnect("Protocol error: didn't expect packet type %d",
type);
}
- token_ptr=&recv_tok;
+ token_ptr = &recv_tok;
} else {
/* No data, and not complete */
- if (maj_status!=GSS_S_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");
-
+ } 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))) {
+ if (!dh_pub_is_valid(dh, dh_server_pub))
+ packet_disconnect("bad server public DH value");
- 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);
+ /* 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);
+
+ if (gex) {
+ hash = kexgex_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,
+ min, nbits, max,
+ dh->p, dh->g,
+ dh->pub_key,
+ dh_server_pub,
+ shared_secret
+ );
+ } else {
+ /* 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 the hash matches the MIC we just got. */
+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
+ packet_disconnect("Hash's MIC didn't verify");
+
+ xfree(msg_tok.value);
+
+ DH_free(dh);
+ if (serverhostkey)
+ xfree(serverhostkey);
+ BN_clear_free(dh_server_pub);
+
+ /* 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);
+ }
+
+ if (gss_kex_context == NULL)
+ gss_kex_context = ctxt;
+ else
+ ssh_gssapi_delete_ctx(&ctxt);
+
kex_derive_keys(kex, hash, shared_secret);
BN_clear_free(shared_secret);
- kex_finish(kex);
+ kex_finish(kex);
}
#endif /* GSSAPI */
/*
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2005 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
{
OM_uint32 maj_status, min_status;
- /* Some GSSAPI implementations use the input value of ret_flags (an
+ /*
+ * 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.*/
+ * activating this non-standard behaviour.
+ */
OM_uint32 ret_flags = 0;
- gss_buffer_desc gssbuf,send_tok,recv_tok,msg_tok;
+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
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;
+ unsigned int klen, kout;
+ unsigned char *kbuf, *hash;
+ DH *dh;
+ int min = -1, max = -1, nbits = -1;
+ BIGNUM *shared_secret = NULL;
+ BIGNUM *dh_client_pub = NULL;
+ int type = 0;
+ int gex;
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) {
- packet_disconnect("Unknown gssapi mechanism");
- }
-
- debug2("%s: Acquiring credentials",__func__);
-
- if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid)))) {
+ /* If we're rekeying, privsep means that some of the private structures
+ * in the GSSAPI code are no longer available. This kludges them back
+ * into life
+ */
+ if (!ssh_gssapi_oid_table_ok())
+ ssh_gssapi_server_mechanisms();
+
+ debug2("%s: Identifying %s", __func__, kex->name);
+ oid = ssh_gssapi_id_kex(NULL, kex->name, &gex);
+ 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);
- packet_disconnect("Unable to acquire credentials for the server");
- }
-
+ fatal("Unable to acquire credentials for the server");
+ }
+
+ if (gex) {
+ debug("Doing group exchange");
+ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
+ min = packet_get_int();
+ nbits = packet_get_int();
+ max = packet_get_int();
+ min = MAX(DH_GRP_MIN, min);
+ max = MIN(DH_GRP_MAX, max);
+ packet_check_eom();
+ if (max < min || nbits < min || max < nbits)
+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
+ min, nbits, max);
+ dh = PRIVSEP(choose_dh(min, nbits, max));
+ if (dh == NULL)
+ packet_disconnect("Protocol error: no matching group found");
+
+ packet_start(SSH2_MSG_KEXGSS_GROUP);
+ packet_put_bignum2(dh->p);
+ packet_put_bignum2(dh->g);
+ packet_send();
+
+ packet_write_wait();
+
+ } else {
+ dh = dh_new_group1();
+ }
+ dh_gen_key(dh, kex->we_need * 8);
+
do {
debug("Wait SSH2_MSG_GSSAPI_INIT");
type = packet_read();
switch(type) {
case SSH2_MSG_KEXGSS_INIT:
- if (dh_client_pub!=NULL)
- packet_disconnect("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)
- packet_disconnect("dh_client_pub == NULL");
- packet_get_bignum2(dh_client_pub);
-
- /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
+ if (dh_client_pub != NULL)
+ fatal("Received KEXGSS_INIT after initialising");
+ recv_tok.value = packet_get_string(&slen);
+ recv_tok.length = slen;
+
+ if ((dh_client_pub = BN_new()) == 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 */
+ recv_tok.value = packet_get_string(&slen);
+ recv_tok.length = slen;
break;
default:
- packet_disconnect("Protocol error: didn't expect packet type %d",
- type);
+ 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);
-
+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
+ &send_tok, &ret_flags));
+
+ xfree(recv_tok.value);
+
+ 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_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) {
+ if (send_tok.length > 0) {
packet_start(SSH2_MSG_KEXGSS_CONTINUE);
- packet_put_string(send_tok.value,send_tok.length);
+ packet_put_string(send_tok.value, send_tok.length);
packet_send();
- packet_write_wait();
- }
- packet_disconnect("gssapi key exchange handshake failed");
+ }
+ packet_disconnect("GSSAPI Key Exchange handshake failed");
}
-
- debug("gss_complete");
+
if (!(ret_flags & GSS_C_MUTUAL_FLAG))
- packet_disconnect("gssapi_mutual authentication failed");
-
+ fatal("Mutual Authentication flag wasn't set");
+
if (!(ret_flags & GSS_C_INTEG_FLAG))
- packet_disconnect("gssapi channel integrity not established");
-
- dh = dh_new_group1();
- dh_gen_key(dh, kex->we_need * 8);
+ fatal("Integrity flag wasn't set");
- if (!dh_pub_is_valid(dh, dh_client_pub))
- packet_disconnect("bad client public DH value");
+ 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);
+ 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
- );
+
+ if (gex) {
+ hash = kexgex_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,
+ min, nbits, max,
+ dh->p, dh->g,
+ dh_client_pub,
+ dh->pub_key,
+ shared_secret
+ );
+ }
+ else {
+ /* 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);
+
+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
fatal("Couldn't get MIC");
- }
-
+
packet_start(SSH2_MSG_KEXGSS_COMPLETE);
packet_put_bignum2(dh->pub_key);
packet_put_string((char *)msg_tok.value,msg_tok.length);
- if (send_tok.length!=0) {
+ if (send_tok.length != 0) {
packet_put_char(1); /* true */
- packet_put_string((char *)send_tok.value,send_tok.length);
+ 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);
-
+ packet_send();
+
+ gss_release_buffer(&min_status, &send_tok);
+ gss_release_buffer(&min_status, &msg_tok);
+
+ if (gss_kex_context == NULL)
+ gss_kex_context = ctxt;
+ else
+ ssh_gssapi_delete_ctx(&ctxt);
+
DH_free(dh);
kex_derive_keys(kex, hash, shared_secret);
return KEY_RSA;
} else if (strcmp(name, "ssh-dss") == 0) {
return KEY_DSA;
- } else if (strcmp(name, "null") == 0){
+ } else if (strcmp(name, "null") == 0) {
return KEY_NULL;
}
debug2("key_type_from_name: unknown key type '%s'", name);
int mm_answer_gss_accept_ctx(int, Buffer *);
int mm_answer_gss_userok(int, Buffer *);
int mm_answer_gss_checkmic(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 *);
{MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx},
{MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
{MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
+ {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
{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_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname},
- {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
#endif
{0, 0, NULL}
};
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
#ifdef GSSAPI
kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
#endif
kex->server = 1;
kex->hostkey_type = buffer_get_int(m);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
}
return (0);
}
return (authenticated);
}
-int
-mm_answer_gss_sign(int socket, Buffer *m) {
- gss_buffer_desc data,hash;
- OM_uint32 major,minor;
+int
+mm_answer_gss_sign(int socket, Buffer *m)
+{
+ gss_buffer_desc data;
+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
+ OM_uint32 major, minor;
u_int len;
- data.value = buffer_get_string(m, &len);
+ data.value = buffer_get_string(m, &len);
data.length = len;
- if (data.length != 20)
- fatal("%s: data length incorrect: %d", __func__,
- (int)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);
+ /* Save the session ID on the first time around */
+ 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);
+ xfree(data.value);
- buffer_clear(m);
- buffer_put_int(m, major);
- buffer_put_string(m, hash.value, hash.length);
+ buffer_clear(m);
+ buffer_put_int(m, major);
+ buffer_put_string(m, hash.value, hash.length);
- mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
- gss_release_buffer(&minor,&hash);
+ gss_release_buffer(&minor, &hash);
- /* Turn on permissions for getpwnam */
+ /* Turn on getpwnam permissions */
monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
-
- return(0);
+
+ return (0);
}
int
return(0);
}
+
#endif /* GSSAPI */
MONITOR_REQ_GSSSETUP, MONITOR_ANS_GSSSETUP,
MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
- MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
MONITOR_REQ_GSSMECHS, MONITOR_ANS_GSSMECHS,
MONITOR_REQ_GSSLOCALNAME, MONITOR_ANS_GSSLOCALNAME,
MONITOR_REQ_GSSERR, MONITOR_ANS_GSSERR,
MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
+ MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
MONITOR_REQ_PAM_START,
MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT,
MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX,
}
OM_uint32
-mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) {
- Buffer m;
- OM_uint32 major;
+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
+{
+ Buffer m;
+ OM_uint32 major;
u_int len;
- buffer_init(&m);
- buffer_put_string(&m, data->value, data->length);
+ 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);
+ 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, &len);
+ major = buffer_get_int(&m);
+ hash->value = buffer_get_string(&m, &len);
hash->length = len;
buffer_free(&m);
-
- return(major);
+
+ return(major);
}
char *
return(0);
}
+
#endif /* GSSAPI */
gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
int mm_ssh_gssapi_userok(char *user);
OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
-OM_uint32 mm_ssh_gssapi_sign(Gssctxt *ctxt, gss_buffer_desc *buffer,
- gss_buffer_desc *hash);
+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
int mm_ssh_gssapi_localname(char **user);
OM_uint32 mm_gss_indicate_mechs(OM_uint32 *minor_status,
gss_OID_set *mech_set);
oClearAllForwardings, oNoHostAuthenticationForLocalhost,
oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
oAddressFamily, oGssAuthentication, oGssKeyEx, oGssDelegateCreds,
+ oGssTrustDns,
oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
oDeprecated, oUnsupported
{ "gssapiauthentication", oGssAuthentication },
{ "gssapikeyexchange", oGssKeyEx },
{ "gssapidelegatecredentials", oGssDelegateCreds },
+ { "gssapitrustdns", oGssTrustDns },
#else
{ "gssapiauthentication", oUnsupported },
{ "gssapikeyexchange", oUnsupported },
{ "gssapidelegatecredentials", oUnsupported },
+ { "gssapitrustdns", oUnsupported },
#endif
{ "fallbacktorsh", oDeprecated },
{ "usersh", oDeprecated },
intptr = &options->gss_deleg_creds;
goto parse_flag;
+ case oGssTrustDns:
+ intptr = &options->gss_trust_dns;
+ goto parse_flag;
+
case oBatchMode:
intptr = &options->batch_mode;
goto parse_flag;
options->gss_authentication = -1;
options->gss_keyex = -1;
options->gss_deleg_creds = -1;
+ options->gss_trust_dns = -1;
options->password_authentication = -1;
options->kbd_interactive_authentication = -1;
options->kbd_interactive_devices = NULL;
options->gss_keyex = 1;
if (options->gss_deleg_creds == -1)
options->gss_deleg_creds = 1;
+ if (options->gss_trust_dns == -1)
+ options->gss_trust_dns = 1;
if (options->password_authentication == -1)
options->password_authentication = 1;
if (options->kbd_interactive_authentication == -1)
int gss_authentication; /* Try GSS authentication */
int gss_keyex;
int gss_deleg_creds; /* Delegate GSS credentials */
+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */
int password_authentication; /* Try password
* authentication. */
int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
#endif
options->kerberos_get_afs_token = -1;
options->gss_authentication=-1;
- options->gss_keyex=-1;
+ options->gss_keyex = -1;
options->gss_cleanup_creds = -1;
options->password_authentication = -1;
options->kbd_interactive_authentication = -1;
sBanner, sUseDNS, sHostbasedAuthentication,
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
- sGssAuthentication, sGssCleanupCreds, sAcceptEnv,
- sGssKeyEx,
+ sGssAuthentication, sGssKeyEx, sGssCleanupCreds, sAcceptEnv,
sUsePrivilegeSeparation,
sDeprecated, sUnsupported
} ServerOpCodes;
int kerberos_get_afs_token; /* If true, try to get AFS token if
* authenticated with Kerberos. */
int gss_authentication; /* If true, permit GSSAPI authentication */
- int gss_keyex; /* If true, permit GSSAPI key exchange. */
+ int gss_keyex; /* If true, permit GSSAPI key exchange */
int gss_cleanup_creds; /* If true, destroy cred cache on logout */
int password_authentication; /* If true, permit password
* authentication. */
}
#endif
-#ifdef GSSAPI
- if (options.gss_authentication) {
- temporarily_use_uid(s->pw);
- ssh_gssapi_storecreds();
- restore_uid();
- }
-#endif
-
#ifdef SSH_AUDIT_EVENTS
if (command != NULL)
PRIVSEP(audit_run_command(command));
#endif /* !MECHGLUE */
/* draft-ietf-secsh-gsskeyex-06 */
-#define SSH2_MSG_KEXGSS_INIT 30
-#define SSH2_MSG_KEXGSS_CONTINUE 31
-#define SSH2_MSG_KEXGSS_COMPLETE 32
-#define SSH2_MSG_KEXGSS_HOSTKEY 33
-#define SSH2_MSG_KEXGSS_ERROR 34
#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60
#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61
#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63
#define SSH_GSS_OIDTYPE 0x06
-#define KEX_GSS_SHA1 "gss-group1-sha1-"
+#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_KEXGSS_GROUPREQ 40
+#define SSH2_MSG_KEXGSS_GROUP 41
+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
typedef struct {
char *filename;
} Gssctxt;
extern ssh_gssapi_mech *supported_mechs[];
-
-char *ssh_gssapi_client_mechanisms(char *);
-gss_OID ssh_gssapi_client_id_kex(Gssctxt *, char *);
+extern Gssctxt *gss_kex_context;
int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
-int ssh_gssapi_check_mechanism(gss_OID oid, const char *host);
-
-/* In the server */
-char *ssh_gssapi_server_mechanisms();
-gss_OID ssh_gssapi_server_id_kex(char *name);
int ssh_gssapi_localname(char **name);
+typedef int ssh_gssapi_check_fn(gss_OID, void *);
+char *ssh_gssapi_client_mechanisms(const char *host);
+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, void *);
+int ssh_gssapi_check_mechanism(gss_OID, void *);
+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int *);
+
+int ssh_gssapi_server_check_mech(gss_OID, void *);
int ssh_gssapi_userok(char *name);
OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
void ssh_gssapi_do_child(char ***, u_int *);
void ssh_gssapi_cleanup_creds(void);
void ssh_gssapi_storecreds(void);
+char * ssh_gssapi_server_mechanisms(void);
+int ssh_gssapi_oid_table_ok();
#ifdef MECHGLUE
gss_cred_id_t __gss_get_mechanism_cred
The default is
.Dq yes .
Note that this option applies to protocol version 2 only.
+.It Cm GSSAPITrustDns
+Set to
+.Dq yes to indicate that the DNS is trusted to securely canonicalize
+the name of the host being connected to. If
+.Dq no, the hostname entered on the
+command line will be passed untouched to the GSSAPI library.
+The default is
+.Dq yes .
+This option only applies to protocol version 2 connections using GSSAPI
+key exchange.
.It Cm HashKnownHosts
Indicates that
.Nm ssh
over another method (e.g.\&
.Cm password )
The default for this option is:
-.Dq hostbased,external-keyx,gssapi-with-mic,gssapi,publickey,keyboard-interactive,password .
+.Dq publickey,gssapi-keyex,external-keyx,gssapi-with-mic,gssapi,password,keyboard-interactive .
.It Cm Protocol
Specifies the protocol versions
.Nm ssh
ssh_kex2(char *host, struct sockaddr *hostaddr)
{
Kex *kex;
+
#ifdef GSSAPI
- char *orig=NULL, *gss=NULL;
+ char *orig = NULL, *gss = NULL;
int len;
+ char *gss_host = NULL;
#endif
xxx_host = host;
/* 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((char *)get_canonical_hostname(1));
+ if (options.gss_trust_dns)
+ gss_host = (char *)get_canonical_hostname(1);
+ else
+ gss_host = host;
+
+ gss = ssh_gssapi_client_mechanisms(gss_host);
if (gss) {
- len = strlen(orig)+strlen(gss)+2;
- myproposal[PROPOSAL_KEX_ALGS]=xmalloc(len);
- snprintf(myproposal[PROPOSAL_KEX_ALGS],len,"%s,%s",gss,orig);
+ debug("Offering GSSAPI proposal: %s", gss);
+ len = strlen(orig) + strlen(gss) + 2;
+ myproposal[PROPOSAL_KEX_ALGS] = xmalloc(len);
+ snprintf(myproposal[PROPOSAL_KEX_ALGS], len, "%s,%s", gss,
+ orig);
}
}
#endif
options.hostkeyalgorithms;
#ifdef GSSAPI
- /* If we've got GSSAPI algorithms, then we also support the
- * 'null' hostkey, as a last resort */
+ /* If we've got GSSAPI algorithms, then we also support the
+ * 'null' hostkey, as a last resort */
if (options.gss_keyex && 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);
+ 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
kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
#ifdef GSSAPI
kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
#endif
kex->client_version_string=client_version_string;
kex->server_version_string=server_version_string;
kex->options.gss_deleg_creds=options.gss_deleg_creds;
#endif
+#ifdef GSSAPI
+ kex->gss_deleg_creds = options.gss_deleg_creds;
+ kex->gss_trust_dns = options.gss_trust_dns;
+ kex->gss_host = gss_host;
+#endif
+
xxx_kex = kex;
dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
int userauth_hostbased(Authctxt *);
int userauth_kerberos(Authctxt *);
-#ifdef GSSAPI
-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
-
#ifdef GSSAPI
int userauth_external(Authctxt *authctxt);
int userauth_gssapi(Authctxt *authctxt);
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 *);
+int userauth_gsskeyex(Authctxt *authctxt);
#endif
void userauth(Authctxt *, char *);
Authmethod authmethods[] = {
#ifdef GSSAPI
+ {"gssapi-keyex",
+ userauth_gsskeyex,
+ &options.gss_authentication,
+ NULL},
{"external-keyx",
userauth_external,
&options.gss_authentication,
static u_int mech = 0;
OM_uint32 min;
int ok = 0;
+ char *gss_host = NULL;
if (!options.gss_authentication) {
verbose("GSSAPI authentication disabled.");
return 0;
}
+ if (options.gss_trust_dns)
+ gss_host = (char *)get_canonical_hostname(1);
+ else
+ gss_host = (char *)authctxt->host;
+
/* Try one GSSAPI method at a time, rather than sending them all at
* once. */
/* My DER encoding requires length<128 */
if (gss_supported->elements[mech].length < 128 &&
ssh_gssapi_check_mechanism(&gss_supported->elements[mech],
- get_canonical_hostname(1)) &&
+ gss_host) &&
!GSS_ERROR(ssh_gssapi_import_name(gssctxt,
- get_canonical_hostname(1)))) {
+ gss_host))) {
ok = 1; /* Mechanism works */
} else {
mech++;
{
Authctxt *authctxt = ctxt;
Gssctxt *gssctxt;
- int oidlen;
- char *oidv;
+ unsigned int oidlen;
+ unsigned char *oidv;
if (authctxt == NULL)
fatal("input_gssapi_response: no authentication context");
xfree(lang);
}
+#ifdef GSI
+extern
+const gss_OID_desc * const gss_mech_globus_gssapi_openssl;
+#define is_gsi_oid(oid) \
+ (oid->length == gss_mech_globus_gssapi_openssl->length && \
+ (memcmp(oid->elements, gss_mech_globus_gssapi_openssl->elements, \
+ oid->length) == 0))
+#endif
+
int
userauth_external(Authctxt *authctxt)
{
/* The client MUST NOT try this method if initial key exchange
was not performed using a GSSAPI-based key exchange
method. */
- if (xxx_kex->kex_type != KEX_GSS_GRP1_SHA1) {
+ if (gss_kex_context == NULL) {
debug2("gsskex not performed, skipping external-keyx");
return 0;
}
debug2("userauth_external");
packet_start(SSH2_MSG_USERAUTH_REQUEST);
#ifdef GSI
- if(options.implicit) {
- packet_put_cstring("");
+ if (options.implicit && is_gsi_oid(gss_kex_context->oid)) {
+ packet_put_cstring("");
} else {
#endif
- packet_put_cstring(authctxt->server_user);
+ packet_put_cstring(authctxt->server_user);
#ifdef GSI
}
#endif
packet_write_wait();
return 1;
}
+int
+userauth_gsskeyex(Authctxt *authctxt)
+{
+ Buffer b;
+ gss_buffer_desc gssbuf;
+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
+ OM_uint32 ms;
+
+ static int attempt = 0;
+ if (attempt++ >= 1)
+ return (0);
+
+ if (gss_kex_context == NULL) {
+ debug("No valid Key exchange context");
+ return (0);
+ }
+
+#ifdef GSI
+ if (options.implicit && is_gsi_oid(gss_kex_context->oid)) {
+ ssh_gssapi_buildmic(&b, "", authctxt->service, "gssapi-keyex");
+ } else {
+#endif
+ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
+ "gssapi-keyex");
+#ifdef GSI
+ }
+#endif
+
+ gssbuf.value = buffer_ptr(&b);
+ gssbuf.length = buffer_len(&b);
+
+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
+ buffer_free(&b);
+ return (0);
+ }
+
+ packet_start(SSH2_MSG_USERAUTH_REQUEST);
+#ifdef GSI
+ if (options.implicit && is_gsi_oid(gss_kex_context->oid)) {
+ packet_put_cstring("");
+ } else {
+#endif
+ packet_put_cstring(authctxt->server_user);
+#ifdef GSI
+ }
+#endif
+ packet_put_cstring(authctxt->service);
+ packet_put_cstring(authctxt->method->name);
+ packet_put_string(mic.value, mic.length);
+ packet_send();
+
+ buffer_free(&b);
+ gss_release_buffer(&ms, &mic);
+
+ return (1);
+}
+
#endif /* GSSAPI */
int
#include "monitor_wrap.h"
#include "monitor_fdpass.h"
-#ifdef GSSAPI
-#include "ssh-gss.h"
+#ifdef USE_SECURITY_SESSION_API
+#include <Security/AuthSession.h>
#endif
-#ifdef GSSAPI
-#include <openssl/md5.h>
-#include "bufaux.h"
-#endif /* GSSAPI */
-
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
/* Log the connection. */
verbose("Connection from %.500s port %d", remote_ip, remote_port);
+#ifdef USE_SECURITY_SESSION_API
+ /*
+ * Create a new security session for use by the new user login if
+ * the current session is the root session or we are not launched
+ * by inetd (eg: debugging mode or server mode). We do not
+ * necessarily need to create a session if we are launched from
+ * inetd because Panther xinetd will create a session for us.
+ *
+ * The only case where this logic will fail is if there is an
+ * inetd running in a non-root session which is not creating
+ * new sessions for us. Then all the users will end up in the
+ * same session (bad).
+ *
+ * When the client exits, the session will be destroyed for us
+ * automatically.
+ *
+ * We must create the session before any credentials are stored
+ * (including AFS pags, which happens a few lines below).
+ */
+ {
+ OSStatus err = 0;
+ SecuritySessionId sid = 0;
+ SessionAttributeBits sattrs = 0;
+
+ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
+ if (err)
+ error("SessionGetInfo() failed with error %.8X",
+ (unsigned) err);
+ else
+ debug("Current Session ID is %.8X / Session Attributes a
+re %.8X",
+ (unsigned) sid, (unsigned) sattrs);
+
+ if (inetd_flag && !(sattrs & sessionIsRoot))
+ debug("Running in inetd mode in a non-root session... "
+ "assuming inetd created the session for us.");
+ else {
+ debug("Creating new security session...");
+ err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
+ if (err)
+ error("SessionCreate() failed with error %.8X",
+ (unsigned) err);
+
+ err = SessionGetInfo(callerSecuritySession, &sid,
+ &sattrs);
+ if (err)
+ error("SessionGetInfo() failed with error %.8X",
+ (unsigned) err);
+ else
+ debug("New Session ID is %.8X / Session Attribut
+es are %.8X",
+ (unsigned) sid, (unsigned) sattrs);
+ }
+ }
+#endif
+
/*
* We don\'t want to listen forever unless the other side
* successfully authenticates itself. So we set up an alarm which is
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
+ /* start key exchange */
+
#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;
-
+ orig = myproposal[PROPOSAL_KEX_ALGS];
+
+ /*
+ * If we don't have a host key, then there's no point advertising
+ * the other key exchange algorithms
+ */
+
+ 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);
+ int len = strlen(orig) + strlen(gss) + 2;
+ newstr = xmalloc(len);
+ snprintf(newstr, len, "%s,%s", gss, orig);
} else if (gss) {
- newstr=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";
+ newstr = orig;
}
+ /*
+ * If we've got GSSAPI mechanisms, then we've got the 'null' host
+ * key alg, but we can't tell people about it unless its the only
+ * host key algorithm we support
+ */
+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
+
if (newstr)
- myproposal[PROPOSAL_KEX_ALGS]=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;
+ /* start key exchange */
+ kex = kex_setup(myproposal);
+ kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
#ifdef GSSAPI
kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
#endif
- kex->server = 1;
- kex->client_version_string=client_version_string;
- kex->server_version_string=server_version_string;
+ kex->server = 1;
+ kex->client_version_string=client_version_string;
+ kex->server_version_string=server_version_string;
kex->load_host_key=&get_hostkey_by_type;
kex->host_key_index=&get_hostkey_index;
The default is
.Dq yes .
Note that this option applies to protocol version 2 only.
-.It Cm GSSAPICleanupCredentials
-Specifies whether to automatically destroy the user's credentials cache
-on logout.
-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.
+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
+doesn't rely on ssh keys to verify host identity.
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.
+.It Cm GSSAPICleanupCredentials
+Specifies whether to automatically destroy the user's credentials cache
+on logout.
The default is
.Dq yes .
Note that this option applies to protocol version 2 only.