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