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 if (preferred_mech_type == GSS_C_NO_OID) { 623 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 624 free_NegotiationToken(&nt); 625 return ret; 626 } 627 628 ctx->preferred_mech_type = preferred_mech_type; 629 } 630 631 /* 632 * The initial token always have a response 633 */ 634 635 ret = send_accept (minor_status, 636 ctx, 637 &mech_output_token, 638 1, 639 get_mic ? &mech_buf : NULL, 640 output_token); 641 if (ret) 642 goto out; 643 644 out: 645 if (mech_output_token.value != NULL) 646 gss_release_buffer(&junk, &mech_output_token); 647 if (mech_buf.value != NULL) { 648 free(mech_buf.value); 649 mech_buf.value = NULL; 650 } 651 free_NegotiationToken(&nt); 652 653 654 if (ret == GSS_S_COMPLETE) { 655 if (src_name != NULL && ctx->mech_src_name != NULL) { 656 spnego_name name; 657 658 name = calloc(1, sizeof(*name)); 659 if (name) { 660 name->mech = ctx->mech_src_name; 661 ctx->mech_src_name = NULL; 662 *src_name = (gss_name_t)name; 663 } 664 } 665 } 666 667 if (mech_type != NULL) 668 *mech_type = ctx->negotiated_mech_type; 669 if (ret_flags != NULL) 670 *ret_flags = ctx->mech_flags; 671 if (time_rec != NULL) 672 *time_rec = ctx->mech_time_rec; 673 674 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 675 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 676 return ret; 677 } 678 679 _gss_spnego_internal_delete_sec_context(&junk, context_handle, 680 GSS_C_NO_BUFFER); 681 682 return ret; 683 } 684 685 686 static OM_uint32 GSSAPI_CALLCONV 687 acceptor_continue 688 (OM_uint32 * minor_status, 689 gss_ctx_id_t * context_handle, 690 const gss_cred_id_t acceptor_cred_handle, 691 const gss_buffer_t input_token_buffer, 692 const gss_channel_bindings_t input_chan_bindings, 693 gss_name_t * src_name, 694 gss_OID * mech_type, 695 gss_buffer_t output_token, 696 OM_uint32 * ret_flags, 697 OM_uint32 * time_rec, 698 gss_cred_id_t *delegated_cred_handle 699 ) 700 { 701 OM_uint32 ret, ret2, minor; 702 NegotiationToken nt; 703 size_t nt_len; 704 NegTokenResp *na; 705 unsigned int negResult = accept_incomplete; 706 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; 707 gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; 708 gss_buffer_desc mech_buf; 709 gssspnego_ctx ctx; 710 711 mech_buf.value = NULL; 712 713 ctx = (gssspnego_ctx)*context_handle; 714 715 /* 716 * The GSS-API encapsulation is only present on the initial 717 * context token (negTokenInit). 718 */ 719 720 ret = decode_NegotiationToken(input_token_buffer->value, 721 input_token_buffer->length, 722 &nt, &nt_len); 723 if (ret) { 724 *minor_status = ret; 725 return GSS_S_DEFECTIVE_TOKEN; 726 } 727 if (nt.element != choice_NegotiationToken_negTokenResp) { 728 *minor_status = 0; 729 return GSS_S_DEFECTIVE_TOKEN; 730 } 731 na = &nt.u.negTokenResp; 732 733 if (na->negResult != NULL) { 734 negResult = *(na->negResult); 735 } 736 737 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 738 739 { 740 gss_buffer_desc ibuf, obuf; 741 int require_mic, get_mic = 0; 742 int require_response; 743 heim_octet_string *mic; 744 745 if (na->responseToken != NULL) { 746 ibuf.length = na->responseToken->length; 747 ibuf.value = na->responseToken->data; 748 mech_input_token = &ibuf; 749 } else { 750 ibuf.value = NULL; 751 ibuf.length = 0; 752 } 753 754 if (mech_input_token != GSS_C_NO_BUFFER) { 755 756 if (ctx->mech_src_name != GSS_C_NO_NAME) 757 gss_release_name(&minor, &ctx->mech_src_name); 758 759 ret = gss_accept_sec_context(&minor, 760 &ctx->negotiated_ctx_id, 761 acceptor_cred_handle, 762 mech_input_token, 763 input_chan_bindings, 764 &ctx->mech_src_name, 765 &ctx->negotiated_mech_type, 766 &obuf, 767 &ctx->mech_flags, 768 &ctx->mech_time_rec, 769 delegated_cred_handle); 770 771 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 772 mech_output_token = &obuf; 773 } 774 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { 775 free_NegotiationToken(&nt); 776 gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor); 777 send_reject (minor_status, output_token); 778 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 779 return ret; 780 } 781 if (ret == GSS_S_COMPLETE) 782 ctx->open = 1; 783 } else 784 ret = GSS_S_COMPLETE; 785 786 ret2 = _gss_spnego_require_mechlist_mic(minor_status, 787 ctx, 788 &require_mic); 789 if (ret2) 790 goto out; 791 792 ctx->require_mic = require_mic; 793 794 mic = na->mechListMIC; 795 if (mic != NULL) 796 require_mic = 1; 797 798 if (ret == GSS_S_COMPLETE) 799 ret = acceptor_complete(minor_status, 800 ctx, 801 &get_mic, 802 &mech_buf, 803 mech_input_token, 804 mech_output_token, 805 na->mechListMIC, 806 output_token); 807 808 if (ctx->mech_flags & GSS_C_DCE_STYLE) 809 require_response = (negResult != accept_completed); 810 else 811 require_response = 0; 812 813 /* 814 * Check whether we need to send a result: there should be only 815 * one accept_completed response sent in the entire negotiation 816 */ 817 if ((mech_output_token != GSS_C_NO_BUFFER && 818 mech_output_token->length != 0) 819 || (ctx->open && negResult == accept_incomplete) 820 || require_response 821 || get_mic) { 822 ret2 = send_accept (minor_status, 823 ctx, 824 mech_output_token, 825 0, 826 get_mic ? &mech_buf : NULL, 827 output_token); 828 if (ret2) 829 goto out; 830 } 831 832 out: 833 if (ret2 != GSS_S_COMPLETE) 834 ret = ret2; 835 if (mech_output_token != NULL) 836 gss_release_buffer(&minor, mech_output_token); 837 if (mech_buf.value != NULL) 838 free(mech_buf.value); 839 free_NegotiationToken(&nt); 840 } 841 842 if (ret == GSS_S_COMPLETE) { 843 if (src_name != NULL && ctx->mech_src_name != NULL) { 844 spnego_name name; 845 846 name = calloc(1, sizeof(*name)); 847 if (name) { 848 name->mech = ctx->mech_src_name; 849 ctx->mech_src_name = NULL; 850 *src_name = (gss_name_t)name; 851 } 852 } 853 } 854 855 if (mech_type != NULL) 856 *mech_type = ctx->negotiated_mech_type; 857 if (ret_flags != NULL) 858 *ret_flags = ctx->mech_flags; 859 if (time_rec != NULL) 860 *time_rec = ctx->mech_time_rec; 861 862 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { 863 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 864 return ret; 865 } 866 867 _gss_spnego_internal_delete_sec_context(&minor, context_handle, 868 GSS_C_NO_BUFFER); 869 870 return ret; 871 } 872 873 OM_uint32 GSSAPI_CALLCONV 874 _gss_spnego_accept_sec_context 875 (OM_uint32 * minor_status, 876 gss_ctx_id_t * context_handle, 877 const gss_cred_id_t acceptor_cred_handle, 878 const gss_buffer_t input_token_buffer, 879 const gss_channel_bindings_t input_chan_bindings, 880 gss_name_t * src_name, 881 gss_OID * mech_type, 882 gss_buffer_t output_token, 883 OM_uint32 * ret_flags, 884 OM_uint32 * time_rec, 885 gss_cred_id_t *delegated_cred_handle 886 ) 887 { 888 _gss_accept_sec_context_t *func; 889 890 *minor_status = 0; 891 892 output_token->length = 0; 893 output_token->value = NULL; 894 895 if (src_name != NULL) 896 *src_name = GSS_C_NO_NAME; 897 if (mech_type != NULL) 898 *mech_type = GSS_C_NO_OID; 899 if (ret_flags != NULL) 900 *ret_flags = 0; 901 if (time_rec != NULL) 902 *time_rec = 0; 903 if (delegated_cred_handle != NULL) 904 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 905 906 907 if (*context_handle == GSS_C_NO_CONTEXT) 908 func = acceptor_start; 909 else 910 func = acceptor_continue; 911 912 913 return (*func)(minor_status, context_handle, acceptor_cred_handle, 914 input_token_buffer, input_chan_bindings, 915 src_name, mech_type, output_token, ret_flags, 916 time_rec, delegated_cred_handle); 917 } 918