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