1 From d49f189d9549a316d9213a441323d72b8f40cccf Mon Sep 17 00:00:00 2001
2 From: oracle <solaris@oracle.com>
3 Date: Mon, 3 Aug 2015 14:37:24 -0700
4 Subject: [PATCH 16/36] GSS-API key exchange support
5
6 ---
7 Makefile.in | 3 +-
8 auth2-gss.c | 41 ++++++-
9 auth2.c | 2 +
10 gss-genr.c | 179 ++++++++++++++++++++++++++++-
11 gss-serv.c | 39 +++++--
12 kex.c | 11 +-
13 kex.h | 11 ++
14 kexgssc.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
15 kexgsss.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++
16 monitor.c | 77 +++++++++++++
17 monitor.h | 3 +
18 monitor_wrap.c | 23 ++++
19 monitor_wrap.h | 1 +
20 readconf.c | 14 +++
21 readconf.h | 1 +
22 servconf.c | 15 +++
23 servconf.h | 1 +
24 ssh-gss.h | 19 ++++
25 ssh_config | 1 +
26 ssh_config.4 | 6 +
27 sshconnect2.c | 104 ++++++++++++++++-
28 sshd.c | 52 +++++++++
29 sshd_config | 3 +-
30 sshd_config.4 | 6 +
31 sshkey.c | 1 +
32 sshkey.h | 3 +
33 26 files changed, 1239 insertions(+), 21 deletions(-)
34 create mode 100644 kexgssc.c
35 create mode 100644 kexgsss.c
36
37 diff --git a/Makefile.in b/Makefile.in
38 index 62e6a84..0148742 100644
39 --- a/Makefile.in
40 +++ b/Makefile.in
41 @@ -87,6 +87,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
42 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
43 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
44 sftp_provider.o \
45 + kexgssc.o \
46 ssh-pkcs11.o smult_curve25519_ref.o \
47 poly1305.o chacha.o cipher-chachapoly.o \
48 ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \
49 @@ -108,7 +109,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
50 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
51 auth2-none.o auth2-passwd.o auth2-pubkey.o \
52 monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \
53 - auth2-gss.o gss-serv.o gss-serv-krb5.o \
54 + auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
55 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
56 sftp-server.o sftp-common.o \
57 roaming_common.o roaming_serv.o \
58 diff --git a/auth2-gss.c b/auth2-gss.c
59 index 1ca8357..24999c8 100644
60 --- a/auth2-gss.c
61 +++ b/auth2-gss.c
62 @@ -1,7 +1,7 @@
63 /* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */
64
65 /*
66 - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
67 + * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
68 *
69 * Redistribution and use in source and binary forms, with or without
70 * modification, are permitted provided that the following conditions
71 @@ -53,6 +53,39 @@ static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
72 static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
73 static int input_gssapi_errtok(int, u_int32_t, void *);
74
75 +/*
76 + * The 'gssapi_keyex' userauth mechanism.
77 + */
78 +static int
79 +userauth_gsskeyex(Authctxt *authctxt)
80 +{
81 + int authenticated = 0;
82 + Buffer b;
83 + gss_buffer_desc mic, gssbuf;
84 + u_int len;
85 +
86 + mic.value = packet_get_string(&len);
87 + mic.length = len;
88 +
89 + packet_check_eom();
90 +
91 + ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
92 + "gssapi-keyex");
93 +
94 + gssbuf.value = buffer_ptr(&b);
95 + gssbuf.length = buffer_len(&b);
96 +
97 + /* gss_kex_context is NULL with privsep, so we can't check it here */
98 + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
99 + &gssbuf, &mic))))
100 + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
101 +
102 + buffer_free(&b);
103 + free(mic.value);
104 +
105 + return (authenticated);
106 +}
107 +
108 /*
109 * We only support those mechanisms that we know about (ie ones that we know
110 * how to check local user kuserok and the like)
111 @@ -290,6 +323,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
112 return 0;
113 }
114
115 +Authmethod method_gsskeyex = {
116 + "gssapi-keyex",
117 + userauth_gsskeyex,
118 + &options.gss_authentication
119 +};
120 +
121 Authmethod method_gssapi = {
122 "gssapi-with-mic",
123 userauth_gssapi,
124 diff --git a/auth2.c b/auth2.c
125 index 32ba663..5a3ef1b 100644
126 --- a/auth2.c
127 +++ b/auth2.c
128 @@ -70,6 +70,7 @@ extern Authmethod method_passwd;
129 extern Authmethod method_kbdint;
130 extern Authmethod method_hostbased;
131 #ifdef GSSAPI
132 +extern Authmethod method_gsskeyex;
133 extern Authmethod method_gssapi;
134 #endif
135
136 @@ -77,6 +78,7 @@ Authmethod *authmethods[] = {
137 &method_none,
138 &method_pubkey,
139 #ifdef GSSAPI
140 + &method_gsskeyex,
141 &method_gssapi,
142 #endif
143 &method_passwd,
144 diff --git a/gss-genr.c b/gss-genr.c
145 index d617d60..9dcf51c 100644
146 --- a/gss-genr.c
147 +++ b/gss-genr.c
148 @@ -1,7 +1,7 @@
149 /* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */
150
151 /*
152 - * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
153 + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
154 *
155 * Redistribution and use in source and binary forms, with or without
156 * modification, are permitted provided that the following conditions
157 @@ -41,12 +41,167 @@
158 #include "buffer.h"
159 #include "log.h"
160 #include "ssh2.h"
161 +#include "cipher.h"
162 +#include "key.h"
163 +#include "kex.h"
164 +#include <openssl/evp.h>
165
166 #include "ssh-gss.h"
167
168 extern u_char *session_id2;
169 extern u_int session_id2_len;
170
171 +typedef struct {
172 + char *encoded;
173 + gss_OID oid;
174 +} ssh_gss_kex_mapping;
175 +
176 +/*
177 + * XXX - It would be nice to find a more elegant way of handling the
178 + * XXX passing of the key exchange context to the userauth routines
179 + */
180 +
181 +Gssctxt *gss_kex_context = NULL;
182 +
183 +static ssh_gss_kex_mapping *gss_enc2oid = NULL;
184 +
185 +int
186 +ssh_gssapi_oid_table_ok() {
187 + return (gss_enc2oid != NULL);
188 +}
189 +
190 +/*
191 + * Return a list of the gss-group1-sha1 mechanisms supported by this program
192 + *
193 + * We test mechanisms to ensure that we can use them, to avoid starting
194 + * a key exchange with a bad mechanism
195 + */
196 +
197 +char *
198 +ssh_gssapi_client_mechanisms(const char *host) {
199 + gss_OID_set gss_supported;
200 + OM_uint32 min_status;
201 +
202 + if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
203 + return NULL;
204 +
205 + return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
206 + host));
207 +}
208 +
209 +char *
210 +ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
211 + const char *data) {
212 + Buffer buf;
213 + size_t i;
214 + int oidpos, enclen;
215 + char *mechs, *encoded;
216 + u_char digest[EVP_MAX_MD_SIZE];
217 + char deroid[2];
218 + const EVP_MD *evp_md = EVP_md5();
219 + EVP_MD_CTX md;
220 +
221 + if (gss_enc2oid != NULL) {
222 + for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
223 + free(gss_enc2oid[i].encoded);
224 + free(gss_enc2oid);
225 + }
226 +
227 + gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
228 + (gss_supported->count + 1));
229 +
230 + buffer_init(&buf);
231 +
232 + oidpos = 0;
233 + for (i = 0; i < gss_supported->count; i++) {
234 + if (gss_supported->elements[i].length < 128 &&
235 + (*check)(NULL, &(gss_supported->elements[i]), data)) {
236 +
237 + deroid[0] = SSH_GSS_OIDTYPE;
238 + deroid[1] = gss_supported->elements[i].length;
239 +
240 + EVP_DigestInit(&md, evp_md);
241 + EVP_DigestUpdate(&md, deroid, 2);
242 + EVP_DigestUpdate(&md,
243 + gss_supported->elements[i].elements,
244 + gss_supported->elements[i].length);
245 + EVP_DigestFinal(&md, digest, NULL);
246 +
247 + encoded = xmalloc(EVP_MD_size(evp_md) * 2);
248 + enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
249 + encoded, EVP_MD_size(evp_md) * 2);
250 +
251 + if (oidpos != 0)
252 + buffer_put_char(&buf, ',');
253 +
254 + buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
255 + sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
256 + buffer_append(&buf, encoded, enclen);
257 + buffer_put_char(&buf, ',');
258 + buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
259 + sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
260 + buffer_append(&buf, encoded, enclen);
261 + buffer_put_char(&buf, ',');
262 + buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
263 + sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
264 + buffer_append(&buf, encoded, enclen);
265 +
266 + gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
267 + gss_enc2oid[oidpos].encoded = encoded;
268 + oidpos++;
269 + }
270 + }
271 + gss_enc2oid[oidpos].oid = NULL;
272 + gss_enc2oid[oidpos].encoded = NULL;
273 +
274 + buffer_put_char(&buf, '\0');
275 +
276 + mechs = xmalloc(buffer_len(&buf));
277 + buffer_get(&buf, mechs, buffer_len(&buf));
278 + buffer_free(&buf);
279 +
280 + if (strlen(mechs) == 0) {
281 + free(mechs);
282 + mechs = NULL;
283 + }
284 +
285 + return (mechs);
286 +}
287 +
288 +gss_OID
289 +ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
290 + int i = 0;
291 +
292 + switch (kex_type) {
293 + case KEX_GSS_GRP1_SHA1:
294 + if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
295 + return GSS_C_NO_OID;
296 + name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
297 + break;
298 + case KEX_GSS_GRP14_SHA1:
299 + if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
300 + return GSS_C_NO_OID;
301 + name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
302 + break;
303 + case KEX_GSS_GEX_SHA1:
304 + if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
305 + return GSS_C_NO_OID;
306 + name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
307 + break;
308 + default:
309 + return GSS_C_NO_OID;
310 + }
311 +
312 + while (gss_enc2oid[i].encoded != NULL &&
313 + strcmp(name, gss_enc2oid[i].encoded) != 0)
314 + i++;
315 +
316 + if (gss_enc2oid[i].oid != NULL && ctx != NULL)
317 + ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
318 +
319 + return gss_enc2oid[i].oid;
320 +}
321 +
322 /* Check that the OID in a data stream matches that in the context */
323 int
324 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
325 @@ -231,6 +386,9 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
326 OM_uint32
327 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
328 {
329 + if (ctx == NULL)
330 + return -1;
331 +
332 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
333 GSS_C_QOP_DEFAULT, buffer, hash)))
334 ssh_gssapi_error(ctx);
335 @@ -238,6 +396,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
336 return (ctx->major);
337 }
338
339 +/* Priviledged when used by server */
340 +OM_uint32
341 +ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
342 +{
343 + if (ctx == NULL)
344 + return -1;
345 +
346 + ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
347 + gssbuf, gssmic, NULL);
348 +
349 + return (ctx->major);
350 +}
351 +
352 void
353 ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
354 const char *context)
355 @@ -256,6 +427,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
356 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
357 OM_uint32 major, minor;
358 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
359 + Gssctxt *intctx = NULL;
360 +
361 + if (ctx == NULL)
362 + ctx = &intctx;
363
364 /* RFC 4462 says we MUST NOT do SPNEGO */
365 if (oid->length == spnego_oid.length &&
366 @@ -274,7 +449,7 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
367 GSS_C_NO_BUFFER);
368 }
369
370 - if (GSS_ERROR(major))
371 + if (GSS_ERROR(major) || intctx != NULL)
372 ssh_gssapi_delete_ctx(ctx);
373
374 return (!GSS_ERROR(major));
375 diff --git a/gss-serv.c b/gss-serv.c
376 index 209ffe8..a45d8fd 100644
377 --- a/gss-serv.c
378 +++ b/gss-serv.c
379 @@ -1,7 +1,7 @@
380 /* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */
381
382 /*
383 - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
384 + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
385 *
386 * Redistribution and use in source and binary forms, with or without
387 * modification, are permitted provided that the following conditions
388 @@ -47,6 +47,7 @@
389 #include "servconf.h"
390
391 #include "ssh-gss.h"
392 +#include "monitor_wrap.h"
393
394 extern ServerOptions options;
395
396 @@ -142,6 +143,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
397 }
398
399 /* Unprivileged */
400 +char *
401 +ssh_gssapi_server_mechanisms() {
402 + gss_OID_set supported;
403 +
404 + ssh_gssapi_supported_oids(&supported);
405 + return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
406 + NULL));
407 +}
408 +
409 +/* Unprivileged */
410 +int
411 +ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data) {
412 + Gssctxt *ctx = NULL;
413 + int res;
414 +
415 + res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
416 + ssh_gssapi_delete_ctx(&ctx);
417 +
418 + return (res);
419 +}
420 +
421 +/* Unprivileged */
422 void
423 ssh_gssapi_supported_oids(gss_OID_set *oidset)
424 {
425 @@ -151,7 +174,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
426 gss_OID_set supported;
427
428 gss_create_empty_oid_set(&min_status, oidset);
429 - gss_indicate_mechs(&min_status, &supported);
430 +
431 + if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
432 + return;
433
434 while (supported_mechs[i]->name != NULL) {
435 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
436 @@ -427,14 +452,4 @@ ssh_gssapi_userok(char *user)
437 return (0);
438 }
439
440 -/* Privileged */
441 -OM_uint32
442 -ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
443 -{
444 - ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
445 - gssbuf, gssmic, NULL);
446 -
447 - return (ctx->major);
448 -}
449 -
450 #endif
451 diff --git a/kex.c b/kex.c
452 index b777b7d..ad0598e 100644
453 --- a/kex.c
454 +++ b/kex.c
455 @@ -55,6 +55,10 @@
456 #include "sshbuf.h"
457 #include "digest.h"
458
459 +#ifdef GSSAPI
460 +#include "ssh-gss.h"
461 +#endif
462 +
463 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
464 # if defined(HAVE_EVP_SHA256)
465 # define evp_ssh_sha256 EVP_sha256
466 @@ -95,6 +99,11 @@ static const struct kexalg kexalgs[] = {
467 #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
468 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
469 #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
470 +#ifdef GSSAPI
471 + { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
472 + { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
473 + { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
474 +#endif
475 { NULL, -1, -1, -1},
476 };
477
478 @@ -126,7 +135,7 @@ kex_alg_by_name(const char *name)
479 const struct kexalg *k;
480
481 for (k = kexalgs; k->name != NULL; k++) {
482 - if (strcmp(k->name, name) == 0)
483 + if (strncmp(k->name, name, strlen(k->name)) == 0)
484 return k;
485 }
486 return NULL;
487 diff --git a/kex.h b/kex.h
488 index d71b532..04b4733 100644
489 --- a/kex.h
490 +++ b/kex.h
491 @@ -93,6 +93,9 @@ enum kex_exchange {
492 KEX_DH_GEX_SHA256,
493 KEX_ECDH_SHA2,
494 KEX_C25519_SHA256,
495 + KEX_GSS_GRP1_SHA1,
496 + KEX_GSS_GRP14_SHA1,
497 + KEX_GSS_GEX_SHA1,
498 KEX_MAX
499 };
500
501 @@ -139,6 +142,10 @@ struct kex {
502 u_int flags;
503 int hash_alg;
504 int ec_nid;
505 +#ifdef GSSAPI
506 + int gss_deleg_creds;
507 + char *gss_host;
508 +#endif
509 char *client_version_string;
510 char *server_version_string;
511 char *failed_choice;
512 @@ -186,6 +193,10 @@ int kexecdh_client(struct ssh *);
513 int kexecdh_server(struct ssh *);
514 int kexc25519_client(struct ssh *);
515 int kexc25519_server(struct ssh *);
516 +#ifdef GSSAPI
517 +int kexgss_client(struct ssh *);
518 +int kexgss_server(struct ssh *);
519 +#endif
520
521 int kex_dh_hash(const char *, const char *,
522 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
523 diff --git a/kexgssc.c b/kexgssc.c
524 new file mode 100644
525 index 0000000..c36d36b
526 --- /dev/null
527 +++ b/kexgssc.c
528 @@ -0,0 +1,347 @@
529 +/*
530 + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
531 + *
532 + * Redistribution and use in source and binary forms, with or without
533 + * modification, are permitted provided that the following conditions
534 + * are met:
535 + * 1. Redistributions of source code must retain the above copyright
536 + * notice, this list of conditions and the following disclaimer.
537 + * 2. Redistributions in binary form must reproduce the above copyright
538 + * notice, this list of conditions and the following disclaimer in the
539 + * documentation and/or other materials provided with the distribution.
540 + *
541 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
542 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
543 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
544 + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
545 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
546 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
547 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
548 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
549 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
550 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
551 + */
552 +
553 +/*
554 + * May 22, 2015
555 + * In version 6.8 a new packet interface has been introduced to OpenSSH,
556 + * while the old packet API has been provided in opacket.c.
557 + * At this moment we are not rewritting GSS-API key exchange code to the new
558 + * API, just adjusting it to still work with new struct ssh.
559 + * Rewritting to the new API can be considered in the future.
560 + */
561 +
562 +#include "includes.h"
563 +
564 +#ifdef GSSAPI
565 +
566 +#include "includes.h"
567 +
568 +#include <openssl/crypto.h>
569 +#include <openssl/bn.h>
570 +
571 +#include <signal.h> /* for sig_atomic_t in kex.h */
572 +#include <string.h>
573 +
574 +#include "xmalloc.h"
575 +#include "buffer.h"
576 +#include "ssh2.h"
577 +#include "key.h"
578 +#include "cipher.h"
579 +#include "digest.h"
580 +#include "kex.h"
581 +#include "log.h"
582 +#include "packet.h"
583 +#include "dh.h"
584 +
585 +#include "ssh-gss.h"
586 +
587 +int
588 +kexgss_client(struct ssh *ssh) {
589 + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
590 + gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
591 + Gssctxt *ctxt;
592 + OM_uint32 maj_status, min_status, ret_flags;
593 + uint_t klen, kout, slen = 0, strlen;
594 + DH *dh;
595 + BIGNUM *dh_server_pub = NULL;
596 + BIGNUM *shared_secret = NULL;
597 + BIGNUM *p = NULL;
598 + BIGNUM *g = NULL;
599 + uchar_t *kbuf;
600 + uchar_t *serverhostkey = NULL;
601 + uchar_t *empty = "";
602 + char *msg;
603 + char *lang;
604 + int type = 0;
605 + int first = 1;
606 + int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
607 + struct kex *kex = ssh->kex;
608 + int r;
609 + uchar_t hash[SSH_DIGEST_MAX_LENGTH];
610 + size_t hashlen;
611 +
612 + /* Initialise our GSSAPI world */
613 + ssh_gssapi_build_ctx(&ctxt);
614 + if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
615 + == GSS_C_NO_OID)
616 + fatal("Couldn't identify host exchange");
617 +
618 + if (ssh_gssapi_import_name(ctxt, kex->gss_host))
619 + fatal("Couldn't import hostname");
620 +
621 + switch (kex->kex_type) {
622 + case KEX_GSS_GRP1_SHA1:
623 + kex->dh = dh_new_group1();
624 + break;
625 + case KEX_GSS_GRP14_SHA1:
626 + kex->dh = dh_new_group14();
627 + break;
628 + case KEX_GSS_GEX_SHA1:
629 + debug("Doing group exchange\n");
630 + nbits = dh_estimate(kex->we_need * 8);
631 + packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
632 + packet_put_int(min);
633 + packet_put_int(nbits);
634 + packet_put_int(max);
635 +
636 + packet_send();
637 +
638 + packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
639 +
640 + if ((p = BN_new()) == NULL)
641 + fatal("BN_new() failed");
642 + packet_get_bignum2(p);
643 + if ((g = BN_new()) == NULL)
644 + fatal("BN_new() failed");
645 + packet_get_bignum2(g);
646 + packet_check_eom();
647 +
648 + if (BN_num_bits(p) < min || BN_num_bits(p) > max)
649 + fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
650 + min, BN_num_bits(p), max);
651 +
652 + kex->dh = dh_new_group(g, p);
653 + break;
654 + default:
655 + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
656 + }
657 +
658 + /* Step 1 - e is dh->pub_key */
659 + dh_gen_key(kex->dh, kex->we_need * 8);
660 +
661 + /* This is f, we initialise it now to make life easier */
662 + dh_server_pub = BN_new();
663 + if (dh_server_pub == NULL)
664 + fatal("dh_server_pub == NULL");
665 +
666 + token_ptr = GSS_C_NO_BUFFER;
667 +
668 + do {
669 + debug("Calling gss_init_sec_context");
670 +
671 + maj_status = ssh_gssapi_init_ctx(ctxt,
672 + kex->gss_deleg_creds, token_ptr, &send_tok,
673 + &ret_flags);
674 +
675 + if (GSS_ERROR(maj_status)) {
676 + if (send_tok.length != 0) {
677 + packet_start(SSH2_MSG_KEXGSS_CONTINUE);
678 + packet_put_string(send_tok.value,
679 + send_tok.length);
680 + }
681 + fatal("gss_init_context failed");
682 + }
683 +
684 + /* If we've got an old receive buffer get rid of it */
685 + if (token_ptr != GSS_C_NO_BUFFER)
686 + free(recv_tok.value);
687 +
688 + if (maj_status == GSS_S_COMPLETE) {
689 + /* If mutual state flag is not true, kex fails */
690 + if (!(ret_flags & GSS_C_MUTUAL_FLAG))
691 + fatal("Mutual authentication failed");
692 +
693 + /* If integ avail flag is not true kex fails */
694 + if (!(ret_flags & GSS_C_INTEG_FLAG))
695 + fatal("Integrity check failed");
696 + }
697 +
698 + /*
699 + * If we have data to send, then the last message that we
700 + * received cannot have been a 'complete'.
701 + */
702 + if (send_tok.length != 0) {
703 + if (first) {
704 + packet_start(SSH2_MSG_KEXGSS_INIT);
705 + packet_put_string(send_tok.value,
706 + send_tok.length);
707 + packet_put_bignum2(kex->dh->pub_key);
708 + first = 0;
709 + } else {
710 + packet_start(SSH2_MSG_KEXGSS_CONTINUE);
711 + packet_put_string(send_tok.value,
712 + send_tok.length);
713 + }
714 + packet_send();
715 + gss_release_buffer(&min_status, &send_tok);
716 +
717 + /* If we've sent them data, they should reply */
718 + do {
719 + type = packet_read();
720 + if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
721 + debug("Received KEXGSS_HOSTKEY");
722 + if (serverhostkey)
723 + fatal("Server host key received"
724 + "more than once");
725 + serverhostkey =
726 + packet_get_string(&slen);
727 + }
728 + } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
729 +
730 + switch (type) {
731 + case SSH2_MSG_KEXGSS_CONTINUE:
732 + debug("Received GSSAPI_CONTINUE");
733 + if (maj_status == GSS_S_COMPLETE)
734 + fatal("GSSAPI Continue received from"
735 + "server when complete");
736 + recv_tok.value = packet_get_string(&strlen);
737 + recv_tok.length = strlen;
738 + break;
739 + case SSH2_MSG_KEXGSS_COMPLETE:
740 + debug("Received GSSAPI_COMPLETE");
741 + packet_get_bignum2(dh_server_pub);
742 + msg_tok.value = packet_get_string(&strlen);
743 + msg_tok.length = strlen;
744 +
745 + /* Is there a token included? */
746 + if (packet_get_char()) {
747 + recv_tok.value=
748 + packet_get_string(&strlen);
749 + recv_tok.length = strlen;
750 + /* If complete - protocol error */
751 + if (maj_status == GSS_S_COMPLETE)
752 + packet_disconnect("Protocol"
753 + " error: received token"
754 + " when complete");
755 + } else {
756 + /* No token included */
757 + if (maj_status != GSS_S_COMPLETE)
758 + packet_disconnect("Protocol"
759 + " error: did not receive"
760 + " final token");
761 + }
762 + break;
763 + case SSH2_MSG_KEXGSS_ERROR:
764 + debug("Received Error");
765 + maj_status = packet_get_int();
766 + min_status = packet_get_int();
767 + msg = packet_get_string(NULL);
768 + lang = packet_get_string(NULL);
769 + fatal("GSSAPI Error: \n%.400s", msg);
770 + default:
771 + packet_disconnect("Protocol error: didn't"
772 + " expect packet type %d", type);
773 + }
774 + token_ptr = &recv_tok;
775 + } else {
776 + /* No data, and not complete */
777 + if (maj_status != GSS_S_COMPLETE)
778 + fatal("Not complete, and no token output");
779 + }
780 + } while (maj_status & GSS_S_CONTINUE_NEEDED);
781 +
782 + /*
783 + * We _must_ have received a COMPLETE message in reply from the
784 + * server, which will have set dh_server_pub and msg_tok
785 + */
786 +
787 + if (type != SSH2_MSG_KEXGSS_COMPLETE)
788 + fatal("Didn't receive SSH2_MSG_KEXGSS_COMPLETE when expected");
789 +
790 + /* Check f in range [1, p-1] */
791 + if (!dh_pub_is_valid(kex->dh, dh_server_pub))
792 + packet_disconnect("bad server public DH value");
793 +
794 + /* compute K=f^x mod p */
795 + klen = DH_size(kex->dh);
796 + kbuf = xmalloc(klen);
797 + kout = DH_compute_key(kbuf, dh_server_pub, kex->dh);
798 + if (kout < 0)
799 + fatal("DH_compute_key: failed");
800 +
801 + shared_secret = BN_new();
802 + if (shared_secret == NULL)
803 + fatal("kexgss_client: BN_new failed");
804 +
805 + if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
806 + fatal("kexdh_client: BN_bin2bn failed");
807 +
808 + memset(kbuf, 0, klen);
809 + free(kbuf);
810 +
811 + hashlen = sizeof (hash);
812 + switch (kex->kex_type) {
813 + case KEX_GSS_GRP1_SHA1:
814 + case KEX_GSS_GRP14_SHA1:
815 + kex_dh_hash(kex->client_version_string,
816 + kex->server_version_string,
817 + buffer_ptr(kex->my), buffer_len(kex->my),
818 + buffer_ptr(kex->peer), buffer_len(kex->peer),
819 + (serverhostkey ? serverhostkey : empty), slen,
820 + kex->dh->pub_key, /* e */
821 + dh_server_pub, /* f */
822 + shared_secret, /* K */
823 + hash, &hashlen);
824 + break;
825 + case KEX_GSS_GEX_SHA1:
826 + kexgex_hash(
827 + kex->hash_alg,
828 + kex->client_version_string,
829 + kex->server_version_string,
830 + buffer_ptr(kex->my), buffer_len(kex->my),
831 + buffer_ptr(kex->peer), buffer_len(kex->peer),
832 + (serverhostkey ? serverhostkey : empty), slen,
833 + min, nbits, max,
834 + kex->dh->p, kex->dh->g,
835 + kex->dh->pub_key,
836 + dh_server_pub,
837 + shared_secret,
838 + hash, &hashlen);
839 + break;
840 + default:
841 + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
842 + }
843 +
844 + gssbuf.value = hash;
845 + gssbuf.length = hashlen;
846 +
847 + /* Verify that the hash matches the MIC we just got. */
848 + if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
849 + packet_disconnect("Hash's MIC didn't verify");
850 +
851 + free(msg_tok.value);
852 +
853 + DH_free(kex->dh);
854 + if (serverhostkey)
855 + free(serverhostkey);
856 + BN_clear_free(dh_server_pub);
857 +
858 + /* save session id */
859 + if (kex->session_id == NULL) {
860 + kex->session_id_len = hashlen;
861 + kex->session_id = xmalloc(kex->session_id_len);
862 + memcpy(kex->session_id, hash, kex->session_id_len);
863 + }
864 +
865 + if (gss_kex_context == NULL)
866 + gss_kex_context = ctxt;
867 + else
868 + ssh_gssapi_delete_ctx(&ctxt);
869 +
870 + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
871 + r = kex_send_newkeys(ssh);
872 + return (r);
873 +}
874 +
875 +#endif /* GSSAPI */
876 diff --git a/kexgsss.c b/kexgsss.c
877 new file mode 100644
878 index 0000000..2009cd9
879 --- /dev/null
880 +++ b/kexgsss.c
881 @@ -0,0 +1,297 @@
882 +/*
883 + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
884 + *
885 + * Redistribution and use in source and binary forms, with or without
886 + * modification, are permitted provided that the following conditions
887 + * are met:
888 + * 1. Redistributions of source code must retain the above copyright
889 + * notice, this list of conditions and the following disclaimer.
890 + * 2. Redistributions in binary form must reproduce the above copyright
891 + * notice, this list of conditions and the following disclaimer in the
892 + * documentation and/or other materials provided with the distribution.
893 + *
894 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
895 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
896 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
897 + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
898 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
899 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
900 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
901 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
902 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
903 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
904 + */
905 +
906 +/*
907 + * May 22, 2015
908 + * In version 6.8 a new packet interface has been introduced to OpenSSH,
909 + * while the old packet API has been provided in opacket.c.
910 + * At this moment we are not rewritting GSS-API key exchange code to the new
911 + * API, just adjusting it to still work with new struct ssh.
912 + * Rewritting to the new API can be considered in the future.
913 + */
914 +
915 +#include "includes.h"
916 +
917 +#ifdef GSSAPI
918 +
919 +#include <signal.h> /* for sig_atomic_t in kex.h */
920 +#include <string.h>
921 +
922 +#include <openssl/crypto.h>
923 +#include <openssl/bn.h>
924 +
925 +#include "xmalloc.h"
926 +#include "buffer.h"
927 +#include "ssh2.h"
928 +#include "key.h"
929 +#include "cipher.h"
930 +#include "digest.h"
931 +#include "kex.h"
932 +#include "log.h"
933 +#include "packet.h"
934 +#include "dh.h"
935 +#include "ssh-gss.h"
936 +#include "monitor_wrap.h"
937 +
938 +int
939 +kexgss_server(struct ssh *ssh)
940 +{
941 + OM_uint32 maj_status, min_status;
942 +
943 + /*
944 + * Some GSSAPI implementations use the input value of ret_flags (an
945 + * output variable) as a means of triggering mechanism specific
946 + * features. Initializing it to zero avoids inadvertently
947 + * activating this non-standard behaviour.
948 + */
949 +
950 + OM_uint32 ret_flags = 0;
951 + gss_buffer_desc gssbuf, recv_tok, msg_tok;
952 + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
953 + Gssctxt *ctxt = NULL;
954 + uint_t slen, klen, kout;
955 + uchar_t *kbuf;
956 + DH *dh;
957 + int min = -1, max = -1, nbits = -1;
958 + BIGNUM *shared_secret = NULL;
959 + BIGNUM *dh_client_pub = NULL;
960 + int type = 0;
961 + gss_OID oid;
962 + char *mechs;
963 + struct kex *kex = ssh->kex;
964 + int r;
965 + uchar_t hash[SSH_DIGEST_MAX_LENGTH];
966 + size_t hashlen;
967 +
968 + /* Initialise GSSAPI */
969 +
970 + /*
971 + * If we're rekeying, privsep means that some of the private structures
972 + * in the GSSAPI code are no longer available. This kludges them back
973 + * into life
974 + */
975 + if (!ssh_gssapi_oid_table_ok())
976 + if ((mechs = ssh_gssapi_server_mechanisms()))
977 + free(mechs);
978 +
979 + debug2("%s: Identifying %s", __func__, kex->name);
980 + oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
981 + if (oid == GSS_C_NO_OID)
982 + fatal("Unknown gssapi mechanism");
983 +
984 + debug2("%s: Acquiring credentials", __func__);
985 +
986 + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
987 + fatal("Unable to acquire credentials for the server");
988 +
989 + switch (kex->kex_type) {
990 + case KEX_GSS_GRP1_SHA1:
991 + kex->dh = dh_new_group1();
992 + break;
993 + case KEX_GSS_GRP14_SHA1:
994 + kex->dh = dh_new_group14();
995 + break;
996 + case KEX_GSS_GEX_SHA1:
997 + debug("Doing group exchange");
998 + packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
999 + min = packet_get_int();
1000 + nbits = packet_get_int();
1001 + max = packet_get_int();
1002 + min = MAX(DH_GRP_MIN, min);
1003 + max = MIN(DH_GRP_MAX, max);
1004 + packet_check_eom();
1005 + if (max < min || nbits < min || max < nbits)
1006 + fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
1007 + min, nbits, max);
1008 + kex->dh = PRIVSEP(choose_dh(min, nbits, max));
1009 + if (kex->dh == NULL)
1010 + packet_disconnect("Protocol error:"
1011 + " no matching group found");
1012 +
1013 + packet_start(SSH2_MSG_KEXGSS_GROUP);
1014 + packet_put_bignum2(kex->dh->p);
1015 + packet_put_bignum2(kex->dh->g);
1016 + packet_send();
1017 +
1018 + packet_write_wait();
1019 + break;
1020 + default:
1021 + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1022 + }
1023 +
1024 + dh_gen_key(kex->dh, kex->we_need * 8);
1025 +
1026 + do {
1027 + debug("Wait SSH2_MSG_GSSAPI_INIT");
1028 + type = packet_read();
1029 + switch (type) {
1030 + case SSH2_MSG_KEXGSS_INIT:
1031 + if (dh_client_pub != NULL)
1032 + fatal("Received KEXGSS_INIT after"
1033 + " initialising");
1034 + recv_tok.value = packet_get_string(&slen);
1035 + recv_tok.length = slen;
1036 +
1037 + if ((dh_client_pub = BN_new()) == NULL)
1038 + fatal("dh_client_pub == NULL");
1039 +
1040 + packet_get_bignum2(dh_client_pub);
1041 +
1042 + /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
1043 + break;
1044 + case SSH2_MSG_KEXGSS_CONTINUE:
1045 + recv_tok.value = packet_get_string(&slen);
1046 + recv_tok.length = slen;
1047 + break;
1048 + default:
1049 + packet_disconnect(
1050 + "Protocol error: didn't expect packet type %d",
1051 + type);
1052 + }
1053 +
1054 + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
1055 + &send_tok, &ret_flags));
1056 +
1057 + free(recv_tok.value);
1058 +
1059 + if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
1060 + fatal("Zero length token output when incomplete");
1061 +
1062 + if (dh_client_pub == NULL)
1063 + fatal("No client public key");
1064 +
1065 + if (maj_status & GSS_S_CONTINUE_NEEDED) {
1066 + debug("Sending GSSAPI_CONTINUE");
1067 + packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1068 + packet_put_string(send_tok.value, send_tok.length);
1069 + packet_send();
1070 + gss_release_buffer(&min_status, &send_tok);
1071 + }
1072 + } while (maj_status & GSS_S_CONTINUE_NEEDED);
1073 +
1074 + if (GSS_ERROR(maj_status)) {
1075 + if (send_tok.length > 0) {
1076 + packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1077 + packet_put_string(send_tok.value, send_tok.length);
1078 + packet_send();
1079 + }
1080 + fatal("accept_ctx died");
1081 + }
1082 +
1083 + if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1084 + fatal("Mutual Authentication flag wasn't set");
1085 +
1086 + if (!(ret_flags & GSS_C_INTEG_FLAG))
1087 + fatal("Integrity flag wasn't set");
1088 +
1089 + if (!dh_pub_is_valid(kex->dh, dh_client_pub))
1090 + packet_disconnect("bad client public DH value");
1091 +
1092 + klen = DH_size(kex->dh);
1093 + kbuf = xmalloc(klen);
1094 + kout = DH_compute_key(kbuf, dh_client_pub, kex->dh);
1095 + if (kout < 0)
1096 + fatal("DH_compute_key: failed");
1097 +
1098 + shared_secret = BN_new();
1099 + if (shared_secret == NULL)
1100 + fatal("kexgss_server: BN_new failed");
1101 +
1102 + if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
1103 + fatal("kexgss_server: BN_bin2bn failed");
1104 +
1105 + memset(kbuf, 0, klen);
1106 + free(kbuf);
1107 +
1108 + hashlen = sizeof (hash);
1109 + switch (kex->kex_type) {
1110 + case KEX_GSS_GRP1_SHA1:
1111 + case KEX_GSS_GRP14_SHA1:
1112 + kex_dh_hash(
1113 + kex->client_version_string, kex->server_version_string,
1114 + buffer_ptr(kex->peer), buffer_len(kex->peer),
1115 + buffer_ptr(kex->my), buffer_len(kex->my),
1116 + NULL, 0, /* Change this if we start sending host keys */
1117 + dh_client_pub, kex->dh->pub_key, shared_secret,
1118 + hash, &hashlen);
1119 + break;
1120 + case KEX_GSS_GEX_SHA1:
1121 + kexgex_hash(
1122 + kex->hash_alg,
1123 + kex->client_version_string, kex->server_version_string,
1124 + buffer_ptr(kex->peer), buffer_len(kex->peer),
1125 + buffer_ptr(kex->my), buffer_len(kex->my),
1126 + NULL, 0,
1127 + min, nbits, max,
1128 + kex->dh->p, kex->dh->g,
1129 + dh_client_pub,
1130 + kex->dh->pub_key,
1131 + shared_secret,
1132 + hash, &hashlen);
1133 + break;
1134 + default:
1135 + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1136 + }
1137 +
1138 + BN_clear_free(dh_client_pub);
1139 +
1140 + if (kex->session_id == NULL) {
1141 + kex->session_id_len = hashlen;
1142 + kex->session_id = xmalloc(kex->session_id_len);
1143 + memcpy(kex->session_id, hash, kex->session_id_len);
1144 + }
1145 +
1146 + gssbuf.value = hash;
1147 + gssbuf.length = hashlen;
1148 +
1149 + if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
1150 + fatal("Couldn't get MIC");
1151 +
1152 + packet_start(SSH2_MSG_KEXGSS_COMPLETE);
1153 + packet_put_bignum2(kex->dh->pub_key);
1154 + packet_put_string(msg_tok.value, msg_tok.length);
1155 +
1156 + if (send_tok.length != 0) {
1157 + packet_put_char(1); /* true */
1158 + packet_put_string(send_tok.value, send_tok.length);
1159 + } else {
1160 + packet_put_char(0); /* false */
1161 + }
1162 + packet_send();
1163 +
1164 + gss_release_buffer(&min_status, &send_tok);
1165 + gss_release_buffer(&min_status, &msg_tok);
1166 +
1167 + if (gss_kex_context == NULL)
1168 + gss_kex_context = ctxt;
1169 + else
1170 + ssh_gssapi_delete_ctx(&ctxt);
1171 +
1172 + DH_free(kex->dh);
1173 +
1174 + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
1175 + r = kex_send_newkeys(ssh);
1176 + return (r);
1177 +}
1178 +#endif /* GSSAPI */
1179 diff --git a/monitor.c b/monitor.c
1180 index b3efbb0..7ac4c61 100644
1181 --- a/monitor.c
1182 +++ b/monitor.c
1183 @@ -160,6 +160,7 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
1184 int mm_answer_gss_accept_ctx(int, Buffer *);
1185 int mm_answer_gss_userok(int, Buffer *);
1186 int mm_answer_gss_checkmic(int, Buffer *);
1187 +int mm_answer_gss_sign(int, Buffer *);
1188 #endif
1189
1190 #ifdef SSH_AUDIT_EVENTS
1191 @@ -244,11 +245,17 @@ struct mon_table mon_dispatch_proto20[] = {
1192 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
1193 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
1194 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
1195 + {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
1196 #endif
1197 {0, 0, NULL}
1198 };
1199
1200 struct mon_table mon_dispatch_postauth20[] = {
1201 +#ifdef GSSAPI
1202 + {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
1203 + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
1204 + {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
1205 +#endif
1206 #ifdef WITH_OPENSSL
1207 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
1208 #endif
1209 @@ -363,6 +370,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
1210 /* Permit requests for moduli and signatures */
1211 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
1212 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
1213 +#ifdef GSSAPI
1214 + /* and for the GSSAPI key exchange */
1215 + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
1216 +#endif
1217 } else {
1218 mon_dispatch = mon_dispatch_proto15;
1219
1220 @@ -502,6 +513,10 @@ monitor_child_postauth(struct monitor *pmonitor)
1221 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
1222 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
1223 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
1224 +#ifdef GSSAPI
1225 + /* and for the GSSAPI key exchange */
1226 + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
1227 +#endif
1228 } else {
1229 mon_dispatch = mon_dispatch_postauth15;
1230 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
1231 @@ -1927,6 +1942,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1232 # endif
1233 #endif /* WITH_OPENSSL */
1234 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1235 +#ifdef GSSAPI
1236 + if (options.gss_keyex) {
1237 + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1238 + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1239 + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1240 + }
1241 +#endif
1242 kex->load_host_public_key=&get_hostkey_public_by_type;
1243 kex->load_host_private_key=&get_hostkey_private_by_type;
1244 kex->host_key_index=&get_hostkey_index;
1245 @@ -2026,6 +2048,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
1246 OM_uint32 major;
1247 u_int len;
1248
1249 + if (!options.gss_authentication && !options.gss_keyex)
1250 + fatal("In GSSAPI monitor when GSSAPI is disabled");
1251 +
1252 goid.elements = buffer_get_string(m, &len);
1253 goid.length = len;
1254
1255 @@ -2053,6 +2078,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1256 OM_uint32 flags = 0; /* GSI needs this */
1257 u_int len;
1258
1259 + if (!options.gss_authentication && !options.gss_keyex)
1260 + fatal("In GSSAPI monitor when GSSAPI is disabled");
1261 +
1262 in.value = buffer_get_string(m, &len);
1263 in.length = len;
1264 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
1265 @@ -2070,6 +2098,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1266 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1267 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1268 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1269 + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1270 }
1271 return (0);
1272 }
1273 @@ -2081,6 +2110,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
1274 OM_uint32 ret;
1275 u_int len;
1276
1277 + if (!options.gss_authentication && !options.gss_keyex)
1278 + fatal("In GSSAPI monitor when GSSAPI is disabled");
1279 +
1280 gssbuf.value = buffer_get_string(m, &len);
1281 gssbuf.length = len;
1282 mic.value = buffer_get_string(m, &len);
1283 @@ -2107,6 +2139,9 @@ mm_answer_gss_userok(int sock, Buffer *m)
1284 {
1285 int authenticated;
1286
1287 + if (!options.gss_authentication && !options.gss_keyex)
1288 + fatal("In GSSAPI monitor when GSSAPI is disabled");
1289 +
1290 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
1291
1292 buffer_clear(m);
1293 @@ -2120,5 +2155,47 @@ mm_answer_gss_userok(int sock, Buffer *m)
1294 /* Monitor loop will terminate if authenticated */
1295 return (authenticated);
1296 }
1297 +
1298 +int
1299 +mm_answer_gss_sign(int socket, Buffer *m)
1300 +{
1301 + gss_buffer_desc data;
1302 + gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1303 + OM_uint32 major, minor;
1304 + u_int len;
1305 +
1306 + if (!options.gss_authentication && !options.gss_keyex)
1307 + fatal("In GSSAPI monitor when GSSAPI is disabled");
1308 +
1309 + data.value = buffer_get_string(m, &len);
1310 + data.length = len;
1311 + if (data.length != 20)
1312 + fatal("%s: data length incorrect: %d", __func__,
1313 + (int) data.length);
1314 +
1315 + /* Save the session ID on the first time around */
1316 + if (session_id2_len == 0) {
1317 + session_id2_len = data.length;
1318 + session_id2 = xmalloc(session_id2_len);
1319 + memcpy(session_id2, data.value, session_id2_len);
1320 + }
1321 + major = ssh_gssapi_sign(gsscontext, &data, &hash);
1322 +
1323 + free(data.value);
1324 +
1325 + buffer_clear(m);
1326 + buffer_put_int(m, major);
1327 + buffer_put_string(m, hash.value, hash.length);
1328 +
1329 + mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
1330 +
1331 + gss_release_buffer(&minor, &hash);
1332 +
1333 + /* Turn on getpwnam permissions */
1334 + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
1335 +
1336 + return (0);
1337 +}
1338 +
1339 #endif /* GSSAPI */
1340
1341 diff --git a/monitor.h b/monitor.h
1342 index da63e7d..1be1545 100644
1343 --- a/monitor.h
1344 +++ b/monitor.h
1345 @@ -68,6 +68,9 @@ enum monitor_reqtype {
1346 #ifdef PAM_ENHANCEMENT
1347 MONITOR_REQ_AUTHMETHOD = 114,
1348 #endif
1349 +#ifdef GSSAPI
1350 + MONITOR_REQ_GSSSIGN = 130, MONITOR_ANS_GSSSIGN = 131,
1351 +#endif
1352 };
1353
1354 struct mm_master;
1355 diff --git a/monitor_wrap.c b/monitor_wrap.c
1356 index 95231a3..2b9ba06 100644
1357 --- a/monitor_wrap.c
1358 +++ b/monitor_wrap.c
1359 @@ -1103,5 +1103,28 @@ mm_ssh_gssapi_userok(char *user)
1360 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1361 return (authenticated);
1362 }
1363 +
1364 +OM_uint32
1365 +mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1366 +{
1367 + Buffer m;
1368 + OM_uint32 major;
1369 + u_int len;
1370 +
1371 + buffer_init(&m);
1372 + buffer_put_string(&m, data->value, data->length);
1373 +
1374 + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1375 + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1376 +
1377 + major = buffer_get_int(&m);
1378 + hash->value = buffer_get_string(&m, &len);
1379 + hash->length = len;
1380 +
1381 + buffer_free(&m);
1382 +
1383 + return(major);
1384 +}
1385 +
1386 #endif /* GSSAPI */
1387
1388 diff --git a/monitor_wrap.h b/monitor_wrap.h
1389 index de4a08f..5c9b2b7 100644
1390 --- a/monitor_wrap.h
1391 +++ b/monitor_wrap.h
1392 @@ -60,6 +60,7 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
1393 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
1394 int mm_ssh_gssapi_userok(char *user);
1395 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
1396 +OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
1397 #endif
1398
1399 #ifdef USE_PAM
1400 diff --git a/readconf.c b/readconf.c
1401 index db37809..50254b6 100644
1402 --- a/readconf.c
1403 +++ b/readconf.c
1404 @@ -147,6 +147,7 @@ typedef enum {
1405 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
1406 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
1407 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
1408 + oGssKeyEx,
1409 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
1410 oSendEnv, oControlPath, oControlMaster, oControlPersist,
1411 oHashKnownHosts,
1412 @@ -196,9 +197,11 @@ static struct {
1413 #if defined(GSSAPI)
1414 { "gssapiauthentication", oGssAuthentication },
1415 { "gssapidelegatecredentials", oGssDelegateCreds },
1416 + { "gssapikeyexchange", oGssKeyEx },
1417 #else
1418 { "gssapiauthentication", oUnsupported },
1419 { "gssapidelegatecredentials", oUnsupported },
1420 + { "gssapikeyexchange", oUnsupported },
1421 #endif
1422 { "fallbacktorsh", oDeprecated },
1423 { "usersh", oDeprecated },
1424 @@ -929,6 +932,10 @@ parse_time:
1425 intptr = &options->gss_authentication;
1426 goto parse_flag;
1427
1428 + case oGssKeyEx:
1429 + intptr = &options->gss_keyex;
1430 + goto parse_flag;
1431 +
1432 case oGssDelegateCreds:
1433 intptr = &options->gss_deleg_creds;
1434 goto parse_flag;
1435 @@ -1643,6 +1650,7 @@ initialize_options(Options * options)
1436 options->pubkey_authentication = -1;
1437 options->challenge_response_authentication = -1;
1438 options->gss_authentication = -1;
1439 + options->gss_keyex = -1;
1440 options->gss_deleg_creds = -1;
1441 options->password_authentication = -1;
1442 options->kbd_interactive_authentication = -1;
1443 @@ -1782,6 +1790,12 @@ fill_default_options(Options * options)
1444 #else
1445 options->gss_authentication = 0;
1446 #endif
1447 + if (options->gss_keyex == -1)
1448 +#ifdef OPTION_DEFAULT_VALUE
1449 + options->gss_keyex = 1;
1450 +#else
1451 + options->gss_keyex = 0;
1452 +#endif
1453 if (options->gss_deleg_creds == -1)
1454 options->gss_deleg_creds = 0;
1455 if (options->password_authentication == -1)
1456 diff --git a/readconf.h b/readconf.h
1457 index b961309..10ba93a 100644
1458 --- a/readconf.h
1459 +++ b/readconf.h
1460 @@ -45,6 +45,7 @@ typedef struct {
1461 int challenge_response_authentication;
1462 /* Try S/Key or TIS, authentication. */
1463 int gss_authentication; /* Try GSS authentication */
1464 + int gss_keyex; /* Try GSS key exchange */
1465 int gss_deleg_creds; /* Delegate GSS credentials */
1466 int password_authentication; /* Try password
1467 * authentication. */
1468 diff --git a/servconf.c b/servconf.c
1469 index 1a68479..53146af 100644
1470 --- a/servconf.c
1471 +++ b/servconf.c
1472 @@ -117,6 +117,7 @@ initialize_server_options(ServerOptions *options)
1473 options->kerberos_ticket_cleanup = -1;
1474 options->kerberos_get_afs_token = -1;
1475 options->gss_authentication=-1;
1476 + options->gss_keyex = -1;
1477 options->gss_cleanup_creds = -1;
1478 options->gss_strict_acceptor = -1;
1479 options->password_authentication = -1;
1480 @@ -300,6 +301,12 @@ fill_default_server_options(ServerOptions *options)
1481 #else
1482 options->gss_authentication = 0;
1483 #endif
1484 + if (options->gss_keyex == -1)
1485 +#ifdef OPTION_DEFAULT_VALUE
1486 + options->gss_keyex = 1;
1487 +#else
1488 + options->gss_keyex = 0;
1489 +#endif
1490 if (options->gss_cleanup_creds == -1)
1491 options->gss_cleanup_creds = 1;
1492 if (options->gss_strict_acceptor == -1)
1493 @@ -442,6 +449,7 @@ typedef enum {
1494 sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes,
1495 sHostKeyAlgorithms,
1496 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
1497 + sGssKeyEx,
1498 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
1499 sAcceptEnv, sPermitTunnel,
1500 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
1501 @@ -518,6 +526,7 @@ static struct {
1502 { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL },
1503 #ifdef GSSAPI
1504 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
1505 + { "gssapikeyexchange", sGssKeyEx, SSHCFG_ALL },
1506 #ifdef USE_GSS_STORE_CRED
1507 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
1508 #else /* USE_GSS_STORE_CRED */
1509 @@ -526,6 +535,7 @@ static struct {
1510 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
1511 #else
1512 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
1513 + { "gssapikeyexchange", sUnsupported, SSHCFG_ALL },
1514 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
1515 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
1516 #endif
1517 @@ -1309,6 +1319,10 @@ process_server_config_line(ServerOptions *options, char *line,
1518 intptr = &options->gss_authentication;
1519 goto parse_flag;
1520
1521 + case sGssKeyEx:
1522 + intptr = &options->gss_keyex;
1523 + goto parse_flag;
1524 +
1525 case sGssCleanupCreds:
1526 intptr = &options->gss_cleanup_creds;
1527 goto parse_flag;
1528 @@ -2355,6 +2369,7 @@ dump_config(ServerOptions *o)
1529 #endif
1530 #ifdef GSSAPI
1531 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
1532 + dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
1533 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
1534 #endif
1535 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
1536 diff --git a/servconf.h b/servconf.h
1537 index 8c86b57..2175645 100644
1538 --- a/servconf.h
1539 +++ b/servconf.h
1540 @@ -122,6 +122,7 @@ typedef struct {
1541 int kerberos_get_afs_token; /* If true, try to get AFS token if
1542 * authenticated with Kerberos. */
1543 int gss_authentication; /* If true, permit GSSAPI authentication */
1544 + int gss_keyex; /* If true, permit GSSAPI key exchange */
1545 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
1546 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
1547 int password_authentication; /* If true, permit password
1548 diff --git a/ssh-gss.h b/ssh-gss.h
1549 index a99d7f0..43c33f9 100644
1550 --- a/ssh-gss.h
1551 +++ b/ssh-gss.h
1552 @@ -61,6 +61,17 @@
1553
1554 #define SSH_GSS_OIDTYPE 0x06
1555
1556 +#define SSH2_MSG_KEXGSS_INIT 30
1557 +#define SSH2_MSG_KEXGSS_CONTINUE 31
1558 +#define SSH2_MSG_KEXGSS_COMPLETE 32
1559 +#define SSH2_MSG_KEXGSS_HOSTKEY 33
1560 +#define SSH2_MSG_KEXGSS_ERROR 34
1561 +#define SSH2_MSG_KEXGSS_GROUPREQ 40
1562 +#define SSH2_MSG_KEXGSS_GROUP 41
1563 +#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
1564 +#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
1565 +#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
1566 +
1567 typedef struct {
1568 char *filename;
1569 char *envvar;
1570 @@ -98,6 +109,7 @@ typedef struct {
1571 } Gssctxt;
1572
1573 extern ssh_gssapi_mech *supported_mechs[];
1574 +extern Gssctxt *gss_kex_context;
1575
1576 int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
1577 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
1578 @@ -122,6 +134,11 @@ void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
1579 int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
1580
1581 /* In the server */
1582 +typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *);
1583 +char *ssh_gssapi_client_mechanisms(const char *host);
1584 +char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *);
1585 +gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
1586 +int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *);
1587 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
1588 int ssh_gssapi_userok(char *name);
1589 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
1590 @@ -129,6 +146,8 @@ void ssh_gssapi_do_child(char ***, u_int *);
1591 void ssh_gssapi_cleanup_creds(void);
1592 void ssh_gssapi_storecreds(void);
1593
1594 +char *ssh_gssapi_server_mechanisms(void);
1595 +int ssh_gssapi_oid_table_ok();
1596 #endif /* GSSAPI */
1597
1598 #endif /* _SSH_GSS_H */
1599 diff --git a/ssh_config b/ssh_config
1600 index 03a228f..ff42df6 100644
1601 --- a/ssh_config
1602 +++ b/ssh_config
1603 @@ -26,6 +26,7 @@
1604 # HostbasedAuthentication no
1605 # GSSAPIAuthentication no
1606 # GSSAPIDelegateCredentials no
1607 +# GSSAPIKeyExchange yes
1608 # BatchMode no
1609 # CheckHostIP yes
1610 # AddressFamily any
1611 diff --git a/ssh_config.4 b/ssh_config.4
1612 index 720451e..016adda 100644
1613 --- a/ssh_config.4
1614 +++ b/ssh_config.4
1615 @@ -757,6 +757,12 @@ Specifies whether user authentication based on GSSAPI is allowed.
1616 The default on Solaris is
1617 .Dq yes .
1618 Note that this option applies to protocol version 2 only.
1619 +.It Cm GSSAPIKeyExchange
1620 +Specifies whether key exchange based on GSSAPI may be used. When using
1621 +GSSAPI key exchange the server need not have a host key.
1622 +The default on Solaris is
1623 +.Dq yes .
1624 +Note that this option applies to protocol version 2 only.
1625 .It Cm GSSAPIDelegateCredentials
1626 Forward (delegate) credentials to the server.
1627 The default is
1628 diff --git a/sshconnect2.c b/sshconnect2.c
1629 index 95593b9..54ddccb 100644
1630 --- a/sshconnect2.c
1631 +++ b/sshconnect2.c
1632 @@ -164,11 +164,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
1633 struct kex *kex;
1634 int r;
1635
1636 +#ifdef GSSAPI
1637 + char *orig = NULL, *gss = NULL;
1638 + char *gss_host = NULL;
1639 +#endif
1640 +
1641 xxx_host = host;
1642 xxx_hostaddr = hostaddr;
1643
1644 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
1645 options.kex_algorithms);
1646 +
1647 +#ifdef GSSAPI
1648 + if (options.gss_keyex) {
1649 + /* Add the GSSAPI mechanisms currently supported on this
1650 + * client to the key exchange algorithm proposal */
1651 + orig = myproposal[PROPOSAL_KEX_ALGS];
1652 +
1653 + gss_host = (char *)get_canonical_hostname(1);
1654 +
1655 + gss = ssh_gssapi_client_mechanisms(gss_host);
1656 + if (gss) {
1657 + debug("Offering GSSAPI proposal: %s", gss);
1658 + xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
1659 + "%s,%s", gss, orig);
1660 + }
1661 + }
1662 +#endif
1663 +
1664 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
1665 compat_cipher_proposal(options.ciphers);
1666 myproposal[PROPOSAL_ENC_ALGS_STOC] =
1667 @@ -197,6 +220,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
1668 order_hostkeyalgs(host, hostaddr, port));
1669 }
1670
1671 +#ifdef GSSAPI
1672 + /* If we've got GSSAPI algorithms, then we also support the
1673 + * 'null' hostkey, as a last resort */
1674 + if (options.gss_keyex && gss) {
1675 + orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
1676 + xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
1677 + "%s,null", orig);
1678 + free(gss);
1679 + }
1680 +#endif
1681 +
1682 if (options.rekey_limit || options.rekey_interval)
1683 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
1684 (time_t)options.rekey_interval);
1685 @@ -215,9 +249,22 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
1686 # endif
1687 #endif
1688 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
1689 +#ifdef GSSAPI
1690 + if (options.gss_keyex) {
1691 + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
1692 + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
1693 + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
1694 + }
1695 +#endif
1696 kex->client_version_string=client_version_string;
1697 kex->server_version_string=server_version_string;
1698 kex->verify_host_key=&verify_host_key_callback;
1699 +#ifdef GSSAPI
1700 + if (options.gss_keyex) {
1701 + kex->gss_deleg_creds = options.gss_deleg_creds;
1702 + kex->gss_host = gss_host;
1703 + }
1704 +#endif
1705
1706 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
1707
1708 @@ -310,6 +357,7 @@ int input_gssapi_token(int type, u_int32_t, void *);
1709 int input_gssapi_hash(int type, u_int32_t, void *);
1710 int input_gssapi_error(int, u_int32_t, void *);
1711 int input_gssapi_errtok(int, u_int32_t, void *);
1712 +int userauth_gsskeyex(Authctxt *authctxt);
1713 #endif
1714
1715 void userauth(Authctxt *, char *);
1716 @@ -325,6 +373,11 @@ static char *authmethods_get(void);
1717
1718 Authmethod authmethods[] = {
1719 #ifdef GSSAPI
1720 + {"gssapi-keyex",
1721 + userauth_gsskeyex,
1722 + NULL,
1723 + &options.gss_authentication,
1724 + NULL},
1725 {"gssapi-with-mic",
1726 userauth_gssapi,
1727 NULL,
1728 @@ -649,7 +702,10 @@ userauth_gssapi(Authctxt *authctxt)
1729 * once. */
1730
1731 if (gss_supported == NULL)
1732 - gss_indicate_mechs(&min, &gss_supported);
1733 + if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
1734 + gss_supported = NULL;
1735 + return 0;
1736 + }
1737
1738 /* Check to see if the mechanism is usable before we offer it */
1739 while (mech < gss_supported->count && !ok) {
1740 @@ -753,8 +809,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
1741 {
1742 Authctxt *authctxt = ctxt;
1743 Gssctxt *gssctxt;
1744 - int oidlen;
1745 - char *oidv;
1746 + u_int oidlen;
1747 + u_char *oidv;
1748
1749 if (authctxt == NULL)
1750 fatal("input_gssapi_response: no authentication context");
1751 @@ -867,6 +923,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
1752 free(lang);
1753 return 0;
1754 }
1755 +
1756 +int
1757 +userauth_gsskeyex(Authctxt *authctxt)
1758 +{
1759 + Buffer b;
1760 + gss_buffer_desc gssbuf;
1761 + gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1762 + OM_uint32 ms;
1763 +
1764 + static int attempt = 0;
1765 + if (attempt++ >= 1)
1766 + return (0);
1767 +
1768 + if (gss_kex_context == NULL) {
1769 + debug("No valid Key exchange context");
1770 + return (0);
1771 + }
1772 +
1773 + ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
1774 + "gssapi-keyex");
1775 +
1776 + gssbuf.value = buffer_ptr(&b);
1777 + gssbuf.length = buffer_len(&b);
1778 +
1779 + if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1780 + buffer_free(&b);
1781 + return (0);
1782 + }
1783 +
1784 + packet_start(SSH2_MSG_USERAUTH_REQUEST);
1785 + packet_put_cstring(authctxt->server_user);
1786 + packet_put_cstring(authctxt->service);
1787 + packet_put_cstring(authctxt->method->name);
1788 + packet_put_string(mic.value, mic.length);
1789 + packet_send();
1790 +
1791 + buffer_free(&b);
1792 + gss_release_buffer(&ms, &mic);
1793 +
1794 + return (1);
1795 +}
1796 +
1797 #endif /* GSSAPI */
1798
1799 int
1800 diff --git a/sshd.c b/sshd.c
1801 index 87032ec..6215b2c 100644
1802 --- a/sshd.c
1803 +++ b/sshd.c
1804 @@ -1833,10 +1833,13 @@ main(int ac, char **av)
1805 logit("Disabling protocol version 1. Could not load host key");
1806 options.protocol &= ~SSH_PROTO_1;
1807 }
1808 +#ifndef GSSAPI
1809 + /* The GSSAPI key exchange can run without a host key */
1810 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1811 logit("Disabling protocol version 2. Could not load host key");
1812 options.protocol &= ~SSH_PROTO_2;
1813 }
1814 +#endif
1815 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1816 logit("sshd: no hostkeys available -- exiting.");
1817 exit(1);
1818 @@ -2594,6 +2597,48 @@ do_ssh2_kex(void)
1819 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
1820 list_hostkey_types());
1821
1822 +#ifdef GSSAPI
1823 + {
1824 + char *orig;
1825 + char *gss = NULL;
1826 + char *newstr = NULL;
1827 + orig = myproposal[PROPOSAL_KEX_ALGS];
1828 +
1829 + /*
1830 + * If we don't have a host key, then there's no point advertising
1831 + * the other key exchange algorithms
1832 + */
1833 +
1834 + if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
1835 + orig = NULL;
1836 +
1837 + if (options.gss_keyex)
1838 + gss = ssh_gssapi_server_mechanisms();
1839 + else
1840 + gss = NULL;
1841 +
1842 + if (gss && orig)
1843 + xasprintf(&newstr, "%s,%s", gss, orig);
1844 + else if (gss)
1845 + newstr = gss;
1846 + else if (orig)
1847 + newstr = orig;
1848 +
1849 + /*
1850 + * If we've got GSSAPI mechanisms, then we've got the 'null' host
1851 + * key alg, but we can't tell people about it unless its the only
1852 + * host key algorithm we support
1853 + */
1854 + if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
1855 + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
1856 +
1857 + if (newstr)
1858 + myproposal[PROPOSAL_KEX_ALGS] = newstr;
1859 + else
1860 + fatal("No supported key exchange algorithms");
1861 + }
1862 +#endif
1863 +
1864 /* start key exchange */
1865 if ((r = kex_setup(active_state, myproposal)) != 0)
1866 fatal("kex_setup: %s", ssh_err(r));
1867 @@ -2608,6 +2653,13 @@ do_ssh2_kex(void)
1868 # endif
1869 #endif
1870 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1871 +#ifdef GSSAPI
1872 + if (options.gss_keyex) {
1873 + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1874 + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1875 + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1876 + }
1877 +#endif
1878 kex->server = 1;
1879 kex->client_version_string=client_version_string;
1880 kex->server_version_string=server_version_string;
1881 diff --git a/sshd_config b/sshd_config
1882 index 4d77f05..611656b 100644
1883 --- a/sshd_config
1884 +++ b/sshd_config
1885 @@ -82,8 +82,9 @@ AuthorizedKeysFile .ssh/authorized_keys
1886 #KerberosGetAFSToken no
1887
1888 # GSSAPI options
1889 -#GSSAPIAuthentication no
1890 +#GSSAPIAuthentication yes
1891 #GSSAPICleanupCredentials yes
1892 +#GSSAPIKeyExchange yes
1893
1894 # Set this to 'yes' to enable PAM authentication, account processing,
1895 # and session processing. If this is enabled, PAM authentication will
1896 diff --git a/sshd_config.4 b/sshd_config.4
1897 index 03c5b52..bd70a68 100644
1898 --- a/sshd_config.4
1899 +++ b/sshd_config.4
1900 @@ -621,6 +621,12 @@ Specifies whether user authentication based on GSSAPI is allowed.
1901 The default on Solaris is
1902 .Dq yes .
1903 Note that this option applies to protocol version 2 only.
1904 +.It Cm GSSAPIKeyExchange
1905 +Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
1906 +doesn't rely on ssh keys to verify host identity.
1907 +The default on Solaris is
1908 +.Dq yes .
1909 +Note that this option applies to protocol version 2 only.
1910 .It Cm GSSAPICleanupCredentials
1911 Specifies whether to automatically destroy the user's credentials cache
1912 on logout.
1913 diff --git a/sshkey.c b/sshkey.c
1914 index 32dd8f2..afe5579 100644
1915 --- a/sshkey.c
1916 +++ b/sshkey.c
1917 @@ -112,6 +112,7 @@ static const struct keytype keytypes[] = {
1918 # endif /* OPENSSL_HAS_NISTP521 */
1919 # endif /* OPENSSL_HAS_ECC */
1920 #endif /* WITH_OPENSSL */
1921 + { "null", "null", KEY_NULL, 0, 0 },
1922 { NULL, NULL, -1, -1, 0 }
1923 };
1924
1925 diff --git a/sshkey.h b/sshkey.h
1926 index c8d3cdd..47bd017 100644
1927 --- a/sshkey.h
1928 +++ b/sshkey.h
1929 @@ -62,6 +62,9 @@ enum sshkey_types {
1930 KEY_DSA_CERT,
1931 KEY_ECDSA_CERT,
1932 KEY_ED25519_CERT,
1933 + KEY_RSA_CERT_V00,
1934 + KEY_DSA_CERT_V00,
1935 + KEY_NULL,
1936 KEY_UNSPEC
1937 };
1938
1939 --
1940 2.5.4 (Apple Git-61)
1941