diff options
Diffstat (limited to 'gss-genr.c')
-rw-r--r-- | gss-genr.c | 275 |
1 files changed, 271 insertions, 4 deletions
diff --git a/gss-genr.c b/gss-genr.c index d617d600a..b4eca3feb 100644 --- a/gss-genr.c +++ b/gss-genr.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */ | 1 | /* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt 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,167 @@ | |||
41 | #include "buffer.h" | 41 | #include "buffer.h" |
42 | #include "log.h" | 42 | #include "log.h" |
43 | #include "ssh2.h" | 43 | #include "ssh2.h" |
44 | #include "cipher.h" | ||
45 | #include "key.h" | ||
46 | #include "kex.h" | ||
47 | #include <openssl/evp.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 | |||
73 | /* | ||
74 | * Return a list of the gss-group1-sha1 mechanisms supported by this program | ||
75 | * | ||
76 | * We test mechanisms to ensure that we can use them, to avoid starting | ||
77 | * a key exchange with a bad mechanism | ||
78 | */ | ||
79 | |||
80 | char * | ||
81 | ssh_gssapi_client_mechanisms(const char *host, const char *client) { | ||
82 | gss_OID_set gss_supported; | ||
83 | OM_uint32 min_status; | ||
84 | |||
85 | if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) | ||
86 | return NULL; | ||
87 | |||
88 | return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, | ||
89 | host, client)); | ||
90 | } | ||
91 | |||
92 | char * | ||
93 | ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, | ||
94 | const char *host, const char *client) { | ||
95 | Buffer buf; | ||
96 | size_t i; | ||
97 | int oidpos, enclen; | ||
98 | char *mechs, *encoded; | ||
99 | u_char digest[EVP_MAX_MD_SIZE]; | ||
100 | char deroid[2]; | ||
101 | const EVP_MD *evp_md = EVP_md5(); | ||
102 | EVP_MD_CTX md; | ||
103 | |||
104 | if (gss_enc2oid != NULL) { | ||
105 | for (i = 0; gss_enc2oid[i].encoded != NULL; i++) | ||
106 | free(gss_enc2oid[i].encoded); | ||
107 | free(gss_enc2oid); | ||
108 | } | ||
109 | |||
110 | gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * | ||
111 | (gss_supported->count + 1)); | ||
112 | |||
113 | buffer_init(&buf); | ||
114 | |||
115 | oidpos = 0; | ||
116 | for (i = 0; i < gss_supported->count; i++) { | ||
117 | if (gss_supported->elements[i].length < 128 && | ||
118 | (*check)(NULL, &(gss_supported->elements[i]), host, client)) { | ||
119 | |||
120 | deroid[0] = SSH_GSS_OIDTYPE; | ||
121 | deroid[1] = gss_supported->elements[i].length; | ||
122 | |||
123 | EVP_DigestInit(&md, evp_md); | ||
124 | EVP_DigestUpdate(&md, deroid, 2); | ||
125 | EVP_DigestUpdate(&md, | ||
126 | gss_supported->elements[i].elements, | ||
127 | gss_supported->elements[i].length); | ||
128 | EVP_DigestFinal(&md, digest, NULL); | ||
129 | |||
130 | encoded = xmalloc(EVP_MD_size(evp_md) * 2); | ||
131 | enclen = __b64_ntop(digest, EVP_MD_size(evp_md), | ||
132 | encoded, EVP_MD_size(evp_md) * 2); | ||
133 | |||
134 | if (oidpos != 0) | ||
135 | buffer_put_char(&buf, ','); | ||
136 | |||
137 | buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, | ||
138 | sizeof(KEX_GSS_GEX_SHA1_ID) - 1); | ||
139 | buffer_append(&buf, encoded, enclen); | ||
140 | buffer_put_char(&buf, ','); | ||
141 | buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, | ||
142 | sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); | ||
143 | buffer_append(&buf, encoded, enclen); | ||
144 | buffer_put_char(&buf, ','); | ||
145 | buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, | ||
146 | sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); | ||
147 | buffer_append(&buf, encoded, enclen); | ||
148 | |||
149 | gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); | ||
150 | gss_enc2oid[oidpos].encoded = encoded; | ||
151 | oidpos++; | ||
152 | } | ||
153 | } | ||
154 | gss_enc2oid[oidpos].oid = NULL; | ||
155 | gss_enc2oid[oidpos].encoded = NULL; | ||
156 | |||
157 | buffer_put_char(&buf, '\0'); | ||
158 | |||
159 | mechs = xmalloc(buffer_len(&buf)); | ||
160 | buffer_get(&buf, mechs, buffer_len(&buf)); | ||
161 | buffer_free(&buf); | ||
162 | |||
163 | if (strlen(mechs) == 0) { | ||
164 | free(mechs); | ||
165 | mechs = NULL; | ||
166 | } | ||
167 | |||
168 | return (mechs); | ||
169 | } | ||
170 | |||
171 | gss_OID | ||
172 | ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { | ||
173 | int i = 0; | ||
174 | |||
175 | switch (kex_type) { | ||
176 | case KEX_GSS_GRP1_SHA1: | ||
177 | if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) | ||
178 | return GSS_C_NO_OID; | ||
179 | name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; | ||
180 | break; | ||
181 | case KEX_GSS_GRP14_SHA1: | ||
182 | if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) | ||
183 | return GSS_C_NO_OID; | ||
184 | name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; | ||
185 | break; | ||
186 | case KEX_GSS_GEX_SHA1: | ||
187 | if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) | ||
188 | return GSS_C_NO_OID; | ||
189 | name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; | ||
190 | break; | ||
191 | default: | ||
192 | return GSS_C_NO_OID; | ||
193 | } | ||
194 | |||
195 | while (gss_enc2oid[i].encoded != NULL && | ||
196 | strcmp(name, gss_enc2oid[i].encoded) != 0) | ||
197 | i++; | ||
198 | |||
199 | if (gss_enc2oid[i].oid != NULL && ctx != NULL) | ||
200 | ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); | ||
201 | |||
202 | return gss_enc2oid[i].oid; | ||
203 | } | ||
204 | |||
50 | /* Check that the OID in a data stream matches that in the context */ | 205 | /* Check that the OID in a data stream matches that in the context */ |
51 | int | 206 | int |
52 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | 207 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) |
@@ -199,7 +354,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, | |||
199 | } | 354 | } |
200 | 355 | ||
201 | ctx->major = gss_init_sec_context(&ctx->minor, | 356 | ctx->major = gss_init_sec_context(&ctx->minor, |
202 | GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, | 357 | ctx->client_creds, &ctx->context, ctx->name, ctx->oid, |
203 | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, | 358 | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, |
204 | 0, NULL, recv_tok, NULL, send_tok, flags, NULL); | 359 | 0, NULL, recv_tok, NULL, send_tok, flags, NULL); |
205 | 360 | ||
@@ -229,8 +384,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) | |||
229 | } | 384 | } |
230 | 385 | ||
231 | OM_uint32 | 386 | OM_uint32 |
387 | ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) | ||
388 | { | ||
389 | gss_buffer_desc gssbuf; | ||
390 | gss_name_t gssname; | ||
391 | OM_uint32 status; | ||
392 | gss_OID_set oidset; | ||
393 | |||
394 | gssbuf.value = (void *) name; | ||
395 | gssbuf.length = strlen(gssbuf.value); | ||
396 | |||
397 | gss_create_empty_oid_set(&status, &oidset); | ||
398 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
399 | |||
400 | ctx->major = gss_import_name(&ctx->minor, &gssbuf, | ||
401 | GSS_C_NT_USER_NAME, &gssname); | ||
402 | |||
403 | if (!ctx->major) | ||
404 | ctx->major = gss_acquire_cred(&ctx->minor, | ||
405 | gssname, 0, oidset, GSS_C_INITIATE, | ||
406 | &ctx->client_creds, NULL, NULL); | ||
407 | |||
408 | gss_release_name(&status, &gssname); | ||
409 | gss_release_oid_set(&status, &oidset); | ||
410 | |||
411 | if (ctx->major) | ||
412 | ssh_gssapi_error(ctx); | ||
413 | |||
414 | return(ctx->major); | ||
415 | } | ||
416 | |||
417 | OM_uint32 | ||
232 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | 418 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) |
233 | { | 419 | { |
420 | if (ctx == NULL) | ||
421 | return -1; | ||
422 | |||
234 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, | 423 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, |
235 | GSS_C_QOP_DEFAULT, buffer, hash))) | 424 | GSS_C_QOP_DEFAULT, buffer, hash))) |
236 | ssh_gssapi_error(ctx); | 425 | ssh_gssapi_error(ctx); |
@@ -238,6 +427,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | |||
238 | return (ctx->major); | 427 | return (ctx->major); |
239 | } | 428 | } |
240 | 429 | ||
430 | /* Priviledged when used by server */ | ||
431 | OM_uint32 | ||
432 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | ||
433 | { | ||
434 | if (ctx == NULL) | ||
435 | return -1; | ||
436 | |||
437 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | ||
438 | gssbuf, gssmic, NULL); | ||
439 | |||
440 | return (ctx->major); | ||
441 | } | ||
442 | |||
241 | void | 443 | void |
242 | ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, | 444 | ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, |
243 | const char *context) | 445 | const char *context) |
@@ -251,11 +453,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, | |||
251 | } | 453 | } |
252 | 454 | ||
253 | int | 455 | int |
254 | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | 456 | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, |
457 | const char *client) | ||
255 | { | 458 | { |
256 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; | 459 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; |
257 | OM_uint32 major, minor; | 460 | OM_uint32 major, minor; |
258 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; | 461 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; |
462 | Gssctxt *intctx = NULL; | ||
463 | |||
464 | if (ctx == NULL) | ||
465 | ctx = &intctx; | ||
259 | 466 | ||
260 | /* RFC 4462 says we MUST NOT do SPNEGO */ | 467 | /* RFC 4462 says we MUST NOT do SPNEGO */ |
261 | if (oid->length == spnego_oid.length && | 468 | if (oid->length == spnego_oid.length && |
@@ -265,6 +472,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | |||
265 | ssh_gssapi_build_ctx(ctx); | 472 | ssh_gssapi_build_ctx(ctx); |
266 | ssh_gssapi_set_oid(*ctx, oid); | 473 | ssh_gssapi_set_oid(*ctx, oid); |
267 | major = ssh_gssapi_import_name(*ctx, host); | 474 | major = ssh_gssapi_import_name(*ctx, host); |
475 | |||
476 | if (!GSS_ERROR(major) && client) | ||
477 | major = ssh_gssapi_client_identity(*ctx, client); | ||
478 | |||
268 | if (!GSS_ERROR(major)) { | 479 | if (!GSS_ERROR(major)) { |
269 | major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, | 480 | major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, |
270 | NULL); | 481 | NULL); |
@@ -274,10 +485,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | |||
274 | GSS_C_NO_BUFFER); | 485 | GSS_C_NO_BUFFER); |
275 | } | 486 | } |
276 | 487 | ||
277 | if (GSS_ERROR(major)) | 488 | if (GSS_ERROR(major) || intctx != NULL) |
278 | ssh_gssapi_delete_ctx(ctx); | 489 | ssh_gssapi_delete_ctx(ctx); |
279 | 490 | ||
280 | return (!GSS_ERROR(major)); | 491 | return (!GSS_ERROR(major)); |
281 | } | 492 | } |
282 | 493 | ||
494 | int | ||
495 | ssh_gssapi_credentials_updated(Gssctxt *ctxt) { | ||
496 | static gss_name_t saved_name = GSS_C_NO_NAME; | ||
497 | static OM_uint32 saved_lifetime = 0; | ||
498 | static gss_OID saved_mech = GSS_C_NO_OID; | ||
499 | static gss_name_t name; | ||
500 | static OM_uint32 last_call = 0; | ||
501 | OM_uint32 lifetime, now, major, minor; | ||
502 | int equal; | ||
503 | |||
504 | now = time(NULL); | ||
505 | |||
506 | if (ctxt) { | ||
507 | debug("Rekey has happened - updating saved versions"); | ||
508 | |||
509 | if (saved_name != GSS_C_NO_NAME) | ||
510 | gss_release_name(&minor, &saved_name); | ||
511 | |||
512 | major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, | ||
513 | &saved_name, &saved_lifetime, NULL, NULL); | ||
514 | |||
515 | if (!GSS_ERROR(major)) { | ||
516 | saved_mech = ctxt->oid; | ||
517 | saved_lifetime+= now; | ||
518 | } else { | ||
519 | /* Handle the error */ | ||
520 | } | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | if (now - last_call < 10) | ||
525 | return 0; | ||
526 | |||
527 | last_call = now; | ||
528 | |||
529 | if (saved_mech == GSS_C_NO_OID) | ||
530 | return 0; | ||
531 | |||
532 | major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, | ||
533 | &name, &lifetime, NULL, NULL); | ||
534 | if (major == GSS_S_CREDENTIALS_EXPIRED) | ||
535 | return 0; | ||
536 | else if (GSS_ERROR(major)) | ||
537 | return 0; | ||
538 | |||
539 | major = gss_compare_name(&minor, saved_name, name, &equal); | ||
540 | gss_release_name(&minor, &name); | ||
541 | if (GSS_ERROR(major)) | ||
542 | return 0; | ||
543 | |||
544 | if (equal && (saved_lifetime < lifetime + now - 10)) | ||
545 | return 1; | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
283 | #endif /* GSSAPI */ | 550 | #endif /* GSSAPI */ |