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-06-07 10:24:45 +0100
commit79f9d21b406c172878896ef41cdc2502fc2f84a7 (patch)
tree71507aaefd925223b1543b10f4342f2df9ea0ee3
parent202f5a676221c244cd450086c334c2b59f339e86 (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. Author: Simon Wilkinson <simon@sxw.org.uk> Author: Colin Watson <cjwatson@debian.org> Author: Jakub Jelen <jjelen@redhat.com> 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-06-07 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.h54
-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.c154
-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, 2640 insertions, 160 deletions
diff --git a/Makefile.in b/Makefile.in
index c9e4294d3..bf1e1de47 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 91aaf34a6..a4a5e0069 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 da396c72a..42ace7789 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
@@ -1361,9 +1365,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1361 break; 1365 break;
1362 1366
1363 /* Do channel operations unless rekeying in progress. */ 1367 /* Do channel operations unless rekeying in progress. */
1364 if (!ssh_packet_is_rekeying(ssh)) 1368 if (!ssh_packet_is_rekeying(ssh)) {
1365 channel_after_select(ssh, readset, writeset); 1369 channel_after_select(ssh, readset, writeset);
1366 1370
1371#ifdef GSSAPI
1372 if (options.gss_renewal_rekey &&
1373 ssh_gssapi_credentials_updated(NULL)) {
1374 debug("credentials updated - forcing rekey");
1375 need_rekeying = 1;
1376 }
1377#endif
1378 }
1379
1367 /* Buffer input from the connection. */ 1380 /* Buffer input from the connection. */
1368 client_process_net_input(ssh, readset); 1381 client_process_net_input(ssh, readset);
1369 1382
diff --git a/configure.ac b/configure.ac
index 460383757..d98e6f74a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -676,6 +676,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
676 [Use tunnel device compatibility to OpenBSD]) 676 [Use tunnel device compatibility to OpenBSD])
677 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 677 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
678 [Prepend the address family to IP tunnel traffic]) 678 [Prepend the address family to IP tunnel traffic])
679 AC_MSG_CHECKING([if we have the Security Authorization Session API])
680 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
681 [SessionCreate(0, 0);],
682 [ac_cv_use_security_session_api="yes"
683 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
684 [platform has the Security Authorization Session API])
685 LIBS="$LIBS -framework Security"
686 AC_MSG_RESULT([yes])],
687 [ac_cv_use_security_session_api="no"
688 AC_MSG_RESULT([no])])
689 AC_MSG_CHECKING([if we have an in-memory credentials cache])
690 AC_TRY_COMPILE(
691 [#include <Kerberos/Kerberos.h>],
692 [cc_context_t c;
693 (void) cc_initialize (&c, 0, NULL, NULL);],
694 [AC_DEFINE([USE_CCAPI], [1],
695 [platform uses an in-memory credentials cache])
696 LIBS="$LIBS -framework Security"
697 AC_MSG_RESULT([yes])
698 if test "x$ac_cv_use_security_session_api" = "xno"; then
699 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
700 fi],
701 [AC_MSG_RESULT([no])]
702 )
679 m4_pattern_allow([AU_IPv]) 703 m4_pattern_allow([AU_IPv])
680 AC_CHECK_DECL([AU_IPv4], [], 704 AC_CHECK_DECL([AU_IPv4], [],
681 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 705 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 b5d4bb2d1..55f4d4bda 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm 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 09c7258e0..144dee512 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])
@@ -697,6 +754,9 @@ kex_free(struct kex *kex)
697 sshbuf_free(kex->server_version); 754 sshbuf_free(kex->server_version);
698 sshbuf_free(kex->client_pub); 755 sshbuf_free(kex->client_pub);
699 free(kex->session_id); 756 free(kex->session_id);
757#ifdef GSSAPI
758 free(kex->gss_host);
759#endif /* GSSAPI */
700 free(kex->failed_choice); 760 free(kex->failed_choice);
701 free(kex->hostkey_alg); 761 free(kex->hostkey_alg);
702 free(kex->name); 762 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 b6e855d5d..5347e900d 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);
@@ -1712,6 +1729,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
1712# ifdef OPENSSL_HAS_ECC 1729# ifdef OPENSSL_HAS_ECC
1713 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 1730 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
1714# endif 1731# endif
1732# ifdef GSSAPI
1733 if (options.gss_keyex) {
1734 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1735 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1736 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
1737 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
1738 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
1739 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
1740 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
1741 }
1742# endif
1715#endif /* WITH_OPENSSL */ 1743#endif /* WITH_OPENSSL */
1716 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 1744 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
1717 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 1745 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
@@ -1805,8 +1833,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1805 u_char *p; 1833 u_char *p;
1806 int r; 1834 int r;
1807 1835
1808 if (!options.gss_authentication) 1836 if (!options.gss_authentication && !options.gss_keyex)
1809 fatal("%s: GSSAPI authentication not enabled", __func__); 1837 fatal("%s: GSSAPI not enabled", __func__);
1810 1838
1811 if ((r = sshbuf_get_string(m, &p, &len)) != 0) 1839 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1812 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1840 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1838,8 +1866,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1838 OM_uint32 flags = 0; /* GSI needs this */ 1866 OM_uint32 flags = 0; /* GSI needs this */
1839 int r; 1867 int r;
1840 1868
1841 if (!options.gss_authentication) 1869 if (!options.gss_authentication && !options.gss_keyex)
1842 fatal("%s: GSSAPI authentication not enabled", __func__); 1870 fatal("%s: GSSAPI not enabled", __func__);
1843 1871
1844 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) 1872 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
1845 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1873 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1859,6 +1887,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1859 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1887 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1860 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1888 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1861 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1889 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1890 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1862 } 1891 }
1863 return (0); 1892 return (0);
1864} 1893}
@@ -1870,8 +1899,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1870 OM_uint32 ret; 1899 OM_uint32 ret;
1871 int r; 1900 int r;
1872 1901
1873 if (!options.gss_authentication) 1902 if (!options.gss_authentication && !options.gss_keyex)
1874 fatal("%s: GSSAPI authentication not enabled", __func__); 1903 fatal("%s: GSSAPI not enabled", __func__);
1875 1904
1876 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || 1905 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
1877 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) 1906 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
@@ -1897,13 +1926,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1897int 1926int
1898mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) 1927mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1899{ 1928{
1900 int r, authenticated; 1929 int r, authenticated, kex;
1901 const char *displayname; 1930 const char *displayname;
1902 1931
1903 if (!options.gss_authentication) 1932 if (!options.gss_authentication && !options.gss_keyex)
1904 fatal("%s: GSSAPI authentication not enabled", __func__); 1933 fatal("%s: GSSAPI not enabled", __func__);
1905 1934
1906 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1935 if ((r = sshbuf_get_u32(m, &kex)) != 0)
1936 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1937
1938 authenticated = authctxt->valid &&
1939 ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
1907 1940
1908 sshbuf_reset(m); 1941 sshbuf_reset(m);
1909 if ((r = sshbuf_put_u32(m, authenticated)) != 0) 1942 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
@@ -1912,7 +1945,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1912 debug3("%s: sending result %d", __func__, authenticated); 1945 debug3("%s: sending result %d", __func__, authenticated);
1913 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); 1946 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
1914 1947
1915 auth_method = "gssapi-with-mic"; 1948 if (kex) {
1949 auth_method = "gssapi-keyex";
1950 } else {
1951 auth_method = "gssapi-with-mic";
1952 }
1916 1953
1917 if ((displayname = ssh_gssapi_displayname()) != NULL) 1954 if ((displayname = ssh_gssapi_displayname()) != NULL)
1918 auth2_record_info(authctxt, "%s", displayname); 1955 auth2_record_info(authctxt, "%s", displayname);
@@ -1920,5 +1957,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1920 /* Monitor loop will terminate if authenticated */ 1957 /* Monitor loop will terminate if authenticated */
1921 return (authenticated); 1958 return (authenticated);
1922} 1959}
1960
1961int
1962mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
1963{
1964 gss_buffer_desc data;
1965 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1966 OM_uint32 major, minor;
1967 size_t len;
1968 u_char *p = NULL;
1969 int r;
1970
1971 if (!options.gss_authentication && !options.gss_keyex)
1972 fatal("%s: GSSAPI not enabled", __func__);
1973
1974 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1975 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1976 data.value = p;
1977 data.length = len;
1978 /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
1979 if (data.length != 20 && data.length != 32 && data.length != 64)
1980 fatal("%s: data length incorrect: %d", __func__,
1981 (int) data.length);
1982
1983 /* Save the session ID on the first time around */
1984 if (session_id2_len == 0) {
1985 session_id2_len = data.length;
1986 session_id2 = xmalloc(session_id2_len);
1987 memcpy(session_id2, data.value, session_id2_len);
1988 }
1989 major = ssh_gssapi_sign(gsscontext, &data, &hash);
1990
1991 free(data.value);
1992
1993 sshbuf_reset(m);
1994
1995 if ((r = sshbuf_put_u32(m, major)) != 0 ||
1996 (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
1997 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1998
1999 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2000
2001 gss_release_buffer(&minor, &hash);
2002
2003 /* Turn on getpwnam permissions */
2004 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2005
2006 /* And credential updating, for when rekeying */
2007 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2008
2009 return (0);
2010}
2011
2012int
2013mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
2014 ssh_gssapi_ccache store;
2015 int r, ok;
2016
2017 if (!options.gss_authentication && !options.gss_keyex)
2018 fatal("%s: GSSAPI not enabled", __func__);
2019
2020 if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
2021 (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
2022 (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
2023 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2024
2025 ok = ssh_gssapi_update_creds(&store);
2026
2027 free(store.filename);
2028 free(store.envvar);
2029 free(store.envval);
2030
2031 sshbuf_reset(m);
2032 if ((r = sshbuf_put_u32(m, ok)) != 0)
2033 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2034
2035 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2036
2037 return(0);
2038}
2039
1923#endif /* GSSAPI */ 2040#endif /* GSSAPI */
1924 2041
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 2afcbaeca..fb585e248 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 },
@@ -1053,10 +1068,42 @@ parse_time:
1053 intptr = &options->gss_authentication; 1068 intptr = &options->gss_authentication;
1054 goto parse_flag; 1069 goto parse_flag;
1055 1070
1071 case oGssKeyEx:
1072 intptr = &options->gss_keyex;
1073 goto parse_flag;
1074
1056 case oGssDelegateCreds: 1075 case oGssDelegateCreds:
1057 intptr = &options->gss_deleg_creds; 1076 intptr = &options->gss_deleg_creds;
1058 goto parse_flag; 1077 goto parse_flag;
1059 1078
1079 case oGssTrustDns:
1080 intptr = &options->gss_trust_dns;
1081 goto parse_flag;
1082
1083 case oGssClientIdentity:
1084 charptr = &options->gss_client_identity;
1085 goto parse_string;
1086
1087 case oGssServerIdentity:
1088 charptr = &options->gss_server_identity;
1089 goto parse_string;
1090
1091 case oGssRenewalRekey:
1092 intptr = &options->gss_renewal_rekey;
1093 goto parse_flag;
1094
1095 case oGssKexAlgorithms:
1096 arg = strdelim(&s);
1097 if (!arg || *arg == '\0')
1098 fatal("%.200s line %d: Missing argument.",
1099 filename, linenum);
1100 if (!kex_gss_names_valid(arg))
1101 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1102 filename, linenum, arg ? arg : "<NONE>");
1103 if (*activep && options->gss_kex_algorithms == NULL)
1104 options->gss_kex_algorithms = xstrdup(arg);
1105 break;
1106
1060 case oBatchMode: 1107 case oBatchMode:
1061 intptr = &options->batch_mode; 1108 intptr = &options->batch_mode;
1062 goto parse_flag; 1109 goto parse_flag;
@@ -1935,7 +1982,13 @@ initialize_options(Options * options)
1935 options->pubkey_authentication = -1; 1982 options->pubkey_authentication = -1;
1936 options->challenge_response_authentication = -1; 1983 options->challenge_response_authentication = -1;
1937 options->gss_authentication = -1; 1984 options->gss_authentication = -1;
1985 options->gss_keyex = -1;
1938 options->gss_deleg_creds = -1; 1986 options->gss_deleg_creds = -1;
1987 options->gss_trust_dns = -1;
1988 options->gss_renewal_rekey = -1;
1989 options->gss_client_identity = NULL;
1990 options->gss_server_identity = NULL;
1991 options->gss_kex_algorithms = NULL;
1939 options->password_authentication = -1; 1992 options->password_authentication = -1;
1940 options->kbd_interactive_authentication = -1; 1993 options->kbd_interactive_authentication = -1;
1941 options->kbd_interactive_devices = NULL; 1994 options->kbd_interactive_devices = NULL;
@@ -2083,8 +2136,18 @@ fill_default_options(Options * options)
2083 options->challenge_response_authentication = 1; 2136 options->challenge_response_authentication = 1;
2084 if (options->gss_authentication == -1) 2137 if (options->gss_authentication == -1)
2085 options->gss_authentication = 0; 2138 options->gss_authentication = 0;
2139 if (options->gss_keyex == -1)
2140 options->gss_keyex = 0;
2086 if (options->gss_deleg_creds == -1) 2141 if (options->gss_deleg_creds == -1)
2087 options->gss_deleg_creds = 0; 2142 options->gss_deleg_creds = 0;
2143 if (options->gss_trust_dns == -1)
2144 options->gss_trust_dns = 0;
2145 if (options->gss_renewal_rekey == -1)
2146 options->gss_renewal_rekey = 0;
2147#ifdef GSSAPI
2148 if (options->gss_kex_algorithms == NULL)
2149 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
2150#endif
2088 if (options->password_authentication == -1) 2151 if (options->password_authentication == -1)
2089 options->password_authentication = 1; 2152 options->password_authentication = 1;
2090 if (options->kbd_interactive_authentication == -1) 2153 if (options->kbd_interactive_authentication == -1)
@@ -2726,7 +2789,14 @@ dump_client_config(Options *o, const char *host)
2726 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); 2789 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
2727#ifdef GSSAPI 2790#ifdef GSSAPI
2728 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); 2791 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
2792 dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
2729 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); 2793 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
2794 dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
2795 dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
2796 dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
2797 dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
2798 dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
2799 o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
2730#endif /* GSSAPI */ 2800#endif /* GSSAPI */
2731 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); 2801 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
2732 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); 2802 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
diff --git a/readconf.h b/readconf.h
index e143a1082..c405b837f 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 ba0a92c7b..f38ba9e44 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 },
@@ -1555,6 +1578,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
1555 intptr = &options->gss_authentication; 1578 intptr = &options->gss_authentication;
1556 goto parse_flag; 1579 goto parse_flag;
1557 1580
1581 case sGssKeyEx:
1582 intptr = &options->gss_keyex;
1583 goto parse_flag;
1584
1558 case sGssCleanupCreds: 1585 case sGssCleanupCreds:
1559 intptr = &options->gss_cleanup_creds; 1586 intptr = &options->gss_cleanup_creds;
1560 goto parse_flag; 1587 goto parse_flag;
@@ -1563,6 +1590,22 @@ process_server_config_line_depth(ServerOptions *options, char *line,
1563 intptr = &options->gss_strict_acceptor; 1590 intptr = &options->gss_strict_acceptor;
1564 goto parse_flag; 1591 goto parse_flag;
1565 1592
1593 case sGssStoreRekey:
1594 intptr = &options->gss_store_rekey;
1595 goto parse_flag;
1596
1597 case sGssKexAlgorithms:
1598 arg = strdelim(&cp);
1599 if (!arg || *arg == '\0')
1600 fatal("%.200s line %d: Missing argument.",
1601 filename, linenum);
1602 if (!kex_gss_names_valid(arg))
1603 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1604 filename, linenum, arg ? arg : "<NONE>");
1605 if (*activep && options->gss_kex_algorithms == NULL)
1606 options->gss_kex_algorithms = xstrdup(arg);
1607 break;
1608
1566 case sPasswordAuthentication: 1609 case sPasswordAuthentication:
1567 intptr = &options->password_authentication; 1610 intptr = &options->password_authentication;
1568 goto parse_flag; 1611 goto parse_flag;
@@ -2791,6 +2834,10 @@ dump_config(ServerOptions *o)
2791#ifdef GSSAPI 2834#ifdef GSSAPI
2792 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2835 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2793 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2836 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2837 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2838 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2839 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2840 dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
2794#endif 2841#endif
2795 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2842 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2796 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2843 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index a420f398d..253cad97e 100644
--- a/servconf.h
+++ b/servconf.h
@@ -137,8 +137,11 @@ typedef struct {
137 int kerberos_get_afs_token; /* If true, try to get AFS token if 137 int kerberos_get_afs_token; /* If true, try to get AFS token if
138 * authenticated with Kerberos. */ 138 * authenticated with Kerberos. */
139 int gss_authentication; /* If true, permit GSSAPI authentication */ 139 int gss_authentication; /* If true, permit GSSAPI authentication */
140 int gss_keyex; /* If true, permit GSSAPI key exchange */
140 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 141 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
141 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 142 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
143 int gss_store_rekey;
144 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
142 int password_authentication; /* If true, permit password 145 int password_authentication; /* If true, permit password
143 * authentication. */ 146 * authentication. */
144 int kbd_interactive_authentication; /* If true, permit */ 147 int kbd_interactive_authentication; /* If true, permit */
diff --git a/session.c b/session.c
index 18cdfa8cf..f9c2c866e 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..50d80bbca 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,34 @@
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_GRP14_SHA256_ID "," \
81 KEX_GSS_GRP16_SHA512_ID "," \
82 KEX_GSS_NISTP256_SHA256_ID "," \
83 KEX_GSS_C25519_SHA256_ID "," \
84 KEX_GSS_GRP14_SHA1_ID "," \
85 KEX_GSS_GEX_SHA1_ID
86
64typedef struct { 87typedef struct {
65 char *filename; 88 char *filename;
66 char *envvar; 89 char *envvar;
67 char *envval; 90 char *envval;
91 struct passwd *owner;
68 void *data; 92 void *data;
69} ssh_gssapi_ccache; 93} ssh_gssapi_ccache;
70 94
@@ -72,8 +96,11 @@ typedef struct {
72 gss_buffer_desc displayname; 96 gss_buffer_desc displayname;
73 gss_buffer_desc exportedname; 97 gss_buffer_desc exportedname;
74 gss_cred_id_t creds; 98 gss_cred_id_t creds;
99 gss_name_t name;
75 struct ssh_gssapi_mech_struct *mech; 100 struct ssh_gssapi_mech_struct *mech;
76 ssh_gssapi_ccache store; 101 ssh_gssapi_ccache store;
102 int used;
103 int updated;
77} ssh_gssapi_client; 104} ssh_gssapi_client;
78 105
79typedef struct ssh_gssapi_mech_struct { 106typedef struct ssh_gssapi_mech_struct {
@@ -84,6 +111,7 @@ typedef struct ssh_gssapi_mech_struct {
84 int (*userok) (ssh_gssapi_client *, char *); 111 int (*userok) (ssh_gssapi_client *, char *);
85 int (*localname) (ssh_gssapi_client *, char **); 112 int (*localname) (ssh_gssapi_client *, char **);
86 void (*storecreds) (ssh_gssapi_client *); 113 void (*storecreds) (ssh_gssapi_client *);
114 int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
87} ssh_gssapi_mech; 115} ssh_gssapi_mech;
88 116
89typedef struct { 117typedef struct {
@@ -94,10 +122,11 @@ typedef struct {
94 gss_OID oid; /* client */ 122 gss_OID oid; /* client */
95 gss_cred_id_t creds; /* server */ 123 gss_cred_id_t creds; /* server */
96 gss_name_t client; /* server */ 124 gss_name_t client; /* server */
97 gss_cred_id_t client_creds; /* server */ 125 gss_cred_id_t client_creds; /* both */
98} Gssctxt; 126} Gssctxt;
99 127
100extern ssh_gssapi_mech *supported_mechs[]; 128extern ssh_gssapi_mech *supported_mechs[];
129extern Gssctxt *gss_kex_context;
101 130
102int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); 131int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
103void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); 132void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -109,6 +138,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
109 138
110struct sshbuf; 139struct sshbuf;
111int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *); 140int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
141int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
112 142
113OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *); 143OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
114OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int, 144OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
@@ -123,17 +153,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
123OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 153OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
124void ssh_gssapi_buildmic(struct sshbuf *, const char *, 154void ssh_gssapi_buildmic(struct sshbuf *, const char *,
125 const char *, const char *); 155 const char *, const char *);
126int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 156int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
157OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
158int ssh_gssapi_credentials_updated(Gssctxt *);
127 159
128/* In the server */ 160/* In the server */
161typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
162 const char *);
163char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
164char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
165 const char *, const char *);
166gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
167int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
168 const char *);
129OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 169OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
130int ssh_gssapi_userok(char *name); 170int ssh_gssapi_userok(char *name, struct passwd *, int kex);
131OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 171OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
132void ssh_gssapi_do_child(char ***, u_int *); 172void ssh_gssapi_do_child(char ***, u_int *);
133void ssh_gssapi_cleanup_creds(void); 173void ssh_gssapi_cleanup_creds(void);
134void ssh_gssapi_storecreds(void); 174void ssh_gssapi_storecreds(void);
135const char *ssh_gssapi_displayname(void); 175const char *ssh_gssapi_displayname(void);
136 176
177char *ssh_gssapi_server_mechanisms(void);
178int ssh_gssapi_oid_table_ok(void);
179
180int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
181void ssh_gssapi_rekey_creds(void);
182
137#endif /* GSSAPI */ 183#endif /* GSSAPI */
138 184
139#endif /* _SSH_GSS_H */ 185#endif /* _SSH_GSS_H */
diff --git a/ssh.1 b/ssh.1
index dce5f404b..7a3ba31ab 100644
--- a/ssh.1
+++ b/ssh.1
@@ -506,7 +506,13 @@ For full details of the options listed below, and their possible values, see
506.It GatewayPorts 506.It GatewayPorts
507.It GlobalKnownHostsFile 507.It GlobalKnownHostsFile
508.It GSSAPIAuthentication 508.It GSSAPIAuthentication
509.It GSSAPIKeyExchange
510.It GSSAPIClientIdentity
509.It GSSAPIDelegateCredentials 511.It GSSAPIDelegateCredentials
512.It GSSAPIKexAlgorithms
513.It GSSAPIRenewalForcesRekey
514.It GSSAPIServerIdentity
515.It GSSAPITrustDns
510.It HashKnownHosts 516.It HashKnownHosts
511.It Host 517.It Host
512.It HostbasedAuthentication 518.It HostbasedAuthentication
@@ -582,6 +588,8 @@ flag),
582(supported message integrity codes), 588(supported message integrity codes),
583.Ar kex 589.Ar kex
584(key exchange algorithms), 590(key exchange algorithms),
591.Ar kex-gss
592(GSSAPI key exchange algorithms),
585.Ar key 593.Ar key
586(key types), 594(key types),
587.Ar key-cert 595.Ar key-cert
diff --git a/ssh.c b/ssh.c
index 98b6ce788..4a81ef810 100644
--- a/ssh.c
+++ b/ssh.c
@@ -773,6 +773,8 @@ main(int ac, char **av)
773 else if (strcmp(optarg, "kex") == 0 || 773 else if (strcmp(optarg, "kex") == 0 ||
774 strcasecmp(optarg, "KexAlgorithms") == 0) 774 strcasecmp(optarg, "KexAlgorithms") == 0)
775 cp = kex_alg_list('\n'); 775 cp = kex_alg_list('\n');
776 else if (strcmp(optarg, "kex-gss") == 0)
777 cp = kex_gss_alg_list('\n');
776 else if (strcmp(optarg, "key") == 0) 778 else if (strcmp(optarg, "key") == 0)
777 cp = sshkey_alg_list(0, 0, 0, '\n'); 779 cp = sshkey_alg_list(0, 0, 0, '\n');
778 else if (strcmp(optarg, "key-cert") == 0) 780 else if (strcmp(optarg, "key-cert") == 0)
@@ -798,8 +800,8 @@ main(int ac, char **av)
798 } else if (strcmp(optarg, "help") == 0) { 800 } else if (strcmp(optarg, "help") == 0) {
799 cp = xstrdup( 801 cp = xstrdup(
800 "cipher\ncipher-auth\ncompression\nkex\n" 802 "cipher\ncipher-auth\ncompression\nkex\n"
801 "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" 803 "kex-gss\nkey\nkey-cert\nkey-plain\n"
802 "protocol-version\nsig"); 804 "key-sig\nmac\nprotocol-version\nsig");
803 } 805 }
804 if (cp == NULL) 806 if (cp == NULL)
805 fatal("Unsupported query \"%s\"", optarg); 807 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 dc010ccbd..e2a2359f9 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-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-gex-sha1-,gss-group14-sha1- .
829This option only applies to 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 1a6545edf..79a22e600 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,41 @@ 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 /* Fall back to specified host if we are using proxy command
223 * and can not use DNS on that socket */
224 if (strcmp(gss_host, "UNKNOWN") == 0) {
225 gss_host = xstrdup(host);
226 }
227 } else {
228 gss_host = xstrdup(host);
229 }
230
231 gss = ssh_gssapi_client_mechanisms(gss_host,
232 options.gss_client_identity, options.gss_kex_algorithms);
233 if (gss) {
234 debug("Offering GSSAPI proposal: %s", gss);
235 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
236 "%s,%s", gss, orig);
237
238 /* If we've got GSSAPI algorithms, then we also support the
239 * 'null' hostkey, as a last resort */
240 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
241 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
242 "%s,null", orig);
243 }
244 }
245#endif
246
209 if (options.rekey_limit || options.rekey_interval) 247 if (options.rekey_limit || options.rekey_interval)
210 ssh_packet_set_rekey_limits(ssh, options.rekey_limit, 248 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
211 options.rekey_interval); 249 options.rekey_interval);
@@ -224,16 +262,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
224# ifdef OPENSSL_HAS_ECC 262# ifdef OPENSSL_HAS_ECC
225 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 263 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
226# endif 264# endif
227#endif 265# ifdef GSSAPI
266 if (options.gss_keyex) {
267 ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
268 ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
269 ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
270 ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
271 ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
272 ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
273 ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
274 }
275# endif
276#endif /* WITH_OPENSSL */
228 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 277 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
229 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; 278 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
230 ssh->kex->verify_host_key=&verify_host_key_callback; 279 ssh->kex->verify_host_key=&verify_host_key_callback;
231 280
281#if defined(GSSAPI) && defined(WITH_OPENSSL)
282 if (options.gss_keyex) {
283 ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
284 ssh->kex->gss_trust_dns = options.gss_trust_dns;
285 ssh->kex->gss_client = options.gss_client_identity;
286 ssh->kex->gss_host = gss_host;
287 }
288#endif
289
232 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); 290 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
233 291
234 /* remove ext-info from the KEX proposals for rekeying */ 292 /* remove ext-info from the KEX proposals for rekeying */
235 myproposal[PROPOSAL_KEX_ALGS] = 293 myproposal[PROPOSAL_KEX_ALGS] =
236 compat_kex_proposal(options.kex_algorithms); 294 compat_kex_proposal(options.kex_algorithms);
295#if defined(GSSAPI) && defined(WITH_OPENSSL)
296 /* repair myproposal after it was crumpled by the */
297 /* ext-info removal above */
298 if (gss) {
299 orig = myproposal[PROPOSAL_KEX_ALGS];
300 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
301 "%s,%s", gss, orig);
302 free(gss);
303 }
304#endif
237 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) 305 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
238 fatal("kex_prop2buf: %s", ssh_err(r)); 306 fatal("kex_prop2buf: %s", ssh_err(r));
239 307
@@ -330,6 +398,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 *); 398static int input_gssapi_token(int type, u_int32_t, struct ssh *);
331static int input_gssapi_error(int, u_int32_t, struct ssh *); 399static int input_gssapi_error(int, u_int32_t, struct ssh *);
332static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 400static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
401static int userauth_gsskeyex(struct ssh *);
333#endif 402#endif
334 403
335void userauth(struct ssh *, char *); 404void userauth(struct ssh *, char *);
@@ -346,6 +415,11 @@ static char *authmethods_get(void);
346 415
347Authmethod authmethods[] = { 416Authmethod authmethods[] = {
348#ifdef GSSAPI 417#ifdef GSSAPI
418 {"gssapi-keyex",
419 userauth_gsskeyex,
420 NULL,
421 &options.gss_keyex,
422 NULL},
349 {"gssapi-with-mic", 423 {"gssapi-with-mic",
350 userauth_gssapi, 424 userauth_gssapi,
351 userauth_gssapi_cleanup, 425 userauth_gssapi_cleanup,
@@ -716,12 +790,31 @@ userauth_gssapi(struct ssh *ssh)
716 OM_uint32 min; 790 OM_uint32 min;
717 int r, ok = 0; 791 int r, ok = 0;
718 gss_OID mech = NULL; 792 gss_OID mech = NULL;
793 char *gss_host;
794
795 if (options.gss_server_identity) {
796 gss_host = xstrdup(options.gss_server_identity);
797 } else if (options.gss_trust_dns) {
798 gss_host = remote_hostname(ssh);
799 /* Fall back to specified host if we are using proxy command
800 * and can not use DNS on that socket */
801 if (strcmp(gss_host, "UNKNOWN") == 0) {
802 gss_host = authctxt->host;
803 }
804 } else {
805 gss_host = xstrdup(authctxt->host);
806 }
719 807
720 /* Try one GSSAPI method at a time, rather than sending them all at 808 /* Try one GSSAPI method at a time, rather than sending them all at
721 * once. */ 809 * once. */
722 810
723 if (authctxt->gss_supported_mechs == NULL) 811 if (authctxt->gss_supported_mechs == NULL)
724 gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 812 if (GSS_ERROR(gss_indicate_mechs(&min,
813 &authctxt->gss_supported_mechs))) {
814 authctxt->gss_supported_mechs = NULL;
815 free(gss_host);
816 return 0;
817 }
725 818
726 /* Check to see whether the mechanism is usable before we offer it */ 819 /* Check to see whether the mechanism is usable before we offer it */
727 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 820 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
@@ -730,13 +823,15 @@ userauth_gssapi(struct ssh *ssh)
730 elements[authctxt->mech_tried]; 823 elements[authctxt->mech_tried];
731 /* My DER encoding requires length<128 */ 824 /* My DER encoding requires length<128 */
732 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 825 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
733 mech, authctxt->host)) { 826 mech, gss_host, options.gss_client_identity)) {
734 ok = 1; /* Mechanism works */ 827 ok = 1; /* Mechanism works */
735 } else { 828 } else {
736 authctxt->mech_tried++; 829 authctxt->mech_tried++;
737 } 830 }
738 } 831 }
739 832
833 free(gss_host);
834
740 if (!ok || mech == NULL) 835 if (!ok || mech == NULL)
741 return 0; 836 return 0;
742 837
@@ -976,6 +1071,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
976 free(lang); 1071 free(lang);
977 return r; 1072 return r;
978} 1073}
1074
1075int
1076userauth_gsskeyex(struct ssh *ssh)
1077{
1078 struct sshbuf *b = NULL;
1079 Authctxt *authctxt = ssh->authctxt;
1080 gss_buffer_desc gssbuf;
1081 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1082 OM_uint32 ms;
1083 int r;
1084
1085 static int attempt = 0;
1086 if (attempt++ >= 1)
1087 return (0);
1088
1089 if (gss_kex_context == NULL) {
1090 debug("No valid Key exchange context");
1091 return (0);
1092 }
1093
1094 if ((b = sshbuf_new()) == NULL)
1095 fatal("%s: sshbuf_new failed", __func__);
1096
1097 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1098 "gssapi-keyex");
1099
1100 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1101 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1102 gssbuf.length = sshbuf_len(b);
1103
1104 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1105 sshbuf_free(b);
1106 return (0);
1107 }
1108
1109 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1110 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1111 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1112 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1113 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1114 (r = sshpkt_send(ssh)) != 0)
1115 fatal("%s: %s", __func__, ssh_err(r));
1116
1117 sshbuf_free(b);
1118 gss_release_buffer(&ms, &mic);
1119
1120 return (1);
1121}
1122
979#endif /* GSSAPI */ 1123#endif /* GSSAPI */
980 1124
981static int 1125static int
diff --git a/sshd.c b/sshd.c
index 6f8f11a3b..02fca5c28 100644
--- a/sshd.c
+++ b/sshd.c
@@ -816,8 +816,8 @@ notify_hostkeys(struct ssh *ssh)
816 } 816 }
817 debug3("%s: sent %u hostkeys", __func__, nkeys); 817 debug3("%s: sent %u hostkeys", __func__, nkeys);
818 if (nkeys == 0) 818 if (nkeys == 0)
819 fatal("%s: no hostkeys", __func__); 819 debug3("%s: no hostkeys", __func__);
820 if ((r = sshpkt_send(ssh)) != 0) 820 else if ((r = sshpkt_send(ssh)) != 0)
821 sshpkt_fatal(ssh, r, "%s: send", __func__); 821 sshpkt_fatal(ssh, r, "%s: send", __func__);
822 sshbuf_free(buf); 822 sshbuf_free(buf);
823} 823}
@@ -1851,7 +1851,8 @@ main(int ac, char **av)
1851 free(fp); 1851 free(fp);
1852 } 1852 }
1853 accumulate_host_timing_secret(cfg, NULL); 1853 accumulate_host_timing_secret(cfg, NULL);
1854 if (!sensitive_data.have_ssh2_key) { 1854 /* The GSSAPI key exchange can run without a host key */
1855 if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
1855 logit("sshd: no hostkeys available -- exiting."); 1856 logit("sshd: no hostkeys available -- exiting.");
1856 exit(1); 1857 exit(1);
1857 } 1858 }
@@ -2342,6 +2343,48 @@ do_ssh2_kex(struct ssh *ssh)
2342 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2343 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2343 list_hostkey_types()); 2344 list_hostkey_types());
2344 2345
2346#if defined(GSSAPI) && defined(WITH_OPENSSL)
2347 {
2348 char *orig;
2349 char *gss = NULL;
2350 char *newstr = NULL;
2351 orig = myproposal[PROPOSAL_KEX_ALGS];
2352
2353 /*
2354 * If we don't have a host key, then there's no point advertising
2355 * the other key exchange algorithms
2356 */
2357
2358 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2359 orig = NULL;
2360
2361 if (options.gss_keyex)
2362 gss = ssh_gssapi_server_mechanisms();
2363 else
2364 gss = NULL;
2365
2366 if (gss && orig)
2367 xasprintf(&newstr, "%s,%s", gss, orig);
2368 else if (gss)
2369 newstr = gss;
2370 else if (orig)
2371 newstr = orig;
2372
2373 /*
2374 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2375 * key alg, but we can't tell people about it unless its the only
2376 * host key algorithm we support
2377 */
2378 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2379 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2380
2381 if (newstr)
2382 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2383 else
2384 fatal("No supported key exchange algorithms");
2385 }
2386#endif
2387
2345 /* start key exchange */ 2388 /* start key exchange */
2346 if ((r = kex_setup(ssh, myproposal)) != 0) 2389 if ((r = kex_setup(ssh, myproposal)) != 0)
2347 fatal("kex_setup: %s", ssh_err(r)); 2390 fatal("kex_setup: %s", ssh_err(r));
@@ -2357,7 +2400,18 @@ do_ssh2_kex(struct ssh *ssh)
2357# ifdef OPENSSL_HAS_ECC 2400# ifdef OPENSSL_HAS_ECC
2358 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 2401 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2359# endif 2402# endif
2360#endif 2403# ifdef GSSAPI
2404 if (options.gss_keyex) {
2405 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2406 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2407 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2408 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2409 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2410 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2411 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2412 }
2413# endif
2414#endif /* WITH_OPENSSL */
2361 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 2415 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2362 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 2416 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2363 kex->load_host_public_key=&get_hostkey_public_by_type; 2417 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 b294efc2d..360e5fb1a 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -644,6 +644,11 @@ Specifies whether to automatically destroy the user's credentials cache
644on logout. 644on logout.
645The default is 645The default is
646.Cm yes . 646.Cm yes .
647.It Cm GSSAPIKeyExchange
648Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
649doesn't rely on ssh keys to verify host identity.
650The default is
651.Cm no .
647.It Cm GSSAPIStrictAcceptorCheck 652.It Cm GSSAPIStrictAcceptorCheck
648Determines whether to be strict about the identity of the GSSAPI acceptor 653Determines whether to be strict about the identity of the GSSAPI acceptor
649a client authenticates against. 654a client authenticates against.
@@ -658,6 +663,31 @@ machine's default store.
658This facility is provided to assist with operation on multi homed machines. 663This facility is provided to assist with operation on multi homed machines.
659The default is 664The default is
660.Cm yes . 665.Cm yes .
666.It Cm GSSAPIStoreCredentialsOnRekey
667Controls whether the user's GSSAPI credentials should be updated following a
668successful connection rekeying. This option can be used to accepted renewed
669or updated credentials from a compatible client. The default is
670.Dq no .
671.Pp
672For this to work
673.Cm GSSAPIKeyExchange
674needs to be enabled in the server and also used by the client.
675.It Cm GSSAPIKexAlgorithms
676The list of key exchange algorithms that are accepted by GSSAPI
677key exchange. Possible values are
678.Bd -literal -offset 3n
679gss-gex-sha1-,
680gss-group1-sha1-,
681gss-group14-sha1-,
682gss-group14-sha256-,
683gss-group16-sha512-,
684gss-nistp256-sha256-,
685gss-curve25519-sha256-
686.Ed
687.Pp
688The default is
689.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-gex-sha1-,gss-group14-sha1- .
690This option only applies to connections using GSSAPI.
661.It Cm HostbasedAcceptedKeyTypes 691.It Cm HostbasedAcceptedKeyTypes
662Specifies the key types that will be accepted for hostbased authentication 692Specifies the key types that will be accepted for hostbased authentication
663as a list of comma-separated patterns. 693as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index 1571e3d93..1ac32a0ec 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 9c1d4f637..f586e8967 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