Lines 1-7
Link Here
|
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 |
Lines 41-52
Link Here
|
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) |
Lines 199-205
ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
Link Here
|
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 |
|
Lines 229-236
ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
Link Here
|
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); |
Lines 238-243
ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
Link Here
|
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) |
Lines 251-261
ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
Link Here
|
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 && |
Lines 265-270
ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
Link Here
|
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); |
Lines 274-283
ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
Link Here
|
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 */ |