diff options
Diffstat (limited to 'gss-genr.c')
-rw-r--r-- | gss-genr.c | 277 |
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 | ||
47 | extern u_char *session_id2; | 50 | extern u_char *session_id2; |
48 | extern u_int session_id2_len; | 51 | extern u_int session_id2_len; |
49 | 52 | ||
53 | typedef 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 | |||
63 | Gssctxt *gss_kex_context = NULL; | ||
64 | |||
65 | static ssh_gss_kex_mapping *gss_enc2oid = NULL; | ||
66 | |||
67 | int | ||
68 | ssh_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 */ |
51 | int | 73 | int |
52 | ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) | 74 | ssh_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 | |||
94 | char * | ||
95 | ssh_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 | |||
106 | char * | ||
107 | ssh_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 | |||
188 | gss_OID | ||
189 | ssh_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 */ |
66 | int | 223 | int |
67 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | 224 | ssh_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 | ||
250 | OM_uint32 | 407 | OM_uint32 |
408 | ssh_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 | |||
438 | OM_uint32 | ||
251 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | 439 | ssh_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 */ | ||
452 | OM_uint32 | ||
453 | ssh_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 | |||
260 | void | 464 | void |
261 | ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, | 465 | ssh_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 | ||
275 | int | 479 | int |
276 | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | 480 | ssh_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 | ||
518 | int | ||
519 | ssh_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 */ |