accept_sec_context.c revision 178825
1/* 2 * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * Portions Copyright (c) 2004 PADL Software Pty Ltd. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "spnego/spnego_locl.h" 35 36RCSID("$Id: accept_sec_context.c 21461 2007-07-10 14:01:13Z lha $"); 37 38static OM_uint32 39send_reject (OM_uint32 *minor_status, 40 gss_buffer_t output_token) 41{ 42 NegotiationToken nt; 43 size_t size; 44 45 nt.element = choice_NegotiationToken_negTokenResp; 46 47 ALLOC(nt.u.negTokenResp.negResult, 1); 48 if (nt.u.negTokenResp.negResult == NULL) { 49 *minor_status = ENOMEM; 50 return GSS_S_FAILURE; 51 } 52 *(nt.u.negTokenResp.negResult) = reject; 53 nt.u.negTokenResp.supportedMech = NULL; 54 nt.u.negTokenResp.responseToken = NULL; 55 nt.u.negTokenResp.mechListMIC = NULL; 56 57 ASN1_MALLOC_ENCODE(NegotiationToken, 58 output_token->value, output_token->length, &nt, 59 &size, *minor_status); 60 free_NegotiationToken(&nt); 61 if (*minor_status != 0) 62 return GSS_S_FAILURE; 63 64 return GSS_S_BAD_MECH; 65} 66 67static OM_uint32 68acceptor_approved(gss_name_t target_name, gss_OID mech) 69{ 70 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 71 gss_OID_set oidset; 72 OM_uint32 junk, ret; 73 74 if (target_name == GSS_C_NO_NAME) 75 return GSS_S_COMPLETE; 76 77 gss_create_empty_oid_set(&junk, &oidset); 78 gss_add_oid_set_member(&junk, mech, &oidset); 79 80 ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset, 81 GSS_C_ACCEPT, &cred, NULL, NULL); 82 gss_release_oid_set(&junk, &oidset); 83 if (ret != GSS_S_COMPLETE) 84 return ret; 85 gss_release_cred(&junk, &cred); 86 87 return GSS_S_COMPLETE; 88} 89 90static OM_uint32 91send_supported_mechs (OM_uint32 *minor_status, 92 gss_buffer_t output_token) 93{ 94 NegotiationTokenWin nt; 95 char hostname[MAXHOSTNAMELEN + 1], *p; 96 gss_buffer_desc name_buf; 97 gss_OID name_type; 98 gss_name_t target_princ; 99 gss_name_t canon_princ; 100 OM_uint32 minor; 101 size_t buf_len; 102 gss_buffer_desc data; 103 OM_uint32 ret; 104 105 memset(&nt, 0, sizeof(nt)); 106 107 nt.element = choice_NegotiationTokenWin_negTokenInit; 108 nt.u.negTokenInit.reqFlags = NULL; 109 nt.u.negTokenInit.mechToken = NULL; 110 nt.u.negTokenInit.negHints = NULL; 111 112 ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME, 113 acceptor_approved, 1, NULL, 114 &nt.u.negTokenInit.mechTypes, NULL); 115 if (ret != GSS_S_COMPLETE) { 116 return ret; 117 } 118 119 memset(&target_princ, 0, sizeof(target_princ)); 120 if (gethostname(hostname, sizeof(hostname) - 2) != 0) { 121 *minor_status = errno; 122 free_NegotiationTokenWin(&nt); 123 return GSS_S_FAILURE; 124 } 125 hostname[sizeof(hostname) - 1] = '\0'; 126 127 /* Send the constructed SAM name for this host */ 128 for (p = hostname; *p != '\0' && *p != '.'; p++) { 129 *p = toupper((unsigned char)*p); 130 } 131 *p++ = '$'; 132 *p = '\0'; 133 134 name_buf.length = strlen(hostname); 135 name_buf.value = hostname; 136 137 ret = gss_import_name(minor_status, &name_buf, 138 GSS_C_NO_OID, 139 &target_princ); 140 if (ret != GSS_S_COMPLETE) { 141 free_NegotiationTokenWin(&nt); 142 return ret; 143 } 144 145 name_buf.length = 0; 146 name_buf.value = NULL; 147 148 /* Canonicalize the name using the preferred mechanism */ 149 ret = gss_canonicalize_name(minor_status, 150 target_princ, 151 GSS_C_NO_OID, 152 &canon_princ); 153 if (ret != GSS_S_COMPLETE) { 154 free_NegotiationTokenWin(&nt); 155 gss_release_name(&minor, &target_princ); 156 return ret; 157 } 158 159 ret = gss_display_name(minor_status, canon_princ, 160 &name_buf, &name_type); 161 if (ret != GSS_S_COMPLETE) { 162 free_NegotiationTokenWin(&nt); 163 gss_release_name(&minor, &canon_princ); 164 gss_release_name(&minor, &target_princ); 165 return ret; 166 } 167 168 gss_release_name(&minor, &canon_princ); 169 gss_release_name(&minor, &target_princ); 170 171 ALLOC(nt.u.negTokenInit.negHints, 1); 172 if (nt.u.negTokenInit.negHints == NULL) { 173 *minor_status = ENOMEM; 174 gss_release_buffer(&minor, &name_buf); 175 free_NegotiationTokenWin(&nt); 176 return GSS_S_FAILURE; 177 } 178 179 ALLOC(nt.u.negTokenInit.negHints->hintName, 1); 180 if (nt.u.negTokenInit.negHints->hintName == NULL) { 181 *minor_status = ENOMEM; 182 gss_release_buffer(&minor, &name_buf); 183 free_NegotiationTokenWin(&nt); 184 return GSS_S_FAILURE; 185 } 186 187 *(nt.u.negTokenInit.negHints->hintName) = name_buf.value; 188 name_buf.value = NULL; 189 nt.u.negTokenInit.negHints->hintAddress = NULL; 190 191 ASN1_MALLOC_ENCODE(NegotiationTokenWin, 192 data.value, data.length, &nt, &buf_len, ret); 193 free_NegotiationTokenWin(&nt); 194 if (ret) { 195 return ret; 196 } 197 if (data.length != buf_len) 198 abort(); 199 200 ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); 201 202 free (data.value); 203 204 if (ret != GSS_S_COMPLETE) 205 return ret; 206 207 *minor_status = 0; 208 209 return GSS_S_CONTINUE_NEEDED; 210} 211 212static OM_uint32 213send_accept (OM_uint32 *minor_status, 214 gssspnego_ctx context_handle, 215 gss_buffer_t mech_token, 216 int initial_response, 217 gss_buffer_t mech_buf, 218 gss_buffer_t output_token) 219{ 220 NegotiationToken nt; 221 OM_uint32 ret; 222 gss_buffer_desc mech_mic_buf; 223 size_t size; 224 225 memset(&nt, 0, sizeof(nt)); 226 227 nt.element = choice_NegotiationToken_negTokenResp; 228 229 ALLOC(nt.u.negTokenResp.negResult, 1); 230 if (nt.u.negTokenResp.negResult == NULL) { 231 *minor_status = ENOMEM; 232 return GSS_S_FAILURE; 233 } 234 235 if (context_handle->open) { 236 if (mech_token != GSS_C_NO_BUFFER 237 && mech_token->length != 0 238 && mech_buf != GSS_C_NO_BUFFER) 239 *(nt.u.negTokenResp.negResult) = accept_incomplete; 240 else 241 *(nt.u.negTokenResp.negResult) = accept_completed; 242 } else { 243 if (initial_response && context_handle->require_mic) 244 *(nt.u.negTokenResp.negResult) = request_mic; 245 else 246 *(nt.u.negTokenResp.negResult) = accept_incomplete; 247 } 248 249 if (initial_response) { 250 ALLOC(nt.u.negTokenResp.supportedMech, 1); 251 if (nt.u.negTokenResp.supportedMech == NULL) { 252 free_NegotiationToken(&nt); 253 *minor_status = ENOMEM; 254 return GSS_S_FAILURE; 255 } 256 257 ret = der_get_oid(context_handle->preferred_mech_type->elements, 258 context_handle->preferred_mech_type->length, 259 nt.u.negTokenResp.supportedMech, 260 NULL); 261 if (ret) { 262 free_NegotiationToken(&nt); 263 *minor_status = ENOMEM; 264 return GSS_S_FAILURE; 265 } 266 } else { 267 nt.u.negTokenResp.supportedMech = NULL; 268 } 269 270 if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) { 271 ALLOC(nt.u.negTokenResp.responseToken, 1); 272 if (nt.u.negTokenResp.responseToken == NULL) { 273 free_NegotiationToken(&nt); 274 *minor_status = ENOMEM; 275 return GSS_S_FAILURE; 276 } 277 nt.u.negTokenResp.responseToken->length = mech_token->length; 278 nt.u.negTokenResp.responseToken->data = mech_token->value; 279 mech_token->length = 0; 280 mech_token->value = NULL; 281 } else { 282 nt.u.negTokenResp.responseToken = NULL; 283 } 284 285 if (mech_buf != GSS_C_NO_BUFFER) { 286 ret = gss_get_mic(minor_status, 287 context_handle->negotiated_ctx_id, 288 0, 289 mech_buf, 290 &mech_mic_buf); 291 if (ret == GSS_S_COMPLETE) { 292 ALLOC(nt.u.negTokenResp.mechListMIC, 1); 293 if (nt.u.negTokenResp.mechListMIC == NULL) { 294 gss_release_buffer(minor_status, &mech_mic_buf); 295 free_NegotiationToken(&nt); 296 *minor_status = ENOMEM; 297 return GSS_S_FAILURE; 298 } 299 nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length; 300 nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value; 301 } else if (ret == GSS_S_UNAVAILABLE) { 302 nt.u.negTokenResp.mechListMIC = NULL; 303 } else { 304 free_NegotiationToken(&nt); 305 return ret; 306 } 307 308 } else 309 nt.u.negTokenResp.mechListMIC = NULL; 310 311 ASN1_MALLOC_ENCODE(NegotiationToken, 312 output_token->value, output_token->length, 313 &nt, &size, ret); 314 if (ret) { 315 free_NegotiationToken(&nt); 316 *minor_status = ret; 317 return GSS_S_FAILURE; 318 } 319 320 /* 321 * The response should not be encapsulated, because 322 * it is a SubsequentContextToken (note though RFC 1964 323 * specifies encapsulation for all _Kerberos_ tokens). 324 */ 325 326 if (*(nt.u.negTokenResp.negResult) == accept_completed) 327 ret = GSS_S_COMPLETE; 328 else 329 ret = GSS_S_CONTINUE_NEEDED; 330 free_NegotiationToken(&nt); 331 return ret; 332} 333 334 335static OM_uint32 336verify_mechlist_mic 337 (OM_uint32 *minor_status, 338 gssspnego_ctx context_handle, 339 gss_buffer_t mech_buf, 340 heim_octet_string *mechListMIC 341 ) 342{ 343 OM_uint32 ret; 344 gss_buffer_desc mic_buf; 345 346 if (context_handle->verified_mic) { 347 /* This doesn't make sense, we've already verified it? */ 348 *minor_status = 0; 349 return GSS_S_DUPLICATE_TOKEN; 350 } 351 352 if (mechListMIC == NULL) { 353 *minor_status = 0; 354 return GSS_S_DEFECTIVE_TOKEN; 355 } 356 357 mic_buf.length = mechListMIC->length; 358 mic_buf.value = mechListMIC->data; 359 360 ret = gss_verify_mic(minor_status, 361 context_handle->negotiated_ctx_id, 362 mech_buf, 363 &mic_buf, 364 NULL); 365 366 if (ret != GSS_S_COMPLETE) 367 ret = GSS_S_DEFECTIVE_TOKEN; 368 369 return ret; 370} 371 372static OM_uint32 373select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p, 374 gss_OID *mech_p) 375{ 376 char mechbuf[64]; 377 size_t mech_len; 378 gss_OID_desc oid; 379 OM_uint32 ret, junk; 380 381 ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1, 382 sizeof(mechbuf), 383 mechType, 384 &mech_len); 385 if (ret) { 386 return GSS_S_DEFECTIVE_TOKEN; 387 } 388 389 oid.length = mech_len; 390 oid.elements = mechbuf + sizeof(mechbuf) - mech_len; 391 392 if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) { 393 return GSS_S_BAD_MECH; 394 } 395 396 *minor_status = 0; 397 398 /* Translate broken MS Kebreros OID */ 399 if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) { 400 gssapi_mech_interface mech; 401 402 mech = __gss_get_mechanism(&_gss_spnego_krb5_mechanism_oid_desc); 403 if (mech == NULL) 404 return GSS_S_BAD_MECH; 405 406 ret = gss_duplicate_oid(minor_status, 407 &_gss_spnego_mskrb_mechanism_oid_desc, 408 mech_p); 409 } else { 410 gssapi_mech_interface mech; 411 412 mech = __gss_get_mechanism(&oid); 413 if (mech == NULL) 414 return GSS_S_BAD_MECH; 415 416 ret = gss_duplicate_oid(minor_status, 417 &mech->gm_mech_oid, 418 mech_p); 419 } 420 421 if (verify_p) { 422 gss_name_t name = GSS_C_NO_NAME; 423 gss_buffer_desc namebuf; 424 char *str = NULL, *host, hostname[MAXHOSTNAMELEN]; 425 426 host = getenv("GSSAPI_SPNEGO_NAME"); 427 if (host == NULL || issuid()) { 428 if (gethostname(hostname, sizeof(hostname)) != 0) { 429 *minor_status = errno; 430 return GSS_S_FAILURE; 431 } 432 asprintf(&str, "host@%s", hostname); 433 host = str; 434 } 435 436 namebuf.length = strlen(host); 437 namebuf.value = host; 438 439 ret = gss_import_name(minor_status, &namebuf, 440 GSS_C_NT_HOSTBASED_SERVICE, &name); 441 if (str) 442 free(str); 443 if (ret != GSS_S_COMPLETE) 444 return ret; 445 446 ret = acceptor_approved(name, *mech_p); 447 gss_release_name(&junk, &name); 448 } 449 450 return ret; 451} 452 453 454static OM_uint32 455acceptor_complete(OM_uint32 * minor_status, 456 gssspnego_ctx ctx, 457 int *get_mic, 458 gss_buffer_t mech_buf, 459 gss_buffer_t mech_input_token, 460 gss_buffer_t mech_output_token, 461 heim_octet_string *mic, 462 gss_buffer_t output_token) 463{ 464 OM_uint32 ret; 465 int require_mic, verify_mic; 466 gss_buffer_desc buf; 467 468 buf.length = 0; 469 buf.value = NULL; 470 471 ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); 472 if (ret) 473 return ret; 474 475 ctx->require_mic = require_mic; 476 477 if (mic != NULL) 478 require_mic = 1; 479 480 if (ctx->open && require_mic) { 481 if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ 482 verify_mic = 1; 483 *get_mic = 0; 484 } else if (mech_output_token != GSS_C_NO_BUFFER && 485 mech_output_token->length == 0) { /* Odd */ 486 *get_mic = verify_mic = 1; 487 } else { /* Even/One */ 488 verify_mic = 0; 489 *get_mic = 1; 490 } 491 492 if (verify_mic || get_mic) { 493 int eret; 494 size_t buf_len; 495 496 ASN1_MALLOC_ENCODE(MechTypeList, 497 mech_buf->value, mech_buf->length, 498 &ctx->initiator_mech_types, &buf_len, eret); 499 if (eret) { 500 *minor_status = eret; 501 return GSS_S_FAILURE; 502 } 503 if (buf.length != buf_len) 504 abort(); 505 } 506 507 if (verify_mic) { 508 ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic); 509 if (ret) { 510 if (get_mic) 511 send_reject (minor_status, output_token); 512 if (buf.value) 513 free(buf.value); 514 return ret; 515 } 516 ctx->verified_mic = 1; 517 } 518 if (buf.value) 519 free(buf.value); 520 521 } else 522 *get_mic = verify_mic = 0; 523 524 return GSS_S_COMPLETE; 525} 526 527 528static OM_uint32 529acceptor_start 530 (OM_uint32 * minor_status, 531 gss_ctx_id_t * context_handle, 532 const gss_cred_id_t acceptor_cred_handle, 533 const gss_buffer_t input_token_buffer, 534 const gss_channel_bindings_t input_chan_bindings, 535 gss_name_t * src_name, 536 gss_OID * mech_type, 537 gss_buffer_t output_token, 538 OM_uint32 * ret_flags, 539 OM_uint32 * time_rec, 540 gss_cred_id_t *delegated_cred_handle 541 ) 542{ 543 OM_uint32 ret, junk, minor; 544 NegotiationToken nt; 545 size_t nt_len; 546 NegTokenInit *ni; 547 int i; 548 gss_buffer_desc data; 549 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 550 gss_buffer_desc mech_output_token; 551 gss_buffer_desc mech_buf; 552 gss_OID preferred_mech_type = GSS_C_NO_OID; 553 gssspnego_ctx ctx; 554 gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle; 555 int get_mic = 0; 556 int first_ok = 0; 557 558 mech_output_token.value = NULL; 559 mech_output_token.length = 0; 560 mech_buf.value = NULL; 561 562 if (input_token_buffer->length == 0) 563 return send_supported_mechs (minor_status, output_token); 564 565 ret = _gss_spnego_alloc_sec_context(minor_status, context_handle); 566 if (ret != GSS_S_COMPLETE) 567 return ret; 568 569 ctx = (gssspnego_ctx)*context_handle; 570 571 /* 572 * The GSS-API encapsulation is only present on the initial 573 * context token (negTokenInit). 574 */ 575 ret = gss_decapsulate_token (input_token_buffer, 576 GSS_SPNEGO_MECHANISM, 577 &data); 578 if (ret) 579 return ret; 580 581 ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len); 582 gss_release_buffer(minor_status, &data); 583 if (ret) { 584 *minor_status = ret; 585 return GSS_S_DEFECTIVE_TOKEN; 586 } 587 if (nt.element != choice_NegotiationToken_negTokenInit) { 588 *minor_status = 0; 589 return GSS_S_DEFECTIVE_TOKEN; 590 } 591 ni = &nt.u.negTokenInit; 592 593 if (ni->mechTypes.len < 1) { 594 free_NegotiationToken(&nt); 595 *minor_status = 0; 596 return GSS_S_DEFECTIVE_TOKEN; 597 } 598 599 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 600 601 ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types); 602 if (ret) { 603 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 604 free_NegotiationToken(&nt); 605 *minor_status = ret; 606 return GSS_S_FAILURE; 607 } 608 609 /* 610 * First we try the opportunistic token if we have support for it, 611 * don't try to verify we have credential for the token, 612 * gss_accept_sec_context will (hopefully) tell us that. 613 * If that failes, 614 */ 615 616 ret = select_mech(minor_status, 617 &ni->mechTypes.val[0], 618 0, 619 &preferred_mech_type); 620 621 if (ret == 0 && ni->mechToken != NULL) { 622 gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL; 623 gss_cred_id_t mech_cred; 624 gss_buffer_desc ibuf; 625 626 ibuf.length = ni->mechToken->length; 627 ibuf.value = ni->mechToken->data; 628 mech_input_token = &ibuf; 629 630 if (acceptor_cred != NULL) 631 mech_cred = acceptor_cred->negotiated_cred_id; 632 else 633 mech_cred = GSS_C_NO_CREDENTIAL; 634 635 if (ctx->mech_src_name != GSS_C_NO_NAME) 636 gss_release_name(&minor, &ctx->mech_src_name); 637 638 if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL) 639 _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id); 640 641 ret = gss_accept_sec_context(&minor, 642 &ctx->negotiated_ctx_id, 643 mech_cred, 644 mech_input_token, 645 input_chan_bindings, 646 &ctx->mech_src_name, 647 &ctx->negotiated_mech_type, 648 &mech_output_token, 649 &ctx->mech_flags, 650 &ctx->mech_time_rec, 651 &mech_delegated_cred); 652 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 653 ctx->preferred_mech_type = preferred_mech_type; 654 ctx->negotiated_mech_type = preferred_mech_type; 655 if (ret == GSS_S_COMPLETE) 656 ctx->open = 1; 657 658 if (mech_delegated_cred && delegated_cred_handle) 659 ret = _gss_spnego_alloc_cred(minor_status, 660 mech_delegated_cred, 661 delegated_cred_handle); 662 else 663 gss_release_cred(&junk, &mech_delegated_cred); 664 665 ret = acceptor_complete(minor_status, 666 ctx, 667 &get_mic, 668 &mech_buf, 669 mech_input_token, 670 &mech_output_token, 671 ni->mechListMIC, 672 output_token); 673 if (ret != GSS_S_COMPLETE) 674 goto out; 675 676 first_ok = 1; 677 } 678 } 679 680 /* 681 * If opportunistic token failed, lets try the other mechs. 682 */ 683 684 if (!first_ok) { 685 686 /* Call glue layer to find first mech we support */ 687 for (i = 1; i < ni->mechTypes.len; ++i) { 688 ret = select_mech(minor_status, 689 &ni->mechTypes.val[i], 690 1, 691 &preferred_mech_type); 692 if (ret == 0) 693 break; 694 } 695 if (preferred_mech_type == GSS_C_NO_OID) { 696 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 697 free_NegotiationToken(&nt); 698 return GSS_S_BAD_MECH; 699 } 700 701 ctx->preferred_mech_type = preferred_mech_type; 702 ctx->negotiated_mech_type = preferred_mech_type; 703 } 704 705 /* 706 * The initial token always have a response 707 */ 708 709 ret = send_accept (minor_status, 710 ctx, 711 &mech_output_token, 712 1, 713 get_mic ? &mech_buf : NULL, 714 output_token); 715 if (ret) 716 goto out; 717 718out: 719 if (mech_output_token.value != NULL) 720 gss_release_buffer(&minor, &mech_output_token); 721 if (mech_buf.value != NULL) { 722 free(mech_buf.value); 723 mech_buf.value = NULL; 724 } 725 free_NegotiationToken(&nt); 726 727 728 if (ret == GSS_S_COMPLETE) { 729 if (src_name != NULL && ctx->mech_src_name != NULL) { 730 spnego_name name; 731 732 name = calloc(1, sizeof(*name)); 733 if (name) { 734 name->mech = ctx->mech_src_name; 735 ctx->mech_src_name = NULL; 736 *src_name = (gss_name_t)name; 737 } 738 } 739 if (delegated_cred_handle != NULL) { 740 *delegated_cred_handle = ctx->delegated_cred_id; 741 ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL; 742 } 743 } 744 745 if (mech_type != NULL) 746 *mech_type = ctx->negotiated_mech_type; 747 if (ret_flags != NULL) 748 *ret_flags = ctx->mech_flags; 749 if (time_rec != NULL) 750 *time_rec = ctx->mech_time_rec; 751 752 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 753 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 754 return ret; 755 } 756 757 _gss_spnego_internal_delete_sec_context(&minor, context_handle, 758 GSS_C_NO_BUFFER); 759 760 return ret; 761} 762 763 764static OM_uint32 765acceptor_continue 766 (OM_uint32 * minor_status, 767 gss_ctx_id_t * context_handle, 768 const gss_cred_id_t acceptor_cred_handle, 769 const gss_buffer_t input_token_buffer, 770 const gss_channel_bindings_t input_chan_bindings, 771 gss_name_t * src_name, 772 gss_OID * mech_type, 773 gss_buffer_t output_token, 774 OM_uint32 * ret_flags, 775 OM_uint32 * time_rec, 776 gss_cred_id_t *delegated_cred_handle 777 ) 778{ 779 OM_uint32 ret, ret2, minor; 780 NegotiationToken nt; 781 size_t nt_len; 782 NegTokenResp *na; 783 unsigned int negResult = accept_incomplete; 784 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 785 gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; 786 gss_buffer_desc mech_buf; 787 gssspnego_ctx ctx; 788 gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle; 789 790 mech_buf.value = NULL; 791 792 ctx = (gssspnego_ctx)*context_handle; 793 794 /* 795 * The GSS-API encapsulation is only present on the initial 796 * context token (negTokenInit). 797 */ 798 799 ret = decode_NegotiationToken(input_token_buffer->value, 800 input_token_buffer->length, 801 &nt, &nt_len); 802 if (ret) { 803 *minor_status = ret; 804 return GSS_S_DEFECTIVE_TOKEN; 805 } 806 if (nt.element != choice_NegotiationToken_negTokenResp) { 807 *minor_status = 0; 808 return GSS_S_DEFECTIVE_TOKEN; 809 } 810 na = &nt.u.negTokenResp; 811 812 if (na->negResult != NULL) { 813 negResult = *(na->negResult); 814 } 815 816 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 817 818 { 819 gss_buffer_desc ibuf, obuf; 820 int require_mic, get_mic = 0; 821 int require_response; 822 heim_octet_string *mic; 823 824 if (na->responseToken != NULL) { 825 ibuf.length = na->responseToken->length; 826 ibuf.value = na->responseToken->data; 827 mech_input_token = &ibuf; 828 } else { 829 ibuf.value = NULL; 830 ibuf.length = 0; 831 } 832 833 if (mech_input_token != GSS_C_NO_BUFFER) { 834 gss_cred_id_t mech_cred; 835 gss_cred_id_t mech_delegated_cred; 836 gss_cred_id_t *mech_delegated_cred_p; 837 838 if (acceptor_cred != NULL) 839 mech_cred = acceptor_cred->negotiated_cred_id; 840 else 841 mech_cred = GSS_C_NO_CREDENTIAL; 842 843 if (delegated_cred_handle != NULL) { 844 mech_delegated_cred = GSS_C_NO_CREDENTIAL; 845 mech_delegated_cred_p = &mech_delegated_cred; 846 } else { 847 mech_delegated_cred_p = NULL; 848 } 849 850 if (ctx->mech_src_name != GSS_C_NO_NAME) 851 gss_release_name(&minor, &ctx->mech_src_name); 852 853 if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL) 854 _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id); 855 856 ret = gss_accept_sec_context(&minor, 857 &ctx->negotiated_ctx_id, 858 mech_cred, 859 mech_input_token, 860 input_chan_bindings, 861 &ctx->mech_src_name, 862 &ctx->negotiated_mech_type, 863 &obuf, 864 &ctx->mech_flags, 865 &ctx->mech_time_rec, 866 mech_delegated_cred_p); 867 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 868 if (mech_delegated_cred_p != NULL && 869 mech_delegated_cred != GSS_C_NO_CREDENTIAL) { 870 ret2 = _gss_spnego_alloc_cred(minor_status, 871 mech_delegated_cred, 872 &ctx->delegated_cred_id); 873 if (ret2 != GSS_S_COMPLETE) 874 ret = ret2; 875 } 876 mech_output_token = &obuf; 877 } 878 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { 879 free_NegotiationToken(&nt); 880 send_reject (minor_status, output_token); 881 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 882 return ret; 883 } 884 if (ret == GSS_S_COMPLETE) 885 ctx->open = 1; 886 } else 887 ret = GSS_S_COMPLETE; 888 889 ret2 = _gss_spnego_require_mechlist_mic(minor_status, 890 ctx, 891 &require_mic); 892 if (ret2) 893 goto out; 894 895 ctx->require_mic = require_mic; 896 897 mic = na->mechListMIC; 898 if (mic != NULL) 899 require_mic = 1; 900 901 if (ret == GSS_S_COMPLETE) 902 ret = acceptor_complete(minor_status, 903 ctx, 904 &get_mic, 905 &mech_buf, 906 mech_input_token, 907 mech_output_token, 908 na->mechListMIC, 909 output_token); 910 911 if (ctx->mech_flags & GSS_C_DCE_STYLE) 912 require_response = (negResult != accept_completed); 913 else 914 require_response = 0; 915 916 /* 917 * Check whether we need to send a result: there should be only 918 * one accept_completed response sent in the entire negotiation 919 */ 920 if ((mech_output_token != GSS_C_NO_BUFFER && 921 mech_output_token->length != 0) 922 || (ctx->open && negResult == accept_incomplete) 923 || require_response 924 || get_mic) { 925 ret2 = send_accept (minor_status, 926 ctx, 927 mech_output_token, 928 0, 929 get_mic ? &mech_buf : NULL, 930 output_token); 931 if (ret2) 932 goto out; 933 } 934 935 out: 936 if (ret2 != GSS_S_COMPLETE) 937 ret = ret2; 938 if (mech_output_token != NULL) 939 gss_release_buffer(&minor, mech_output_token); 940 if (mech_buf.value != NULL) 941 free(mech_buf.value); 942 free_NegotiationToken(&nt); 943 } 944 945 if (ret == GSS_S_COMPLETE) { 946 if (src_name != NULL && ctx->mech_src_name != NULL) { 947 spnego_name name; 948 949 name = calloc(1, sizeof(*name)); 950 if (name) { 951 name->mech = ctx->mech_src_name; 952 ctx->mech_src_name = NULL; 953 *src_name = (gss_name_t)name; 954 } 955 } 956 if (delegated_cred_handle != NULL) { 957 *delegated_cred_handle = ctx->delegated_cred_id; 958 ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL; 959 } 960 } 961 962 if (mech_type != NULL) 963 *mech_type = ctx->negotiated_mech_type; 964 if (ret_flags != NULL) 965 *ret_flags = ctx->mech_flags; 966 if (time_rec != NULL) 967 *time_rec = ctx->mech_time_rec; 968 969 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 970 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 971 return ret; 972 } 973 974 _gss_spnego_internal_delete_sec_context(&minor, context_handle, 975 GSS_C_NO_BUFFER); 976 977 return ret; 978} 979 980OM_uint32 981_gss_spnego_accept_sec_context 982 (OM_uint32 * minor_status, 983 gss_ctx_id_t * context_handle, 984 const gss_cred_id_t acceptor_cred_handle, 985 const gss_buffer_t input_token_buffer, 986 const gss_channel_bindings_t input_chan_bindings, 987 gss_name_t * src_name, 988 gss_OID * mech_type, 989 gss_buffer_t output_token, 990 OM_uint32 * ret_flags, 991 OM_uint32 * time_rec, 992 gss_cred_id_t *delegated_cred_handle 993 ) 994{ 995 _gss_accept_sec_context_t *func; 996 997 *minor_status = 0; 998 999 output_token->length = 0; 1000 output_token->value = NULL; 1001 1002 if (src_name != NULL) 1003 *src_name = GSS_C_NO_NAME; 1004 if (mech_type != NULL) 1005 *mech_type = GSS_C_NO_OID; 1006 if (ret_flags != NULL) 1007 *ret_flags = 0; 1008 if (time_rec != NULL) 1009 *time_rec = 0; 1010 if (delegated_cred_handle != NULL) 1011 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 1012 1013 1014 if (*context_handle == GSS_C_NO_CONTEXT) 1015 func = acceptor_start; 1016 else 1017 func = acceptor_continue; 1018 1019 1020 return (*func)(minor_status, context_handle, acceptor_cred_handle, 1021 input_token_buffer, input_chan_bindings, 1022 src_name, mech_type, output_token, ret_flags, 1023 time_rec, delegated_cred_handle); 1024} 1025