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