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>2018-10-20 22:54:00 +0100
commit72b1d308e6400194ef6e4e7dd45bfa48fa39b5e6 (patch)
tree2a3b57ae5446f4273804064ccc42659adfc2a3b2 /gss-genr.c
parent3d246f10429fc9a37b98eabef94fe8dc7c61002b (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. Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2018-10-20 Patch-Name: gssapi.patch
Diffstat (limited to 'gss-genr.c')
-rw-r--r--gss-genr.c280
1 files changed, 276 insertions, 4 deletions
diff --git a/gss-genr.c b/gss-genr.c
index d56257b4a..491e62cee 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
@@ -39,14 +39,37 @@
39#include "xmalloc.h" 39#include "xmalloc.h"
40#include "ssherr.h" 40#include "ssherr.h"
41#include "sshbuf.h" 41#include "sshbuf.h"
42#include "sshkey.h"
42#include "log.h" 43#include "log.h"
43#include "ssh2.h" 44#include "ssh2.h"
45#include "cipher.h"
46#include "kex.h"
47#include "digest.h"
44 48
45#include "ssh-gss.h" 49#include "ssh-gss.h"
46 50
47extern u_char *session_id2; 51extern u_char *session_id2;
48extern u_int session_id2_len; 52extern u_int session_id2_len;
49 53
54typedef struct {
55 char *encoded;
56 gss_OID oid;
57} ssh_gss_kex_mapping;
58
59/*
60 * XXX - It would be nice to find a more elegant way of handling the
61 * XXX passing of the key exchange context to the userauth routines
62 */
63
64Gssctxt *gss_kex_context = NULL;
65
66static ssh_gss_kex_mapping *gss_enc2oid = NULL;
67
68int
69ssh_gssapi_oid_table_ok(void) {
70 return (gss_enc2oid != NULL);
71}
72
50/* sshbuf_get for gss_buffer_desc */ 73/* sshbuf_get for gss_buffer_desc */
51int 74int
52ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) 75ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
@@ -62,6 +85,143 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
62 return 0; 85 return 0;
63} 86}
64 87
88/*
89 * Return a list of the gss-group1-sha1 mechanisms supported by this program
90 *
91 * We test mechanisms to ensure that we can use them, to avoid starting
92 * a key exchange with a bad mechanism
93 */
94
95char *
96ssh_gssapi_client_mechanisms(const char *host, const char *client) {
97 gss_OID_set gss_supported;
98 OM_uint32 min_status;
99
100 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
101 return NULL;
102
103 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
104 host, client));
105}
106
107char *
108ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
109 const char *host, const char *client) {
110 struct sshbuf *buf;
111 size_t i;
112 int r, oidpos, enclen;
113 char *mechs, *encoded;
114 u_char digest[SSH_DIGEST_MAX_LENGTH];
115 char deroid[2];
116 struct ssh_digest_ctx *md;
117
118 if (gss_enc2oid != NULL) {
119 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
120 free(gss_enc2oid[i].encoded);
121 free(gss_enc2oid);
122 }
123
124 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
125 (gss_supported->count + 1));
126
127 if ((buf = sshbuf_new()) == NULL)
128 fatal("%s: sshbuf_new failed", __func__);
129
130 oidpos = 0;
131 for (i = 0; i < gss_supported->count; i++) {
132 if (gss_supported->elements[i].length < 128 &&
133 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
134
135 deroid[0] = SSH_GSS_OIDTYPE;
136 deroid[1] = gss_supported->elements[i].length;
137
138 if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
139 ssh_digest_update(md, deroid, 2) != 0 ||
140 ssh_digest_update(md,
141 gss_supported->elements[i].elements,
142 gss_supported->elements[i].length) != 0 ||
143 ssh_digest_final(md, digest, sizeof(digest)) != 0)
144 fatal("%s: digest failed", __func__);
145
146 encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
147 * 2);
148 enclen = __b64_ntop(digest,
149 ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
150 ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
151
152 if (oidpos != 0) {
153 if ((r = sshbuf_put_u8(buf, ',')) != 0)
154 fatal("%s: buffer error: %s",
155 __func__, ssh_err(r));
156 }
157
158 if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID,
159 sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 ||
160 (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
161 (r = sshbuf_put_u8(buf, ',')) != 0 ||
162 (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID,
163 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 ||
164 (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
165 (r = sshbuf_put_u8(buf, ',')) != 0 ||
166 (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID,
167 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 ||
168 (r = sshbuf_put(buf, encoded, enclen)) != 0)
169 fatal("%s: buffer error: %s",
170 __func__, ssh_err(r));
171
172 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
173 gss_enc2oid[oidpos].encoded = encoded;
174 oidpos++;
175 }
176 }
177 gss_enc2oid[oidpos].oid = NULL;
178 gss_enc2oid[oidpos].encoded = NULL;
179
180 if ((mechs = sshbuf_dup_string(buf)) == NULL)
181 fatal("%s: sshbuf_dup_string failed", __func__);
182
183 if (strlen(mechs) == 0) {
184 free(mechs);
185 mechs = NULL;
186 }
187
188 return (mechs);
189}
190
191gss_OID
192ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
193 int i = 0;
194
195 switch (kex_type) {
196 case KEX_GSS_GRP1_SHA1:
197 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
198 return GSS_C_NO_OID;
199 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
200 break;
201 case KEX_GSS_GRP14_SHA1:
202 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
203 return GSS_C_NO_OID;
204 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
205 break;
206 case KEX_GSS_GEX_SHA1:
207 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
208 return GSS_C_NO_OID;
209 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
210 break;
211 default:
212 return GSS_C_NO_OID;
213 }
214
215 while (gss_enc2oid[i].encoded != NULL &&
216 strcmp(name, gss_enc2oid[i].encoded) != 0)
217 i++;
218
219 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
220 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
221
222 return gss_enc2oid[i].oid;
223}
224
65/* Check that the OID in a data stream matches that in the context */ 225/* Check that the OID in a data stream matches that in the context */
66int 226int
67ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 227ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -218,7 +378,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
218 } 378 }
219 379
220 ctx->major = gss_init_sec_context(&ctx->minor, 380 ctx->major = gss_init_sec_context(&ctx->minor,
221 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 381 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
222 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 382 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
223 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 383 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
224 384
@@ -248,8 +408,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
248} 408}
249 409
250OM_uint32 410OM_uint32
411ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
412{
413 gss_buffer_desc gssbuf;
414 gss_name_t gssname;
415 OM_uint32 status;
416 gss_OID_set oidset;
417
418 gssbuf.value = (void *) name;
419 gssbuf.length = strlen(gssbuf.value);
420
421 gss_create_empty_oid_set(&status, &oidset);
422 gss_add_oid_set_member(&status, ctx->oid, &oidset);
423
424 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
425 GSS_C_NT_USER_NAME, &gssname);
426
427 if (!ctx->major)
428 ctx->major = gss_acquire_cred(&ctx->minor,
429 gssname, 0, oidset, GSS_C_INITIATE,
430 &ctx->client_creds, NULL, NULL);
431
432 gss_release_name(&status, &gssname);
433 gss_release_oid_set(&status, &oidset);
434
435 if (ctx->major)
436 ssh_gssapi_error(ctx);
437
438 return(ctx->major);
439}
440
441OM_uint32
251ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 442ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
252{ 443{
444 if (ctx == NULL)
445 return -1;
446
253 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 447 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
254 GSS_C_QOP_DEFAULT, buffer, hash))) 448 GSS_C_QOP_DEFAULT, buffer, hash)))
255 ssh_gssapi_error(ctx); 449 ssh_gssapi_error(ctx);
@@ -257,6 +451,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
257 return (ctx->major); 451 return (ctx->major);
258} 452}
259 453
454/* Priviledged when used by server */
455OM_uint32
456ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
457{
458 if (ctx == NULL)
459 return -1;
460
461 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
462 gssbuf, gssmic, NULL);
463
464 return (ctx->major);
465}
466
260void 467void
261ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, 468ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
262 const char *context) 469 const char *context)
@@ -273,11 +480,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
273} 480}
274 481
275int 482int
276ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 483ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
484 const char *client)
277{ 485{
278 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 486 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
279 OM_uint32 major, minor; 487 OM_uint32 major, minor;
280 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 488 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
489 Gssctxt *intctx = NULL;
490
491 if (ctx == NULL)
492 ctx = &intctx;
281 493
282 /* RFC 4462 says we MUST NOT do SPNEGO */ 494 /* RFC 4462 says we MUST NOT do SPNEGO */
283 if (oid->length == spnego_oid.length && 495 if (oid->length == spnego_oid.length &&
@@ -287,6 +499,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
287 ssh_gssapi_build_ctx(ctx); 499 ssh_gssapi_build_ctx(ctx);
288 ssh_gssapi_set_oid(*ctx, oid); 500 ssh_gssapi_set_oid(*ctx, oid);
289 major = ssh_gssapi_import_name(*ctx, host); 501 major = ssh_gssapi_import_name(*ctx, host);
502
503 if (!GSS_ERROR(major) && client)
504 major = ssh_gssapi_client_identity(*ctx, client);
505
290 if (!GSS_ERROR(major)) { 506 if (!GSS_ERROR(major)) {
291 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 507 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
292 NULL); 508 NULL);
@@ -296,10 +512,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
296 GSS_C_NO_BUFFER); 512 GSS_C_NO_BUFFER);
297 } 513 }
298 514
299 if (GSS_ERROR(major)) 515 if (GSS_ERROR(major) || intctx != NULL)
300 ssh_gssapi_delete_ctx(ctx); 516 ssh_gssapi_delete_ctx(ctx);
301 517
302 return (!GSS_ERROR(major)); 518 return (!GSS_ERROR(major));
303} 519}
304 520
521int
522ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
523 static gss_name_t saved_name = GSS_C_NO_NAME;
524 static OM_uint32 saved_lifetime = 0;
525 static gss_OID saved_mech = GSS_C_NO_OID;
526 static gss_name_t name;
527 static OM_uint32 last_call = 0;
528 OM_uint32 lifetime, now, major, minor;
529 int equal;
530
531 now = time(NULL);
532
533 if (ctxt) {
534 debug("Rekey has happened - updating saved versions");
535
536 if (saved_name != GSS_C_NO_NAME)
537 gss_release_name(&minor, &saved_name);
538
539 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
540 &saved_name, &saved_lifetime, NULL, NULL);
541
542 if (!GSS_ERROR(major)) {
543 saved_mech = ctxt->oid;
544 saved_lifetime+= now;
545 } else {
546 /* Handle the error */
547 }
548 return 0;
549 }
550
551 if (now - last_call < 10)
552 return 0;
553
554 last_call = now;
555
556 if (saved_mech == GSS_C_NO_OID)
557 return 0;
558
559 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
560 &name, &lifetime, NULL, NULL);
561 if (major == GSS_S_CREDENTIALS_EXPIRED)
562 return 0;
563 else if (GSS_ERROR(major))
564 return 0;
565
566 major = gss_compare_name(&minor, saved_name, name, &equal);
567 gss_release_name(&minor, &name);
568 if (GSS_ERROR(major))
569 return 0;
570
571 if (equal && (saved_lifetime < lifetime + now - 10))
572 return 1;
573
574 return 0;
575}
576
305#endif /* GSSAPI */ 577#endif /* GSSAPI */