1/* 2 * Copyright (c) 1997 - 2005 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#include <heim_threads.h> 38#include <gssapi_spi.h> 39#include <pkinit_asn1.h> 40#include <hex.h> 41 42OM_uint32 43__gsskrb5_ccache_lifetime(OM_uint32 *minor_status, 44 krb5_context context, 45 krb5_ccache id, 46 krb5_principal principal, 47 time_t *endtime) 48{ 49 krb5_creds in_cred, out_cred; 50 krb5_const_realm realm; 51 krb5_error_code kret; 52 53 memset(&in_cred, 0, sizeof(in_cred)); 54 in_cred.client = principal; 55 56 realm = krb5_principal_get_realm(context, principal); 57 if (realm == NULL) { 58 _gsskrb5_clear_status (); 59 *minor_status = KRB5_PRINC_NOMATCH; /* XXX */ 60 return GSS_S_FAILURE; 61 } 62 63 kret = krb5_make_principal(context, &in_cred.server, 64 realm, KRB5_TGS_NAME, realm, NULL); 65 if (kret) { 66 *minor_status = kret; 67 return GSS_S_FAILURE; 68 } 69 70 kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred); 71 krb5_free_principal(context, in_cred.server); 72 if (kret) { 73 *minor_status = 0; 74 *endtime = 0; 75 return GSS_S_COMPLETE; 76 } 77 78 *endtime = out_cred.times.endtime; 79 krb5_free_cred_contents(context, &out_cred); 80 81 return GSS_S_COMPLETE; 82} 83 84/* 85 * Check if there is at least one entry in the keytab before 86 * declaring it as an useful keytab. 87 */ 88 89static int 90check_keytab(krb5_context context, 91 gsskrb5_cred handle, 92 const char *service, 93 int require_lkdc) 94{ 95 krb5_keytab_entry tmp; 96 krb5_error_code ret; 97 krb5_kt_cursor c; 98 int found = 0; 99 100 ret = krb5_kt_start_seq_get (context, handle->keytab, &c); 101 if (ret) 102 return 0; 103 while (!found && krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { 104 krb5_principal principal = tmp.principal; 105 106 if (service) { 107 if (principal->name.name_string.len < 1 108 || strcmp(principal->name.name_string.val[0], service) != 0) 109 goto next; 110 } 111 if (require_lkdc) { 112 if (krb5_principal_is_lkdc(context, principal)) 113 found = 1; 114 if (krb5_principal_is_pku2u(context, principal)) 115 found = 1; 116 } else 117 found = 1; 118 next: 119 krb5_kt_free_entry(context, &tmp); 120 } 121 krb5_kt_end_seq_get (context, handle->keytab, &c); 122 123 return found; 124} 125 126/* 127 * 128 */ 129 130static krb5_error_code 131get_keytab(krb5_context context, gsskrb5_cred handle, int require_lkdc) 132{ 133 krb5_error_code kret; 134 135 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); 136 137 if (_gsskrb5_keytab != NULL) { 138 char *name = NULL; 139 140 kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name); 141 if (kret == 0) { 142 kret = krb5_kt_resolve(context, name, &handle->keytab); 143 krb5_xfree(name); 144 } 145 } else 146 kret = krb5_kt_default(context, &handle->keytab); 147 148 if (kret) 149 goto out; 150 151 /* 152 * If caller requested, check that we have the user in the keytab. 153 */ 154 155 if (handle->principal) { 156 krb5_keytab_entry entry; 157 158 if (krb5_principal_is_gss_hostbased_service(context, handle->principal)) { 159 /* 160 * check if we have a service in the keytab 161 */ 162 const char *service = handle->principal->name.name_string.val[0]; 163 164 if (!check_keytab(context, handle, service, require_lkdc)) { 165 kret = KRB5_KT_NOTFOUND; 166 krb5_set_error_message(context, kret, 167 "Didn't find service %s in keytab", service); 168 goto out; 169 } 170 } else { 171 kret = krb5_kt_get_entry(context, handle->keytab, handle->principal, 172 0, 0, &entry); 173 if (kret) 174 goto out; 175 176 /* 177 * Update the name with the entry from the keytab in case we 178 * have a gss hostname service name principal 179 */ 180 krb5_free_principal(context, handle->principal); 181 kret = krb5_copy_principal(context, entry.principal, &handle->principal); 182 krb5_kt_free_entry(context, &entry); 183 if (kret) 184 goto out; 185 } 186 187 } else { 188 if (!check_keytab(context, handle, NULL, require_lkdc)) { 189 kret = KRB5_KT_NOTFOUND; 190 goto out; 191 } 192 } 193 194 out: 195 if (kret && handle->keytab) { 196 krb5_kt_close(context, handle->keytab); 197 handle->keytab = NULL; 198 } 199 200 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); 201 202 return (kret); 203} 204 205static OM_uint32 acquire_initiator_cred 206 (OM_uint32 * minor_status, 207 krb5_context context, 208 const gss_name_t desired_name, 209 OM_uint32 time_req, 210 gss_cred_usage_t cred_usage, 211 gsskrb5_cred handle 212 ) 213{ 214 OM_uint32 ret = GSS_S_FAILURE; 215 krb5_creds cred; 216 krb5_principal def_princ = NULL; 217 krb5_get_init_creds_opt *opt; 218 krb5_ccache ccache = NULL; 219 krb5_error_code kret; 220 221 memset(&cred, 0, sizeof(cred)); 222 223 /* 224 * If we have a preferred principal, lets try to find it in all 225 * caches, otherwise, fall back to default cache, ignore all 226 * errors while searching. 227 */ 228 229 if (handle->principal) { 230 kret = krb5_cc_cache_match (context, 231 handle->principal, 232 &ccache); 233 if (kret == 0) { 234 goto found; 235 } 236 } 237 238 if (ccache == NULL) { 239 kret = krb5_cc_default(context, &ccache); 240 if (kret) 241 goto end; 242 } 243 kret = krb5_cc_get_principal(context, ccache, &def_princ); 244 if (kret != 0) { 245 /* we'll try to use a keytab below */ 246 krb5_cc_close(context, ccache); 247 def_princ = NULL; 248 kret = 0; 249 } else if (handle->principal == NULL) { 250 kret = krb5_copy_principal(context, def_princ, &handle->principal); 251 if (kret) 252 goto end; 253 } else if (handle->principal != NULL && 254 krb5_principal_compare(context, handle->principal, 255 def_princ) == FALSE) { 256 krb5_free_principal(context, def_princ); 257 def_princ = NULL; 258 krb5_cc_close(context, ccache); 259 ccache = NULL; 260 } 261 if (def_princ == NULL) { 262 /* We have no existing credentials cache, 263 * so attempt to get a TGT using a keytab. 264 */ 265 if (handle->principal == NULL) { 266 kret = krb5_get_default_principal(context, &handle->principal); 267 if (kret) 268 goto end; 269 } 270 /* 271 * Require user is in the keytab before trying to talk to 272 * the KDC. 273 */ 274 kret = get_keytab(context, handle, 0); 275 if (kret) 276 goto end; 277 /* since the name might have changed, let double check the credential cache */ 278 kret = krb5_cc_cache_match(context, handle->principal, &ccache); 279 if (kret == 0) 280 goto found; 281 kret = krb5_get_init_creds_opt_alloc(context, &opt); 282 if (kret) 283 goto end; 284 kret = krb5_get_init_creds_keytab(context, &cred, 285 handle->principal, handle->keytab, 286 0, NULL, opt); 287 krb5_get_init_creds_opt_free(context, opt); 288 if (kret) 289 goto end; 290 kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache); 291 if (kret) 292 goto end; 293 kret = krb5_cc_initialize(context, ccache, cred.client); 294 if (kret) { 295 krb5_cc_destroy(context, ccache); 296 goto end; 297 } 298 kret = krb5_cc_store_cred(context, ccache, &cred); 299 if (kret) { 300 krb5_cc_destroy(context, ccache); 301 goto end; 302 } 303 handle->endtime = cred.times.endtime; 304 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 305 306 } else { 307 found: 308 ret = __gsskrb5_ccache_lifetime(minor_status, 309 context, 310 ccache, 311 handle->principal, 312 &handle->endtime); 313 if (ret != GSS_S_COMPLETE) { 314 krb5_cc_close(context, ccache); 315 goto end; 316 } 317 kret = 0; 318 } 319 320 handle->ccache = ccache; 321 ret = GSS_S_COMPLETE; 322 323end: 324 if (cred.client != NULL) 325 krb5_free_cred_contents(context, &cred); 326 if (def_princ != NULL) 327 krb5_free_principal(context, def_princ); 328 if (ret != GSS_S_COMPLETE && kret != 0) 329 *minor_status = kret; 330 return (ret); 331} 332 333static OM_uint32 acquire_acceptor_cred 334 (OM_uint32 * minor_status, 335 krb5_context context, 336 const gss_name_t desired_name, 337 OM_uint32 time_req, 338 gss_cred_usage_t cred_usage, 339 gsskrb5_cred handle 340 ) 341{ 342 krb5_error_code kret; 343 344 kret = get_keytab(context, handle, 0); 345 346 if (kret) { 347 if (handle->keytab != NULL) { 348 krb5_kt_close(context, handle->keytab); 349 handle->keytab = NULL; 350 } 351 *minor_status = kret; 352 return GSS_S_FAILURE; 353 } 354 355 handle->endtime = INT_MAX; 356 357 return GSS_S_COMPLETE; 358} 359 360static OM_uint32 361_acquire_uuid_name(OM_uint32 *minor_status, 362 krb5_context context, 363 krb5_const_principal princ, 364 int *iakerb, 365 gsskrb5_cred handle) 366{ 367 krb5_error_code ret; 368 krb5_uuid uuid; 369 370 *iakerb = 0; 371 372 if (princ->name.name_type != KRB5_NT_CACHE_UUID) 373 return GSS_S_BAD_NAMETYPE; 374 375 if (princ->name.name_string.len != 1 || strcmp(princ->realm, "UUID") != 0) 376 return GSS_S_BAD_NAME; 377 378 if (krb5_string_to_uuid(princ->name.name_string.val[0], uuid)) 379 return GSS_S_BAD_NAME; 380 381 ret = krb5_cc_resolve_by_uuid(context, NULL, 382 &handle->ccache, uuid); 383 if (ret) { 384 *minor_status = ret; 385 return GSS_S_FAILURE; 386 } 387 388 ret = krb5_cc_get_principal(context, handle->ccache, &handle->principal); 389 if (ret) { 390 *minor_status = ret; 391 return GSS_S_FAILURE; 392 } 393 394 { 395 krb5_data data; 396 397 ret = krb5_cc_get_config(context, handle->ccache, NULL, "iakerb", &data); 398 if (ret == 0) { 399 *iakerb = 1; 400 handle->endtime = INT_MAX; 401 krb5_data_free(&data); 402 return 0; 403 } 404 } 405 406 return __gsskrb5_ccache_lifetime(minor_status, 407 context, 408 handle->ccache, 409 handle->principal, 410 &handle->endtime); 411} 412 413 414OM_uint32 GSSAPI_CALLCONV 415_gsskrb5_acquire_cred(OM_uint32 * minor_status, 416 const gss_name_t desired_name, 417 OM_uint32 time_req, 418 const gss_OID_set desired_mechs, 419 gss_cred_usage_t cred_usage, 420 gss_cred_id_t * output_cred_handle, 421 gss_OID_set * actual_mechs, 422 OM_uint32 * time_rec) 423{ 424 krb5_const_principal principal = (krb5_const_principal)desired_name; 425 krb5_context context; 426 gsskrb5_cred handle; 427 OM_uint32 ret, junk; 428 429 cred_usage &= GSS_C_OPTION_MASK; 430 431 if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 432 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 433 return GSS_S_FAILURE; 434 } 435 436 GSSAPI_KRB5_INIT(&context); 437 438 *output_cred_handle = NULL; 439 440 handle = calloc(1, sizeof(*handle)); 441 if (handle == NULL) { 442 *minor_status = ENOMEM; 443 return (GSS_S_FAILURE); 444 } 445 446 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 447 448 if (principal && principal->name.name_type == KRB5_NT_CACHE_UUID) { 449 int iakerb = 0; 450 451 ret = _acquire_uuid_name(minor_status, context, principal, &iakerb, handle); 452 if (iakerb) { 453 *minor_status = 0; 454 ret = GSS_S_BAD_NAME; 455 } 456 if (ret) { 457 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 458 return ret; 459 } 460 goto out; 461 } 462 463 if (principal) { 464 krb5_error_code kret; 465 466 kret = krb5_copy_principal(context, principal, &handle->principal); 467 if (kret) { 468 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 469 *minor_status = kret; 470 return GSS_S_FAILURE; 471 } 472 } 473 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { 474 ret = acquire_initiator_cred(minor_status, context, 475 desired_name, time_req, 476 cred_usage, handle); 477 if (ret != GSS_S_COMPLETE) { 478 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 479 krb5_free_principal(context, handle->principal); 480 free(handle); 481 return (ret); 482 } 483 } 484 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { 485 ret = acquire_acceptor_cred(minor_status, context, 486 desired_name, time_req, 487 cred_usage, handle); 488 if (ret != GSS_S_COMPLETE) { 489 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 490 krb5_free_principal(context, handle->principal); 491 free(handle); 492 return (ret); 493 } 494 } 495 496 out: 497 498 handle->usage = cred_usage; 499 *minor_status = 0; 500 *output_cred_handle = (gss_cred_id_t)handle; 501 502 ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle, 503 NULL, time_rec, NULL, actual_mechs); 504 if (ret) { 505 _gsskrb5_release_cred(&junk, output_cred_handle); 506 return ret; 507 } 508 509 return (GSS_S_COMPLETE); 510} 511 512OM_uint32 513_gssiakerb_acquire_cred(OM_uint32 * minor_status, 514 const gss_name_t desired_name, 515 OM_uint32 time_req, 516 const gss_OID_set desired_mechs, 517 gss_cred_usage_t cred_usage, 518 gss_cred_id_t * output_cred_handle, 519 gss_OID_set * actual_mechs, 520 OM_uint32 * time_rec) 521{ 522 krb5_principal princ = (krb5_principal)desired_name; 523 OM_uint32 major_status, junk; 524 krb5_context context; 525 krb5_error_code ret; 526 gsskrb5_cred handle; 527 krb5_data data; 528 int iakerb = 0; 529 530 GSSAPI_KRB5_INIT(&context); 531 532 *minor_status = 0; 533 *output_cred_handle = NULL; 534 535 if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) 536 return GSS_S_FAILURE; 537 if (princ == NULL) 538 return GSS_S_FAILURE; 539 540 handle = calloc(1, sizeof(*handle)); 541 if (handle == NULL) 542 return GSS_S_FAILURE; 543 544 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 545 546 major_status = _acquire_uuid_name(minor_status, context, princ, &iakerb, handle); 547 if (major_status) 548 return major_status; 549 if (!iakerb) 550 return GSS_S_BAD_NAME; 551 552 if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "password", &data)) == 0) { 553 554 ret = asprintf(&handle->password, "%.*s", (int)data.length, (char *)data.data); 555 memset(data.data, 0, data.length); 556 krb5_data_free(&data); 557 if (ret <= 0 || handle->password == NULL) { 558 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 559 *minor_status = ENOMEM; 560 return GSS_S_FAILURE; 561 } 562 563#ifdef PKINIT 564 } else if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "certificate-ref", &data)) == 0) { 565 hx509_certs certs; 566 hx509_query *q; 567 568 ret = hx509_certs_init(context->hx509ctx, "KEYCHAIN:", 0, NULL, &certs); 569 if (ret) { 570 krb5_data_free(&data); 571 hx509_certs_free(&certs); 572 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 573 *minor_status = ret; 574 return GSS_S_FAILURE; 575 } 576 577 ret = hx509_query_alloc(context->hx509ctx, &q); 578 if (ret) { 579 krb5_data_free(&data); 580 hx509_certs_free(&certs); 581 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 582 *minor_status = ret; 583 return GSS_S_FAILURE; 584 } 585 586 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 587 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); 588 hx509_query_match_persistent(q, &data); 589 590 ret = _krb5_pk_find_cert(context, 1, certs, q, &handle->cert); 591 krb5_data_free(&data); 592 hx509_certs_free(&certs); 593 hx509_query_free(context->hx509ctx, q); 594 if (ret != 0) { 595 _gss_mg_log(1, "gss-krb5: failed to find certificate ref %d", ret); 596 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 597 *minor_status = ret; 598 return GSS_S_FAILURE; 599 } 600#endif 601 } else if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "iakerb", &data)) == 0) { 602 handle->cred_flags |= GSS_CF_IAKERB_RESOLVED; 603 krb5_data_free(&data); 604 } else { 605 _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); 606 *minor_status = 0; 607 return GSS_S_FAILURE; 608 } 609 610 handle->usage = GSS_C_INITIATE; 611 handle->endtime = INT_MAX; 612 613 *output_cred_handle = (gss_cred_id_t)handle; 614 *minor_status = 0; 615 return GSS_S_COMPLETE; 616} 617 618 619OM_uint32 620_gss_iakerb_acquire_cred_ext(OM_uint32 * minor_status, 621 const gss_name_t desired_name, 622 gss_const_OID credential_type, 623 const void *credential_data, 624 OM_uint32 time_req, 625 gss_const_OID desired_mech, 626 gss_cred_usage_t cred_usage, 627 gss_cred_id_t * output_cred_handle) 628{ 629 krb5_context context; 630 gsskrb5_cred handle; 631 krb5_error_code ret; 632 krb5_creds cred; 633 gss_buffer_t credential_buffer = NULL; 634#ifdef PKINIT 635 hx509_cert cert = NULL; 636#endif 637 638 memset(&cred, 0, sizeof(cred)); 639 640 if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) 641 return GSS_S_FAILURE; 642 643 GSSAPI_KRB5_INIT_STATUS(&context, status); 644 645 /* pick up the credential */ 646 647 if (gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 648 649 credential_buffer = (gss_buffer_t)credential_data; 650 651 if (credential_buffer->length + 1 < credential_buffer->length) 652 return GSS_S_FAILURE; 653 654#ifdef PKINIT 655 } else if (gss_oid_equal(credential_type, GSS_C_CRED_CERTIFICATE)) { 656 657 cert = (hx509_cert)credential_data; 658 659 } else if (gss_oid_equal(credential_type, GSS_C_CRED_SecIdentity)) { 660 661 ret = hx509_cert_init_SecFramework(context->hx509ctx, rk_UNCONST(credential_data), &cert); 662 if (ret) { 663 *minor_status = ret; 664 return GSS_S_FAILURE; 665 } 666#endif 667 } else { 668 *minor_status = KRB5_NOCREDS_SUPPLIED; 669 return GSS_S_FAILURE; 670 } 671 672 673 if (desired_name == GSS_C_NO_NAME) 674 return GSS_S_FAILURE; 675 676 handle = calloc(1, sizeof(*handle)); 677 if (handle == NULL) 678 return (GSS_S_FAILURE); 679 680 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 681 682 handle->usage = GSS_C_INITIATE; 683 684 { 685 krb5_principal princ = (krb5_principal)desired_name; 686 687 ret = krb5_copy_principal(context, princ, &handle->principal); 688 if (ret) { 689 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 690 free(handle); 691 *minor_status = ret; 692 return GSS_S_FAILURE; 693 } 694 } 695 696 if (credential_buffer) { 697 698 handle->password = malloc(credential_buffer->length + 1); 699 if (handle->password == NULL) { 700 krb5_free_principal(context, handle->principal); 701 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 702 free(handle); 703 *minor_status = ENOMEM; 704 return GSS_S_FAILURE; 705 } 706 707 memcpy(handle->password, credential_buffer->value, credential_buffer->length); 708 handle->password[credential_buffer->length] = '\0'; 709 } 710#ifdef PKINIT 711 if (cert) 712 handle->cert = heim_retain(cert); 713#endif 714 715 handle->keytab = NULL; 716 handle->ccache = NULL; 717 handle->endtime = INT_MAX; 718 719 /* 720 * Lets overwrite the same credentials if we already have it 721 */ 722 ret = krb5_cc_cache_match(context, handle->principal, &handle->ccache); 723 if (ret) { 724 ret = krb5_cc_new_unique(context, krb5_cc_type_api, NULL, &handle->ccache); 725 if (ret) 726 goto out; 727 } 728 729 ret = krb5_cc_initialize(context, handle->ccache, handle->principal); 730 if (ret) 731 goto out; 732 733 { 734 krb5_data data; 735 krb5_data_zero(&data); 736 krb5_cc_set_config(context, handle->ccache, NULL, "iakerb", &data); 737 } 738 739 if (handle->password) { 740 krb5_data pw; 741 pw.data = handle->password; 742 pw.length = strlen(handle->password); 743 ret = krb5_cc_set_config(context, handle->ccache, NULL, "password", &pw); 744 if (ret) 745 goto out; 746 } 747#ifdef PKINIT 748 if (handle->cert) { 749 krb5_data pd; 750 ret = hx509_cert_get_persistent(handle->cert, &pd); 751 if (ret) 752 goto out; 753 ret = krb5_cc_set_config(context, handle->ccache, NULL, "certificate-ref", &pd); 754 der_free_octet_string(&pd); 755 if (ret) 756 goto out; 757 } 758#endif 759 760 *output_cred_handle = (gss_cred_id_t) handle; 761 762 *minor_status = 0; 763 764 return GSS_S_COMPLETE; 765 766 out: 767 768 krb5_free_principal(context, handle->principal); 769 if (handle->password) { 770 memset(handle->password, 0, strlen(handle->password)); 771 free(handle->password); 772 } 773#ifdef PKINIT 774 if (handle->cert) 775 hx509_cert_free(handle->cert); 776#endif 777 if (handle->ccache) 778 krb5_cc_destroy(context, handle->ccache); 779 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 780 free(handle); 781 *minor_status = ret; 782 return GSS_S_FAILURE; 783} 784 785/* 786 * 787 */ 788 789OM_uint32 GSSAPI_CALLCONV 790_gss_krb5_acquire_cred_ext(OM_uint32 * minor_status, 791 const gss_name_t desired_name, 792 gss_const_OID credential_type, 793 const void *credential_data, 794 OM_uint32 time_req, 795 gss_const_OID desired_mech, 796 gss_cred_usage_t cred_usage, 797 gss_cred_id_t * output_cred_handle) 798{ 799 krb5_init_creds_context ctx = NULL; 800 krb5_get_init_creds_opt *opt = NULL; 801 krb5_principal principal; 802 krb5_context context; 803 krb5_error_code kret; 804 gsskrb5_cred handle = NULL; 805 krb5_ccache ccache = NULL, ccachereplace = NULL; 806 char *passwordstr = NULL; 807 char *cache_name = NULL; 808 char *lkdc_hostname = NULL; 809 hx509_cert hxcert = NULL; 810 heim_array_t bundleacl = NULL; 811 krb5_principal new_name = NULL; 812 813 GSSAPI_KRB5_INIT(&context); 814 815 cred_usage &= GSS_C_OPTION_MASK; 816 817 if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 818 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 819 return GSS_S_FAILURE; 820 } 821 822 if (desired_name == GSS_C_NO_NAME) 823 return GSS_S_FAILURE; 824 825 if (gss_oid_equal(credential_type, GSS_C_CRED_HEIMBASE)) { 826 heim_object_t pw, cname, cert, lkdc; 827 heim_dict_t dict = (heim_dict_t)credential_data; 828 829 pw = heim_dict_copy_value(dict, _gsskrb5_kGSSICPassword); 830 if (pw) { 831 if (heim_get_tid(pw) == heim_string_get_type_id()) { 832 passwordstr = heim_string_copy_utf8(pw); 833 if (passwordstr == NULL) { 834 kret = ENOMEM; 835 goto out; 836 } 837 } else if (heim_get_tid(pw) == heim_data_get_type_id()) { 838 passwordstr = malloc(heim_data_get_length(pw) + 1); 839 if (passwordstr == NULL) { 840 kret = ENOMEM; 841 goto out; 842 } 843 memcpy(passwordstr, heim_data_get_bytes(pw), heim_data_get_length(pw)); 844 passwordstr[heim_data_get_length(pw)] = '\0'; 845 } 846 heim_release(pw); 847 } 848 849 cname = heim_dict_copy_value(dict, _gsskrb5_kGSSICKerberosCacheName); 850 if (cname) { 851 cache_name = heim_string_copy_utf8(cname); 852 heim_release(cname); 853 } 854 855 bundleacl = heim_dict_copy_value(dict, _gsskrb5_kGSSICAppIdentifierACL); 856 857#ifdef PKINIT 858 cert = heim_dict_copy_value(dict, _gsskrb5_kGSSICCertificate); 859 if (cert) { 860 kret = hx509_cert_init_SecFramework(context->hx509ctx, cert, &hxcert); 861 if (kret) 862 goto out; 863 heim_release(cert); 864 } 865#endif 866 867 lkdc = heim_dict_copy_value(dict, _gsskrb5_kGSSICLKDCHostname); 868 if (lkdc) { 869 lkdc_hostname = heim_string_copy_utf8(lkdc); 870 heim_release(lkdc); 871 } 872 873 } else if (gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 874 gss_buffer_t password = (gss_buffer_t)credential_data; 875 876 passwordstr = malloc(password->length + 1); 877 if (passwordstr == NULL) { 878 kret = ENOMEM; 879 goto out; 880 } 881 882 memcpy(passwordstr, password->value, password->length); 883 passwordstr[password->length] = '\0'; 884 885 } else { 886 *minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */ 887 return GSS_S_FAILURE; 888 } 889 890 if (passwordstr == NULL && hxcert == NULL) { 891 *minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */ 892 return GSS_S_FAILURE; 893 } 894 895 *output_cred_handle = NULL; 896 897 handle = calloc(1, sizeof(*handle)); 898 if (handle == NULL) { 899 kret = krb5_enomem(context); 900 goto out; 901 } 902 903 principal = (krb5_principal)desired_name; 904 905 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 906 907 kret = krb5_copy_principal(context, principal, &handle->principal); 908 if (kret) 909 goto out; 910 911 kret = krb5_cc_new_unique(context, cache_name, NULL, &ccache); 912 if (kret) 913 goto out; 914 915 kret = krb5_get_init_creds_opt_alloc(context, &opt); 916 if (kret) 917 goto out; 918 919 krb5_get_init_creds_opt_set_default_flags(context, "gss", krb5_principal_get_realm(context, principal), opt); 920 921 krb5_get_init_creds_opt_set_forwardable(opt, 1); 922 krb5_get_init_creds_opt_set_proxiable(opt, 1); 923 krb5_get_init_creds_opt_set_renew_life(opt, 3600 * 24 * 30); /* 1 month */ 924 krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE); 925 krb5_get_init_creds_opt_set_win2k(context, opt, TRUE); 926 927 if (hxcert) { 928 char *cert_pool[2] = { "KEYCHAIN:", NULL }; 929 kret = krb5_get_init_creds_opt_set_pkinit(context, opt, principal, 930 NULL, "KEYCHAIN:", 931 cert_pool, NULL, 8, 932 NULL, NULL, NULL); 933 if (kret) 934 goto out; 935 } 936 937 kret = krb5_init_creds_init(context, handle->principal, NULL, NULL, NULL, opt, &ctx); 938 if (kret) 939 goto out; 940 941 if (passwordstr) { 942 kret = krb5_init_creds_set_password(context, ctx, passwordstr); 943 944 memset(passwordstr, 0, strlen(passwordstr)); 945 free(passwordstr); 946 passwordstr = NULL; 947 948 if (kret) 949 goto out; 950 } 951 952 if (hxcert) { 953 kret = krb5_init_creds_set_pkinit_client_cert(context, ctx, hxcert); 954 if (kret) 955 goto out; 956 } 957 958 if (lkdc_hostname) { 959 kret = krb5_init_creds_set_kdc_hostname(context, ctx, lkdc_hostname); 960 free(lkdc_hostname); 961 lkdc_hostname = NULL; 962 if (kret) 963 goto out; 964 } 965 966 kret = krb5_init_creds_get(context, ctx); 967 if (kret) 968 goto out; 969 970 handle->endtime = _krb5_init_creds_get_cred_endtime(context, ctx); 971 972 /* 973 * If we where subjected to a referral, update the name of the credential 974 */ 975 new_name = _krb5_init_creds_get_cred_client(context, ctx); 976 if (new_name && !krb5_principal_compare(context, new_name, handle->principal)) { 977 krb5_free_principal(context, handle->principal); 978 kret = krb5_copy_principal(context, new_name, &handle->principal); 979 if (kret) 980 goto out; 981 } 982 983 /* 984 * Now store the credential 985 */ 986 987 if (cache_name) { 988 /* check if caller told us to use a specific cache */ 989 kret = krb5_cc_resolve(context, cache_name, &ccachereplace); 990 if (kret) 991 goto out; 992 993 } else { 994 /* 995 * check if there an existing cache to overwrite before we lay 996 * down the new cache 997 */ 998 (void)krb5_cc_cache_match(context, principal, &ccachereplace); 999 } 1000 1001 1002 kret = krb5_init_creds_store(context, ctx, ccache); 1003 if (kret == 0) 1004 kret = krb5_init_creds_store_config(context, ctx, ccache); 1005 1006 if (bundleacl) 1007 krb5_cc_set_acl(context, ccache, "kHEIMAttrBundleIdentifierACL", bundleacl); 1008 1009 krb5_init_creds_free(context, ctx); 1010 ctx = NULL; 1011 if (kret) 1012 goto out; 1013 1014 krb5_get_init_creds_opt_free(context, opt); 1015 opt = NULL; 1016 1017 /* 1018 * If we have a credential with the same naame, lets overwrite it 1019 */ 1020 1021 if (ccachereplace) { 1022 kret = krb5_cc_move(context, ccache, ccachereplace); 1023 if (kret) 1024 goto out; 1025 handle->ccache = ccachereplace; 1026 ccachereplace = NULL; 1027 } else { 1028 handle->ccache = ccache; 1029 } 1030 1031 handle->usage = cred_usage; 1032 *minor_status = 0; 1033 *output_cred_handle = (gss_cred_id_t)handle; 1034 1035 if (cache_name) 1036 free(cache_name); 1037 1038 heim_release(bundleacl); 1039 1040 return GSS_S_COMPLETE; 1041 1042 out: 1043 if (bundleacl) 1044 heim_release(bundleacl); 1045 if (opt) 1046 krb5_get_init_creds_opt_free(context, opt); 1047 if (ctx) 1048 krb5_init_creds_free(context, ctx); 1049 if (lkdc_hostname) 1050 free(lkdc_hostname); 1051 if (cache_name) 1052 free(cache_name); 1053 if (passwordstr) { 1054 memset(passwordstr, 0, strlen(passwordstr)); 1055 free(passwordstr); 1056 } 1057 if (ccachereplace) 1058 krb5_cc_close(context, ccachereplace); 1059 if (ccache) 1060 krb5_cc_destroy(context, ccache); 1061 if (handle) { 1062 if (handle->principal) 1063 krb5_free_principal(context, handle->principal); 1064 1065 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 1066 free(handle); 1067 } 1068 1069 *minor_status = kret; 1070 return GSS_S_FAILURE; 1071} 1072 1073 1074#ifdef PKINIT 1075 1076krb5_error_code 1077_gsspku2u_principal(krb5_context context, 1078 struct hx509_cert_data *cert, 1079 krb5_principal *principal) 1080{ 1081 hx509_octet_string_list list; 1082 krb5_error_code ret; 1083 int found = 0; 1084 unsigned i; 1085 char *name; 1086 1087 *principal = NULL; 1088 1089 /* 1090 * First try to map PKINIT SAN to a Kerberos principal 1091 */ 1092 1093 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx, cert, 1094 &asn1_oid_id_pkinit_san, 1095 &list); 1096 if (ret == 0) { 1097 for (i = 0; !found && i < list.len; i++) { 1098 KRB5PrincipalName r; 1099 1100 ret = decode_KRB5PrincipalName(list.val[i].data, 1101 list.val[i].length, 1102 &r, NULL); 1103 if (ret) 1104 continue; 1105 1106 ret = _krb5_principalname2krb5_principal(context, principal, 1107 r.principalName, 1108 KRB5_PKU2U_REALM_NAME); 1109 free_KRB5PrincipalName(&r); 1110 if (ret == 0) 1111 found = 1; 1112 } 1113 hx509_free_octet_string_list(&list); 1114 } 1115 if (found) 1116 return 0; 1117 1118 /* 1119 * 1120 */ 1121 1122 ret = hx509_cert_get_appleid(context->hx509ctx, cert, &name); 1123 if (ret == 0) { 1124 ret = krb5_make_principal(context, principal, 1125 KRB5_PKU2U_REALM_NAME, 1126 name, NULL); 1127 hx509_xfree(name); 1128 if (ret == 0) { 1129 (*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL; 1130 return 0; 1131 } 1132 } 1133 1134 /* 1135 * Give up and just WELLKNOWN and assertion instead 1136 */ 1137 1138 ret = krb5_make_principal(context, principal, KRB5_PKU2U_REALM_NAME, 1139 KRB5_WELLKNOWN_NAME, KRB5_NULL_NAME, NULL); 1140 if (ret == 0) 1141 (*principal)->name.name_type = KRB5_NT_WELLKNOWN; 1142 return ret; 1143} 1144 1145 1146 1147 1148struct search { 1149 krb5_context context; 1150 krb5_principal principal; 1151}; 1152 1153static int 1154match_pkinit_san(hx509_context context, hx509_cert cert, void *ctx) 1155{ 1156 struct search *s = ctx; 1157 return _krb5_pk_match_cert(s->context, s->principal, cert, 0); 1158} 1159 1160OM_uint32 1161_gsspku2u_acquire_cred(OM_uint32 * minor_status, 1162 const gss_name_t desired_name, 1163 OM_uint32 time_req, 1164 const gss_OID_set desired_mechs, 1165 gss_cred_usage_t cred_usage, 1166 gss_cred_id_t * output_cred_handle, 1167 gss_OID_set * actual_mechs, 1168 OM_uint32 * time_rec) 1169{ 1170 krb5_context context; 1171 gsskrb5_cred handle; 1172 hx509_query *q; 1173 hx509_certs certs = NULL; 1174 OM_uint32 ret; 1175 krb5_principal name = (krb5_principal)desired_name; 1176 1177 /* remove non-options from cred_usage */ 1178 cred_usage = (cred_usage & GSS_C_OPTION_MASK); 1179 1180 if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 1181 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 1182 return GSS_S_FAILURE; 1183 } 1184 1185 GSSAPI_KRB5_INIT(&context); 1186 1187 *output_cred_handle = NULL; 1188 if (time_rec) 1189 *time_rec = GSS_C_INDEFINITE; 1190 if (actual_mechs) 1191 *actual_mechs = GSS_C_NO_OID_SET; 1192 1193 /* 1194 * We can't acquire credential for specific names that are not 1195 * PKU2U names, so don't try. 1196 */ 1197 1198 if (name && !krb5_principal_is_pku2u(context, name)) { 1199 *minor_status = 0; 1200 return GSS_S_BAD_NAME; 1201 } 1202 1203 handle = calloc(1, sizeof(*handle)); 1204 if (handle == NULL) 1205 return (GSS_S_FAILURE); 1206 1207 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 1208 1209 handle->usage = cred_usage; 1210 1211 if ((cred_usage == GSS_C_INITIATE) || (cred_usage == GSS_C_BOTH)) { 1212 struct search s; 1213 1214 ret = hx509_certs_init(context->hx509ctx, "KEYCHAIN:", 0, NULL, &certs); 1215 if (ret) { 1216 *minor_status = ret; 1217 goto fail; 1218 } 1219 1220 ret = hx509_query_alloc(context->hx509ctx, &q); 1221 if (ret) { 1222 *minor_status = ret; 1223 goto fail; 1224 } 1225 1226 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 1227 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); 1228 1229 if (name) { 1230 s.context = context; 1231 s.principal = name; 1232 hx509_query_match_cmp_func(q, match_pkinit_san, &s); 1233 } 1234 1235 ret = _krb5_pk_find_cert(context, 1, certs, q, &handle->cert); 1236 hx509_query_free(context->hx509ctx, q); 1237 if (ret) { 1238 *minor_status = ret; 1239 goto fail; 1240 } 1241 1242 if (name) 1243 ret = krb5_copy_principal(context, name, &handle->principal); 1244 else 1245 ret = _gsspku2u_principal(context, handle->cert, &handle->principal); 1246 if (ret) { 1247 *minor_status = ret; 1248 goto fail; 1249 } 1250 1251 } 1252 1253 if ((cred_usage == GSS_C_ACCEPT) || (cred_usage == GSS_C_BOTH)) { 1254 ret = get_keytab(context, handle, 1); 1255 if (ret) { 1256 *minor_status = ret; 1257 goto fail; 1258 } 1259 } 1260 if (certs) 1261 hx509_certs_free(&certs); 1262 1263 *output_cred_handle = (gss_cred_id_t)handle; 1264 return GSS_S_COMPLETE; 1265 1266 fail: 1267 if (certs) 1268 hx509_certs_free(&certs); 1269 if (handle->keytab) 1270 krb5_kt_close(context, handle->keytab); 1271 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 1272 free(handle); 1273 1274 return GSS_S_FAILURE; 1275} 1276 1277#endif 1278