1/* 2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved. 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 "gsskrb5_locl.h" 37 38#ifdef __APPLE__ 39 40#include <notify.h> 41#include <notify_keys.h> 42#include "kcm.h" 43 44/* 45 * Provide a negative cache that store a couple of entries in the 46 * process, this will take the edge of application that are very 47 * insistant on calling gss_init_sec_context(). 48 * 49 * The cache users noticiation from KCM to know when to clear the 50 * cache. 51 */ 52 53struct negative_cache { 54 gss_OID mech; 55 krb5_principal client; 56 krb5_principal server; 57 OM_uint32 major; 58 OM_uint32 minor; 59 const char *message; 60}; 61 62static HEIMDAL_MUTEX nc_mutex = HEIMDAL_MUTEX_INITIALIZER; 63 64static struct gnegative_cache { 65 int inited; 66 int token_cache; 67 int token_time; 68 size_t next_entry; 69 struct negative_cache cache[7]; 70} nc; 71 72static void 73free_entry(krb5_context context, struct negative_cache *e) 74{ 75 if (e->server) 76 krb5_free_principal(context, e->server); 77 if (e->client) 78 krb5_free_principal(context, e->client); 79 if (e->message) 80 krb5_free_error_message(context, e->message); 81 e->client = NULL; 82 e->server = NULL; 83 e->message = NULL; 84} 85 86static OM_uint32 87check_neg_cache(OM_uint32 *minor_status, 88 krb5_context context, 89 const gss_OID mech, 90 gss_cred_id_t gss_cred, 91 gss_name_t target_name) 92{ 93 krb5_principal server = (krb5_principal)target_name; 94 gsskrb5_cred cred = (gsskrb5_cred)gss_cred; 95 OM_uint32 major; 96 int check = 0, i; 97 98 HEIMDAL_MUTEX_lock(&nc_mutex); 99 100 if (!nc.inited) { 101 102 (void)notify_register_check(KRB5_KCM_NOTIFY_CACHE_CHANGED, &nc.token_cache); 103 (void)notify_register_check(kNotifyClockSet, &nc.token_time); 104 105 nc.inited = 1; 106 } 107 108 notify_check(nc.token_cache, &check); 109 if (!check) 110 notify_check(nc.token_time, &check); 111 112 /* if something changed, remove cache and return success */ 113 if (check) { 114 for (i = 0; i < sizeof(nc.cache)/sizeof(nc.cache[0]); i++) 115 free_entry(context, &nc.cache[i]); 116 _gss_mg_log(1, "krb5-isc: got a notification, drop negative cache"); 117 HEIMDAL_MUTEX_unlock(&nc_mutex); 118 return GSS_S_COMPLETE; 119 } 120 121 /* check if match */ 122 for (i = 0; i < sizeof(nc.cache)/sizeof(nc.cache[0]); i++) { 123 if (gss_oid_equal(nc.cache[i].mech, mech) == 0) 124 continue; 125 if (nc.cache[i].server == NULL) 126 continue; 127 if (!krb5_principal_compare(context, server, nc.cache[i].server)) 128 continue; 129 if (cred && cred->principal) { 130 if (nc.cache[i].client == NULL) 131 continue; 132 if (!krb5_principal_compare(context, cred->principal, 133 nc.cache[i].client)) 134 continue; 135 } else if (nc.cache[i].client) 136 continue; 137 138 *minor_status = nc.cache[i].minor; 139 major = nc.cache[i].major; 140 141 _gss_mg_log(1, "gss-isc: negative cache %d/%d - %s", 142 (int)nc.cache[i].major, 143 (int)nc.cache[i].minor, 144 nc.cache[i].message); 145 146 krb5_set_error_message(context, *minor_status, "%s (negative cache)", 147 nc.cache[i].message); 148 149 HEIMDAL_MUTEX_unlock(&nc_mutex); 150 return major; 151 } 152 153 HEIMDAL_MUTEX_unlock(&nc_mutex); 154 155 _gss_mg_log(1, "gss-isc: not negative cache"); 156 157 return GSS_S_COMPLETE; 158} 159 160static void 161update_neg_cache(OM_uint32 major_status, OM_uint32 minor_status, 162 krb5_context context, 163 gss_OID mech, 164 gsskrb5_cred cred, 165 krb5_principal server) 166{ 167 HEIMDAL_MUTEX_lock(&nc_mutex); 168 169 free_entry(context, &nc.cache[nc.next_entry]); 170 171 nc.cache[nc.next_entry].mech = mech; 172 krb5_copy_principal(context, server, &nc.cache[nc.next_entry].server); 173 if (cred && cred->principal) { 174 krb5_copy_principal(context, cred->principal, 175 &nc.cache[nc.next_entry].client); 176 } 177 nc.cache[nc.next_entry].major = major_status; 178 nc.cache[nc.next_entry].minor = minor_status; 179 nc.cache[nc.next_entry].message = 180 krb5_get_error_message(context, minor_status); 181 182 nc.next_entry = (nc.next_entry + 1) % 183 (sizeof(nc.cache)/sizeof(nc.cache[0])); 184 185 HEIMDAL_MUTEX_unlock(&nc_mutex); 186} 187 188#endif /* __APPLE__ */ 189 190#define GKIS(name) \ 191static \ 192OM_uint32 name(OM_uint32 *, gsskrb5_cred, gsskrb5_ctx, \ 193 krb5_context, gss_name_t, const gss_OID, \ 194 OM_uint32, OM_uint32, const gss_channel_bindings_t, \ 195 const gss_buffer_t, gss_buffer_t, \ 196 OM_uint32 *, OM_uint32 *) 197 198#ifdef PKINIT 199GKIS(init_pku2u_auth); 200GKIS(step_pku2u_auth); 201#endif 202GKIS(init_iakerb_auth); 203GKIS(step_iakerb_auth_as); 204GKIS(step_iakerb_auth_tgs); 205GKIS(init_krb5_auth); 206GKIS(step_setup_keys); 207GKIS(init_auth_step); 208GKIS(wait_repl_mutual); 209GKIS(step_completed); 210 211/* 212 * copy the addresses from `input_chan_bindings' (if any) to 213 * the auth context `ac' 214 */ 215 216static OM_uint32 217set_addresses (krb5_context context, 218 krb5_auth_context ac, 219 const gss_channel_bindings_t input_chan_bindings) 220{ 221 /* Port numbers are expected to be in application_data.value, 222 * initator's port first */ 223 224 krb5_address initiator_addr, acceptor_addr; 225 krb5_error_code kret; 226 227 if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS 228 || input_chan_bindings->application_data.length != 229 2 * sizeof(ac->local_port)) 230 return 0; 231 232 memset(&initiator_addr, 0, sizeof(initiator_addr)); 233 memset(&acceptor_addr, 0, sizeof(acceptor_addr)); 234 235 ac->local_port = 236 *(int16_t *) input_chan_bindings->application_data.value; 237 238 ac->remote_port = 239 *((int16_t *) input_chan_bindings->application_data.value + 1); 240 241 kret = _gsskrb5i_address_to_krb5addr(context, 242 input_chan_bindings->acceptor_addrtype, 243 &input_chan_bindings->acceptor_address, 244 ac->remote_port, 245 &acceptor_addr); 246 if (kret) 247 return kret; 248 249 kret = _gsskrb5i_address_to_krb5addr(context, 250 input_chan_bindings->initiator_addrtype, 251 &input_chan_bindings->initiator_address, 252 ac->local_port, 253 &initiator_addr); 254 if (kret) { 255 krb5_free_address (context, &acceptor_addr); 256 return kret; 257 } 258 259 kret = krb5_auth_con_setaddrs(context, 260 ac, 261 &initiator_addr, /* local address */ 262 &acceptor_addr); /* remote address */ 263 264 krb5_free_address (context, &initiator_addr); 265 krb5_free_address (context, &acceptor_addr); 266 267#if 0 268 free(input_chan_bindings->application_data.value); 269 input_chan_bindings->application_data.value = NULL; 270 input_chan_bindings->application_data.length = 0; 271#endif 272 273 return kret; 274} 275 276OM_uint32 277_gsskrb5_create_ctx(OM_uint32 * minor_status, 278 gss_ctx_id_t * context_handle, 279 krb5_context context, 280 const gss_channel_bindings_t input_chan_bindings, 281 gss_OID mech) 282{ 283 krb5_error_code kret; 284 gsskrb5_ctx ctx; 285 286 *context_handle = NULL; 287 288 ctx = calloc(1, sizeof(*ctx)); 289 if (ctx == NULL) { 290 *minor_status = ENOMEM; 291 return GSS_S_FAILURE; 292 } 293 ctx->mech = mech; 294 ctx->auth_context = NULL; 295 ctx->deleg_auth_context = NULL; 296 ctx->source = NULL; 297 ctx->target = NULL; 298 ctx->kcred = NULL; 299 ctx->ccache = NULL; 300 ctx->flags = 0; 301 ctx->more_flags = 0; 302 ctx->service_keyblock = NULL; 303 ctx->ticket = NULL; 304 krb5_data_zero(&ctx->fwd_data); 305 306 HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex); 307 308 kret = krb5_auth_con_init (context, &ctx->auth_context); 309 if (kret) { 310 *minor_status = kret; 311 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 312 return GSS_S_FAILURE; 313 } 314 315 kret = krb5_auth_con_init (context, &ctx->deleg_auth_context); 316 if (kret) { 317 *minor_status = kret; 318 krb5_auth_con_free(context, ctx->auth_context); 319 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 320 return GSS_S_FAILURE; 321 } 322 323 kret = set_addresses(context, ctx->auth_context, input_chan_bindings); 324 if (kret) { 325 *minor_status = kret; 326 327 krb5_auth_con_free(context, ctx->auth_context); 328 krb5_auth_con_free(context, ctx->deleg_auth_context); 329 330 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 331 332 return GSS_S_BAD_BINDINGS; 333 } 334 335 kret = set_addresses(context, ctx->deleg_auth_context, input_chan_bindings); 336 if (kret) { 337 *minor_status = kret; 338 339 krb5_auth_con_free(context, ctx->auth_context); 340 krb5_auth_con_free(context, ctx->deleg_auth_context); 341 342 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 343 344 return GSS_S_BAD_BINDINGS; 345 } 346 347 /* 348 * We need a sequence number 349 */ 350 351 krb5_auth_con_addflags(context, ctx->auth_context, 352 KRB5_AUTH_CONTEXT_DO_SEQUENCE, NULL); 353 354 /* 355 * We need a sequence number 356 */ 357 358 krb5_auth_con_addflags(context, 359 ctx->deleg_auth_context, 360 KRB5_AUTH_CONTEXT_DO_SEQUENCE | 361 KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED, 362 NULL); 363 364 *context_handle = (gss_ctx_id_t)ctx; 365 366 return GSS_S_COMPLETE; 367} 368 369/* 370 * On success, set ctx->kcred to a valid ticket 371 */ 372 373static OM_uint32 374gsskrb5_get_creds( 375 OM_uint32 * minor_status, 376 krb5_context context, 377 krb5_ccache ccache, 378 gsskrb5_ctx ctx, 379 const gss_name_t target_name, 380 int use_dns, 381 OM_uint32 time_req, 382 OM_uint32 * time_rec) 383{ 384 OM_uint32 ret; 385 krb5_error_code kret; 386 krb5_creds this_cred; 387 OM_uint32 lifetime_rec; 388 389 if (ctx->target) { 390 krb5_free_principal(context, ctx->target); 391 ctx->target = NULL; 392 } 393 if (ctx->kcred) { 394 krb5_free_creds(context, ctx->kcred); 395 ctx->kcred = NULL; 396 } 397 398 ret = _gsskrb5_canon_name(minor_status, context, use_dns, 399 ctx->source, target_name, &ctx->target); 400 if (ret) 401 return ret; 402 403 if (_krb5_have_debug(context, 1)) { 404 char *str; 405 ret = krb5_unparse_name(context, ctx->target, &str); 406 if (ret == 0) { 407 _gss_mg_log(1, "gss-krb5: ISC server %s %s", str, 408 use_dns ? "dns" : "referals"); 409 krb5_xfree(str); 410 } 411 } 412 413 memset(&this_cred, 0, sizeof(this_cred)); 414 this_cred.client = ctx->source; 415 this_cred.server = ctx->target; 416 417 if (time_req && time_req != GSS_C_INDEFINITE) { 418 krb5_timestamp ts; 419 420 krb5_timeofday (context, &ts); 421 this_cred.times.endtime = ts + time_req; 422 } 423 424 kret = krb5_get_credentials(context, 425 KRB5_TC_MATCH_REFERRAL, 426 ccache, 427 &this_cred, 428 &ctx->kcred); 429 if (kret) { 430 _gss_mg_log(1, "gss-krb5: ISC get cred failed with %d %s", kret, 431 use_dns ? "dns" : "referals"); 432 *minor_status = kret; 433 return GSS_S_FAILURE; 434 } 435 436 if (_krb5_have_debug(context, 1)) { 437 char *str; 438 ret = krb5_unparse_name(context, ctx->kcred->server, &str); 439 if (ret == 0) { 440 _gss_mg_log(1, "gss-krb5: ISC will use %s", str); 441 krb5_xfree(str); 442 } 443 } 444 445 ctx->endtime = ctx->kcred->times.endtime; 446 447 ret = _gsskrb5_lifetime_left(minor_status, context, 448 ctx->endtime, &lifetime_rec); 449 if (ret) return ret; 450 451 if (lifetime_rec == 0) { 452 _gss_mg_log(1, "gss-krb5: credentials expired"); 453 *minor_status = 0; 454 return GSS_S_CONTEXT_EXPIRED; 455 } 456 457 if (time_rec) *time_rec = lifetime_rec; 458 459 return GSS_S_COMPLETE; 460} 461 462static OM_uint32 463initiator_ready(OM_uint32 * minor_status, 464 gsskrb5_ctx ctx, 465 krb5_context context, 466 OM_uint32 *ret_flags) 467{ 468 OM_uint32 ret; 469 int32_t seq_number; 470 int is_cfx = 0; 471 472 krb5_free_creds(context, ctx->kcred); 473 ctx->kcred = NULL; 474 475 if (ctx->more_flags & CLOSE_CCACHE) 476 krb5_cc_close(context, ctx->ccache); 477 ctx->ccache = NULL; 478 479 krb5_auth_con_getremoteseqnumber (context, ctx->auth_context, &seq_number); 480 481 _gsskrb5i_is_cfx(context, ctx, 0); 482 is_cfx = (ctx->more_flags & IS_CFX); 483 484 ret = _gssapi_msg_order_create(minor_status, 485 &ctx->gk5c.order, 486 _gssapi_msg_order_f(ctx->flags), 487 seq_number, 0, is_cfx); 488 if (ret) return ret; 489 490 ctx->initiator_state = step_completed; 491 ctx->more_flags |= OPEN; 492 493 if (ret_flags) 494 *ret_flags = ctx->flags; 495 496 return GSS_S_COMPLETE; 497} 498 499/* 500 * handle delegated creds in init-sec-context 501 */ 502 503static void 504do_delegation (krb5_context context, 505 krb5_auth_context ac, 506 krb5_ccache ccache, 507 krb5_creds *cred, 508 krb5_const_principal name, 509 krb5_data *fwd_data, 510 uint32_t flagmask, 511 uint32_t *flags) 512{ 513 krb5_creds creds; 514 KDCOptions fwd_flags; 515 krb5_error_code kret; 516 517 memset (&creds, 0, sizeof(creds)); 518 krb5_data_zero (fwd_data); 519 520 kret = krb5_cc_get_principal(context, ccache, &creds.client); 521 if (kret) 522 goto out; 523 524 kret = krb5_make_principal(context, 525 &creds.server, 526 creds.client->realm, 527 KRB5_TGS_NAME, 528 creds.client->realm, 529 NULL); 530 if (kret) 531 goto out; 532 533 creds.times.endtime = 0; 534 535 memset(&fwd_flags, 0, sizeof(fwd_flags)); 536 fwd_flags.forwarded = 1; 537 fwd_flags.forwardable = 1; 538 539 if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/ 540 name->name.name_string.len < 2) 541 goto out; 542 543 /* 544 * First check for forward credentials in the cache instead of 545 * going out to the network (expensive). 546 */ 547 kret = krb5_cc_get_config(context, ccache, 548 name, "gss-forward-cache", fwd_data); 549 if (kret) { 550 kret = krb5_get_forwarded_creds(context, 551 ac, 552 ccache, 553 KDCOptions2int(fwd_flags), 554 name->name.name_string.val[1], 555 &creds, 556 fwd_data); 557 if (kret == 0) { 558 /* 559 * Ignore here here, its probably credential caches 560 * that can't delete credentials (FILE), or store the, 561 * or some other wierd configuration, anyway just 562 * ignore it. 563 */ 564 krb5_cc_set_config(context, ccache, name, 565 "gss-forward-cache", fwd_data); 566 } 567 } 568 569 out: 570 _gss_mg_log(1, "gss-krb5: delegation %s -> %s", 571 (flagmask & GSS_C_DELEG_POLICY_FLAG) ? 572 "ok-as-delegate" : "delegate", 573 fwd_data->length ? "yes" : "no"); 574 575 if (kret) 576 *flags &= ~flagmask; 577 else 578 *flags |= flagmask; 579 580 if (creds.client) 581 krb5_free_principal(context, creds.client); 582 if (creds.server) 583 krb5_free_principal(context, creds.server); 584} 585 586 587static OM_uint32 588setup_icc(krb5_context context, 589 gsskrb5_ctx ctx, 590 krb5_principal client) 591{ 592 krb5_error_code ret; 593 594 heim_assert(ctx->gic_opt == NULL, "icc already setup"); 595 596 _gss_mg_log(1, "gss-iakerb: setup_icc: cert: %s passwd: %s", 597 ctx->cert ? "yes" : "no", 598 ctx->password ? "yes" : "no"); 599 600 601 ret = krb5_get_init_creds_opt_alloc(context, &ctx->gic_opt); 602 if (ret) 603 return ret; 604 605 krb5_get_init_creds_opt_set_canonicalize(context, ctx->gic_opt, TRUE); 606 607#ifdef PKINIT 608 if (ctx->cert) { 609 char *cert_pool[2] = { "KEYCHAIN:", NULL }; 610 611 ret = krb5_get_init_creds_opt_set_pkinit(context, ctx->gic_opt, client, 612 NULL, "KEYCHAIN:", 613 cert_pool, NULL, 8, 614 NULL, NULL, NULL); 615 if (ret) 616 return ret; 617 } 618#endif 619 620 ret = krb5_init_creds_init(context, client, NULL, NULL, 0, 621 ctx->gic_opt, &ctx->asctx); 622 if (ret) 623 return ret; 624 625 626#ifndef PKINIT 627 heim_assert(ctx->password, "no password"); 628#else 629 heim_assert(ctx->password || ctx->cert, "no password nor cert ?"); 630 631 if (ctx->cert) { 632 ret = krb5_init_creds_set_pkinit_client_cert(context, ctx->asctx, ctx->cert); 633 if (ret) 634 return ret; 635 } 636#endif 637 if (ctx->password) { 638 ret = krb5_init_creds_set_password(context, ctx->asctx, ctx->password); 639 if (ret) 640 return ret; 641 } 642 643 return 0; 644} 645 646#ifdef PKINIT 647 648static OM_uint32 649init_pku2u_auth(OM_uint32 * minor_status, 650 gsskrb5_cred cred, 651 gsskrb5_ctx ctx, 652 krb5_context context, 653 gss_name_t name, 654 const gss_OID mech_type, 655 OM_uint32 req_flags, 656 OM_uint32 time_req, 657 gss_channel_bindings_t channel_bindings, 658 const gss_buffer_t input_token, 659 gss_buffer_t output_token, 660 OM_uint32 * ret_flags, 661 OM_uint32 * time_rec) 662{ 663 OM_uint32 maj_stat = GSS_S_FAILURE; 664 krb5_error_code ret; 665 krb5_principal client = NULL; 666 667 *minor_status = 0; 668 669 ctx->messages = krb5_storage_emem(); 670 if (ctx->messages == NULL) { 671 *minor_status = ENOMEM; 672 return GSS_S_FAILURE; 673 } 674 675 /* 676 * XXX Search for existing credentials here before going of and 677 * doing PK-U2U 678 */ 679 680 681 /* 682 * Pick upp the certificate from gsskrb5_cred. 683 * XXX fix better mapping for client principal. 684 */ 685 686 if (cred == NULL) { 687 gss_cred_id_t temp; 688 gsskrb5_cred cred2; 689 690 maj_stat = _gsspku2u_acquire_cred(minor_status, GSS_C_NO_NAME, 691 GSS_C_INDEFINITE, GSS_C_NO_OID_SET, 692 GSS_C_INITIATE, &temp, NULL, NULL); 693 if (maj_stat) 694 return maj_stat; 695 696 cred2 = (gsskrb5_cred)temp; 697 698 ret = krb5_copy_principal(context, cred2->principal, &client); 699 if (ret) { 700 _gsskrb5_release_cred(minor_status, &temp); 701 *minor_status = ret; 702 return GSS_S_FAILURE; 703 } 704 705 ctx->cert = hx509_cert_ref(cred2->cert); 706 _gsskrb5_release_cred(minor_status, &temp); 707 708 maj_stat = GSS_S_FAILURE; 709 } else if (cred->cert) { 710 ret = krb5_copy_principal(context, cred->principal, &client); 711 if (ret) { 712 *minor_status = ret; 713 return GSS_S_FAILURE; 714 } 715 ctx->cert = hx509_cert_ref(cred->cert); 716 717 } else { 718 *minor_status = EINVAL; 719 return GSS_S_FAILURE; 720 } 721 722 ret = setup_icc(context, ctx, client); 723 if (ret) { 724 *minor_status = ret; 725 goto out; 726 } 727 728 /* XXX should be based on target_name */ 729 ret = krb5_init_creds_set_service(context, ctx->asctx, "WELLKNOWN/NULL"); 730 if (ret) { 731 *minor_status = ret; 732 goto out; 733 } 734 735 if (krb5_principal_is_null(context, client)) { 736 InitiatorNameAssertion na; 737 hx509_name subject; 738 krb5_data data; 739 size_t size; 740 Name n; 741 742 memset(&na, 0, sizeof(na)); 743 memset(&n, 0, sizeof(n)); 744 745 na.initiatorName = calloc(1, sizeof(*na.initiatorName)); 746 if (na.initiatorName == NULL) { 747 *minor_status = ENOMEM; 748 goto out; 749 } 750 751 ret = hx509_cert_get_subject(ctx->cert, &subject); 752 if (ret) { 753 free_InitiatorNameAssertion(&na); 754 *minor_status = ret; 755 goto out; 756 } 757 758 ret = hx509_name_to_Name(subject, &n); 759 hx509_name_free(&subject); 760 if (ret) { 761 free_InitiatorNameAssertion(&na); 762 *minor_status = ret; 763 goto out; 764 } 765 766 na.initiatorName->element = 767 choice_InitiatorName_nameNotInCert; 768 na.initiatorName->u.nameNotInCert.element = 769 choice_GeneralName_directoryName; 770 na.initiatorName->u.nameNotInCert.u.directoryName.element = 771 choice_GeneralName_directoryName_rdnSequence; 772 na.initiatorName->u.nameNotInCert.u.directoryName.u.rdnSequence.len = 773 n.u.rdnSequence.len; 774 na.initiatorName->u.nameNotInCert.u.directoryName.u.rdnSequence.val = 775 n.u.rdnSequence.val; 776 777 ASN1_MALLOC_ENCODE(InitiatorNameAssertion, data.data, data.length, 778 &na, &size, ret); 779 free_InitiatorNameAssertion(&na); 780 if (ret) 781 goto out; 782 if (size != data.length) 783 krb5_abortx(context, "internal error in ASN.1 encoder"); 784 785 ret = _krb5_init_creds_set_pku2u(context, ctx->asctx, &data); 786 krb5_data_free(&data); 787 } else { 788 ret = _krb5_init_creds_set_pku2u(context, ctx->asctx, NULL); 789 } 790 791 if (ret) { 792 *minor_status = ret; 793 goto out; 794 } 795 796 maj_stat = GSS_S_COMPLETE; 797 ctx->initiator_state = step_pku2u_auth; 798 out: 799 if (client) 800 krb5_free_principal(context, client); 801 802 return maj_stat; 803} 804 805static OM_uint32 806step_pku2u_auth(OM_uint32 * minor_status, 807 gsskrb5_cred cred, 808 gsskrb5_ctx ctx, 809 krb5_context context, 810 gss_name_t name, 811 const gss_OID mech_type, 812 OM_uint32 req_flags, 813 OM_uint32 time_req, 814 gss_channel_bindings_t channel_bindings, 815 const gss_buffer_t input_token, 816 gss_buffer_t output_token, 817 OM_uint32 * ret_flags, 818 OM_uint32 * time_rec) 819{ 820 OM_uint32 maj_stat; 821 unsigned int flags = 0; 822 krb5_error_code ret; 823 krb5_data in, out; 824 825 krb5_data_zero(&out); 826 827 if (input_token && input_token->length) { 828 829 krb5_storage_write(ctx->messages, 830 input_token->value, 831 input_token->length); 832 833 maj_stat = _gsskrb5_decapsulate (minor_status, 834 input_token, 835 &in, 836 "\x06\x00", 837 ctx->mech); 838 if (maj_stat) 839 return maj_stat; 840 } else 841 krb5_data_zero(&in); 842 843 maj_stat = GSS_S_FAILURE; 844 845 ret = krb5_init_creds_step(context, ctx->asctx, 846 &in, &out, NULL, NULL, &flags); 847 if (ret) 848 goto out; 849 850 if ((flags & 1) == 0) { 851 852 ctx->kcred = calloc(1, sizeof(*ctx->kcred)); 853 if (ctx->kcred == NULL) { 854 ret = ENOMEM; 855 goto out; 856 } 857 858 /* 859 * Pull out credential and store in ctx->kcred and just use 860 * that as the credential in AP-REQ. 861 */ 862 863 ret = krb5_init_creds_get_creds(context, ctx->asctx, ctx->kcred); 864 krb5_init_creds_free(context, ctx->asctx); 865 ctx->asctx = NULL; 866 if (ret) 867 goto out; 868 869 ret = krb5_copy_principal(context, ctx->kcred->client, &ctx->source); 870 if (ret) 871 goto out; 872 ret = krb5_copy_principal(context, ctx->kcred->server, &ctx->target); 873 if (ret) 874 goto out; 875 876 /* XXX store credential in credential cache */ 877#if 0 878 { 879 krb5_ccache id = NULL; 880 881 ret = krb5_cc_new_unique(context, "API", NULL, &id); 882 if (ret == 0) 883 ret = krb5_cc_initialize(context, id, ctx->kcred->client); 884 if (ret == 0) 885 krb5_cc_store_cred(context, id, ctx->kcred); 886 if (id) 887 krb5_cc_close(id); 888 } 889#endif 890 891 maj_stat = GSS_S_COMPLETE; 892 ctx->initiator_state = step_setup_keys; 893 894 } else { 895 ret = _gsskrb5_encapsulate (minor_status, &out, output_token, 896 (u_char *)"\x05\x00", ctx->mech); 897 if (ret) 898 goto out; 899 900 krb5_storage_write(ctx->messages, 901 output_token->value, 902 output_token->length); 903 904 maj_stat = GSS_S_CONTINUE_NEEDED; 905 } 906 907 out: 908 *minor_status = ret; 909 910 return maj_stat; 911} 912 913#endif /* PKINIT */ 914 915 916/* 917 * IAKERB 918 */ 919 920OM_uint32 921_gsskrb5_iakerb_parse_header(OM_uint32 *minor_status, 922 krb5_context context, 923 gsskrb5_ctx ctx, 924 const gss_buffer_t input_token, 925 krb5_data *data) 926{ 927 krb5_error_code ret; 928 OM_uint32 maj_stat; 929 uint8_t type[2]; 930 size_t size; 931 932 maj_stat = _gssapi_decapsulate(minor_status, input_token, type, data, GSS_IAKERB_MECHANISM); 933 if (maj_stat) 934 return maj_stat; 935 936 if (memcmp(type, "\x05\x01", 2) == 0) { 937 IAKERB_HEADER header; 938 939 ret = decode_IAKERB_HEADER(data->data, data->length, &header, &size); 940 if (ret) { 941 *minor_status = ret; 942 return GSS_S_FAILURE; 943 } 944 945 heim_assert(data->length >= size, "internal asn1 decoder failure"); 946 947 data->data = ((uint8_t *)data->data) + size; 948 data->length -= size; 949 950 if (header.cookie) { 951 if (ctx->cookie) 952 krb5_free_data(context, ctx->cookie); 953 (void)krb5_copy_data(context, header.cookie, &ctx->cookie); 954 } 955 956 if (ctx->iakerbrealm) 957 free(ctx->iakerbrealm); 958 ctx->iakerbrealm = strdup(header.target_realm); 959 960 free_IAKERB_HEADER(&header); 961 962 return GSS_S_COMPLETE; 963 964 } else if (memcmp(type, "\x03\x00", 2) == 0) { 965 /* XXX parse KRB-ERROR and set appropriate minor code */ 966 *minor_status = 0; 967 return GSS_S_FAILURE; 968 } else { 969 *minor_status = 0; 970 return GSS_S_DEFECTIVE_TOKEN; 971 } 972} 973 974OM_uint32 975_gsskrb5_iakerb_make_header(OM_uint32 *minor_status, 976 krb5_context context, 977 gsskrb5_ctx ctx, 978 krb5_realm realm, 979 krb5_data *kdata, 980 gss_buffer_t output_token) 981{ 982 IAKERB_HEADER header; 983 unsigned char *data; 984 size_t length, size; 985 OM_uint32 maj_stat; 986 krb5_data iadata; 987 int ret; 988 989 header.target_realm = realm; 990 header.cookie = ctx->cookie; 991 992 ASN1_MALLOC_ENCODE(IAKERB_HEADER, data, length, &header, &size, ret); 993 if (ret) { 994 *minor_status = ret; 995 return GSS_S_FAILURE; 996 } 997 heim_assert(length == size, "internal asn1 encoder error"); 998 999 if (ctx->cookie) { 1000 krb5_free_data(context, ctx->cookie); 1001 ctx->cookie = NULL; 1002 } 1003 1004 iadata.length = length + kdata->length; 1005 iadata.data = malloc(iadata.length); 1006 if (iadata.data == NULL) { 1007 free(data); 1008 *minor_status = ENOMEM; 1009 return GSS_S_FAILURE; 1010 } 1011 memcpy(iadata.data, data, length); 1012 memcpy(((uint8_t *)iadata.data) + length, kdata->data, kdata->length); 1013 free(data); 1014 1015 maj_stat = _gsskrb5_encapsulate(minor_status, &iadata, output_token, 1016 (u_char *)"\x05\x01", ctx->mech); 1017 free(iadata.data); 1018 1019 return maj_stat; 1020} 1021 1022static OM_uint32 1023init_iakerb_auth(OM_uint32 * minor_status, 1024 gsskrb5_cred cred, 1025 gsskrb5_ctx ctx, 1026 krb5_context context, 1027 gss_name_t target_name, 1028 const gss_OID mech_type, 1029 OM_uint32 req_flags, 1030 OM_uint32 time_req, 1031 gss_channel_bindings_t channel_bindings, 1032 const gss_buffer_t input_token, 1033 gss_buffer_t output_token, 1034 OM_uint32 * ret_flags, 1035 OM_uint32 * time_rec) 1036{ 1037 krb5_error_code ret; 1038 1039 ctx->messages = krb5_storage_emem(); 1040 if (ctx->messages == NULL) { 1041 *minor_status = ENOMEM; 1042 return GSS_S_FAILURE; 1043 } 1044 1045 /* 1046 * XXX 1047 */ 1048 1049 if (cred == NULL) 1050 return GSS_S_FAILURE; 1051 1052 ret = krb5_copy_principal(context, cred->principal, &ctx->source); 1053 if (ret) { 1054 *minor_status = ret; 1055 return GSS_S_FAILURE; 1056 } 1057 1058 /* XXX this kind of sucks, forcing referrals and then fixing up LKDC realm later */ 1059 ret = krb5_copy_principal(context, (krb5_const_principal)target_name, &ctx->target); 1060 if (ret) { 1061 *minor_status = ret; 1062 return GSS_S_FAILURE; 1063 } 1064 krb5_principal_set_realm(context, ctx->target, ctx->source->realm); 1065 1066 if (cred->password) { 1067 1068 ctx->password = strdup(cred->password); 1069 if (ctx->password == NULL) { 1070 *minor_status = ENOMEM; 1071 return GSS_S_FAILURE; 1072 } 1073 1074#ifdef PKINIT 1075 } else if (cred->cert) { 1076 1077 ctx->cert = heim_retain(cred->cert); 1078#endif 1079 } else if (cred->cred_flags & GSS_CF_IAKERB_RESOLVED) { 1080 /* all work done in auth_tgs */ 1081 } else { 1082 1083 *minor_status = EINVAL; 1084 return GSS_S_FAILURE; 1085 } 1086 1087 ctx->ccache = cred->ccache; 1088 1089 /* capture ctx->ccache */ 1090 krb5_cc_get_config(context, ctx->ccache, NULL, "FriendlyName", &ctx->friendlyname); 1091 krb5_cc_get_config(context, ctx->ccache, NULL, "lkdc-hostname", &ctx->lkdchostname); 1092 1093 if (cred->cred_flags & GSS_CF_IAKERB_RESOLVED) { 1094 ctx->initiator_state = step_iakerb_auth_tgs; 1095 } else { 1096 ctx->initiator_state = step_iakerb_auth_as; 1097 } 1098 1099 *minor_status = 0; 1100 1101 1102 return GSS_S_COMPLETE; 1103} 1104 1105static OM_uint32 1106step_iakerb_auth_as(OM_uint32 * minor_status, 1107 gsskrb5_cred cred, 1108 gsskrb5_ctx ctx, 1109 krb5_context context, 1110 gss_name_t name, 1111 const gss_OID mech_type, 1112 OM_uint32 req_flags, 1113 OM_uint32 time_req, 1114 gss_channel_bindings_t channel_bindings, 1115 const gss_buffer_t input_token, 1116 gss_buffer_t output_token, 1117 OM_uint32 * ret_flags, 1118 OM_uint32 * time_rec) 1119{ 1120 OM_uint32 maj_stat; 1121 krb5_data in, out; 1122 krb5_realm realm = NULL; 1123 krb5_error_code ret; 1124 unsigned int flags = 0; 1125 1126 if (ctx->asctx == NULL) { 1127 /* first packet */ 1128 1129 ret = setup_icc(context, ctx, ctx->source); 1130 if (ret) { 1131 *minor_status = ret; 1132 return GSS_S_FAILURE; 1133 } 1134 1135 krb5_data_zero(&in); 1136 1137 } else { 1138 1139 krb5_storage_write(ctx->messages, 1140 input_token->value, 1141 input_token->length); 1142 1143 maj_stat = _gsskrb5_iakerb_parse_header(minor_status, context, ctx, input_token, &in); 1144 if (maj_stat) 1145 return maj_stat; 1146 } 1147 1148 ret = krb5_init_creds_step(context, ctx->asctx, &in, &out, NULL, &realm, &flags); 1149 if (ret) { 1150 _gss_mg_log(1, "gss-iakerb: init_creds_step: %d", ret); 1151 _gsskrb5_error_token(minor_status, ctx->mech, context, ret, 1152 NULL, NULL, output_token); 1153 *minor_status = ret; 1154 return GSS_S_FAILURE; 1155 } 1156 1157 if (flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) { 1158 1159 heim_assert(realm != NULL, "krb5_init_creds_step return data w/o a realm"); 1160 1161 maj_stat = _gsskrb5_iakerb_make_header(minor_status, context, ctx, realm, &out, output_token); 1162 if (maj_stat) 1163 return maj_stat; 1164 1165 krb5_storage_write(ctx->messages, 1166 output_token->value, 1167 output_token->length); 1168 1169 1170 return GSS_S_CONTINUE_NEEDED; 1171 1172 } else { 1173 krb5_creds kcred; 1174 1175 memset(&kcred, 0, sizeof(kcred)); 1176 1177 _gss_mg_log(1, "gss-iakerb: going to state auth-tgs"); 1178 1179 heim_assert(out.length == 0, "output of AS-REQ not 0 when done"); 1180 1181 /* store credential in credential cache and lets continue the TGS REQ */ 1182 ret = krb5_init_creds_get_creds(context, ctx->asctx, &kcred); 1183 if (ret) { 1184 *minor_status = ret; 1185 _gsskrb5_error_token(minor_status, ctx->mech, context, ret, 1186 NULL, NULL, output_token); 1187 return GSS_S_FAILURE; 1188 } 1189 1190 //(void)krb5_cc_new_unique(context, "MEMORY", NULL, &ctx->ccache); 1191 (void)krb5_cc_initialize(context, ctx->ccache, kcred.client); 1192 (void)krb5_cc_store_cred(context, ctx->ccache, &kcred); 1193 if (ctx->password) { 1194 krb5_data pw; 1195 pw.data = ctx->password; 1196 pw.length = strlen(ctx->password); 1197 (void)krb5_cc_set_config(context, ctx->ccache, NULL, "password", &pw); 1198 } 1199#ifdef PKINIT 1200 if (ctx->cert) { 1201 krb5_data pd; 1202 ret = hx509_cert_get_persistent(ctx->cert, &pd); 1203 if (ret) { 1204 *minor_status = ret; 1205 return GSS_S_FAILURE; 1206 } 1207 krb5_cc_set_config(context, ctx->ccache, NULL, "certificate-ref", &pd); 1208 der_free_octet_string(&pd); 1209 } 1210#endif 1211 if (ctx->friendlyname.length) 1212 krb5_cc_set_config(context, ctx->ccache, NULL, "FriendlyName", &ctx->friendlyname); 1213 if (ctx->lkdchostname.length) { 1214 krb5_data data = { 1, "1" } ; 1215 krb5_cc_set_config(context, ctx->ccache, NULL, "lkdc-hostname", &ctx->lkdchostname); 1216 krb5_cc_set_config(context, ctx->ccache, NULL, "nah-created", &data); 1217 krb5_cc_set_config(context, ctx->ccache, NULL, "iakerb", &data); 1218 } 1219 1220 /* update source if we got a referrals */ 1221 krb5_free_principal(context, ctx->source); 1222 (void)krb5_copy_principal(context, kcred.client, &ctx->source); 1223 1224 krb5_free_cred_contents(context, &kcred); 1225 1226 ctx->initiator_state = step_iakerb_auth_tgs; 1227 1228 return GSS_S_COMPLETE; 1229 } 1230 1231} 1232 1233static OM_uint32 1234step_iakerb_auth_tgs(OM_uint32 * minor_status, 1235 gsskrb5_cred cred, 1236 gsskrb5_ctx ctx, 1237 krb5_context context, 1238 gss_name_t name, 1239 const gss_OID mech_type, 1240 OM_uint32 req_flags, 1241 OM_uint32 time_req, 1242 gss_channel_bindings_t channel_bindings, 1243 const gss_buffer_t input_token, 1244 gss_buffer_t output_token, 1245 OM_uint32 * ret_flags, 1246 OM_uint32 * time_rec) 1247{ 1248 krb5_realm realm = NULL; 1249 OM_uint32 maj_stat; 1250 krb5_data in, out; 1251 krb5_error_code ret; 1252 unsigned int flags = 0; 1253 1254 *minor_status = 0; 1255 krb5_data_zero(&out); 1256 1257 if (ctx->tgsctx == NULL) { 1258 krb5_creds incred; 1259 1260 memset(&incred, 0, sizeof(incred)); 1261 1262 incred.client = ctx->source; 1263 incred.server = ctx->target; 1264 1265 ret = krb5_tkt_creds_init(context, ctx->ccache, &incred, 0, &ctx->tgsctx); 1266 if (ret) { 1267 *minor_status = ret; 1268 _gsskrb5_error_token(minor_status, ctx->mech, context, ret, 1269 NULL, NULL, output_token); 1270 return GSS_S_FAILURE; 1271 } 1272 1273 } else { 1274 1275 krb5_storage_write(ctx->messages, 1276 input_token->value, 1277 input_token->length); 1278 1279 maj_stat = _gsskrb5_iakerb_parse_header(minor_status, context, ctx, input_token, &in); 1280 if (maj_stat) 1281 return maj_stat; 1282 } 1283 1284 ret = krb5_tkt_creds_step(context, ctx->tgsctx, &in, &out, &realm, &flags); 1285 if (ret) { 1286 _gss_mg_log(1, "gss-iakerb: tkt_creds_step: %d", ret); 1287 _gsskrb5_error_token(minor_status, ctx->mech, context, ret, 1288 NULL, NULL, output_token); 1289 *minor_status = ret; 1290 return GSS_S_FAILURE; 1291 } 1292 1293 if (flags & KRB5_TKT_STATE_CONTINUE) { 1294 1295 maj_stat = _gsskrb5_iakerb_make_header(minor_status, context, ctx, realm, &out, output_token); 1296 krb5_data_free(&out); 1297 if (maj_stat != GSS_S_COMPLETE) 1298 return maj_stat; 1299 1300 krb5_storage_write(ctx->messages, 1301 output_token->value, 1302 output_token->length); 1303 1304 return GSS_S_CONTINUE_NEEDED; 1305 1306 } else { 1307 heim_assert(out.length == 0, "output data is not zero"); 1308 1309 _gss_mg_log(1, "gss-iakerb: going to state setup-keys"); 1310 1311 ret = krb5_tkt_creds_get_creds(context, ctx->tgsctx, &ctx->kcred); 1312 if (ret) { 1313 _gss_mg_log(1, "gss-iakerb: tkt_get_creds: %d", ret); 1314 _gsskrb5_error_token(minor_status, ctx->mech, context, ret, 1315 NULL, NULL, output_token); 1316 *minor_status = ret; 1317 return GSS_S_FAILURE; 1318 } 1319 1320 ctx->endtime = ctx->kcred->times.endtime; 1321 1322 ctx->initiator_state = step_setup_keys; 1323 1324 return GSS_S_COMPLETE; 1325 } 1326} 1327 1328/* 1329 * KRB5 1330 */ 1331 1332static OM_uint32 1333init_krb5_auth(OM_uint32 * minor_status, 1334 gsskrb5_cred cred, 1335 gsskrb5_ctx ctx, 1336 krb5_context context, 1337 gss_name_t name, 1338 const gss_OID mech_type, 1339 OM_uint32 req_flags, 1340 OM_uint32 time_req, 1341 const gss_channel_bindings_t input_chan_bindings, 1342 const gss_buffer_t input_token, 1343 gss_buffer_t output_token, 1344 OM_uint32 * ret_flags, 1345 OM_uint32 * time_rec) 1346{ 1347 OM_uint32 ret = GSS_S_FAILURE; 1348 krb5_error_code kret; 1349 krb5_data outbuf; 1350 krb5_data fwd_data; 1351 OM_uint32 lifetime_rec; 1352 int allow_dns = 1; 1353 1354 krb5_data_zero(&outbuf); 1355 krb5_data_zero(&fwd_data); 1356 1357 *minor_status = 0; 1358 1359 /* 1360 * If ctx->ccache is set, we already have a cache and source 1361 */ 1362 1363 if (ctx->ccache == NULL) { 1364 1365 if (cred == NULL) { 1366 kret = krb5_cc_default (context, &ctx->ccache); 1367 if (kret) { 1368 *minor_status = kret; 1369 ret = GSS_S_FAILURE; 1370 goto failure; 1371 } 1372 ctx->more_flags |= CLOSE_CCACHE; 1373 } else 1374 ctx->ccache = cred->ccache; 1375 1376 kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source); 1377 if (kret) { 1378 *minor_status = kret; 1379 ret = GSS_S_FAILURE; 1380 goto failure; 1381 } 1382 } 1383 1384 /* 1385 * This is hideous glue for (NFS) clients that wants to limit the 1386 * available enctypes to what it can support (encryption in 1387 * kernel). If there is no enctypes selected for this credential, 1388 * reset it to the default set of enctypes. 1389 */ 1390 { 1391 krb5_enctype *enctypes = NULL; 1392 1393 if (cred && cred->enctypes) 1394 enctypes = cred->enctypes; 1395 krb5_set_default_in_tkt_etypes(context, enctypes); 1396 } 1397 1398 /* canon name if needed for client + target realm */ 1399 kret = krb5_cc_get_config(context, ctx->ccache, NULL, 1400 "realm-config", &outbuf); 1401 if (kret == 0) { 1402 /* XXX 2 is no server canon */ 1403 if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2)) 1404 allow_dns = 0; 1405 krb5_data_free(&outbuf); 1406 } 1407 1408 if (_gss_mg_log_level(1)) { 1409 char *str; 1410 ret = krb5_unparse_name(context, ctx->source, &str); 1411 if (ret == 0) { 1412 _gss_mg_log(1, "gss-krb5: ISC client: %s", str); 1413 krb5_xfree(str); 1414 } 1415 } 1416 1417 /* 1418 * First we try w/o dns, hope that the KDC have register alias 1419 * (and referrals if cross realm) for this principal. If that 1420 * fails and if we are allowed to using this realm try again with 1421 * DNS canonicalizion. 1422 */ 1423 ret = gsskrb5_get_creds(minor_status, context, ctx->ccache, 1424 ctx, name, 0, time_req, 1425 time_rec); 1426 if (ret && allow_dns) 1427 ret = gsskrb5_get_creds(minor_status, context, ctx->ccache, 1428 ctx, name, 1, time_req, 1429 time_rec); 1430 if (ret) 1431 goto failure; 1432 1433 ctx->endtime = ctx->kcred->times.endtime; 1434 1435 ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); 1436 if (ret) 1437 goto failure; 1438 1439 ret = _gsskrb5_lifetime_left(minor_status, 1440 context, 1441 ctx->endtime, 1442 &lifetime_rec); 1443 if (ret) 1444 goto failure; 1445 1446 if (lifetime_rec == 0) { 1447 _gss_mg_log(1, "gss-krb5: credentials expired"); 1448 *minor_status = 0; 1449 ret = GSS_S_CONTEXT_EXPIRED; 1450 goto failure; 1451 } 1452 1453 ctx->initiator_state = step_setup_keys; 1454 1455 return GSS_S_COMPLETE; 1456 1457failure: 1458 1459#ifdef __APPLE__ 1460 if (GSS_ERROR(ret)) 1461 update_neg_cache(ret, *minor_status, context, 1462 ctx->mech, cred, (krb5_principal)name); 1463#endif 1464 1465 return ret; 1466} 1467 1468/* 1469 * Common part of iakerb, pku2u and krb5 1470 */ 1471 1472static OM_uint32 1473step_setup_keys(OM_uint32 * minor_status, 1474 gsskrb5_cred cred, 1475 gsskrb5_ctx ctx, 1476 krb5_context context, 1477 gss_name_t name, 1478 const gss_OID mech_type, 1479 OM_uint32 req_flags, 1480 OM_uint32 time_req, 1481 const gss_channel_bindings_t input_chan_bindings, 1482 const gss_buffer_t input_token, 1483 gss_buffer_t output_token, 1484 OM_uint32 * ret_flags, 1485 OM_uint32 * time_rec) 1486{ 1487 krb5_error_code kret; 1488 1489 heim_assert(ctx->kcred != NULL, "gsskrb5 context is missing kerberos credential"); 1490 1491 krb5_auth_con_setkey(context, 1492 ctx->auth_context, 1493 &ctx->kcred->session); 1494 1495 kret = krb5_auth_con_generatelocalsubkey(context, 1496 ctx->auth_context, 1497 &ctx->kcred->session); 1498 if (kret) { 1499 *minor_status = kret; 1500 return GSS_S_FAILURE; 1501 } 1502 1503 ctx->initiator_state = init_auth_step; 1504 1505 return GSS_S_COMPLETE; 1506} 1507 1508 1509static OM_uint32 1510init_auth_step(OM_uint32 * minor_status, 1511 gsskrb5_cred cred, 1512 gsskrb5_ctx ctx, 1513 krb5_context context, 1514 gss_name_t name, 1515 const gss_OID mech_type, 1516 OM_uint32 req_flags, 1517 OM_uint32 time_req, 1518 const gss_channel_bindings_t input_chan_bindings, 1519 const gss_buffer_t input_token, 1520 gss_buffer_t output_token, 1521 OM_uint32 * ret_flags, 1522 OM_uint32 * time_rec) 1523{ 1524 krb5_crypto crypto = NULL; 1525 OM_uint32 ret = GSS_S_FAILURE; 1526 krb5_error_code kret; 1527 krb5_flags ap_options; 1528 krb5_data outbuf; 1529 uint32_t flags; 1530 krb5_data authenticator; 1531 Checksum cksum; 1532 krb5_enctype enctype; 1533 1534 krb5_data fwd_data, timedata, pkt_cksum; 1535 int32_t offset = 0, oldoffset = 0; 1536 uint32_t flagmask; 1537 1538 krb5_data_zero(&outbuf); 1539 krb5_data_zero(&fwd_data); 1540 krb5_data_zero(&pkt_cksum); 1541 memset(&cksum, 0, sizeof(cksum)); 1542 1543 *minor_status = 0; 1544 1545 /* 1546 * If the credential doesn't have ok-as-delegate, check if there 1547 * is a realm setting and use that. 1548 */ 1549 if (!ctx->kcred->flags.b.ok_as_delegate && ctx->ccache) { 1550 krb5_data data; 1551 1552 ret = krb5_cc_get_config(context, ctx->ccache, NULL, 1553 "realm-config", &data); 1554 if (ret == 0) { 1555 /* XXX 1 is use ok-as-delegate */ 1556 if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0) 1557 req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); 1558 krb5_data_free(&data); 1559 } 1560 } 1561 1562 flagmask = 0; 1563 1564 /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */ 1565 if ((req_flags & GSS_C_DELEG_POLICY_FLAG) 1566 && ctx->kcred->flags.b.ok_as_delegate) 1567 flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG; 1568 /* if there still is a GSS_C_DELEG_FLAG, use that */ 1569 if (req_flags & GSS_C_DELEG_FLAG) 1570 flagmask |= GSS_C_DELEG_FLAG; 1571 1572 1573 flags = 0; 1574 ap_options = 0; 1575 if ((flagmask & GSS_C_DELEG_FLAG) && ctx->ccache) { 1576 do_delegation (context, 1577 ctx->deleg_auth_context, 1578 ctx->ccache, ctx->kcred, ctx->target, 1579 &fwd_data, flagmask, &flags); 1580 } 1581 1582 if (req_flags & GSS_C_MUTUAL_FLAG) { 1583 flags |= GSS_C_MUTUAL_FLAG; 1584 ap_options |= AP_OPTS_MUTUAL_REQUIRED; 1585 } 1586 1587 if (req_flags & GSS_C_REPLAY_FLAG) 1588 flags |= GSS_C_REPLAY_FLAG; 1589 if (req_flags & GSS_C_SEQUENCE_FLAG) 1590 flags |= GSS_C_SEQUENCE_FLAG; 1591#if 0 1592 if (req_flags & GSS_C_ANON_FLAG) 1593 ; /* XXX */ 1594#endif 1595 if (req_flags & GSS_C_DCE_STYLE) { 1596 /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */ 1597 flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG; 1598 ap_options |= AP_OPTS_MUTUAL_REQUIRED; 1599 } 1600 if (req_flags & GSS_C_IDENTIFY_FLAG) 1601 flags |= GSS_C_IDENTIFY_FLAG; 1602 if (req_flags & GSS_C_EXTENDED_ERROR_FLAG) 1603 flags |= GSS_C_EXTENDED_ERROR_FLAG; 1604 1605 if (req_flags & GSS_C_CONF_FLAG) { 1606 flags |= GSS_C_CONF_FLAG; 1607 } 1608 if (req_flags & GSS_C_INTEG_FLAG) { 1609 flags |= GSS_C_INTEG_FLAG; 1610 } 1611 if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) { 1612 flags |= GSS_C_CONF_FLAG; 1613 flags |= GSS_C_INTEG_FLAG; 1614 } 1615 flags |= GSS_C_TRANS_FLAG; 1616 1617 ctx->flags = flags; 1618 ctx->more_flags |= LOCAL; 1619 1620 ret = krb5_crypto_init(context, ctx->auth_context->local_subkey, 1621 0, &crypto); 1622 if (ret) { 1623 goto failure; 1624 } 1625 1626 if (ctx->messages) { 1627 GSS_KRB5_FINISHED pkt; 1628 krb5_data pkts; 1629 size_t size; 1630 1631 memset(&pkt, 0, sizeof(pkt)); 1632 1633 ret = krb5_storage_to_data(ctx->messages, &pkts); 1634 krb5_storage_free(ctx->messages); 1635 ctx->messages = NULL; 1636 if (ret) 1637 goto failure; 1638 1639 ret = krb5_create_checksum(context, 1640 crypto, 1641 KRB5_KU_FINISHED, 1642 0, 1643 pkts.data, 1644 pkts.length, 1645 &pkt.gss_mic); 1646 krb5_data_free(&pkts); 1647 if (ret) 1648 goto failure; 1649 1650 ASN1_MALLOC_ENCODE(GSS_KRB5_FINISHED, 1651 pkt_cksum.data, pkt_cksum.length, 1652 &pkt, &size, ret); 1653 free_GSS_KRB5_FINISHED(&pkt); 1654 if (ret) 1655 goto failure; 1656 if (pkt_cksum.length != size) 1657 krb5_abortx(context, "internal error in ASN.1 encoder"); 1658 } 1659 1660 ret = _gsskrb5_create_8003_checksum (minor_status, 1661 context, 1662 crypto, 1663 input_chan_bindings, 1664 flags, 1665 &fwd_data, 1666 &pkt_cksum, 1667 &cksum); 1668 if (ret) 1669 goto failure; 1670 1671 enctype = ctx->auth_context->keyblock->keytype; 1672 1673 if (ctx->ccache) { 1674 1675 ret = krb5_cc_get_config(context, ctx->ccache, ctx->target, 1676 "time-offset", &timedata); 1677 if (ret == 0) { 1678 if (timedata.length == 4) { 1679 const u_char *p = timedata.data; 1680 offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0); 1681 } 1682 krb5_data_free(&timedata); 1683 } 1684 1685 if (offset) { 1686 krb5_get_kdc_sec_offset(context, &oldoffset, NULL); 1687 krb5_set_kdc_sec_offset(context, offset, -1); 1688 } 1689 } 1690 1691 kret = _krb5_build_authenticator(context, 1692 ctx->auth_context, 1693 enctype, 1694 ctx->kcred, 1695 &cksum, 1696 &authenticator, 1697 KRB5_KU_AP_REQ_AUTH); 1698 1699 if (kret) { 1700 if (offset) 1701 krb5_set_kdc_sec_offset (context, oldoffset, -1); 1702 *minor_status = kret; 1703 ret = GSS_S_FAILURE; 1704 goto failure; 1705 } 1706 1707 kret = krb5_build_ap_req (context, 1708 enctype, 1709 ctx->kcred, 1710 ap_options, 1711 authenticator, 1712 &outbuf); 1713 if (offset) 1714 krb5_set_kdc_sec_offset (context, oldoffset, -1); 1715 if (kret) { 1716 *minor_status = kret; 1717 ret = GSS_S_FAILURE; 1718 goto failure; 1719 } 1720 1721 if (flags & GSS_C_DCE_STYLE) { 1722 output_token->value = outbuf.data; 1723 output_token->length = outbuf.length; 1724 } else { 1725 ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token, 1726 (u_char *)(intptr_t)"\x01\x00", ctx->mech); 1727 krb5_data_free (&outbuf); 1728 if (ret) 1729 goto failure; 1730 } 1731 1732 if (crypto) 1733 krb5_crypto_destroy(context, crypto); 1734 free_Checksum(&cksum); 1735 krb5_data_free (&fwd_data); 1736 krb5_data_free (&pkt_cksum); 1737 1738 if (flags & GSS_C_MUTUAL_FLAG) { 1739 ctx->initiator_state = wait_repl_mutual; 1740 return GSS_S_CONTINUE_NEEDED; 1741 } 1742 1743 return initiator_ready(minor_status, ctx, context, ret_flags); 1744 1745failure: 1746 if (crypto) 1747 krb5_crypto_destroy(context, crypto); 1748 free_Checksum(&cksum); 1749 krb5_data_free (&fwd_data); 1750 krb5_data_free (&pkt_cksum); 1751 return ret; 1752} 1753 1754static OM_uint32 1755handle_error_packet(OM_uint32 *minor_status, 1756 krb5_context context, 1757 gsskrb5_ctx ctx, 1758 krb5_data indata) 1759{ 1760 krb5_error_code kret; 1761 KRB_ERROR error; 1762 1763 heim_assert(ctx->initiator_state == wait_repl_mutual, 1764 "handle_error in wrong state"); 1765 1766 kret = krb5_rd_error(context, &indata, &error); 1767 if (kret == 0) { 1768 kret = krb5_error_from_rd_error(context, &error, NULL); 1769 1770 /* 1771 * If we get back an error code that we know about, then lets retry again: 1772 * - KRB5KRB_AP_ERR_MODIFIED: delete entry and retry 1773 * - KRB5KRB_AP_ERR_SKEW: time skew sent, retry again with right time skew 1774 */ 1775 1776 if (kret == KRB5KRB_AP_ERR_MODIFIED && ctx->ccache) { 1777 1778 if ((ctx->more_flags & RETRIED_NEWTICKET) == 0) { 1779 krb5_creds mcreds; 1780 1781 krb5_cc_clear_mcred(&mcreds); 1782 mcreds.client = ctx->source; 1783 mcreds.server = ctx->target; 1784 1785 krb5_cc_remove_cred(context, ctx->ccache, 0, &mcreds); 1786 1787 ctx->initiator_state = init_krb5_auth; 1788 } 1789 ctx->more_flags |= RETRIED_NEWTICKET; 1790 1791 } else if (kret == KRB5KRB_AP_ERR_SKEW && ctx->ccache) { 1792 1793 if ((ctx->more_flags & RETRIED_SKEW) == 0) { 1794 krb5_data timedata; 1795 uint8_t p[4]; 1796 int32_t t = (int32_t)(error.stime - time(NULL)); 1797 1798 _gss_mg_encode_be_uint32(t, p); 1799 1800 timedata.data = p; 1801 timedata.length = sizeof(p); 1802 1803 krb5_cc_set_config(context, ctx->ccache, ctx->target, 1804 "time-offset", &timedata); 1805 1806 ctx->initiator_state = init_auth_step; 1807 } 1808 ctx->more_flags |= RETRIED_SKEW; 1809 } 1810 free_KRB_ERROR (&error); 1811 } 1812 1813 if (ctx->initiator_state != wait_repl_mutual) 1814 return GSS_S_COMPLETE; 1815 1816 *minor_status = kret; 1817 return GSS_S_FAILURE; 1818 1819} 1820 1821 1822static OM_uint32 1823wait_repl_mutual(OM_uint32 * minor_status, 1824 gsskrb5_cred cred, 1825 gsskrb5_ctx ctx, 1826 krb5_context context, 1827 gss_name_t name, 1828 const gss_OID mech_type, 1829 OM_uint32 req_flags, 1830 OM_uint32 time_req, 1831 const gss_channel_bindings_t input_chan_bindings, 1832 const gss_buffer_t input_token, 1833 gss_buffer_t output_token, 1834 OM_uint32 * ret_flags, 1835 OM_uint32 * time_rec) 1836{ 1837 OM_uint32 ret; 1838 krb5_error_code kret; 1839 krb5_data indata; 1840 krb5_ap_rep_enc_part *repl; 1841 1842 output_token->length = 0; 1843 output_token->value = NULL; 1844 1845 if (IS_DCE_STYLE(ctx)) { 1846 /* There is no OID wrapping. */ 1847 indata.length = input_token->length; 1848 indata.data = input_token->value; 1849 kret = krb5_rd_rep(context, 1850 ctx->auth_context, 1851 &indata, 1852 &repl); 1853 if (kret) { 1854 ret = _gsskrb5_decapsulate(minor_status, 1855 input_token, 1856 &indata, 1857 "\x03\x00", 1858 GSS_KRB5_MECHANISM); 1859 if (ret == GSS_S_COMPLETE) { 1860 return handle_error_packet(minor_status, context, ctx, indata); 1861 } else { 1862 *minor_status = kret; 1863 return GSS_S_FAILURE; 1864 } 1865 } 1866 } else { 1867 ret = _gsskrb5_decapsulate (minor_status, 1868 input_token, 1869 &indata, 1870 "\x02\x00", 1871 ctx->mech); 1872 if (ret == GSS_S_DEFECTIVE_TOKEN) { 1873 /* check if there is an error token sent instead */ 1874 ret = _gsskrb5_decapsulate (minor_status, 1875 input_token, 1876 &indata, 1877 "\x03\x00", 1878 ctx->mech); 1879 if (ret == GSS_S_COMPLETE) 1880 return handle_error_packet(minor_status, context, ctx, indata); 1881 } 1882 kret = krb5_rd_rep (context, 1883 ctx->auth_context, 1884 &indata, 1885 &repl); 1886 if (kret) { 1887 *minor_status = kret; 1888 return GSS_S_FAILURE; 1889 } 1890 } 1891 1892 krb5_free_ap_rep_enc_part (context, 1893 repl); 1894 1895 *minor_status = 0; 1896 if (time_rec) { 1897 ret = _gsskrb5_lifetime_left(minor_status, 1898 context, 1899 ctx->endtime, 1900 time_rec); 1901 if (ret) 1902 return ret; 1903 } 1904 1905 if (req_flags & GSS_C_DCE_STYLE) { 1906 int32_t local_seq, remote_seq; 1907 krb5_data outbuf; 1908 1909 /* 1910 * So DCE_STYLE is strange. The client echos the seq number 1911 * that the server used in the server's mk_rep in its own 1912 * mk_rep(). After when done, it resets to it's own seq number 1913 * for the gss_wrap calls. 1914 */ 1915 1916 krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &remote_seq); 1917 krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq); 1918 krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq); 1919 1920 kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); 1921 if (kret) { 1922 *minor_status = kret; 1923 return GSS_S_FAILURE; 1924 } 1925 1926 /* reset local seq number */ 1927 krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq); 1928 1929 output_token->length = outbuf.length; 1930 output_token->value = outbuf.data; 1931 } 1932 1933 return initiator_ready(minor_status, ctx, context, ret_flags); 1934} 1935 1936static OM_uint32 1937step_completed(OM_uint32 * minor_status, 1938 gsskrb5_cred cred, 1939 gsskrb5_ctx ctx, 1940 krb5_context context, 1941 gss_name_t name, 1942 const gss_OID mech_type, 1943 OM_uint32 req_flags, 1944 OM_uint32 time_req, 1945 const gss_channel_bindings_t input_chan_bindings, 1946 const gss_buffer_t input_token, 1947 gss_buffer_t output_token, 1948 OM_uint32 * ret_flags, 1949 OM_uint32 * time_rec) 1950{ 1951 /* 1952 * If we get there, the caller have called 1953 * gss_init_sec_context() one time too many. 1954 */ 1955 _gsskrb5_set_status(EINVAL, "init_sec_context " 1956 "called one time too many"); 1957 *minor_status = EINVAL; 1958 return GSS_S_BAD_STATUS; 1959} 1960 1961/* 1962 * gss_init_sec_context 1963 */ 1964 1965OM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context 1966(OM_uint32 * minor_status, 1967 const gss_cred_id_t cred_handle, 1968 gss_ctx_id_t * context_handle, 1969 const gss_name_t target_name, 1970 const gss_OID mech_type, 1971 OM_uint32 req_flags, 1972 OM_uint32 time_req, 1973 const gss_channel_bindings_t input_chan_bindings, 1974 const gss_buffer_t input_token, 1975 gss_OID * actual_mech_type, 1976 gss_buffer_t output_token, 1977 OM_uint32 * ret_flags, 1978 OM_uint32 * time_rec 1979 ) 1980{ 1981 krb5_context context; 1982 gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 1983 gsskrb5_ctx ctx; 1984 OM_uint32 ret; 1985 gss_OID mech = GSS_C_NO_OID; 1986 gsskrb5_initator_state start_state; 1987 1988 GSSAPI_KRB5_INIT (&context); 1989 1990 output_token->length = 0; 1991 output_token->value = NULL; 1992 1993 if (context_handle == NULL) { 1994 *minor_status = 0; 1995 return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; 1996 } 1997 1998 if (ret_flags) 1999 *ret_flags = 0; 2000 if (time_rec) 2001 *time_rec = 0; 2002 2003 if (target_name == GSS_C_NO_NAME) { 2004 if (actual_mech_type) 2005 *actual_mech_type = GSS_C_NO_OID; 2006 *minor_status = 0; 2007 return GSS_S_BAD_NAME; 2008 } 2009 2010 if (cred && (cred->usage != GSS_C_INITIATE && cred->usage != GSS_C_BOTH)) { 2011 krb5_set_error_message(context, GSS_KRB5_S_G_BAD_USAGE, 2012 "ISC: Credentials not of " 2013 "usage type initiator or both"); 2014 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 2015 return GSS_S_DEFECTIVE_CREDENTIAL; 2016 } 2017 2018 if (gss_oid_equal(mech_type, GSS_KRB5_MECHANISM)) { 2019 mech = GSS_KRB5_MECHANISM; 2020 start_state = init_krb5_auth; 2021 } else if (gss_oid_equal(mech_type, GSS_IAKERB_MECHANISM)) { 2022 mech = GSS_IAKERB_MECHANISM; 2023 start_state = init_iakerb_auth; 2024#ifdef PKINIT 2025 } else if (gss_oid_equal(mech_type, GSS_PKU2U_MECHANISM)) { 2026 mech = GSS_PKU2U_MECHANISM; 2027 start_state = init_pku2u_auth; 2028#endif 2029 } else 2030 return GSS_S_BAD_MECH; 2031 2032 if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) { 2033#ifdef __APPLE__ 2034 ret = check_neg_cache(minor_status, context, mech, 2035 cred_handle, target_name); 2036 if (ret != GSS_S_COMPLETE) 2037 return ret; 2038#endif /* __APPLE__ */ 2039 2040 if (*context_handle != GSS_C_NO_CONTEXT) { 2041 *minor_status = 0; 2042 return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; 2043 } 2044 2045 ret = _gsskrb5_create_ctx(minor_status, 2046 context_handle, 2047 context, 2048 input_chan_bindings, 2049 mech); 2050 if (ret) 2051 return ret; 2052 2053 ctx = (gsskrb5_ctx) *context_handle; 2054 ctx->initiator_state = start_state; 2055 } else { 2056 ctx = (gsskrb5_ctx) *context_handle; 2057 } 2058 2059 if (ctx == NULL) { 2060 *minor_status = 0; 2061 return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; 2062 } 2063 2064 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 2065 2066 if (actual_mech_type) 2067 *actual_mech_type = ctx->mech; 2068 2069 do { 2070 ret = ctx->initiator_state(minor_status, cred, ctx, context, target_name, 2071 mech, req_flags, time_req, input_chan_bindings, input_token, 2072 output_token, ret_flags, time_rec); 2073 2074 } while (ret == GSS_S_COMPLETE && 2075 ctx->initiator_state != step_completed && 2076 output_token->length == 0); 2077 2078 if (GSS_ERROR(ret)) { 2079 if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE)) 2080 krb5_cc_close(context, ctx->ccache); 2081 ctx->ccache = NULL; 2082 } 2083 2084 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 2085 2086 /* destroy context in case of error */ 2087 if (GSS_ERROR(ret)) { 2088 OM_uint32 junk; 2089 _gsskrb5_delete_sec_context(&junk, context_handle, GSS_C_NO_BUFFER); 2090 } 2091 2092 return ret; 2093 2094} 2095