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-06-05 07:06:44 +0100
commit7ce79be85036c4b36937f1b1ba85f6094068412c (patch)
treec964917d8395ef5605cff9513aad4458b222beae
parent102062f825fb26a74295a1c089c00c4c4c76b68a (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-06-05 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 6f001bb36..c31821acc 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 \ 120 sftp-server.o sftp-common.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 8696f258e..f7a23afba 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) < 0) {
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 16ae1a363..7417eafa4 100644
--- a/auth2.c
+++ b/auth2.c
@@ -75,6 +75,7 @@ extern Authmethod method_passwd;
75extern Authmethod method_kbdint; 75extern Authmethod method_kbdint;
76extern Authmethod method_hostbased; 76extern Authmethod method_hostbased;
77#ifdef GSSAPI 77#ifdef GSSAPI
78extern Authmethod method_gsskeyex;
78extern Authmethod method_gssapi; 79extern Authmethod method_gssapi;
79#endif 80#endif
80 81
@@ -82,6 +83,7 @@ Authmethod *authmethods[] = {
82 &method_none, 83 &method_none,
83 &method_pubkey, 84 &method_pubkey,
84#ifdef GSSAPI 85#ifdef GSSAPI
86 &method_gsskeyex,
85 &method_gssapi, 87 &method_gssapi,
86#endif 88#endif
87 &method_passwd, 89 &method_passwd,
diff --git a/canohost.c b/canohost.c
index f71a08568..404731d24 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) < 0) {
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 086c0dfe8..9b90c64f3 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
@@ -1374,9 +1378,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1374 break; 1378 break;
1375 1379
1376 /* Do channel operations unless rekeying in progress. */ 1380 /* Do channel operations unless rekeying in progress. */
1377 if (!ssh_packet_is_rekeying(ssh)) 1381 if (!ssh_packet_is_rekeying(ssh)) {
1378 channel_after_select(ssh, readset, writeset); 1382 channel_after_select(ssh, readset, writeset);
1379 1383
1384#ifdef GSSAPI
1385 if (options.gss_renewal_rekey &&
1386 ssh_gssapi_credentials_updated(NULL)) {
1387 debug("credentials updated - forcing rekey");
1388 need_rekeying = 1;
1389 }
1390#endif
1391 }
1392
1380 /* Buffer input from the connection. */ 1393 /* Buffer input from the connection. */
1381 client_process_net_input(ssh, readset); 1394 client_process_net_input(ssh, readset);
1382 1395
diff --git a/configure.ac b/configure.ac
index 30be6c182..2869f7042 100644
--- a/configure.ac
+++ b/configure.ac
@@ -665,6 +665,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
665 [Use tunnel device compatibility to OpenBSD]) 665 [Use tunnel device compatibility to OpenBSD])
666 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 666 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
667 [Prepend the address family to IP tunnel traffic]) 667 [Prepend the address family to IP tunnel traffic])
668 AC_MSG_CHECKING([if we have the Security Authorization Session API])
669 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
670 [SessionCreate(0, 0);],
671 [ac_cv_use_security_session_api="yes"
672 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
673 [platform has the Security Authorization Session API])
674 LIBS="$LIBS -framework Security"
675 AC_MSG_RESULT([yes])],
676 [ac_cv_use_security_session_api="no"
677 AC_MSG_RESULT([no])])
678 AC_MSG_CHECKING([if we have an in-memory credentials cache])
679 AC_TRY_COMPILE(
680 [#include <Kerberos/Kerberos.h>],
681 [cc_context_t c;
682 (void) cc_initialize (&c, 0, NULL, NULL);],
683 [AC_DEFINE([USE_CCAPI], [1],
684 [platform uses an in-memory credentials cache])
685 LIBS="$LIBS -framework Security"
686 AC_MSG_RESULT([yes])
687 if test "x$ac_cv_use_security_session_api" = "xno"; then
688 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
689 fi],
690 [AC_MSG_RESULT([no])]
691 )
668 m4_pattern_allow([AU_IPv]) 692 m4_pattern_allow([AU_IPv])
669 AC_CHECK_DECL([AU_IPv4], [], 693 AC_CHECK_DECL([AU_IPv4], [],
670 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 694 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 1c879640c..a29f32c5c 100644
--- a/hmac.c
+++ b/hmac.c
@@ -19,6 +19,7 @@
19 19
20#include <sys/types.h> 20#include <sys/types.h>
21#include <string.h> 21#include <string.h>
22#include <stdlib.h>
22 23
23#include "sshbuf.h" 24#include "sshbuf.h"
24#include "digest.h" 25#include "digest.h"
diff --git a/kex.c b/kex.c
index 34808b5c3..a2a4794e8 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, -1, -1, -1}, 119 { NULL, -1, -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, -1, -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
@@ -301,6 +335,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
301 return r; 335 return r;
302} 336}
303 337
338/* Validate GSS KEX method name list */
339int
340kex_gss_names_valid(const char *names)
341{
342 char *s, *cp, *p;
343
344 if (names == NULL || *names == '\0')
345 return 0;
346 s = cp = xstrdup(names);
347 for ((p = strsep(&cp, ",")); p && *p != '\0';
348 (p = strsep(&cp, ","))) {
349 if (strncmp(p, "gss-", 4) != 0
350 || kex_alg_by_name(p) == NULL) {
351 error("Unsupported KEX algorithm \"%.100s\"", p);
352 free(s);
353 return 0;
354 }
355 }
356 debug3("gss kex names ok: [%s]", names);
357 free(s);
358 return 1;
359}
360
304/* put algorithm proposal into buffer */ 361/* put algorithm proposal into buffer */
305int 362int
306kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 363kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
@@ -657,6 +714,9 @@ kex_free(struct kex *kex)
657 sshbuf_free(kex->server_version); 714 sshbuf_free(kex->server_version);
658 sshbuf_free(kex->client_pub); 715 sshbuf_free(kex->client_pub);
659 free(kex->session_id); 716 free(kex->session_id);
717#ifdef GSSAPI
718 free(kex->gss_host);
719#endif /* GSSAPI */
660 free(kex->failed_choice); 720 free(kex->failed_choice);
661 free(kex->hostkey_alg); 721 free(kex->hostkey_alg);
662 free(kex->name); 722 free(kex->name);
diff --git a/kex.h b/kex.h
index 6d446d1cc..2d5f1d4ed 100644
--- a/kex.h
+++ b/kex.h
@@ -103,6 +103,15 @@ enum kex_exchange {
103 KEX_ECDH_SHA2, 103 KEX_ECDH_SHA2,
104 KEX_C25519_SHA256, 104 KEX_C25519_SHA256,
105 KEX_KEM_SNTRUP4591761X25519_SHA512, 105 KEX_KEM_SNTRUP4591761X25519_SHA512,
106#ifdef GSSAPI
107 KEX_GSS_GRP1_SHA1,
108 KEX_GSS_GRP14_SHA1,
109 KEX_GSS_GRP14_SHA256,
110 KEX_GSS_GRP16_SHA512,
111 KEX_GSS_GEX_SHA1,
112 KEX_GSS_NISTP256_SHA256,
113 KEX_GSS_C25519_SHA256,
114#endif
106 KEX_MAX 115 KEX_MAX
107}; 116};
108 117
@@ -154,6 +163,12 @@ struct kex {
154 u_int flags; 163 u_int flags;
155 int hash_alg; 164 int hash_alg;
156 int ec_nid; 165 int ec_nid;
166#ifdef GSSAPI
167 int gss_deleg_creds;
168 int gss_trust_dns;
169 char *gss_host;
170 char *gss_client;
171#endif
157 char *failed_choice; 172 char *failed_choice;
158 int (*verify_host_key)(struct sshkey *, struct ssh *); 173 int (*verify_host_key)(struct sshkey *, struct ssh *);
159 struct sshkey *(*load_host_public_key)(int, int, struct ssh *); 174 struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
@@ -175,8 +190,10 @@ struct kex {
175 190
176int kex_names_valid(const char *); 191int kex_names_valid(const char *);
177char *kex_alg_list(char); 192char *kex_alg_list(char);
193char *kex_gss_alg_list(char);
178char *kex_names_cat(const char *, const char *); 194char *kex_names_cat(const char *, const char *);
179int kex_assemble_names(char **, const char *, const char *); 195int kex_assemble_names(char **, const char *, const char *);
196int kex_gss_names_valid(const char *);
180 197
181int kex_exchange_identification(struct ssh *, int, const char *); 198int kex_exchange_identification(struct ssh *, int, const char *);
182 199
@@ -203,6 +220,12 @@ int kexgex_client(struct ssh *);
203int kexgex_server(struct ssh *); 220int kexgex_server(struct ssh *);
204int kex_gen_client(struct ssh *); 221int kex_gen_client(struct ssh *);
205int kex_gen_server(struct ssh *); 222int kex_gen_server(struct ssh *);
223#if defined(GSSAPI) && defined(WITH_OPENSSL)
224int kexgssgex_client(struct ssh *);
225int kexgssgex_server(struct ssh *);
226int kexgss_client(struct ssh *);
227int kexgss_server(struct ssh *);
228#endif
206 229
207int kex_dh_keypair(struct kex *); 230int kex_dh_keypair(struct kex *);
208int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, 231int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
@@ -235,6 +258,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
235 const BIGNUM *, const u_char *, size_t, 258 const BIGNUM *, const u_char *, size_t,
236 u_char *, size_t *); 259 u_char *, size_t *);
237 260
261int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
262 const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
263 const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
264 const struct sshbuf *client_pub, const struct sshbuf *server_pub,
265 const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
266
238void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) 267void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
239 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) 268 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
240 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); 269 __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 2abbb9ef6..569dc83f3 100644
--- a/kexgen.c
+++ b/kexgen.c
@@ -43,7 +43,7 @@
43static int input_kex_gen_init(int, u_int32_t, struct ssh *); 43static int input_kex_gen_init(int, u_int32_t, struct ssh *);
44static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); 44static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
45 45
46static int 46int
47kex_gen_hash( 47kex_gen_hash(
48 int hash_alg, 48 int hash_alg,
49 const struct sshbuf *client_version, 49 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 51dc11d76..3d11eba62 100644
--- a/mac.c
+++ b/mac.c
@@ -29,6 +29,7 @@
29 29
30#include <string.h> 30#include <string.h>
31#include <stdio.h> 31#include <stdio.h>
32#include <stdlib.h>
32 33
33#include "digest.h" 34#include "digest.h"
34#include "hmac.h" 35#include "hmac.h"
diff --git a/monitor.c b/monitor.c
index 60e529444..0766d6ef5 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 186e8f022..8e4c1c1f8 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 fdebb3aa4..69164a8c0 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -61,8 +61,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
61OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 61OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
62OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 62OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
63 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 63 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
64int mm_ssh_gssapi_userok(char *user); 64int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
65OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 65OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
66OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
67int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
66#endif 68#endif
67 69
68#ifdef USE_PAM 70#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index ec497e79f..4d699e5f1 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 },
@@ -983,10 +998,42 @@ parse_time:
983 intptr = &options->gss_authentication; 998 intptr = &options->gss_authentication;
984 goto parse_flag; 999 goto parse_flag;
985 1000
1001 case oGssKeyEx:
1002 intptr = &options->gss_keyex;
1003 goto parse_flag;
1004
986 case oGssDelegateCreds: 1005 case oGssDelegateCreds:
987 intptr = &options->gss_deleg_creds; 1006 intptr = &options->gss_deleg_creds;
988 goto parse_flag; 1007 goto parse_flag;
989 1008
1009 case oGssTrustDns:
1010 intptr = &options->gss_trust_dns;
1011 goto parse_flag;
1012
1013 case oGssClientIdentity:
1014 charptr = &options->gss_client_identity;
1015 goto parse_string;
1016
1017 case oGssServerIdentity:
1018 charptr = &options->gss_server_identity;
1019 goto parse_string;
1020
1021 case oGssRenewalRekey:
1022 intptr = &options->gss_renewal_rekey;
1023 goto parse_flag;
1024
1025 case oGssKexAlgorithms:
1026 arg = strdelim(&s);
1027 if (!arg || *arg == '\0')
1028 fatal("%.200s line %d: Missing argument.",
1029 filename, linenum);
1030 if (!kex_gss_names_valid(arg))
1031 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1032 filename, linenum, arg ? arg : "<NONE>");
1033 if (*activep && options->gss_kex_algorithms == NULL)
1034 options->gss_kex_algorithms = xstrdup(arg);
1035 break;
1036
990 case oBatchMode: 1037 case oBatchMode:
991 intptr = &options->batch_mode; 1038 intptr = &options->batch_mode;
992 goto parse_flag; 1039 goto parse_flag;
@@ -1854,7 +1901,13 @@ initialize_options(Options * options)
1854 options->pubkey_authentication = -1; 1901 options->pubkey_authentication = -1;
1855 options->challenge_response_authentication = -1; 1902 options->challenge_response_authentication = -1;
1856 options->gss_authentication = -1; 1903 options->gss_authentication = -1;
1904 options->gss_keyex = -1;
1857 options->gss_deleg_creds = -1; 1905 options->gss_deleg_creds = -1;
1906 options->gss_trust_dns = -1;
1907 options->gss_renewal_rekey = -1;
1908 options->gss_client_identity = NULL;
1909 options->gss_server_identity = NULL;
1910 options->gss_kex_algorithms = NULL;
1858 options->password_authentication = -1; 1911 options->password_authentication = -1;
1859 options->kbd_interactive_authentication = -1; 1912 options->kbd_interactive_authentication = -1;
1860 options->kbd_interactive_devices = NULL; 1913 options->kbd_interactive_devices = NULL;
@@ -2000,8 +2053,18 @@ fill_default_options(Options * options)
2000 options->challenge_response_authentication = 1; 2053 options->challenge_response_authentication = 1;
2001 if (options->gss_authentication == -1) 2054 if (options->gss_authentication == -1)
2002 options->gss_authentication = 0; 2055 options->gss_authentication = 0;
2056 if (options->gss_keyex == -1)
2057 options->gss_keyex = 0;
2003 if (options->gss_deleg_creds == -1) 2058 if (options->gss_deleg_creds == -1)
2004 options->gss_deleg_creds = 0; 2059 options->gss_deleg_creds = 0;
2060 if (options->gss_trust_dns == -1)
2061 options->gss_trust_dns = 0;
2062 if (options->gss_renewal_rekey == -1)
2063 options->gss_renewal_rekey = 0;
2064#ifdef GSSAPI
2065 if (options->gss_kex_algorithms == NULL)
2066 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
2067#endif
2005 if (options->password_authentication == -1) 2068 if (options->password_authentication == -1)
2006 options->password_authentication = 1; 2069 options->password_authentication = 1;
2007 if (options->kbd_interactive_authentication == -1) 2070 if (options->kbd_interactive_authentication == -1)
@@ -2616,7 +2679,14 @@ dump_client_config(Options *o, const char *host)
2616 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); 2679 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
2617#ifdef GSSAPI 2680#ifdef GSSAPI
2618 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); 2681 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
2682 dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
2619 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); 2683 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
2684 dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
2685 dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
2686 dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
2687 dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
2688 dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
2689 o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
2620#endif /* GSSAPI */ 2690#endif /* GSSAPI */
2621 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); 2691 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
2622 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); 2692 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 ffac5d2c7..ffdad31e7 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 },
@@ -1485,6 +1508,10 @@ process_server_config_line(ServerOptions *options, char *line,
1485 intptr = &options->gss_authentication; 1508 intptr = &options->gss_authentication;
1486 goto parse_flag; 1509 goto parse_flag;
1487 1510
1511 case sGssKeyEx:
1512 intptr = &options->gss_keyex;
1513 goto parse_flag;
1514
1488 case sGssCleanupCreds: 1515 case sGssCleanupCreds:
1489 intptr = &options->gss_cleanup_creds; 1516 intptr = &options->gss_cleanup_creds;
1490 goto parse_flag; 1517 goto parse_flag;
@@ -1493,6 +1520,22 @@ process_server_config_line(ServerOptions *options, char *line,
1493 intptr = &options->gss_strict_acceptor; 1520 intptr = &options->gss_strict_acceptor;
1494 goto parse_flag; 1521 goto parse_flag;
1495 1522
1523 case sGssStoreRekey:
1524 intptr = &options->gss_store_rekey;
1525 goto parse_flag;
1526
1527 case sGssKexAlgorithms:
1528 arg = strdelim(&cp);
1529 if (!arg || *arg == '\0')
1530 fatal("%.200s line %d: Missing argument.",
1531 filename, linenum);
1532 if (!kex_gss_names_valid(arg))
1533 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1534 filename, linenum, arg ? arg : "<NONE>");
1535 if (*activep && options->gss_kex_algorithms == NULL)
1536 options->gss_kex_algorithms = xstrdup(arg);
1537 break;
1538
1496 case sPasswordAuthentication: 1539 case sPasswordAuthentication:
1497 intptr = &options->password_authentication; 1540 intptr = &options->password_authentication;
1498 goto parse_flag; 1541 goto parse_flag;
@@ -2579,6 +2622,10 @@ dump_config(ServerOptions *o)
2579#ifdef GSSAPI 2622#ifdef GSSAPI
2580 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2623 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2581 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2624 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2625 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2626 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2627 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2628 dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
2582#endif 2629#endif
2583 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2630 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2584 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2631 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 54e0a8d8d..a476d5220 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 ac06b08e9..ac3d9d19d 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 9480eba8d..a1c7d2305 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 91e7c3511..42be7d88f 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 412629637..c3c8b274a 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -754,10 +754,67 @@ The default is
754Specifies whether user authentication based on GSSAPI is allowed. 754Specifies whether user authentication based on GSSAPI is allowed.
755The default is 755The default is
756.Cm no . 756.Cm no .
757.It Cm GSSAPIClientIdentity
758If set, specifies the GSSAPI client identity that ssh should use when
759connecting to the server. The default is unset, which means that the default
760identity will be used.
757.It Cm GSSAPIDelegateCredentials 761.It Cm GSSAPIDelegateCredentials
758Forward (delegate) credentials to the server. 762Forward (delegate) credentials to the server.
759The default is 763The default is
760.Cm no . 764.Cm no .
765.It Cm GSSAPIKeyExchange
766Specifies whether key exchange based on GSSAPI may be used. When using
767GSSAPI key exchange the server need not have a host key.
768The default is
769.Dq no .
770.It Cm GSSAPIRenewalForcesRekey
771If set to
772.Dq yes
773then renewal of the client's GSSAPI credentials will force the rekeying of the
774ssh connection. With a compatible server, this will delegate the renewed
775credentials to a session on the server.
776.Pp
777Checks are made to ensure that credentials are only propagated when the new
778credentials match the old ones on the originating client and where the
779receiving server still has the old set in its cache.
780.Pp
781The default is
782.Dq no .
783.Pp
784For this to work
785.Cm GSSAPIKeyExchange
786needs to be enabled in the server and also used by the client.
787.It Cm GSSAPIServerIdentity
788If set, specifies the GSSAPI server identity that ssh should expect when
789connecting to the server. The default is unset, which means that the
790expected GSSAPI server identity will be determined from the target
791hostname.
792.It Cm GSSAPITrustDns
793Set to
794.Dq yes
795to indicate that the DNS is trusted to securely canonicalize
796the name of the host being connected to. If
797.Dq no ,
798the hostname entered on the
799command line will be passed untouched to the GSSAPI library.
800The default is
801.Dq no .
802.It Cm GSSAPIKexAlgorithms
803The list of key exchange algorithms that are offered for GSSAPI
804key exchange. Possible values are
805.Bd -literal -offset 3n
806gss-gex-sha1-,
807gss-group1-sha1-,
808gss-group14-sha1-,
809gss-group14-sha256-,
810gss-group16-sha512-,
811gss-nistp256-sha256-,
812gss-curve25519-sha256-
813.Ed
814.Pp
815The default is
816.Dq gss-gex-sha1-,gss-group14-sha1- .
817This option only applies to protocol version 2 connections using GSSAPI.
761.It Cm HashKnownHosts 818.It Cm HashKnownHosts
762Indicates that 819Indicates that
763.Xr ssh 1 820.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index dffee90b1..4020371ae 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,
@@ -698,12 +766,25 @@ userauth_gssapi(struct ssh *ssh)
698 OM_uint32 min; 766 OM_uint32 min;
699 int r, ok = 0; 767 int r, ok = 0;
700 gss_OID mech = NULL; 768 gss_OID mech = NULL;
769 char *gss_host;
770
771 if (options.gss_server_identity)
772 gss_host = xstrdup(options.gss_server_identity);
773 else if (options.gss_trust_dns)
774 gss_host = remote_hostname(ssh);
775 else
776 gss_host = xstrdup(authctxt->host);
701 777
702 /* Try one GSSAPI method at a time, rather than sending them all at 778 /* Try one GSSAPI method at a time, rather than sending them all at
703 * once. */ 779 * once. */
704 780
705 if (authctxt->gss_supported_mechs == NULL) 781 if (authctxt->gss_supported_mechs == NULL)
706 gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 782 if (GSS_ERROR(gss_indicate_mechs(&min,
783 &authctxt->gss_supported_mechs))) {
784 authctxt->gss_supported_mechs = NULL;
785 free(gss_host);
786 return 0;
787 }
707 788
708 /* Check to see whether the mechanism is usable before we offer it */ 789 /* Check to see whether the mechanism is usable before we offer it */
709 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 790 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
@@ -712,13 +793,15 @@ userauth_gssapi(struct ssh *ssh)
712 elements[authctxt->mech_tried]; 793 elements[authctxt->mech_tried];
713 /* My DER encoding requires length<128 */ 794 /* My DER encoding requires length<128 */
714 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 795 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
715 mech, authctxt->host)) { 796 mech, gss_host, options.gss_client_identity)) {
716 ok = 1; /* Mechanism works */ 797 ok = 1; /* Mechanism works */
717 } else { 798 } else {
718 authctxt->mech_tried++; 799 authctxt->mech_tried++;
719 } 800 }
720 } 801 }
721 802
803 free(gss_host);
804
722 if (!ok || mech == NULL) 805 if (!ok || mech == NULL)
723 return 0; 806 return 0;
724 807
@@ -958,6 +1041,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
958 free(lang); 1041 free(lang);
959 return r; 1042 return r;
960} 1043}
1044
1045int
1046userauth_gsskeyex(struct ssh *ssh)
1047{
1048 struct sshbuf *b = NULL;
1049 Authctxt *authctxt = ssh->authctxt;
1050 gss_buffer_desc gssbuf;
1051 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1052 OM_uint32 ms;
1053 int r;
1054
1055 static int attempt = 0;
1056 if (attempt++ >= 1)
1057 return (0);
1058
1059 if (gss_kex_context == NULL) {
1060 debug("No valid Key exchange context");
1061 return (0);
1062 }
1063
1064 if ((b = sshbuf_new()) == NULL)
1065 fatal("%s: sshbuf_new failed", __func__);
1066
1067 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1068 "gssapi-keyex");
1069
1070 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1071 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1072 gssbuf.length = sshbuf_len(b);
1073
1074 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1075 sshbuf_free(b);
1076 return (0);
1077 }
1078
1079 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1080 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1081 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1082 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1083 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1084 (r = sshpkt_send(ssh)) != 0)
1085 fatal("%s: %s", __func__, ssh_err(r));
1086
1087 sshbuf_free(b);
1088 gss_release_buffer(&ms, &mic);
1089
1090 return (1);
1091}
1092
961#endif /* GSSAPI */ 1093#endif /* GSSAPI */
962 1094
963static int 1095static int
diff --git a/sshd.c b/sshd.c
index cbd3bce91..98680721b 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}
@@ -1769,7 +1773,8 @@ main(int ac, char **av)
1769 free(fp); 1773 free(fp);
1770 } 1774 }
1771 accumulate_host_timing_secret(cfg, NULL); 1775 accumulate_host_timing_secret(cfg, NULL);
1772 if (!sensitive_data.have_ssh2_key) { 1776 /* The GSSAPI key exchange can run without a host key */
1777 if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
1773 logit("sshd: no hostkeys available -- exiting."); 1778 logit("sshd: no hostkeys available -- exiting.");
1774 exit(1); 1779 exit(1);
1775 } 1780 }
@@ -2064,6 +2069,60 @@ main(int ac, char **av)
2064 rdomain == NULL ? "" : "\""); 2069 rdomain == NULL ? "" : "\"");
2065 free(laddr); 2070 free(laddr);
2066 2071
2072#ifdef USE_SECURITY_SESSION_API
2073 /*
2074 * Create a new security session for use by the new user login if
2075 * the current session is the root session or we are not launched
2076 * by inetd (eg: debugging mode or server mode). We do not
2077 * necessarily need to create a session if we are launched from
2078 * inetd because Panther xinetd will create a session for us.
2079 *
2080 * The only case where this logic will fail is if there is an
2081 * inetd running in a non-root session which is not creating
2082 * new sessions for us. Then all the users will end up in the
2083 * same session (bad).
2084 *
2085 * When the client exits, the session will be destroyed for us
2086 * automatically.
2087 *
2088 * We must create the session before any credentials are stored
2089 * (including AFS pags, which happens a few lines below).
2090 */
2091 {
2092 OSStatus err = 0;
2093 SecuritySessionId sid = 0;
2094 SessionAttributeBits sattrs = 0;
2095
2096 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2097 if (err)
2098 error("SessionGetInfo() failed with error %.8X",
2099 (unsigned) err);
2100 else
2101 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2102 (unsigned) sid, (unsigned) sattrs);
2103
2104 if (inetd_flag && !(sattrs & sessionIsRoot))
2105 debug("Running in inetd mode in a non-root session... "
2106 "assuming inetd created the session for us.");
2107 else {
2108 debug("Creating new security session...");
2109 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2110 if (err)
2111 error("SessionCreate() failed with error %.8X",
2112 (unsigned) err);
2113
2114 err = SessionGetInfo(callerSecuritySession, &sid,
2115 &sattrs);
2116 if (err)
2117 error("SessionGetInfo() failed with error %.8X",
2118 (unsigned) err);
2119 else
2120 debug("New Session ID is %.8X / Session Attributes are %.8X",
2121 (unsigned) sid, (unsigned) sattrs);
2122 }
2123 }
2124#endif
2125
2067 /* 2126 /*
2068 * We don't want to listen forever unless the other side 2127 * We don't want to listen forever unless the other side
2069 * successfully authenticates itself. So we set up an alarm which is 2128 * successfully authenticates itself. So we set up an alarm which is
@@ -2260,6 +2319,48 @@ do_ssh2_kex(struct ssh *ssh)
2260 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2319 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2261 list_hostkey_types()); 2320 list_hostkey_types());
2262 2321
2322#if defined(GSSAPI) && defined(WITH_OPENSSL)
2323 {
2324 char *orig;
2325 char *gss = NULL;
2326 char *newstr = NULL;
2327 orig = myproposal[PROPOSAL_KEX_ALGS];
2328
2329 /*
2330 * If we don't have a host key, then there's no point advertising
2331 * the other key exchange algorithms
2332 */
2333
2334 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2335 orig = NULL;
2336
2337 if (options.gss_keyex)
2338 gss = ssh_gssapi_server_mechanisms();
2339 else
2340 gss = NULL;
2341
2342 if (gss && orig)
2343 xasprintf(&newstr, "%s,%s", gss, orig);
2344 else if (gss)
2345 newstr = gss;
2346 else if (orig)
2347 newstr = orig;
2348
2349 /*
2350 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2351 * key alg, but we can't tell people about it unless its the only
2352 * host key algorithm we support
2353 */
2354 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2355 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2356
2357 if (newstr)
2358 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2359 else
2360 fatal("No supported key exchange algorithms");
2361 }
2362#endif
2363
2263 /* start key exchange */ 2364 /* start key exchange */
2264 if ((r = kex_setup(ssh, myproposal)) != 0) 2365 if ((r = kex_setup(ssh, myproposal)) != 0)
2265 fatal("kex_setup: %s", ssh_err(r)); 2366 fatal("kex_setup: %s", ssh_err(r));
@@ -2275,7 +2376,18 @@ do_ssh2_kex(struct ssh *ssh)
2275# ifdef OPENSSL_HAS_ECC 2376# ifdef OPENSSL_HAS_ECC
2276 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 2377 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2277# endif 2378# endif
2278#endif 2379# ifdef GSSAPI
2380 if (options.gss_keyex) {
2381 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2382 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2383 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2384 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2385 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2386 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2387 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2388 }
2389# endif
2390#endif /* WITH_OPENSSL */
2279 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 2391 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2280 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 2392 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2281 kex->load_host_public_key=&get_hostkey_public_by_type; 2393 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 b224f2929..2baa6622b 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -653,6 +653,11 @@ Specifies whether to automatically destroy the user's credentials cache
653on logout. 653on logout.
654The default is 654The default is
655.Cm yes . 655.Cm yes .
656.It Cm GSSAPIKeyExchange
657Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
658doesn't rely on ssh keys to verify host identity.
659The default is
660.Cm no .
656.It Cm GSSAPIStrictAcceptorCheck 661.It Cm GSSAPIStrictAcceptorCheck
657Determines whether to be strict about the identity of the GSSAPI acceptor 662Determines whether to be strict about the identity of the GSSAPI acceptor
658a client authenticates against. 663a client authenticates against.
@@ -667,6 +672,31 @@ machine's default store.
667This facility is provided to assist with operation on multi homed machines. 672This facility is provided to assist with operation on multi homed machines.
668The default is 673The default is
669.Cm yes . 674.Cm yes .
675.It Cm GSSAPIStoreCredentialsOnRekey
676Controls whether the user's GSSAPI credentials should be updated following a
677successful connection rekeying. This option can be used to accepted renewed
678or updated credentials from a compatible client. The default is
679.Dq no .
680.Pp
681For this to work
682.Cm GSSAPIKeyExchange
683needs to be enabled in the server and also used by the client.
684.It Cm GSSAPIKexAlgorithms
685The list of key exchange algorithms that are accepted by GSSAPI
686key exchange. Possible values are
687.Bd -literal -offset 3n
688gss-gex-sha1-,
689gss-group1-sha1-,
690gss-group14-sha1-,
691gss-group14-sha256-,
692gss-group16-sha512-,
693gss-nistp256-sha256-,
694gss-curve25519-sha256-
695.Ed
696.Pp
697The default is
698.Dq gss-gex-sha1-,gss-group14-sha1- .
699This option only applies to protocol version 2 connections using GSSAPI.
670.It Cm HostbasedAcceptedKeyTypes 700.It Cm HostbasedAcceptedKeyTypes
671Specifies the key types that will be accepted for hostbased authentication 701Specifies the key types that will be accepted for hostbased authentication
672as a list of comma-separated patterns. 702as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index ad1957762..789cd61ef 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -135,6 +135,7 @@ static const struct keytype keytypes[] = {
135# endif /* OPENSSL_HAS_NISTP521 */ 135# endif /* OPENSSL_HAS_NISTP521 */
136# endif /* OPENSSL_HAS_ECC */ 136# endif /* OPENSSL_HAS_ECC */
137#endif /* WITH_OPENSSL */ 137#endif /* WITH_OPENSSL */
138 { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
138 { NULL, NULL, NULL, -1, -1, 0, 0 } 139 { NULL, NULL, NULL, -1, -1, 0, 0 }
139}; 140};
140 141
@@ -223,7 +224,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
223 const struct keytype *kt; 224 const struct keytype *kt;
224 225
225 for (kt = keytypes; kt->type != -1; kt++) { 226 for (kt = keytypes; kt->type != -1; kt++) {
226 if (kt->name == NULL) 227 if (kt->name == NULL || kt->type == KEY_NULL)
227 continue; 228 continue;
228 if (!include_sigonly && kt->sigonly) 229 if (!include_sigonly && kt->sigonly)
229 continue; 230 continue;
diff --git a/sshkey.h b/sshkey.h
index a91e60436..c11106c93 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