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