summaryrefslogtreecommitdiff
path: root/gss-genr.c
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2019-06-05 07:06:44 +0100
commit7ce79be85036c4b36937f1b1ba85f6094068412c (patch)
treec964917d8395ef5605cff9513aad4458b222beae /gss-genr.c
parent102062f825fb26a74295a1c089c00c4c4c76b68a (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2019-06-05 Patch-Name: gssapi.patch
Diffstat (limited to 'gss-genr.c')
-rw-r--r--gss-genr.c300
1 files changed, 296 insertions, 4 deletions
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 */