diff options
Diffstat (limited to 'gss-genr.c')
-rw-r--r-- | gss-genr.c | 280 |
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 | ||
47 | extern u_char *session_id2; | 51 | extern u_char *session_id2; |
48 | extern u_int session_id2_len; | 52 | extern u_int session_id2_len; |
49 | 53 | ||
54 | typedef 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 | |||
64 | Gssctxt *gss_kex_context = NULL; | ||
65 | |||
66 | static ssh_gss_kex_mapping *gss_enc2oid = NULL; | ||
67 | |||
68 | int | ||
69 | ssh_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 */ |
51 | int | 74 | int |
52 | ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) | 75 | ssh_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 | |||
95 | char * | ||
96 | ssh_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 | |||
107 | char * | ||
108 | ssh_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 | |||
191 | gss_OID | ||
192 | ssh_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 */ |
66 | int | 226 | int |
67 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | 227 | ssh_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 | ||
250 | OM_uint32 | 410 | OM_uint32 |
411 | ssh_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 | |||
441 | OM_uint32 | ||
251 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | 442 | ssh_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 */ | ||
455 | OM_uint32 | ||
456 | ssh_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 | |||
260 | void | 467 | void |
261 | ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, | 468 | ssh_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 | ||
275 | int | 482 | int |
276 | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | 483 | ssh_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 | ||
521 | int | ||
522 | ssh_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 */ |