1 /* 2 * Copyright (c) 1997 - 2004 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: init_sec_context.c 19411 2006-12-18 15:42:03Z lha $"); 37 38 /* 39 * Is target_name an sane target for `mech�. 40 */ 41 42 static OM_uint32 43 initiator_approved(gss_name_t target_name, gss_OID mech) 44 { 45 OM_uint32 min_stat, maj_stat; 46 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 47 gss_buffer_desc out; 48 49 maj_stat = gss_init_sec_context(&min_stat, 50 GSS_C_NO_CREDENTIAL, 51 &ctx, 52 target_name, 53 mech, 54 0, 55 GSS_C_INDEFINITE, 56 GSS_C_NO_CHANNEL_BINDINGS, 57 GSS_C_NO_BUFFER, 58 NULL, 59 &out, 60 NULL, 61 NULL); 62 if (GSS_ERROR(maj_stat)) 63 return GSS_S_BAD_MECH; 64 gss_release_buffer(&min_stat, &out); 65 gss_delete_sec_context(&min_stat, &ctx, NULL); 66 67 return GSS_S_COMPLETE; 68 } 69 70 /* 71 * Send a reply. Note that we only need to send a reply if we 72 * need to send a MIC or a mechanism token. Otherwise, we can 73 * return an empty buffer. 74 * 75 * The return value of this will be returned to the API, so it 76 * must return GSS_S_CONTINUE_NEEDED if a token was generated. 77 */ 78 static OM_uint32 79 spnego_reply_internal(OM_uint32 *minor_status, 80 gssspnego_ctx context_handle, 81 const gss_buffer_t mech_buf, 82 gss_buffer_t mech_token, 83 gss_buffer_t output_token) 84 { 85 NegotiationToken nt; 86 gss_buffer_desc mic_buf; 87 OM_uint32 ret; 88 size_t size; 89 90 if (mech_buf == GSS_C_NO_BUFFER && mech_token->length == 0) { 91 output_token->length = 0; 92 output_token->value = NULL; 93 94 return context_handle->open ? GSS_S_COMPLETE : GSS_S_FAILURE; 95 } 96 97 memset(&nt, 0, sizeof(nt)); 98 99 nt.element = choice_NegotiationToken_negTokenResp; 100 101 ALLOC(nt.u.negTokenResp.negResult, 1); 102 if (nt.u.negTokenResp.negResult == NULL) { 103 *minor_status = ENOMEM; 104 return GSS_S_FAILURE; 105 } 106 107 nt.u.negTokenResp.supportedMech = NULL; 108 109 output_token->length = 0; 110 output_token->value = NULL; 111 112 if (mech_token->length == 0) { 113 nt.u.negTokenResp.responseToken = NULL; 114 *(nt.u.negTokenResp.negResult) = accept_completed; 115 } else { 116 ALLOC(nt.u.negTokenResp.responseToken, 1); 117 if (nt.u.negTokenResp.responseToken == NULL) { 118 free_NegotiationToken(&nt); 119 *minor_status = ENOMEM; 120 return GSS_S_FAILURE; 121 } 122 nt.u.negTokenResp.responseToken->length = mech_token->length; 123 nt.u.negTokenResp.responseToken->data = mech_token->value; 124 mech_token->length = 0; 125 mech_token->value = NULL; 126 127 *(nt.u.negTokenResp.negResult) = accept_incomplete; 128 } 129 130 if (mech_buf != GSS_C_NO_BUFFER) { 131 132 ret = gss_get_mic(minor_status, 133 context_handle->negotiated_ctx_id, 134 0, 135 mech_buf, 136 &mic_buf); 137 if (ret == GSS_S_COMPLETE) { 138 ALLOC(nt.u.negTokenResp.mechListMIC, 1); 139 if (nt.u.negTokenResp.mechListMIC == NULL) { 140 gss_release_buffer(minor_status, &mic_buf); 141 free_NegotiationToken(&nt); 142 *minor_status = ENOMEM; 143 return GSS_S_FAILURE; 144 } 145 146 nt.u.negTokenResp.mechListMIC->length = mic_buf.length; 147 nt.u.negTokenResp.mechListMIC->data = mic_buf.value; 148 } else if (ret == GSS_S_UNAVAILABLE) { 149 nt.u.negTokenResp.mechListMIC = NULL; 150 } if (ret) { 151 free_NegotiationToken(&nt); 152 *minor_status = ENOMEM; 153 return GSS_S_FAILURE; 154 } 155 } else { 156 nt.u.negTokenResp.mechListMIC = NULL; 157 } 158 159 ASN1_MALLOC_ENCODE(NegotiationToken, 160 output_token->value, output_token->length, 161 &nt, &size, ret); 162 if (ret) { 163 free_NegotiationToken(&nt); 164 *minor_status = ret; 165 return GSS_S_FAILURE; 166 } 167 168 if (*(nt.u.negTokenResp.negResult) == accept_completed) 169 ret = GSS_S_COMPLETE; 170 else 171 ret = GSS_S_CONTINUE_NEEDED; 172 173 free_NegotiationToken(&nt); 174 return ret; 175 } 176 177 static OM_uint32 178 spnego_initial 179 (OM_uint32 * minor_status, 180 gssspnego_cred cred, 181 gss_ctx_id_t * context_handle, 182 const gss_name_t target_name, 183 const gss_OID mech_type, 184 OM_uint32 req_flags, 185 OM_uint32 time_req, 186 const gss_channel_bindings_t input_chan_bindings, 187 const gss_buffer_t input_token, 188 gss_OID * actual_mech_type, 189 gss_buffer_t output_token, 190 OM_uint32 * ret_flags, 191 OM_uint32 * time_rec 192 ) 193 { 194 NegTokenInit ni; 195 int ret; 196 OM_uint32 sub, minor; 197 gss_buffer_desc mech_token; 198 u_char *buf; 199 size_t buf_size, buf_len; 200 gss_buffer_desc data; 201 size_t ni_len; 202 gss_ctx_id_t context; 203 gssspnego_ctx ctx; 204 spnego_name name = (spnego_name)target_name; 205 206 *minor_status = 0; 207 208 memset (&ni, 0, sizeof(ni)); 209 210 *context_handle = GSS_C_NO_CONTEXT; 211 212 if (target_name == GSS_C_NO_NAME) 213 return GSS_S_BAD_NAME; 214 215 sub = _gss_spnego_alloc_sec_context(&minor, &context); 216 if (GSS_ERROR(sub)) { 217 *minor_status = minor; 218 return sub; 219 } 220 ctx = (gssspnego_ctx)context; 221 222 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 223 224 ctx->local = 1; 225 226 sub = gss_import_name(&minor, &name->value, &name->type, &ctx->target_name); 227 if (GSS_ERROR(sub)) { 228 *minor_status = minor; 229 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 230 return sub; 231 } 232 233 sub = _gss_spnego_indicate_mechtypelist(&minor, 234 ctx->target_name, 235 initiator_approved, 236 0, 237 cred, 238 &ni.mechTypes, 239 &ctx->preferred_mech_type); 240 if (GSS_ERROR(sub)) { 241 *minor_status = minor; 242 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 243 return sub; 244 } 245 246 ni.reqFlags = NULL; 247 248 /* 249 * If we have a credential handle, use it to select the mechanism 250 * that we will use 251 */ 252 253 /* generate optimistic token */ 254 sub = gss_init_sec_context(&minor, 255 (cred != NULL) ? cred->negotiated_cred_id : 256 GSS_C_NO_CREDENTIAL, 257 &ctx->negotiated_ctx_id, 258 ctx->target_name, 259 ctx->preferred_mech_type, 260 req_flags, 261 time_req, 262 input_chan_bindings, 263 input_token, 264 &ctx->negotiated_mech_type, 265 &mech_token, 266 &ctx->mech_flags, 267 &ctx->mech_time_rec); 268 if (GSS_ERROR(sub)) { 269 free_NegTokenInit(&ni); 270 *minor_status = minor; 271 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 272 return sub; 273 } 274 if (sub == GSS_S_COMPLETE) 275 ctx->maybe_open = 1; 276 277 if (mech_token.length != 0) { 278 ALLOC(ni.mechToken, 1); 279 if (ni.mechToken == NULL) { 280 free_NegTokenInit(&ni); 281 gss_release_buffer(&minor, &mech_token); 282 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 283 *minor_status = ENOMEM; 284 return GSS_S_FAILURE; 285 } 286 ni.mechToken->length = mech_token.length; 287 ni.mechToken->data = malloc(mech_token.length); 288 if (ni.mechToken->data == NULL && mech_token.length != 0) { 289 free_NegTokenInit(&ni); 290 gss_release_buffer(&minor, &mech_token); 291 *minor_status = ENOMEM; 292 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 293 return GSS_S_FAILURE; 294 } 295 memcpy(ni.mechToken->data, mech_token.value, mech_token.length); 296 gss_release_buffer(&minor, &mech_token); 297 } else 298 ni.mechToken = NULL; 299 300 ni.mechListMIC = NULL; 301 302 ni_len = length_NegTokenInit(&ni); 303 buf_size = 1 + der_length_len(ni_len) + ni_len; 304 305 buf = malloc(buf_size); 306 if (buf == NULL) { 307 free_NegTokenInit(&ni); 308 *minor_status = ENOMEM; 309 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 310 return GSS_S_FAILURE; 311 } 312 313 ret = encode_NegTokenInit(buf + buf_size - 1, 314 ni_len, 315 &ni, &buf_len); 316 if (ret == 0 && ni_len != buf_len) 317 abort(); 318 319 if (ret == 0) { 320 size_t tmp; 321 322 ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, 323 buf_size - buf_len, 324 buf_len, 325 ASN1_C_CONTEXT, 326 CONS, 327 0, 328 &tmp); 329 if (ret == 0 && tmp + buf_len != buf_size) 330 abort(); 331 } 332 if (ret) { 333 *minor_status = ret; 334 free(buf); 335 free_NegTokenInit(&ni); 336 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 337 return GSS_S_FAILURE; 338 } 339 340 data.value = buf; 341 data.length = buf_size; 342 343 ctx->initiator_mech_types.len = ni.mechTypes.len; 344 ctx->initiator_mech_types.val = ni.mechTypes.val; 345 ni.mechTypes.len = 0; 346 ni.mechTypes.val = NULL; 347 348 free_NegTokenInit(&ni); 349 350 sub = gss_encapsulate_token(&data, 351 GSS_SPNEGO_MECHANISM, 352 output_token); 353 free (buf); 354 355 if (sub) { 356 _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); 357 return sub; 358 } 359 360 if (actual_mech_type) 361 *actual_mech_type = ctx->negotiated_mech_type; 362 if (ret_flags) 363 *ret_flags = ctx->mech_flags; 364 if (time_rec) 365 *time_rec = ctx->mech_time_rec; 366 367 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 368 369 *context_handle = context; 370 371 return GSS_S_CONTINUE_NEEDED; 372 } 373 374 static OM_uint32 375 spnego_reply 376 (OM_uint32 * minor_status, 377 const gssspnego_cred cred, 378 gss_ctx_id_t * context_handle, 379 const gss_name_t target_name, 380 const gss_OID mech_type, 381 OM_uint32 req_flags, 382 OM_uint32 time_req, 383 const gss_channel_bindings_t input_chan_bindings, 384 const gss_buffer_t input_token, 385 gss_OID * actual_mech_type, 386 gss_buffer_t output_token, 387 OM_uint32 * ret_flags, 388 OM_uint32 * time_rec 389 ) 390 { 391 OM_uint32 ret, minor; 392 NegTokenResp resp; 393 size_t len, taglen; 394 gss_OID_desc mech; 395 int require_mic; 396 size_t buf_len; 397 gss_buffer_desc mic_buf, mech_buf; 398 gss_buffer_desc mech_output_token; 399 gssspnego_ctx ctx; 400 401 *minor_status = 0; 402 403 ctx = (gssspnego_ctx)*context_handle; 404 405 output_token->length = 0; 406 output_token->value = NULL; 407 408 mech_output_token.length = 0; 409 mech_output_token.value = NULL; 410 411 mech_buf.value = NULL; 412 mech_buf.length = 0; 413 414 ret = der_match_tag_and_length(input_token->value, input_token->length, 415 ASN1_C_CONTEXT, CONS, 1, &len, &taglen); 416 if (ret) 417 return ret; 418 419 if (len > input_token->length - taglen) 420 return ASN1_OVERRUN; 421 422 ret = decode_NegTokenResp((const unsigned char *)input_token->value+taglen, 423 len, &resp, NULL); 424 if (ret) { 425 *minor_status = ENOMEM; 426 return GSS_S_FAILURE; 427 } 428 429 if (resp.negResult == NULL 430 || *(resp.negResult) == reject 431 /* || resp.supportedMech == NULL */ 432 ) 433 { 434 free_NegTokenResp(&resp); 435 return GSS_S_BAD_MECH; 436 } 437 438 /* 439 * Pick up the mechanism that the acceptor selected, only allow it 440 * to be sent in packet. 441 */ 442 443 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 444 445 if (resp.supportedMech) { 446 447 if (ctx->oidlen) { 448 free_NegTokenResp(&resp); 449 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 450 return GSS_S_BAD_MECH; 451 } 452 ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1, 453 sizeof(ctx->oidbuf), 454 resp.supportedMech, 455 &ctx->oidlen); 456 /* Avoid recursively embedded SPNEGO */ 457 if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length && 458 memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, 459 GSS_SPNEGO_MECHANISM->elements, 460 ctx->oidlen) == 0)) 461 { 462 free_NegTokenResp(&resp); 463 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 464 return GSS_S_BAD_MECH; 465 } 466 467 /* check if the acceptor took our optimistic token */ 468 if (ctx->oidlen != ctx->preferred_mech_type->length || 469 memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, 470 ctx->preferred_mech_type->elements, 471 ctx->oidlen) != 0) 472 { 473 gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id, 474 GSS_C_NO_BUFFER); 475 ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; 476 } 477 } else if (ctx->oidlen == 0) { 478 free_NegTokenResp(&resp); 479 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 480 return GSS_S_BAD_MECH; 481 } 482 483 if (resp.responseToken != NULL || 484 ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { 485 gss_buffer_desc mech_input_token; 486 487 if (resp.responseToken) { 488 mech_input_token.length = resp.responseToken->length; 489 mech_input_token.value = resp.responseToken->data; 490 } else { 491 mech_input_token.length = 0; 492 mech_input_token.value = NULL; 493 } 494 495 496 mech.length = ctx->oidlen; 497 mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen; 498 499 /* Fall through as if the negotiated mechanism 500 was requested explicitly */ 501 ret = gss_init_sec_context(&minor, 502 (cred != NULL) ? cred->negotiated_cred_id : 503 GSS_C_NO_CREDENTIAL, 504 &ctx->negotiated_ctx_id, 505 ctx->target_name, 506 &mech, 507 req_flags, 508 time_req, 509 input_chan_bindings, 510 &mech_input_token, 511 &ctx->negotiated_mech_type, 512 &mech_output_token, 513 &ctx->mech_flags, 514 &ctx->mech_time_rec); 515 if (GSS_ERROR(ret)) { 516 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 517 free_NegTokenResp(&resp); 518 *minor_status = minor; 519 return ret; 520 } 521 if (ret == GSS_S_COMPLETE) { 522 ctx->open = 1; 523 } 524 } else if (*(resp.negResult) == accept_completed) { 525 if (ctx->maybe_open) 526 ctx->open = 1; 527 } 528 529 if (*(resp.negResult) == request_mic) { 530 ctx->require_mic = 1; 531 } 532 533 if (ctx->open) { 534 /* 535 * Verify the mechListMIC if one was provided or CFX was 536 * used and a non-preferred mechanism was selected 537 */ 538 if (resp.mechListMIC != NULL) { 539 require_mic = 1; 540 } else { 541 ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, 542 &require_mic); 543 if (ret) { 544 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 545 free_NegTokenResp(&resp); 546 gss_release_buffer(&minor, &mech_output_token); 547 return ret; 548 } 549 } 550 } else { 551 require_mic = 0; 552 } 553 554 if (require_mic) { 555 ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, 556 &ctx->initiator_mech_types, &buf_len, ret); 557 if (ret) { 558 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 559 free_NegTokenResp(&resp); 560 gss_release_buffer(&minor, &mech_output_token); 561 *minor_status = ret; 562 return GSS_S_FAILURE; 563 } 564 if (mech_buf.length != buf_len) 565 abort(); 566 567 if (resp.mechListMIC == NULL) { 568 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 569 free(mech_buf.value); 570 free_NegTokenResp(&resp); 571 *minor_status = 0; 572 return GSS_S_DEFECTIVE_TOKEN; 573 } 574 mic_buf.length = resp.mechListMIC->length; 575 mic_buf.value = resp.mechListMIC->data; 576 577 if (mech_output_token.length == 0) { 578 ret = gss_verify_mic(minor_status, 579 ctx->negotiated_ctx_id, 580 &mech_buf, 581 &mic_buf, 582 NULL); 583 if (ret) { 584 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 585 free(mech_buf.value); 586 gss_release_buffer(&minor, &mech_output_token); 587 free_NegTokenResp(&resp); 588 return GSS_S_DEFECTIVE_TOKEN; 589 } 590 ctx->verified_mic = 1; 591 } 592 } 593 594 ret = spnego_reply_internal(minor_status, ctx, 595 require_mic ? &mech_buf : NULL, 596 &mech_output_token, 597 output_token); 598 599 if (mech_buf.value != NULL) 600 free(mech_buf.value); 601 602 free_NegTokenResp(&resp); 603 gss_release_buffer(&minor, &mech_output_token); 604 605 if (actual_mech_type) 606 *actual_mech_type = ctx->negotiated_mech_type; 607 if (ret_flags) 608 *ret_flags = ctx->mech_flags; 609 if (time_rec) 610 *time_rec = ctx->mech_time_rec; 611 612 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 613 return ret; 614 } 615 616 OM_uint32 _gss_spnego_init_sec_context 617 (OM_uint32 * minor_status, 618 const gss_cred_id_t initiator_cred_handle, 619 gss_ctx_id_t * context_handle, 620 const gss_name_t target_name, 621 const gss_OID mech_type, 622 OM_uint32 req_flags, 623 OM_uint32 time_req, 624 const gss_channel_bindings_t input_chan_bindings, 625 const gss_buffer_t input_token, 626 gss_OID * actual_mech_type, 627 gss_buffer_t output_token, 628 OM_uint32 * ret_flags, 629 OM_uint32 * time_rec 630 ) 631 { 632 gssspnego_cred cred = (gssspnego_cred)initiator_cred_handle; 633 634 if (*context_handle == GSS_C_NO_CONTEXT) 635 return spnego_initial (minor_status, 636 cred, 637 context_handle, 638 target_name, 639 mech_type, 640 req_flags, 641 time_req, 642 input_chan_bindings, 643 input_token, 644 actual_mech_type, 645 output_token, 646 ret_flags, 647 time_rec); 648 else 649 return spnego_reply (minor_status, 650 cred, 651 context_handle, 652 target_name, 653 mech_type, 654 req_flags, 655 time_req, 656 input_chan_bindings, 657 input_token, 658 actual_mech_type, 659 output_token, 660 ret_flags, 661 time_rec); 662 } 663 664