summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2020-02-21 12:01:36 +0000
commit34aff3aa136e5a65f441b25811dd466488fda087 (patch)
treee2170faeed03d67545255d3d3c9d62280414c0b2
parentf0de78bd4f29fa688c5df116f3f9cd43543a76d0 (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: 2020-02-21 Patch-Name: gssapi.patch
-rw-r--r--Makefile.in3
-rw-r--r--README.md33
-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--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--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.c6
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.557
-rw-r--r--sshconnect2.c142
-rw-r--r--sshd.c62
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.530
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
38 files changed, 2624 insertions, 160 deletions
diff --git a/Makefile.in b/Makefile.in
index e7549470c..b68c1710f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
109 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ 109 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
110 kexgexc.o kexgexs.o \ 110 kexgexc.o kexgexs.o \
111 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ 111 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \
112 kexgssc.o \
112 sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \ 113 sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
113 sshbuf-io.o 114 sshbuf-io.o
114 115
@@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
125 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 126 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
126 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 127 auth2-none.o auth2-passwd.o auth2-pubkey.o \
127 monitor.o monitor_wrap.o auth-krb5.o \ 128 monitor.o monitor_wrap.o auth-krb5.o \
128 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 129 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
129 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 130 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
130 sftp-server.o sftp-common.o \ 131 sftp-server.o sftp-common.o \
131 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ 132 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff --git a/README.md b/README.md
index 28fb43d2a..5b73d24c0 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,36 @@
1Portable OpenSSH with GSSAPI Key Exchange patches
2=================================================
3
4Currently, there are two branches with gssapi key exchange related
5patches:
6
7 * fedora/master: Changes that are shipped in Fedora
8 * debian/master: Changes that are shipped in Debian
9
10The target is to converge to a shared repository with single master
11branch from where we could build releases for both OSes.
12
13
14What is in:
15
16 * The original patch implementing missing parts of RFC4462 by Simon Wilkinson
17 adapted to the current OpenSSH versions and with several fixes
18 * New methods for GSSAPI Kex from IETF draft [1] from Jakub Jelen
19
20
21Missing kerberos-related parts:
22
23 * .k5login and .kusers support available in Fedora [2] [3].
24 * Improved handling of kerberos ccache location [4]
25
26
27[1] https://tools.ietf.org/html/draft-ietf-curdle-gss-keyex-sha2-08
28[2] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-kuserok.patch
29[3] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-GSSAPIEnablek5users.patch
30[4] https://bugzilla.mindrot.org/show_bug.cgi?id=2775
31
32-------------------------------------------------------------------------------
33
1# Portable OpenSSH 34# Portable OpenSSH
2 35
3[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh) 36[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh)
diff --git a/auth.c b/auth.c
index 086b8ebb1..687c57b42 100644
--- a/auth.c
+++ b/auth.c
@@ -400,7 +400,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
400 case PERMIT_NO_PASSWD: 400 case PERMIT_NO_PASSWD:
401 if (strcmp(method, "publickey") == 0 || 401 if (strcmp(method, "publickey") == 0 ||
402 strcmp(method, "hostbased") == 0 || 402 strcmp(method, "hostbased") == 0 ||
403 strcmp(method, "gssapi-with-mic") == 0) 403 strcmp(method, "gssapi-with-mic") == 0 ||
404 strcmp(method, "gssapi-keyex") == 0)
404 return 1; 405 return 1;
405 break; 406 break;
406 case PERMIT_FORCED_ONLY: 407 case PERMIT_FORCED_ONLY:
@@ -725,99 +726,6 @@ fakepw(void)
725} 726}
726 727
727/* 728/*
728 * Returns the remote DNS hostname as a string. The returned string must not
729 * be freed. NB. this will usually trigger a DNS query the first time it is
730 * called.
731 * This function does additional checks on the hostname to mitigate some
732 * attacks on legacy rhosts-style authentication.
733 * XXX is RhostsRSAAuthentication vulnerable to these?
734 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
735 */
736
737static char *
738remote_hostname(struct ssh *ssh)
739{
740 struct sockaddr_storage from;
741 socklen_t fromlen;
742 struct addrinfo hints, *ai, *aitop;
743 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
744 const char *ntop = ssh_remote_ipaddr(ssh);
745
746 /* Get IP address of client. */
747 fromlen = sizeof(from);
748 memset(&from, 0, sizeof(from));
749 if (getpeername(ssh_packet_get_connection_in(ssh),
750 (struct sockaddr *)&from, &fromlen) == -1) {
751 debug("getpeername failed: %.100s", strerror(errno));
752 return xstrdup(ntop);
753 }
754
755 ipv64_normalise_mapped(&from, &fromlen);
756 if (from.ss_family == AF_INET6)
757 fromlen = sizeof(struct sockaddr_in6);
758
759 debug3("Trying to reverse map address %.100s.", ntop);
760 /* Map the IP address to a host name. */
761 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
762 NULL, 0, NI_NAMEREQD) != 0) {
763 /* Host name not found. Use ip address. */
764 return xstrdup(ntop);
765 }
766
767 /*
768 * if reverse lookup result looks like a numeric hostname,
769 * someone is trying to trick us by PTR record like following:
770 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
771 */
772 memset(&hints, 0, sizeof(hints));
773 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
774 hints.ai_flags = AI_NUMERICHOST;
775 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
776 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
777 name, ntop);
778 freeaddrinfo(ai);
779 return xstrdup(ntop);
780 }
781
782 /* Names are stored in lowercase. */
783 lowercase(name);
784
785 /*
786 * Map it back to an IP address and check that the given
787 * address actually is an address of this host. This is
788 * necessary because anyone with access to a name server can
789 * define arbitrary names for an IP address. Mapping from
790 * name to IP address can be trusted better (but can still be
791 * fooled if the intruder has access to the name server of
792 * the domain).
793 */
794 memset(&hints, 0, sizeof(hints));
795 hints.ai_family = from.ss_family;
796 hints.ai_socktype = SOCK_STREAM;
797 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
798 logit("reverse mapping checking getaddrinfo for %.700s "
799 "[%s] failed.", name, ntop);
800 return xstrdup(ntop);
801 }
802 /* Look for the address from the list of addresses. */
803 for (ai = aitop; ai; ai = ai->ai_next) {
804 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
805 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
806 (strcmp(ntop, ntop2) == 0))
807 break;
808 }
809 freeaddrinfo(aitop);
810 /* If we reached the end of the list, the address was not there. */
811 if (ai == NULL) {
812 /* Address not found for the host name. */
813 logit("Address %.100s maps to %.600s, but this does not "
814 "map back to the address.", ntop, name);
815 return xstrdup(ntop);
816 }
817 return xstrdup(name);
818}
819
820/*
821 * Return the canonical name of the host in the other side of the current 729 * Return the canonical name of the host in the other side of the current
822 * connection. The host name is cached, so it is efficient to call this 730 * connection. The host name is cached, so it is efficient to call this
823 * several times. 731 * several times.
diff --git a/auth2-gss.c b/auth2-gss.c
index 9351e0428..d6446c0cf 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -55,6 +55,48 @@ static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *
55static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 55static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
56 56
57/* 57/*
58 * The 'gssapi_keyex' userauth mechanism.
59 */
60static int
61userauth_gsskeyex(struct ssh *ssh)
62{
63 Authctxt *authctxt = ssh->authctxt;
64 int r, authenticated = 0;
65 struct sshbuf *b = NULL;
66 gss_buffer_desc mic, gssbuf;
67 u_char *p;
68 size_t len;
69
70 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
71 (r = sshpkt_get_end(ssh)) != 0)
72 fatal("%s: %s", __func__, ssh_err(r));
73
74 if ((b = sshbuf_new()) == NULL)
75 fatal("%s: sshbuf_new failed", __func__);
76
77 mic.value = p;
78 mic.length = len;
79
80 ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
81 "gssapi-keyex");
82
83 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
84 fatal("%s: sshbuf_mutable_ptr failed", __func__);
85 gssbuf.length = sshbuf_len(b);
86
87 /* gss_kex_context is NULL with privsep, so we can't check it here */
88 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
89 &gssbuf, &mic))))
90 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
91 authctxt->pw, 1));
92
93 sshbuf_free(b);
94 free(mic.value);
95
96 return (authenticated);
97}
98
99/*
58 * We only support those mechanisms that we know about (ie ones that we know 100 * We only support those mechanisms that we know about (ie ones that we know
59 * how to check local user kuserok and the like) 101 * how to check local user kuserok and the like)
60 */ 102 */
@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
260 if ((r = sshpkt_get_end(ssh)) != 0) 302 if ((r = sshpkt_get_end(ssh)) != 0)
261 fatal("%s: %s", __func__, ssh_err(r)); 303 fatal("%s: %s", __func__, ssh_err(r));
262 304
263 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 305 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
306 authctxt->pw, 1));
264 307
265 if ((!use_privsep || mm_is_monitor()) && 308 if ((!use_privsep || mm_is_monitor()) &&
266 (displayname = ssh_gssapi_displayname()) != NULL) 309 (displayname = ssh_gssapi_displayname()) != NULL)
@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
306 gssbuf.length = sshbuf_len(b); 349 gssbuf.length = sshbuf_len(b);
307 350
308 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 351 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
309 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 352 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
353 authctxt->pw, 0));
310 else 354 else
311 logit("GSSAPI MIC check failed"); 355 logit("GSSAPI MIC check failed");
312 356
@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
326 return 0; 370 return 0;
327} 371}
328 372
373Authmethod method_gsskeyex = {
374 "gssapi-keyex",
375 userauth_gsskeyex,
376 &options.gss_authentication
377};
378
329Authmethod method_gssapi = { 379Authmethod method_gssapi = {
330 "gssapi-with-mic", 380 "gssapi-with-mic",
331 userauth_gssapi, 381 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 0e7762242..1c217268c 100644
--- a/auth2.c
+++ b/auth2.c
@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
73extern Authmethod method_kbdint; 73extern Authmethod method_kbdint;
74extern Authmethod method_hostbased; 74extern Authmethod method_hostbased;
75#ifdef GSSAPI 75#ifdef GSSAPI
76extern Authmethod method_gsskeyex;
76extern Authmethod method_gssapi; 77extern Authmethod method_gssapi;
77#endif 78#endif
78 79
@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
80 &method_none, 81 &method_none,
81 &method_pubkey, 82 &method_pubkey,
82#ifdef GSSAPI 83#ifdef GSSAPI
84 &method_gsskeyex,
83 &method_gssapi, 85 &method_gssapi,
84#endif 86#endif
85 &method_passwd, 87 &method_passwd,
diff --git a/canohost.c b/canohost.c
index abea9c6e6..8e81b5193 100644
--- a/canohost.c
+++ b/canohost.c
@@ -35,6 +35,99 @@
35#include "canohost.h" 35#include "canohost.h"
36#include "misc.h" 36#include "misc.h"
37 37
38/*
39 * Returns the remote DNS hostname as a string. The returned string must not
40 * be freed. NB. this will usually trigger a DNS query the first time it is
41 * called.
42 * This function does additional checks on the hostname to mitigate some
43 * attacks on legacy rhosts-style authentication.
44 * XXX is RhostsRSAAuthentication vulnerable to these?
45 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
46 */
47
48char *
49remote_hostname(struct ssh *ssh)
50{
51 struct sockaddr_storage from;
52 socklen_t fromlen;
53 struct addrinfo hints, *ai, *aitop;
54 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
55 const char *ntop = ssh_remote_ipaddr(ssh);
56
57 /* Get IP address of client. */
58 fromlen = sizeof(from);
59 memset(&from, 0, sizeof(from));
60 if (getpeername(ssh_packet_get_connection_in(ssh),
61 (struct sockaddr *)&from, &fromlen) == -1) {
62 debug("getpeername failed: %.100s", strerror(errno));
63 return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop);
127 }
128 return xstrdup(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 ebd0dbca1..1bdac6a46 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
@@ -1379,9 +1383,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1379 break; 1383 break;
1380 1384
1381 /* Do channel operations unless rekeying in progress. */ 1385 /* Do channel operations unless rekeying in progress. */
1382 if (!ssh_packet_is_rekeying(ssh)) 1386 if (!ssh_packet_is_rekeying(ssh)) {
1383 channel_after_select(ssh, readset, writeset); 1387 channel_after_select(ssh, readset, writeset);
1384 1388
1389#ifdef GSSAPI
1390 if (options.gss_renewal_rekey &&
1391 ssh_gssapi_credentials_updated(NULL)) {
1392 debug("credentials updated - forcing rekey");
1393 need_rekeying = 1;
1394 }
1395#endif
1396 }
1397
1385 /* Buffer input from the connection. */ 1398 /* Buffer input from the connection. */
1386 client_process_net_input(ssh, readset); 1399 client_process_net_input(ssh, readset);
1387 1400
diff --git a/configure.ac b/configure.ac
index b689db4b5..efafb6bd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -674,6 +674,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
674 [Use tunnel device compatibility to OpenBSD]) 674 [Use tunnel device compatibility to OpenBSD])
675 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 675 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
676 [Prepend the address family to IP tunnel traffic]) 676 [Prepend the address family to IP tunnel traffic])
677 AC_MSG_CHECKING([if we have the Security Authorization Session API])
678 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
679 [SessionCreate(0, 0);],
680 [ac_cv_use_security_session_api="yes"
681 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
682 [platform has the Security Authorization Session API])
683 LIBS="$LIBS -framework Security"
684 AC_MSG_RESULT([yes])],
685 [ac_cv_use_security_session_api="no"
686 AC_MSG_RESULT([no])])
687 AC_MSG_CHECKING([if we have an in-memory credentials cache])
688 AC_TRY_COMPILE(
689 [#include <Kerberos/Kerberos.h>],
690 [cc_context_t c;
691 (void) cc_initialize (&c, 0, NULL, NULL);],
692 [AC_DEFINE([USE_CCAPI], [1],
693 [platform uses an in-memory credentials cache])
694 LIBS="$LIBS -framework Security"
695 AC_MSG_RESULT([yes])
696 if test "x$ac_cv_use_security_session_api" = "xno"; then
697 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
698 fi],
699 [AC_MSG_RESULT([no])]
700 )
677 m4_pattern_allow([AU_IPv]) 701 m4_pattern_allow([AU_IPv])
678 AC_CHECK_DECL([AU_IPv4], [], 702 AC_CHECK_DECL([AU_IPv4], [],
679 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 703 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/kex.c b/kex.c
index ce85f0439..574c76093 100644
--- a/kex.c
+++ b/kex.c
@@ -57,11 +57,16 @@
57#include "misc.h" 57#include "misc.h"
58#include "dispatch.h" 58#include "dispatch.h"
59#include "monitor.h" 59#include "monitor.h"
60#include "xmalloc.h"
60 61
61#include "ssherr.h" 62#include "ssherr.h"
62#include "sshbuf.h" 63#include "sshbuf.h"
63#include "digest.h" 64#include "digest.h"
64 65
66#ifdef GSSAPI
67#include "ssh-gss.h"
68#endif
69
65/* prototype */ 70/* prototype */
66static int kex_choose_conf(struct ssh *); 71static int kex_choose_conf(struct ssh *);
67static int kex_input_newkeys(int, u_int32_t, struct ssh *); 72static int kex_input_newkeys(int, u_int32_t, struct ssh *);
@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = {
115#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 120#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
116 { NULL, 0, -1, -1}, 121 { NULL, 0, -1, -1},
117}; 122};
123static const struct kexalg gss_kexalgs[] = {
124#ifdef GSSAPI
125 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
126 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
127 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
128 { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
129 { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
130 { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
131 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
132 { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
133#endif
134 { NULL, 0, -1, -1},
135};
118 136
119char * 137static char *
120kex_alg_list(char sep) 138kex_alg_list_internal(char sep, const struct kexalg *algs)
121{ 139{
122 char *ret = NULL, *tmp; 140 char *ret = NULL, *tmp;
123 size_t nlen, rlen = 0; 141 size_t nlen, rlen = 0;
124 const struct kexalg *k; 142 const struct kexalg *k;
125 143
126 for (k = kexalgs; k->name != NULL; k++) { 144 for (k = algs; k->name != NULL; k++) {
127 if (ret != NULL) 145 if (ret != NULL)
128 ret[rlen++] = sep; 146 ret[rlen++] = sep;
129 nlen = strlen(k->name); 147 nlen = strlen(k->name);
@@ -138,6 +156,18 @@ kex_alg_list(char sep)
138 return ret; 156 return ret;
139} 157}
140 158
159char *
160kex_alg_list(char sep)
161{
162 return kex_alg_list_internal(sep, kexalgs);
163}
164
165char *
166kex_gss_alg_list(char sep)
167{
168 return kex_alg_list_internal(sep, gss_kexalgs);
169}
170
141static const struct kexalg * 171static const struct kexalg *
142kex_alg_by_name(const char *name) 172kex_alg_by_name(const char *name)
143{ 173{
@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name)
147 if (strcmp(k->name, name) == 0) 177 if (strcmp(k->name, name) == 0)
148 return k; 178 return k;
149 } 179 }
180 for (k = gss_kexalgs; k->name != NULL; k++) {
181 if (strncmp(k->name, name, strlen(k->name)) == 0)
182 return k;
183 }
150 return NULL; 184 return NULL;
151} 185}
152 186
@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
315 return r; 349 return r;
316} 350}
317 351
352/* Validate GSS KEX method name list */
353int
354kex_gss_names_valid(const char *names)
355{
356 char *s, *cp, *p;
357
358 if (names == NULL || *names == '\0')
359 return 0;
360 s = cp = xstrdup(names);
361 for ((p = strsep(&cp, ",")); p && *p != '\0';
362 (p = strsep(&cp, ","))) {
363 if (strncmp(p, "gss-", 4) != 0
364 || kex_alg_by_name(p) == NULL) {
365 error("Unsupported KEX algorithm \"%.100s\"", p);
366 free(s);
367 return 0;
368 }
369 }
370 debug3("gss kex names ok: [%s]", names);
371 free(s);
372 return 1;
373}
374
318/* put algorithm proposal into buffer */ 375/* put algorithm proposal into buffer */
319int 376int
320kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 377kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
@@ -698,6 +755,9 @@ kex_free(struct kex *kex)
698 sshbuf_free(kex->server_version); 755 sshbuf_free(kex->server_version);
699 sshbuf_free(kex->client_pub); 756 sshbuf_free(kex->client_pub);
700 free(kex->session_id); 757 free(kex->session_id);
758#ifdef GSSAPI
759 free(kex->gss_host);
760#endif /* GSSAPI */
701 free(kex->failed_choice); 761 free(kex->failed_choice);
702 free(kex->hostkey_alg); 762 free(kex->hostkey_alg);
703 free(kex->name); 763 free(kex->name);
diff --git a/kex.h b/kex.h
index a5ae6ac05..fe7141414 100644
--- a/kex.h
+++ b/kex.h
@@ -102,6 +102,15 @@ enum kex_exchange {
102 KEX_ECDH_SHA2, 102 KEX_ECDH_SHA2,
103 KEX_C25519_SHA256, 103 KEX_C25519_SHA256,
104 KEX_KEM_SNTRUP4591761X25519_SHA512, 104 KEX_KEM_SNTRUP4591761X25519_SHA512,
105#ifdef GSSAPI
106 KEX_GSS_GRP1_SHA1,
107 KEX_GSS_GRP14_SHA1,
108 KEX_GSS_GRP14_SHA256,
109 KEX_GSS_GRP16_SHA512,
110 KEX_GSS_GEX_SHA1,
111 KEX_GSS_NISTP256_SHA256,
112 KEX_GSS_C25519_SHA256,
113#endif
105 KEX_MAX 114 KEX_MAX
106}; 115};
107 116
@@ -153,6 +162,12 @@ struct kex {
153 u_int flags; 162 u_int flags;
154 int hash_alg; 163 int hash_alg;
155 int ec_nid; 164 int ec_nid;
165#ifdef GSSAPI
166 int gss_deleg_creds;
167 int gss_trust_dns;
168 char *gss_host;
169 char *gss_client;
170#endif
156 char *failed_choice; 171 char *failed_choice;
157 int (*verify_host_key)(struct sshkey *, struct ssh *); 172 int (*verify_host_key)(struct sshkey *, struct ssh *);
158 struct sshkey *(*load_host_public_key)(int, int, struct ssh *); 173 struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
@@ -174,8 +189,10 @@ struct kex {
174 189
175int kex_names_valid(const char *); 190int kex_names_valid(const char *);
176char *kex_alg_list(char); 191char *kex_alg_list(char);
192char *kex_gss_alg_list(char);
177char *kex_names_cat(const char *, const char *); 193char *kex_names_cat(const char *, const char *);
178int kex_assemble_names(char **, const char *, const char *); 194int kex_assemble_names(char **, const char *, const char *);
195int kex_gss_names_valid(const char *);
179 196
180int kex_exchange_identification(struct ssh *, int, const char *); 197int kex_exchange_identification(struct ssh *, int, const char *);
181 198
@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *);
202int kexgex_server(struct ssh *); 219int kexgex_server(struct ssh *);
203int kex_gen_client(struct ssh *); 220int kex_gen_client(struct ssh *);
204int kex_gen_server(struct ssh *); 221int kex_gen_server(struct ssh *);
222#if defined(GSSAPI) && defined(WITH_OPENSSL)
223int kexgssgex_client(struct ssh *);
224int kexgssgex_server(struct ssh *);
225int kexgss_client(struct ssh *);
226int kexgss_server(struct ssh *);
227#endif
205 228
206int kex_dh_keypair(struct kex *); 229int kex_dh_keypair(struct kex *);
207int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, 230int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
234 const BIGNUM *, const u_char *, size_t, 257 const BIGNUM *, const u_char *, size_t,
235 u_char *, size_t *); 258 u_char *, size_t *);
236 259
260int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
261 const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
262 const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
263 const struct sshbuf *client_pub, const struct sshbuf *server_pub,
264 const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
265
237void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) 266void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
238 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) 267 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
239 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); 268 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
diff --git a/kexdh.c b/kexdh.c
index 67133e339..edaa46762 100644
--- a/kexdh.c
+++ b/kexdh.c
@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex)
48{ 48{
49 switch (kex->kex_type) { 49 switch (kex->kex_type) {
50 case KEX_DH_GRP1_SHA1: 50 case KEX_DH_GRP1_SHA1:
51#ifdef GSSAPI
52 case KEX_GSS_GRP1_SHA1:
53#endif
51 kex->dh = dh_new_group1(); 54 kex->dh = dh_new_group1();
52 break; 55 break;
53 case KEX_DH_GRP14_SHA1: 56 case KEX_DH_GRP14_SHA1:
54 case KEX_DH_GRP14_SHA256: 57 case KEX_DH_GRP14_SHA256:
58#ifdef GSSAPI
59 case KEX_GSS_GRP14_SHA1:
60 case KEX_GSS_GRP14_SHA256:
61#endif
55 kex->dh = dh_new_group14(); 62 kex->dh = dh_new_group14();
56 break; 63 break;
57 case KEX_DH_GRP16_SHA512: 64 case KEX_DH_GRP16_SHA512:
65#ifdef GSSAPI
66 case KEX_GSS_GRP16_SHA512:
67#endif
58 kex->dh = dh_new_group16(); 68 kex->dh = dh_new_group16();
59 break; 69 break;
60 case KEX_DH_GRP18_SHA512: 70 case KEX_DH_GRP18_SHA512:
diff --git a/kexgen.c b/kexgen.c
index 69348b964..c0e8c2f44 100644
--- a/kexgen.c
+++ b/kexgen.c
@@ -44,7 +44,7 @@
44static int input_kex_gen_init(int, u_int32_t, struct ssh *); 44static int input_kex_gen_init(int, u_int32_t, struct ssh *);
45static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); 45static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
46 46
47static int 47int
48kex_gen_hash( 48kex_gen_hash(
49 int hash_alg, 49 int hash_alg,
50 const struct sshbuf *client_version, 50 const struct sshbuf *client_version,
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..f6e1405eb
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,606 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#if defined(GSSAPI) && defined(WITH_OPENSSL)
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "sshbuf.h"
38#include "ssh2.h"
39#include "sshkey.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45#include "digest.h"
46#include "ssherr.h"
47
48#include "ssh-gss.h"
49
50int
51kexgss_client(struct ssh *ssh)
52{
53 struct kex *kex = ssh->kex;
54 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
55 recv_tok = GSS_C_EMPTY_BUFFER,
56 gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
57 Gssctxt *ctxt;
58 OM_uint32 maj_status, min_status, ret_flags;
59 struct sshbuf *server_blob = NULL;
60 struct sshbuf *shared_secret = NULL;
61 struct sshbuf *server_host_key_blob = NULL;
62 struct sshbuf *empty = NULL;
63 u_char *msg;
64 int type = 0;
65 int first = 1;
66 u_char hash[SSH_DIGEST_MAX_LENGTH];
67 size_t hashlen;
68 u_char c;
69 int r;
70
71 /* Initialise our GSSAPI world */
72 ssh_gssapi_build_ctx(&ctxt);
73 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
74 == GSS_C_NO_OID)
75 fatal("Couldn't identify host exchange");
76
77 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
78 fatal("Couldn't import hostname");
79
80 if (kex->gss_client &&
81 ssh_gssapi_client_identity(ctxt, kex->gss_client))
82 fatal("Couldn't acquire client credentials");
83
84 /* Step 1 */
85 switch (kex->kex_type) {
86 case KEX_GSS_GRP1_SHA1:
87 case KEX_GSS_GRP14_SHA1:
88 case KEX_GSS_GRP14_SHA256:
89 case KEX_GSS_GRP16_SHA512:
90 r = kex_dh_keypair(kex);
91 break;
92 case KEX_GSS_NISTP256_SHA256:
93 r = kex_ecdh_keypair(kex);
94 break;
95 case KEX_GSS_C25519_SHA256:
96 r = kex_c25519_keypair(kex);
97 break;
98 default:
99 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
100 }
101 if (r != 0)
102 return r;
103
104 token_ptr = GSS_C_NO_BUFFER;
105
106 do {
107 debug("Calling gss_init_sec_context");
108
109 maj_status = ssh_gssapi_init_ctx(ctxt,
110 kex->gss_deleg_creds, token_ptr, &send_tok,
111 &ret_flags);
112
113 if (GSS_ERROR(maj_status)) {
114 /* XXX Useles code: Missing send? */
115 if (send_tok.length != 0) {
116 if ((r = sshpkt_start(ssh,
117 SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
118 (r = sshpkt_put_string(ssh, send_tok.value,
119 send_tok.length)) != 0)
120 fatal("sshpkt failed: %s", ssh_err(r));
121 }
122 fatal("gss_init_context failed");
123 }
124
125 /* If we've got an old receive buffer get rid of it */
126 if (token_ptr != GSS_C_NO_BUFFER)
127 gss_release_buffer(&min_status, &recv_tok);
128
129 if (maj_status == GSS_S_COMPLETE) {
130 /* If mutual state flag is not true, kex fails */
131 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
132 fatal("Mutual authentication failed");
133
134 /* If integ avail flag is not true kex fails */
135 if (!(ret_flags & GSS_C_INTEG_FLAG))
136 fatal("Integrity check failed");
137 }
138
139 /*
140 * If we have data to send, then the last message that we
141 * received cannot have been a 'complete'.
142 */
143 if (send_tok.length != 0) {
144 if (first) {
145 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
146 (r = sshpkt_put_string(ssh, send_tok.value,
147 send_tok.length)) != 0 ||
148 (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
149 fatal("failed to construct packet: %s", ssh_err(r));
150 first = 0;
151 } else {
152 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
153 (r = sshpkt_put_string(ssh, send_tok.value,
154 send_tok.length)) != 0)
155 fatal("failed to construct packet: %s", ssh_err(r));
156 }
157 if ((r = sshpkt_send(ssh)) != 0)
158 fatal("failed to send packet: %s", ssh_err(r));
159 gss_release_buffer(&min_status, &send_tok);
160
161 /* If we've sent them data, they should reply */
162 do {
163 type = ssh_packet_read(ssh);
164 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
165 debug("Received KEXGSS_HOSTKEY");
166 if (server_host_key_blob)
167 fatal("Server host key received more than once");
168 if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
169 fatal("Failed to read server host key: %s", ssh_err(r));
170 }
171 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
172
173 switch (type) {
174 case SSH2_MSG_KEXGSS_CONTINUE:
175 debug("Received GSSAPI_CONTINUE");
176 if (maj_status == GSS_S_COMPLETE)
177 fatal("GSSAPI Continue received from server when complete");
178 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
179 &recv_tok)) != 0 ||
180 (r = sshpkt_get_end(ssh)) != 0)
181 fatal("Failed to read token: %s", ssh_err(r));
182 break;
183 case SSH2_MSG_KEXGSS_COMPLETE:
184 debug("Received GSSAPI_COMPLETE");
185 if (msg_tok.value != NULL)
186 fatal("Received GSSAPI_COMPLETE twice?");
187 if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
188 (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
189 &msg_tok)) != 0)
190 fatal("Failed to read message: %s", ssh_err(r));
191
192 /* Is there a token included? */
193 if ((r = sshpkt_get_u8(ssh, &c)) != 0)
194 fatal("sshpkt failed: %s", ssh_err(r));
195 if (c) {
196 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
197 ssh, &recv_tok)) != 0)
198 fatal("Failed to read token: %s", ssh_err(r));
199 /* If we're already complete - protocol error */
200 if (maj_status == GSS_S_COMPLETE)
201 sshpkt_disconnect(ssh, "Protocol error: received token when complete");
202 } else {
203 /* No token included */
204 if (maj_status != GSS_S_COMPLETE)
205 sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
206 }
207 if ((r = sshpkt_get_end(ssh)) != 0) {
208 fatal("Expecting end of packet.");
209 }
210 break;
211 case SSH2_MSG_KEXGSS_ERROR:
212 debug("Received Error");
213 if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
214 (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
215 (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
216 (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
217 (r = sshpkt_get_end(ssh)) != 0)
218 fatal("sshpkt_get failed: %s", ssh_err(r));
219 fatal("GSSAPI Error: \n%.400s", msg);
220 default:
221 sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
222 type);
223 }
224 token_ptr = &recv_tok;
225 } else {
226 /* No data, and not complete */
227 if (maj_status != GSS_S_COMPLETE)
228 fatal("Not complete, and no token output");
229 }
230 } while (maj_status & GSS_S_CONTINUE_NEEDED);
231
232 /*
233 * We _must_ have received a COMPLETE message in reply from the
234 * server, which will have set server_blob and msg_tok
235 */
236
237 if (type != SSH2_MSG_KEXGSS_COMPLETE)
238 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
239
240 /* compute shared secret */
241 switch (kex->kex_type) {
242 case KEX_GSS_GRP1_SHA1:
243 case KEX_GSS_GRP14_SHA1:
244 case KEX_GSS_GRP14_SHA256:
245 case KEX_GSS_GRP16_SHA512:
246 r = kex_dh_dec(kex, server_blob, &shared_secret);
247 break;
248 case KEX_GSS_C25519_SHA256:
249 if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
250 fatal("The received key has MSB of last octet set!");
251 r = kex_c25519_dec(kex, server_blob, &shared_secret);
252 break;
253 case KEX_GSS_NISTP256_SHA256:
254 if (sshbuf_len(server_blob) != 65)
255 fatal("The received NIST-P256 key did not match"
256 "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
257
258 if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
259 fatal("The received NIST-P256 key does not have first octet 0x04");
260
261 r = kex_ecdh_dec(kex, server_blob, &shared_secret);
262 break;
263 default:
264 r = SSH_ERR_INVALID_ARGUMENT;
265 break;
266 }
267 if (r != 0)
268 goto out;
269
270 if ((empty = sshbuf_new()) == NULL) {
271 r = SSH_ERR_ALLOC_FAIL;
272 goto out;
273 }
274
275 hashlen = sizeof(hash);
276 if ((r = kex_gen_hash(
277 kex->hash_alg,
278 kex->client_version,
279 kex->server_version,
280 kex->my,
281 kex->peer,
282 (server_host_key_blob ? server_host_key_blob : empty),
283 kex->client_pub,
284 server_blob,
285 shared_secret,
286 hash, &hashlen)) != 0)
287 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
288
289 gssbuf.value = hash;
290 gssbuf.length = hashlen;
291
292 /* Verify that the hash matches the MIC we just got. */
293 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
294 sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
295
296 gss_release_buffer(&min_status, &msg_tok);
297
298 if (kex->gss_deleg_creds)
299 ssh_gssapi_credentials_updated(ctxt);
300
301 if (gss_kex_context == NULL)
302 gss_kex_context = ctxt;
303 else
304 ssh_gssapi_delete_ctx(&ctxt);
305
306 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
307 r = kex_send_newkeys(ssh);
308
309out:
310 explicit_bzero(hash, sizeof(hash));
311 explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
312 sshbuf_free(empty);
313 sshbuf_free(server_host_key_blob);
314 sshbuf_free(server_blob);
315 sshbuf_free(shared_secret);
316 sshbuf_free(kex->client_pub);
317 kex->client_pub = NULL;
318 return r;
319}
320
321int
322kexgssgex_client(struct ssh *ssh)
323{
324 struct kex *kex = ssh->kex;
325 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
326 recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
327 msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
328 Gssctxt *ctxt;
329 OM_uint32 maj_status, min_status, ret_flags;
330 struct sshbuf *shared_secret = NULL;
331 BIGNUM *p = NULL;
332 BIGNUM *g = NULL;
333 struct sshbuf *buf = NULL;
334 struct sshbuf *server_host_key_blob = NULL;
335 struct sshbuf *server_blob = NULL;
336 BIGNUM *dh_server_pub = NULL;
337 u_char *msg;
338 int type = 0;
339 int first = 1;
340 u_char hash[SSH_DIGEST_MAX_LENGTH];
341 size_t hashlen;
342 const BIGNUM *pub_key, *dh_p, *dh_g;
343 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
344 struct sshbuf *empty = NULL;
345 u_char c;
346 int r;
347
348 /* Initialise our GSSAPI world */
349 ssh_gssapi_build_ctx(&ctxt);
350 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
351 == GSS_C_NO_OID)
352 fatal("Couldn't identify host exchange");
353
354 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
355 fatal("Couldn't import hostname");
356
357 if (kex->gss_client &&
358 ssh_gssapi_client_identity(ctxt, kex->gss_client))
359 fatal("Couldn't acquire client credentials");
360
361 debug("Doing group exchange");
362 nbits = dh_estimate(kex->dh_need * 8);
363
364 kex->min = DH_GRP_MIN;
365 kex->max = DH_GRP_MAX;
366 kex->nbits = nbits;
367 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
368 (r = sshpkt_put_u32(ssh, min)) != 0 ||
369 (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
370 (r = sshpkt_put_u32(ssh, max)) != 0 ||
371 (r = sshpkt_send(ssh)) != 0)
372 fatal("Failed to construct a packet: %s", ssh_err(r));
373
374 if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
375 fatal("Error: %s", ssh_err(r));
376
377 if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
378 (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
379 (r = sshpkt_get_end(ssh)) != 0)
380 fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
381
382 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
383 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
384 min, BN_num_bits(p), max);
385
386 if ((kex->dh = dh_new_group(g, p)) == NULL)
387 fatal("dn_new_group() failed");
388 p = g = NULL; /* belong to kex->dh now */
389
390 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
391 goto out;
392 DH_get0_key(kex->dh, &pub_key, NULL);
393
394 token_ptr = GSS_C_NO_BUFFER;
395
396 do {
397 /* Step 2 - call GSS_Init_sec_context() */
398 debug("Calling gss_init_sec_context");
399
400 maj_status = ssh_gssapi_init_ctx(ctxt,
401 kex->gss_deleg_creds, token_ptr, &send_tok,
402 &ret_flags);
403
404 if (GSS_ERROR(maj_status)) {
405 /* XXX Useles code: Missing send? */
406 if (send_tok.length != 0) {
407 if ((r = sshpkt_start(ssh,
408 SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
409 (r = sshpkt_put_string(ssh, send_tok.value,
410 send_tok.length)) != 0)
411 fatal("sshpkt failed: %s", ssh_err(r));
412 }
413 fatal("gss_init_context failed");
414 }
415
416 /* If we've got an old receive buffer get rid of it */
417 if (token_ptr != GSS_C_NO_BUFFER)
418 gss_release_buffer(&min_status, &recv_tok);
419
420 if (maj_status == GSS_S_COMPLETE) {
421 /* If mutual state flag is not true, kex fails */
422 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
423 fatal("Mutual authentication failed");
424
425 /* If integ avail flag is not true kex fails */
426 if (!(ret_flags & GSS_C_INTEG_FLAG))
427 fatal("Integrity check failed");
428 }
429
430 /*
431 * If we have data to send, then the last message that we
432 * received cannot have been a 'complete'.
433 */
434 if (send_tok.length != 0) {
435 if (first) {
436 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
437 (r = sshpkt_put_string(ssh, send_tok.value,
438 send_tok.length)) != 0 ||
439 (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
440 fatal("sshpkt failed: %s", ssh_err(r));
441 first = 0;
442 } else {
443 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
444 (r = sshpkt_put_string(ssh,send_tok.value,
445 send_tok.length)) != 0)
446 fatal("sshpkt failed: %s", ssh_err(r));
447 }
448 if ((r = sshpkt_send(ssh)) != 0)
449 fatal("sshpkt_send failed: %s", ssh_err(r));
450 gss_release_buffer(&min_status, &send_tok);
451
452 /* If we've sent them data, they should reply */
453 do {
454 type = ssh_packet_read(ssh);
455 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
456 debug("Received KEXGSS_HOSTKEY");
457 if (server_host_key_blob)
458 fatal("Server host key received more than once");
459 if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
460 fatal("sshpkt failed: %s", ssh_err(r));
461 }
462 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
463
464 switch (type) {
465 case SSH2_MSG_KEXGSS_CONTINUE:
466 debug("Received GSSAPI_CONTINUE");
467 if (maj_status == GSS_S_COMPLETE)
468 fatal("GSSAPI Continue received from server when complete");
469 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
470 &recv_tok)) != 0 ||
471 (r = sshpkt_get_end(ssh)) != 0)
472 fatal("sshpkt failed: %s", ssh_err(r));
473 break;
474 case SSH2_MSG_KEXGSS_COMPLETE:
475 debug("Received GSSAPI_COMPLETE");
476 if (msg_tok.value != NULL)
477 fatal("Received GSSAPI_COMPLETE twice?");
478 if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
479 (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
480 &msg_tok)) != 0)
481 fatal("sshpkt failed: %s", ssh_err(r));
482
483 /* Is there a token included? */
484 if ((r = sshpkt_get_u8(ssh, &c)) != 0)
485 fatal("sshpkt failed: %s", ssh_err(r));
486 if (c) {
487 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
488 ssh, &recv_tok)) != 0 ||
489 (r = sshpkt_get_end(ssh)) != 0)
490 fatal("sshpkt failed: %s", ssh_err(r));
491 /* If we're already complete - protocol error */
492 if (maj_status == GSS_S_COMPLETE)
493 sshpkt_disconnect(ssh, "Protocol error: received token when complete");
494 } else {
495 /* No token included */
496 if (maj_status != GSS_S_COMPLETE)
497 sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
498 }
499 break;
500 case SSH2_MSG_KEXGSS_ERROR:
501 debug("Received Error");
502 if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
503 (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
504 (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
505 (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
506 (r = sshpkt_get_end(ssh)) != 0)
507 fatal("sshpkt failed: %s", ssh_err(r));
508 fatal("GSSAPI Error: \n%.400s", msg);
509 default:
510 sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
511 type);
512 }
513 token_ptr = &recv_tok;
514 } else {
515 /* No data, and not complete */
516 if (maj_status != GSS_S_COMPLETE)
517 fatal("Not complete, and no token output");
518 }
519 } while (maj_status & GSS_S_CONTINUE_NEEDED);
520
521 /*
522 * We _must_ have received a COMPLETE message in reply from the
523 * server, which will have set dh_server_pub and msg_tok
524 */
525
526 if (type != SSH2_MSG_KEXGSS_COMPLETE)
527 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
528
529 /* 7. C verifies that the key Q_S is valid */
530 /* 8. C computes shared secret */
531 if ((buf = sshbuf_new()) == NULL ||
532 (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
533 (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
534 goto out;
535 sshbuf_free(buf);
536 buf = NULL;
537
538 if ((shared_secret = sshbuf_new()) == NULL) {
539 r = SSH_ERR_ALLOC_FAIL;
540 goto out;
541 }
542
543 if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
544 goto out;
545 if ((empty = sshbuf_new()) == NULL) {
546 r = SSH_ERR_ALLOC_FAIL;
547 goto out;
548 }
549
550 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
551 hashlen = sizeof(hash);
552 if ((r = kexgex_hash(
553 kex->hash_alg,
554 kex->client_version,
555 kex->server_version,
556 kex->my,
557 kex->peer,
558 (server_host_key_blob ? server_host_key_blob : empty),
559 kex->min, kex->nbits, kex->max,
560 dh_p, dh_g,
561 pub_key,
562 dh_server_pub,
563 sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
564 hash, &hashlen)) != 0)
565 fatal("Failed to calculate hash: %s", ssh_err(r));
566
567 gssbuf.value = hash;
568 gssbuf.length = hashlen;
569
570 /* Verify that the hash matches the MIC we just got. */
571 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
572 sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
573
574 gss_release_buffer(&min_status, &msg_tok);
575
576 /* save session id */
577 if (kex->session_id == NULL) {
578 kex->session_id_len = hashlen;
579 kex->session_id = xmalloc(kex->session_id_len);
580 memcpy(kex->session_id, hash, kex->session_id_len);
581 }
582
583 if (kex->gss_deleg_creds)
584 ssh_gssapi_credentials_updated(ctxt);
585
586 if (gss_kex_context == NULL)
587 gss_kex_context = ctxt;
588 else
589 ssh_gssapi_delete_ctx(&ctxt);
590
591 /* Finally derive the keys and send them */
592 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
593 r = kex_send_newkeys(ssh);
594out:
595 sshbuf_free(buf);
596 sshbuf_free(server_blob);
597 sshbuf_free(empty);
598 explicit_bzero(hash, sizeof(hash));
599 DH_free(kex->dh);
600 kex->dh = NULL;
601 BN_clear_free(dh_server_pub);
602 sshbuf_free(shared_secret);
603 sshbuf_free(server_host_key_blob);
604 return r;
605}
606#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..60bc02deb
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,474 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#if defined(GSSAPI) && defined(WITH_OPENSSL)
28
29#include <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "sshbuf.h"
36#include "ssh2.h"
37#include "sshkey.h"
38#include "cipher.h"
39#include "kex.h"
40#include "log.h"
41#include "packet.h"
42#include "dh.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */
46#include "servconf.h"
47#include "ssh-gss.h"
48#include "digest.h"
49#include "ssherr.h"
50
51extern ServerOptions options;
52
53int
54kexgss_server(struct ssh *ssh)
55{
56 struct kex *kex = ssh->kex;
57 OM_uint32 maj_status, min_status;
58
59 /*
60 * Some GSSAPI implementations use the input value of ret_flags (an
61 * output variable) as a means of triggering mechanism specific
62 * features. Initializing it to zero avoids inadvertently
63 * activating this non-standard behaviour.
64 */
65
66 OM_uint32 ret_flags = 0;
67 gss_buffer_desc gssbuf, recv_tok, msg_tok;
68 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
69 Gssctxt *ctxt = NULL;
70 struct sshbuf *shared_secret = NULL;
71 struct sshbuf *client_pubkey = NULL;
72 struct sshbuf *server_pubkey = NULL;
73 struct sshbuf *empty = sshbuf_new();
74 int type = 0;
75 gss_OID oid;
76 char *mechs;
77 u_char hash[SSH_DIGEST_MAX_LENGTH];
78 size_t hashlen;
79 int r;
80
81 /* Initialise GSSAPI */
82
83 /* If we're rekeying, privsep means that some of the private structures
84 * in the GSSAPI code are no longer available. This kludges them back
85 * into life
86 */
87 if (!ssh_gssapi_oid_table_ok()) {
88 mechs = ssh_gssapi_server_mechanisms();
89 free(mechs);
90 }
91
92 debug2("%s: Identifying %s", __func__, kex->name);
93 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
94 if (oid == GSS_C_NO_OID)
95 fatal("Unknown gssapi mechanism");
96
97 debug2("%s: Acquiring credentials", __func__);
98
99 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
100 fatal("Unable to acquire credentials for the server");
101
102 do {
103 debug("Wait SSH2_MSG_KEXGSS_INIT");
104 type = ssh_packet_read(ssh);
105 switch(type) {
106 case SSH2_MSG_KEXGSS_INIT:
107 if (client_pubkey != NULL)
108 fatal("Received KEXGSS_INIT after initialising");
109 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
110 &recv_tok)) != 0 ||
111 (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
112 (r = sshpkt_get_end(ssh)) != 0)
113 fatal("sshpkt failed: %s", ssh_err(r));
114
115 switch (kex->kex_type) {
116 case KEX_GSS_GRP1_SHA1:
117 case KEX_GSS_GRP14_SHA1:
118 case KEX_GSS_GRP14_SHA256:
119 case KEX_GSS_GRP16_SHA512:
120 r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
121 &shared_secret);
122 break;
123 case KEX_GSS_NISTP256_SHA256:
124 r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
125 &shared_secret);
126 break;
127 case KEX_GSS_C25519_SHA256:
128 r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
129 &shared_secret);
130 break;
131 default:
132 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
133 }
134 if (r != 0)
135 goto out;
136
137 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
138 break;
139 case SSH2_MSG_KEXGSS_CONTINUE:
140 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
141 &recv_tok)) != 0 ||
142 (r = sshpkt_get_end(ssh)) != 0)
143 fatal("sshpkt failed: %s", ssh_err(r));
144 break;
145 default:
146 sshpkt_disconnect(ssh,
147 "Protocol error: didn't expect packet type %d",
148 type);
149 }
150
151 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
152 &send_tok, &ret_flags));
153
154 gss_release_buffer(&min_status, &recv_tok);
155
156 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
157 fatal("Zero length token output when incomplete");
158
159 if (client_pubkey == NULL)
160 fatal("No client public key");
161
162 if (maj_status & GSS_S_CONTINUE_NEEDED) {
163 debug("Sending GSSAPI_CONTINUE");
164 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
165 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
166 (r = sshpkt_send(ssh)) != 0)
167 fatal("sshpkt failed: %s", ssh_err(r));
168 gss_release_buffer(&min_status, &send_tok);
169 }
170 } while (maj_status & GSS_S_CONTINUE_NEEDED);
171
172 if (GSS_ERROR(maj_status)) {
173 if (send_tok.length > 0) {
174 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
175 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
176 (r = sshpkt_send(ssh)) != 0)
177 fatal("sshpkt failed: %s", ssh_err(r));
178 }
179 fatal("accept_ctx died");
180 }
181
182 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
183 fatal("Mutual Authentication flag wasn't set");
184
185 if (!(ret_flags & GSS_C_INTEG_FLAG))
186 fatal("Integrity flag wasn't set");
187
188 hashlen = sizeof(hash);
189 if ((r = kex_gen_hash(
190 kex->hash_alg,
191 kex->client_version,
192 kex->server_version,
193 kex->peer,
194 kex->my,
195 empty,
196 client_pubkey,
197 server_pubkey,
198 shared_secret,
199 hash, &hashlen)) != 0)
200 goto out;
201
202 gssbuf.value = hash;
203 gssbuf.length = hashlen;
204
205 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
206 fatal("Couldn't get MIC");
207
208 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
209 (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
210 (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
211 fatal("sshpkt failed: %s", ssh_err(r));
212
213 if (send_tok.length != 0) {
214 if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
215 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
216 fatal("sshpkt failed: %s", ssh_err(r));
217 } else {
218 if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
219 fatal("sshpkt failed: %s", ssh_err(r));
220 }
221 if ((r = sshpkt_send(ssh)) != 0)
222 fatal("sshpkt_send failed: %s", ssh_err(r));
223
224 gss_release_buffer(&min_status, &send_tok);
225 gss_release_buffer(&min_status, &msg_tok);
226
227 if (gss_kex_context == NULL)
228 gss_kex_context = ctxt;
229 else
230 ssh_gssapi_delete_ctx(&ctxt);
231
232 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
233 r = kex_send_newkeys(ssh);
234
235 /* If this was a rekey, then save out any delegated credentials we
236 * just exchanged. */
237 if (options.gss_store_rekey)
238 ssh_gssapi_rekey_creds();
239out:
240 sshbuf_free(empty);
241 explicit_bzero(hash, sizeof(hash));
242 sshbuf_free(shared_secret);
243 sshbuf_free(client_pubkey);
244 sshbuf_free(server_pubkey);
245 return r;
246}
247
248int
249kexgssgex_server(struct ssh *ssh)
250{
251 struct kex *kex = ssh->kex;
252 OM_uint32 maj_status, min_status;
253
254 /*
255 * Some GSSAPI implementations use the input value of ret_flags (an
256 * output variable) as a means of triggering mechanism specific
257 * features. Initializing it to zero avoids inadvertently
258 * activating this non-standard behaviour.
259 */
260
261 OM_uint32 ret_flags = 0;
262 gss_buffer_desc gssbuf, recv_tok, msg_tok;
263 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
264 Gssctxt *ctxt = NULL;
265 struct sshbuf *shared_secret = NULL;
266 int type = 0;
267 gss_OID oid;
268 char *mechs;
269 u_char hash[SSH_DIGEST_MAX_LENGTH];
270 size_t hashlen;
271 BIGNUM *dh_client_pub = NULL;
272 const BIGNUM *pub_key, *dh_p, *dh_g;
273 int min = -1, max = -1, nbits = -1;
274 int cmin = -1, cmax = -1; /* client proposal */
275 struct sshbuf *empty = sshbuf_new();
276 int r;
277
278 /* Initialise GSSAPI */
279
280 /* If we're rekeying, privsep means that some of the private structures
281 * in the GSSAPI code are no longer available. This kludges them back
282 * into life
283 */
284 if (!ssh_gssapi_oid_table_ok())
285 if ((mechs = ssh_gssapi_server_mechanisms()))
286 free(mechs);
287
288 debug2("%s: Identifying %s", __func__, kex->name);
289 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
290 if (oid == GSS_C_NO_OID)
291 fatal("Unknown gssapi mechanism");
292
293 debug2("%s: Acquiring credentials", __func__);
294
295 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
296 fatal("Unable to acquire credentials for the server");
297
298 /* 5. S generates an ephemeral key pair (do the allocations early) */
299 debug("Doing group exchange");
300 ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
301 /* store client proposal to provide valid signature */
302 if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
303 (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
304 (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
305 (r = sshpkt_get_end(ssh)) != 0)
306 fatal("sshpkt failed: %s", ssh_err(r));
307 kex->nbits = nbits;
308 kex->min = cmin;
309 kex->max = cmax;
310 min = MAX(DH_GRP_MIN, cmin);
311 max = MIN(DH_GRP_MAX, cmax);
312 nbits = MAXIMUM(DH_GRP_MIN, nbits);
313 nbits = MINIMUM(DH_GRP_MAX, nbits);
314 if (max < min || nbits < min || max < nbits)
315 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
316 min, nbits, max);
317 kex->dh = PRIVSEP(choose_dh(min, nbits, max));
318 if (kex->dh == NULL) {
319 sshpkt_disconnect(ssh, "Protocol error: no matching group found");
320 fatal("Protocol error: no matching group found");
321 }
322
323 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
324 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
325 (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
326 (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
327 (r = sshpkt_send(ssh)) != 0)
328 fatal("sshpkt failed: %s", ssh_err(r));
329
330 if ((r = ssh_packet_write_wait(ssh)) != 0)
331 fatal("ssh_packet_write_wait: %s", ssh_err(r));
332
333 /* Compute our exchange value in parallel with the client */
334 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
335 goto out;
336
337 do {
338 debug("Wait SSH2_MSG_GSSAPI_INIT");
339 type = ssh_packet_read(ssh);
340 switch(type) {
341 case SSH2_MSG_KEXGSS_INIT:
342 if (dh_client_pub != NULL)
343 fatal("Received KEXGSS_INIT after initialising");
344 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
345 &recv_tok)) != 0 ||
346 (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
347 (r = sshpkt_get_end(ssh)) != 0)
348 fatal("sshpkt failed: %s", ssh_err(r));
349
350 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
351 break;
352 case SSH2_MSG_KEXGSS_CONTINUE:
353 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
354 &recv_tok)) != 0 ||
355 (r = sshpkt_get_end(ssh)) != 0)
356 fatal("sshpkt failed: %s", ssh_err(r));
357 break;
358 default:
359 sshpkt_disconnect(ssh,
360 "Protocol error: didn't expect packet type %d",
361 type);
362 }
363
364 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
365 &send_tok, &ret_flags));
366
367 gss_release_buffer(&min_status, &recv_tok);
368
369 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
370 fatal("Zero length token output when incomplete");
371
372 if (dh_client_pub == NULL)
373 fatal("No client public key");
374
375 if (maj_status & GSS_S_CONTINUE_NEEDED) {
376 debug("Sending GSSAPI_CONTINUE");
377 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
378 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
379 (r = sshpkt_send(ssh)) != 0)
380 fatal("sshpkt failed: %s", ssh_err(r));
381 gss_release_buffer(&min_status, &send_tok);
382 }
383 } while (maj_status & GSS_S_CONTINUE_NEEDED);
384
385 if (GSS_ERROR(maj_status)) {
386 if (send_tok.length > 0) {
387 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
388 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
389 (r = sshpkt_send(ssh)) != 0)
390 fatal("sshpkt failed: %s", ssh_err(r));
391 }
392 fatal("accept_ctx died");
393 }
394
395 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
396 fatal("Mutual Authentication flag wasn't set");
397
398 if (!(ret_flags & GSS_C_INTEG_FLAG))
399 fatal("Integrity flag wasn't set");
400
401 /* calculate shared secret */
402 if ((shared_secret = sshbuf_new()) == NULL) {
403 r = SSH_ERR_ALLOC_FAIL;
404 goto out;
405 }
406 if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
407 goto out;
408
409 DH_get0_key(kex->dh, &pub_key, NULL);
410 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
411 hashlen = sizeof(hash);
412 if ((r = kexgex_hash(
413 kex->hash_alg,
414 kex->client_version,
415 kex->server_version,
416 kex->peer,
417 kex->my,
418 empty,
419 cmin, nbits, cmax,
420 dh_p, dh_g,
421 dh_client_pub,
422 pub_key,
423 sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
424 hash, &hashlen)) != 0)
425 fatal("kexgex_hash failed: %s", ssh_err(r));
426
427 gssbuf.value = hash;
428 gssbuf.length = hashlen;
429
430 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
431 fatal("Couldn't get MIC");
432
433 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
434 (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
435 (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
436 fatal("sshpkt failed: %s", ssh_err(r));
437
438 if (send_tok.length != 0) {
439 if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
440 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
441 fatal("sshpkt failed: %s", ssh_err(r));
442 } else {
443 if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
444 fatal("sshpkt failed: %s", ssh_err(r));
445 }
446 if ((r = sshpkt_send(ssh)) != 0)
447 fatal("sshpkt failed: %s", ssh_err(r));
448
449 gss_release_buffer(&min_status, &send_tok);
450 gss_release_buffer(&min_status, &msg_tok);
451
452 if (gss_kex_context == NULL)
453 gss_kex_context = ctxt;
454 else
455 ssh_gssapi_delete_ctx(&ctxt);
456
457 /* Finally derive the keys and send them */
458 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
459 r = kex_send_newkeys(ssh);
460
461 /* If this was a rekey, then save out any delegated credentials we
462 * just exchanged. */
463 if (options.gss_store_rekey)
464 ssh_gssapi_rekey_creds();
465out:
466 sshbuf_free(empty);
467 explicit_bzero(hash, sizeof(hash));
468 DH_free(kex->dh);
469 kex->dh = NULL;
470 BN_clear_free(dh_client_pub);
471 sshbuf_free(shared_secret);
472 return r;
473}
474#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --git a/monitor.c b/monitor.c
index 2ce89fe90..ebf76c7f9 100644
--- a/monitor.c
+++ b/monitor.c
@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
148int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *); 148int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
149int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); 149int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
150int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); 150int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
151int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
152int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
151#endif 153#endif
152 154
153#ifdef SSH_AUDIT_EVENTS 155#ifdef SSH_AUDIT_EVENTS
@@ -220,11 +222,18 @@ struct mon_table mon_dispatch_proto20[] = {
220 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, 222 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
221 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, 223 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
222 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, 224 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
225 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
223#endif 226#endif
224 {0, 0, NULL} 227 {0, 0, NULL}
225}; 228};
226 229
227struct mon_table mon_dispatch_postauth20[] = { 230struct mon_table mon_dispatch_postauth20[] = {
231#ifdef GSSAPI
232 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
233 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
234 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
235 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
236#endif
228#ifdef WITH_OPENSSL 237#ifdef WITH_OPENSSL
229 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 238 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
230#endif 239#endif
@@ -293,6 +302,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
293 /* Permit requests for moduli and signatures */ 302 /* Permit requests for moduli and signatures */
294 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 303 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
295 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 304 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
305#ifdef GSSAPI
306 /* and for the GSSAPI key exchange */
307 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
308#endif
296 309
297 /* The first few requests do not require asynchronous access */ 310 /* The first few requests do not require asynchronous access */
298 while (!authenticated) { 311 while (!authenticated) {
@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
406 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 419 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
407 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 420 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
408 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 421 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
422#ifdef GSSAPI
423 /* and for the GSSAPI key exchange */
424 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
425#endif
409 426
410 if (auth_opts->permit_pty_flag) { 427 if (auth_opts->permit_pty_flag) {
411 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); 428 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
@@ -1713,6 +1730,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
1713# ifdef OPENSSL_HAS_ECC 1730# ifdef OPENSSL_HAS_ECC
1714 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 1731 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
1715# endif 1732# endif
1733# ifdef GSSAPI
1734 if (options.gss_keyex) {
1735 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1736 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1737 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
1738 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
1739 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
1740 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
1741 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
1742 }
1743# endif
1716#endif /* WITH_OPENSSL */ 1744#endif /* WITH_OPENSSL */
1717 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 1745 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
1718 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 1746 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
@@ -1806,8 +1834,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1806 u_char *p; 1834 u_char *p;
1807 int r; 1835 int r;
1808 1836
1809 if (!options.gss_authentication) 1837 if (!options.gss_authentication && !options.gss_keyex)
1810 fatal("%s: GSSAPI authentication not enabled", __func__); 1838 fatal("%s: GSSAPI not enabled", __func__);
1811 1839
1812 if ((r = sshbuf_get_string(m, &p, &len)) != 0) 1840 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1813 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1841 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1839,8 +1867,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1839 OM_uint32 flags = 0; /* GSI needs this */ 1867 OM_uint32 flags = 0; /* GSI needs this */
1840 int r; 1868 int r;
1841 1869
1842 if (!options.gss_authentication) 1870 if (!options.gss_authentication && !options.gss_keyex)
1843 fatal("%s: GSSAPI authentication not enabled", __func__); 1871 fatal("%s: GSSAPI not enabled", __func__);
1844 1872
1845 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) 1873 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
1846 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1874 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1860,6 +1888,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1860 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1888 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1861 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1889 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1862 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1890 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1891 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1863 } 1892 }
1864 return (0); 1893 return (0);
1865} 1894}
@@ -1871,8 +1900,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1871 OM_uint32 ret; 1900 OM_uint32 ret;
1872 int r; 1901 int r;
1873 1902
1874 if (!options.gss_authentication) 1903 if (!options.gss_authentication && !options.gss_keyex)
1875 fatal("%s: GSSAPI authentication not enabled", __func__); 1904 fatal("%s: GSSAPI not enabled", __func__);
1876 1905
1877 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || 1906 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
1878 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) 1907 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
@@ -1898,13 +1927,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1898int 1927int
1899mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) 1928mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1900{ 1929{
1901 int r, authenticated; 1930 int r, authenticated, kex;
1902 const char *displayname; 1931 const char *displayname;
1903 1932
1904 if (!options.gss_authentication) 1933 if (!options.gss_authentication && !options.gss_keyex)
1905 fatal("%s: GSSAPI authentication not enabled", __func__); 1934 fatal("%s: GSSAPI not enabled", __func__);
1906 1935
1907 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1936 if ((r = sshbuf_get_u32(m, &kex)) != 0)
1937 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1938
1939 authenticated = authctxt->valid &&
1940 ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
1908 1941
1909 sshbuf_reset(m); 1942 sshbuf_reset(m);
1910 if ((r = sshbuf_put_u32(m, authenticated)) != 0) 1943 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
@@ -1913,7 +1946,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1913 debug3("%s: sending result %d", __func__, authenticated); 1946 debug3("%s: sending result %d", __func__, authenticated);
1914 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); 1947 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
1915 1948
1916 auth_method = "gssapi-with-mic"; 1949 if (kex) {
1950 auth_method = "gssapi-keyex";
1951 } else {
1952 auth_method = "gssapi-with-mic";
1953 }
1917 1954
1918 if ((displayname = ssh_gssapi_displayname()) != NULL) 1955 if ((displayname = ssh_gssapi_displayname()) != NULL)
1919 auth2_record_info(authctxt, "%s", displayname); 1956 auth2_record_info(authctxt, "%s", displayname);
@@ -1921,5 +1958,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1921 /* Monitor loop will terminate if authenticated */ 1958 /* Monitor loop will terminate if authenticated */
1922 return (authenticated); 1959 return (authenticated);
1923} 1960}
1961
1962int
1963mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
1964{
1965 gss_buffer_desc data;
1966 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1967 OM_uint32 major, minor;
1968 size_t len;
1969 u_char *p = NULL;
1970 int r;
1971
1972 if (!options.gss_authentication && !options.gss_keyex)
1973 fatal("%s: GSSAPI not enabled", __func__);
1974
1975 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1976 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1977 data.value = p;
1978 data.length = len;
1979 /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
1980 if (data.length != 20 && data.length != 32 && data.length != 64)
1981 fatal("%s: data length incorrect: %d", __func__,
1982 (int) data.length);
1983
1984 /* Save the session ID on the first time around */
1985 if (session_id2_len == 0) {
1986 session_id2_len = data.length;
1987 session_id2 = xmalloc(session_id2_len);
1988 memcpy(session_id2, data.value, session_id2_len);
1989 }
1990 major = ssh_gssapi_sign(gsscontext, &data, &hash);
1991
1992 free(data.value);
1993
1994 sshbuf_reset(m);
1995
1996 if ((r = sshbuf_put_u32(m, major)) != 0 ||
1997 (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
1998 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1999
2000 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2001
2002 gss_release_buffer(&minor, &hash);
2003
2004 /* Turn on getpwnam permissions */
2005 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2006
2007 /* And credential updating, for when rekeying */
2008 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2009
2010 return (0);
2011}
2012
2013int
2014mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
2015 ssh_gssapi_ccache store;
2016 int r, ok;
2017
2018 if (!options.gss_authentication && !options.gss_keyex)
2019 fatal("%s: GSSAPI not enabled", __func__);
2020
2021 if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
2022 (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
2023 (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
2024 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2025
2026 ok = ssh_gssapi_update_creds(&store);
2027
2028 free(store.filename);
2029 free(store.envvar);
2030 free(store.envval);
2031
2032 sshbuf_reset(m);
2033 if ((r = sshbuf_put_u32(m, ok)) != 0)
2034 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2035
2036 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2037
2038 return(0);
2039}
2040
1924#endif /* GSSAPI */ 2041#endif /* GSSAPI */
1925 2042
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 001a8fa1c..6edb509a3 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -993,13 +993,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
993} 993}
994 994
995int 995int
996mm_ssh_gssapi_userok(char *user) 996mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
997{ 997{
998 struct sshbuf *m; 998 struct sshbuf *m;
999 int r, authenticated = 0; 999 int r, authenticated = 0;
1000 1000
1001 if ((m = sshbuf_new()) == NULL) 1001 if ((m = sshbuf_new()) == NULL)
1002 fatal("%s: sshbuf_new failed", __func__); 1002 fatal("%s: sshbuf_new failed", __func__);
1003 if ((r = sshbuf_put_u32(m, kex)) != 0)
1004 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1003 1005
1004 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); 1006 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
1005 mm_request_receive_expect(pmonitor->m_recvfd, 1007 mm_request_receive_expect(pmonitor->m_recvfd,
@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user)
1012 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1014 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1013 return (authenticated); 1015 return (authenticated);
1014} 1016}
1017
1018OM_uint32
1019mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1020{
1021 struct sshbuf *m;
1022 OM_uint32 major;
1023 int r;
1024
1025 if ((m = sshbuf_new()) == NULL)
1026 fatal("%s: sshbuf_new failed", __func__);
1027 if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
1028 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1029
1030 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
1031 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
1032
1033 if ((r = sshbuf_get_u32(m, &major)) != 0 ||
1034 (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
1035 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1036
1037 sshbuf_free(m);
1038
1039 return (major);
1040}
1041
1042int
1043mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1044{
1045 struct sshbuf *m;
1046 int r, ok;
1047
1048 if ((m = sshbuf_new()) == NULL)
1049 fatal("%s: sshbuf_new failed", __func__);
1050
1051 if ((r = sshbuf_put_cstring(m,
1052 store->filename ? store->filename : "")) != 0 ||
1053 (r = sshbuf_put_cstring(m,
1054 store->envvar ? store->envvar : "")) != 0 ||
1055 (r = sshbuf_put_cstring(m,
1056 store->envval ? store->envval : "")) != 0)
1057 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1058
1059 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
1060 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
1061
1062 if ((r = sshbuf_get_u32(m, &ok)) != 0)
1063 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1064
1065 sshbuf_free(m);
1066
1067 return (ok);
1068}
1069
1015#endif /* GSSAPI */ 1070#endif /* GSSAPI */
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 23ab096aa..485590c18 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -64,8 +64,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
64OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 64OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
65OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 65OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
66 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 66 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
67int mm_ssh_gssapi_userok(char *user); 67int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
68OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 68OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
69OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
70int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
69#endif 71#endif
70 72
71#ifdef USE_PAM 73#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index f3cac6b3a..da8022dd0 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
@@ -160,6 +161,8 @@ typedef enum {
160 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 161 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
161 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 162 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
162 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 163 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
164 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
165 oGssServerIdentity, oGssKexAlgorithms,
163 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 166 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
164 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, 167 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
165 oHashKnownHosts, 168 oHashKnownHosts,
@@ -204,10 +207,22 @@ static struct {
204 /* Sometimes-unsupported options */ 207 /* Sometimes-unsupported options */
205#if defined(GSSAPI) 208#if defined(GSSAPI)
206 { "gssapiauthentication", oGssAuthentication }, 209 { "gssapiauthentication", oGssAuthentication },
210 { "gssapikeyexchange", oGssKeyEx },
207 { "gssapidelegatecredentials", oGssDelegateCreds }, 211 { "gssapidelegatecredentials", oGssDelegateCreds },
212 { "gssapitrustdns", oGssTrustDns },
213 { "gssapiclientidentity", oGssClientIdentity },
214 { "gssapiserveridentity", oGssServerIdentity },
215 { "gssapirenewalforcesrekey", oGssRenewalRekey },
216 { "gssapikexalgorithms", oGssKexAlgorithms },
208# else 217# else
209 { "gssapiauthentication", oUnsupported }, 218 { "gssapiauthentication", oUnsupported },
219 { "gssapikeyexchange", oUnsupported },
210 { "gssapidelegatecredentials", oUnsupported }, 220 { "gssapidelegatecredentials", oUnsupported },
221 { "gssapitrustdns", oUnsupported },
222 { "gssapiclientidentity", oUnsupported },
223 { "gssapiserveridentity", oUnsupported },
224 { "gssapirenewalforcesrekey", oUnsupported },
225 { "gssapikexalgorithms", oUnsupported },
211#endif 226#endif
212#ifdef ENABLE_PKCS11 227#ifdef ENABLE_PKCS11
213 { "pkcs11provider", oPKCS11Provider }, 228 { "pkcs11provider", oPKCS11Provider },
@@ -1029,10 +1044,42 @@ parse_time:
1029 intptr = &options->gss_authentication; 1044 intptr = &options->gss_authentication;
1030 goto parse_flag; 1045 goto parse_flag;
1031 1046
1047 case oGssKeyEx:
1048 intptr = &options->gss_keyex;
1049 goto parse_flag;
1050
1032 case oGssDelegateCreds: 1051 case oGssDelegateCreds:
1033 intptr = &options->gss_deleg_creds; 1052 intptr = &options->gss_deleg_creds;
1034 goto parse_flag; 1053 goto parse_flag;
1035 1054
1055 case oGssTrustDns:
1056 intptr = &options->gss_trust_dns;
1057 goto parse_flag;
1058
1059 case oGssClientIdentity:
1060 charptr = &options->gss_client_identity;
1061 goto parse_string;
1062
1063 case oGssServerIdentity:
1064 charptr = &options->gss_server_identity;
1065 goto parse_string;
1066
1067 case oGssRenewalRekey:
1068 intptr = &options->gss_renewal_rekey;
1069 goto parse_flag;
1070
1071 case oGssKexAlgorithms:
1072 arg = strdelim(&s);
1073 if (!arg || *arg == '\0')
1074 fatal("%.200s line %d: Missing argument.",
1075 filename, linenum);
1076 if (!kex_gss_names_valid(arg))
1077 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1078 filename, linenum, arg ? arg : "<NONE>");
1079 if (*activep && options->gss_kex_algorithms == NULL)
1080 options->gss_kex_algorithms = xstrdup(arg);
1081 break;
1082
1036 case oBatchMode: 1083 case oBatchMode:
1037 intptr = &options->batch_mode; 1084 intptr = &options->batch_mode;
1038 goto parse_flag; 1085 goto parse_flag;
@@ -1911,7 +1958,13 @@ initialize_options(Options * options)
1911 options->pubkey_authentication = -1; 1958 options->pubkey_authentication = -1;
1912 options->challenge_response_authentication = -1; 1959 options->challenge_response_authentication = -1;
1913 options->gss_authentication = -1; 1960 options->gss_authentication = -1;
1961 options->gss_keyex = -1;
1914 options->gss_deleg_creds = -1; 1962 options->gss_deleg_creds = -1;
1963 options->gss_trust_dns = -1;
1964 options->gss_renewal_rekey = -1;
1965 options->gss_client_identity = NULL;
1966 options->gss_server_identity = NULL;
1967 options->gss_kex_algorithms = NULL;
1915 options->password_authentication = -1; 1968 options->password_authentication = -1;
1916 options->kbd_interactive_authentication = -1; 1969 options->kbd_interactive_authentication = -1;
1917 options->kbd_interactive_devices = NULL; 1970 options->kbd_interactive_devices = NULL;
@@ -2059,8 +2112,18 @@ fill_default_options(Options * options)
2059 options->challenge_response_authentication = 1; 2112 options->challenge_response_authentication = 1;
2060 if (options->gss_authentication == -1) 2113 if (options->gss_authentication == -1)
2061 options->gss_authentication = 0; 2114 options->gss_authentication = 0;
2115 if (options->gss_keyex == -1)
2116 options->gss_keyex = 0;
2062 if (options->gss_deleg_creds == -1) 2117 if (options->gss_deleg_creds == -1)
2063 options->gss_deleg_creds = 0; 2118 options->gss_deleg_creds = 0;
2119 if (options->gss_trust_dns == -1)
2120 options->gss_trust_dns = 0;
2121 if (options->gss_renewal_rekey == -1)
2122 options->gss_renewal_rekey = 0;
2123#ifdef GSSAPI
2124 if (options->gss_kex_algorithms == NULL)
2125 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
2126#endif
2064 if (options->password_authentication == -1) 2127 if (options->password_authentication == -1)
2065 options->password_authentication = 1; 2128 options->password_authentication = 1;
2066 if (options->kbd_interactive_authentication == -1) 2129 if (options->kbd_interactive_authentication == -1)
@@ -2702,7 +2765,14 @@ dump_client_config(Options *o, const char *host)
2702 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); 2765 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
2703#ifdef GSSAPI 2766#ifdef GSSAPI
2704 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); 2767 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
2768 dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
2705 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); 2769 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
2770 dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
2771 dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
2772 dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
2773 dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
2774 dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
2775 o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
2706#endif /* GSSAPI */ 2776#endif /* GSSAPI */
2707 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); 2777 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
2708 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); 2778 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
diff --git a/readconf.h b/readconf.h
index feedb3d20..a8a8870d7 100644
--- a/readconf.h
+++ b/readconf.h
@@ -41,7 +41,13 @@ typedef struct {
41 int challenge_response_authentication; 41 int challenge_response_authentication;
42 /* Try S/Key or TIS, authentication. */ 42 /* Try S/Key or TIS, authentication. */
43 int gss_authentication; /* Try GSS authentication */ 43 int gss_authentication; /* Try GSS authentication */
44 int gss_keyex; /* Try GSS key exchange */
44 int gss_deleg_creds; /* Delegate GSS credentials */ 45 int gss_deleg_creds; /* Delegate GSS credentials */
46 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
47 int gss_renewal_rekey; /* Credential renewal forces rekey */
48 char *gss_client_identity; /* Principal to initiate GSSAPI with */
49 char *gss_server_identity; /* GSSAPI target principal */
50 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
45 int password_authentication; /* Try password 51 int password_authentication; /* Try password
46 * authentication. */ 52 * authentication. */
47 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 53 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index 70f5f73f0..191575a16 100644
--- a/servconf.c
+++ b/servconf.c
@@ -69,6 +69,7 @@
69#include "auth.h" 69#include "auth.h"
70#include "myproposal.h" 70#include "myproposal.h"
71#include "digest.h" 71#include "digest.h"
72#include "ssh-gss.h"
72 73
73static void add_listen_addr(ServerOptions *, const char *, 74static void add_listen_addr(ServerOptions *, const char *,
74 const char *, int); 75 const char *, int);
@@ -133,8 +134,11 @@ initialize_server_options(ServerOptions *options)
133 options->kerberos_ticket_cleanup = -1; 134 options->kerberos_ticket_cleanup = -1;
134 options->kerberos_get_afs_token = -1; 135 options->kerberos_get_afs_token = -1;
135 options->gss_authentication=-1; 136 options->gss_authentication=-1;
137 options->gss_keyex = -1;
136 options->gss_cleanup_creds = -1; 138 options->gss_cleanup_creds = -1;
137 options->gss_strict_acceptor = -1; 139 options->gss_strict_acceptor = -1;
140 options->gss_store_rekey = -1;
141 options->gss_kex_algorithms = NULL;
138 options->password_authentication = -1; 142 options->password_authentication = -1;
139 options->kbd_interactive_authentication = -1; 143 options->kbd_interactive_authentication = -1;
140 options->challenge_response_authentication = -1; 144 options->challenge_response_authentication = -1;
@@ -375,10 +379,18 @@ fill_default_server_options(ServerOptions *options)
375 options->kerberos_get_afs_token = 0; 379 options->kerberos_get_afs_token = 0;
376 if (options->gss_authentication == -1) 380 if (options->gss_authentication == -1)
377 options->gss_authentication = 0; 381 options->gss_authentication = 0;
382 if (options->gss_keyex == -1)
383 options->gss_keyex = 0;
378 if (options->gss_cleanup_creds == -1) 384 if (options->gss_cleanup_creds == -1)
379 options->gss_cleanup_creds = 1; 385 options->gss_cleanup_creds = 1;
380 if (options->gss_strict_acceptor == -1) 386 if (options->gss_strict_acceptor == -1)
381 options->gss_strict_acceptor = 1; 387 options->gss_strict_acceptor = 1;
388 if (options->gss_store_rekey == -1)
389 options->gss_store_rekey = 0;
390#ifdef GSSAPI
391 if (options->gss_kex_algorithms == NULL)
392 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
393#endif
382 if (options->password_authentication == -1) 394 if (options->password_authentication == -1)
383 options->password_authentication = 1; 395 options->password_authentication = 1;
384 if (options->kbd_interactive_authentication == -1) 396 if (options->kbd_interactive_authentication == -1)
@@ -531,6 +543,7 @@ typedef enum {
531 sHostKeyAlgorithms, 543 sHostKeyAlgorithms,
532 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 544 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
533 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 545 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
546 sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
534 sAcceptEnv, sSetEnv, sPermitTunnel, 547 sAcceptEnv, sSetEnv, sPermitTunnel,
535 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, 548 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
536 sUsePrivilegeSeparation, sAllowAgentForwarding, 549 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -607,12 +620,22 @@ static struct {
607#ifdef GSSAPI 620#ifdef GSSAPI
608 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 621 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
609 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 622 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
623 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
610 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 624 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
625 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
626 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
627 { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
611#else 628#else
612 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 629 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
613 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 630 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
631 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
614 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 632 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
633 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
634 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
635 { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
615#endif 636#endif
637 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
638 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
616 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 639 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
617 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 640 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
618 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 641 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1548,6 +1571,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
1548 intptr = &options->gss_authentication; 1571 intptr = &options->gss_authentication;
1549 goto parse_flag; 1572 goto parse_flag;
1550 1573
1574 case sGssKeyEx:
1575 intptr = &options->gss_keyex;
1576 goto parse_flag;
1577
1551 case sGssCleanupCreds: 1578 case sGssCleanupCreds:
1552 intptr = &options->gss_cleanup_creds; 1579 intptr = &options->gss_cleanup_creds;
1553 goto parse_flag; 1580 goto parse_flag;
@@ -1556,6 +1583,22 @@ process_server_config_line_depth(ServerOptions *options, char *line,
1556 intptr = &options->gss_strict_acceptor; 1583 intptr = &options->gss_strict_acceptor;
1557 goto parse_flag; 1584 goto parse_flag;
1558 1585
1586 case sGssStoreRekey:
1587 intptr = &options->gss_store_rekey;
1588 goto parse_flag;
1589
1590 case sGssKexAlgorithms:
1591 arg = strdelim(&cp);
1592 if (!arg || *arg == '\0')
1593 fatal("%.200s line %d: Missing argument.",
1594 filename, linenum);
1595 if (!kex_gss_names_valid(arg))
1596 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1597 filename, linenum, arg ? arg : "<NONE>");
1598 if (*activep && options->gss_kex_algorithms == NULL)
1599 options->gss_kex_algorithms = xstrdup(arg);
1600 break;
1601
1559 case sPasswordAuthentication: 1602 case sPasswordAuthentication:
1560 intptr = &options->password_authentication; 1603 intptr = &options->password_authentication;
1561 goto parse_flag; 1604 goto parse_flag;
@@ -2777,6 +2820,10 @@ dump_config(ServerOptions *o)
2777#ifdef GSSAPI 2820#ifdef GSSAPI
2778 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2821 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2779 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2822 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2823 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2824 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2825 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2826 dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
2780#endif 2827#endif
2781 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2828 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2782 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2829 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 4202a2d02..3f47ea25e 100644
--- a/servconf.h
+++ b/servconf.h
@@ -132,8 +132,11 @@ typedef struct {
132 int kerberos_get_afs_token; /* If true, try to get AFS token if 132 int kerberos_get_afs_token; /* If true, try to get AFS token if
133 * authenticated with Kerberos. */ 133 * authenticated with Kerberos. */
134 int gss_authentication; /* If true, permit GSSAPI authentication */ 134 int gss_authentication; /* If true, permit GSSAPI authentication */
135 int gss_keyex; /* If true, permit GSSAPI key exchange */
135 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 136 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
136 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 137 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
138 int gss_store_rekey;
139 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
137 int password_authentication; /* If true, permit password 140 int password_authentication; /* If true, permit password
138 * authentication. */ 141 * authentication. */
139 int kbd_interactive_authentication; /* If true, permit */ 142 int kbd_interactive_authentication; /* If true, permit */
diff --git a/session.c b/session.c
index 8c0e54f79..06a33442a 100644
--- a/session.c
+++ b/session.c
@@ -2678,13 +2678,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
2678 2678
2679#ifdef KRB5 2679#ifdef KRB5
2680 if (options.kerberos_ticket_cleanup && 2680 if (options.kerberos_ticket_cleanup &&
2681 authctxt->krb5_ctx) 2681 authctxt->krb5_ctx) {
2682 temporarily_use_uid(authctxt->pw);
2682 krb5_cleanup_proc(authctxt); 2683 krb5_cleanup_proc(authctxt);
2684 restore_uid();
2685 }
2683#endif 2686#endif
2684 2687
2685#ifdef GSSAPI 2688#ifdef GSSAPI
2686 if (options.gss_cleanup_creds) 2689 if (options.gss_cleanup_creds) {
2690 temporarily_use_uid(authctxt->pw);
2687 ssh_gssapi_cleanup_creds(); 2691 ssh_gssapi_cleanup_creds();
2692 restore_uid();
2693 }
2688#endif 2694#endif
2689 2695
2690 /* remove agent socket */ 2696 /* 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 60de6087a..db5c65bc7 100644
--- a/ssh.1
+++ b/ssh.1
@@ -503,7 +503,13 @@ For full details of the options listed below, and their possible values, see
503.It GatewayPorts 503.It GatewayPorts
504.It GlobalKnownHostsFile 504.It GlobalKnownHostsFile
505.It GSSAPIAuthentication 505.It GSSAPIAuthentication
506.It GSSAPIKeyExchange
507.It GSSAPIClientIdentity
506.It GSSAPIDelegateCredentials 508.It GSSAPIDelegateCredentials
509.It GSSAPIKexAlgorithms
510.It GSSAPIRenewalForcesRekey
511.It GSSAPIServerIdentity
512.It GSSAPITrustDns
507.It HashKnownHosts 513.It HashKnownHosts
508.It Host 514.It Host
509.It HostbasedAuthentication 515.It HostbasedAuthentication
@@ -579,6 +585,8 @@ flag),
579(supported message integrity codes), 585(supported message integrity codes),
580.Ar kex 586.Ar kex
581(key exchange algorithms), 587(key exchange algorithms),
588.Ar kex-gss
589(GSSAPI key exchange algorithms),
582.Ar key 590.Ar key
583(key types), 591(key types),
584.Ar key-cert 592.Ar key-cert
diff --git a/ssh.c b/ssh.c
index 15aee569e..110cf9c19 100644
--- a/ssh.c
+++ b/ssh.c
@@ -747,6 +747,8 @@ main(int ac, char **av)
747 else if (strcmp(optarg, "kex") == 0 || 747 else if (strcmp(optarg, "kex") == 0 ||
748 strcasecmp(optarg, "KexAlgorithms") == 0) 748 strcasecmp(optarg, "KexAlgorithms") == 0)
749 cp = kex_alg_list('\n'); 749 cp = kex_alg_list('\n');
750 else if (strcmp(optarg, "kex-gss") == 0)
751 cp = kex_gss_alg_list('\n');
750 else if (strcmp(optarg, "key") == 0) 752 else if (strcmp(optarg, "key") == 0)
751 cp = sshkey_alg_list(0, 0, 0, '\n'); 753 cp = sshkey_alg_list(0, 0, 0, '\n');
752 else if (strcmp(optarg, "key-cert") == 0) 754 else if (strcmp(optarg, "key-cert") == 0)
@@ -772,8 +774,8 @@ main(int ac, char **av)
772 } else if (strcmp(optarg, "help") == 0) { 774 } else if (strcmp(optarg, "help") == 0) {
773 cp = xstrdup( 775 cp = xstrdup(
774 "cipher\ncipher-auth\ncompression\nkex\n" 776 "cipher\ncipher-auth\ncompression\nkex\n"
775 "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" 777 "kex-gss\nkey\nkey-cert\nkey-plain\n"
776 "protocol-version\nsig"); 778 "key-sig\nmac\nprotocol-version\nsig");
777 } 779 }
778 if (cp == NULL) 780 if (cp == NULL)
779 fatal("Unsupported query \"%s\"", optarg); 781 fatal("Unsupported query \"%s\"", optarg);
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 06a32d314..3f4906972 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -766,10 +766,67 @@ The default is
766Specifies whether user authentication based on GSSAPI is allowed. 766Specifies whether user authentication based on GSSAPI is allowed.
767The default is 767The default is
768.Cm no . 768.Cm no .
769.It Cm GSSAPIClientIdentity
770If set, specifies the GSSAPI client identity that ssh should use when
771connecting to the server. The default is unset, which means that the default
772identity will be used.
769.It Cm GSSAPIDelegateCredentials 773.It Cm GSSAPIDelegateCredentials
770Forward (delegate) credentials to the server. 774Forward (delegate) credentials to the server.
771The default is 775The default is
772.Cm no . 776.Cm no .
777.It Cm GSSAPIKeyExchange
778Specifies whether key exchange based on GSSAPI may be used. When using
779GSSAPI key exchange the server need not have a host key.
780The default is
781.Dq no .
782.It Cm GSSAPIRenewalForcesRekey
783If set to
784.Dq yes
785then renewal of the client's GSSAPI credentials will force the rekeying of the
786ssh connection. With a compatible server, this will delegate the renewed
787credentials to a session on the server.
788.Pp
789Checks are made to ensure that credentials are only propagated when the new
790credentials match the old ones on the originating client and where the
791receiving server still has the old set in its cache.
792.Pp
793The default is
794.Dq no .
795.Pp
796For this to work
797.Cm GSSAPIKeyExchange
798needs to be enabled in the server and also used by the client.
799.It Cm GSSAPIServerIdentity
800If set, specifies the GSSAPI server identity that ssh should expect when
801connecting to the server. The default is unset, which means that the
802expected GSSAPI server identity will be determined from the target
803hostname.
804.It Cm GSSAPITrustDns
805Set to
806.Dq yes
807to indicate that the DNS is trusted to securely canonicalize
808the name of the host being connected to. If
809.Dq no ,
810the hostname entered on the
811command line will be passed untouched to the GSSAPI library.
812The default is
813.Dq no .
814.It Cm GSSAPIKexAlgorithms
815The list of key exchange algorithms that are offered for GSSAPI
816key exchange. Possible values are
817.Bd -literal -offset 3n
818gss-gex-sha1-,
819gss-group1-sha1-,
820gss-group14-sha1-,
821gss-group14-sha256-,
822gss-group16-sha512-,
823gss-nistp256-sha256-,
824gss-curve25519-sha256-
825.Ed
826.Pp
827The default is
828.Dq gss-gex-sha1-,gss-group14-sha1- .
829This option only applies to protocol version 2 connections using GSSAPI.
773.It Cm HashKnownHosts 830.It Cm HashKnownHosts
774Indicates that 831Indicates that
775.Xr ssh 1 832.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index af00fb30c..03bc87eb4 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -80,8 +80,6 @@
80#endif 80#endif
81 81
82/* import */ 82/* import */
83extern char *client_version_string;
84extern char *server_version_string;
85extern Options options; 83extern Options options;
86 84
87/* 85/*
@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
163 char *s, *all_key; 161 char *s, *all_key;
164 int r, use_known_hosts_order = 0; 162 int r, use_known_hosts_order = 0;
165 163
164#if defined(GSSAPI) && defined(WITH_OPENSSL)
165 char *orig = NULL, *gss = NULL;
166 char *gss_host = NULL;
167#endif
168
166 xxx_host = host; 169 xxx_host = host;
167 xxx_hostaddr = hostaddr; 170 xxx_hostaddr = hostaddr;
168 171
@@ -206,6 +209,35 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
206 compat_pkalg_proposal(options.hostkeyalgorithms); 209 compat_pkalg_proposal(options.hostkeyalgorithms);
207 } 210 }
208 211
212#if defined(GSSAPI) && defined(WITH_OPENSSL)
213 if (options.gss_keyex) {
214 /* Add the GSSAPI mechanisms currently supported on this
215 * client to the key exchange algorithm proposal */
216 orig = myproposal[PROPOSAL_KEX_ALGS];
217
218 if (options.gss_server_identity)
219 gss_host = xstrdup(options.gss_server_identity);
220 else if (options.gss_trust_dns)
221 gss_host = remote_hostname(ssh);
222 else
223 gss_host = xstrdup(host);
224
225 gss = ssh_gssapi_client_mechanisms(gss_host,
226 options.gss_client_identity, options.gss_kex_algorithms);
227 if (gss) {
228 debug("Offering GSSAPI proposal: %s", gss);
229 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
230 "%s,%s", gss, orig);
231
232 /* If we've got GSSAPI algorithms, then we also support the
233 * 'null' hostkey, as a last resort */
234 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
235 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
236 "%s,null", orig);
237 }
238 }
239#endif
240
209 if (options.rekey_limit || options.rekey_interval) 241 if (options.rekey_limit || options.rekey_interval)
210 ssh_packet_set_rekey_limits(ssh, options.rekey_limit, 242 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
211 options.rekey_interval); 243 options.rekey_interval);
@@ -224,16 +256,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
224# ifdef OPENSSL_HAS_ECC 256# ifdef OPENSSL_HAS_ECC
225 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 257 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
226# endif 258# endif
227#endif 259# ifdef GSSAPI
260 if (options.gss_keyex) {
261 ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
262 ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
263 ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
264 ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
265 ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
266 ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
267 ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
268 }
269# endif
270#endif /* WITH_OPENSSL */
228 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 271 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
229 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; 272 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
230 ssh->kex->verify_host_key=&verify_host_key_callback; 273 ssh->kex->verify_host_key=&verify_host_key_callback;
231 274
275#if defined(GSSAPI) && defined(WITH_OPENSSL)
276 if (options.gss_keyex) {
277 ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
278 ssh->kex->gss_trust_dns = options.gss_trust_dns;
279 ssh->kex->gss_client = options.gss_client_identity;
280 ssh->kex->gss_host = gss_host;
281 }
282#endif
283
232 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); 284 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
233 285
234 /* remove ext-info from the KEX proposals for rekeying */ 286 /* remove ext-info from the KEX proposals for rekeying */
235 myproposal[PROPOSAL_KEX_ALGS] = 287 myproposal[PROPOSAL_KEX_ALGS] =
236 compat_kex_proposal(options.kex_algorithms); 288 compat_kex_proposal(options.kex_algorithms);
289#if defined(GSSAPI) && defined(WITH_OPENSSL)
290 /* repair myproposal after it was crumpled by the */
291 /* ext-info removal above */
292 if (gss) {
293 orig = myproposal[PROPOSAL_KEX_ALGS];
294 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
295 "%s,%s", gss, orig);
296 free(gss);
297 }
298#endif
237 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) 299 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
238 fatal("kex_prop2buf: %s", ssh_err(r)); 300 fatal("kex_prop2buf: %s", ssh_err(r));
239 301
@@ -330,6 +392,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
330static int input_gssapi_token(int type, u_int32_t, struct ssh *); 392static int input_gssapi_token(int type, u_int32_t, struct ssh *);
331static int input_gssapi_error(int, u_int32_t, struct ssh *); 393static int input_gssapi_error(int, u_int32_t, struct ssh *);
332static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 394static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
395static int userauth_gsskeyex(struct ssh *);
333#endif 396#endif
334 397
335void userauth(struct ssh *, char *); 398void userauth(struct ssh *, char *);
@@ -346,6 +409,11 @@ static char *authmethods_get(void);
346 409
347Authmethod authmethods[] = { 410Authmethod authmethods[] = {
348#ifdef GSSAPI 411#ifdef GSSAPI
412 {"gssapi-keyex",
413 userauth_gsskeyex,
414 NULL,
415 &options.gss_keyex,
416 NULL},
349 {"gssapi-with-mic", 417 {"gssapi-with-mic",
350 userauth_gssapi, 418 userauth_gssapi,
351 userauth_gssapi_cleanup, 419 userauth_gssapi_cleanup,
@@ -716,12 +784,25 @@ userauth_gssapi(struct ssh *ssh)
716 OM_uint32 min; 784 OM_uint32 min;
717 int r, ok = 0; 785 int r, ok = 0;
718 gss_OID mech = NULL; 786 gss_OID mech = NULL;
787 char *gss_host;
788
789 if (options.gss_server_identity)
790 gss_host = xstrdup(options.gss_server_identity);
791 else if (options.gss_trust_dns)
792 gss_host = remote_hostname(ssh);
793 else
794 gss_host = xstrdup(authctxt->host);
719 795
720 /* Try one GSSAPI method at a time, rather than sending them all at 796 /* Try one GSSAPI method at a time, rather than sending them all at
721 * once. */ 797 * once. */
722 798
723 if (authctxt->gss_supported_mechs == NULL) 799 if (authctxt->gss_supported_mechs == NULL)
724 gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 800 if (GSS_ERROR(gss_indicate_mechs(&min,
801 &authctxt->gss_supported_mechs))) {
802 authctxt->gss_supported_mechs = NULL;
803 free(gss_host);
804 return 0;
805 }
725 806
726 /* Check to see whether the mechanism is usable before we offer it */ 807 /* Check to see whether the mechanism is usable before we offer it */
727 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 808 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
@@ -730,13 +811,15 @@ userauth_gssapi(struct ssh *ssh)
730 elements[authctxt->mech_tried]; 811 elements[authctxt->mech_tried];
731 /* My DER encoding requires length<128 */ 812 /* My DER encoding requires length<128 */
732 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 813 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
733 mech, authctxt->host)) { 814 mech, gss_host, options.gss_client_identity)) {
734 ok = 1; /* Mechanism works */ 815 ok = 1; /* Mechanism works */
735 } else { 816 } else {
736 authctxt->mech_tried++; 817 authctxt->mech_tried++;
737 } 818 }
738 } 819 }
739 820
821 free(gss_host);
822
740 if (!ok || mech == NULL) 823 if (!ok || mech == NULL)
741 return 0; 824 return 0;
742 825
@@ -976,6 +1059,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
976 free(lang); 1059 free(lang);
977 return r; 1060 return r;
978} 1061}
1062
1063int
1064userauth_gsskeyex(struct ssh *ssh)
1065{
1066 struct sshbuf *b = NULL;
1067 Authctxt *authctxt = ssh->authctxt;
1068 gss_buffer_desc gssbuf;
1069 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1070 OM_uint32 ms;
1071 int r;
1072
1073 static int attempt = 0;
1074 if (attempt++ >= 1)
1075 return (0);
1076
1077 if (gss_kex_context == NULL) {
1078 debug("No valid Key exchange context");
1079 return (0);
1080 }
1081
1082 if ((b = sshbuf_new()) == NULL)
1083 fatal("%s: sshbuf_new failed", __func__);
1084
1085 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1086 "gssapi-keyex");
1087
1088 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1089 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1090 gssbuf.length = sshbuf_len(b);
1091
1092 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1093 sshbuf_free(b);
1094 return (0);
1095 }
1096
1097 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1098 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1099 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1100 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1101 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1102 (r = sshpkt_send(ssh)) != 0)
1103 fatal("%s: %s", __func__, ssh_err(r));
1104
1105 sshbuf_free(b);
1106 gss_release_buffer(&ms, &mic);
1107
1108 return (1);
1109}
1110
979#endif /* GSSAPI */ 1111#endif /* GSSAPI */
980 1112
981static int 1113static int
diff --git a/sshd.c b/sshd.c
index 60b2aaf73..d92f03aaf 100644
--- a/sshd.c
+++ b/sshd.c
@@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh)
817 } 817 }
818 debug3("%s: sent %u hostkeys", __func__, nkeys); 818 debug3("%s: sent %u hostkeys", __func__, nkeys);
819 if (nkeys == 0) 819 if (nkeys == 0)
820 fatal("%s: no hostkeys", __func__); 820 debug3("%s: no hostkeys", __func__);
821 if ((r = sshpkt_send(ssh)) != 0) 821 else if ((r = sshpkt_send(ssh)) != 0)
822 sshpkt_fatal(ssh, r, "%s: send", __func__); 822 sshpkt_fatal(ssh, r, "%s: send", __func__);
823 sshbuf_free(buf); 823 sshbuf_free(buf);
824} 824}
@@ -1852,7 +1852,8 @@ main(int ac, char **av)
1852 free(fp); 1852 free(fp);
1853 } 1853 }
1854 accumulate_host_timing_secret(cfg, NULL); 1854 accumulate_host_timing_secret(cfg, NULL);
1855 if (!sensitive_data.have_ssh2_key) { 1855 /* The GSSAPI key exchange can run without a host key */
1856 if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
1856 logit("sshd: no hostkeys available -- exiting."); 1857 logit("sshd: no hostkeys available -- exiting.");
1857 exit(1); 1858 exit(1);
1858 } 1859 }
@@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh)
2347 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2348 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2348 list_hostkey_types()); 2349 list_hostkey_types());
2349 2350
2351#if defined(GSSAPI) && defined(WITH_OPENSSL)
2352 {
2353 char *orig;
2354 char *gss = NULL;
2355 char *newstr = NULL;
2356 orig = myproposal[PROPOSAL_KEX_ALGS];
2357
2358 /*
2359 * If we don't have a host key, then there's no point advertising
2360 * the other key exchange algorithms
2361 */
2362
2363 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2364 orig = NULL;
2365
2366 if (options.gss_keyex)
2367 gss = ssh_gssapi_server_mechanisms();
2368 else
2369 gss = NULL;
2370
2371 if (gss && orig)
2372 xasprintf(&newstr, "%s,%s", gss, orig);
2373 else if (gss)
2374 newstr = gss;
2375 else if (orig)
2376 newstr = orig;
2377
2378 /*
2379 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2380 * key alg, but we can't tell people about it unless its the only
2381 * host key algorithm we support
2382 */
2383 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2384 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2385
2386 if (newstr)
2387 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2388 else
2389 fatal("No supported key exchange algorithms");
2390 }
2391#endif
2392
2350 /* start key exchange */ 2393 /* start key exchange */
2351 if ((r = kex_setup(ssh, myproposal)) != 0) 2394 if ((r = kex_setup(ssh, myproposal)) != 0)
2352 fatal("kex_setup: %s", ssh_err(r)); 2395 fatal("kex_setup: %s", ssh_err(r));
@@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh)
2362# ifdef OPENSSL_HAS_ECC 2405# ifdef OPENSSL_HAS_ECC
2363 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 2406 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2364# endif 2407# endif
2365#endif 2408# ifdef GSSAPI
2409 if (options.gss_keyex) {
2410 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2411 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2412 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2413 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2414 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2415 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2416 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2417 }
2418# endif
2419#endif /* WITH_OPENSSL */
2366 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 2420 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2367 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 2421 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2368 kex->load_host_public_key=&get_hostkey_public_by_type; 2422 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 70ccea449..f6b41a2f8 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -646,6 +646,11 @@ Specifies whether to automatically destroy the user's credentials cache
646on logout. 646on logout.
647The default is 647The default is
648.Cm yes . 648.Cm yes .
649.It Cm GSSAPIKeyExchange
650Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
651doesn't rely on ssh keys to verify host identity.
652The default is
653.Cm no .
649.It Cm GSSAPIStrictAcceptorCheck 654.It Cm GSSAPIStrictAcceptorCheck
650Determines whether to be strict about the identity of the GSSAPI acceptor 655Determines whether to be strict about the identity of the GSSAPI acceptor
651a client authenticates against. 656a client authenticates against.
@@ -660,6 +665,31 @@ machine's default store.
660This facility is provided to assist with operation on multi homed machines. 665This facility is provided to assist with operation on multi homed machines.
661The default is 666The default is
662.Cm yes . 667.Cm yes .
668.It Cm GSSAPIStoreCredentialsOnRekey
669Controls whether the user's GSSAPI credentials should be updated following a
670successful connection rekeying. This option can be used to accepted renewed
671or updated credentials from a compatible client. The default is
672.Dq no .
673.Pp
674For this to work
675.Cm GSSAPIKeyExchange
676needs to be enabled in the server and also used by the client.
677.It Cm GSSAPIKexAlgorithms
678The list of key exchange algorithms that are accepted by GSSAPI
679key exchange. Possible values are
680.Bd -literal -offset 3n
681gss-gex-sha1-,
682gss-group1-sha1-,
683gss-group14-sha1-,
684gss-group14-sha256-,
685gss-group16-sha512-,
686gss-nistp256-sha256-,
687gss-curve25519-sha256-
688.Ed
689.Pp
690The default is
691.Dq gss-gex-sha1-,gss-group14-sha1- .
692This option only applies to protocol version 2 connections using GSSAPI.
663.It Cm HostbasedAcceptedKeyTypes 693.It Cm HostbasedAcceptedKeyTypes
664Specifies the key types that will be accepted for hostbased authentication 694Specifies the key types that will be accepted for hostbased authentication
665as a list of comma-separated patterns. 695as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index 57995ee68..fd5b77246 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -154,6 +154,7 @@ static const struct keytype keytypes[] = {
154 KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 }, 154 KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 },
155# endif /* OPENSSL_HAS_ECC */ 155# endif /* OPENSSL_HAS_ECC */
156#endif /* WITH_OPENSSL */ 156#endif /* WITH_OPENSSL */
157 { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
157 { NULL, NULL, NULL, -1, -1, 0, 0 } 158 { NULL, NULL, NULL, -1, -1, 0, 0 }
158}; 159};
159 160
@@ -255,7 +256,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
255 const struct keytype *kt; 256 const struct keytype *kt;
256 257
257 for (kt = keytypes; kt->type != -1; kt++) { 258 for (kt = keytypes; kt->type != -1; kt++) {
258 if (kt->name == NULL) 259 if (kt->name == NULL || kt->type == KEY_NULL)
259 continue; 260 continue;
260 if (!include_sigonly && kt->sigonly) 261 if (!include_sigonly && kt->sigonly)
261 continue; 262 continue;
diff --git a/sshkey.h b/sshkey.h
index 71a3fddcb..37a43a67a 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -69,6 +69,7 @@ enum sshkey_types {
69 KEY_ECDSA_SK_CERT, 69 KEY_ECDSA_SK_CERT,
70 KEY_ED25519_SK, 70 KEY_ED25519_SK,
71 KEY_ED25519_SK_CERT, 71 KEY_ED25519_SK_CERT,
72 KEY_NULL,
72 KEY_UNSPEC 73 KEY_UNSPEC
73}; 74};
74 75