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 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 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 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 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 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 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 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 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 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 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 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 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