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