summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2019-10-09 23:06:20 +0100
commit9da806e67101afdc0d3a1d304659927acf18f5c5 (patch)
tree4cb56e13b3b3b14147366a04a7ff691f76908bf7
parent4213eec74e74de6310c27a40c3e9759a08a73996 (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2019-10-09 Patch-Name: gssapi.patch
-rw-r--r--Makefile.in3
-rw-r--r--auth-krb5.c17
-rw-r--r--auth.c96
-rw-r--r--auth2-gss.c56
-rw-r--r--auth2.c2
-rw-r--r--canohost.c93
-rw-r--r--canohost.h3
-rw-r--r--clientloop.c15
-rw-r--r--configure.ac24
-rw-r--r--gss-genr.c300
-rw-r--r--gss-serv-krb5.c85
-rw-r--r--gss-serv.c186
-rw-r--r--hmac.c1
-rw-r--r--kex.c66
-rw-r--r--kex.h29
-rw-r--r--kexdh.c10
-rw-r--r--kexgen.c2
-rw-r--r--kexgssc.c606
-rw-r--r--kexgsss.c474
-rw-r--r--mac.c1
-rw-r--r--monitor.c139
-rw-r--r--monitor.h2
-rw-r--r--monitor_wrap.c57
-rw-r--r--monitor_wrap.h4
-rw-r--r--readconf.c70
-rw-r--r--readconf.h6
-rw-r--r--servconf.c47
-rw-r--r--servconf.h3
-rw-r--r--session.c10
-rw-r--r--ssh-gss.h50
-rw-r--r--ssh.18
-rw-r--r--ssh.c4
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.557
-rw-r--r--sshconnect2.c140
-rw-r--r--sshd.c120
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.530
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
40 files changed, 2664 insertions, 160 deletions
diff --git a/Makefile.in b/Makefile.in
index adb1977e2..ab29e4f05 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
100 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ 100 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
101 kexgexc.o kexgexs.o \ 101 kexgexc.o kexgexs.o \
102 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ 102 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \
103 kexgssc.o \
103 platform-pledge.o platform-tracing.o platform-misc.o 104 platform-pledge.o platform-tracing.o platform-misc.o
104 105
105 106
@@ -114,7 +115,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
114 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 115 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
115 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 116 auth2-none.o auth2-passwd.o auth2-pubkey.o \
116 monitor.o monitor_wrap.o auth-krb5.o \ 117 monitor.o monitor_wrap.o auth-krb5.o \
117 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 118 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
118 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 119 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
119 sftp-server.o sftp-common.o sftp-realpath.o \ 120 sftp-server.o sftp-common.o sftp-realpath.o \
120 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ 121 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff --git a/auth-krb5.c b/auth-krb5.c
index 3096f1c8e..204752e1b 100644
--- a/auth-krb5.c
+++ b/auth-krb5.c
@@ -182,8 +182,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
182 182
183 len = strlen(authctxt->krb5_ticket_file) + 6; 183 len = strlen(authctxt->krb5_ticket_file) + 6;
184 authctxt->krb5_ccname = xmalloc(len); 184 authctxt->krb5_ccname = xmalloc(len);
185#ifdef USE_CCAPI
186 snprintf(authctxt->krb5_ccname, len, "API:%s",
187 authctxt->krb5_ticket_file);
188#else
185 snprintf(authctxt->krb5_ccname, len, "FILE:%s", 189 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
186 authctxt->krb5_ticket_file); 190 authctxt->krb5_ticket_file);
191#endif
187 192
188#ifdef USE_PAM 193#ifdef USE_PAM
189 if (options.use_pam) 194 if (options.use_pam)
@@ -240,15 +245,22 @@ krb5_cleanup_proc(Authctxt *authctxt)
240#ifndef HEIMDAL 245#ifndef HEIMDAL
241krb5_error_code 246krb5_error_code
242ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { 247ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
243 int tmpfd, ret, oerrno; 248 int ret, oerrno;
244 char ccname[40]; 249 char ccname[40];
245 mode_t old_umask; 250 mode_t old_umask;
251#ifdef USE_CCAPI
252 char cctemplate[] = "API:krb5cc_%d";
253#else
254 char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
255 int tmpfd;
256#endif
246 257
247 ret = snprintf(ccname, sizeof(ccname), 258 ret = snprintf(ccname, sizeof(ccname),
248 "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); 259 cctemplate, geteuid());
249 if (ret < 0 || (size_t)ret >= sizeof(ccname)) 260 if (ret < 0 || (size_t)ret >= sizeof(ccname))
250 return ENOMEM; 261 return ENOMEM;
251 262
263#ifndef USE_CCAPI
252 old_umask = umask(0177); 264 old_umask = umask(0177);
253 tmpfd = mkstemp(ccname + strlen("FILE:")); 265 tmpfd = mkstemp(ccname + strlen("FILE:"));
254 oerrno = errno; 266 oerrno = errno;
@@ -265,6 +277,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
265 return oerrno; 277 return oerrno;
266 } 278 }
267 close(tmpfd); 279 close(tmpfd);
280#endif
268 281
269 return (krb5_cc_resolve(ctx, ccname, ccache)); 282 return (krb5_cc_resolve(ctx, ccname, ccache));
270} 283}
diff --git a/auth.c b/auth.c
index ca450f4e4..47c27773c 100644
--- a/auth.c
+++ b/auth.c
@@ -399,7 +399,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
399 case PERMIT_NO_PASSWD: 399 case PERMIT_NO_PASSWD:
400 if (strcmp(method, "publickey") == 0 || 400 if (strcmp(method, "publickey") == 0 ||
401 strcmp(method, "hostbased") == 0 || 401 strcmp(method, "hostbased") == 0 ||
402 strcmp(method, "gssapi-with-mic") == 0) 402 strcmp(method, "gssapi-with-mic") == 0 ||
403 strcmp(method, "gssapi-keyex") == 0)
403 return 1; 404 return 1;
404 break; 405 break;
405 case PERMIT_FORCED_ONLY: 406 case PERMIT_FORCED_ONLY:
@@ -724,99 +725,6 @@ fakepw(void)
724} 725}
725 726
726/* 727/*
727 * Returns the remote DNS hostname as a string. The returned string must not
728 * be freed. NB. this will usually trigger a DNS query the first time it is
729 * called.
730 * This function does additional checks on the hostname to mitigate some
731 * attacks on legacy rhosts-style authentication.
732 * XXX is RhostsRSAAuthentication vulnerable to these?
733 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
734 */
735
736static char *
737remote_hostname(struct ssh *ssh)
738{
739 struct sockaddr_storage from;
740 socklen_t fromlen;
741 struct addrinfo hints, *ai, *aitop;
742 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
743 const char *ntop = ssh_remote_ipaddr(ssh);
744
745 /* Get IP address of client. */
746 fromlen = sizeof(from);
747 memset(&from, 0, sizeof(from));
748 if (getpeername(ssh_packet_get_connection_in(ssh),
749 (struct sockaddr *)&from, &fromlen) == -1) {
750 debug("getpeername failed: %.100s", strerror(errno));
751 return strdup(ntop);
752 }
753
754 ipv64_normalise_mapped(&from, &fromlen);
755 if (from.ss_family == AF_INET6)
756 fromlen = sizeof(struct sockaddr_in6);
757
758 debug3("Trying to reverse map address %.100s.", ntop);
759 /* Map the IP address to a host name. */
760 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
761 NULL, 0, NI_NAMEREQD) != 0) {
762 /* Host name not found. Use ip address. */
763 return strdup(ntop);
764 }
765
766 /*
767 * if reverse lookup result looks like a numeric hostname,
768 * someone is trying to trick us by PTR record like following:
769 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
770 */
771 memset(&hints, 0, sizeof(hints));
772 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
773 hints.ai_flags = AI_NUMERICHOST;
774 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
775 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
776 name, ntop);
777 freeaddrinfo(ai);
778 return strdup(ntop);
779 }
780
781 /* Names are stored in lowercase. */
782 lowercase(name);
783
784 /*
785 * Map it back to an IP address and check that the given
786 * address actually is an address of this host. This is
787 * necessary because anyone with access to a name server can
788 * define arbitrary names for an IP address. Mapping from
789 * name to IP address can be trusted better (but can still be
790 * fooled if the intruder has access to the name server of
791 * the domain).
792 */
793 memset(&hints, 0, sizeof(hints));
794 hints.ai_family = from.ss_family;
795 hints.ai_socktype = SOCK_STREAM;
796 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
797 logit("reverse mapping checking getaddrinfo for %.700s "
798 "[%s] failed.", name, ntop);
799 return strdup(ntop);
800 }
801 /* Look for the address from the list of addresses. */
802 for (ai = aitop; ai; ai = ai->ai_next) {
803 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
804 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
805 (strcmp(ntop, ntop2) == 0))
806 break;
807 }
808 freeaddrinfo(aitop);
809 /* If we reached the end of the list, the address was not there. */
810 if (ai == NULL) {
811 /* Address not found for the host name. */
812 logit("Address %.100s maps to %.600s, but this does not "
813 "map back to the address.", ntop, name);
814 return strdup(ntop);
815 }
816 return strdup(name);
817}
818
819/*
820 * Return the canonical name of the host in the other side of the current 728 * Return the canonical name of the host in the other side of the current
821 * connection. The host name is cached, so it is efficient to call this 729 * connection. The host name is cached, so it is efficient to call this
822 * several times. 730 * several times.
diff --git a/auth2-gss.c b/auth2-gss.c
index 9351e0428..d6446c0cf 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -55,6 +55,48 @@ static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *
55static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 55static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
56 56
57/* 57/*
58 * The 'gssapi_keyex' userauth mechanism.
59 */
60static int
61userauth_gsskeyex(struct ssh *ssh)
62{
63 Authctxt *authctxt = ssh->authctxt;
64 int r, authenticated = 0;
65 struct sshbuf *b = NULL;
66 gss_buffer_desc mic, gssbuf;
67 u_char *p;
68 size_t len;
69
70 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
71 (r = sshpkt_get_end(ssh)) != 0)
72 fatal("%s: %s", __func__, ssh_err(r));
73
74 if ((b = sshbuf_new()) == NULL)
75 fatal("%s: sshbuf_new failed", __func__);
76
77 mic.value = p;
78 mic.length = len;
79
80 ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
81 "gssapi-keyex");
82
83 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
84 fatal("%s: sshbuf_mutable_ptr failed", __func__);
85 gssbuf.length = sshbuf_len(b);
86
87 /* gss_kex_context is NULL with privsep, so we can't check it here */
88 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
89 &gssbuf, &mic))))
90 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
91 authctxt->pw, 1));
92
93 sshbuf_free(b);
94 free(mic.value);
95
96 return (authenticated);
97}
98
99/*
58 * We only support those mechanisms that we know about (ie ones that we know 100 * We only support those mechanisms that we know about (ie ones that we know
59 * how to check local user kuserok and the like) 101 * how to check local user kuserok and the like)
60 */ 102 */
@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
260 if ((r = sshpkt_get_end(ssh)) != 0) 302 if ((r = sshpkt_get_end(ssh)) != 0)
261 fatal("%s: %s", __func__, ssh_err(r)); 303 fatal("%s: %s", __func__, ssh_err(r));
262 304
263 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 305 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
306 authctxt->pw, 1));
264 307
265 if ((!use_privsep || mm_is_monitor()) && 308 if ((!use_privsep || mm_is_monitor()) &&
266 (displayname = ssh_gssapi_displayname()) != NULL) 309 (displayname = ssh_gssapi_displayname()) != NULL)
@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
306 gssbuf.length = sshbuf_len(b); 349 gssbuf.length = sshbuf_len(b);
307 350
308 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 351 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
309 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 352 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
353 authctxt->pw, 0));
310 else 354 else
311 logit("GSSAPI MIC check failed"); 355 logit("GSSAPI MIC check failed");
312 356
@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
326 return 0; 370 return 0;
327} 371}
328 372
373Authmethod method_gsskeyex = {
374 "gssapi-keyex",
375 userauth_gsskeyex,
376 &options.gss_authentication
377};
378
329Authmethod method_gssapi = { 379Authmethod method_gssapi = {
330 "gssapi-with-mic", 380 "gssapi-with-mic",
331 userauth_gssapi, 381 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 0e7762242..1c217268c 100644
--- a/auth2.c
+++ b/auth2.c
@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
73extern Authmethod method_kbdint; 73extern Authmethod method_kbdint;
74extern Authmethod method_hostbased; 74extern Authmethod method_hostbased;
75#ifdef GSSAPI 75#ifdef GSSAPI
76extern Authmethod method_gsskeyex;
76extern Authmethod method_gssapi; 77extern Authmethod method_gssapi;
77#endif 78#endif
78 79
@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
80 &method_none, 81 &method_none,
81 &method_pubkey, 82 &method_pubkey,
82#ifdef GSSAPI 83#ifdef GSSAPI
84 &method_gsskeyex,
83 &method_gssapi, 85 &method_gssapi,
84#endif 86#endif
85 &method_passwd, 87 &method_passwd,
diff --git a/canohost.c b/canohost.c
index abea9c6e6..9a00fc2cf 100644
--- a/canohost.c
+++ b/canohost.c
@@ -35,6 +35,99 @@
35#include "canohost.h" 35#include "canohost.h"
36#include "misc.h" 36#include "misc.h"
37 37
38/*
39 * Returns the remote DNS hostname as a string. The returned string must not
40 * be freed. NB. this will usually trigger a DNS query the first time it is
41 * called.
42 * This function does additional checks on the hostname to mitigate some
43 * attacks on legacy rhosts-style authentication.
44 * XXX is RhostsRSAAuthentication vulnerable to these?
45 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
46 */
47
48char *
49remote_hostname(struct ssh *ssh)
50{
51 struct sockaddr_storage from;
52 socklen_t fromlen;
53 struct addrinfo hints, *ai, *aitop;
54 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
55 const char *ntop = ssh_remote_ipaddr(ssh);
56
57 /* Get IP address of client. */
58 fromlen = sizeof(from);
59 memset(&from, 0, sizeof(from));
60 if (getpeername(ssh_packet_get_connection_in(ssh),
61 (struct sockaddr *)&from, &fromlen) == -1) {
62 debug("getpeername failed: %.100s", strerror(errno));
63 return strdup(ntop);
64 }
65
66 ipv64_normalise_mapped(&from, &fromlen);
67 if (from.ss_family == AF_INET6)
68 fromlen = sizeof(struct sockaddr_in6);
69
70 debug3("Trying to reverse map address %.100s.", ntop);
71 /* Map the IP address to a host name. */
72 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
73 NULL, 0, NI_NAMEREQD) != 0) {
74 /* Host name not found. Use ip address. */
75 return strdup(ntop);
76 }
77
78 /*
79 * if reverse lookup result looks like a numeric hostname,
80 * someone is trying to trick us by PTR record like following:
81 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
82 */
83 memset(&hints, 0, sizeof(hints));
84 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
85 hints.ai_flags = AI_NUMERICHOST;
86 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
87 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
88 name, ntop);
89 freeaddrinfo(ai);
90 return strdup(ntop);
91 }
92
93 /* Names are stored in lowercase. */
94 lowercase(name);
95
96 /*
97 * Map it back to an IP address and check that the given
98 * address actually is an address of this host. This is
99 * necessary because anyone with access to a name server can
100 * define arbitrary names for an IP address. Mapping from
101 * name to IP address can be trusted better (but can still be
102 * fooled if the intruder has access to the name server of
103 * the domain).
104 */
105 memset(&hints, 0, sizeof(hints));
106 hints.ai_family = from.ss_family;
107 hints.ai_socktype = SOCK_STREAM;
108 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
109 logit("reverse mapping checking getaddrinfo for %.700s "
110 "[%s] failed.", name, ntop);
111 return strdup(ntop);
112 }
113 /* Look for the address from the list of addresses. */
114 for (ai = aitop; ai; ai = ai->ai_next) {
115 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
116 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
117 (strcmp(ntop, ntop2) == 0))
118 break;
119 }
120 freeaddrinfo(aitop);
121 /* If we reached the end of the list, the address was not there. */
122 if (ai == NULL) {
123 /* Address not found for the host name. */
124 logit("Address %.100s maps to %.600s, but this does not "
125 "map back to the address.", ntop, name);
126 return strdup(ntop);
127 }
128 return strdup(name);
129}
130
38void 131void
39ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) 132ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
40{ 133{
diff --git a/canohost.h b/canohost.h
index 26d62855a..0cadc9f18 100644
--- a/canohost.h
+++ b/canohost.h
@@ -15,6 +15,9 @@
15#ifndef _CANOHOST_H 15#ifndef _CANOHOST_H
16#define _CANOHOST_H 16#define _CANOHOST_H
17 17
18struct ssh;
19
20char *remote_hostname(struct ssh *);
18char *get_peer_ipaddr(int); 21char *get_peer_ipaddr(int);
19int get_peer_port(int); 22int get_peer_port(int);
20char *get_local_ipaddr(int); 23char *get_local_ipaddr(int);
diff --git a/clientloop.c b/clientloop.c
index b5a1f7038..9def2a1a9 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -112,6 +112,10 @@
112#include "ssherr.h" 112#include "ssherr.h"
113#include "hostfile.h" 113#include "hostfile.h"
114 114
115#ifdef GSSAPI
116#include "ssh-gss.h"
117#endif
118
115/* import options */ 119/* import options */
116extern Options options; 120extern Options options;
117 121
@@ -1373,9 +1377,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1373 break; 1377 break;
1374 1378
1375 /* Do channel operations unless rekeying in progress. */ 1379 /* Do channel operations unless rekeying in progress. */
1376 if (!ssh_packet_is_rekeying(ssh)) 1380 if (!ssh_packet_is_rekeying(ssh)) {
1377 channel_after_select(ssh, readset, writeset); 1381 channel_after_select(ssh, readset, writeset);
1378 1382
1383#ifdef GSSAPI
1384 if (options.gss_renewal_rekey &&
1385 ssh_gssapi_credentials_updated(NULL)) {
1386 debug("credentials updated - forcing rekey");
1387 need_rekeying = 1;
1388 }
1389#endif
1390 }
1391
1379 /* Buffer input from the connection. */ 1392 /* Buffer input from the connection. */
1380 client_process_net_input(ssh, readset); 1393 client_process_net_input(ssh, readset);
1381 1394
diff --git a/configure.ac b/configure.ac
index 3e93c0276..1c2512314 100644
--- a/configure.ac
+++ b/configure.ac
@@ -666,6 +666,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
666 [Use tunnel device compatibility to OpenBSD]) 666 [Use tunnel device compatibility to OpenBSD])
667 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 667 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
668 [Prepend the address family to IP tunnel traffic]) 668 [Prepend the address family to IP tunnel traffic])
669 AC_MSG_CHECKING([if we have the Security Authorization Session API])
670 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
671 [SessionCreate(0, 0);],
672 [ac_cv_use_security_session_api="yes"
673 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
674 [platform has the Security Authorization Session API])
675 LIBS="$LIBS -framework Security"
676 AC_MSG_RESULT([yes])],
677 [ac_cv_use_security_session_api="no"
678 AC_MSG_RESULT([no])])
679 AC_MSG_CHECKING([if we have an in-memory credentials cache])
680 AC_TRY_COMPILE(
681 [#include <Kerberos/Kerberos.h>],
682 [cc_context_t c;
683 (void) cc_initialize (&c, 0, NULL, NULL);],
684 [AC_DEFINE([USE_CCAPI], [1],
685 [platform uses an in-memory credentials cache])
686 LIBS="$LIBS -framework Security"
687 AC_MSG_RESULT([yes])
688 if test "x$ac_cv_use_security_session_api" = "xno"; then
689 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
690 fi],
691 [AC_MSG_RESULT([no])]
692 )
669 m4_pattern_allow([AU_IPv]) 693 m4_pattern_allow([AU_IPv])
670 AC_CHECK_DECL([AU_IPv4], [], 694 AC_CHECK_DECL([AU_IPv4], [],
671 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 695 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index d56257b4a..763a63ffa 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */ 1/* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -41,12 +41,36 @@
41#include "sshbuf.h" 41#include "sshbuf.h"
42#include "log.h" 42#include "log.h"
43#include "ssh2.h" 43#include "ssh2.h"
44#include "cipher.h"
45#include "sshkey.h"
46#include "kex.h"
47#include "digest.h"
48#include "packet.h"
44 49
45#include "ssh-gss.h" 50#include "ssh-gss.h"
46 51
47extern u_char *session_id2; 52extern u_char *session_id2;
48extern u_int session_id2_len; 53extern u_int session_id2_len;
49 54
55typedef struct {
56 char *encoded;
57 gss_OID oid;
58} ssh_gss_kex_mapping;
59
60/*
61 * XXX - It would be nice to find a more elegant way of handling the
62 * XXX passing of the key exchange context to the userauth routines
63 */
64
65Gssctxt *gss_kex_context = NULL;
66
67static ssh_gss_kex_mapping *gss_enc2oid = NULL;
68
69int
70ssh_gssapi_oid_table_ok(void) {
71 return (gss_enc2oid != NULL);
72}
73
50/* sshbuf_get for gss_buffer_desc */ 74/* sshbuf_get for gss_buffer_desc */
51int 75int
52ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) 76ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
@@ -62,6 +86,162 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
62 return 0; 86 return 0;
63} 87}
64 88
89/* sshpkt_get of gss_buffer_desc */
90int
91ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
92{
93 int r;
94 u_char *p;
95 size_t len;
96
97 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
98 return r;
99 g->value = p;
100 g->length = len;
101 return 0;
102}
103
104/*
105 * Return a list of the gss-group1-sha1 mechanisms supported by this program
106 *
107 * We test mechanisms to ensure that we can use them, to avoid starting
108 * a key exchange with a bad mechanism
109 */
110
111char *
112ssh_gssapi_client_mechanisms(const char *host, const char *client,
113 const char *kex) {
114 gss_OID_set gss_supported = NULL;
115 OM_uint32 min_status;
116
117 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
118 return NULL;
119
120 return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
121 host, client, kex);
122}
123
124char *
125ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
126 const char *host, const char *client, const char *kex) {
127 struct sshbuf *buf = NULL;
128 size_t i;
129 int r = SSH_ERR_ALLOC_FAIL;
130 int oidpos, enclen;
131 char *mechs, *encoded;
132 u_char digest[SSH_DIGEST_MAX_LENGTH];
133 char deroid[2];
134 struct ssh_digest_ctx *md = NULL;
135 char *s, *cp, *p;
136
137 if (gss_enc2oid != NULL) {
138 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
139 free(gss_enc2oid[i].encoded);
140 free(gss_enc2oid);
141 }
142
143 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
144 (gss_supported->count + 1));
145
146 if ((buf = sshbuf_new()) == NULL)
147 fatal("%s: sshbuf_new failed", __func__);
148
149 oidpos = 0;
150 s = cp = xstrdup(kex);
151 for (i = 0; i < gss_supported->count; i++) {
152 if (gss_supported->elements[i].length < 128 &&
153 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
154
155 deroid[0] = SSH_GSS_OIDTYPE;
156 deroid[1] = gss_supported->elements[i].length;
157
158 if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
159 (r = ssh_digest_update(md, deroid, 2)) != 0 ||
160 (r = ssh_digest_update(md,
161 gss_supported->elements[i].elements,
162 gss_supported->elements[i].length)) != 0 ||
163 (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
164 fatal("%s: digest failed: %s", __func__,
165 ssh_err(r));
166 ssh_digest_free(md);
167 md = NULL;
168
169 encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
170 * 2);
171 enclen = __b64_ntop(digest,
172 ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
173 ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
174
175 cp = strncpy(s, kex, strlen(kex));
176 for ((p = strsep(&cp, ",")); p && *p != '\0';
177 (p = strsep(&cp, ","))) {
178 if (sshbuf_len(buf) != 0 &&
179 (r = sshbuf_put_u8(buf, ',')) != 0)
180 fatal("%s: sshbuf_put_u8 error: %s",
181 __func__, ssh_err(r));
182 if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
183 (r = sshbuf_put(buf, encoded, enclen)) != 0)
184 fatal("%s: sshbuf_put error: %s",
185 __func__, ssh_err(r));
186 }
187
188 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
189 gss_enc2oid[oidpos].encoded = encoded;
190 oidpos++;
191 }
192 }
193 free(s);
194 gss_enc2oid[oidpos].oid = NULL;
195 gss_enc2oid[oidpos].encoded = NULL;
196
197 if ((mechs = sshbuf_dup_string(buf)) == NULL)
198 fatal("%s: sshbuf_dup_string failed", __func__);
199
200 sshbuf_free(buf);
201
202 if (strlen(mechs) == 0) {
203 free(mechs);
204 mechs = NULL;
205 }
206
207 return (mechs);
208}
209
210gss_OID
211ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
212 int i = 0;
213
214#define SKIP_KEX_NAME(type) \
215 case type: \
216 if (strlen(name) < sizeof(type##_ID)) \
217 return GSS_C_NO_OID; \
218 name += sizeof(type##_ID) - 1; \
219 break;
220
221 switch (kex_type) {
222 SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1)
223 SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1)
224 SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256)
225 SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512)
226 SKIP_KEX_NAME(KEX_GSS_GEX_SHA1)
227 SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256)
228 SKIP_KEX_NAME(KEX_GSS_C25519_SHA256)
229 default:
230 return GSS_C_NO_OID;
231 }
232
233#undef SKIP_KEX_NAME
234
235 while (gss_enc2oid[i].encoded != NULL &&
236 strcmp(name, gss_enc2oid[i].encoded) != 0)
237 i++;
238
239 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
240 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
241
242 return gss_enc2oid[i].oid;
243}
244
65/* Check that the OID in a data stream matches that in the context */ 245/* Check that the OID in a data stream matches that in the context */
66int 246int
67ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 247ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -218,7 +398,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
218 } 398 }
219 399
220 ctx->major = gss_init_sec_context(&ctx->minor, 400 ctx->major = gss_init_sec_context(&ctx->minor,
221 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 401 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
222 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 402 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
223 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 403 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
224 404
@@ -248,8 +428,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
248} 428}
249 429
250OM_uint32 430OM_uint32
431ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
432{
433 gss_buffer_desc gssbuf;
434 gss_name_t gssname;
435 OM_uint32 status;
436 gss_OID_set oidset;
437
438 gssbuf.value = (void *) name;
439 gssbuf.length = strlen(gssbuf.value);
440
441 gss_create_empty_oid_set(&status, &oidset);
442 gss_add_oid_set_member(&status, ctx->oid, &oidset);
443
444 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
445 GSS_C_NT_USER_NAME, &gssname);
446
447 if (!ctx->major)
448 ctx->major = gss_acquire_cred(&ctx->minor,
449 gssname, 0, oidset, GSS_C_INITIATE,
450 &ctx->client_creds, NULL, NULL);
451
452 gss_release_name(&status, &gssname);
453 gss_release_oid_set(&status, &oidset);
454
455 if (ctx->major)
456 ssh_gssapi_error(ctx);
457
458 return(ctx->major);
459}
460
461OM_uint32
251ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 462ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
252{ 463{
464 if (ctx == NULL)
465 return -1;
466
253 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 467 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
254 GSS_C_QOP_DEFAULT, buffer, hash))) 468 GSS_C_QOP_DEFAULT, buffer, hash)))
255 ssh_gssapi_error(ctx); 469 ssh_gssapi_error(ctx);
@@ -257,6 +471,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
257 return (ctx->major); 471 return (ctx->major);
258} 472}
259 473
474/* Priviledged when used by server */
475OM_uint32
476ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
477{
478 if (ctx == NULL)
479 return -1;
480
481 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
482 gssbuf, gssmic, NULL);
483
484 return (ctx->major);
485}
486
260void 487void
261ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, 488ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
262 const char *context) 489 const char *context)
@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
273} 500}
274 501
275int 502int
276ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 503ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
504 const char *client)
277{ 505{
278 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 506 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
279 OM_uint32 major, minor; 507 OM_uint32 major, minor;
280 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 508 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
509 Gssctxt *intctx = NULL;
510
511 if (ctx == NULL)
512 ctx = &intctx;
281 513
282 /* RFC 4462 says we MUST NOT do SPNEGO */ 514 /* RFC 4462 says we MUST NOT do SPNEGO */
283 if (oid->length == spnego_oid.length && 515 if (oid->length == spnego_oid.length &&
@@ -287,6 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
287 ssh_gssapi_build_ctx(ctx); 519 ssh_gssapi_build_ctx(ctx);
288 ssh_gssapi_set_oid(*ctx, oid); 520 ssh_gssapi_set_oid(*ctx, oid);
289 major = ssh_gssapi_import_name(*ctx, host); 521 major = ssh_gssapi_import_name(*ctx, host);
522
523 if (!GSS_ERROR(major) && client)
524 major = ssh_gssapi_client_identity(*ctx, client);
525
290 if (!GSS_ERROR(major)) { 526 if (!GSS_ERROR(major)) {
291 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 527 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
292 NULL); 528 NULL);
@@ -296,10 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
296 GSS_C_NO_BUFFER); 532 GSS_C_NO_BUFFER);
297 } 533 }
298 534
299 if (GSS_ERROR(major)) 535 if (GSS_ERROR(major) || intctx != NULL)
300 ssh_gssapi_delete_ctx(ctx); 536 ssh_gssapi_delete_ctx(ctx);
301 537
302 return (!GSS_ERROR(major)); 538 return (!GSS_ERROR(major));
303} 539}
304 540
541int
542ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
543 static gss_name_t saved_name = GSS_C_NO_NAME;
544 static OM_uint32 saved_lifetime = 0;
545 static gss_OID saved_mech = GSS_C_NO_OID;
546 static gss_name_t name;
547 static OM_uint32 last_call = 0;
548 OM_uint32 lifetime, now, major, minor;
549 int equal;
550
551 now = time(NULL);
552
553 if (ctxt) {
554 debug("Rekey has happened - updating saved versions");
555
556 if (saved_name != GSS_C_NO_NAME)
557 gss_release_name(&minor, &saved_name);
558
559 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
560 &saved_name, &saved_lifetime, NULL, NULL);
561
562 if (!GSS_ERROR(major)) {
563 saved_mech = ctxt->oid;
564 saved_lifetime+= now;
565 } else {
566 /* Handle the error */
567 }
568 return 0;
569 }
570
571 if (now - last_call < 10)
572 return 0;
573
574 last_call = now;
575
576 if (saved_mech == GSS_C_NO_OID)
577 return 0;
578
579 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
580 &name, &lifetime, NULL, NULL);
581 if (major == GSS_S_CREDENTIALS_EXPIRED)
582 return 0;
583 else if (GSS_ERROR(major))
584 return 0;
585
586 major = gss_compare_name(&minor, saved_name, name, &equal);
587 gss_release_name(&minor, &name);
588 if (GSS_ERROR(major))
589 return 0;
590
591 if (equal && (saved_lifetime < lifetime + now - 10))
592 return 1;
593
594 return 0;
595}
596
305#endif /* GSSAPI */ 597#endif /* GSSAPI */
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index a151bc1e4..ef9beb67c 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */ 1/* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -120,8 +120,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
120 krb5_error_code problem; 120 krb5_error_code problem;
121 krb5_principal princ; 121 krb5_principal princ;
122 OM_uint32 maj_status, min_status; 122 OM_uint32 maj_status, min_status;
123 int len;
124 const char *errmsg; 123 const char *errmsg;
124 const char *new_ccname;
125 125
126 if (client->creds == NULL) { 126 if (client->creds == NULL) {
127 debug("No credentials stored"); 127 debug("No credentials stored");
@@ -180,11 +180,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
180 return; 180 return;
181 } 181 }
182 182
183 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 183 new_ccname = krb5_cc_get_name(krb_context, ccache);
184
184 client->store.envvar = "KRB5CCNAME"; 185 client->store.envvar = "KRB5CCNAME";
185 len = strlen(client->store.filename) + 6; 186#ifdef USE_CCAPI
186 client->store.envval = xmalloc(len); 187 xasprintf(&client->store.envval, "API:%s", new_ccname);
187 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 188 client->store.filename = NULL;
189#else
190 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
191 client->store.filename = xstrdup(new_ccname);
192#endif
188 193
189#ifdef USE_PAM 194#ifdef USE_PAM
190 if (options.use_pam) 195 if (options.use_pam)
@@ -196,6 +201,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
196 return; 201 return;
197} 202}
198 203
204int
205ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
206 ssh_gssapi_client *client)
207{
208 krb5_ccache ccache = NULL;
209 krb5_principal principal = NULL;
210 char *name = NULL;
211 krb5_error_code problem;
212 OM_uint32 maj_status, min_status;
213
214 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
215 logit("krb5_cc_resolve(): %.100s",
216 krb5_get_err_text(krb_context, problem));
217 return 0;
218 }
219
220 /* Find out who the principal in this cache is */
221 if ((problem = krb5_cc_get_principal(krb_context, ccache,
222 &principal))) {
223 logit("krb5_cc_get_principal(): %.100s",
224 krb5_get_err_text(krb_context, problem));
225 krb5_cc_close(krb_context, ccache);
226 return 0;
227 }
228
229 if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
230 logit("krb5_unparse_name(): %.100s",
231 krb5_get_err_text(krb_context, problem));
232 krb5_free_principal(krb_context, principal);
233 krb5_cc_close(krb_context, ccache);
234 return 0;
235 }
236
237
238 if (strcmp(name,client->exportedname.value)!=0) {
239 debug("Name in local credentials cache differs. Not storing");
240 krb5_free_principal(krb_context, principal);
241 krb5_cc_close(krb_context, ccache);
242 krb5_free_unparsed_name(krb_context, name);
243 return 0;
244 }
245 krb5_free_unparsed_name(krb_context, name);
246
247 /* Name matches, so lets get on with it! */
248
249 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
250 logit("krb5_cc_initialize(): %.100s",
251 krb5_get_err_text(krb_context, problem));
252 krb5_free_principal(krb_context, principal);
253 krb5_cc_close(krb_context, ccache);
254 return 0;
255 }
256
257 krb5_free_principal(krb_context, principal);
258
259 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
260 ccache))) {
261 logit("gss_krb5_copy_ccache() failed. Sorry!");
262 krb5_cc_close(krb_context, ccache);
263 return 0;
264 }
265
266 return 1;
267}
268
199ssh_gssapi_mech gssapi_kerberos_mech = { 269ssh_gssapi_mech gssapi_kerberos_mech = {
200 "toWM5Slw5Ew8Mqkay+al2g==", 270 "toWM5Slw5Ew8Mqkay+al2g==",
201 "Kerberos", 271 "Kerberos",
@@ -203,7 +273,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
203 NULL, 273 NULL,
204 &ssh_gssapi_krb5_userok, 274 &ssh_gssapi_krb5_userok,
205 NULL, 275 NULL,
206 &ssh_gssapi_krb5_storecreds 276 &ssh_gssapi_krb5_storecreds,
277 &ssh_gssapi_krb5_updatecreds
207}; 278};
208 279
209#endif /* KRB5 */ 280#endif /* KRB5 */
diff --git a/gss-serv.c b/gss-serv.c
index ab3a15f0f..1d47870e7 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -44,17 +44,19 @@
44#include "session.h" 44#include "session.h"
45#include "misc.h" 45#include "misc.h"
46#include "servconf.h" 46#include "servconf.h"
47#include "uidswap.h"
47 48
48#include "ssh-gss.h" 49#include "ssh-gss.h"
50#include "monitor_wrap.h"
49 51
50extern ServerOptions options; 52extern ServerOptions options;
51 53
52static ssh_gssapi_client gssapi_client = 54static ssh_gssapi_client gssapi_client =
53 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 55 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
54 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 56 GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0};
55 57
56ssh_gssapi_mech gssapi_null_mech = 58ssh_gssapi_mech gssapi_null_mech =
57 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 59 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
58 60
59#ifdef KRB5 61#ifdef KRB5
60extern ssh_gssapi_mech gssapi_kerberos_mech; 62extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -141,6 +143,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
141} 143}
142 144
143/* Unprivileged */ 145/* Unprivileged */
146char *
147ssh_gssapi_server_mechanisms(void) {
148 if (supported_oids == NULL)
149 ssh_gssapi_prepare_supported_oids();
150 return (ssh_gssapi_kex_mechs(supported_oids,
151 &ssh_gssapi_server_check_mech, NULL, NULL,
152 options.gss_kex_algorithms));
153}
154
155/* Unprivileged */
156int
157ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
158 const char *dummy) {
159 Gssctxt *ctx = NULL;
160 int res;
161
162 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
163 ssh_gssapi_delete_ctx(&ctx);
164
165 return (res);
166}
167
168/* Unprivileged */
144void 169void
145ssh_gssapi_supported_oids(gss_OID_set *oidset) 170ssh_gssapi_supported_oids(gss_OID_set *oidset)
146{ 171{
@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
150 gss_OID_set supported; 175 gss_OID_set supported;
151 176
152 gss_create_empty_oid_set(&min_status, oidset); 177 gss_create_empty_oid_set(&min_status, oidset);
153 gss_indicate_mechs(&min_status, &supported); 178
179 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
180 return;
154 181
155 while (supported_mechs[i]->name != NULL) { 182 while (supported_mechs[i]->name != NULL) {
156 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 183 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -276,8 +303,48 @@ OM_uint32
276ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 303ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
277{ 304{
278 int i = 0; 305 int i = 0;
306 int equal = 0;
307 gss_name_t new_name = GSS_C_NO_NAME;
308 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
309
310 if (options.gss_store_rekey && client->used && ctx->client_creds) {
311 if (client->mech->oid.length != ctx->oid->length ||
312 (memcmp(client->mech->oid.elements,
313 ctx->oid->elements, ctx->oid->length) !=0)) {
314 debug("Rekeyed credentials have different mechanism");
315 return GSS_S_COMPLETE;
316 }
317
318 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
319 ctx->client_creds, ctx->oid, &new_name,
320 NULL, NULL, NULL))) {
321 ssh_gssapi_error(ctx);
322 return (ctx->major);
323 }
324
325 ctx->major = gss_compare_name(&ctx->minor, client->name,
326 new_name, &equal);
327
328 if (GSS_ERROR(ctx->major)) {
329 ssh_gssapi_error(ctx);
330 return (ctx->major);
331 }
332
333 if (!equal) {
334 debug("Rekeyed credentials have different name");
335 return GSS_S_COMPLETE;
336 }
279 337
280 gss_buffer_desc ename; 338 debug("Marking rekeyed credentials for export");
339
340 gss_release_name(&ctx->minor, &client->name);
341 gss_release_cred(&ctx->minor, &client->creds);
342 client->name = new_name;
343 client->creds = ctx->client_creds;
344 ctx->client_creds = GSS_C_NO_CREDENTIAL;
345 client->updated = 1;
346 return GSS_S_COMPLETE;
347 }
281 348
282 client->mech = NULL; 349 client->mech = NULL;
283 350
@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
292 if (client->mech == NULL) 359 if (client->mech == NULL)
293 return GSS_S_FAILURE; 360 return GSS_S_FAILURE;
294 361
362 if (ctx->client_creds &&
363 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
364 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
365 ssh_gssapi_error(ctx);
366 return (ctx->major);
367 }
368
295 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 369 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
296 &client->displayname, NULL))) { 370 &client->displayname, NULL))) {
297 ssh_gssapi_error(ctx); 371 ssh_gssapi_error(ctx);
@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
309 return (ctx->major); 383 return (ctx->major);
310 } 384 }
311 385
386 gss_release_buffer(&ctx->minor, &ename);
387
312 /* We can't copy this structure, so we just move the pointer to it */ 388 /* We can't copy this structure, so we just move the pointer to it */
313 client->creds = ctx->client_creds; 389 client->creds = ctx->client_creds;
314 ctx->client_creds = GSS_C_NO_CREDENTIAL; 390 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -356,19 +432,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
356 432
357/* Privileged */ 433/* Privileged */
358int 434int
359ssh_gssapi_userok(char *user) 435ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
360{ 436{
361 OM_uint32 lmin; 437 OM_uint32 lmin;
362 438
439 (void) kex; /* used in privilege separation */
440
363 if (gssapi_client.exportedname.length == 0 || 441 if (gssapi_client.exportedname.length == 0 ||
364 gssapi_client.exportedname.value == NULL) { 442 gssapi_client.exportedname.value == NULL) {
365 debug("No suitable client data"); 443 debug("No suitable client data");
366 return 0; 444 return 0;
367 } 445 }
368 if (gssapi_client.mech && gssapi_client.mech->userok) 446 if (gssapi_client.mech && gssapi_client.mech->userok)
369 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 447 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
448 gssapi_client.used = 1;
449 gssapi_client.store.owner = pw;
370 return 1; 450 return 1;
371 else { 451 } else {
372 /* Destroy delegated credentials if userok fails */ 452 /* Destroy delegated credentials if userok fails */
373 gss_release_buffer(&lmin, &gssapi_client.displayname); 453 gss_release_buffer(&lmin, &gssapi_client.displayname);
374 gss_release_buffer(&lmin, &gssapi_client.exportedname); 454 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -382,14 +462,90 @@ ssh_gssapi_userok(char *user)
382 return (0); 462 return (0);
383} 463}
384 464
385/* Privileged */ 465/* These bits are only used for rekeying. The unpriviledged child is running
386OM_uint32 466 * as the user, the monitor is root.
387ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 467 *
468 * In the child, we want to :
469 * *) Ask the monitor to store our credentials into the store we specify
470 * *) If it succeeds, maybe do a PAM update
471 */
472
473/* Stuff for PAM */
474
475#ifdef USE_PAM
476static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
477 struct pam_response **resp, void *data)
388{ 478{
389 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 479 return (PAM_CONV_ERR);
390 gssbuf, gssmic, NULL); 480}
481#endif
391 482
392 return (ctx->major); 483void
484ssh_gssapi_rekey_creds(void) {
485 int ok;
486#ifdef USE_PAM
487 int ret;
488 pam_handle_t *pamh = NULL;
489 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
490 char *envstr;
491#endif
492
493 if (gssapi_client.store.filename == NULL &&
494 gssapi_client.store.envval == NULL &&
495 gssapi_client.store.envvar == NULL)
496 return;
497
498 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
499
500 if (!ok)
501 return;
502
503 debug("Rekeyed credentials stored successfully");
504
505 /* Actually managing to play with the ssh pam stack from here will
506 * be next to impossible. In any case, we may want different options
507 * for rekeying. So, use our own :)
508 */
509#ifdef USE_PAM
510 if (!use_privsep) {
511 debug("Not even going to try and do PAM with privsep disabled");
512 return;
513 }
514
515 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
516 &pamconv, &pamh);
517 if (ret)
518 return;
519
520 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
521 gssapi_client.store.envval);
522
523 ret = pam_putenv(pamh, envstr);
524 if (!ret)
525 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
526 pam_end(pamh, PAM_SUCCESS);
527#endif
528}
529
530int
531ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
532 int ok = 0;
533
534 /* Check we've got credentials to store */
535 if (!gssapi_client.updated)
536 return 0;
537
538 gssapi_client.updated = 0;
539
540 temporarily_use_uid(gssapi_client.store.owner);
541 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
542 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
543 else
544 debug("No update function for this mechanism");
545
546 restore_uid();
547
548 return ok;
393} 549}
394 550
395/* Privileged */ 551/* Privileged */
diff --git a/hmac.c b/hmac.c
index 32688876d..a79e8569c 100644
--- a/hmac.c
+++ b/hmac.c
@@ -21,6 +21,7 @@
21 21
22#include <stdlib.h> 22#include <stdlib.h>
23#include <string.h> 23#include <string.h>
24#include <stdlib.h>
24 25
25#include "sshbuf.h" 26#include "sshbuf.h"
26#include "digest.h" 27#include "digest.h"
diff --git a/kex.c b/kex.c
index 49d701568..e09355dbd 100644
--- a/kex.c
+++ b/kex.c
@@ -55,11 +55,16 @@
55#include "misc.h" 55#include "misc.h"
56#include "dispatch.h" 56#include "dispatch.h"
57#include "monitor.h" 57#include "monitor.h"
58#include "xmalloc.h"
58 59
59#include "ssherr.h" 60#include "ssherr.h"
60#include "sshbuf.h" 61#include "sshbuf.h"
61#include "digest.h" 62#include "digest.h"
62 63
64#ifdef GSSAPI
65#include "ssh-gss.h"
66#endif
67
63/* prototype */ 68/* prototype */
64static int kex_choose_conf(struct ssh *); 69static int kex_choose_conf(struct ssh *);
65static int kex_input_newkeys(int, u_int32_t, struct ssh *); 70static int kex_input_newkeys(int, u_int32_t, struct ssh *);
@@ -113,15 +118,28 @@ static const struct kexalg kexalgs[] = {
113#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 118#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
114 { NULL, 0, -1, -1}, 119 { NULL, 0, -1, -1},
115}; 120};
121static const struct kexalg gss_kexalgs[] = {
122#ifdef GSSAPI
123 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
124 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
125 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
126 { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
127 { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
128 { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
129 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
130 { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
131#endif
132 { NULL, 0, -1, -1 },
133};
116 134
117char * 135static char *
118kex_alg_list(char sep) 136kex_alg_list_internal(char sep, const struct kexalg *algs)
119{ 137{
120 char *ret = NULL, *tmp; 138 char *ret = NULL, *tmp;
121 size_t nlen, rlen = 0; 139 size_t nlen, rlen = 0;
122 const struct kexalg *k; 140 const struct kexalg *k;
123 141
124 for (k = kexalgs; k->name != NULL; k++) { 142 for (k = algs; k->name != NULL; k++) {
125 if (ret != NULL) 143 if (ret != NULL)
126 ret[rlen++] = sep; 144 ret[rlen++] = sep;
127 nlen = strlen(k->name); 145 nlen = strlen(k->name);
@@ -136,6 +154,18 @@ kex_alg_list(char sep)
136 return ret; 154 return ret;
137} 155}
138 156
157char *
158kex_alg_list(char sep)
159{
160 return kex_alg_list_internal(sep, kexalgs);
161}
162
163char *
164kex_gss_alg_list(char sep)
165{
166 return kex_alg_list_internal(sep, gss_kexalgs);
167}
168
139static const struct kexalg * 169static const struct kexalg *
140kex_alg_by_name(const char *name) 170kex_alg_by_name(const char *name)
141{ 171{
@@ -145,6 +175,10 @@ kex_alg_by_name(const char *name)
145 if (strcmp(k->name, name) == 0) 175 if (strcmp(k->name, name) == 0)
146 return k; 176 return k;
147 } 177 }
178 for (k = gss_kexalgs; k->name != NULL; k++) {
179 if (strncmp(k->name, name, strlen(k->name)) == 0)
180 return k;
181 }
148 return NULL; 182 return NULL;
149} 183}
150 184
@@ -313,6 +347,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
313 return r; 347 return r;
314} 348}
315 349
350/* Validate GSS KEX method name list */
351int
352kex_gss_names_valid(const char *names)
353{
354 char *s, *cp, *p;
355
356 if (names == NULL || *names == '\0')
357 return 0;
358 s = cp = xstrdup(names);
359 for ((p = strsep(&cp, ",")); p && *p != '\0';
360 (p = strsep(&cp, ","))) {
361 if (strncmp(p, "gss-", 4) != 0
362 || kex_alg_by_name(p) == NULL) {
363 error("Unsupported KEX algorithm \"%.100s\"", p);
364 free(s);
365 return 0;
366 }
367 }
368 debug3("gss kex names ok: [%s]", names);
369 free(s);
370 return 1;
371}
372
316/* put algorithm proposal into buffer */ 373/* put algorithm proposal into buffer */
317int 374int
318kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 375kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
@@ -696,6 +753,9 @@ kex_free(struct kex *kex)
696 sshbuf_free(kex->server_version); 753 sshbuf_free(kex->server_version);
697 sshbuf_free(kex->client_pub); 754 sshbuf_free(kex->client_pub);
698 free(kex->session_id); 755 free(kex->session_id);
756#ifdef GSSAPI
757 free(kex->gss_host);
758#endif /* GSSAPI */
699 free(kex->failed_choice); 759 free(kex->failed_choice);
700 free(kex->hostkey_alg); 760 free(kex->hostkey_alg);
701 free(kex->name); 761 free(kex->name);
diff --git a/kex.h b/kex.h
index a5ae6ac05..fe7141414 100644
--- a/kex.h
+++ b/kex.h
@@ -102,6 +102,15 @@ enum kex_exchange {
102 KEX_ECDH_SHA2, 102 KEX_ECDH_SHA2,
103 KEX_C25519_SHA256, 103 KEX_C25519_SHA256,
104 KEX_KEM_SNTRUP4591761X25519_SHA512, 104 KEX_KEM_SNTRUP4591761X25519_SHA512,
105#ifdef GSSAPI
106 KEX_GSS_GRP1_SHA1,
107 KEX_GSS_GRP14_SHA1,
108 KEX_GSS_GRP14_SHA256,
109 KEX_GSS_GRP16_SHA512,
110 KEX_GSS_GEX_SHA1,
111 KEX_GSS_NISTP256_SHA256,
112 KEX_GSS_C25519_SHA256,
113#endif
105 KEX_MAX 114 KEX_MAX
106}; 115};
107 116
@@ -153,6 +162,12 @@ struct kex {
153 u_int flags; 162 u_int flags;
154 int hash_alg; 163 int hash_alg;
155 int ec_nid; 164 int ec_nid;
165#ifdef GSSAPI
166 int gss_deleg_creds;
167 int gss_trust_dns;
168 char *gss_host;
169 char *gss_client;
170#endif
156 char *failed_choice; 171 char *failed_choice;
157 int (*verify_host_key)(struct sshkey *, struct ssh *); 172 int (*verify_host_key)(struct sshkey *, struct ssh *);
158 struct sshkey *(*load_host_public_key)(int, int, struct ssh *); 173 struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
@@ -174,8 +189,10 @@ struct kex {
174 189
175int kex_names_valid(const char *); 190int kex_names_valid(const char *);
176char *kex_alg_list(char); 191char *kex_alg_list(char);
192char *kex_gss_alg_list(char);
177char *kex_names_cat(const char *, const char *); 193char *kex_names_cat(const char *, const char *);
178int kex_assemble_names(char **, const char *, const char *); 194int kex_assemble_names(char **, const char *, const char *);
195int kex_gss_names_valid(const char *);
179 196
180int kex_exchange_identification(struct ssh *, int, const char *); 197int kex_exchange_identification(struct ssh *, int, const char *);
181 198
@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *);
202int kexgex_server(struct ssh *); 219int kexgex_server(struct ssh *);
203int kex_gen_client(struct ssh *); 220int kex_gen_client(struct ssh *);
204int kex_gen_server(struct ssh *); 221int kex_gen_server(struct ssh *);
222#if defined(GSSAPI) && defined(WITH_OPENSSL)
223int kexgssgex_client(struct ssh *);
224int kexgssgex_server(struct ssh *);
225int kexgss_client(struct ssh *);
226int kexgss_server(struct ssh *);
227#endif
205 228
206int kex_dh_keypair(struct kex *); 229int kex_dh_keypair(struct kex *);
207int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, 230int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
234 const BIGNUM *, const u_char *, size_t, 257 const BIGNUM *, const u_char *, size_t,
235 u_char *, size_t *); 258 u_char *, size_t *);
236 259
260int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
261 const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
262 const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
263 const struct sshbuf *client_pub, const struct sshbuf *server_pub,
264 const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
265
237void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) 266void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
238 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) 267 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
239 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); 268 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
diff --git a/kexdh.c b/kexdh.c
index 67133e339..edaa46762 100644
--- a/kexdh.c
+++ b/kexdh.c
@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex)
48{ 48{
49 switch (kex->kex_type) { 49 switch (kex->kex_type) {
50 case KEX_DH_GRP1_SHA1: 50 case KEX_DH_GRP1_SHA1:
51#ifdef GSSAPI
52 case KEX_GSS_GRP1_SHA1:
53#endif
51 kex->dh = dh_new_group1(); 54 kex->dh = dh_new_group1();
52 break; 55 break;
53 case KEX_DH_GRP14_SHA1: 56 case KEX_DH_GRP14_SHA1:
54 case KEX_DH_GRP14_SHA256: 57 case KEX_DH_GRP14_SHA256:
58#ifdef GSSAPI
59 case KEX_GSS_GRP14_SHA1:
60 case KEX_GSS_GRP14_SHA256:
61#endif
55 kex->dh = dh_new_group14(); 62 kex->dh = dh_new_group14();
56 break; 63 break;
57 case KEX_DH_GRP16_SHA512: 64 case KEX_DH_GRP16_SHA512:
65#ifdef GSSAPI
66 case KEX_GSS_GRP16_SHA512:
67#endif
58 kex->dh = dh_new_group16(); 68 kex->dh = dh_new_group16();
59 break; 69 break;
60 case KEX_DH_GRP18_SHA512: 70 case KEX_DH_GRP18_SHA512:
diff --git a/kexgen.c b/kexgen.c
index bb996b504..d353ed8b0 100644
--- a/kexgen.c
+++ b/kexgen.c
@@ -44,7 +44,7 @@
44static int input_kex_gen_init(int, u_int32_t, struct ssh *); 44static int input_kex_gen_init(int, u_int32_t, struct ssh *);
45static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); 45static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
46 46
47static int 47int
48kex_gen_hash( 48kex_gen_hash(
49 int hash_alg, 49 int hash_alg,
50 const struct sshbuf *client_version, 50 const struct sshbuf *client_version,
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..f6e1405eb
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,606 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#if defined(GSSAPI) && defined(WITH_OPENSSL)
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "sshbuf.h"
38#include "ssh2.h"
39#include "sshkey.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45#include "digest.h"
46#include "ssherr.h"
47
48#include "ssh-gss.h"
49
50int
51kexgss_client(struct ssh *ssh)
52{
53 struct kex *kex = ssh->kex;
54 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
55 recv_tok = GSS_C_EMPTY_BUFFER,
56 gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
57 Gssctxt *ctxt;
58 OM_uint32 maj_status, min_status, ret_flags;
59 struct sshbuf *server_blob = NULL;
60 struct sshbuf *shared_secret = NULL;
61 struct sshbuf *server_host_key_blob = NULL;
62 struct sshbuf *empty = NULL;
63 u_char *msg;
64 int type = 0;
65 int first = 1;
66 u_char hash[SSH_DIGEST_MAX_LENGTH];
67 size_t hashlen;
68 u_char c;
69 int r;
70
71 /* Initialise our GSSAPI world */
72 ssh_gssapi_build_ctx(&ctxt);
73 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
74 == GSS_C_NO_OID)
75 fatal("Couldn't identify host exchange");
76
77 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
78 fatal("Couldn't import hostname");
79
80 if (kex->gss_client &&
81 ssh_gssapi_client_identity(ctxt, kex->gss_client))
82 fatal("Couldn't acquire client credentials");
83
84 /* Step 1 */
85 switch (kex->kex_type) {
86 case KEX_GSS_GRP1_SHA1:
87 case KEX_GSS_GRP14_SHA1:
88 case KEX_GSS_GRP14_SHA256:
89 case KEX_GSS_GRP16_SHA512:
90 r = kex_dh_keypair(kex);
91 break;
92 case KEX_GSS_NISTP256_SHA256:
93 r = kex_ecdh_keypair(kex);
94 break;
95 case KEX_GSS_C25519_SHA256:
96 r = kex_c25519_keypair(kex);
97 break;
98 default:
99 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
100 }
101 if (r != 0)
102 return r;
103
104 token_ptr = GSS_C_NO_BUFFER;
105
106 do {
107 debug("Calling gss_init_sec_context");
108
109 maj_status = ssh_gssapi_init_ctx(ctxt,
110 kex->gss_deleg_creds, token_ptr, &send_tok,
111 &ret_flags);
112
113 if (GSS_ERROR(maj_status)) {
114 /* XXX Useles code: Missing send? */
115 if (send_tok.length != 0) {
116 if ((r = sshpkt_start(ssh,
117 SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
118 (r = sshpkt_put_string(ssh, send_tok.value,
119 send_tok.length)) != 0)
120 fatal("sshpkt failed: %s", ssh_err(r));
121 }
122 fatal("gss_init_context failed");
123 }
124
125 /* If we've got an old receive buffer get rid of it */
126 if (token_ptr != GSS_C_NO_BUFFER)
127 gss_release_buffer(&min_status, &recv_tok);
128
129 if (maj_status == GSS_S_COMPLETE) {
130 /* If mutual state flag is not true, kex fails */
131 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
132 fatal("Mutual authentication failed");
133
134 /* If integ avail flag is not true kex fails */
135 if (!(ret_flags & GSS_C_INTEG_FLAG))
136 fatal("Integrity check failed");
137 }
138
139 /*
140 * If we have data to send, then the last message that we
141 * received cannot have been a 'complete'.
142 */
143 if (send_tok.length != 0) {
144 if (first) {
145 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
146 (r = sshpkt_put_string(ssh, send_tok.value,
147 send_tok.length)) != 0 ||
148 (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
149 fatal("failed to construct packet: %s", ssh_err(r));
150 first = 0;
151 } else {
152 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
153 (r = sshpkt_put_string(ssh, send_tok.value,
154 send_tok.length)) != 0)
155 fatal("failed to construct packet: %s", ssh_err(r));
156 }
157 if ((r = sshpkt_send(ssh)) != 0)
158 fatal("failed to send packet: %s", ssh_err(r));
159 gss_release_buffer(&min_status, &send_tok);
160
161 /* If we've sent them data, they should reply */
162 do {
163 type = ssh_packet_read(ssh);
164 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
165 debug("Received KEXGSS_HOSTKEY");
166 if (server_host_key_blob)
167 fatal("Server host key received more than once");
168 if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
169 fatal("Failed to read server host key: %s", ssh_err(r));
170 }
171 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
172
173 switch (type) {
174 case SSH2_MSG_KEXGSS_CONTINUE:
175 debug("Received GSSAPI_CONTINUE");
176 if (maj_status == GSS_S_COMPLETE)
177 fatal("GSSAPI Continue received from server when complete");
178 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
179 &recv_tok)) != 0 ||
180 (r = sshpkt_get_end(ssh)) != 0)
181 fatal("Failed to read token: %s", ssh_err(r));
182 break;
183 case SSH2_MSG_KEXGSS_COMPLETE:
184 debug("Received GSSAPI_COMPLETE");
185 if (msg_tok.value != NULL)
186 fatal("Received GSSAPI_COMPLETE twice?");
187 if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
188 (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
189 &msg_tok)) != 0)
190 fatal("Failed to read message: %s", ssh_err(r));
191
192 /* Is there a token included? */
193 if ((r = sshpkt_get_u8(ssh, &c)) != 0)
194 fatal("sshpkt failed: %s", ssh_err(r));
195 if (c) {
196 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
197 ssh, &recv_tok)) != 0)
198 fatal("Failed to read token: %s", ssh_err(r));
199 /* If we're already complete - protocol error */
200 if (maj_status == GSS_S_COMPLETE)
201 sshpkt_disconnect(ssh, "Protocol error: received token when complete");
202 } else {
203 /* No token included */
204 if (maj_status != GSS_S_COMPLETE)
205 sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
206 }
207 if ((r = sshpkt_get_end(ssh)) != 0) {
208 fatal("Expecting end of packet.");
209 }
210 break;
211 case SSH2_MSG_KEXGSS_ERROR:
212 debug("Received Error");
213 if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
214 (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
215 (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
216 (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
217 (r = sshpkt_get_end(ssh)) != 0)
218 fatal("sshpkt_get failed: %s", ssh_err(r));
219 fatal("GSSAPI Error: \n%.400s", msg);
220 default:
221 sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
222 type);
223 }
224 token_ptr = &recv_tok;
225 } else {
226 /* No data, and not complete */
227 if (maj_status != GSS_S_COMPLETE)
228 fatal("Not complete, and no token output");
229 }
230 } while (maj_status & GSS_S_CONTINUE_NEEDED);
231
232 /*
233 * We _must_ have received a COMPLETE message in reply from the
234 * server, which will have set server_blob and msg_tok
235 */
236
237 if (type != SSH2_MSG_KEXGSS_COMPLETE)
238 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
239
240 /* compute shared secret */
241 switch (kex->kex_type) {
242 case KEX_GSS_GRP1_SHA1:
243 case KEX_GSS_GRP14_SHA1:
244 case KEX_GSS_GRP14_SHA256:
245 case KEX_GSS_GRP16_SHA512:
246 r = kex_dh_dec(kex, server_blob, &shared_secret);
247 break;
248 case KEX_GSS_C25519_SHA256:
249 if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
250 fatal("The received key has MSB of last octet set!");
251 r = kex_c25519_dec(kex, server_blob, &shared_secret);
252 break;
253 case KEX_GSS_NISTP256_SHA256:
254 if (sshbuf_len(server_blob) != 65)
255 fatal("The received NIST-P256 key did not match"
256 "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
257
258 if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
259 fatal("The received NIST-P256 key does not have first octet 0x04");
260
261 r = kex_ecdh_dec(kex, server_blob, &shared_secret);
262 break;
263 default:
264 r = SSH_ERR_INVALID_ARGUMENT;
265 break;
266 }
267 if (r != 0)
268 goto out;
269
270 if ((empty = sshbuf_new()) == NULL) {
271 r = SSH_ERR_ALLOC_FAIL;
272 goto out;
273 }
274
275 hashlen = sizeof(hash);
276 if ((r = kex_gen_hash(
277 kex->hash_alg,
278 kex->client_version,
279 kex->server_version,
280 kex->my,
281 kex->peer,
282 (server_host_key_blob ? server_host_key_blob : empty),
283 kex->client_pub,
284 server_blob,
285 shared_secret,
286 hash, &hashlen)) != 0)
287 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
288
289 gssbuf.value = hash;
290 gssbuf.length = hashlen;
291
292 /* Verify that the hash matches the MIC we just got. */
293 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
294 sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
295
296 gss_release_buffer(&min_status, &msg_tok);
297
298 if (kex->gss_deleg_creds)
299 ssh_gssapi_credentials_updated(ctxt);
300
301 if (gss_kex_context == NULL)
302 gss_kex_context = ctxt;
303 else
304 ssh_gssapi_delete_ctx(&ctxt);
305
306 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
307 r = kex_send_newkeys(ssh);
308
309out:
310 explicit_bzero(hash, sizeof(hash));
311 explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
312 sshbuf_free(empty);
313 sshbuf_free(server_host_key_blob);
314 sshbuf_free(server_blob);
315 sshbuf_free(shared_secret);
316 sshbuf_free(kex->client_pub);
317 kex->client_pub = NULL;
318 return r;
319}
320
321int
322kexgssgex_client(struct ssh *ssh)
323{
324 struct kex *kex = ssh->kex;
325 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
326 recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
327 msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
328 Gssctxt *ctxt;
329 OM_uint32 maj_status, min_status, ret_flags;
330 struct sshbuf *shared_secret = NULL;
331 BIGNUM *p = NULL;
332 BIGNUM *g = NULL;
333 struct sshbuf *buf = NULL;
334 struct sshbuf *server_host_key_blob = NULL;
335 struct sshbuf *server_blob = NULL;
336 BIGNUM *dh_server_pub = NULL;
337 u_char *msg;
338 int type = 0;
339 int first = 1;
340 u_char hash[SSH_DIGEST_MAX_LENGTH];
341 size_t hashlen;
342 const BIGNUM *pub_key, *dh_p, *dh_g;
343 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
344 struct sshbuf *empty = NULL;
345 u_char c;
346 int r;
347
348 /* Initialise our GSSAPI world */
349 ssh_gssapi_build_ctx(&ctxt);
350 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
351 == GSS_C_NO_OID)
352 fatal("Couldn't identify host exchange");
353
354 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
355 fatal("Couldn't import hostname");
356
357 if (kex->gss_client &&
358 ssh_gssapi_client_identity(ctxt, kex->gss_client))
359 fatal("Couldn't acquire client credentials");
360
361 debug("Doing group exchange");
362 nbits = dh_estimate(kex->dh_need * 8);
363
364 kex->min = DH_GRP_MIN;
365 kex->max = DH_GRP_MAX;
366 kex->nbits = nbits;
367 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
368 (r = sshpkt_put_u32(ssh, min)) != 0 ||
369 (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
370 (r = sshpkt_put_u32(ssh, max)) != 0 ||
371 (r = sshpkt_send(ssh)) != 0)
372 fatal("Failed to construct a packet: %s", ssh_err(r));
373
374 if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
375 fatal("Error: %s", ssh_err(r));
376
377 if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
378 (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
379 (r = sshpkt_get_end(ssh)) != 0)
380 fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
381
382 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
383 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
384 min, BN_num_bits(p), max);
385
386 if ((kex->dh = dh_new_group(g, p)) == NULL)
387 fatal("dn_new_group() failed");
388 p = g = NULL; /* belong to kex->dh now */
389
390 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
391 goto out;
392 DH_get0_key(kex->dh, &pub_key, NULL);
393
394 token_ptr = GSS_C_NO_BUFFER;
395
396 do {
397 /* Step 2 - call GSS_Init_sec_context() */
398 debug("Calling gss_init_sec_context");
399
400 maj_status = ssh_gssapi_init_ctx(ctxt,
401 kex->gss_deleg_creds, token_ptr, &send_tok,
402 &ret_flags);
403
404 if (GSS_ERROR(maj_status)) {
405 /* XXX Useles code: Missing send? */
406 if (send_tok.length != 0) {
407 if ((r = sshpkt_start(ssh,
408 SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
409 (r = sshpkt_put_string(ssh, send_tok.value,
410 send_tok.length)) != 0)
411 fatal("sshpkt failed: %s", ssh_err(r));
412 }
413 fatal("gss_init_context failed");
414 }
415
416 /* If we've got an old receive buffer get rid of it */
417 if (token_ptr != GSS_C_NO_BUFFER)
418 gss_release_buffer(&min_status, &recv_tok);
419
420 if (maj_status == GSS_S_COMPLETE) {
421 /* If mutual state flag is not true, kex fails */
422 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
423 fatal("Mutual authentication failed");
424
425 /* If integ avail flag is not true kex fails */
426 if (!(ret_flags & GSS_C_INTEG_FLAG))
427 fatal("Integrity check failed");
428 }
429
430 /*
431 * If we have data to send, then the last message that we
432 * received cannot have been a 'complete'.
433 */
434 if (send_tok.length != 0) {
435 if (first) {
436 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
437 (r = sshpkt_put_string(ssh, send_tok.value,
438 send_tok.length)) != 0 ||
439 (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
440 fatal("sshpkt failed: %s", ssh_err(r));
441 first = 0;
442 } else {
443 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
444 (r = sshpkt_put_string(ssh,send_tok.value,
445 send_tok.length)) != 0)
446 fatal("sshpkt failed: %s", ssh_err(r));
447 }
448 if ((r = sshpkt_send(ssh)) != 0)
449 fatal("sshpkt_send failed: %s", ssh_err(r));
450 gss_release_buffer(&min_status, &send_tok);
451
452 /* If we've sent them data, they should reply */
453 do {
454 type = ssh_packet_read(ssh);
455 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
456 debug("Received KEXGSS_HOSTKEY");
457 if (server_host_key_blob)
458 fatal("Server host key received more than once");
459 if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
460 fatal("sshpkt failed: %s", ssh_err(r));
461 }
462 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
463
464 switch (type) {
465 case SSH2_MSG_KEXGSS_CONTINUE:
466 debug("Received GSSAPI_CONTINUE");
467 if (maj_status == GSS_S_COMPLETE)
468 fatal("GSSAPI Continue received from server when complete");
469 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
470 &recv_tok)) != 0 ||
471 (r = sshpkt_get_end(ssh)) != 0)
472 fatal("sshpkt failed: %s", ssh_err(r));
473 break;
474 case SSH2_MSG_KEXGSS_COMPLETE:
475 debug("Received GSSAPI_COMPLETE");
476 if (msg_tok.value != NULL)
477 fatal("Received GSSAPI_COMPLETE twice?");
478 if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
479 (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
480 &msg_tok)) != 0)
481 fatal("sshpkt failed: %s", ssh_err(r));
482
483 /* Is there a token included? */
484 if ((r = sshpkt_get_u8(ssh, &c)) != 0)
485 fatal("sshpkt failed: %s", ssh_err(r));
486 if (c) {
487 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
488 ssh, &recv_tok)) != 0 ||
489 (r = sshpkt_get_end(ssh)) != 0)
490 fatal("sshpkt failed: %s", ssh_err(r));
491 /* If we're already complete - protocol error */
492 if (maj_status == GSS_S_COMPLETE)
493 sshpkt_disconnect(ssh, "Protocol error: received token when complete");
494 } else {
495 /* No token included */
496 if (maj_status != GSS_S_COMPLETE)
497 sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
498 }
499 break;
500 case SSH2_MSG_KEXGSS_ERROR:
501 debug("Received Error");
502 if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
503 (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
504 (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
505 (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
506 (r = sshpkt_get_end(ssh)) != 0)
507 fatal("sshpkt failed: %s", ssh_err(r));
508 fatal("GSSAPI Error: \n%.400s", msg);
509 default:
510 sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
511 type);
512 }
513 token_ptr = &recv_tok;
514 } else {
515 /* No data, and not complete */
516 if (maj_status != GSS_S_COMPLETE)
517 fatal("Not complete, and no token output");
518 }
519 } while (maj_status & GSS_S_CONTINUE_NEEDED);
520
521 /*
522 * We _must_ have received a COMPLETE message in reply from the
523 * server, which will have set dh_server_pub and msg_tok
524 */
525
526 if (type != SSH2_MSG_KEXGSS_COMPLETE)
527 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
528
529 /* 7. C verifies that the key Q_S is valid */
530 /* 8. C computes shared secret */
531 if ((buf = sshbuf_new()) == NULL ||
532 (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
533 (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
534 goto out;
535 sshbuf_free(buf);
536 buf = NULL;
537
538 if ((shared_secret = sshbuf_new()) == NULL) {
539 r = SSH_ERR_ALLOC_FAIL;
540 goto out;
541 }
542
543 if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
544 goto out;
545 if ((empty = sshbuf_new()) == NULL) {
546 r = SSH_ERR_ALLOC_FAIL;
547 goto out;
548 }
549
550 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
551 hashlen = sizeof(hash);
552 if ((r = kexgex_hash(
553 kex->hash_alg,
554 kex->client_version,
555 kex->server_version,
556 kex->my,
557 kex->peer,
558 (server_host_key_blob ? server_host_key_blob : empty),
559 kex->min, kex->nbits, kex->max,
560 dh_p, dh_g,
561 pub_key,
562 dh_server_pub,
563 sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
564 hash, &hashlen)) != 0)
565 fatal("Failed to calculate hash: %s", ssh_err(r));
566
567 gssbuf.value = hash;
568 gssbuf.length = hashlen;
569
570 /* Verify that the hash matches the MIC we just got. */
571 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
572 sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
573
574 gss_release_buffer(&min_status, &msg_tok);
575
576 /* save session id */
577 if (kex->session_id == NULL) {
578 kex->session_id_len = hashlen;
579 kex->session_id = xmalloc(kex->session_id_len);
580 memcpy(kex->session_id, hash, kex->session_id_len);
581 }
582
583 if (kex->gss_deleg_creds)
584 ssh_gssapi_credentials_updated(ctxt);
585
586 if (gss_kex_context == NULL)
587 gss_kex_context = ctxt;
588 else
589 ssh_gssapi_delete_ctx(&ctxt);
590
591 /* Finally derive the keys and send them */
592 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
593 r = kex_send_newkeys(ssh);
594out:
595 sshbuf_free(buf);
596 sshbuf_free(server_blob);
597 sshbuf_free(empty);
598 explicit_bzero(hash, sizeof(hash));
599 DH_free(kex->dh);
600 kex->dh = NULL;
601 BN_clear_free(dh_server_pub);
602 sshbuf_free(shared_secret);
603 sshbuf_free(server_host_key_blob);
604 return r;
605}
606#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..60bc02deb
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,474 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#if defined(GSSAPI) && defined(WITH_OPENSSL)
28
29#include <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "sshbuf.h"
36#include "ssh2.h"
37#include "sshkey.h"
38#include "cipher.h"
39#include "kex.h"
40#include "log.h"
41#include "packet.h"
42#include "dh.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */
46#include "servconf.h"
47#include "ssh-gss.h"
48#include "digest.h"
49#include "ssherr.h"
50
51extern ServerOptions options;
52
53int
54kexgss_server(struct ssh *ssh)
55{
56 struct kex *kex = ssh->kex;
57 OM_uint32 maj_status, min_status;
58
59 /*
60 * Some GSSAPI implementations use the input value of ret_flags (an
61 * output variable) as a means of triggering mechanism specific
62 * features. Initializing it to zero avoids inadvertently
63 * activating this non-standard behaviour.
64 */
65
66 OM_uint32 ret_flags = 0;
67 gss_buffer_desc gssbuf, recv_tok, msg_tok;
68 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
69 Gssctxt *ctxt = NULL;
70 struct sshbuf *shared_secret = NULL;
71 struct sshbuf *client_pubkey = NULL;
72 struct sshbuf *server_pubkey = NULL;
73 struct sshbuf *empty = sshbuf_new();
74 int type = 0;
75 gss_OID oid;
76 char *mechs;
77 u_char hash[SSH_DIGEST_MAX_LENGTH];
78 size_t hashlen;
79 int r;
80
81 /* Initialise GSSAPI */
82
83 /* If we're rekeying, privsep means that some of the private structures
84 * in the GSSAPI code are no longer available. This kludges them back
85 * into life
86 */
87 if (!ssh_gssapi_oid_table_ok()) {
88 mechs = ssh_gssapi_server_mechanisms();
89 free(mechs);
90 }
91
92 debug2("%s: Identifying %s", __func__, kex->name);
93 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
94 if (oid == GSS_C_NO_OID)
95 fatal("Unknown gssapi mechanism");
96
97 debug2("%s: Acquiring credentials", __func__);
98
99 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
100 fatal("Unable to acquire credentials for the server");
101
102 do {
103 debug("Wait SSH2_MSG_KEXGSS_INIT");
104 type = ssh_packet_read(ssh);
105 switch(type) {
106 case SSH2_MSG_KEXGSS_INIT:
107 if (client_pubkey != NULL)
108 fatal("Received KEXGSS_INIT after initialising");
109 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
110 &recv_tok)) != 0 ||
111 (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
112 (r = sshpkt_get_end(ssh)) != 0)
113 fatal("sshpkt failed: %s", ssh_err(r));
114
115 switch (kex->kex_type) {
116 case KEX_GSS_GRP1_SHA1:
117 case KEX_GSS_GRP14_SHA1:
118 case KEX_GSS_GRP14_SHA256:
119 case KEX_GSS_GRP16_SHA512:
120 r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
121 &shared_secret);
122 break;
123 case KEX_GSS_NISTP256_SHA256:
124 r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
125 &shared_secret);
126 break;
127 case KEX_GSS_C25519_SHA256:
128 r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
129 &shared_secret);
130 break;
131 default:
132 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
133 }
134 if (r != 0)
135 goto out;
136
137 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
138 break;
139 case SSH2_MSG_KEXGSS_CONTINUE:
140 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
141 &recv_tok)) != 0 ||
142 (r = sshpkt_get_end(ssh)) != 0)
143 fatal("sshpkt failed: %s", ssh_err(r));
144 break;
145 default:
146 sshpkt_disconnect(ssh,
147 "Protocol error: didn't expect packet type %d",
148 type);
149 }
150
151 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
152 &send_tok, &ret_flags));
153
154 gss_release_buffer(&min_status, &recv_tok);
155
156 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
157 fatal("Zero length token output when incomplete");
158
159 if (client_pubkey == NULL)
160 fatal("No client public key");
161
162 if (maj_status & GSS_S_CONTINUE_NEEDED) {
163 debug("Sending GSSAPI_CONTINUE");
164 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
165 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
166 (r = sshpkt_send(ssh)) != 0)
167 fatal("sshpkt failed: %s", ssh_err(r));
168 gss_release_buffer(&min_status, &send_tok);
169 }
170 } while (maj_status & GSS_S_CONTINUE_NEEDED);
171
172 if (GSS_ERROR(maj_status)) {
173 if (send_tok.length > 0) {
174 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
175 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
176 (r = sshpkt_send(ssh)) != 0)
177 fatal("sshpkt failed: %s", ssh_err(r));
178 }
179 fatal("accept_ctx died");
180 }
181
182 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
183 fatal("Mutual Authentication flag wasn't set");
184
185 if (!(ret_flags & GSS_C_INTEG_FLAG))
186 fatal("Integrity flag wasn't set");
187
188 hashlen = sizeof(hash);
189 if ((r = kex_gen_hash(
190 kex->hash_alg,
191 kex->client_version,
192 kex->server_version,
193 kex->peer,
194 kex->my,
195 empty,
196 client_pubkey,
197 server_pubkey,
198 shared_secret,
199 hash, &hashlen)) != 0)
200 goto out;
201
202 gssbuf.value = hash;
203 gssbuf.length = hashlen;
204
205 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
206 fatal("Couldn't get MIC");
207
208 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
209 (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
210 (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
211 fatal("sshpkt failed: %s", ssh_err(r));
212
213 if (send_tok.length != 0) {
214 if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
215 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
216 fatal("sshpkt failed: %s", ssh_err(r));
217 } else {
218 if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
219 fatal("sshpkt failed: %s", ssh_err(r));
220 }
221 if ((r = sshpkt_send(ssh)) != 0)
222 fatal("sshpkt_send failed: %s", ssh_err(r));
223
224 gss_release_buffer(&min_status, &send_tok);
225 gss_release_buffer(&min_status, &msg_tok);
226
227 if (gss_kex_context == NULL)
228 gss_kex_context = ctxt;
229 else
230 ssh_gssapi_delete_ctx(&ctxt);
231
232 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
233 r = kex_send_newkeys(ssh);
234
235 /* If this was a rekey, then save out any delegated credentials we
236 * just exchanged. */
237 if (options.gss_store_rekey)
238 ssh_gssapi_rekey_creds();
239out:
240 sshbuf_free(empty);
241 explicit_bzero(hash, sizeof(hash));
242 sshbuf_free(shared_secret);
243 sshbuf_free(client_pubkey);
244 sshbuf_free(server_pubkey);
245 return r;
246}
247
248int
249kexgssgex_server(struct ssh *ssh)
250{
251 struct kex *kex = ssh->kex;
252 OM_uint32 maj_status, min_status;
253
254 /*
255 * Some GSSAPI implementations use the input value of ret_flags (an
256 * output variable) as a means of triggering mechanism specific
257 * features. Initializing it to zero avoids inadvertently
258 * activating this non-standard behaviour.
259 */
260
261 OM_uint32 ret_flags = 0;
262 gss_buffer_desc gssbuf, recv_tok, msg_tok;
263 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
264 Gssctxt *ctxt = NULL;
265 struct sshbuf *shared_secret = NULL;
266 int type = 0;
267 gss_OID oid;
268 char *mechs;
269 u_char hash[SSH_DIGEST_MAX_LENGTH];
270 size_t hashlen;
271 BIGNUM *dh_client_pub = NULL;
272 const BIGNUM *pub_key, *dh_p, *dh_g;
273 int min = -1, max = -1, nbits = -1;
274 int cmin = -1, cmax = -1; /* client proposal */
275 struct sshbuf *empty = sshbuf_new();
276 int r;
277
278 /* Initialise GSSAPI */
279
280 /* If we're rekeying, privsep means that some of the private structures
281 * in the GSSAPI code are no longer available. This kludges them back
282 * into life
283 */
284 if (!ssh_gssapi_oid_table_ok())
285 if ((mechs = ssh_gssapi_server_mechanisms()))
286 free(mechs);
287
288 debug2("%s: Identifying %s", __func__, kex->name);
289 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
290 if (oid == GSS_C_NO_OID)
291 fatal("Unknown gssapi mechanism");
292
293 debug2("%s: Acquiring credentials", __func__);
294
295 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
296 fatal("Unable to acquire credentials for the server");
297
298 /* 5. S generates an ephemeral key pair (do the allocations early) */
299 debug("Doing group exchange");
300 ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
301 /* store client proposal to provide valid signature */
302 if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
303 (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
304 (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
305 (r = sshpkt_get_end(ssh)) != 0)
306 fatal("sshpkt failed: %s", ssh_err(r));
307 kex->nbits = nbits;
308 kex->min = cmin;
309 kex->max = cmax;
310 min = MAX(DH_GRP_MIN, cmin);
311 max = MIN(DH_GRP_MAX, cmax);
312 nbits = MAXIMUM(DH_GRP_MIN, nbits);
313 nbits = MINIMUM(DH_GRP_MAX, nbits);
314 if (max < min || nbits < min || max < nbits)
315 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
316 min, nbits, max);
317 kex->dh = PRIVSEP(choose_dh(min, nbits, max));
318 if (kex->dh == NULL) {
319 sshpkt_disconnect(ssh, "Protocol error: no matching group found");
320 fatal("Protocol error: no matching group found");
321 }
322
323 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
324 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
325 (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
326 (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
327 (r = sshpkt_send(ssh)) != 0)
328 fatal("sshpkt failed: %s", ssh_err(r));
329
330 if ((r = ssh_packet_write_wait(ssh)) != 0)
331 fatal("ssh_packet_write_wait: %s", ssh_err(r));
332
333 /* Compute our exchange value in parallel with the client */
334 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
335 goto out;
336
337 do {
338 debug("Wait SSH2_MSG_GSSAPI_INIT");
339 type = ssh_packet_read(ssh);
340 switch(type) {
341 case SSH2_MSG_KEXGSS_INIT:
342 if (dh_client_pub != NULL)
343 fatal("Received KEXGSS_INIT after initialising");
344 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
345 &recv_tok)) != 0 ||
346 (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
347 (r = sshpkt_get_end(ssh)) != 0)
348 fatal("sshpkt failed: %s", ssh_err(r));
349
350 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
351 break;
352 case SSH2_MSG_KEXGSS_CONTINUE:
353 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
354 &recv_tok)) != 0 ||
355 (r = sshpkt_get_end(ssh)) != 0)
356 fatal("sshpkt failed: %s", ssh_err(r));
357 break;
358 default:
359 sshpkt_disconnect(ssh,
360 "Protocol error: didn't expect packet type %d",
361 type);
362 }
363
364 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
365 &send_tok, &ret_flags));
366
367 gss_release_buffer(&min_status, &recv_tok);
368
369 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
370 fatal("Zero length token output when incomplete");
371
372 if (dh_client_pub == NULL)
373 fatal("No client public key");
374
375 if (maj_status & GSS_S_CONTINUE_NEEDED) {
376 debug("Sending GSSAPI_CONTINUE");
377 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
378 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
379 (r = sshpkt_send(ssh)) != 0)
380 fatal("sshpkt failed: %s", ssh_err(r));
381 gss_release_buffer(&min_status, &send_tok);
382 }
383 } while (maj_status & GSS_S_CONTINUE_NEEDED);
384
385 if (GSS_ERROR(maj_status)) {
386 if (send_tok.length > 0) {
387 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
388 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
389 (r = sshpkt_send(ssh)) != 0)
390 fatal("sshpkt failed: %s", ssh_err(r));
391 }
392 fatal("accept_ctx died");
393 }
394
395 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
396 fatal("Mutual Authentication flag wasn't set");
397
398 if (!(ret_flags & GSS_C_INTEG_FLAG))
399 fatal("Integrity flag wasn't set");
400
401 /* calculate shared secret */
402 if ((shared_secret = sshbuf_new()) == NULL) {
403 r = SSH_ERR_ALLOC_FAIL;
404 goto out;
405 }
406 if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
407 goto out;
408
409 DH_get0_key(kex->dh, &pub_key, NULL);
410 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
411 hashlen = sizeof(hash);
412 if ((r = kexgex_hash(
413 kex->hash_alg,
414 kex->client_version,
415 kex->server_version,
416 kex->peer,
417 kex->my,
418 empty,
419 cmin, nbits, cmax,
420 dh_p, dh_g,
421 dh_client_pub,
422 pub_key,
423 sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
424 hash, &hashlen)) != 0)
425 fatal("kexgex_hash failed: %s", ssh_err(r));
426
427 gssbuf.value = hash;
428 gssbuf.length = hashlen;
429
430 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
431 fatal("Couldn't get MIC");
432
433 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
434 (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
435 (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
436 fatal("sshpkt failed: %s", ssh_err(r));
437
438 if (send_tok.length != 0) {
439 if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
440 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
441 fatal("sshpkt failed: %s", ssh_err(r));
442 } else {
443 if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
444 fatal("sshpkt failed: %s", ssh_err(r));
445 }
446 if ((r = sshpkt_send(ssh)) != 0)
447 fatal("sshpkt failed: %s", ssh_err(r));
448
449 gss_release_buffer(&min_status, &send_tok);
450 gss_release_buffer(&min_status, &msg_tok);
451
452 if (gss_kex_context == NULL)
453 gss_kex_context = ctxt;
454 else
455 ssh_gssapi_delete_ctx(&ctxt);
456
457 /* Finally derive the keys and send them */
458 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
459 r = kex_send_newkeys(ssh);
460
461 /* If this was a rekey, then save out any delegated credentials we
462 * just exchanged. */
463 if (options.gss_store_rekey)
464 ssh_gssapi_rekey_creds();
465out:
466 sshbuf_free(empty);
467 explicit_bzero(hash, sizeof(hash));
468 DH_free(kex->dh);
469 kex->dh = NULL;
470 BN_clear_free(dh_client_pub);
471 sshbuf_free(shared_secret);
472 return r;
473}
474#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --git a/mac.c b/mac.c
index f3dda6692..de346ed20 100644
--- a/mac.c
+++ b/mac.c
@@ -30,6 +30,7 @@
30#include <stdlib.h> 30#include <stdlib.h>
31#include <string.h> 31#include <string.h>
32#include <stdio.h> 32#include <stdio.h>
33#include <stdlib.h>
33 34
34#include "digest.h" 35#include "digest.h"
35#include "hmac.h" 36#include "hmac.h"
diff --git a/monitor.c b/monitor.c
index 00af44f98..bead9e204 100644
--- a/monitor.c
+++ b/monitor.c
@@ -147,6 +147,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
147int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *); 147int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
148int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); 148int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
149int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); 149int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
150int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
151int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
150#endif 152#endif
151 153
152#ifdef SSH_AUDIT_EVENTS 154#ifdef SSH_AUDIT_EVENTS
@@ -219,11 +221,18 @@ struct mon_table mon_dispatch_proto20[] = {
219 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, 221 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
220 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, 222 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
221 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, 223 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
224 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
222#endif 225#endif
223 {0, 0, NULL} 226 {0, 0, NULL}
224}; 227};
225 228
226struct mon_table mon_dispatch_postauth20[] = { 229struct mon_table mon_dispatch_postauth20[] = {
230#ifdef GSSAPI
231 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
232 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
233 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
234 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
235#endif
227#ifdef WITH_OPENSSL 236#ifdef WITH_OPENSSL
228 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 237 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
229#endif 238#endif
@@ -292,6 +301,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
292 /* Permit requests for moduli and signatures */ 301 /* Permit requests for moduli and signatures */
293 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 302 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
294 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 303 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
304#ifdef GSSAPI
305 /* and for the GSSAPI key exchange */
306 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
307#endif
295 308
296 /* The first few requests do not require asynchronous access */ 309 /* The first few requests do not require asynchronous access */
297 while (!authenticated) { 310 while (!authenticated) {
@@ -405,6 +418,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
405 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 418 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
406 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 419 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
407 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 420 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
421#ifdef GSSAPI
422 /* and for the GSSAPI key exchange */
423 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
424#endif
408 425
409 if (auth_opts->permit_pty_flag) { 426 if (auth_opts->permit_pty_flag) {
410 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); 427 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
@@ -1687,6 +1704,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
1687# ifdef OPENSSL_HAS_ECC 1704# ifdef OPENSSL_HAS_ECC
1688 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 1705 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
1689# endif 1706# endif
1707# ifdef GSSAPI
1708 if (options.gss_keyex) {
1709 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1710 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1711 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
1712 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
1713 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
1714 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
1715 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
1716 }
1717# endif
1690#endif /* WITH_OPENSSL */ 1718#endif /* WITH_OPENSSL */
1691 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 1719 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
1692 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 1720 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
@@ -1780,8 +1808,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1780 u_char *p; 1808 u_char *p;
1781 int r; 1809 int r;
1782 1810
1783 if (!options.gss_authentication) 1811 if (!options.gss_authentication && !options.gss_keyex)
1784 fatal("%s: GSSAPI authentication not enabled", __func__); 1812 fatal("%s: GSSAPI not enabled", __func__);
1785 1813
1786 if ((r = sshbuf_get_string(m, &p, &len)) != 0) 1814 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1787 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1815 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1813,8 +1841,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1813 OM_uint32 flags = 0; /* GSI needs this */ 1841 OM_uint32 flags = 0; /* GSI needs this */
1814 int r; 1842 int r;
1815 1843
1816 if (!options.gss_authentication) 1844 if (!options.gss_authentication && !options.gss_keyex)
1817 fatal("%s: GSSAPI authentication not enabled", __func__); 1845 fatal("%s: GSSAPI not enabled", __func__);
1818 1846
1819 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) 1847 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
1820 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1848 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1834,6 +1862,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1834 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1862 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1835 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1863 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1836 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1864 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1865 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1837 } 1866 }
1838 return (0); 1867 return (0);
1839} 1868}
@@ -1845,8 +1874,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1845 OM_uint32 ret; 1874 OM_uint32 ret;
1846 int r; 1875 int r;
1847 1876
1848 if (!options.gss_authentication) 1877 if (!options.gss_authentication && !options.gss_keyex)
1849 fatal("%s: GSSAPI authentication not enabled", __func__); 1878 fatal("%s: GSSAPI not enabled", __func__);
1850 1879
1851 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || 1880 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
1852 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) 1881 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
@@ -1872,13 +1901,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1872int 1901int
1873mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) 1902mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1874{ 1903{
1875 int r, authenticated; 1904 int r, authenticated, kex;
1876 const char *displayname; 1905 const char *displayname;
1877 1906
1878 if (!options.gss_authentication) 1907 if (!options.gss_authentication && !options.gss_keyex)
1879 fatal("%s: GSSAPI authentication not enabled", __func__); 1908 fatal("%s: GSSAPI not enabled", __func__);
1880 1909
1881 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1910 if ((r = sshbuf_get_u32(m, &kex)) != 0)
1911 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1912
1913 authenticated = authctxt->valid &&
1914 ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
1882 1915
1883 sshbuf_reset(m); 1916 sshbuf_reset(m);
1884 if ((r = sshbuf_put_u32(m, authenticated)) != 0) 1917 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
@@ -1887,7 +1920,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1887 debug3("%s: sending result %d", __func__, authenticated); 1920 debug3("%s: sending result %d", __func__, authenticated);
1888 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); 1921 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
1889 1922
1890 auth_method = "gssapi-with-mic"; 1923 if (kex) {
1924 auth_method = "gssapi-keyex";
1925 } else {
1926 auth_method = "gssapi-with-mic";
1927 }
1891 1928
1892 if ((displayname = ssh_gssapi_displayname()) != NULL) 1929 if ((displayname = ssh_gssapi_displayname()) != NULL)
1893 auth2_record_info(authctxt, "%s", displayname); 1930 auth2_record_info(authctxt, "%s", displayname);
@@ -1895,5 +1932,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1895 /* Monitor loop will terminate if authenticated */ 1932 /* Monitor loop will terminate if authenticated */
1896 return (authenticated); 1933 return (authenticated);
1897} 1934}
1935
1936int
1937mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
1938{
1939 gss_buffer_desc data;
1940 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1941 OM_uint32 major, minor;
1942 size_t len;
1943 u_char *p = NULL;
1944 int r;
1945
1946 if (!options.gss_authentication && !options.gss_keyex)
1947 fatal("%s: GSSAPI not enabled", __func__);
1948
1949 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1950 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1951 data.value = p;
1952 data.length = len;
1953 /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
1954 if (data.length != 20 && data.length != 32 && data.length != 64)
1955 fatal("%s: data length incorrect: %d", __func__,
1956 (int) data.length);
1957
1958 /* Save the session ID on the first time around */
1959 if (session_id2_len == 0) {
1960 session_id2_len = data.length;
1961 session_id2 = xmalloc(session_id2_len);
1962 memcpy(session_id2, data.value, session_id2_len);
1963 }
1964 major = ssh_gssapi_sign(gsscontext, &data, &hash);
1965
1966 free(data.value);
1967
1968 sshbuf_reset(m);
1969
1970 if ((r = sshbuf_put_u32(m, major)) != 0 ||
1971 (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
1972 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1973
1974 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
1975
1976 gss_release_buffer(&minor, &hash);
1977
1978 /* Turn on getpwnam permissions */
1979 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
1980
1981 /* And credential updating, for when rekeying */
1982 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
1983
1984 return (0);
1985}
1986
1987int
1988mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
1989 ssh_gssapi_ccache store;
1990 int r, ok;
1991
1992 if (!options.gss_authentication && !options.gss_keyex)
1993 fatal("%s: GSSAPI not enabled", __func__);
1994
1995 if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
1996 (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
1997 (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
1998 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1999
2000 ok = ssh_gssapi_update_creds(&store);
2001
2002 free(store.filename);
2003 free(store.envvar);
2004 free(store.envval);
2005
2006 sshbuf_reset(m);
2007 if ((r = sshbuf_put_u32(m, ok)) != 0)
2008 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2009
2010 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2011
2012 return(0);
2013}
2014
1898#endif /* GSSAPI */ 2015#endif /* GSSAPI */
1899 2016
diff --git a/monitor.h b/monitor.h
index 683e5e071..2b1a2d590 100644
--- a/monitor.h
+++ b/monitor.h
@@ -63,6 +63,8 @@ enum monitor_reqtype {
63 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, 63 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
64 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, 64 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
65 65
66 MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
67 MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
66}; 68};
67 69
68struct ssh; 70struct ssh;
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 4169b7604..fdca39a6a 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -978,13 +978,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
978} 978}
979 979
980int 980int
981mm_ssh_gssapi_userok(char *user) 981mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
982{ 982{
983 struct sshbuf *m; 983 struct sshbuf *m;
984 int r, authenticated = 0; 984 int r, authenticated = 0;
985 985
986 if ((m = sshbuf_new()) == NULL) 986 if ((m = sshbuf_new()) == NULL)
987 fatal("%s: sshbuf_new failed", __func__); 987 fatal("%s: sshbuf_new failed", __func__);
988 if ((r = sshbuf_put_u32(m, kex)) != 0)
989 fatal("%s: buffer error: %s", __func__, ssh_err(r));
988 990
989 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); 991 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
990 mm_request_receive_expect(pmonitor->m_recvfd, 992 mm_request_receive_expect(pmonitor->m_recvfd,
@@ -997,4 +999,57 @@ mm_ssh_gssapi_userok(char *user)
997 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 999 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
998 return (authenticated); 1000 return (authenticated);
999} 1001}
1002
1003OM_uint32
1004mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1005{
1006 struct sshbuf *m;
1007 OM_uint32 major;
1008 int r;
1009
1010 if ((m = sshbuf_new()) == NULL)
1011 fatal("%s: sshbuf_new failed", __func__);
1012 if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
1013 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1014
1015 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
1016 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
1017
1018 if ((r = sshbuf_get_u32(m, &major)) != 0 ||
1019 (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
1020 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1021
1022 sshbuf_free(m);
1023
1024 return (major);
1025}
1026
1027int
1028mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1029{
1030 struct sshbuf *m;
1031 int r, ok;
1032
1033 if ((m = sshbuf_new()) == NULL)
1034 fatal("%s: sshbuf_new failed", __func__);
1035
1036 if ((r = sshbuf_put_cstring(m,
1037 store->filename ? store->filename : "")) != 0 ||
1038 (r = sshbuf_put_cstring(m,
1039 store->envvar ? store->envvar : "")) != 0 ||
1040 (r = sshbuf_put_cstring(m,
1041 store->envval ? store->envval : "")) != 0)
1042 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1043
1044 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
1045 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
1046
1047 if ((r = sshbuf_get_u32(m, &ok)) != 0)
1048 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1049
1050 sshbuf_free(m);
1051
1052 return (ok);
1053}
1054
1000#endif /* GSSAPI */ 1055#endif /* GSSAPI */
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 191277f3a..92dda574b 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -63,8 +63,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
63OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 63OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
64OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 64OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
65 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 65 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
66int mm_ssh_gssapi_userok(char *user); 66int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
67OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 67OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
68OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
69int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
68#endif 70#endif
69 71
70#ifdef USE_PAM 72#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index f78b4d6fe..3c68d1a88 100644
--- a/readconf.c
+++ b/readconf.c
@@ -67,6 +67,7 @@
67#include "uidswap.h" 67#include "uidswap.h"
68#include "myproposal.h" 68#include "myproposal.h"
69#include "digest.h" 69#include "digest.h"
70#include "ssh-gss.h"
70 71
71/* Format of the configuration file: 72/* Format of the configuration file:
72 73
@@ -162,6 +163,8 @@ typedef enum {
162 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 163 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
163 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 164 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
164 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 165 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
166 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
167 oGssServerIdentity, oGssKexAlgorithms,
165 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 168 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
166 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, 169 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
167 oHashKnownHosts, 170 oHashKnownHosts,
@@ -202,10 +205,22 @@ static struct {
202 /* Sometimes-unsupported options */ 205 /* Sometimes-unsupported options */
203#if defined(GSSAPI) 206#if defined(GSSAPI)
204 { "gssapiauthentication", oGssAuthentication }, 207 { "gssapiauthentication", oGssAuthentication },
208 { "gssapikeyexchange", oGssKeyEx },
205 { "gssapidelegatecredentials", oGssDelegateCreds }, 209 { "gssapidelegatecredentials", oGssDelegateCreds },
210 { "gssapitrustdns", oGssTrustDns },
211 { "gssapiclientidentity", oGssClientIdentity },
212 { "gssapiserveridentity", oGssServerIdentity },
213 { "gssapirenewalforcesrekey", oGssRenewalRekey },
214 { "gssapikexalgorithms", oGssKexAlgorithms },
206# else 215# else
207 { "gssapiauthentication", oUnsupported }, 216 { "gssapiauthentication", oUnsupported },
217 { "gssapikeyexchange", oUnsupported },
208 { "gssapidelegatecredentials", oUnsupported }, 218 { "gssapidelegatecredentials", oUnsupported },
219 { "gssapitrustdns", oUnsupported },
220 { "gssapiclientidentity", oUnsupported },
221 { "gssapiserveridentity", oUnsupported },
222 { "gssapirenewalforcesrekey", oUnsupported },
223 { "gssapikexalgorithms", oUnsupported },
209#endif 224#endif
210#ifdef ENABLE_PKCS11 225#ifdef ENABLE_PKCS11
211 { "pkcs11provider", oPKCS11Provider }, 226 { "pkcs11provider", oPKCS11Provider },
@@ -988,10 +1003,42 @@ parse_time:
988 intptr = &options->gss_authentication; 1003 intptr = &options->gss_authentication;
989 goto parse_flag; 1004 goto parse_flag;
990 1005
1006 case oGssKeyEx:
1007 intptr = &options->gss_keyex;
1008 goto parse_flag;
1009
991 case oGssDelegateCreds: 1010 case oGssDelegateCreds:
992 intptr = &options->gss_deleg_creds; 1011 intptr = &options->gss_deleg_creds;
993 goto parse_flag; 1012 goto parse_flag;
994 1013
1014 case oGssTrustDns:
1015 intptr = &options->gss_trust_dns;
1016 goto parse_flag;
1017
1018 case oGssClientIdentity:
1019 charptr = &options->gss_client_identity;
1020 goto parse_string;
1021
1022 case oGssServerIdentity:
1023 charptr = &options->gss_server_identity;
1024 goto parse_string;
1025
1026 case oGssRenewalRekey:
1027 intptr = &options->gss_renewal_rekey;
1028 goto parse_flag;
1029
1030 case oGssKexAlgorithms:
1031 arg = strdelim(&s);
1032 if (!arg || *arg == '\0')
1033 fatal("%.200s line %d: Missing argument.",
1034 filename, linenum);
1035 if (!kex_gss_names_valid(arg))
1036 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1037 filename, linenum, arg ? arg : "<NONE>");
1038 if (*activep && options->gss_kex_algorithms == NULL)
1039 options->gss_kex_algorithms = xstrdup(arg);
1040 break;
1041
995 case oBatchMode: 1042 case oBatchMode:
996 intptr = &options->batch_mode; 1043 intptr = &options->batch_mode;
997 goto parse_flag; 1044 goto parse_flag;
@@ -1863,7 +1910,13 @@ initialize_options(Options * options)
1863 options->pubkey_authentication = -1; 1910 options->pubkey_authentication = -1;
1864 options->challenge_response_authentication = -1; 1911 options->challenge_response_authentication = -1;
1865 options->gss_authentication = -1; 1912 options->gss_authentication = -1;
1913 options->gss_keyex = -1;
1866 options->gss_deleg_creds = -1; 1914 options->gss_deleg_creds = -1;
1915 options->gss_trust_dns = -1;
1916 options->gss_renewal_rekey = -1;
1917 options->gss_client_identity = NULL;
1918 options->gss_server_identity = NULL;
1919 options->gss_kex_algorithms = NULL;
1867 options->password_authentication = -1; 1920 options->password_authentication = -1;
1868 options->kbd_interactive_authentication = -1; 1921 options->kbd_interactive_authentication = -1;
1869 options->kbd_interactive_devices = NULL; 1922 options->kbd_interactive_devices = NULL;
@@ -2009,8 +2062,18 @@ fill_default_options(Options * options)
2009 options->challenge_response_authentication = 1; 2062 options->challenge_response_authentication = 1;
2010 if (options->gss_authentication == -1) 2063 if (options->gss_authentication == -1)
2011 options->gss_authentication = 0; 2064 options->gss_authentication = 0;
2065 if (options->gss_keyex == -1)
2066 options->gss_keyex = 0;
2012 if (options->gss_deleg_creds == -1) 2067 if (options->gss_deleg_creds == -1)
2013 options->gss_deleg_creds = 0; 2068 options->gss_deleg_creds = 0;
2069 if (options->gss_trust_dns == -1)
2070 options->gss_trust_dns = 0;
2071 if (options->gss_renewal_rekey == -1)
2072 options->gss_renewal_rekey = 0;
2073#ifdef GSSAPI
2074 if (options->gss_kex_algorithms == NULL)
2075 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
2076#endif
2014 if (options->password_authentication == -1) 2077 if (options->password_authentication == -1)
2015 options->password_authentication = 1; 2078 options->password_authentication = 1;
2016 if (options->kbd_interactive_authentication == -1) 2079 if (options->kbd_interactive_authentication == -1)
@@ -2625,7 +2688,14 @@ dump_client_config(Options *o, const char *host)
2625 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); 2688 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
2626#ifdef GSSAPI 2689#ifdef GSSAPI
2627 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); 2690 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
2691 dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
2628 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); 2692 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
2693 dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
2694 dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
2695 dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
2696 dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
2697 dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
2698 o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
2629#endif /* GSSAPI */ 2699#endif /* GSSAPI */
2630 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); 2700 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
2631 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); 2701 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
diff --git a/readconf.h b/readconf.h
index 8e36bf32a..0bff6d80a 100644
--- a/readconf.h
+++ b/readconf.h
@@ -40,7 +40,13 @@ typedef struct {
40 int challenge_response_authentication; 40 int challenge_response_authentication;
41 /* Try S/Key or TIS, authentication. */ 41 /* Try S/Key or TIS, authentication. */
42 int gss_authentication; /* Try GSS authentication */ 42 int gss_authentication; /* Try GSS authentication */
43 int gss_keyex; /* Try GSS key exchange */
43 int gss_deleg_creds; /* Delegate GSS credentials */ 44 int gss_deleg_creds; /* Delegate GSS credentials */
45 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
46 int gss_renewal_rekey; /* Credential renewal forces rekey */
47 char *gss_client_identity; /* Principal to initiate GSSAPI with */
48 char *gss_server_identity; /* GSSAPI target principal */
49 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
44 int password_authentication; /* Try password 50 int password_authentication; /* Try password
45 * authentication. */ 51 * authentication. */
46 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 52 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index e76f9c39e..f63eb0b94 100644
--- a/servconf.c
+++ b/servconf.c
@@ -64,6 +64,7 @@
64#include "auth.h" 64#include "auth.h"
65#include "myproposal.h" 65#include "myproposal.h"
66#include "digest.h" 66#include "digest.h"
67#include "ssh-gss.h"
67 68
68static void add_listen_addr(ServerOptions *, const char *, 69static void add_listen_addr(ServerOptions *, const char *,
69 const char *, int); 70 const char *, int);
@@ -124,8 +125,11 @@ initialize_server_options(ServerOptions *options)
124 options->kerberos_ticket_cleanup = -1; 125 options->kerberos_ticket_cleanup = -1;
125 options->kerberos_get_afs_token = -1; 126 options->kerberos_get_afs_token = -1;
126 options->gss_authentication=-1; 127 options->gss_authentication=-1;
128 options->gss_keyex = -1;
127 options->gss_cleanup_creds = -1; 129 options->gss_cleanup_creds = -1;
128 options->gss_strict_acceptor = -1; 130 options->gss_strict_acceptor = -1;
131 options->gss_store_rekey = -1;
132 options->gss_kex_algorithms = NULL;
129 options->password_authentication = -1; 133 options->password_authentication = -1;
130 options->kbd_interactive_authentication = -1; 134 options->kbd_interactive_authentication = -1;
131 options->challenge_response_authentication = -1; 135 options->challenge_response_authentication = -1;
@@ -351,10 +355,18 @@ fill_default_server_options(ServerOptions *options)
351 options->kerberos_get_afs_token = 0; 355 options->kerberos_get_afs_token = 0;
352 if (options->gss_authentication == -1) 356 if (options->gss_authentication == -1)
353 options->gss_authentication = 0; 357 options->gss_authentication = 0;
358 if (options->gss_keyex == -1)
359 options->gss_keyex = 0;
354 if (options->gss_cleanup_creds == -1) 360 if (options->gss_cleanup_creds == -1)
355 options->gss_cleanup_creds = 1; 361 options->gss_cleanup_creds = 1;
356 if (options->gss_strict_acceptor == -1) 362 if (options->gss_strict_acceptor == -1)
357 options->gss_strict_acceptor = 1; 363 options->gss_strict_acceptor = 1;
364 if (options->gss_store_rekey == -1)
365 options->gss_store_rekey = 0;
366#ifdef GSSAPI
367 if (options->gss_kex_algorithms == NULL)
368 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
369#endif
358 if (options->password_authentication == -1) 370 if (options->password_authentication == -1)
359 options->password_authentication = 1; 371 options->password_authentication = 1;
360 if (options->kbd_interactive_authentication == -1) 372 if (options->kbd_interactive_authentication == -1)
@@ -498,6 +510,7 @@ typedef enum {
498 sHostKeyAlgorithms, 510 sHostKeyAlgorithms,
499 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 511 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
500 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 512 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
513 sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
501 sAcceptEnv, sSetEnv, sPermitTunnel, 514 sAcceptEnv, sSetEnv, sPermitTunnel,
502 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, 515 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
503 sUsePrivilegeSeparation, sAllowAgentForwarding, 516 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -572,12 +585,22 @@ static struct {
572#ifdef GSSAPI 585#ifdef GSSAPI
573 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 586 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
574 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 587 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
588 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
575 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 589 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
590 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
591 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
592 { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
576#else 593#else
577 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 594 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
578 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 595 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
596 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
579 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 597 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
598 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
599 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
600 { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
580#endif 601#endif
602 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
603 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
581 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 604 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
582 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 605 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
583 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 606 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1488,6 +1511,10 @@ process_server_config_line(ServerOptions *options, char *line,
1488 intptr = &options->gss_authentication; 1511 intptr = &options->gss_authentication;
1489 goto parse_flag; 1512 goto parse_flag;
1490 1513
1514 case sGssKeyEx:
1515 intptr = &options->gss_keyex;
1516 goto parse_flag;
1517
1491 case sGssCleanupCreds: 1518 case sGssCleanupCreds:
1492 intptr = &options->gss_cleanup_creds; 1519 intptr = &options->gss_cleanup_creds;
1493 goto parse_flag; 1520 goto parse_flag;
@@ -1496,6 +1523,22 @@ process_server_config_line(ServerOptions *options, char *line,
1496 intptr = &options->gss_strict_acceptor; 1523 intptr = &options->gss_strict_acceptor;
1497 goto parse_flag; 1524 goto parse_flag;
1498 1525
1526 case sGssStoreRekey:
1527 intptr = &options->gss_store_rekey;
1528 goto parse_flag;
1529
1530 case sGssKexAlgorithms:
1531 arg = strdelim(&cp);
1532 if (!arg || *arg == '\0')
1533 fatal("%.200s line %d: Missing argument.",
1534 filename, linenum);
1535 if (!kex_gss_names_valid(arg))
1536 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1537 filename, linenum, arg ? arg : "<NONE>");
1538 if (*activep && options->gss_kex_algorithms == NULL)
1539 options->gss_kex_algorithms = xstrdup(arg);
1540 break;
1541
1499 case sPasswordAuthentication: 1542 case sPasswordAuthentication:
1500 intptr = &options->password_authentication; 1543 intptr = &options->password_authentication;
1501 goto parse_flag; 1544 goto parse_flag;
@@ -2585,6 +2628,10 @@ dump_config(ServerOptions *o)
2585#ifdef GSSAPI 2628#ifdef GSSAPI
2586 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2629 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2587 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2630 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2631 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2632 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2633 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2634 dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
2588#endif 2635#endif
2589 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2636 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2590 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2637 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 5483da051..29329ba1f 100644
--- a/servconf.h
+++ b/servconf.h
@@ -126,8 +126,11 @@ typedef struct {
126 int kerberos_get_afs_token; /* If true, try to get AFS token if 126 int kerberos_get_afs_token; /* If true, try to get AFS token if
127 * authenticated with Kerberos. */ 127 * authenticated with Kerberos. */
128 int gss_authentication; /* If true, permit GSSAPI authentication */ 128 int gss_authentication; /* If true, permit GSSAPI authentication */
129 int gss_keyex; /* If true, permit GSSAPI key exchange */
129 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 130 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
130 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 131 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
132 int gss_store_rekey;
133 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
131 int password_authentication; /* If true, permit password 134 int password_authentication; /* If true, permit password
132 * authentication. */ 135 * authentication. */
133 int kbd_interactive_authentication; /* If true, permit */ 136 int kbd_interactive_authentication; /* If true, permit */
diff --git a/session.c b/session.c
index 8f5d7e0a4..f1a47f766 100644
--- a/session.c
+++ b/session.c
@@ -2674,13 +2674,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
2674 2674
2675#ifdef KRB5 2675#ifdef KRB5
2676 if (options.kerberos_ticket_cleanup && 2676 if (options.kerberos_ticket_cleanup &&
2677 authctxt->krb5_ctx) 2677 authctxt->krb5_ctx) {
2678 temporarily_use_uid(authctxt->pw);
2678 krb5_cleanup_proc(authctxt); 2679 krb5_cleanup_proc(authctxt);
2680 restore_uid();
2681 }
2679#endif 2682#endif
2680 2683
2681#ifdef GSSAPI 2684#ifdef GSSAPI
2682 if (options.gss_cleanup_creds) 2685 if (options.gss_cleanup_creds) {
2686 temporarily_use_uid(authctxt->pw);
2683 ssh_gssapi_cleanup_creds(); 2687 ssh_gssapi_cleanup_creds();
2688 restore_uid();
2689 }
2684#endif 2690#endif
2685 2691
2686 /* remove agent socket */ 2692 /* remove agent socket */
diff --git a/ssh-gss.h b/ssh-gss.h
index 36180d07a..70dd36658 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */ 1/* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 3 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
@@ -61,10 +61,30 @@
61 61
62#define SSH_GSS_OIDTYPE 0x06 62#define SSH_GSS_OIDTYPE 0x06
63 63
64#define SSH2_MSG_KEXGSS_INIT 30
65#define SSH2_MSG_KEXGSS_CONTINUE 31
66#define SSH2_MSG_KEXGSS_COMPLETE 32
67#define SSH2_MSG_KEXGSS_HOSTKEY 33
68#define SSH2_MSG_KEXGSS_ERROR 34
69#define SSH2_MSG_KEXGSS_GROUPREQ 40
70#define SSH2_MSG_KEXGSS_GROUP 41
71#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
72#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
73#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-"
74#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-"
75#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
76#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-"
77#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-"
78
79#define GSS_KEX_DEFAULT_KEX \
80 KEX_GSS_GEX_SHA1_ID "," \
81 KEX_GSS_GRP14_SHA1_ID
82
64typedef struct { 83typedef struct {
65 char *filename; 84 char *filename;
66 char *envvar; 85 char *envvar;
67 char *envval; 86 char *envval;
87 struct passwd *owner;
68 void *data; 88 void *data;
69} ssh_gssapi_ccache; 89} ssh_gssapi_ccache;
70 90
@@ -72,8 +92,11 @@ typedef struct {
72 gss_buffer_desc displayname; 92 gss_buffer_desc displayname;
73 gss_buffer_desc exportedname; 93 gss_buffer_desc exportedname;
74 gss_cred_id_t creds; 94 gss_cred_id_t creds;
95 gss_name_t name;
75 struct ssh_gssapi_mech_struct *mech; 96 struct ssh_gssapi_mech_struct *mech;
76 ssh_gssapi_ccache store; 97 ssh_gssapi_ccache store;
98 int used;
99 int updated;
77} ssh_gssapi_client; 100} ssh_gssapi_client;
78 101
79typedef struct ssh_gssapi_mech_struct { 102typedef struct ssh_gssapi_mech_struct {
@@ -84,6 +107,7 @@ typedef struct ssh_gssapi_mech_struct {
84 int (*userok) (ssh_gssapi_client *, char *); 107 int (*userok) (ssh_gssapi_client *, char *);
85 int (*localname) (ssh_gssapi_client *, char **); 108 int (*localname) (ssh_gssapi_client *, char **);
86 void (*storecreds) (ssh_gssapi_client *); 109 void (*storecreds) (ssh_gssapi_client *);
110 int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
87} ssh_gssapi_mech; 111} ssh_gssapi_mech;
88 112
89typedef struct { 113typedef struct {
@@ -94,10 +118,11 @@ typedef struct {
94 gss_OID oid; /* client */ 118 gss_OID oid; /* client */
95 gss_cred_id_t creds; /* server */ 119 gss_cred_id_t creds; /* server */
96 gss_name_t client; /* server */ 120 gss_name_t client; /* server */
97 gss_cred_id_t client_creds; /* server */ 121 gss_cred_id_t client_creds; /* both */
98} Gssctxt; 122} Gssctxt;
99 123
100extern ssh_gssapi_mech *supported_mechs[]; 124extern ssh_gssapi_mech *supported_mechs[];
125extern Gssctxt *gss_kex_context;
101 126
102int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); 127int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
103void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); 128void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
109 134
110struct sshbuf; 135struct sshbuf;
111int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *); 136int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
137int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
112 138
113OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *); 139OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
114OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int, 140OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
@@ -123,17 +149,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
123OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 149OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
124void ssh_gssapi_buildmic(struct sshbuf *, const char *, 150void ssh_gssapi_buildmic(struct sshbuf *, const char *,
125 const char *, const char *); 151 const char *, const char *);
126int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 152int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
153OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
154int ssh_gssapi_credentials_updated(Gssctxt *);
127 155
128/* In the server */ 156/* In the server */
157typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
158 const char *);
159char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
160char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
161 const char *, const char *);
162gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
163int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
164 const char *);
129OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 165OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
130int ssh_gssapi_userok(char *name); 166int ssh_gssapi_userok(char *name, struct passwd *, int kex);
131OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 167OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
132void ssh_gssapi_do_child(char ***, u_int *); 168void ssh_gssapi_do_child(char ***, u_int *);
133void ssh_gssapi_cleanup_creds(void); 169void ssh_gssapi_cleanup_creds(void);
134void ssh_gssapi_storecreds(void); 170void ssh_gssapi_storecreds(void);
135const char *ssh_gssapi_displayname(void); 171const char *ssh_gssapi_displayname(void);
136 172
173char *ssh_gssapi_server_mechanisms(void);
174int ssh_gssapi_oid_table_ok(void);
175
176int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
177void ssh_gssapi_rekey_creds(void);
178
137#endif /* GSSAPI */ 179#endif /* GSSAPI */
138 180
139#endif /* _SSH_GSS_H */ 181#endif /* _SSH_GSS_H */
diff --git a/ssh.1 b/ssh.1
index 424d6c3e8..26940ad55 100644
--- a/ssh.1
+++ b/ssh.1
@@ -497,7 +497,13 @@ For full details of the options listed below, and their possible values, see
497.It GatewayPorts 497.It GatewayPorts
498.It GlobalKnownHostsFile 498.It GlobalKnownHostsFile
499.It GSSAPIAuthentication 499.It GSSAPIAuthentication
500.It GSSAPIKeyExchange
501.It GSSAPIClientIdentity
500.It GSSAPIDelegateCredentials 502.It GSSAPIDelegateCredentials
503.It GSSAPIKexAlgorithms
504.It GSSAPIRenewalForcesRekey
505.It GSSAPIServerIdentity
506.It GSSAPITrustDns
501.It HashKnownHosts 507.It HashKnownHosts
502.It Host 508.It Host
503.It HostbasedAuthentication 509.It HostbasedAuthentication
@@ -573,6 +579,8 @@ flag),
573(supported message integrity codes), 579(supported message integrity codes),
574.Ar kex 580.Ar kex
575(key exchange algorithms), 581(key exchange algorithms),
582.Ar kex-gss
583(GSSAPI key exchange algorithms),
576.Ar key 584.Ar key
577(key types), 585(key types),
578.Ar key-cert 586.Ar key-cert
diff --git a/ssh.c b/ssh.c
index ee51823cd..2da9f5d0d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -736,6 +736,8 @@ main(int ac, char **av)
736 cp = mac_alg_list('\n'); 736 cp = mac_alg_list('\n');
737 else if (strcmp(optarg, "kex") == 0) 737 else if (strcmp(optarg, "kex") == 0)
738 cp = kex_alg_list('\n'); 738 cp = kex_alg_list('\n');
739 else if (strcmp(optarg, "kex-gss") == 0)
740 cp = kex_gss_alg_list('\n');
739 else if (strcmp(optarg, "key") == 0) 741 else if (strcmp(optarg, "key") == 0)
740 cp = sshkey_alg_list(0, 0, 0, '\n'); 742 cp = sshkey_alg_list(0, 0, 0, '\n');
741 else if (strcmp(optarg, "key-cert") == 0) 743 else if (strcmp(optarg, "key-cert") == 0)
@@ -748,7 +750,7 @@ main(int ac, char **av)
748 cp = xstrdup("2"); 750 cp = xstrdup("2");
749 else if (strcmp(optarg, "help") == 0) { 751 else if (strcmp(optarg, "help") == 0) {
750 cp = xstrdup( 752 cp = xstrdup(
751 "cipher\ncipher-auth\nkex\nkey\n" 753 "cipher\ncipher-auth\nkex\nkex-gss\nkey\n"
752 "key-cert\nkey-plain\nmac\n" 754 "key-cert\nkey-plain\nmac\n"
753 "protocol-version\nsig"); 755 "protocol-version\nsig");
754 } 756 }
diff --git a/ssh_config b/ssh_config
index 5e8ef548b..1ff999b68 100644
--- a/ssh_config
+++ b/ssh_config
@@ -24,6 +24,8 @@
24# HostbasedAuthentication no 24# HostbasedAuthentication no
25# GSSAPIAuthentication no 25# GSSAPIAuthentication no
26# GSSAPIDelegateCredentials no 26# GSSAPIDelegateCredentials no
27# GSSAPIKeyExchange no
28# GSSAPITrustDNS no
27# BatchMode no 29# BatchMode no
28# CheckHostIP yes 30# CheckHostIP yes
29# AddressFamily any 31# AddressFamily any
diff --git a/ssh_config.5 b/ssh_config.5
index 02a87892d..f4668673b 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -758,10 +758,67 @@ The default is
758Specifies whether user authentication based on GSSAPI is allowed. 758Specifies whether user authentication based on GSSAPI is allowed.
759The default is 759The default is
760.Cm no . 760.Cm no .
761.It Cm GSSAPIClientIdentity
762If set, specifies the GSSAPI client identity that ssh should use when
763connecting to the server. The default is unset, which means that the default
764identity will be used.
761.It Cm GSSAPIDelegateCredentials 765.It Cm GSSAPIDelegateCredentials
762Forward (delegate) credentials to the server. 766Forward (delegate) credentials to the server.
763The default is 767The default is
764.Cm no . 768.Cm no .
769.It Cm GSSAPIKeyExchange
770Specifies whether key exchange based on GSSAPI may be used. When using
771GSSAPI key exchange the server need not have a host key.
772The default is
773.Dq no .
774.It Cm GSSAPIRenewalForcesRekey
775If set to
776.Dq yes
777then renewal of the client's GSSAPI credentials will force the rekeying of the
778ssh connection. With a compatible server, this will delegate the renewed
779credentials to a session on the server.
780.Pp
781Checks are made to ensure that credentials are only propagated when the new
782credentials match the old ones on the originating client and where the
783receiving server still has the old set in its cache.
784.Pp
785The default is
786.Dq no .
787.Pp
788For this to work
789.Cm GSSAPIKeyExchange
790needs to be enabled in the server and also used by the client.
791.It Cm GSSAPIServerIdentity
792If set, specifies the GSSAPI server identity that ssh should expect when
793connecting to the server. The default is unset, which means that the
794expected GSSAPI server identity will be determined from the target
795hostname.
796.It Cm GSSAPITrustDns
797Set to
798.Dq yes
799to indicate that the DNS is trusted to securely canonicalize
800the name of the host being connected to. If
801.Dq no ,
802the hostname entered on the
803command line will be passed untouched to the GSSAPI library.
804The default is
805.Dq no .
806.It Cm GSSAPIKexAlgorithms
807The list of key exchange algorithms that are offered for GSSAPI
808key exchange. Possible values are
809.Bd -literal -offset 3n
810gss-gex-sha1-,
811gss-group1-sha1-,
812gss-group14-sha1-,
813gss-group14-sha256-,
814gss-group16-sha512-,
815gss-nistp256-sha256-,
816gss-curve25519-sha256-
817.Ed
818.Pp
819The default is
820.Dq gss-gex-sha1-,gss-group14-sha1- .
821This option only applies to protocol version 2 connections using GSSAPI.
765.It Cm HashKnownHosts 822.It Cm HashKnownHosts
766Indicates that 823Indicates that
767.Xr ssh 1 824.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 87fa70a40..a4ec75ca1 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -78,8 +78,6 @@
78#endif 78#endif
79 79
80/* import */ 80/* import */
81extern char *client_version_string;
82extern char *server_version_string;
83extern Options options; 81extern Options options;
84 82
85/* 83/*
@@ -161,6 +159,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
161 char *s, *all_key; 159 char *s, *all_key;
162 int r; 160 int r;
163 161
162#if defined(GSSAPI) && defined(WITH_OPENSSL)
163 char *orig = NULL, *gss = NULL;
164 char *gss_host = NULL;
165#endif
166
164 xxx_host = host; 167 xxx_host = host;
165 xxx_hostaddr = hostaddr; 168 xxx_hostaddr = hostaddr;
166 169
@@ -193,6 +196,35 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
193 order_hostkeyalgs(host, hostaddr, port)); 196 order_hostkeyalgs(host, hostaddr, port));
194 } 197 }
195 198
199#if defined(GSSAPI) && defined(WITH_OPENSSL)
200 if (options.gss_keyex) {
201 /* Add the GSSAPI mechanisms currently supported on this
202 * client to the key exchange algorithm proposal */
203 orig = myproposal[PROPOSAL_KEX_ALGS];
204
205 if (options.gss_server_identity)
206 gss_host = xstrdup(options.gss_server_identity);
207 else if (options.gss_trust_dns)
208 gss_host = remote_hostname(ssh);
209 else
210 gss_host = xstrdup(host);
211
212 gss = ssh_gssapi_client_mechanisms(gss_host,
213 options.gss_client_identity, options.gss_kex_algorithms);
214 if (gss) {
215 debug("Offering GSSAPI proposal: %s", gss);
216 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
217 "%s,%s", gss, orig);
218
219 /* If we've got GSSAPI algorithms, then we also support the
220 * 'null' hostkey, as a last resort */
221 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
222 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
223 "%s,null", orig);
224 }
225 }
226#endif
227
196 if (options.rekey_limit || options.rekey_interval) 228 if (options.rekey_limit || options.rekey_interval)
197 ssh_packet_set_rekey_limits(ssh, options.rekey_limit, 229 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
198 options.rekey_interval); 230 options.rekey_interval);
@@ -211,16 +243,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
211# ifdef OPENSSL_HAS_ECC 243# ifdef OPENSSL_HAS_ECC
212 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 244 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
213# endif 245# endif
246# ifdef GSSAPI
247 if (options.gss_keyex) {
248 ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
249 ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
250 ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
251 ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
252 ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
253 ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
254 ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
255 }
256# endif
214#endif 257#endif
215 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 258 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
216 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; 259 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
217 ssh->kex->verify_host_key=&verify_host_key_callback; 260 ssh->kex->verify_host_key=&verify_host_key_callback;
218 261
262#if defined(GSSAPI) && defined(WITH_OPENSSL)
263 if (options.gss_keyex) {
264 ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
265 ssh->kex->gss_trust_dns = options.gss_trust_dns;
266 ssh->kex->gss_client = options.gss_client_identity;
267 ssh->kex->gss_host = gss_host;
268 }
269#endif
270
219 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); 271 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
220 272
221 /* remove ext-info from the KEX proposals for rekeying */ 273 /* remove ext-info from the KEX proposals for rekeying */
222 myproposal[PROPOSAL_KEX_ALGS] = 274 myproposal[PROPOSAL_KEX_ALGS] =
223 compat_kex_proposal(options.kex_algorithms); 275 compat_kex_proposal(options.kex_algorithms);
276#if defined(GSSAPI) && defined(WITH_OPENSSL)
277 /* repair myproposal after it was crumpled by the */
278 /* ext-info removal above */
279 if (gss) {
280 orig = myproposal[PROPOSAL_KEX_ALGS];
281 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
282 "%s,%s", gss, orig);
283 free(gss);
284 }
285#endif
224 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) 286 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
225 fatal("kex_prop2buf: %s", ssh_err(r)); 287 fatal("kex_prop2buf: %s", ssh_err(r));
226 288
@@ -317,6 +379,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
317static int input_gssapi_token(int type, u_int32_t, struct ssh *); 379static int input_gssapi_token(int type, u_int32_t, struct ssh *);
318static int input_gssapi_error(int, u_int32_t, struct ssh *); 380static int input_gssapi_error(int, u_int32_t, struct ssh *);
319static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 381static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
382static int userauth_gsskeyex(struct ssh *);
320#endif 383#endif
321 384
322void userauth(struct ssh *, char *); 385void userauth(struct ssh *, char *);
@@ -333,6 +396,11 @@ static char *authmethods_get(void);
333 396
334Authmethod authmethods[] = { 397Authmethod authmethods[] = {
335#ifdef GSSAPI 398#ifdef GSSAPI
399 {"gssapi-keyex",
400 userauth_gsskeyex,
401 NULL,
402 &options.gss_keyex,
403 NULL},
336 {"gssapi-with-mic", 404 {"gssapi-with-mic",
337 userauth_gssapi, 405 userauth_gssapi,
338 userauth_gssapi_cleanup, 406 userauth_gssapi_cleanup,
@@ -697,12 +765,25 @@ userauth_gssapi(struct ssh *ssh)
697 OM_uint32 min; 765 OM_uint32 min;
698 int r, ok = 0; 766 int r, ok = 0;
699 gss_OID mech = NULL; 767 gss_OID mech = NULL;
768 char *gss_host;
769
770 if (options.gss_server_identity)
771 gss_host = xstrdup(options.gss_server_identity);
772 else if (options.gss_trust_dns)
773 gss_host = remote_hostname(ssh);
774 else
775 gss_host = xstrdup(authctxt->host);
700 776
701 /* Try one GSSAPI method at a time, rather than sending them all at 777 /* Try one GSSAPI method at a time, rather than sending them all at
702 * once. */ 778 * once. */
703 779
704 if (authctxt->gss_supported_mechs == NULL) 780 if (authctxt->gss_supported_mechs == NULL)
705 gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 781 if (GSS_ERROR(gss_indicate_mechs(&min,
782 &authctxt->gss_supported_mechs))) {
783 authctxt->gss_supported_mechs = NULL;
784 free(gss_host);
785 return 0;
786 }
706 787
707 /* Check to see whether the mechanism is usable before we offer it */ 788 /* Check to see whether the mechanism is usable before we offer it */
708 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 789 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
@@ -711,13 +792,15 @@ userauth_gssapi(struct ssh *ssh)
711 elements[authctxt->mech_tried]; 792 elements[authctxt->mech_tried];
712 /* My DER encoding requires length<128 */ 793 /* My DER encoding requires length<128 */
713 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 794 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
714 mech, authctxt->host)) { 795 mech, gss_host, options.gss_client_identity)) {
715 ok = 1; /* Mechanism works */ 796 ok = 1; /* Mechanism works */
716 } else { 797 } else {
717 authctxt->mech_tried++; 798 authctxt->mech_tried++;
718 } 799 }
719 } 800 }
720 801
802 free(gss_host);
803
721 if (!ok || mech == NULL) 804 if (!ok || mech == NULL)
722 return 0; 805 return 0;
723 806
@@ -957,6 +1040,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
957 free(lang); 1040 free(lang);
958 return r; 1041 return r;
959} 1042}
1043
1044int
1045userauth_gsskeyex(struct ssh *ssh)
1046{
1047 struct sshbuf *b = NULL;
1048 Authctxt *authctxt = ssh->authctxt;
1049 gss_buffer_desc gssbuf;
1050 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1051 OM_uint32 ms;
1052 int r;
1053
1054 static int attempt = 0;
1055 if (attempt++ >= 1)
1056 return (0);
1057
1058 if (gss_kex_context == NULL) {
1059 debug("No valid Key exchange context");
1060 return (0);
1061 }
1062
1063 if ((b = sshbuf_new()) == NULL)
1064 fatal("%s: sshbuf_new failed", __func__);
1065
1066 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1067 "gssapi-keyex");
1068
1069 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1070 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1071 gssbuf.length = sshbuf_len(b);
1072
1073 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1074 sshbuf_free(b);
1075 return (0);
1076 }
1077
1078 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1079 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1080 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1081 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1082 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1083 (r = sshpkt_send(ssh)) != 0)
1084 fatal("%s: %s", __func__, ssh_err(r));
1085
1086 sshbuf_free(b);
1087 gss_release_buffer(&ms, &mic);
1088
1089 return (1);
1090}
1091
960#endif /* GSSAPI */ 1092#endif /* GSSAPI */
961 1093
962static int 1094static int
diff --git a/sshd.c b/sshd.c
index 11571c010..3a5c1ea78 100644
--- a/sshd.c
+++ b/sshd.c
@@ -123,6 +123,10 @@
123#include "version.h" 123#include "version.h"
124#include "ssherr.h" 124#include "ssherr.h"
125 125
126#ifdef USE_SECURITY_SESSION_API
127#include <Security/AuthSession.h>
128#endif
129
126/* Re-exec fds */ 130/* Re-exec fds */
127#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) 131#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
128#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) 132#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
@@ -796,8 +800,8 @@ notify_hostkeys(struct ssh *ssh)
796 } 800 }
797 debug3("%s: sent %u hostkeys", __func__, nkeys); 801 debug3("%s: sent %u hostkeys", __func__, nkeys);
798 if (nkeys == 0) 802 if (nkeys == 0)
799 fatal("%s: no hostkeys", __func__); 803 debug3("%s: no hostkeys", __func__);
800 if ((r = sshpkt_send(ssh)) != 0) 804 else if ((r = sshpkt_send(ssh)) != 0)
801 sshpkt_fatal(ssh, r, "%s: send", __func__); 805 sshpkt_fatal(ssh, r, "%s: send", __func__);
802 sshbuf_free(buf); 806 sshbuf_free(buf);
803} 807}
@@ -1773,7 +1777,8 @@ main(int ac, char **av)
1773 free(fp); 1777 free(fp);
1774 } 1778 }
1775 accumulate_host_timing_secret(cfg, NULL); 1779 accumulate_host_timing_secret(cfg, NULL);
1776 if (!sensitive_data.have_ssh2_key) { 1780 /* The GSSAPI key exchange can run without a host key */
1781 if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
1777 logit("sshd: no hostkeys available -- exiting."); 1782 logit("sshd: no hostkeys available -- exiting.");
1778 exit(1); 1783 exit(1);
1779 } 1784 }
@@ -2069,6 +2074,60 @@ main(int ac, char **av)
2069 rdomain == NULL ? "" : "\""); 2074 rdomain == NULL ? "" : "\"");
2070 free(laddr); 2075 free(laddr);
2071 2076
2077#ifdef USE_SECURITY_SESSION_API
2078 /*
2079 * Create a new security session for use by the new user login if
2080 * the current session is the root session or we are not launched
2081 * by inetd (eg: debugging mode or server mode). We do not
2082 * necessarily need to create a session if we are launched from
2083 * inetd because Panther xinetd will create a session for us.
2084 *
2085 * The only case where this logic will fail is if there is an
2086 * inetd running in a non-root session which is not creating
2087 * new sessions for us. Then all the users will end up in the
2088 * same session (bad).
2089 *
2090 * When the client exits, the session will be destroyed for us
2091 * automatically.
2092 *
2093 * We must create the session before any credentials are stored
2094 * (including AFS pags, which happens a few lines below).
2095 */
2096 {
2097 OSStatus err = 0;
2098 SecuritySessionId sid = 0;
2099 SessionAttributeBits sattrs = 0;
2100
2101 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2102 if (err)
2103 error("SessionGetInfo() failed with error %.8X",
2104 (unsigned) err);
2105 else
2106 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2107 (unsigned) sid, (unsigned) sattrs);
2108
2109 if (inetd_flag && !(sattrs & sessionIsRoot))
2110 debug("Running in inetd mode in a non-root session... "
2111 "assuming inetd created the session for us.");
2112 else {
2113 debug("Creating new security session...");
2114 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2115 if (err)
2116 error("SessionCreate() failed with error %.8X",
2117 (unsigned) err);
2118
2119 err = SessionGetInfo(callerSecuritySession, &sid,
2120 &sattrs);
2121 if (err)
2122 error("SessionGetInfo() failed with error %.8X",
2123 (unsigned) err);
2124 else
2125 debug("New Session ID is %.8X / Session Attributes are %.8X",
2126 (unsigned) sid, (unsigned) sattrs);
2127 }
2128 }
2129#endif
2130
2072 /* 2131 /*
2073 * We don't want to listen forever unless the other side 2132 * We don't want to listen forever unless the other side
2074 * successfully authenticates itself. So we set up an alarm which is 2133 * successfully authenticates itself. So we set up an alarm which is
@@ -2265,6 +2324,48 @@ do_ssh2_kex(struct ssh *ssh)
2265 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2324 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2266 list_hostkey_types()); 2325 list_hostkey_types());
2267 2326
2327#if defined(GSSAPI) && defined(WITH_OPENSSL)
2328 {
2329 char *orig;
2330 char *gss = NULL;
2331 char *newstr = NULL;
2332 orig = myproposal[PROPOSAL_KEX_ALGS];
2333
2334 /*
2335 * If we don't have a host key, then there's no point advertising
2336 * the other key exchange algorithms
2337 */
2338
2339 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2340 orig = NULL;
2341
2342 if (options.gss_keyex)
2343 gss = ssh_gssapi_server_mechanisms();
2344 else
2345 gss = NULL;
2346
2347 if (gss && orig)
2348 xasprintf(&newstr, "%s,%s", gss, orig);
2349 else if (gss)
2350 newstr = gss;
2351 else if (orig)
2352 newstr = orig;
2353
2354 /*
2355 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2356 * key alg, but we can't tell people about it unless its the only
2357 * host key algorithm we support
2358 */
2359 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2360 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2361
2362 if (newstr)
2363 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2364 else
2365 fatal("No supported key exchange algorithms");
2366 }
2367#endif
2368
2268 /* start key exchange */ 2369 /* start key exchange */
2269 if ((r = kex_setup(ssh, myproposal)) != 0) 2370 if ((r = kex_setup(ssh, myproposal)) != 0)
2270 fatal("kex_setup: %s", ssh_err(r)); 2371 fatal("kex_setup: %s", ssh_err(r));
@@ -2280,7 +2381,18 @@ do_ssh2_kex(struct ssh *ssh)
2280# ifdef OPENSSL_HAS_ECC 2381# ifdef OPENSSL_HAS_ECC
2281 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 2382 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2282# endif 2383# endif
2283#endif 2384# ifdef GSSAPI
2385 if (options.gss_keyex) {
2386 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2387 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2388 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2389 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2390 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2391 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2392 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2393 }
2394# endif
2395#endif /* WITH_OPENSSL */
2284 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 2396 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2285 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 2397 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2286 kex->load_host_public_key=&get_hostkey_public_by_type; 2398 kex->load_host_public_key=&get_hostkey_public_by_type;
diff --git a/sshd_config b/sshd_config
index 19b7c91a1..2c48105f8 100644
--- a/sshd_config
+++ b/sshd_config
@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys
69# GSSAPI options 69# GSSAPI options
70#GSSAPIAuthentication no 70#GSSAPIAuthentication no
71#GSSAPICleanupCredentials yes 71#GSSAPICleanupCredentials yes
72#GSSAPIStrictAcceptorCheck yes
73#GSSAPIKeyExchange no
72 74
73# Set this to 'yes' to enable PAM authentication, account processing, 75# Set this to 'yes' to enable PAM authentication, account processing,
74# and session processing. If this is enabled, PAM authentication will 76# and session processing. If this is enabled, PAM authentication will
diff --git a/sshd_config.5 b/sshd_config.5
index 9486f2a1c..cec3c3c4e 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -655,6 +655,11 @@ Specifies whether to automatically destroy the user's credentials cache
655on logout. 655on logout.
656The default is 656The default is
657.Cm yes . 657.Cm yes .
658.It Cm GSSAPIKeyExchange
659Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
660doesn't rely on ssh keys to verify host identity.
661The default is
662.Cm no .
658.It Cm GSSAPIStrictAcceptorCheck 663.It Cm GSSAPIStrictAcceptorCheck
659Determines whether to be strict about the identity of the GSSAPI acceptor 664Determines whether to be strict about the identity of the GSSAPI acceptor
660a client authenticates against. 665a client authenticates against.
@@ -669,6 +674,31 @@ machine's default store.
669This facility is provided to assist with operation on multi homed machines. 674This facility is provided to assist with operation on multi homed machines.
670The default is 675The default is
671.Cm yes . 676.Cm yes .
677.It Cm GSSAPIStoreCredentialsOnRekey
678Controls whether the user's GSSAPI credentials should be updated following a
679successful connection rekeying. This option can be used to accepted renewed
680or updated credentials from a compatible client. The default is
681.Dq no .
682.Pp
683For this to work
684.Cm GSSAPIKeyExchange
685needs to be enabled in the server and also used by the client.
686.It Cm GSSAPIKexAlgorithms
687The list of key exchange algorithms that are accepted by GSSAPI
688key exchange. Possible values are
689.Bd -literal -offset 3n
690gss-gex-sha1-,
691gss-group1-sha1-,
692gss-group14-sha1-,
693gss-group14-sha256-,
694gss-group16-sha512-,
695gss-nistp256-sha256-,
696gss-curve25519-sha256-
697.Ed
698.Pp
699The default is
700.Dq gss-gex-sha1-,gss-group14-sha1- .
701This option only applies to protocol version 2 connections using GSSAPI.
672.It Cm HostbasedAcceptedKeyTypes 702.It Cm HostbasedAcceptedKeyTypes
673Specifies the key types that will be accepted for hostbased authentication 703Specifies the key types that will be accepted for hostbased authentication
674as a list of comma-separated patterns. 704as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index ef90563b3..4d2048b6a 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -145,6 +145,7 @@ static const struct keytype keytypes[] = {
145# endif /* OPENSSL_HAS_NISTP521 */ 145# endif /* OPENSSL_HAS_NISTP521 */
146# endif /* OPENSSL_HAS_ECC */ 146# endif /* OPENSSL_HAS_ECC */
147#endif /* WITH_OPENSSL */ 147#endif /* WITH_OPENSSL */
148 { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
148 { NULL, NULL, NULL, -1, -1, 0, 0 } 149 { NULL, NULL, NULL, -1, -1, 0, 0 }
149}; 150};
150 151
@@ -233,7 +234,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
233 const struct keytype *kt; 234 const struct keytype *kt;
234 235
235 for (kt = keytypes; kt->type != -1; kt++) { 236 for (kt = keytypes; kt->type != -1; kt++) {
236 if (kt->name == NULL) 237 if (kt->name == NULL || kt->type == KEY_NULL)
237 continue; 238 continue;
238 if (!include_sigonly && kt->sigonly) 239 if (!include_sigonly && kt->sigonly)
239 continue; 240 continue;
diff --git a/sshkey.h b/sshkey.h
index 1119a7b07..1bf30d055 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -65,6 +65,7 @@ enum sshkey_types {
65 KEY_ED25519_CERT, 65 KEY_ED25519_CERT,
66 KEY_XMSS, 66 KEY_XMSS,
67 KEY_XMSS_CERT, 67 KEY_XMSS_CERT,
68 KEY_NULL,
68 KEY_UNSPEC 69 KEY_UNSPEC
69}; 70};
70 71