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