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