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