#
# GSS-API key exchange support
#
# Updated to apply to OpenSSH 6.5.
# Default value for GSSAPIKeyExchange changed to yes to match SunSSH behavior.
#
# Update Sep 5, 2016:
# Upstream renamed and moved canohost.c`get_canonical_hostname to sshd-specific
# auth.c`auth_get_canonical_hostname. In Solaris specific GSS-API key exchange
# code we need this functionality on the client side too, for canonicalizing
# server hostbased service principal. We have moved remote_hostname back to
#
# TODO:
# When we upgrade Kerberos in Solaris to future version 1.15, we will use
# krb5_expand_hostname for hostname canonicalization instead.
#
# Upstream rejected GSS-API key exchange several times before.
#
diff -pur old/Makefile.in new/Makefile.in
--- old/Makefile.in
+++ new/Makefile.in
@@ -87,6 +87,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
+ kexgssc.o \
@@ -107,7 +108,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
diff -pur old/auth.c new/auth.c
--- old/auth.c
+++ new/auth.c
@@ -786,99 +786,6 @@ fakepw(void)
}
/*
- * Returns the remote DNS hostname as a string. The returned string must not
- * be freed. NB. this will usually trigger a DNS query the first time it is
- * called.
- * This function does additional checks on the hostname to mitigate some
- * attacks on legacy rhosts-style authentication.
- * XXX is RhostsRSAAuthentication vulnerable to these?
- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
- */
-
-static char *
-remote_hostname(struct ssh *ssh)
-{
- struct sockaddr_storage from;
- socklen_t fromlen;
- struct addrinfo hints, *ai, *aitop;
- char name[NI_MAXHOST], ntop2[NI_MAXHOST];
- const char *ntop = ssh_remote_ipaddr(ssh);
-
- /* Get IP address of client. */
- fromlen = sizeof(from);
- memset(&from, 0, sizeof(from));
- if (getpeername(ssh_packet_get_connection_in(ssh),
- (struct sockaddr *)&from, &fromlen) < 0) {
- debug("getpeername failed: %.100s", strerror(errno));
- return strdup(ntop);
- }
-
- ipv64_normalise_mapped(&from, &fromlen);
- if (from.ss_family == AF_INET6)
- fromlen = sizeof(struct sockaddr_in6);
-
- debug3("Trying to reverse map address %.100s.", ntop);
- /* Map the IP address to a host name. */
- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
- NULL, 0, NI_NAMEREQD) != 0) {
- /* Host name not found. Use ip address. */
- return strdup(ntop);
- }
-
- /*
- * if reverse lookup result looks like a numeric hostname,
- * someone is trying to trick us by PTR record like following:
- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
- */
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM; /*dummy*/
- hints.ai_flags = AI_NUMERICHOST;
- if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
- logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
- name, ntop);
- freeaddrinfo(ai);
- return strdup(ntop);
- }
-
- /* Names are stored in lowercase. */
- lowercase(name);
-
- /*
- * Map it back to an IP address and check that the given
- * address actually is an address of this host. This is
- * necessary because anyone with access to a name server can
- * define arbitrary names for an IP address. Mapping from
- * name to IP address can be trusted better (but can still be
- * fooled if the intruder has access to the name server of
- * the domain).
- */
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_STREAM;
- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
- logit("reverse mapping checking getaddrinfo for %.700s "
- "[%s] failed.", name, ntop);
- return strdup(ntop);
- }
- /* Look for the address from the list of addresses. */
- for (ai = aitop; ai; ai = ai->ai_next) {
- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
- (strcmp(ntop, ntop2) == 0))
- break;
- }
- freeaddrinfo(aitop);
- /* If we reached the end of the list, the address was not there. */
- if (ai == NULL) {
- /* Address not found for the host name. */
- logit("Address %.100s maps to %.600s, but this does not "
- "map back to the address.", ntop, name);
- return strdup(ntop);
- }
- return strdup(name);
-}
-
-/*
* Return the canonical name of the host in the other side of the current
* connection. The host name is cached, so it is efficient to call this
* several times.
diff -pur old/auth2-gss.c new/auth2-gss.c
--- old/auth2-gss.c
+++ new/auth2-gss.c
@@ -1,7 +1,7 @@
/* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */
/*
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -53,6 +53,39 @@ static int input_gssapi_mic(int type, u_
static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
static int input_gssapi_errtok(int, u_int32_t, void *);
+/*
+ * The 'gssapi_keyex' userauth mechanism.
+ */
+static int
+userauth_gsskeyex(Authctxt *authctxt)
+{
+ int authenticated = 0;
+ Buffer b;
+ gss_buffer_desc mic, gssbuf;
+ 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);
+
+ /* 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))))
+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+
+ buffer_free(&b);
+ free(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)
@@ -290,6 +323,12 @@ input_gssapi_mic(int type, u_int32_t ple
return 0;
}
+Authmethod method_gsskeyex = {
+ "gssapi-keyex",
+ userauth_gsskeyex,
+};
+
Authmethod method_gssapi = {
"gssapi-with-mic",
userauth_gssapi,
diff -pur old/auth2.c new/auth2.c
--- old/auth2.c
+++ new/auth2.c
@@ -70,6 +70,7 @@ extern Authmethod method_passwd;
extern Authmethod method_kbdint;
extern Authmethod method_hostbased;
#ifdef GSSAPI
+extern Authmethod method_gsskeyex;
extern Authmethod method_gssapi;
#endif
@@ -77,6 +78,7 @@ Authmethod *authmethods[] = {
&method_none,
&method_pubkey,
#ifdef GSSAPI
+ &method_gsskeyex,
&method_gssapi,
#endif
&method_passwd,
diff -pur old/canohost.c new/canohost.c
--- old/canohost.c
+++ new/canohost.c
@@ -202,3 +202,97 @@ get_local_port(int sock)
{
return get_sock_port(sock, 1);
}
+
+/*
+ * Returns the remote DNS hostname as a string. The returned string must not
+ * be freed. NB. this will usually trigger a DNS query the first time it is
+ * called.
+ * This function does additional checks on the hostname to mitigate some
+ * attacks on legacy rhosts-style authentication.
+ * XXX is RhostsRSAAuthentication vulnerable to these?
+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
+ */
+
+/* Oracle Solaris - moved out of auth.c for use in GSSKEX in sshconnect2.c */
+char *
+remote_hostname(struct ssh *ssh)
+{
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+ struct addrinfo hints, *ai, *aitop;
+ char name[NI_MAXHOST], ntop2[NI_MAXHOST];
+ const char *ntop = ssh_remote_ipaddr(ssh);
+
+ /* Get IP address of client. */
+ fromlen = sizeof(from);
+ memset(&from, 0, sizeof(from));
+ if (getpeername(ssh_packet_get_connection_in(ssh),
+ (struct sockaddr *)&from, &fromlen) < 0) {
+ debug("getpeername failed: %.100s", strerror(errno));
+ return strdup(ntop);
+ }
+
+ ipv64_normalise_mapped(&from, &fromlen);
+ if (from.ss_family == AF_INET6)
+ fromlen = sizeof(struct sockaddr_in6);
+
+ debug3("Trying to reverse map address %.100s.", ntop);
+ /* Map the IP address to a host name. */
+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
+ NULL, 0, NI_NAMEREQD) != 0) {
+ /* Host name not found. Use ip address. */
+ return strdup(ntop);
+ }
+
+ /*
+ * if reverse lookup result looks like a numeric hostname,
+ * someone is trying to trick us by PTR record like following:
+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
+ */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
+ name, ntop);
+ freeaddrinfo(ai);
+ return strdup(ntop);
+ }
+
+ /* Names are stored in lowercase. */
+ lowercase(name);
+
+ /*
+ * Map it back to an IP address and check that the given
+ * address actually is an address of this host. This is
+ * necessary because anyone with access to a name server can
+ * define arbitrary names for an IP address. Mapping from
+ * name to IP address can be trusted better (but can still be
+ * fooled if the intruder has access to the name server of
+ * the domain).
+ */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
+ logit("reverse mapping checking getaddrinfo for %.700s "
+ "[%s] failed.", name, ntop);
+ return strdup(ntop);
+ }
+ /* Look for the address from the list of addresses. */
+ for (ai = aitop; ai; ai = ai->ai_next) {
+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
+ (strcmp(ntop, ntop2) == 0))
+ break;
+ }
+ freeaddrinfo(aitop);
+ /* If we reached the end of the list, the address was not there. */
+ if (ai == NULL) {
+ /* Address not found for the host name. */
+ logit("Address %.100s maps to %.600s, but this does not "
+ "map back to the address.", ntop, name);
+ return strdup(ntop);
+ }
+ return strdup(name);
+}
diff -pur old/canohost.h new/canohost.h
--- old/canohost.h
+++ new/canohost.h
@@ -21,6 +21,9 @@ char *get_local_ipaddr(int);
char *get_local_name(int);
int get_local_port(int);
+#include "packet.h"
+char *remote_hostname(struct ssh *);
+
#endif /* _CANOHOST_H */
void ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *);
diff -pur old/gss-genr.c new/gss-genr.c
--- old/gss-genr.c
+++ new/gss-genr.c
@@ -1,7 +1,7 @@
/* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */
/*
- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -41,12 +41,167 @@
#include "buffer.h"
#include "log.h"
#include "ssh2.h"
+#include "cipher.h"
+#include "key.h"
+#include "kex.h"
+#include <openssl/evp.h>
#include "ssh-gss.h"
extern u_char *session_id2;
extern u_int session_id2_len;
+typedef struct {
+ char *encoded;
+ gss_OID oid;
+} ssh_gss_kex_mapping;
+
+/*
+ * 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
+ *
+ * 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(const char *host) {
+ gss_OID_set gss_supported;
+ OM_uint32 min_status;
+
+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
+ return NULL;
+
+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
+ host));
+}
+
+char *
+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
+ const char *data) {
+ Buffer buf;
+ size_t i;
+ int oidpos, enclen;
+ char *mechs, *encoded;
+ u_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++)
+ free(gss_enc2oid[i].encoded);
+ free(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)(NULL, &(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,
+ gss_supported->elements[i].elements,
+ gss_supported->elements[i].length);
+ EVP_DigestFinal(&md, digest, NULL);
+
+ 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_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);
+ buffer_put_char(&buf, ',');
+ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
+ sizeof(KEX_GSS_GRP14_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));
+ buffer_free(&buf);
+
+ if (strlen(mechs) == 0) {
+ free(mechs);
+ mechs = NULL;
+ }
+
+ return (mechs);
+}
+
+gss_OID
+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
+ int i = 0;
+
+ switch (kex_type) {
+ case KEX_GSS_GRP1_SHA1:
+ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
+ return GSS_C_NO_OID;
+ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
+ break;
+ case KEX_GSS_GRP14_SHA1:
+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
+ return GSS_C_NO_OID;
+ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
+ break;
+ case KEX_GSS_GEX_SHA1:
+ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
+ return GSS_C_NO_OID;
+ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
+ break;
+ default:
+ return GSS_C_NO_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;
+}
+
/* 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)
@@ -231,6 +386,9 @@ ssh_gssapi_import_name(Gssctxt *ctx, con
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);
@@ -238,6 +396,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer
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)
@@ -256,6 +427,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx
gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
OM_uint32 major, minor;
gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
+ Gssctxt *intctx = NULL;
+
+ if (ctx == NULL)
+ ctx = &intctx;
/* RFC 4462 says we MUST NOT do SPNEGO */
if (oid->length == spnego_oid.length &&
@@ -274,7 +449,7 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx
GSS_C_NO_BUFFER);
}
- if (GSS_ERROR(major))
+ if (GSS_ERROR(major) || intctx != NULL)
ssh_gssapi_delete_ctx(ctx);
return (!GSS_ERROR(major));
diff -pur old/gss-serv.c new/gss-serv.c
--- old/gss-serv.c
+++ new/gss-serv.c
@@ -1,7 +1,7 @@
/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */
/*
- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -47,6 +47,7 @@
#include "servconf.h"
#include "ssh-gss.h"
+#include "monitor_wrap.h"
extern ServerOptions options;
@@ -142,6 +143,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss
}
/* Unprivileged */
+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));
+}
+
+/* Unprivileged */
+int
+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data) {
+ Gssctxt *ctx = NULL;
+ int res;
+
+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
+ ssh_gssapi_delete_ctx(&ctx);
+
+ return (res);
+}
+
+/* Unprivileged */
void
ssh_gssapi_supported_oids(gss_OID_set *oidset)
{
@@ -151,7 +174,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o
gss_OID_set supported;
gss_create_empty_oid_set(&min_status, oidset);
- gss_indicate_mechs(&min_status, &supported);
+
+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
+ return;
while (supported_mechs[i]->name != NULL) {
if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -427,14 +452,4 @@ ssh_gssapi_userok(char *user)
return (0);
}
-/* Privileged */
-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
--- old/kex.c
+++ new/kex.c
@@ -55,6 +55,10 @@
#include "sshbuf.h"
#include "digest.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
# if defined(HAVE_EVP_SHA256)
# define evp_ssh_sha256 EVP_sha256
@@ -111,6 +115,11 @@ static const struct kexalg kexalgs[] = {
#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
+#ifdef GSSAPI
+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
+#endif
{ NULL, -1, -1, -1},
};
@@ -142,7 +151,7 @@ kex_alg_by_name(const char *name)
const struct kexalg *k;
for (k = kexalgs; k->name != NULL; k++) {
- if (strcmp(k->name, name) == 0)
+ if (strncmp(k->name, name, strlen(k->name)) == 0)
return k;
}
return NULL;
--- old/kex.h
+++ new/kex.h
@@ -98,6 +98,9 @@ enum kex_exchange {
KEX_DH_GEX_SHA256,
KEX_ECDH_SHA2,
KEX_C25519_SHA256,
+ KEX_GSS_GRP1_SHA1,
+ KEX_GSS_GRP14_SHA1,
+ KEX_GSS_GEX_SHA1,
KEX_MAX
};
@@ -146,6 +149,10 @@ struct kex {
u_int flags;
int hash_alg;
int ec_nid;
+#ifdef GSSAPI
+ int gss_deleg_creds;
+ char *gss_host;
+#endif
char *client_version_string;
char *server_version_string;
char *failed_choice;
@@ -195,6 +202,10 @@ int kexecdh_client(struct ssh *);
int kexecdh_server(struct ssh *);
int kexc25519_client(struct ssh *);
int kexc25519_server(struct ssh *);
+#ifdef GSSAPI
+int kexgss_client(struct ssh *);
+int kexgss_server(struct ssh *);
+#endif
int kex_dh_hash(int, const char *, const char *,
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
diff -pur old/monitor.c new/monitor.c
--- old/monitor.c
+++ new/monitor.c
@@ -161,6 +161,7 @@ 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_checkmic(int, Buffer *);
+int mm_answer_gss_sign(int, Buffer *);
#endif
#ifdef SSH_AUDIT_EVENTS
@@ -245,11 +246,17 @@ struct mon_table mon_dispatch_proto20[]
{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},
#endif
{0, 0, NULL}
};
struct mon_table mon_dispatch_postauth20[] = {
+#ifdef GSSAPI
+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
+#endif
#ifdef WITH_OPENSSL
{MONITOR_REQ_MODULI, 0, mm_answer_moduli},
#endif
@@ -364,6 +371,10 @@ monitor_child_preauth(Authctxt *_authctx
/* 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);
+#endif
} else {
mon_dispatch = mon_dispatch_proto15;
@@ -503,6 +514,10 @@ monitor_child_postauth(struct monitor *p
monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
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_GSSSETUP, 1);
+#endif
} else {
mon_dispatch = mon_dispatch_postauth15;
monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1939,6 +1954,13 @@ monitor_apply_keystate(struct monitor *p
# endif
#endif /* WITH_OPENSSL */
kex->kex[KEX_C25519_SHA256] = kexc25519_server;
+#ifdef GSSAPI
+ if (options.gss_keyex) {
+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
+ }
+#endif
kex->load_host_public_key=&get_hostkey_public_by_type;
kex->load_host_private_key=&get_hostkey_private_by_type;
kex->host_key_index=&get_hostkey_index;
@@ -2038,6 +2060,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer
OM_uint32 major;
u_int len;
+ if (!options.gss_authentication && !options.gss_keyex)
+ fatal("In GSSAPI monitor when GSSAPI is disabled");
+
goid.elements = buffer_get_string(m, &len);
goid.length = len;
@@ -2065,6 +2090,9 @@ mm_answer_gss_accept_ctx(int sock, Buffe
OM_uint32 flags = 0; /* GSI needs this */
u_int len;
+ if (!options.gss_authentication && !options.gss_keyex)
+ fatal("In GSSAPI monitor when GSSAPI is disabled");
+
in.value = buffer_get_string(m, &len);
in.length = len;
major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2082,6 +2110,7 @@ mm_answer_gss_accept_ctx(int sock, Buffe
monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
}
return (0);
}
@@ -2093,6 +2122,9 @@ mm_answer_gss_checkmic(int sock, Buffer
OM_uint32 ret;
u_int len;
+ if (!options.gss_authentication && !options.gss_keyex)
+ fatal("In GSSAPI monitor when GSSAPI is disabled");
+
gssbuf.value = buffer_get_string(m, &len);
gssbuf.length = len;
mic.value = buffer_get_string(m, &len);
@@ -2119,6 +2151,9 @@ mm_answer_gss_userok(int sock, Buffer *m
{
int authenticated;
+ if (!options.gss_authentication && !options.gss_keyex)
+ fatal("In GSSAPI monitor when GSSAPI is disabled");
+
authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
buffer_clear(m);
@@ -2132,5 +2167,47 @@ mm_answer_gss_userok(int sock, Buffer *m
/* Monitor loop will terminate if authenticated */
return (authenticated);
}
+
+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;
+
+ if (!options.gss_authentication && !options.gss_keyex)
+ fatal("In GSSAPI monitor when GSSAPI is disabled");
+
+ data.value = buffer_get_string(m, &len);
+ data.length = len;
+ if (data.length != 20)
+ fatal("%s: data length incorrect: %d", __func__,
+ (int) data.length);
+
+ /* 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);
+
+ free(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 getpwnam permissions */
+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
+
+ return (0);
+}
+
#endif /* GSSAPI */
diff -pur old/monitor.h new/monitor.h
--- old/monitor.h
+++ new/monitor.h
@@ -68,6 +68,9 @@ enum monitor_reqtype {
#ifdef PAM_ENHANCEMENT
MONITOR_REQ_AUTHMETHOD = 114,
#endif
+#ifdef GSSAPI
+ MONITOR_REQ_GSSSIGN = 130, MONITOR_ANS_GSSSIGN = 131,
+#endif
};
struct mm_master;
diff -pur old/monitor_wrap.c new/monitor_wrap.c
@@ -1108,5 +1108,28 @@ mm_ssh_gssapi_userok(char *user)
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;
+ u_int len;
+
+ 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, &len);
+ hash->length = len;
+
+ buffer_free(&m);
+
+ return(major);
+}
+
#endif /* GSSAPI */
diff -pur old/monitor_wrap.h new/monitor_wrap.h
@@ -62,6 +62,7 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssct
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 *, gss_buffer_t, gss_buffer_t);
#endif
#ifdef USE_PAM
diff -pur old/readconf.c new/readconf.c
--- old/readconf.c
+++ new/readconf.c
@@ -160,6 +160,7 @@ typedef enum {
oClearAllForwardings, oNoHostAuthenticationForLocalhost,
oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
oAddressFamily, oGssAuthentication, oGssDelegateCreds,
+ oGssKeyEx,
oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
oSendEnv, oControlPath, oControlMaster, oControlPersist,
oHashKnownHosts,
@@ -211,11 +212,15 @@ static struct {
{ "gssauthentication", oGssAuthentication }, /* alias */
{ "gssapidelegatecredentials", oGssDelegateCreds },
{ "gssdelegatecreds", oGssDelegateCreds }, /* alias */
+ { "gssapikeyexchange", oGssKeyEx },
+ { "gsskeyex", oGssKeyEx }, /* alias */
#else
{ "gssapiauthentication", oUnsupported },
{ "gssauthentication", oUnsupported },
{ "gssapidelegatecredentials", oUnsupported },
{ "gssdelegatecreds", oUnsupported },
+ { "gssapikeyexchange", oUnsupported },
+ { "gsskeyex", oUnsupported },
#endif
{ "fallbacktorsh", oDeprecated },
{ "usersh", oDeprecated },
@@ -1002,6 +1007,10 @@ parse_time:
intptr = &options->gss_authentication;
goto parse_flag;
+ case oGssKeyEx:
+ intptr = &options->gss_keyex;
+ goto parse_flag;
+
case oGssDelegateCreds:
intptr = &options->gss_deleg_creds;
goto parse_flag;
@@ -1824,6 +1833,7 @@ initialize_options(Options * options)
options->pubkey_authentication = -1;
options->challenge_response_authentication = -1;
options->gss_authentication = -1;
+ options->gss_keyex = -1;
options->gss_deleg_creds = -1;
options->password_authentication = -1;
options->kbd_interactive_authentication = -1;
@@ -1979,6 +1989,12 @@ fill_default_options(Options * options)
#else
options->gss_authentication = 0;
#endif
+ if (options->gss_keyex == -1)
+#ifdef OPTION_DEFAULT_VALUE
+ options->gss_keyex = 1;
+#else
+ options->gss_keyex = 0;
+#endif
if (options->gss_deleg_creds == -1)
options->gss_deleg_creds = 0;
if (options->password_authentication == -1)
diff -pur old/readconf.h new/readconf.h
--- old/readconf.h
+++ new/readconf.h
@@ -45,6 +45,7 @@ typedef struct {
int challenge_response_authentication;
/* Try S/Key or TIS, authentication. */
int gss_authentication; /* Try GSS authentication */
+ int gss_keyex; /* Try GSS key exchange */
int gss_deleg_creds; /* Delegate GSS credentials */
int password_authentication; /* Try password
* authentication. */
diff -pur old/servconf.c new/servconf.c
--- old/servconf.c
+++ new/servconf.c
@@ -117,6 +117,7 @@ initialize_server_options(ServerOptions
options->kerberos_ticket_cleanup = -1;
options->kerberos_get_afs_token = -1;
options->gss_authentication=-1;
+ options->gss_keyex = -1;
options->gss_cleanup_creds = -1;
options->gss_strict_acceptor = -1;
options->password_authentication = -1;
@@ -312,6 +313,12 @@ fill_default_server_options(ServerOption
#else
options->gss_authentication = 0;
#endif
+ if (options->gss_keyex == -1)
+#ifdef OPTION_DEFAULT_VALUE
+ options->gss_keyex = 1;
+#else
+ options->gss_keyex = 0;
+#endif
if (options->gss_cleanup_creds == -1)
options->gss_cleanup_creds = 1;
if (options->gss_strict_acceptor == -1)
@@ -457,6 +464,7 @@ typedef enum {
sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes,
sHostKeyAlgorithms,
sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
+ sGssKeyEx,
sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
sAcceptEnv, sPermitTunnel,
sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
@@ -534,6 +542,8 @@ static struct {
#ifdef GSSAPI
{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
{ "gssauthentication", sGssAuthentication, SSHCFG_ALL }, /* alias */
+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_ALL },
+ { "gsskeyex", sGssKeyEx, SSHCFG_ALL }, /* alias */
#ifdef USE_GSS_STORE_CRED
{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
#else /* USE_GSS_STORE_CRED */
@@ -543,6 +553,8 @@ static struct {
#else
{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
{ "gssauthentication", sUnsupported, SSHCFG_ALL }, /* alias */
+ { "gssapikeyexchange", sUnsupported,, SSHCFG_ALL },
+ { "gsskeyex", sUnsupported,, SSHCFG_ALL },
{ "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
{ "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
#endif
@@ -1328,6 +1340,10 @@ process_server_config_line(ServerOptions
intptr = &options->gss_authentication;
goto parse_flag;
+ case sGssKeyEx:
+ intptr = &options->gss_keyex;
+ goto parse_flag;
+
case sGssCleanupCreds:
intptr = &options->gss_cleanup_creds;
goto parse_flag;
@@ -2416,6 +2432,7 @@ dump_config(ServerOptions *o)
#endif
#ifdef GSSAPI
dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
#ifndef USE_GSS_STORE_CRED
dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
#endif /* !USE_GSS_STORE_CRED */
diff -pur old/servconf.h new/servconf.h
--- old/servconf.h
+++ new/servconf.h
@@ -122,6 +122,7 @@ typedef struct {
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_cleanup_creds; /* If true, destroy cred cache on logout */
int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
int password_authentication; /* If true, permit password
diff -pur old/ssh-gss.h new/ssh-gss.h
--- old/ssh-gss.h
+++ new/ssh-gss.h
@@ -61,6 +61,17 @@
#define SSH_GSS_OIDTYPE 0x06
+#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_GRP14_SHA1_ID "gss-group14-sha1-"
+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
+
typedef struct {
char *filename;
char *envvar;
@@ -98,6 +109,7 @@ typedef struct {
} Gssctxt;
extern ssh_gssapi_mech *supported_mechs[];
+extern Gssctxt *gss_kex_context;
int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -122,6 +134,11 @@ void ssh_gssapi_buildmic(Buffer *, const
int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
/* In the server */
+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *);
+char *ssh_gssapi_client_mechanisms(const char *host);
+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *);
+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *);
OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
int ssh_gssapi_userok(char *name);
OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
@@ -129,6 +146,8 @@ 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();
#endif /* GSSAPI */
#endif /* _SSH_GSS_H */
diff -pur old/ssh_config.5 new/ssh_config.5
--- old/ssh_config.5
+++ new/ssh_config.5
@@ -834,6 +834,12 @@ The default is
Specifies whether user authentication based on GSSAPI is allowed.
The default on Solaris is
.Dq yes .
+.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 on Solaris is
+.Dq yes .
+Note that this option applies to protocol version 2 only.
.It Cm GSSAPIDelegateCredentials
Forward (delegate) credentials to the server.
The default is
diff -pur old/sshconnect2.c new/sshconnect2.c
@@ -165,11 +165,35 @@ ssh_kex2(char *host, struct sockaddr *ho
char *s;
struct kex *kex;
int r;
+#ifdef GSSAPI
+ char *orig = NULL, *gss = NULL;
+ char *gss_host = NULL;
+#endif
xxx_host = host;
xxx_hostaddr = hostaddr;
- if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
+ if (options.kex_algorithms != NULL)
+ myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
+
+#ifdef GSSAPI
+ if (options.gss_keyex) {
+ /* Add the GSSAPI mechanisms currently supported on this
+ * client to the key exchange algorithm proposal */
+ orig = myproposal[PROPOSAL_KEX_ALGS];
+
+ gss_host = (char *)remote_hostname(active_state);
+
+ gss = ssh_gssapi_client_mechanisms(gss_host);
+ if (gss) {
+ debug("Offering GSSAPI proposal: %s", gss);
+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
+ "%s,%s", gss, orig);
+ }
+ }
+#endif
+
+ if (!(s = kex_names_cat(myproposal[PROPOSAL_KEX_ALGS], "ext-info-c")))
fatal("%s: kex_names_cat", __func__);
myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s);
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
@@ -196,6 +220,17 @@ ssh_kex2(char *host, struct sockaddr *ho
order_hostkeyalgs(host, hostaddr, port));
}
+#ifdef GSSAPI
+ /* 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];
+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
+ "%s,null", orig);
+ free(gss);
+ }
+#endif
+
packet_set_rekey_limits((u_int32_t)options.rekey_limit,
(time_t)options.rekey_interval);
@@ -217,9 +252,22 @@ ssh_kex2(char *host, struct sockaddr *ho
# endif
#endif
kex->kex[KEX_C25519_SHA256] = kexc25519_client;
+#ifdef GSSAPI
+ if (options.gss_keyex) {
+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
+ kex->kex[KEX_GSS_GRP14_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->verify_host_key=&verify_host_key_callback;
+#ifdef GSSAPI
+ if (options.gss_keyex) {
+ kex->gss_deleg_creds = options.gss_deleg_creds;
+ kex->gss_host = gss_host;
+ }
+#endif
dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
@@ -315,6 +363,7 @@ int input_gssapi_token(int type, u_int32
int input_gssapi_hash(int type, u_int32_t, void *);
int input_gssapi_error(int, u_int32_t, void *);
int input_gssapi_errtok(int, u_int32_t, void *);
+int userauth_gsskeyex(Authctxt *authctxt);
#endif
void userauth(Authctxt *, char *);
@@ -330,6 +379,11 @@ static char *authmethods_get(void);
Authmethod authmethods[] = {
#ifdef GSSAPI
+ {"gssapi-keyex",
+ userauth_gsskeyex,
+ NULL,
+ NULL},
{"gssapi-with-mic",
userauth_gssapi,
NULL,
@@ -672,7 +726,10 @@ userauth_gssapi(Authctxt *authctxt)
* once. */
if (gss_supported == NULL)
- gss_indicate_mechs(&min, &gss_supported);
+ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
+ gss_supported = NULL;
+ return 0;
+ }
/* Check to see if the mechanism is usable before we offer it */
while (mech < gss_supported->count && !ok) {
@@ -776,8 +833,8 @@ input_gssapi_response(int type, u_int32_
{
Authctxt *authctxt = ctxt;
Gssctxt *gssctxt;
- int oidlen;
- char *oidv;
+ u_int oidlen;
+ u_char *oidv;
if (authctxt == NULL)
fatal("input_gssapi_response: no authentication context");
@@ -890,6 +947,48 @@ input_gssapi_error(int type, u_int32_t p
free(lang);
return 0;
}
+
+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);
+ }
+
+ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
+ "gssapi-keyex");
+
+ 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);
+ packet_put_cstring(authctxt->server_user);
+ 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
diff -pur old/sshd.c new/sshd.c
--- old/sshd.c
+++ new/sshd.c
@@ -1892,10 +1892,13 @@ main(int ac, char **av)
logit("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) {
logit("Disabling protocol version 2. Could not load host key");
options.protocol &= ~SSH_PROTO_2;
}
+#endif
if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
logit("sshd: no hostkeys available -- exiting.");
exit(1);
@@ -2656,6 +2659,48 @@ do_ssh2_kex(void)
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
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 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)
+ xasprintf(&newstr, "%s,%s", gss, orig);
+ else if (gss)
+ newstr = gss;
+ else if (orig)
+ 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;
+ else
+ fatal("No supported key exchange algorithms");
+ }
+#endif
+
/* start key exchange */
if ((r = kex_setup(active_state, myproposal)) != 0)
fatal("kex_setup: %s", ssh_err(r));
@@ -2673,6 +2718,13 @@ do_ssh2_kex(void)
# endif
#endif
kex->kex[KEX_C25519_SHA256] = kexc25519_server;
+#ifdef GSSAPI
+ if (options.gss_keyex) {
+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+ kex->kex[KEX_GSS_GRP14_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;
diff -pur old/sshd_config.5 new/sshd_config.5
@@ -632,6 +632,11 @@ The default is
Specifies whether user authentication based on GSSAPI is allowed.
The default on Solaris is
.Dq yes .
+.It Cm GSSAPIKeyExchange
+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
+doesn't rely on ssh keys to verify host identity.
+The default on Solaris is
+.Dq yes .
.It Cm GSSAPICleanupCredentials
Specifies whether to automatically destroy the user's credentials cache
on logout.
diff -pur old/sshkey.c new/sshkey.c
--- old/sshkey.c
+++ new/sshkey.c
@@ -115,6 +115,7 @@ static const struct keytype keytypes[] =
# endif /* OPENSSL_HAS_NISTP521 */
# endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
+ { "null", "null", KEY_NULL, 0, 0 },
{ NULL, NULL, -1, -1, 0, 0 }
};
diff -pur old/sshkey.h new/sshkey.h
--- old/sshkey.h
+++ new/sshkey.h
@@ -62,6 +62,7 @@ enum sshkey_types {
KEY_DSA_CERT,
KEY_ECDSA_CERT,
KEY_ED25519_CERT,
+ KEY_NULL,
KEY_UNSPEC
};