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_locl.h" 35 36static OM_uint32 37send_reject (OM_uint32 *minor_status, 38 gss_buffer_t output_token) 39{ 40 NegotiationToken nt; 41 size_t size; 42 43 nt.element = choice_NegotiationToken_negTokenResp; 44 45 ALLOC(nt.u.negTokenResp.negResult, 1); 46 if (nt.u.negTokenResp.negResult == NULL) { 47 *minor_status = ENOMEM; 48 return GSS_S_FAILURE; 49 } 50 *(nt.u.negTokenResp.negResult) = reject; 51 nt.u.negTokenResp.supportedMech = NULL; 52 nt.u.negTokenResp.responseToken = NULL; 53 nt.u.negTokenResp.mechListMIC = NULL; 54 55 ASN1_MALLOC_ENCODE(NegotiationToken, 56 output_token->value, output_token->length, &nt, 57 &size, *minor_status); 58 free_NegotiationToken(&nt); 59 if (*minor_status != 0) 60 return GSS_S_FAILURE; 61 62 return GSS_S_BAD_MECH; 63} 64 65static OM_uint32 66acceptor_approved(void *userptr, 67 gss_name_t target_name, 68 const gss_cred_id_t cred_handle, 69 gss_OID mech) 70{ 71 OM_uint32 junk, ret; 72 gss_OID_set oidset; 73 74 if (cred_handle) { 75 int present = 0; 76 77 ret = gss_inquire_cred(&junk, 78 cred_handle, 79 NULL, 80 NULL, 81 NULL, 82 &oidset); 83 if (ret != GSS_S_COMPLETE) 84 return ret; 85 86 ret = gss_test_oid_set_member(&junk, mech, oidset, &present); 87 gss_release_oid_set(&junk, &oidset); 88 89 if (ret != GSS_S_COMPLETE || present == 0) 90 return GSS_S_FAILURE; 91 92 } else { 93 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 94 95 if (target_name == GSS_C_NO_NAME) 96 return GSS_S_COMPLETE; 97 98 gss_create_empty_oid_set(&junk, &oidset); 99 gss_add_oid_set_member(&junk, mech, &oidset); 100 101 ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset, 102 GSS_C_ACCEPT, &cred, NULL, NULL); 103 gss_release_oid_set(&junk, &oidset); 104 if (ret != GSS_S_COMPLETE) 105 return ret; 106 gss_release_cred(&junk, &cred); 107 } 108 109 return GSS_S_COMPLETE; 110} 111 112static OM_uint32 113send_supported_mechs (OM_uint32 *minor_status, 114 gss_buffer_t output_token) 115{ 116 NegotiationTokenWin nt; 117 size_t buf_len = 0; 118 gss_buffer_desc data; 119 OM_uint32 ret; 120 121 memset(&nt, 0, sizeof(nt)); 122 123 nt.element = choice_NegotiationTokenWin_negTokenInit; 124 nt.u.negTokenInit.reqFlags = NULL; 125 nt.u.negTokenInit.mechToken = NULL; 126 nt.u.negTokenInit.negHints = NULL; 127 128 ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME, 129 acceptor_approved, NULL, 1, NULL, 130 &nt.u.negTokenInit.mechTypes, NULL); 131 if (ret != GSS_S_COMPLETE) { 132 return ret; 133 } 134 135 ALLOC(nt.u.negTokenInit.negHints, 1); 136 if (nt.u.negTokenInit.negHints == NULL) { 137 *minor_status = ENOMEM; 138 free_NegotiationTokenWin(&nt); 139 return GSS_S_FAILURE; 140 } 141 142 ALLOC(nt.u.negTokenInit.negHints->hintName, 1); 143 if (nt.u.negTokenInit.negHints->hintName == NULL) { 144 *minor_status = ENOMEM; 145 free_NegotiationTokenWin(&nt); 146 return GSS_S_FAILURE; 147 } 148 149 *nt.u.negTokenInit.negHints->hintName = strdup("not_defined_in_RFC4178@please_ignore"); 150 nt.u.negTokenInit.negHints->hintAddress = NULL; 151 152 ASN1_MALLOC_ENCODE(NegotiationTokenWin, 153 data.value, data.length, &nt, &buf_len, ret); 154 free_NegotiationTokenWin(&nt); 155 if (ret) { 156 *minor_status = ret; 157 return GSS_S_FAILURE; 158 } 159 if (data.length != buf_len) { 160 abort(); 161 UNREACHABLE(return GSS_S_FAILURE); 162 } 163 164 ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); 165 166 free (data.value); 167 168 if (ret != GSS_S_COMPLETE) 169 return ret; 170 171 *minor_status = 0; 172 173 return GSS_S_CONTINUE_NEEDED; 174} 175 176static OM_uint32 177send_accept (OM_uint32 *minor_status, 178 gssspnego_ctx context_handle, 179 gss_buffer_t mech_token, 180 int initial_response, 181 gss_buffer_t mech_buf, 182 gss_buffer_t output_token) 183{ 184 NegotiationToken nt; 185 OM_uint32 ret; 186 gss_buffer_desc mech_mic_buf; 187 size_t size; 188 189 memset(&nt, 0, sizeof(nt)); 190 191 nt.element = choice_NegotiationToken_negTokenResp; 192 193 ALLOC(nt.u.negTokenResp.negResult, 1); 194 if (nt.u.negTokenResp.negResult == NULL) { 195 *minor_status = ENOMEM; 196 return GSS_S_FAILURE; 197 } 198 199 if (context_handle->flags.open) { 200 if (mech_token != GSS_C_NO_BUFFER 201 && mech_token->length != 0 202 && mech_buf != GSS_C_NO_BUFFER) 203 *(nt.u.negTokenResp.negResult) = accept_incomplete; 204 else 205 *(nt.u.negTokenResp.negResult) = accept_completed; 206 } else { 207 if (initial_response && context_handle->flags.require_mic) 208 *(nt.u.negTokenResp.negResult) = request_mic; 209 else 210 *(nt.u.negTokenResp.negResult) = accept_incomplete; 211 } 212 213 if (initial_response && context_handle->preferred_mech_type) { 214 ALLOC(nt.u.negTokenResp.supportedMech, 1); 215 if (nt.u.negTokenResp.supportedMech == NULL) { 216 *minor_status = ENOMEM; 217 ret = GSS_S_FAILURE; 218 goto out; 219 } 220 221 ret = der_get_oid(context_handle->preferred_mech_type->elements, 222 context_handle->preferred_mech_type->length, 223 nt.u.negTokenResp.supportedMech, 224 NULL); 225 if (ret) { 226 *minor_status = ENOMEM; 227 ret = GSS_S_FAILURE; 228 goto out; 229 } 230 } else { 231 nt.u.negTokenResp.supportedMech = NULL; 232 } 233 234 if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) { 235 ALLOC(nt.u.negTokenResp.responseToken, 1); 236 if (nt.u.negTokenResp.responseToken == NULL) { 237 *minor_status = ENOMEM; 238 ret = GSS_S_FAILURE; 239 goto out; 240 } 241 nt.u.negTokenResp.responseToken->length = mech_token->length; 242 nt.u.negTokenResp.responseToken->data = mech_token->value; 243 mech_token->length = 0; 244 mech_token->value = NULL; 245 } else { 246 nt.u.negTokenResp.responseToken = NULL; 247 } 248 249 /* 250 * Can't send mechbuf until Lion is fixed to not send SIGN/SEAL, 251 * and then when we get around to verifying, can't actually handle 252 * gss_verify_mic(), only do this when its safe to omit though. 253 */ 254 if (gss_oid_equal(context_handle->negotiated_mech_type, GSS_NTLM_MECHANISM) 255 && context_handle->flags.safe_omit) 256 mech_buf = NULL; 257 258 if (mech_buf != GSS_C_NO_BUFFER) { 259 ret = gss_get_mic(minor_status, 260 context_handle->negotiated_ctx_id, 261 0, 262 mech_buf, 263 &mech_mic_buf); 264 if (ret == GSS_S_COMPLETE) { 265 ALLOC(nt.u.negTokenResp.mechListMIC, 1); 266 if (nt.u.negTokenResp.mechListMIC == NULL) { 267 gss_release_buffer(minor_status, &mech_mic_buf); 268 *minor_status = ENOMEM; 269 ret = GSS_S_FAILURE; 270 goto out; 271 } 272 nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length; 273 nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value; 274 } else if (ret == GSS_S_UNAVAILABLE) { 275 nt.u.negTokenResp.mechListMIC = NULL; 276 } else { 277 free_NegotiationToken(&nt); 278 return ret; 279 } 280 281 } else 282 nt.u.negTokenResp.mechListMIC = NULL; 283 284 ASN1_MALLOC_ENCODE(NegotiationToken, 285 output_token->value, output_token->length, 286 &nt, &size, ret); 287 if (ret) { 288 *minor_status = ENOMEM; 289 ret = GSS_S_FAILURE; 290 goto out; 291 } 292 293 /* 294 * The response should not be encapsulated, because 295 * it is a SubsequentContextToken (note though RFC 1964 296 * specifies encapsulation for all _Kerberos_ tokens). 297 */ 298 299 if (*(nt.u.negTokenResp.negResult) == accept_completed) 300 ret = GSS_S_COMPLETE; 301 else 302 ret = GSS_S_CONTINUE_NEEDED; 303 304 out: 305 free_NegotiationToken(&nt); 306 return ret; 307} 308 309/* 310 * 311 */ 312 313static OM_uint32 314select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p, gss_OID *selected_mech, gss_OID *prefered_mech) 315{ 316 char mechbuf[64]; 317 size_t mech_len; 318 gss_OID_desc oid; 319 gss_OID oidp; 320 OM_uint32 ret, junk; 321 322 ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1, 323 sizeof(mechbuf), 324 mechType, 325 &mech_len); 326 if (ret) { 327 return GSS_S_DEFECTIVE_TOKEN; 328 } 329 330 oid.length = (OM_uint32)mech_len; 331 oid.elements = mechbuf + sizeof(mechbuf) - mech_len; 332 333 if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) { 334 return GSS_S_BAD_MECH; 335 } 336 337 *minor_status = 0; 338 339 /* Translate broken MS Kebreros OID */ 340 if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) 341 oidp = &_gss_spnego_krb5_mechanism_oid_desc; 342 else 343 oidp = &oid; 344 345 346 *selected_mech = _gss_mg_support_mechanism(oidp); 347 if (*selected_mech == NULL) 348 return GSS_S_BAD_MECH; 349 350 if (prefered_mech) { 351 if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) { 352 *prefered_mech = &_gss_spnego_mskrb_mechanism_oid_desc; 353 } else { 354 *prefered_mech = *selected_mech; 355 } 356 } 357 358 if (verify_p) { 359 gss_name_t name = GSS_C_NO_NAME; 360 gss_buffer_desc namebuf; 361 char *host = NULL; 362 363 if (!issuid()) 364 host = getenv("GSSAPI_SPNEGO_NAME"); 365 if (host == NULL) 366 host = "host@"; 367 368 namebuf.length = strlen(host); 369 namebuf.value = host; 370 371 ret = gss_import_name(minor_status, &namebuf, 372 GSS_C_NT_HOSTBASED_SERVICE, &name); 373 if (ret != GSS_S_COMPLETE) 374 return ret; 375 376 ret = acceptor_approved(NULL, name, GSS_C_NO_CREDENTIAL, *selected_mech); 377 gss_release_name(&junk, &name); 378 } 379 380 return ret; 381} 382 383 384static OM_uint32 385acceptor_complete(OM_uint32 * minor_status, 386 gssspnego_ctx ctx, 387 int *get_mic, 388 gss_buffer_t mech_input_token, 389 gss_buffer_t mech_output_token, 390 heim_octet_string *mic, 391 gss_buffer_t output_token) 392{ 393 gss_buffer_desc buf; 394 OM_uint32 ret; 395 int verify_mic; 396 397 buf.length = 0; 398 buf.value = NULL; 399 400 ctx->flags.require_mic = _gss_spnego_require_mechlist_mic(ctx); 401 402 if (mic != NULL) 403 ctx->flags.require_mic = 1; 404 405 if (ctx->flags.open && ctx->flags.require_mic) { 406 if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ 407 verify_mic = 1; 408 *get_mic = 0; 409 } else if (mech_output_token != GSS_C_NO_BUFFER && 410 mech_output_token->length == 0) { /* Odd */ 411 *get_mic = verify_mic = 1; 412 } else { /* Even/One */ 413 verify_mic = 0; 414 *get_mic = 1; 415 } 416 417 if (verify_mic && mic == NULL && ctx->flags.safe_omit) { 418 /* 419 * Peer is old and didn't send a mic while we expected 420 * one, but since it safe to omit, let do that 421 */ 422 } else if (verify_mic) { 423 ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx, mic); 424 if (ret) { 425 if (*get_mic) 426 send_reject(minor_status, output_token); 427 if (buf.value) 428 free(buf.value); 429 return ret; 430 } 431 } 432 } else 433 *get_mic = 0; 434 435 return GSS_S_COMPLETE; 436} 437 438 439static OM_uint32 GSSAPI_CALLCONV 440acceptor_start 441 (OM_uint32 * minor_status, 442 gss_ctx_id_t * context_handle, 443 const gss_cred_id_t acceptor_cred_handle, 444 const gss_buffer_t input_token_buffer, 445 const gss_channel_bindings_t input_chan_bindings, 446 gss_name_t * src_name, 447 gss_OID * mech_type, 448 gss_buffer_t output_token, 449 OM_uint32 * ret_flags, 450 OM_uint32 * time_rec, 451 gss_cred_id_t *delegated_cred_handle 452 ) 453{ 454 OM_uint32 ret, junk; 455 NegotiationToken nt; 456 size_t size; 457 NegTokenInit *ni; 458 gss_buffer_desc data; 459 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 460 gss_buffer_desc mech_output_token; 461 gssspnego_ctx ctx; 462 int get_mic = 0; 463 int first_ok = 0; 464 465 memset(&nt, 0, sizeof(nt)); 466 467 mech_output_token.value = NULL; 468 mech_output_token.length = 0; 469 470 if (input_token_buffer->length == 0) 471 return send_supported_mechs (minor_status, output_token); 472 473 ret = _gss_spnego_alloc_sec_context(minor_status, context_handle); 474 if (ret != GSS_S_COMPLETE) 475 return ret; 476 477 ctx = (gssspnego_ctx)*context_handle; 478 479 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 480 481 /* 482 * The GSS-API encapsulation is only present on the initial 483 * context token (negTokenInit). 484 */ 485 ret = gss_decapsulate_token (input_token_buffer, 486 GSS_SPNEGO_MECHANISM, 487 &data); 488 if (ret) 489 goto out; 490 491 ret = decode_NegotiationToken(data.value, data.length, &nt, &size); 492 gss_release_buffer(minor_status, &data); 493 if (ret) { 494 *minor_status = ret; 495 ret = GSS_S_DEFECTIVE_TOKEN; 496 goto out; 497 } 498 if (nt.element != choice_NegotiationToken_negTokenInit) { 499 *minor_status = 0; 500 ret = GSS_S_DEFECTIVE_TOKEN; 501 goto out; 502 } 503 ni = &nt.u.negTokenInit; 504 505 if (ni->mechTypes.len < 1) { 506 *minor_status = 0; 507 ret = GSS_S_DEFECTIVE_TOKEN; 508 goto out; 509 } 510 511 { 512 MechTypeList mt; 513 int kret; 514 515 mt.len = ni->mechTypes.len; 516 mt.val = ni->mechTypes.val; 517 518 ASN1_MALLOC_ENCODE(MechTypeList, 519 ctx->NegTokenInit_mech_types.value, 520 ctx->NegTokenInit_mech_types.length, 521 &mt, &size, kret); 522 if (kret) { 523 *minor_status = kret; 524 ret = GSS_S_FAILURE; 525 goto out; 526 } 527 //XXX heim_assert(ctx->NegTokenInit_mech_types.length == size, "asn1 internal error"); 528 } 529 530 /* 531 * First we try the opportunistic token if we have support for it, 532 * don't try to verify we have credential for the token, 533 * gss_accept_sec_context() will (hopefully) tell us that. 534 * If that failes, 535 */ 536 537 ret = select_mech(minor_status, 538 &ni->mechTypes.val[0], 539 0, 540 &ctx->selected_mech_type, 541 &ctx->preferred_mech_type); 542 543 if (ret == 0 && ni->mechToken != NULL) { 544 gss_buffer_desc ibuf; 545 546 ibuf.length = ni->mechToken->length; 547 ibuf.value = ni->mechToken->data; 548 mech_input_token = &ibuf; 549 550 if (ctx->mech_src_name != GSS_C_NO_NAME) 551 gss_release_name(&junk, &ctx->mech_src_name); 552 553 ret = gss_accept_sec_context(minor_status, 554 &ctx->negotiated_ctx_id, 555 acceptor_cred_handle, 556 mech_input_token, 557 input_chan_bindings, 558 &ctx->mech_src_name, 559 &ctx->negotiated_mech_type, 560 &mech_output_token, 561 &ctx->mech_flags, 562 &ctx->mech_time_rec, 563 delegated_cred_handle); 564 565 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 566 /* 567 * Check that client sent what they said the would 568 */ 569 if (!gss_oid_equal(&ctx->negotiated_mech_type, &ctx->selected_mech_type)) { 570 _gss_mg_log(1, "client didn't send the mech they said they would"); 571 } 572 573 first_ok = 1; 574 } else { 575 gss_mg_collect_error(ctx->selected_mech_type, ret, *minor_status); 576 ctx->preferred_mech_type = GSS_C_NO_OID; 577 } 578 579 if (ret == GSS_S_COMPLETE) { 580 ret = acceptor_complete(minor_status, 581 ctx, 582 &get_mic, 583 mech_input_token, 584 &mech_output_token, 585 ni->mechListMIC, 586 output_token); 587 if (ret != GSS_S_COMPLETE) 588 goto out; 589 ctx->flags.open = 1; 590 } 591 } else { 592 *minor_status = 0; 593 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 594 return gss_mg_set_error_string(GSS_C_NO_OID, GSS_S_NO_CONTEXT, 595 *minor_status, 596 "SPNEGO acceptor didn't find a prefered mechanism"); 597 } 598 599 /* 600 * If opportunistic token failed, lets try the other mechs. 601 */ 602 603 if (!first_ok && ni->mechToken != NULL) { 604 size_t j; 605 606 /* Call glue layer to find first mech we support */ 607 for (j = 1; j < ni->mechTypes.len; ++j) { 608 ret = select_mech(&junk, 609 &ni->mechTypes.val[j], 610 1, 611 &ctx->preferred_mech_type, 612 NULL); 613 if (ret == 0) 614 break; 615 } 616 if (ctx->preferred_mech_type == GSS_C_NO_OID) { 617 heim_assert(ret != 0, "no oid and no error code?"); 618 goto out; 619 } 620 } 621 622 /* 623 * The initial token always have a response 624 */ 625 626 ret = send_accept (minor_status, 627 ctx, 628 &mech_output_token, 629 1, 630 get_mic ? &ctx->NegTokenInit_mech_types : NULL, 631 output_token); 632 if (ret) 633 goto out; 634 635out: 636 if (mech_output_token.value != NULL) 637 gss_release_buffer(&junk, &mech_output_token); 638 free_NegotiationToken(&nt); 639 640 641 if (ret == GSS_S_COMPLETE) { 642 _gss_spnego_fixup_ntlm(ctx); 643 644 if (src_name != NULL && ctx->mech_src_name != NULL) { 645 spnego_name name; 646 647 name = calloc(1, sizeof(*name)); 648 if (name) { 649 name->mech = ctx->mech_src_name; 650 ctx->mech_src_name = NULL; 651 *src_name = (gss_name_t)name; 652 } 653 } 654 } 655 656 if (mech_type != NULL) 657 *mech_type = ctx->negotiated_mech_type; 658 if (ret_flags != NULL) 659 *ret_flags = ctx->mech_flags; 660 if (time_rec != NULL) 661 *time_rec = ctx->mech_time_rec; 662 663 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 664 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 665 return ret; 666 } 667 668 _gss_spnego_internal_delete_sec_context(&junk, context_handle, 669 GSS_C_NO_BUFFER); 670 671 return ret; 672} 673 674 675static OM_uint32 GSSAPI_CALLCONV 676acceptor_continue 677 (OM_uint32 * minor_status, 678 gss_ctx_id_t * context_handle, 679 const gss_cred_id_t acceptor_cred_handle, 680 const gss_buffer_t input_token_buffer, 681 const gss_channel_bindings_t input_chan_bindings, 682 gss_name_t * src_name, 683 gss_OID * mech_type, 684 gss_buffer_t output_token, 685 OM_uint32 * ret_flags, 686 OM_uint32 * time_rec, 687 gss_cred_id_t *delegated_cred_handle 688 ) 689{ 690 OM_uint32 ret, ret2, minor, junk; 691 NegotiationToken nt; 692 size_t nt_len; 693 NegTokenResp *na; 694 unsigned int negResult = accept_incomplete; 695 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 696 gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; 697 gssspnego_ctx ctx; 698 699 ctx = (gssspnego_ctx)*context_handle; 700 701 /* 702 * The GSS-API encapsulation is only present on the initial 703 * context token (negTokenInit). 704 */ 705 706 ret = decode_NegotiationToken(input_token_buffer->value, 707 input_token_buffer->length, 708 &nt, &nt_len); 709 if (ret) { 710 *minor_status = ret; 711 return GSS_S_DEFECTIVE_TOKEN; 712 } 713 if (nt.element != choice_NegotiationToken_negTokenResp) { 714 *minor_status = 0; 715 return GSS_S_DEFECTIVE_TOKEN; 716 } 717 na = &nt.u.negTokenResp; 718 719 if (na->negResult != NULL) { 720 negResult = *(na->negResult); 721 } 722 723 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 724 725 { 726 gss_buffer_desc ibuf, obuf; 727 int get_mic = 0; 728 int require_response; 729 730 if (na->responseToken != NULL) { 731 ibuf.length = na->responseToken->length; 732 ibuf.value = na->responseToken->data; 733 mech_input_token = &ibuf; 734 } else { 735 ibuf.value = NULL; 736 ibuf.length = 0; 737 } 738 739 if (mech_input_token != GSS_C_NO_BUFFER) { 740 741 if (ctx->mech_src_name != GSS_C_NO_NAME) 742 gss_release_name(&minor, &ctx->mech_src_name); 743 744 ret = gss_accept_sec_context(minor_status, 745 &ctx->negotiated_ctx_id, 746 acceptor_cred_handle, 747 mech_input_token, 748 input_chan_bindings, 749 &ctx->mech_src_name, 750 &ctx->negotiated_mech_type, 751 &obuf, 752 &ctx->mech_flags, 753 &ctx->mech_time_rec, 754 delegated_cred_handle); 755 756 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 757 mech_output_token = &obuf; 758 } 759 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { 760 free_NegotiationToken(&nt); 761 gss_mg_collect_error(ctx->negotiated_mech_type, ret, *minor_status); 762 send_reject(&junk, output_token); 763 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 764 return ret; 765 } 766 if (ret == GSS_S_COMPLETE) 767 ctx->flags.open = 1; 768 } else 769 ret = GSS_S_COMPLETE; 770 771 if (ret == GSS_S_COMPLETE) 772 ret = acceptor_complete(minor_status, 773 ctx, 774 &get_mic, 775 mech_input_token, 776 mech_output_token, 777 na->mechListMIC, 778 output_token); 779 780 if (ctx->mech_flags & GSS_C_DCE_STYLE) 781 require_response = (negResult != accept_completed); 782 else 783 require_response = 0; 784 785 /* 786 * Check whether we need to send a result: there should be only 787 * one accept_completed response sent in the entire negotiation 788 */ 789 if ((mech_output_token != GSS_C_NO_BUFFER && 790 mech_output_token->length != 0) 791 || (ctx->flags.open && negResult == accept_incomplete) 792 || require_response 793 || get_mic) { 794 ret2 = send_accept (minor_status, 795 ctx, 796 mech_output_token, 797 0, 798 get_mic ? &ctx->NegTokenInit_mech_types : NULL, 799 output_token); 800 if (ret2) 801 goto out; 802 } else 803 ret2 = GSS_S_COMPLETE; 804 805 out: 806 if (ret2 != GSS_S_COMPLETE) 807 ret = ret2; 808 if (mech_output_token != NULL) 809 gss_release_buffer(&minor, mech_output_token); 810 free_NegotiationToken(&nt); 811 } 812 813 if (ret == GSS_S_COMPLETE) { 814 815 _gss_spnego_fixup_ntlm(ctx); 816 817 if (src_name != NULL && ctx->mech_src_name != NULL) { 818 spnego_name name; 819 820 name = calloc(1, sizeof(*name)); 821 if (name) { 822 name->mech = ctx->mech_src_name; 823 ctx->mech_src_name = NULL; 824 *src_name = (gss_name_t)name; 825 } 826 } 827 } 828 829 if (mech_type != NULL) 830 *mech_type = ctx->negotiated_mech_type; 831 if (ret_flags != NULL) 832 *ret_flags = ctx->mech_flags; 833 if (time_rec != NULL) 834 *time_rec = ctx->mech_time_rec; 835 836 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 837 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 838 return ret; 839 } 840 841 _gss_spnego_internal_delete_sec_context(&minor, context_handle, 842 GSS_C_NO_BUFFER); 843 844 return ret; 845} 846 847OM_uint32 GSSAPI_CALLCONV 848_gss_spnego_accept_sec_context 849 (OM_uint32 * minor_status, 850 gss_ctx_id_t * context_handle, 851 const gss_cred_id_t acceptor_cred_handle, 852 const gss_buffer_t input_token_buffer, 853 const gss_channel_bindings_t input_chan_bindings, 854 gss_name_t * src_name, 855 gss_OID * mech_type, 856 gss_buffer_t output_token, 857 OM_uint32 * ret_flags, 858 OM_uint32 * time_rec, 859 gss_cred_id_t *delegated_cred_handle 860 ) 861{ 862 _gss_accept_sec_context_t *func; 863 864 *minor_status = 0; 865 866 output_token->length = 0; 867 output_token->value = NULL; 868 869 if (src_name != NULL) 870 *src_name = GSS_C_NO_NAME; 871 if (mech_type != NULL) 872 *mech_type = GSS_C_NO_OID; 873 if (ret_flags != NULL) 874 *ret_flags = 0; 875 if (time_rec != NULL) 876 *time_rec = 0; 877 if (delegated_cred_handle != NULL) 878 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 879 880 881 if (*context_handle == GSS_C_NO_CONTEXT) 882 func = acceptor_start; 883 else 884 func = acceptor_continue; 885 886 887 return (*func)(minor_status, context_handle, acceptor_cred_handle, 888 input_token_buffer, input_chan_bindings, 889 src_name, mech_type, output_token, ret_flags, 890 time_rec, delegated_cred_handle); 891} 892