accept_sec_context.c revision 1.1.1.1.22.1
1/* $NetBSD: accept_sec_context.c,v 1.1.1.1.22.1 2014/08/10 06:47:28 tls Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2006 Kungliga Tekniska H��gskolan 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 38static OM_uint32 39send_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 67static OM_uint32 68acceptor_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 90static OM_uint32 91send_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 154static OM_uint32 155send_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 277static OM_uint32 278verify_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 314static OM_uint32 315select_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 406static OM_uint32 407acceptor_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 471static OM_uint32 GSSAPI_CALLCONV 472acceptor_start 473 (OM_uint32 * minor_status, 474 gss_ctx_id_t * context_handle, 475 const gss_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 if (preferred_mech_type == GSS_C_NO_OID) { 625 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 626 free_NegotiationToken(&nt); 627 return ret; 628 } 629 630 ctx->preferred_mech_type = preferred_mech_type; 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 646out: 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 688static OM_uint32 GSSAPI_CALLCONV 689acceptor_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 875OM_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