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