1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * context_establish.c
24 *
25 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 *
28 */
29
30 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 #include <string.h>
33 #include "dh_gssapi.h"
34
35 /*
36 * The following 2 routines convert a gss_channel_binding to a DH
37 * channel_binding and vis versa. We can no longer assume a simple
38 * cast because a GSS buffer_t uses a size_t for the length field wich
39 * is 64 bits in a 64 bit process. The xdr encoding always assumes the
40 * length to be 32 bits :<.
41 */
42
43 static dh_channel_binding_t
GSS2DH_channel_binding(dh_channel_binding_t dh_binding,gss_channel_bindings_t gss_binding)44 GSS2DH_channel_binding(dh_channel_binding_t dh_binding,
45 gss_channel_bindings_t gss_binding)
46 {
47 if (gss_binding == GSS_C_NO_CHANNEL_BINDINGS)
48 return (NULL);
49
50 dh_binding->initiator_addrtype = gss_binding->initiator_addrtype;
51 dh_binding->initiator_address.dh_buffer_desc_len =
52 (uint32_t)gss_binding->initiator_address.length;
53 if (gss_binding->initiator_address.length !=
54 dh_binding->initiator_address.dh_buffer_desc_len)
55 return (NULL);
56 dh_binding->initiator_address.dh_buffer_desc_val =
57 gss_binding->initiator_address.value;
58 dh_binding->acceptor_addrtype = gss_binding->acceptor_addrtype;
59 dh_binding->acceptor_address.dh_buffer_desc_len =
60 (uint32_t)gss_binding->acceptor_address.length;
61 dh_binding->acceptor_address.dh_buffer_desc_val =
62 gss_binding->acceptor_address.value;
63 dh_binding->application_data.dh_buffer_desc_len =
64 (uint32_t)gss_binding->application_data.length;
65 dh_binding->application_data.dh_buffer_desc_val =
66 gss_binding->application_data.value;
67
68 return (dh_binding);
69 }
70
71 static gss_channel_bindings_t
DH2GSS_channel_binding(gss_channel_bindings_t gss_binding,dh_channel_binding_t dh_binding)72 DH2GSS_channel_binding(gss_channel_bindings_t gss_binding,
73 dh_channel_binding_t dh_binding)
74 {
75 if (dh_binding == NULL)
76 return (GSS_C_NO_CHANNEL_BINDINGS);
77
78 gss_binding->initiator_addrtype = dh_binding->initiator_addrtype;
79 gss_binding->initiator_address.length =
80 dh_binding->initiator_address.dh_buffer_desc_len;
81 gss_binding->initiator_address.value =
82 dh_binding->initiator_address.dh_buffer_desc_val;
83 gss_binding->acceptor_addrtype = dh_binding->acceptor_addrtype;
84 gss_binding->acceptor_address.length =
85 dh_binding->acceptor_address.dh_buffer_desc_len;
86 gss_binding->acceptor_address.value =
87 dh_binding->acceptor_address.dh_buffer_desc_val;
88 gss_binding->application_data.length =
89 dh_binding->application_data.dh_buffer_desc_len;
90 gss_binding->application_data.value =
91 dh_binding->application_data.dh_buffer_desc_val;
92
93 return (gss_binding);
94 }
95
96 /*
97 * Routine to compare that two gss_buffers are the same.
98 */
99 static bool_t
gss_buffer_cmp(gss_buffer_t b1,gss_buffer_t b2)100 gss_buffer_cmp(gss_buffer_t b1, gss_buffer_t b2)
101 {
102 if (b1->length != b2->length)
103 return (FALSE);
104 if (b1->length == 0)
105 return (TRUE);
106 if (b1->value == b2->value)
107 return (TRUE);
108 if (b1->value == 0 || b2->value == 0)
109 return (FALSE);
110
111 return (memcmp(b1->value, b2->value, b1->length) == 0);
112 }
113
114 /*
115 * Compare if two channel bindings are the same. If the local binding is
116 * NULL then we always return TRUE. This indicates that the local host
117 * does not care about any bindings.
118 */
119
120 static bool_t
gss_chanbind_cmp(gss_channel_bindings_t local,gss_channel_bindings_t remote)121 gss_chanbind_cmp(gss_channel_bindings_t local, gss_channel_bindings_t remote)
122 {
123 if (local == NULL)
124 return (TRUE); /* local doesn't care so we won't either */
125
126 if (remote == NULL)
127 return (FALSE);
128
129 if (local->initiator_addrtype != remote->initiator_addrtype)
130 return (FALSE);
131
132 if (local->initiator_addrtype != GSS_C_AF_NULLADDR)
133 if (gss_buffer_cmp(&local->initiator_address,
134 &remote->initiator_address) == FALSE)
135 return (FALSE);
136
137 if (local->acceptor_addrtype != remote->acceptor_addrtype)
138 return (FALSE);
139
140 if (local->acceptor_addrtype != GSS_C_AF_NULLADDR)
141 if (gss_buffer_cmp(&local->acceptor_address,
142 &remote->acceptor_address) == FALSE)
143 return (FALSE);
144
145 return (gss_buffer_cmp(&local->application_data,
146 &remote->application_data));
147 }
148
149 /*
150 * Generate an accept token for a context and channel binding puting the
151 * generated token output.
152 */
153
154 static
155 OM_uint32
gen_accept_token(dh_gss_context_t ctx,gss_channel_bindings_t channel,gss_buffer_t output)156 gen_accept_token(dh_gss_context_t ctx, /* Diffie-Hellman context */
157 gss_channel_bindings_t channel, /* channel bindings */
158 gss_buffer_t output /* The accept token */)
159 {
160 dh_token_desc token;
161 /* Grap a pointer to the context_t part of the token */
162 dh_cntx_t accept = &token.ver.dh_version_u.
163 body.dh_token_body_desc_u.accept_context.cntx;
164 dh_key_set keys;
165 dh_channel_binding_desc dh_binding;
166
167 /* Set the version number from the context. */
168 token.ver.verno = ctx->proto_version;
169 /* Set the token type to be an ACCEPT token. */
170 token.ver.dh_version_u.body.type = DH_ACCEPT_CNTX;
171 /* Set our self as the remote for the other end. */
172 accept->remote = ctx->local;
173 /* The remote side to us is the local side at the other end. */
174 accept->local = ctx->remote;
175 /* Our context flags */
176 accept->flags = ctx->flags;
177 /* When we will expire */
178 accept->expire = ctx->expire;
179 /* Our channel bindings */
180 accept->channel = GSS2DH_channel_binding(&dh_binding, channel);
181 /* Package the context session keys into a key_set */
182 keys.dh_key_set_len = ctx->no_keys;
183 keys.dh_key_set_val = ctx->keys;
184
185 /* Build the token */
186 return (__make_token(output, NULL, &token, &keys));
187 }
188
189 /*
190 * Check if a credential is valid for the requested usage. Note that
191 * Diffie-Hellman only supports credentials based on the callers net
192 * name. netname will point to the users rpc netname. It is up to the
193 * caller to free the netname.
194 */
195
196 static OM_uint32
validate_cred(dh_context_t cntx,OM_uint32 * minor,dh_cred_id_t cred,gss_cred_usage_t usage,dh_principal * netname)197 validate_cred(dh_context_t cntx, /* Diffie-Hellman mechanism context */
198 OM_uint32 *minor, /* Mechanism status */
199 dh_cred_id_t cred, /* Diffie-Hellman credential */
200 gss_cred_usage_t usage, /* Cred usage */
201 dh_principal *netname /* Cred owner */)
202 {
203 /* Set minor status */
204 *minor = DH_SUCCESS;
205 *netname = NULL;
206
207 /*
208 * See if the users creditial is available, i.e.,
209 * the user is "key logged" in.
210 */
211 if (!cntx->keyopts->key_secretkey_is_set()) {
212 *minor = DH_NO_SECRET;
213 return (GSS_S_NO_CRED);
214 }
215
216
217 /*
218 * Get the netname.
219 */
220
221 if ((*netname = cntx->keyopts->get_principal()) == NULL) {
222 *minor = DH_NO_PRINCIPAL;
223 return (GSS_S_NO_CRED);
224 }
225
226 /*
227 * Check if the supplied cred is valid for the requested usage.
228 * The default cred never expires and has a usage of GSS_C_BOTH.
229 */
230
231 if ((gss_cred_id_t)cred != GSS_C_NO_CREDENTIAL) {
232 if ((cred->usage != usage &&
233 cred->usage != GSS_C_BOTH) ||
234 strcmp(*netname, cred->principal) != 0) {
235 free(*netname);
236 return (GSS_S_NO_CRED);
237 }
238
239 /* See if the cred is still valid */
240 if (cred->expire != GSS_C_INDEFINITE &&
241 time(0) > cred->expire) {
242 free(*netname);
243 return (GSS_S_CREDENTIALS_EXPIRED);
244 }
245 }
246 return (GSS_S_COMPLETE);
247 }
248
249
250 /*
251 * establish_session_keys: This routine decrypts the session keys supplied
252 * and uses those keys to verifiy the signature over the input token
253 * match the signature in the token.
254 */
255 static OM_uint32
establish_session_keys(dh_context_t dhctx,const char * remote,dh_key_set_t keys,dh_signature_t sig,dh_token_t token)256 establish_session_keys(dh_context_t dhctx, const char *remote,
257 dh_key_set_t keys, dh_signature_t sig, dh_token_t token)
258 {
259 OM_uint32 stat;
260 int i, j;
261 des_block *saved_keys;
262 char *saved_sig;
263
264 /*
265 * The following variable is used by the keyopts key_decryptsessions
266 * entry point. If this variable is non zero and the underling
267 * mechanism uses a cache of public keys, then get the public key
268 * for the remote out of that cache. When key_decrptsessions return
269 * this variable will be set to non zero if the key did come
270 * out of the cache, otherwise it will be set to zero.
271 */
272 int key_was_from_cache = 1;
273
274 /* Save the keyset so if we fail we can try again */
275 if ((saved_keys = New(des_block, keys->dh_key_set_len)) == NULL)
276 return (DH_NOMEM_FAILURE);
277
278 for (i = 0; i < keys->dh_key_set_len; i++)
279 saved_keys[i] = keys->dh_key_set_val[i];
280
281 /* Save the unencrypted signature as well for retry attempt */
282 if ((saved_sig = New(char, sig->dh_signature_len)) == NULL) {
283 Free(saved_keys);
284 return (DH_NOMEM_FAILURE);
285 }
286 memcpy(saved_sig, sig->dh_signature_val, sig->dh_signature_len);
287
288 /*
289 * We will try to decrypt the session keys up to two times.
290 * The first time will let the underlying mechanism use a
291 * public key cache, if the set of session keys fail to
292 * validate the signature that is reported in the deserialized
293 * token, and those session keys were decrypted by a key
294 * derived from a public key cache, then we will try again but
295 * this time will advise the underlying mechanism not to use
296 * its cache.
297 */
298
299 for (i = 0; key_was_from_cache && i < 2; i++) {
300 /*
301 * Decrypt the session keys using the mechanism specific
302 * routine and if this is the second time, don't use
303 * the cache.
304 */
305 if (i == 1)
306 key_was_from_cache = 0;
307 if (dhctx->keyopts->key_decryptsessions(remote,
308 keys->dh_key_set_val,
309 keys->dh_key_set_len,
310 &key_was_from_cache)) {
311 Free(saved_keys);
312 Free(saved_sig);
313 return (DH_SESSION_CIPHER_FAILURE);
314 }
315
316 #ifdef DH_DEBUG
317 fprintf(stderr, "Received session keys %s the cache:\n",
318 key_was_form_cache ? "using" : "not using");
319 for (i = 0; i < keys->dh_key_set_len; i++)
320 fprintf(stderr, "%08.8x%08.8x ",
321 keys->dh_key_set_val[i].key.high,
322 keys->dh_key_set_val[i].key.low);
323 fprintf(stderr, "\n");
324 #endif
325
326 /*
327 * Now verify that the extracted signature from the
328 * deserialized token is the same as our calculation
329 * of the signature.
330 */
331 if ((stat = __verify_sig(token, DH_MECH_QOP, keys, sig)) ==
332 DH_SUCCESS) {
333 Free(saved_keys);
334 Free(saved_sig);
335 return (DH_SUCCESS);
336
337 }
338
339 /* Restore the keys and signature for retry */
340 for (j = 0; j < keys->dh_key_set_len; j++)
341 keys->dh_key_set_val[j] = saved_keys[j];
342
343 memcpy(sig->dh_signature_val, saved_sig, sig->dh_signature_len);
344 }
345
346 Free(saved_keys);
347 Free(saved_sig);
348 return (stat);
349 }
350 /*
351 * This is the Diffie-Hellman mechanism entry point for the
352 * gss_accept_sec context. See RFC 2078 for details. This
353 * routine accepts a context establish token from the initator
354 * and optionally produces a token to send back to the initator to
355 * establish a GSS security context. The established context will
356 * be return via the *gss_ctx paramater.
357 */
358
359 OM_uint32
__dh_gss_accept_sec_context(void * ctx,OM_uint32 * minor,gss_ctx_id_t * gss_ctx,gss_cred_id_t cred,gss_buffer_t input,gss_channel_bindings_t channel,gss_name_t * principal,gss_OID * mech,gss_buffer_t output,OM_uint32 * flags,OM_uint32 * expire,gss_cred_id_t * del_cred)360 __dh_gss_accept_sec_context(void *ctx, /* Per mechanism context */
361 OM_uint32 *minor, /* Mechanism status */
362 gss_ctx_id_t *gss_ctx, /* GSS context */
363 gss_cred_id_t cred, /* GSS credential */
364 gss_buffer_t input, /* Input from initiator */
365 /* Local channel bindings */
366 gss_channel_bindings_t channel,
367 gss_name_t *principal, /* Initiator name */
368 gss_OID* mech, /* Returned mechanism */
369 gss_buffer_t output, /* Token to send initiator */
370 OM_uint32 *flags, /* flags of context */
371 OM_uint32 *expire, /* Time left on context */
372 gss_cred_id_t *del_cred /* Delegated credential */)
373 {
374 dh_token_desc token;
375 /* ctx is a Diffie-Hellman mechanism context */
376 dh_context_t dhctx = (dh_context_t)ctx;
377 dh_gss_context_t g_cntx = NULL;
378 dh_principal netname = NULL;
379 dh_init_context_t clnt;
380 OM_uint32 stat;
381 int i;
382 dh_signature sig;
383 struct gss_channel_bindings_struct dh_binding_desc;
384 gss_channel_bindings_t dh_binding;
385
386 /* Check for required parameters */
387 if (input == NULL)
388 return (GSS_S_CALL_INACCESSIBLE_READ);
389 if (minor == NULL || output == NULL || gss_ctx == NULL)
390 return (GSS_S_CALL_INACCESSIBLE_WRITE);
391
392 /* Give outputs sane values if present */
393 *minor = 0;
394 if (principal)
395 *principal = NULL;
396 if (mech)
397 *mech = GSS_C_NO_OID;
398 if (flags)
399 *flags = 0;
400 if (expire)
401 *expire = 0;
402 if (del_cred)
403 *del_cred = GSS_C_NO_CREDENTIAL;
404
405 output->length = 0;
406 output->value = 0;
407
408 /*
409 * Diffie-Hellman never returns GSS_S_CONTINUE_NEEDED from a
410 * gss_accept_sec_context so the only context read should be
411 * GSS_C_NO_CONTEXT.
412 */
413 if (*gss_ctx != GSS_C_NO_CONTEXT)
414 return (GSS_S_NO_CONTEXT);
415
416 /* Valdidate the local credentinal and retrieve then principal name */
417 stat = validate_cred(dhctx, minor,
418 (dh_cred_id_t)cred, GSS_C_ACCEPT, &netname);
419 if (stat != GSS_S_COMPLETE)
420 return (stat);
421
422 /*
423 * Deserialize the input into token, extracting the signature
424 * into sig. Where sig is our calculation of the MD5 check sum
425 * over the input token up to the signature.
426 */
427 memset(&sig, 0, sizeof (sig));
428 if (*minor = __get_ap_token(input, dhctx->mech, &token, &sig)) {
429 free(netname);
430 __free_signature(&sig);
431 return (GSS_S_DEFECTIVE_TOKEN);
432 }
433
434 /* set clnt to point to the init context part of token */
435 clnt = &token.ver.dh_version_u.body.dh_token_body_desc_u.init_context;
436
437 /* Check that this context is really for us */
438 if (strcmp(clnt->cntx.local, netname) != 0) {
439 free(netname);
440 *minor = DH_NOT_LOCAL;
441 stat = GSS_S_DEFECTIVE_TOKEN;
442 goto cleanup;
443 }
444 free(netname);
445
446 /*
447 * See if this is a DH protocol version that we can handle.
448 * Currently we can handle the one and only DH_PROTO_VERSION.
449 */
450
451 if (token.ver.verno != DH_PROTO_VERSION) {
452 *minor = DH_PROTO_MISMATCH;
453 stat = GSS_S_DEFECTIVE_TOKEN;
454 goto cleanup;
455 }
456
457 /* Decrypt the session keys and verify the signature */
458 if ((*minor = establish_session_keys(dhctx, clnt->cntx.remote,
459 &clnt->keys,
460 &sig, &token)) != DH_SUCCESS) {
461 stat = GSS_S_BAD_SIG;
462 goto cleanup;
463 }
464
465 /* Check that the channel bindings are the same */
466 dh_binding = DH2GSS_channel_binding(&dh_binding_desc,
467 clnt->cntx.channel);
468 if (!gss_chanbind_cmp(channel, dh_binding)) {
469 stat = GSS_S_BAD_BINDINGS;
470 goto cleanup;
471 }
472
473 /* Everything is OK, so allocate the context */
474 if ((g_cntx = New(dh_gss_context_desc, 1)) == NULL) {
475 *minor = DH_NOMEM_FAILURE;
476 stat = GSS_S_FAILURE;
477 goto cleanup;
478 }
479
480 /*
481 * The context is now established for us, though we may still
482 * need to send a token if the initiator requested mutual
483 * authentications.
484 */
485 g_cntx->state = ESTABLISHED;
486 /* We're not the initiator */
487 g_cntx->initiate = 0;
488 /* Set the protocol version from the token */
489 g_cntx->proto_version = token.ver.verno;
490 /* Initialize the sequence history */
491 __dh_init_seq_hist(g_cntx);
492 /* Set debug to false */
493 g_cntx->debug = 0;
494
495 /* Set who the initiator is */
496 if ((g_cntx->remote = strdup(clnt->cntx.remote)) == NULL) {
497 *minor = DH_NOMEM_FAILURE;
498 stat = GSS_S_FAILURE;
499 goto cleanup;
500 }
501
502 /* Set who we are */
503 if ((g_cntx->local = strdup(clnt->cntx.local)) == NULL) {
504 *minor = DH_NOMEM_FAILURE;
505 stat = GSS_S_FAILURE;
506 goto cleanup;
507 }
508
509 /* Stash a copy of the session keys for the context */
510 g_cntx->no_keys = clnt->keys.dh_key_set_len;
511 if ((g_cntx->keys = New(des_block, g_cntx->no_keys)) == NULL) {
512 *minor = DH_NOMEM_FAILURE;
513 stat = GSS_S_FAILURE;
514 goto cleanup;
515 }
516
517 for (i = 0; i < g_cntx->no_keys; i++)
518 g_cntx->keys[i] = clnt->keys.dh_key_set_val[i];
519
520 /* Set the flags and expire time */
521 g_cntx->flags = clnt->cntx.flags;
522 g_cntx->expire = clnt->cntx.expire;
523
524 /* Create output token if needed */
525 if (g_cntx->flags & GSS_C_MUTUAL_FLAG) {
526 if (*minor = gen_accept_token(g_cntx, channel, output)) {
527 stat = GSS_S_FAILURE;
528 goto cleanup;
529 }
530 }
531
532 /* This is now a valid context */
533 if ((*minor = __dh_install_context(g_cntx)) != DH_SUCCESS) {
534 stat = GSS_S_FAILURE;
535 goto cleanup;
536 }
537
538 /* Return the GSS context to the caller */
539 *gss_ctx = (gss_ctx_id_t)g_cntx;
540
541 /* Return the remote principal if requested */
542 if (principal)
543 *principal = (gss_name_t)strdup(g_cntx->remote);
544 /* Return the flags if requested */
545 if (flags)
546 *flags = g_cntx->flags;
547 /* Return the expire time if requested */
548 if (expire)
549 *expire = g_cntx->expire;
550 /* Return the mechanism if requested */
551 if (mech)
552 *mech = dhctx->mech;
553
554 /* Release storage of the signature */
555 __free_signature(&sig);
556
557 /* Tear down the deserialize token */
558 xdr_free(xdr_dh_token_desc, (char *)&token);
559
560 /* We're done */
561 return (GSS_S_COMPLETE);
562
563 cleanup:
564 /* Destroy incomplete context */
565 if (g_cntx) {
566 __dh_destroy_seq_hist(g_cntx);
567 (void) __dh_remove_context(g_cntx);
568 free(g_cntx->remote);
569 free(g_cntx->local);
570 Free(g_cntx->keys);
571 Free(g_cntx);
572 }
573
574 /* Release the signature and the deserialized token. */
575 __free_signature(&sig);
576 xdr_free(xdr_dh_token_desc, (char *)&token);
577
578 return (stat);
579 }
580
581
582 /*
583 * gen_init_token: create a token to pass to the other side
584 * to create a GSS context.
585 */
586 static
587 OM_uint32
gen_init_token(dh_gss_context_t cntx,dh_context_t dhctx,gss_channel_bindings_t channel,gss_buffer_t result)588 gen_init_token(dh_gss_context_t cntx, /* Diffie-Hellman GSS context */
589 dh_context_t dhctx, /* Diffie-Hellman mechanism context */
590 gss_channel_bindings_t channel, /* local channel bindings */
591 gss_buffer_t result /* The serialized token to send */)
592 {
593 dh_token_desc token; /* Unserialed token */
594 dh_init_context_t remote; /* init_context in token */
595 dh_key_set keys, ukeys; /* encrypted and unencrypted keys */
596 int i, stat;
597 dh_channel_binding_desc dh_binding;
598
599 /* Create key_set for session keys */
600 if ((keys.dh_key_set_val = New(des_block, cntx->no_keys)) == NULL)
601 return (DH_NOMEM_FAILURE);
602
603 keys.dh_key_set_len = cntx->no_keys;
604 for (i = 0; i < cntx->no_keys; i++)
605 keys.dh_key_set_val[i] = cntx->keys[i];
606
607 /* Initialize token from GSS context */
608 memset(&token, 0, sizeof (token));
609 token.ver.verno = cntx->proto_version;
610 token.ver.dh_version_u.body.type = DH_INIT_CNTX;
611
612 /* Set remote to init_context part of token */
613 remote = &token.ver.dh_version_u.body.dh_token_body_desc_u.init_context;
614 /* We're the remote to the other side */
615 remote->cntx.remote = cntx->local;
616 /* And they are the local */
617 remote->cntx.local = cntx->remote;
618 /* Set our flags */
619 remote->cntx.flags = cntx->flags;
620 /* Set the expire time */
621 remote->cntx.expire = cntx->expire;
622 /* hand of our channel bindings */
623 remote->cntx.channel = GSS2DH_channel_binding(&dh_binding, channel);
624 /* set the tokens keys */
625 remote->keys = keys;
626
627
628 /* Encrypt the keys for the other side */
629
630 if (dhctx->keyopts->key_encryptsessions(cntx->remote,
631 keys.dh_key_set_val,
632 cntx->no_keys)) {
633 Free(keys.dh_key_set_val);
634 return (DH_SESSION_CIPHER_FAILURE);
635 }
636
637 /* Package up our session keys */
638 ukeys.dh_key_set_len = cntx->no_keys;
639 ukeys.dh_key_set_val = cntx->keys;
640 /*
641 * Make an APPLICATION 0 token and place it in result.
642 * Note that the unecrypted ukeys key_set is used to sign
643 * the token.
644 */
645 stat = __make_ap_token(result, dhctx->mech, &token, &ukeys);
646
647 /* We're don with the encrypted session keys */
648 Free(keys.dh_key_set_val);
649
650 /* Return our status */
651 return (stat);
652 }
653
654 /*
655 * create_context: Builds the initial Diffie-Hellman GSS context.
656 * It should always be the case that *gss_ctx == GSS_C_NO_CONTEXT
657 * on entering this routine. Given the inputs we created a Diffie-Hellman
658 * context from them. This routine will call gen_init_token above to
659 * generate the output token to pass to the other side.
660 */
661 static
662 OM_uint32
create_context(OM_uint32 * minor,dh_context_t cntx,dh_gss_context_t * gss_ctx,dh_principal netname,dh_principal target,gss_channel_bindings_t channel,OM_uint32 flags_req,OM_uint32 time_req,OM_uint32 * flags_rec,OM_uint32 * time_rec,gss_buffer_t results)663 create_context(OM_uint32 *minor, /* Diffie-Hellman specific status */
664 dh_context_t cntx, /* Diffie-Hellman mech context */
665 dh_gss_context_t *gss_ctx, /* DH GSS context */
666 dh_principal netname, /* Local principal */
667 dh_principal target, /* Remote principal */
668 gss_channel_bindings_t channel, /* Channel bindings */
669 OM_uint32 flags_req, /* Flags to set on context */
670 OM_uint32 time_req, /* Time to live for context */
671 OM_uint32 *flags_rec, /* Flags that were actually set */
672 OM_uint32 *time_rec, /* Time actually received */
673 gss_buffer_t results /* Output token for the other side */)
674 {
675 dh_gss_context_t dh_gss_ctx; /* The Diffie-Hellman context to create */
676 time_t now = time(0); /* Used to set the expire time */
677 OM_uint32 expire; /* Time left on the context */
678
679 /* Create the Diffie-Hellman context */
680 if ((*gss_ctx = dh_gss_ctx = New(dh_gss_context_desc, 1)) == NULL) {
681 *minor = DH_NOMEM_FAILURE;
682 return (GSS_S_FAILURE);
683 }
684
685 /* We're not established yet */
686 dh_gss_ctx->state = INCOMPLETE;
687 /* We're the initiator */
688 dh_gss_ctx->initiate = 1;
689 /* Set the protocol version for the context */
690 dh_gss_ctx->proto_version = DH_PROTO_VERSION;
691 /* Initialize the sequence and replay history */
692 __dh_init_seq_hist(dh_gss_ctx);
693 /* Turn off debugging */
694 dh_gss_ctx->debug = 0;
695
696 dh_gss_ctx->local = NULL;
697
698 /* Remember who we want to talk to. */
699 if ((dh_gss_ctx->remote = strdup(target)) == NULL) {
700 *minor = DH_NOMEM_FAILURE;
701 goto cleanup;
702 }
703
704 /* Rember who we are. */
705 if ((dh_gss_ctx->local = strdup(netname)) == NULL) {
706 *minor = DH_NOMEM_FAILURE;
707 goto cleanup;
708 }
709
710 /* Set up the session key */
711 dh_gss_ctx->no_keys = 3;
712 dh_gss_ctx->keys = New(des_block, 3);
713 if (dh_gss_ctx->keys == NULL) {
714 *minor = DH_NOMEM_FAILURE;
715 goto cleanup;
716 }
717
718 /* Call the mechanism specific key generator */
719 if (cntx->keyopts->key_gendeskeys(dh_gss_ctx->keys, 3)) {
720 *minor = DH_NOMEM_FAILURE;
721 goto cleanup;
722 }
723
724 #ifdef DH_DEBUG
725 {
726 int i;
727
728 fprintf(stderr, "Generated session keys:\n");
729 for (i = 0; i < dh_gss_ctx->no_keys; i++)
730 fprintf(stderr, "%08.8x%08.8x ",
731 dh_gss_ctx->keys[i].key.high,
732 dh_gss_ctx->keys[i].key.low);
733 fprintf(stderr, "\n");
734 }
735 #endif
736
737 /*
738 * We don't support currently support
739 * GSS_C_ANON_FLAG and GSS_C_DELEG_FLAG and GSS_C_CONF_FLAG
740 */
741
742 dh_gss_ctx->flags = (flags_req &
743 (GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG |
744 GSS_C_SEQUENCE_FLAG | GSS_C_REPLAY_FLAG));
745
746 /* This mechanism does integrity */
747 dh_gss_ctx->flags |= GSS_C_INTEG_FLAG;
748
749 /* Return flags to the caller if they care */
750 if (flags_rec)
751 *flags_rec = dh_gss_ctx->flags;
752
753 /* Set expire, 0 is the default, which means indefinite */
754 expire = time_req ? time_req : GSS_C_INDEFINITE;
755 /* Actually set the expire time for the context */
756 dh_gss_ctx->expire = expire == GSS_C_INDEFINITE ?
757 expire : expire + now;
758 /* Tell the call the time given to the context if they care */
759 if (time_rec)
760 *time_rec = expire;
761
762 /* Gennerate the output token to send to the other side */
763 *minor = gen_init_token(dh_gss_ctx, cntx,
764 channel, results);
765 if (*minor != DH_SUCCESS)
766 goto cleanup;
767
768 /* Recored this context as valid */
769 if ((*minor = __dh_install_context(dh_gss_ctx)) != DH_SUCCESS)
770 goto cleanup;
771
772 /* If we ask for mutal authentication return continue needed */
773 dh_gss_ctx->state = dh_gss_ctx->flags & GSS_C_MUTUAL_FLAG ?
774 INCOMPLETE : ESTABLISHED;
775
776 return (dh_gss_ctx->state == ESTABLISHED ?
777 GSS_S_COMPLETE : GSS_S_CONTINUE_NEEDED);
778 cleanup:
779
780 __dh_destroy_seq_hist(dh_gss_ctx);
781 free(dh_gss_ctx->remote);
782 free(dh_gss_ctx->local);
783 Free(dh_gss_ctx->keys);
784 Free(dh_gss_ctx);
785
786 /*
787 * Let the caller of gss_init_sec_context know that they don't
788 * have a context.
789 */
790 *gss_ctx = (dh_gss_context_t)GSS_C_NO_CONTEXT;
791
792 return (GSS_S_FAILURE);
793 }
794
795 /*
796 * continue_context: Proccess the token from the otherside in the case
797 * of mutual authentication.
798 */
799 static
800 OM_uint32
continue_context(OM_uint32 * minor,gss_buffer_t token,dh_gss_context_t dh_gss_ctx,gss_channel_bindings_t channel)801 continue_context(OM_uint32 *minor, gss_buffer_t token,
802 dh_gss_context_t dh_gss_ctx, gss_channel_bindings_t channel)
803 {
804 dh_key_set keys;
805 dh_token_desc tok;
806 dh_cntx_t remote_ctx;
807 struct gss_channel_bindings_struct remote_chan_desc;
808 gss_channel_bindings_t remote_chan;
809
810 /* Set minor to sane state */
811 *minor = DH_SUCCESS;
812
813 /* This should never happen */
814 if (token == NULL || token->length == 0)
815 return (GSS_S_DEFECTIVE_TOKEN);
816
817 /* Package the session keys for __get_token) */
818 keys.dh_key_set_len = dh_gss_ctx->no_keys;
819 keys.dh_key_set_val = dh_gss_ctx->keys;
820
821 /* Deserialize the input token into tok using the session keys */
822 if (*minor = __get_token(token, NULL, &tok, &keys))
823 return (*minor == DH_VERIFIER_MISMATCH ?
824 GSS_S_BAD_SIG : GSS_S_DEFECTIVE_TOKEN);
825
826 /*
827 * See if this is a Diffie-Hellman protocol version that we
828 * can handle. Currently we can only handle the protocol version that
829 * we initiated.
830 */
831 if (tok.ver.verno != dh_gss_ctx->proto_version) {
832 *minor = DH_PROTO_MISMATCH;
833 xdr_free(xdr_dh_token_desc, (char *)&tok);
834 return (GSS_S_DEFECTIVE_TOKEN);
835 }
836
837 /* Make sure this is the right type of token */
838 if (tok.ver.dh_version_u.body.type != DH_ACCEPT_CNTX) {
839 xdr_free(xdr_dh_token_desc, (char *)&tok);
840 return (GSS_S_DEFECTIVE_TOKEN);
841 }
842
843 /* Grab a pointer to the context part of the token */
844 remote_ctx = &tok.ver.dh_version_u.
845 body.dh_token_body_desc_u.accept_context.cntx;
846
847 /* Make sure this is from the remote and for us */
848 if (strcmp(remote_ctx->remote, dh_gss_ctx->remote) ||
849 strcmp(remote_ctx->local, dh_gss_ctx->local)) {
850 xdr_free(xdr_dh_token_desc, (char *)&tok);
851 return (GSS_S_DEFECTIVE_TOKEN);
852 }
853
854 /* Make sure if the optional channel_bindings are the same */
855 remote_chan = DH2GSS_channel_binding(&remote_chan_desc,
856 remote_ctx->channel);
857 if (!gss_chanbind_cmp(channel, remote_chan)) {
858 xdr_free(xdr_dh_token_desc, (char *)&tok);
859 return (GSS_S_BAD_BINDINGS);
860 }
861
862 /* Update the context flags with what the remote will accept */
863 dh_gss_ctx->flags = remote_ctx->flags;
864
865 /* We now have an established context */
866 dh_gss_ctx->state = ESTABLISHED;
867
868 /* Release the deserialized token, tok */
869 xdr_free(xdr_dh_token_desc, (char *)&tok);
870
871 return (GSS_S_COMPLETE);
872 }
873
874 /*
875 * This is the Diffie-Hellman mechanism entry point for the
876 * gss_int_sec context. See RFC 2078 for details. This
877 * routine creates a new context or continues a previously created
878 * context if mutual authentication had been requested on the orignal
879 * context. The first call to this routine should set *context to
880 * GSS_C_NO_CONTEXT and input_token to GSS_C_NO_BUFFER or input_token->length
881 * to zero. To continue a context in the case of mutual authentication
882 * gss_ctx should point to the initial context and input_token should point
883 * to the token received from the remote. The established context will
884 * be return via the *context paramater in all cases.
885 */
886
887
888 OM_uint32
__dh_gss_init_sec_context(void * ctx,OM_uint32 * minor,gss_cred_id_t cred,gss_ctx_id_t * context,gss_name_t target,gss_OID mech,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t channel,gss_buffer_t input_token,gss_OID * mech_rec,gss_buffer_t output_token,OM_uint32 * flags_rec,OM_uint32 * time_rec)889 __dh_gss_init_sec_context(void *ctx, /* Per Mechananism context */
890 OM_uint32 *minor, /* Mech status */
891 gss_cred_id_t cred, /* Local credentials */
892 gss_ctx_id_t *context, /* The context to create */
893 gss_name_t target, /* The server to talk to */
894 gss_OID mech, /* The mechanism to use */
895 OM_uint32 req_flags, /* Requested context flags */
896 OM_uint32 time_req, /* Requested life time */
897 gss_channel_bindings_t channel, /* Local bindings */
898 gss_buffer_t input_token, /* Token from remote */
899 gss_OID *mech_rec, /* Optional mech to return */
900 gss_buffer_t output_token, /* Token for remote */
901 OM_uint32 *flags_rec, /* Actual flags received */
902 OM_uint32 *time_rec /* Actual life time received */)
903 {
904 dh_context_t cntx = (dh_context_t)ctx;
905 dh_gss_context_t dh_gss_ctx = (dh_gss_context_t)*context;
906 dh_principal netname;
907 dh_cred_id_t dh_cred = (dh_cred_id_t)cred;
908 OM_uint32 stat;
909
910 /* We need these */
911 if (minor == 0 || output_token == 0)
912 return (GSS_S_CALL_INACCESSIBLE_WRITE);
913
914 /* Set to sane state */
915 *minor = DH_SUCCESS;
916 output_token->length = 0;
917 output_token->value = NULL;
918 if (mech_rec)
919 *mech_rec = cntx->mech; /* Note this should not be duped. */
920
921 /* Check that were the right mechanism */
922 if ((mech != GSS_C_NULL_OID) &&
923 (!__OID_equal(mech, cntx->mech))) {
924 return (GSS_S_BAD_MECH);
925 }
926
927 /* Validate the cred and obtain our netname in the process. */
928 stat = validate_cred(cntx, minor, dh_cred, GSS_C_INITIATE, &netname);
929 if (stat != GSS_S_COMPLETE)
930 return (stat);
931
932 /* validate target name */
933 /*
934 * we could check that the target is in the proper form and
935 * possibly do a lookup up on the host part.
936 */
937
938 /* checks for new context */
939 if (dh_gss_ctx == (dh_gss_context_t)GSS_C_NO_CONTEXT) {
940
941 if (input_token != GSS_C_NO_BUFFER &&
942 input_token->length != 0)
943 return (GSS_S_DEFECTIVE_TOKEN);
944
945 /* Create a new context */
946 stat = create_context(minor, cntx, &dh_gss_ctx, netname,
947 (dh_principal)target, channel, req_flags,
948 time_req, flags_rec, time_rec,
949 output_token);
950
951 /* Set the GSS context to the Diffie-Hellman context */
952 *context = (gss_ctx_id_t)dh_gss_ctx;
953
954 } else {
955
956 /* Validate the context */
957 if ((*minor = __dh_validate_context(dh_gss_ctx)) != DH_SUCCESS)
958 return (GSS_S_NO_CONTEXT);
959
960 /* Authenticate the server */
961 stat = continue_context(minor,
962 input_token, dh_gss_ctx, channel);
963
964 }
965
966 free(netname);
967 return (stat);
968 }
969