diff options
Diffstat (limited to 'gss-genr.c')
-rw-r--r-- | gss-genr.c | 209 |
1 files changed, 167 insertions, 42 deletions
diff --git a/gss-genr.c b/gss-genr.c index 57f12a2dc..42f942b58 100644 --- a/gss-genr.c +++ b/gss-genr.c | |||
@@ -39,12 +39,160 @@ | |||
39 | #include "buffer.h" | 39 | #include "buffer.h" |
40 | #include "log.h" | 40 | #include "log.h" |
41 | #include "ssh2.h" | 41 | #include "ssh2.h" |
42 | #include "cipher.h" | ||
43 | #include "key.h" | ||
44 | #include "kex.h" | ||
45 | #include <openssl/evp.h> | ||
42 | 46 | ||
43 | #include "ssh-gss.h" | 47 | #include "ssh-gss.h" |
44 | 48 | ||
45 | extern u_char *session_id2; | 49 | extern u_char *session_id2; |
46 | extern u_int session_id2_len; | 50 | extern u_int session_id2_len; |
47 | 51 | ||
52 | typedef struct { | ||
53 | char *encoded; | ||
54 | gss_OID oid; | ||
55 | } ssh_gss_kex_mapping; | ||
56 | |||
57 | /* | ||
58 | * XXX - It would be nice to find a more elegant way of handling the | ||
59 | * XXX passing of the key exchange context to the userauth routines | ||
60 | */ | ||
61 | |||
62 | Gssctxt *gss_kex_context = NULL; | ||
63 | |||
64 | static ssh_gss_kex_mapping *gss_enc2oid = NULL; | ||
65 | |||
66 | int | ||
67 | ssh_gssapi_oid_table_ok() { | ||
68 | return (gss_enc2oid != NULL); | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * Return a list of the gss-group1-sha1 mechanisms supported by this program | ||
73 | * | ||
74 | * We test mechanisms to ensure that we can use them, to avoid starting | ||
75 | * a key exchange with a bad mechanism | ||
76 | */ | ||
77 | |||
78 | char * | ||
79 | ssh_gssapi_client_mechanisms(const char *host) { | ||
80 | gss_OID_set gss_supported; | ||
81 | OM_uint32 min_status; | ||
82 | |||
83 | gss_indicate_mechs(&min_status, &gss_supported); | ||
84 | |||
85 | return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, | ||
86 | host)); | ||
87 | } | ||
88 | |||
89 | char * | ||
90 | ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, | ||
91 | const char *data) { | ||
92 | Buffer buf; | ||
93 | size_t i; | ||
94 | int oidpos, enclen; | ||
95 | char *mechs, *encoded; | ||
96 | u_char digest[EVP_MAX_MD_SIZE]; | ||
97 | char deroid[2]; | ||
98 | const EVP_MD *evp_md = EVP_md5(); | ||
99 | EVP_MD_CTX md; | ||
100 | |||
101 | if (gss_enc2oid != NULL) { | ||
102 | for (i = 0; gss_enc2oid[i].encoded != NULL; i++) | ||
103 | xfree(gss_enc2oid[i].encoded); | ||
104 | xfree(gss_enc2oid); | ||
105 | } | ||
106 | |||
107 | gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * | ||
108 | (gss_supported->count + 1)); | ||
109 | |||
110 | buffer_init(&buf); | ||
111 | |||
112 | oidpos = 0; | ||
113 | for (i = 0; i < gss_supported->count; i++) { | ||
114 | if (gss_supported->elements[i].length < 128 && | ||
115 | (*check)(NULL, &(gss_supported->elements[i]), data)) { | ||
116 | |||
117 | deroid[0] = SSH_GSS_OIDTYPE; | ||
118 | deroid[1] = gss_supported->elements[i].length; | ||
119 | |||
120 | EVP_DigestInit(&md, evp_md); | ||
121 | EVP_DigestUpdate(&md, deroid, 2); | ||
122 | EVP_DigestUpdate(&md, | ||
123 | gss_supported->elements[i].elements, | ||
124 | gss_supported->elements[i].length); | ||
125 | EVP_DigestFinal(&md, digest, NULL); | ||
126 | |||
127 | encoded = xmalloc(EVP_MD_size(evp_md) * 2); | ||
128 | enclen = __b64_ntop(digest, EVP_MD_size(evp_md), | ||
129 | encoded, EVP_MD_size(evp_md) * 2); | ||
130 | |||
131 | if (oidpos != 0) | ||
132 | buffer_put_char(&buf, ','); | ||
133 | |||
134 | buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, | ||
135 | sizeof(KEX_GSS_GEX_SHA1_ID) - 1); | ||
136 | buffer_append(&buf, encoded, enclen); | ||
137 | buffer_put_char(&buf, ','); | ||
138 | buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, | ||
139 | sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); | ||
140 | buffer_append(&buf, encoded, enclen); | ||
141 | buffer_put_char(&buf, ','); | ||
142 | buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, | ||
143 | sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); | ||
144 | buffer_append(&buf, encoded, enclen); | ||
145 | |||
146 | gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); | ||
147 | gss_enc2oid[oidpos].encoded = encoded; | ||
148 | oidpos++; | ||
149 | } | ||
150 | } | ||
151 | gss_enc2oid[oidpos].oid = NULL; | ||
152 | gss_enc2oid[oidpos].encoded = NULL; | ||
153 | |||
154 | buffer_put_char(&buf, '\0'); | ||
155 | |||
156 | mechs = xmalloc(buffer_len(&buf)); | ||
157 | buffer_get(&buf, mechs, buffer_len(&buf)); | ||
158 | buffer_free(&buf); | ||
159 | |||
160 | if (strlen(mechs) == 0) { | ||
161 | xfree(mechs); | ||
162 | mechs = NULL; | ||
163 | } | ||
164 | |||
165 | return (mechs); | ||
166 | } | ||
167 | |||
168 | gss_OID | ||
169 | ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { | ||
170 | int i = 0; | ||
171 | |||
172 | switch (kex_type) { | ||
173 | case KEX_GSS_GRP1_SHA1: | ||
174 | name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; | ||
175 | break; | ||
176 | case KEX_GSS_GRP14_SHA1: | ||
177 | name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; | ||
178 | break; | ||
179 | case KEX_GSS_GEX_SHA1: | ||
180 | name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; | ||
181 | break; | ||
182 | default: | ||
183 | return GSS_C_NO_OID; | ||
184 | } | ||
185 | |||
186 | while (gss_enc2oid[i].encoded != NULL && | ||
187 | strcmp(name, gss_enc2oid[i].encoded) != 0) | ||
188 | i++; | ||
189 | |||
190 | if (gss_enc2oid[i].oid != NULL && ctx != NULL) | ||
191 | ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); | ||
192 | |||
193 | return gss_enc2oid[i].oid; | ||
194 | } | ||
195 | |||
48 | /* Check that the OID in a data stream matches that in the context */ | 196 | /* Check that the OID in a data stream matches that in the context */ |
49 | int | 197 | int |
50 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | 198 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) |
@@ -107,7 +255,7 @@ ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, | |||
107 | /* The GSSAPI error */ | 255 | /* The GSSAPI error */ |
108 | do { | 256 | do { |
109 | gss_display_status(&lmin, ctxt->major, | 257 | gss_display_status(&lmin, ctxt->major, |
110 | GSS_C_GSS_CODE, GSS_C_NULL_OID, &ctx, &msg); | 258 | GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); |
111 | 259 | ||
112 | buffer_append(&b, msg.value, msg.length); | 260 | buffer_append(&b, msg.value, msg.length); |
113 | buffer_put_char(&b, '\n'); | 261 | buffer_put_char(&b, '\n'); |
@@ -118,7 +266,7 @@ ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, | |||
118 | /* The mechanism specific error */ | 266 | /* The mechanism specific error */ |
119 | do { | 267 | do { |
120 | gss_display_status(&lmin, ctxt->minor, | 268 | gss_display_status(&lmin, ctxt->minor, |
121 | GSS_C_MECH_CODE, GSS_C_NULL_OID, &ctx, &msg); | 269 | GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); |
122 | 270 | ||
123 | buffer_append(&b, msg.value, msg.length); | 271 | buffer_append(&b, msg.value, msg.length); |
124 | buffer_put_char(&b, '\n'); | 272 | buffer_put_char(&b, '\n'); |
@@ -226,45 +374,28 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) | |||
226 | return (ctx->major); | 374 | return (ctx->major); |
227 | } | 375 | } |
228 | 376 | ||
229 | /* Acquire credentials for a server running on the current host. | ||
230 | * Requires that the context structure contains a valid OID | ||
231 | */ | ||
232 | |||
233 | /* Returns a GSSAPI error code */ | ||
234 | OM_uint32 | 377 | OM_uint32 |
235 | ssh_gssapi_acquire_cred(Gssctxt *ctx) | 378 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) |
236 | { | 379 | { |
237 | OM_uint32 status; | 380 | if (ctx == NULL) |
238 | char lname[MAXHOSTNAMELEN]; | 381 | return -1; |
239 | gss_OID_set oidset; | ||
240 | |||
241 | gss_create_empty_oid_set(&status, &oidset); | ||
242 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
243 | |||
244 | if (gethostname(lname, MAXHOSTNAMELEN)) { | ||
245 | gss_release_oid_set(&status, &oidset); | ||
246 | return (-1); | ||
247 | } | ||
248 | 382 | ||
249 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | 383 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, |
250 | gss_release_oid_set(&status, &oidset); | 384 | GSS_C_QOP_DEFAULT, buffer, hash))) |
251 | return (ctx->major); | ||
252 | } | ||
253 | |||
254 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
255 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) | ||
256 | ssh_gssapi_error(ctx); | 385 | ssh_gssapi_error(ctx); |
257 | 386 | ||
258 | gss_release_oid_set(&status, &oidset); | ||
259 | return (ctx->major); | 387 | return (ctx->major); |
260 | } | 388 | } |
261 | 389 | ||
390 | /* Priviledged when used by server */ | ||
262 | OM_uint32 | 391 | OM_uint32 |
263 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | 392 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) |
264 | { | 393 | { |
265 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, | 394 | if (ctx == NULL) |
266 | GSS_C_QOP_DEFAULT, buffer, hash))) | 395 | return -1; |
267 | ssh_gssapi_error(ctx); | 396 | |
397 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | ||
398 | gssbuf, gssmic, NULL); | ||
268 | 399 | ||
269 | return (ctx->major); | 400 | return (ctx->major); |
270 | } | 401 | } |
@@ -281,22 +412,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, | |||
281 | buffer_put_cstring(b, context); | 412 | buffer_put_cstring(b, context); |
282 | } | 413 | } |
283 | 414 | ||
284 | OM_uint32 | ||
285 | ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) | ||
286 | { | ||
287 | if (*ctx) | ||
288 | ssh_gssapi_delete_ctx(ctx); | ||
289 | ssh_gssapi_build_ctx(ctx); | ||
290 | ssh_gssapi_set_oid(*ctx, oid); | ||
291 | return (ssh_gssapi_acquire_cred(*ctx)); | ||
292 | } | ||
293 | |||
294 | int | 415 | int |
295 | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | 416 | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) |
296 | { | 417 | { |
297 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; | 418 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; |
298 | OM_uint32 major, minor; | 419 | OM_uint32 major, minor; |
299 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; | 420 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; |
421 | Gssctxt *intctx = NULL; | ||
422 | |||
423 | if (ctx == NULL) | ||
424 | ctx = &intctx; | ||
300 | 425 | ||
301 | /* RFC 4462 says we MUST NOT do SPNEGO */ | 426 | /* RFC 4462 says we MUST NOT do SPNEGO */ |
302 | if (oid->length == spnego_oid.length && | 427 | if (oid->length == spnego_oid.length && |
@@ -315,7 +440,7 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | |||
315 | GSS_C_NO_BUFFER); | 440 | GSS_C_NO_BUFFER); |
316 | } | 441 | } |
317 | 442 | ||
318 | if (GSS_ERROR(major)) | 443 | if (GSS_ERROR(major) || intctx != NULL) |
319 | ssh_gssapi_delete_ctx(ctx); | 444 | ssh_gssapi_delete_ctx(ctx); |
320 | 445 | ||
321 | return (!GSS_ERROR(major)); | 446 | return (!GSS_ERROR(major)); |