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