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_locl.h" 35 36 /* 37 * Is target_name an sane target for `mech´. 38 */ 39 40 static OM_uint32 41 initiator_approved(gss_name_t target_name, gss_OID mech) 42 { 43 OM_uint32 min_stat, maj_stat; 44 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 45 gss_buffer_desc out; 46 47 maj_stat = gss_init_sec_context(&min_stat, 48 GSS_C_NO_CREDENTIAL, 49 &ctx, 50 target_name, 51 mech, 52 0, 53 GSS_C_INDEFINITE, 54 GSS_C_NO_CHANNEL_BINDINGS, 55 GSS_C_NO_BUFFER, 56 NULL, 57 &out, 58 NULL, 59 NULL); 60 if (GSS_ERROR(maj_stat)) { 61 gss_mg_collect_error(mech, maj_stat, min_stat); 62 return GSS_S_BAD_MECH; 63 } 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 gss_cred_id_t 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, 256 &ctx->negotiated_ctx_id, 257 ctx->target_name, 258 ctx->preferred_mech_type, 259 req_flags, 260 time_req, 261 input_chan_bindings, 262 input_token, 263 &ctx->negotiated_mech_type, 264 &mech_token, 265 &ctx->mech_flags, 266 &ctx->mech_time_rec); 267 if (GSS_ERROR(sub)) { 268 free_NegTokenInit(&ni); 269 *minor_status = minor; 270 gss_mg_collect_error(ctx->preferred_mech_type, sub, 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 gss_cred_id_t 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 NegotiationToken resp; 393 gss_OID_desc mech; 394 int require_mic; 395 size_t buf_len = 0; 396 gss_buffer_desc mic_buf, mech_buf; 397 gss_buffer_desc mech_output_token; 398 gssspnego_ctx ctx; 399 400 *minor_status = 0; 401 402 ctx = (gssspnego_ctx)*context_handle; 403 404 output_token->length = 0; 405 output_token->value = NULL; 406 407 mech_output_token.length = 0; 408 mech_output_token.value = NULL; 409 410 mech_buf.value = NULL; 411 mech_buf.length = 0; 412 413 ret = decode_NegotiationToken(input_token->value, input_token->length, 414 &resp, NULL); 415 if (ret) 416 return ret; 417 418 if (resp.element != choice_NegotiationToken_negTokenResp) { 419 free_NegotiationToken(&resp); 420 *minor_status = 0; 421 return GSS_S_BAD_MECH; 422 } 423 424 if (resp.u.negTokenResp.negResult == NULL 425 || *(resp.u.negTokenResp.negResult) == reject 426 /* || resp.u.negTokenResp.supportedMech == NULL */ 427 ) 428 { 429 free_NegotiationToken(&resp); 430 return GSS_S_BAD_MECH; 431 } 432 433 /* 434 * Pick up the mechanism that the acceptor selected, only allow it 435 * to be sent in packet. 436 */ 437 438 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 439 440 if (resp.u.negTokenResp.supportedMech) { 441 442 if (ctx->oidlen) { 443 free_NegotiationToken(&resp); 444 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 445 return GSS_S_BAD_MECH; 446 } 447 ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1, 448 sizeof(ctx->oidbuf), 449 resp.u.negTokenResp.supportedMech, 450 &ctx->oidlen); 451 /* Avoid recursively embedded SPNEGO */ 452 if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length && 453 memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, 454 GSS_SPNEGO_MECHANISM->elements, 455 ctx->oidlen) == 0)) 456 { 457 free_NegotiationToken(&resp); 458 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 459 return GSS_S_BAD_MECH; 460 } 461 462 /* check if the acceptor took our optimistic token */ 463 if (ctx->oidlen != ctx->preferred_mech_type->length || 464 memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, 465 ctx->preferred_mech_type->elements, 466 ctx->oidlen) != 0) 467 { 468 gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id, 469 GSS_C_NO_BUFFER); 470 ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; 471 } 472 } else if (ctx->oidlen == 0) { 473 free_NegotiationToken(&resp); 474 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 475 return GSS_S_BAD_MECH; 476 } 477 478 /* if a token (of non zero length), or no context, pass to underlaying mech */ 479 if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) || 480 ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { 481 gss_buffer_desc mech_input_token; 482 483 if (resp.u.negTokenResp.responseToken) { 484 mech_input_token.length = resp.u.negTokenResp.responseToken->length; 485 mech_input_token.value = resp.u.negTokenResp.responseToken->data; 486 } else { 487 mech_input_token.length = 0; 488 mech_input_token.value = NULL; 489 } 490 491 492 mech.length = ctx->oidlen; 493 mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen; 494 495 /* Fall through as if the negotiated mechanism 496 was requested explicitly */ 497 ret = gss_init_sec_context(&minor, 498 cred, 499 &ctx->negotiated_ctx_id, 500 ctx->target_name, 501 &mech, 502 req_flags, 503 time_req, 504 input_chan_bindings, 505 &mech_input_token, 506 &ctx->negotiated_mech_type, 507 &mech_output_token, 508 &ctx->mech_flags, 509 &ctx->mech_time_rec); 510 if (GSS_ERROR(ret)) { 511 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 512 free_NegotiationToken(&resp); 513 gss_mg_collect_error(&mech, ret, minor); 514 *minor_status = minor; 515 return ret; 516 } 517 if (ret == GSS_S_COMPLETE) { 518 ctx->open = 1; 519 } 520 } else if (*(resp.u.negTokenResp.negResult) == accept_completed) { 521 if (ctx->maybe_open) 522 ctx->open = 1; 523 } 524 525 if (*(resp.u.negTokenResp.negResult) == request_mic) { 526 ctx->require_mic = 1; 527 } 528 529 if (ctx->open) { 530 /* 531 * Verify the mechListMIC if one was provided or CFX was 532 * used and a non-preferred mechanism was selected 533 */ 534 if (resp.u.negTokenResp.mechListMIC != NULL) { 535 require_mic = 1; 536 } else { 537 ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, 538 &require_mic); 539 if (ret) { 540 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 541 free_NegotiationToken(&resp); 542 gss_release_buffer(&minor, &mech_output_token); 543 return ret; 544 } 545 } 546 } else { 547 require_mic = 0; 548 } 549 550 if (require_mic) { 551 ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, 552 &ctx->initiator_mech_types, &buf_len, ret); 553 if (ret) { 554 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 555 free_NegotiationToken(&resp); 556 gss_release_buffer(&minor, &mech_output_token); 557 *minor_status = ret; 558 return GSS_S_FAILURE; 559 } 560 if (mech_buf.length != buf_len) { 561 abort(); 562 UNREACHABLE(return GSS_S_FAILURE); 563 } 564 565 if (resp.u.negTokenResp.mechListMIC == NULL) { 566 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 567 free(mech_buf.value); 568 free_NegotiationToken(&resp); 569 *minor_status = 0; 570 return GSS_S_DEFECTIVE_TOKEN; 571 } 572 mic_buf.length = resp.u.negTokenResp.mechListMIC->length; 573 mic_buf.value = resp.u.negTokenResp.mechListMIC->data; 574 575 if (mech_output_token.length == 0) { 576 ret = gss_verify_mic(minor_status, 577 ctx->negotiated_ctx_id, 578 &mech_buf, 579 &mic_buf, 580 NULL); 581 if (ret) { 582 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 583 free(mech_buf.value); 584 gss_release_buffer(&minor, &mech_output_token); 585 free_NegotiationToken(&resp); 586 return GSS_S_DEFECTIVE_TOKEN; 587 } 588 ctx->verified_mic = 1; 589 } 590 } 591 592 ret = spnego_reply_internal(minor_status, ctx, 593 require_mic ? &mech_buf : NULL, 594 &mech_output_token, 595 output_token); 596 597 if (mech_buf.value != NULL) 598 free(mech_buf.value); 599 600 free_NegotiationToken(&resp); 601 gss_release_buffer(&minor, &mech_output_token); 602 603 if (actual_mech_type) 604 *actual_mech_type = ctx->negotiated_mech_type; 605 if (ret_flags) 606 *ret_flags = ctx->mech_flags; 607 if (time_rec) 608 *time_rec = ctx->mech_time_rec; 609 610 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 611 return ret; 612 } 613 614 OM_uint32 GSSAPI_CALLCONV 615 _gss_spnego_init_sec_context 616 (OM_uint32 * minor_status, 617 const gss_cred_id_t initiator_cred_handle, 618 gss_ctx_id_t * context_handle, 619 const gss_name_t target_name, 620 const gss_OID mech_type, 621 OM_uint32 req_flags, 622 OM_uint32 time_req, 623 const gss_channel_bindings_t input_chan_bindings, 624 const gss_buffer_t input_token, 625 gss_OID * actual_mech_type, 626 gss_buffer_t output_token, 627 OM_uint32 * ret_flags, 628 OM_uint32 * time_rec 629 ) 630 { 631 if (*context_handle == GSS_C_NO_CONTEXT) 632 return spnego_initial (minor_status, 633 initiator_cred_handle, 634 context_handle, 635 target_name, 636 mech_type, 637 req_flags, 638 time_req, 639 input_chan_bindings, 640 input_token, 641 actual_mech_type, 642 output_token, 643 ret_flags, 644 time_rec); 645 else 646 return spnego_reply (minor_status, 647 initiator_cred_handle, 648 context_handle, 649 target_name, 650 mech_type, 651 req_flags, 652 time_req, 653 input_chan_bindings, 654 input_token, 655 actual_mech_type, 656 output_token, 657 ret_flags, 658 time_rec); 659 } 660 661