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) { 214 ALLOC(nt.u.negTokenResp.supportedMech, 1); 215 if (nt.u.negTokenResp.supportedMech == NULL) { 216 free_NegotiationToken(&nt); 217 *minor_status = ENOMEM; 218 return GSS_S_FAILURE; 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 ret = GSS_S_FAILURE; 227 *minor_status = ENOMEM; 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 free_NegotiationToken(&nt); 238 *minor_status = ENOMEM; 239 return GSS_S_FAILURE; 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 ret = GSS_S_FAILURE; 269 *minor_status = ENOMEM; 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 ret = GSS_S_FAILURE; 289 *minor_status = ENOMEM; 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 out: 304 free_NegotiationToken(&nt); 305 return ret; 306} 307 308/* 309 * 310 */ 311 312static OM_uint32 313select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p, 314 gss_OID *mech_p) 315{ 316 char mechbuf[64]; 317 size_t mech_len; 318 gss_OID_desc oid; 319 gss_OID oidp; 320 gss_OID_set mechs; 321 OM_uint32 ret, junk; 322 unsigned int n; 323 324 ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1, 325 sizeof(mechbuf), 326 mechType, 327 &mech_len); 328 if (ret) { 329 return GSS_S_DEFECTIVE_TOKEN; 330 } 331 332 oid.length = (OM_uint32)mech_len; 333 oid.elements = mechbuf + sizeof(mechbuf) - mech_len; 334 335 if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) { 336 return GSS_S_BAD_MECH; 337 } 338 339 *minor_status = 0; 340 341 /* Translate broken MS Kebreros OID */ 342 if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) 343 oidp = &_gss_spnego_krb5_mechanism_oid_desc; 344 else 345 oidp = &oid; 346 347 348 ret = gss_indicate_mechs(&junk, &mechs); 349 if (ret) { 350 return (ret); 351 } 352 353 for (n = 0; n < mechs->count; n++) { 354 if (gss_oid_equal(&mechs->elements[n], oidp)) 355 break; 356 } 357 358 if (n == mechs->count) { 359 gss_release_oid_set(&junk, &mechs); 360 return GSS_S_BAD_MECH; 361 } 362 363 ret = gss_duplicate_oid(minor_status, &oid, mech_p); 364 gss_release_oid_set(&junk, &mechs); 365 if (ret) 366 return ret; 367 368 if (verify_p) { 369 gss_name_t name = GSS_C_NO_NAME; 370 gss_buffer_desc namebuf; 371 char *str = NULL, *host, hostname[MAXHOSTNAMELEN]; 372 373 host = getenv("GSSAPI_SPNEGO_NAME"); 374 if (host == NULL || issuid()) { 375 int rv; 376 if (gethostname(hostname, sizeof(hostname)) != 0) { 377 *minor_status = errno; 378 return GSS_S_FAILURE; 379 } 380 rv = asprintf(&str, "host@%s", hostname); 381 if (rv < 0 || str == NULL) { 382 *minor_status = ENOMEM; 383 return GSS_S_FAILURE; 384 } 385 host = str; 386 } 387 388 namebuf.length = strlen(host); 389 namebuf.value = host; 390 391 ret = gss_import_name(minor_status, &namebuf, 392 GSS_C_NT_HOSTBASED_SERVICE, &name); 393 if (str) 394 free(str); 395 if (ret != GSS_S_COMPLETE) 396 return ret; 397 398 ret = acceptor_approved(NULL, name, GSS_C_NO_CREDENTIAL, *mech_p); 399 gss_release_name(&junk, &name); 400 } 401 402 return ret; 403} 404 405 406static OM_uint32 407acceptor_complete(OM_uint32 * minor_status, 408 gssspnego_ctx ctx, 409 int *get_mic, 410 gss_buffer_t mech_input_token, 411 gss_buffer_t mech_output_token, 412 heim_octet_string *mic, 413 gss_buffer_t output_token) 414{ 415 gss_buffer_desc buf; 416 OM_uint32 ret; 417 int verify_mic; 418 419 buf.length = 0; 420 buf.value = NULL; 421 422 ctx->flags.require_mic = _gss_spnego_require_mechlist_mic(ctx); 423 424 if (mic != NULL) 425 ctx->flags.require_mic = 1; 426 427 if (ctx->flags.open && ctx->flags.require_mic) { 428 if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ 429 verify_mic = 1; 430 *get_mic = 0; 431 } else if (mech_output_token != GSS_C_NO_BUFFER && 432 mech_output_token->length == 0) { /* Odd */ 433 *get_mic = verify_mic = 1; 434 } else { /* Even/One */ 435 verify_mic = 0; 436 *get_mic = 1; 437 } 438 439 if (verify_mic && mic == NULL && ctx->flags.safe_omit) { 440 /* 441 * Peer is old and didn't send a mic while we expected 442 * one, but since it safe to omit, let do that 443 */ 444 } else if (verify_mic) { 445 ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx, mic); 446 if (ret) { 447 if (*get_mic) 448 send_reject(minor_status, output_token); 449 if (buf.value) 450 free(buf.value); 451 return ret; 452 } 453 } 454 } else 455 *get_mic = 0; 456 457 return GSS_S_COMPLETE; 458} 459 460 461static OM_uint32 GSSAPI_CALLCONV 462acceptor_start 463 (OM_uint32 * minor_status, 464 gss_ctx_id_t * context_handle, 465 const gss_cred_id_t acceptor_cred_handle, 466 const gss_buffer_t input_token_buffer, 467 const gss_channel_bindings_t input_chan_bindings, 468 gss_name_t * src_name, 469 gss_OID * mech_type, 470 gss_buffer_t output_token, 471 OM_uint32 * ret_flags, 472 OM_uint32 * time_rec, 473 gss_cred_id_t *delegated_cred_handle 474 ) 475{ 476 OM_uint32 ret, junk; 477 NegotiationToken nt; 478 size_t size; 479 NegTokenInit *ni; 480 gss_buffer_desc data; 481 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 482 gss_buffer_desc mech_output_token; 483 gss_OID preferred_mech_type = GSS_C_NO_OID; 484 gssspnego_ctx ctx; 485 int get_mic = 0; 486 int first_ok = 0; 487 488 memset(&nt, 0, sizeof(nt)); 489 490 mech_output_token.value = NULL; 491 mech_output_token.length = 0; 492 493 if (input_token_buffer->length == 0) 494 return send_supported_mechs (minor_status, output_token); 495 496 ret = _gss_spnego_alloc_sec_context(minor_status, context_handle); 497 if (ret != GSS_S_COMPLETE) 498 return ret; 499 500 ctx = (gssspnego_ctx)*context_handle; 501 502 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 503 504 /* 505 * The GSS-API encapsulation is only present on the initial 506 * context token (negTokenInit). 507 */ 508 ret = gss_decapsulate_token (input_token_buffer, 509 GSS_SPNEGO_MECHANISM, 510 &data); 511 if (ret) 512 goto out; 513 514 ret = decode_NegotiationToken(data.value, data.length, &nt, &size); 515 gss_release_buffer(minor_status, &data); 516 if (ret) { 517 *minor_status = ret; 518 ret = GSS_S_DEFECTIVE_TOKEN; 519 goto out; 520 } 521 if (nt.element != choice_NegotiationToken_negTokenInit) { 522 *minor_status = 0; 523 ret = GSS_S_DEFECTIVE_TOKEN; 524 goto out; 525 } 526 ni = &nt.u.negTokenInit; 527 528 if (ni->mechTypes.len < 1) { 529 *minor_status = 0; 530 ret = GSS_S_DEFECTIVE_TOKEN; 531 goto out; 532 } 533 534 { 535 MechTypeList mt; 536 int kret; 537 538 mt.len = ni->mechTypes.len; 539 mt.val = ni->mechTypes.val; 540 541 ASN1_MALLOC_ENCODE(MechTypeList, 542 ctx->NegTokenInit_mech_types.value, 543 ctx->NegTokenInit_mech_types.length, 544 &mt, &size, kret); 545 if (kret) { 546 *minor_status = kret; 547 ret = GSS_S_FAILURE; 548 goto out; 549 } 550 //XXX heim_assert(ctx->NegTokenInit_mech_types.length == size, "asn1 internal error"); 551 } 552 553 /* 554 * First we try the opportunistic token if we have support for it, 555 * don't try to verify we have credential for the token, 556 * gss_accept_sec_context() will (hopefully) tell us that. 557 * If that failes, 558 */ 559 560 ret = select_mech(minor_status, 561 &ni->mechTypes.val[0], 562 0, 563 &preferred_mech_type); 564 565 if (ret == 0 && ni->mechToken != NULL) { 566 gss_buffer_desc ibuf; 567 568 ibuf.length = ni->mechToken->length; 569 ibuf.value = ni->mechToken->data; 570 mech_input_token = &ibuf; 571 572 if (ctx->mech_src_name != GSS_C_NO_NAME) 573 gss_release_name(&junk, &ctx->mech_src_name); 574 575 ret = gss_accept_sec_context(minor_status, 576 &ctx->negotiated_ctx_id, 577 acceptor_cred_handle, 578 mech_input_token, 579 input_chan_bindings, 580 &ctx->mech_src_name, 581 &ctx->negotiated_mech_type, 582 &mech_output_token, 583 &ctx->mech_flags, 584 &ctx->mech_time_rec, 585 delegated_cred_handle); 586 587 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 588 ctx->preferred_mech_type = preferred_mech_type; 589 preferred_mech_type = GSS_C_NO_OID; 590 first_ok = 1; 591 } else { 592 gss_mg_collect_error(preferred_mech_type, ret, *minor_status); 593 gss_release_oid(&junk, &preferred_mech_type); 594 } 595 596 if (ret == GSS_S_COMPLETE) { 597 ret = acceptor_complete(minor_status, 598 ctx, 599 &get_mic, 600 mech_input_token, 601 &mech_output_token, 602 ni->mechListMIC, 603 output_token); 604 if (ret != GSS_S_COMPLETE) 605 goto out; 606 ctx->flags.open = 1; 607 } 608 } else { 609 *minor_status = 0; 610 return gss_mg_set_error_string(GSS_C_NO_OID, GSS_S_NO_CONTEXT, 611 *minor_status, 612 "SPNEGO acceptor didn't find a prefered mechanism"); 613 } 614 615 /* 616 * If opportunistic token failed, lets try the other mechs. 617 */ 618 619 if (!first_ok && ni->mechToken != NULL) { 620 size_t j; 621 622 gss_release_oid(&junk, &preferred_mech_type); 623 624 /* Call glue layer to find first mech we support */ 625 for (j = 1; j < ni->mechTypes.len; ++j) { 626 ret = select_mech(&junk, 627 &ni->mechTypes.val[j], 628 1, 629 &preferred_mech_type); 630 if (ret == 0) 631 break; 632 gss_release_oid(&junk, &preferred_mech_type); 633 } 634 if (preferred_mech_type == GSS_C_NO_OID) { 635 goto out; 636 } 637 638 ctx->preferred_mech_type = preferred_mech_type; 639 preferred_mech_type = GSS_C_NO_OID; 640 } 641 642 /* 643 * The initial token always have a response 644 */ 645 646 ret = send_accept (minor_status, 647 ctx, 648 &mech_output_token, 649 1, 650 get_mic ? &ctx->NegTokenInit_mech_types : NULL, 651 output_token); 652 if (ret) 653 goto out; 654 655out: 656 if(preferred_mech_type) 657 gss_release_oid(&junk, &preferred_mech_type); 658 659 if (mech_output_token.value != NULL) 660 gss_release_buffer(&junk, &mech_output_token); 661 free_NegotiationToken(&nt); 662 663 664 if (ret == GSS_S_COMPLETE) { 665 _gss_spnego_fixup_ntlm(ctx); 666 667 if (src_name != NULL && ctx->mech_src_name != NULL) { 668 spnego_name name; 669 670 name = calloc(1, sizeof(*name)); 671 if (name) { 672 name->mech = ctx->mech_src_name; 673 ctx->mech_src_name = NULL; 674 *src_name = (gss_name_t)name; 675 } 676 } 677 } 678 679 if (mech_type != NULL) 680 *mech_type = ctx->negotiated_mech_type; 681 if (ret_flags != NULL) 682 *ret_flags = ctx->mech_flags; 683 if (time_rec != NULL) 684 *time_rec = ctx->mech_time_rec; 685 686 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 687 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 688 return ret; 689 } 690 691 _gss_spnego_internal_delete_sec_context(&junk, context_handle, 692 GSS_C_NO_BUFFER); 693 694 return ret; 695} 696 697 698static OM_uint32 GSSAPI_CALLCONV 699acceptor_continue 700 (OM_uint32 * minor_status, 701 gss_ctx_id_t * context_handle, 702 const gss_cred_id_t acceptor_cred_handle, 703 const gss_buffer_t input_token_buffer, 704 const gss_channel_bindings_t input_chan_bindings, 705 gss_name_t * src_name, 706 gss_OID * mech_type, 707 gss_buffer_t output_token, 708 OM_uint32 * ret_flags, 709 OM_uint32 * time_rec, 710 gss_cred_id_t *delegated_cred_handle 711 ) 712{ 713 OM_uint32 ret, ret2, minor, junk; 714 NegotiationToken nt; 715 size_t nt_len; 716 NegTokenResp *na; 717 unsigned int negResult = accept_incomplete; 718 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 719 gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; 720 gssspnego_ctx ctx; 721 722 ctx = (gssspnego_ctx)*context_handle; 723 724 /* 725 * The GSS-API encapsulation is only present on the initial 726 * context token (negTokenInit). 727 */ 728 729 ret = decode_NegotiationToken(input_token_buffer->value, 730 input_token_buffer->length, 731 &nt, &nt_len); 732 if (ret) { 733 *minor_status = ret; 734 return GSS_S_DEFECTIVE_TOKEN; 735 } 736 if (nt.element != choice_NegotiationToken_negTokenResp) { 737 *minor_status = 0; 738 return GSS_S_DEFECTIVE_TOKEN; 739 } 740 na = &nt.u.negTokenResp; 741 742 if (na->negResult != NULL) { 743 negResult = *(na->negResult); 744 } 745 746 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 747 748 { 749 gss_buffer_desc ibuf, obuf; 750 int get_mic = 0; 751 int require_response; 752 753 if (na->responseToken != NULL) { 754 ibuf.length = na->responseToken->length; 755 ibuf.value = na->responseToken->data; 756 mech_input_token = &ibuf; 757 } else { 758 ibuf.value = NULL; 759 ibuf.length = 0; 760 } 761 762 if (mech_input_token != GSS_C_NO_BUFFER) { 763 764 if (ctx->mech_src_name != GSS_C_NO_NAME) 765 gss_release_name(&minor, &ctx->mech_src_name); 766 767 ret = gss_accept_sec_context(minor_status, 768 &ctx->negotiated_ctx_id, 769 acceptor_cred_handle, 770 mech_input_token, 771 input_chan_bindings, 772 &ctx->mech_src_name, 773 &ctx->negotiated_mech_type, 774 &obuf, 775 &ctx->mech_flags, 776 &ctx->mech_time_rec, 777 delegated_cred_handle); 778 779 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 780 mech_output_token = &obuf; 781 } 782 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { 783 free_NegotiationToken(&nt); 784 gss_mg_collect_error(ctx->negotiated_mech_type, ret, *minor_status); 785 send_reject(&junk, output_token); 786 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 787 return ret; 788 } 789 if (ret == GSS_S_COMPLETE) 790 ctx->flags.open = 1; 791 } else 792 ret = GSS_S_COMPLETE; 793 794 if (ret == GSS_S_COMPLETE) 795 ret = acceptor_complete(minor_status, 796 ctx, 797 &get_mic, 798 mech_input_token, 799 mech_output_token, 800 na->mechListMIC, 801 output_token); 802 803 if (ctx->mech_flags & GSS_C_DCE_STYLE) 804 require_response = (negResult != accept_completed); 805 else 806 require_response = 0; 807 808 /* 809 * Check whether we need to send a result: there should be only 810 * one accept_completed response sent in the entire negotiation 811 */ 812 if ((mech_output_token != GSS_C_NO_BUFFER && 813 mech_output_token->length != 0) 814 || (ctx->flags.open && negResult == accept_incomplete) 815 || require_response 816 || get_mic) { 817 ret2 = send_accept (minor_status, 818 ctx, 819 mech_output_token, 820 0, 821 get_mic ? &ctx->NegTokenInit_mech_types : NULL, 822 output_token); 823 if (ret2) 824 goto out; 825 } else 826 ret2 = GSS_S_COMPLETE; 827 828 out: 829 if (ret2 != GSS_S_COMPLETE) 830 ret = ret2; 831 if (mech_output_token != NULL) 832 gss_release_buffer(&minor, mech_output_token); 833 free_NegotiationToken(&nt); 834 } 835 836 if (ret == GSS_S_COMPLETE) { 837 838 _gss_spnego_fixup_ntlm(ctx); 839 840 if (src_name != NULL && ctx->mech_src_name != NULL) { 841 spnego_name name; 842 843 name = calloc(1, sizeof(*name)); 844 if (name) { 845 name->mech = ctx->mech_src_name; 846 ctx->mech_src_name = NULL; 847 *src_name = (gss_name_t)name; 848 } 849 } 850 } 851 852 if (mech_type != NULL) 853 *mech_type = ctx->negotiated_mech_type; 854 if (ret_flags != NULL) 855 *ret_flags = ctx->mech_flags; 856 if (time_rec != NULL) 857 *time_rec = ctx->mech_time_rec; 858 859 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 860 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 861 return ret; 862 } 863 864 _gss_spnego_internal_delete_sec_context(&minor, context_handle, 865 GSS_C_NO_BUFFER); 866 867 return ret; 868} 869 870OM_uint32 GSSAPI_CALLCONV 871_gss_spnego_accept_sec_context 872 (OM_uint32 * minor_status, 873 gss_ctx_id_t * context_handle, 874 const gss_cred_id_t acceptor_cred_handle, 875 const gss_buffer_t input_token_buffer, 876 const gss_channel_bindings_t input_chan_bindings, 877 gss_name_t * src_name, 878 gss_OID * mech_type, 879 gss_buffer_t output_token, 880 OM_uint32 * ret_flags, 881 OM_uint32 * time_rec, 882 gss_cred_id_t *delegated_cred_handle 883 ) 884{ 885 _gss_accept_sec_context_t *func; 886 887 *minor_status = 0; 888 889 output_token->length = 0; 890 output_token->value = NULL; 891 892 if (src_name != NULL) 893 *src_name = GSS_C_NO_NAME; 894 if (mech_type != NULL) 895 *mech_type = GSS_C_NO_OID; 896 if (ret_flags != NULL) 897 *ret_flags = 0; 898 if (time_rec != NULL) 899 *time_rec = 0; 900 if (delegated_cred_handle != NULL) 901 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 902 903 904 if (*context_handle == GSS_C_NO_CONTEXT) 905 func = acceptor_start; 906 else 907 func = acceptor_continue; 908 909 910 return (*func)(minor_status, context_handle, acceptor_cred_handle, 911 input_token_buffer, input_chan_bindings, 912 src_name, mech_type, output_token, ret_flags, 913 time_rec, delegated_cred_handle); 914} 915