kinit.c
1 /* 2 * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 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 "kuser_locl.h" 37 38 #ifdef __APPLE__ 39 #include <Security/Security.h> 40 #endif 41 42 struct krb5_dh_moduli; 43 struct AlgorithmIdentifier; 44 struct _krb5_krb_auth_data; 45 struct _krb5_key_data; 46 struct _krb5_key_type; 47 struct _krb5_checksum_type; 48 struct _krb5_encryption_type; 49 struct _krb5_srv_query_ctx; 50 struct krb5_fast_state; 51 struct _krb5_srp_group; 52 struct _krb5_srp; 53 54 #include <heimbase.h> 55 #include <hx509.h> 56 #include <krb5-private.h> 57 58 static void usage (int ret) __attribute__((noreturn)); 59 60 61 int forwardable_flag = -1; 62 int proxiable_flag = -1; 63 int renewable_flag = -1; 64 int renew_flag = 0; 65 int home_directory_flag = 1; 66 int pac_flag = -1; 67 int validate_flag = 0; 68 int version_flag = 0; 69 int help_flag = 0; 70 int addrs_flag = -1; 71 struct getarg_strings extra_addresses; 72 int anonymous_flag = 0; 73 char *lifetime = NULL; 74 char *renew_life = NULL; 75 char *server_str = NULL; 76 char *cred_cache = NULL; 77 char *start_str = NULL; 78 char *kdc_hostname = NULL; 79 static int switch_cache_flags = 1; 80 struct getarg_strings etype_str; 81 int use_keytab = 0; 82 char *keytab_str = NULL; 83 int do_afslog = -1; 84 int fcache_version; 85 char *password_file = NULL; 86 char *pk_user_id = NULL; 87 int pk_enterprise_flag = 0; 88 struct hx509_cert_data *ent_user_id = NULL; 89 char *pk_x509_anchors = NULL; 90 int pk_use_enckey = 0; 91 static int canonicalize_flag = 0; 92 static int enterprise_flag = 0; 93 static int ok_as_delegate_flag = 0; 94 static char *fast_armor_cache_string = NULL; 95 static int use_referrals_flag = 0; 96 static int verbose_flag = 0; 97 static int windows_flag = 0; 98 #ifdef __APPLE__ 99 static int keychain_flag = 0; 100 static SecKeychainItemRef passwordItem = NULL; 101 struct getarg_strings bundle_acl_strings; 102 #endif 103 104 105 static struct getargs args[] = { 106 /* 107 * used by MIT 108 * a: ~A 109 * V: verbose 110 * F: ~f 111 * P: ~p 112 * C: v4 cache name? 113 * 5: 114 * 115 * old flags 116 * 4: 117 * 9: 118 */ 119 { "afslog", 0 , arg_flag, &do_afslog, 120 NP_("obtain afs tokens", ""), NULL }, 121 122 { "cache", 'c', arg_string, &cred_cache, 123 NP_("credentials cache", ""), "cachename" }, 124 125 #if KRB5_FORWARDABLE_DEFAULT 126 { "forwardable", 0 , arg_negative_flag, &forwardable_flag, 127 NP_("don't get forwardable tickets", "")}, 128 { NULL, 'f', arg_flag, &forwardable_flag, 129 NP_("get forwardable tickets", "")}, 130 #else 131 { "forwardable", 'f', arg_flag, &forwardable_flag, 132 NP_("get forwardable tickets", "")}, 133 #endif 134 { "keytab", 't', arg_string, &keytab_str, 135 NP_("keytab to use", ""), "keytabname" }, 136 137 { "lifetime", 'l', arg_string, &lifetime, 138 NP_("lifetime of tickets", ""), "time" }, 139 140 { "proxiable", 'p', arg_flag, &proxiable_flag, 141 NP_("get proxiable tickets", ""), NULL }, 142 143 { "renew", 'R', arg_flag, &renew_flag, 144 NP_("renew TGT", ""), NULL }, 145 146 { "renewable", 0, arg_flag, &renewable_flag, 147 NP_("get renewable tickets", ""), NULL }, 148 149 { "renewable-life", 'r', arg_string, &renew_life, 150 NP_("renewable lifetime of tickets", ""), "time" }, 151 152 { "server", 'S', arg_string, &server_str, 153 NP_("server to get ticket for", ""), "principal" }, 154 155 { "start-time", 's', arg_string, &start_str, 156 NP_("when ticket gets valid", ""), "time" }, 157 158 { "kdc-hostname", 0, arg_string, &kdc_hostname, 159 NP_("redirect the request to specific KDC", ""), "hostname" }, 160 161 { "use-keytab", 'k', arg_flag, &use_keytab, 162 NP_("get key from keytab", ""), NULL }, 163 164 { "validate", 'v', arg_flag, &validate_flag, 165 NP_("validate TGT", ""), NULL }, 166 167 { "enctypes", 'e', arg_strings, &etype_str, 168 NP_("encryption types to use", ""), "enctypes" }, 169 170 { "fcache-version", 0, arg_integer, &fcache_version, 171 NP_("file cache version to create", ""), NULL }, 172 173 { "addresses", 'A', arg_negative_flag, &addrs_flag, 174 NP_("request a ticket with no addresses", ""), NULL }, 175 176 { NULL, 'a', arg_flag, &addrs_flag, 177 NP_("request a ticket with addresses", "") }, 178 179 { "extra-addresses", 0, arg_strings, &extra_addresses, 180 NP_("include these extra addresses", ""), "addresses" }, 181 182 { "anonymous", 0, arg_flag, &anonymous_flag, 183 NP_("request an anonymous ticket", ""), NULL }, 184 185 { "request-pac", 0, arg_flag, &pac_flag, 186 NP_("request a Windows PAC", ""), NULL }, 187 188 { "password-file", 0, arg_string, &password_file, 189 NP_("read the password from a file", ""), NULL }, 190 191 { "canonicalize",0, arg_flag, &canonicalize_flag, 192 NP_("canonicalize client principal", ""), NULL }, 193 194 { "enterprise",0, arg_flag, &enterprise_flag, 195 NP_("parse principal as a KRB5-NT-ENTERPRISE name", ""), NULL }, 196 #ifdef PKINIT 197 { "pk-enterprise", 0, arg_flag, &pk_enterprise_flag, 198 NP_("use enterprise name from certificate", ""), NULL }, 199 200 { "pk-user", 'C', arg_string, &pk_user_id, 201 NP_("principal's public/private/certificate identifier", ""), "id" }, 202 203 { "x509-anchors", 'D', arg_string, &pk_x509_anchors, 204 NP_("directory with CA certificates", ""), "directory" }, 205 206 { "pk-use-enckey", 0, arg_flag, &pk_use_enckey, 207 NP_("Use RSA encrypted reply (instead of DH)", ""), NULL }, 208 #endif 209 210 { "change-default", 0, arg_negative_flag, &switch_cache_flags, 211 NP_("switch the default cache to the new credentials cache", ""), NULL }, 212 213 { "ok-as-delegate", 0, arg_flag, &ok_as_delegate_flag, 214 NP_("honor ok-as-delegate on tickets", ""), NULL }, 215 216 { "fast-armor-cache", 0, arg_string, &fast_armor_cache_string, 217 NP_("use this credential cache as FAST armor cache", ""), "cache" }, 218 219 { "use-referrals", 0, arg_flag, &use_referrals_flag, 220 NP_("only use referrals, no dns canalisation", ""), NULL }, 221 222 { "verbose", 'V',arg_flag, &verbose_flag, 223 NP_("verbose output", "") }, 224 225 { "home-directory", 0, arg_negative_flag, &home_directory_flag, 226 NP_("don't touch home directory", ""), NULL }, 227 #ifdef __APPLE__ 228 { "keychain", 0, arg_flag, &keychain_flag, 229 NP_("save password in keychain if successful", ""), NULL }, 230 231 { "bundle-acl", 0, arg_strings, &bundle_acl_strings, 232 NP_("signing id allowed to use this credential", ""), NULL }, 233 #endif 234 { "windows", 0, arg_flag, &windows_flag, 235 NP_("get windows behavior", ""), NULL }, 236 237 { "version", 0, arg_flag, &version_flag, NULL, NULL }, 238 { "help", 0, arg_flag, &help_flag, NULL, NULL } 239 }; 240 241 static void 242 usage (int ret) 243 { 244 arg_printusage_i18n (args, 245 sizeof(args)/sizeof(*args), 246 N_("Usage: ", ""), 247 NULL, 248 "[principal [command]]", 249 getarg_i18n); 250 exit (ret); 251 } 252 253 static krb5_error_code 254 get_server(krb5_context context, 255 krb5_principal client, 256 const char *server, 257 krb5_principal *princ) 258 { 259 krb5_const_realm realm; 260 if(server) 261 return krb5_parse_name(context, server, princ); 262 263 realm = krb5_principal_get_realm(context, client); 264 return krb5_make_principal(context, princ, realm, 265 KRB5_TGS_NAME, realm, NULL); 266 } 267 268 static int 269 renew_validate(krb5_context context, 270 int renew, 271 int validate, 272 krb5_ccache cache, 273 const char *server, 274 krb5_deltat life) 275 { 276 krb5_error_code ret; 277 krb5_creds in, *out = NULL; 278 krb5_kdc_flags flags; 279 280 memset(&in, 0, sizeof(in)); 281 282 ret = krb5_cc_get_principal(context, cache, &in.client); 283 if(ret) { 284 krb5_warn(context, ret, "krb5_cc_get_principal"); 285 return ret; 286 } 287 ret = get_server(context, in.client, server, &in.server); 288 if(ret) { 289 krb5_warn(context, ret, "get_server"); 290 goto out; 291 } 292 293 if (renew) { 294 /* 295 * no need to check the error here, it's only to be 296 * friendly to the user 297 */ 298 krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out); 299 } 300 301 flags.i = 0; 302 flags.b.renewable = flags.b.renew = renew; 303 flags.b.validate = validate; 304 305 if (forwardable_flag != -1) 306 flags.b.forwardable = forwardable_flag; 307 else if (out) 308 flags.b.forwardable = out->flags.b.forwardable; 309 310 if (proxiable_flag != -1) 311 flags.b.proxiable = proxiable_flag; 312 else if (out) 313 flags.b.proxiable = out->flags.b.proxiable; 314 315 if (anonymous_flag) 316 flags.b.request_anonymous = anonymous_flag; 317 if(life) 318 in.times.endtime = time(NULL) + life; 319 320 if (out) { 321 krb5_free_creds (context, out); 322 out = NULL; 323 } 324 325 326 ret = krb5_get_kdc_cred(context, 327 cache, 328 flags, 329 NULL, 330 NULL, 331 &in, 332 &out); 333 if(ret) { 334 krb5_warn(context, ret, "krb5_get_kdc_cred"); 335 goto out; 336 } 337 ret = krb5_cc_initialize(context, cache, in.client); 338 if(ret) { 339 krb5_free_creds (context, out); 340 krb5_warn(context, ret, "krb5_cc_initialize"); 341 goto out; 342 } 343 ret = krb5_cc_store_cred(context, cache, out); 344 345 if(ret == 0 && server == NULL) { 346 /* only do this if it's a general renew-my-tgt request */ 347 #ifndef NO_AFS 348 if(do_afslog && k_hasafs()) 349 krb5_afslog(context, cache, NULL, NULL); 350 #endif 351 } 352 353 krb5_free_creds (context, out); 354 if(ret) { 355 krb5_warn(context, ret, "krb5_cc_store_cred"); 356 goto out; 357 } 358 out: 359 krb5_free_cred_contents(context, &in); 360 return ret; 361 } 362 363 static krb5_error_code 364 get_new_tickets(krb5_context context, 365 krb5_principal principal, 366 krb5_ccache ccache, 367 krb5_deltat ticket_life, 368 int interactive, 369 bool empty_placeholder) 370 { 371 krb5_error_code ret; 372 krb5_get_init_creds_opt *opt; 373 krb5_creds cred; 374 char passwd[256]; 375 krb5_deltat start_time = 0; 376 krb5_deltat renew = 0; 377 const char *renewstr = NULL; 378 krb5_enctype *enctype = NULL; 379 krb5_ccache tempccache; 380 krb5_init_creds_context icc; 381 krb5_keytab kt = NULL; 382 int will_use_keytab = (use_keytab || keytab_str); 383 krb5_prompter_fct prompter = NULL; 384 int need_prompt; 385 386 passwd[0] = '\0'; 387 388 if (password_file) { 389 FILE *f; 390 391 if (strcasecmp("STDIN", password_file) == 0) 392 f = stdin; 393 else 394 f = fopen(password_file, "r"); 395 if (f == NULL) 396 krb5_errx(context, 1, "Failed to open the password file %s", 397 password_file); 398 399 if (fgets(passwd, sizeof(passwd), f) == NULL) 400 krb5_errx(context, 1, 401 N_("Failed to read password from file %s", ""), 402 password_file); 403 if (f != stdin) 404 fclose(f); 405 passwd[strcspn(passwd, "\n")] = '\0'; 406 } 407 408 #if defined(__APPLE__) && !defined(__APPLE_TARGET_EMBEDDED__) 409 if (passwd[0] == '\0' && !will_use_keytab && home_directory_flag) { 410 const char *realm; 411 OSStatus osret; 412 UInt32 length; 413 void *buffer; 414 char *name; 415 416 realm = krb5_principal_get_realm(context, principal); 417 418 ret = krb5_unparse_name_flags(context, principal, 419 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); 420 if (ret) 421 goto nopassword; 422 423 osret = SecKeychainFindGenericPassword(NULL, (UInt32)strlen(realm), realm, 424 (UInt32)strlen(name), name, 425 &length, &buffer, &passwordItem); 426 free(name); 427 if (osret != noErr) 428 goto nopassword; 429 430 if (length < sizeof(passwd) - 1) { 431 memcpy(passwd, buffer, length); 432 passwd[length] = '\0'; 433 } 434 SecKeychainItemFreeContent(NULL, buffer); 435 nopassword: 436 do { } while(0); 437 } 438 #endif 439 440 need_prompt = !(pk_user_id || ent_user_id || anonymous_flag || will_use_keytab || passwd[0] != '\0') && interactive; 441 if (need_prompt) 442 prompter = krb5_prompter_posix; 443 else 444 prompter = krb5_prompter_print_only; 445 446 memset(&cred, 0, sizeof(cred)); 447 448 ret = krb5_get_init_creds_opt_alloc (context, &opt); 449 if (ret) 450 krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); 451 452 krb5_get_init_creds_opt_set_default_flags(context, "kinit", 453 krb5_principal_get_realm(context, principal), opt); 454 455 if(forwardable_flag != -1) 456 krb5_get_init_creds_opt_set_forwardable (opt, forwardable_flag); 457 458 if(proxiable_flag != -1) 459 krb5_get_init_creds_opt_set_proxiable (opt, proxiable_flag); 460 if(anonymous_flag) 461 krb5_get_init_creds_opt_set_anonymous (opt, anonymous_flag); 462 if (pac_flag != -1) 463 krb5_get_init_creds_opt_set_pac_request(context, opt, 464 pac_flag ? TRUE : FALSE); 465 if (canonicalize_flag) 466 krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE); 467 if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag) 468 krb5_get_init_creds_opt_set_win2k(context, opt, TRUE); 469 if (pk_user_id || ent_user_id || anonymous_flag) { 470 ret = krb5_get_init_creds_opt_set_pkinit(context, opt, 471 principal, 472 pk_user_id, 473 pk_x509_anchors, 474 NULL, 475 NULL, 476 pk_use_enckey ? 2 : 0 | 477 anonymous_flag ? 4 : 0, 478 interactive ? krb5_prompter_posix : krb5_prompter_print_only, 479 NULL, 480 passwd); 481 if (ret) 482 krb5_err(context, 1, ret, "krb5_get_init_creds_opt_set_pkinit"); 483 if (ent_user_id) 484 krb5_get_init_creds_opt_set_pkinit_user_cert(context, opt, ent_user_id); 485 } 486 487 if (addrs_flag != -1) 488 krb5_get_init_creds_opt_set_addressless(context, opt, 489 addrs_flag ? FALSE : TRUE); 490 491 if (renew_life == NULL && renewable_flag) 492 renewstr = "1 month"; 493 if (renew_life) 494 renewstr = renew_life; 495 if (renewstr) { 496 renew = parse_time (renewstr, "s"); 497 if (renew < 0) 498 errx (1, "unparsable time: %s", renewstr); 499 500 krb5_get_init_creds_opt_set_renew_life (opt, renew); 501 } 502 503 if(ticket_life != 0) 504 krb5_get_init_creds_opt_set_tkt_life (opt, ticket_life); 505 506 if(start_str) { 507 int tmp = parse_time (start_str, "s"); 508 if (tmp < 0) 509 errx (1, N_("unparsable time: %s", ""), start_str); 510 511 start_time = tmp; 512 } 513 514 if(etype_str.num_strings) { 515 int i; 516 517 enctype = malloc(etype_str.num_strings * sizeof(*enctype)); 518 if(enctype == NULL) 519 errx(1, "out of memory"); 520 for(i = 0; i < etype_str.num_strings; i++) { 521 ret = krb5_string_to_enctype(context, 522 etype_str.strings[i], 523 &enctype[i]); 524 if(ret) 525 krb5_err(context, 1, ret, "unrecognized enctype: %s", 526 etype_str.strings[i]); 527 } 528 krb5_get_init_creds_opt_set_etype_list(opt, enctype, 529 etype_str.num_strings); 530 } 531 532 ret = krb5_init_creds_init(context, principal, 533 prompter, NULL, 534 start_time, opt, &icc); 535 if (ret) 536 krb5_err (context, 1, ret, "krb5_init_creds_init"); 537 538 if (server_str) { 539 ret = krb5_init_creds_set_service(context, icc, server_str); 540 if (ret) 541 krb5_err (context, 1, ret, "krb5_init_creds_set_service"); 542 } 543 544 if (kdc_hostname) 545 krb5_init_creds_set_kdc_hostname(context, icc, kdc_hostname); 546 547 if (fast_armor_cache_string) { 548 krb5_ccache fastid; 549 550 ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid); 551 if (ret) 552 krb5_err(context, 1, ret, "krb5_cc_resolve(FAST cache)"); 553 554 ret = krb5_init_creds_set_fast_ccache(context, icc, fastid); 555 if (ret) 556 krb5_err(context, 1, ret, "krb5_init_creds_set_fast_ccache"); 557 } 558 559 if(will_use_keytab) { 560 if(keytab_str) 561 ret = krb5_kt_resolve(context, keytab_str, &kt); 562 else 563 ret = krb5_kt_default(context, &kt); 564 if (ret) 565 krb5_err (context, 1, ret, "resolving keytab"); 566 567 ret = krb5_init_creds_set_keytab(context, icc, kt); 568 if (ret) 569 krb5_err (context, 1, ret, "krb5_init_creds_set_keytab"); 570 } 571 572 if (passwd[0] == '\0' && need_prompt) { 573 char *p, *prompt; 574 575 krb5_unparse_name(context, principal, &p); 576 asprintf (&prompt, N_("%s's password: ", ""), p); 577 free(p); 578 579 if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ 580 memset(passwd, 0, sizeof(passwd)); 581 errx(1, "failed to read password"); 582 } 583 free (prompt); 584 } 585 586 if (passwd[0]) { 587 ret = krb5_init_creds_set_password(context, icc, passwd); 588 if (ret) 589 krb5_err(context, 1, ret, "krb5_init_creds_set_password"); 590 } 591 592 ret = krb5_init_creds_get(context, icc); 593 594 #ifdef __APPLE__ 595 /* 596 * Save password in Keychain 597 */ 598 if (ret == 0 && keychain_flag && passwordItem == NULL) { 599 krb5_error_code ret2; 600 const char *realm; 601 char *name; 602 603 realm = krb5_principal_get_realm(context, principal); 604 ret2 = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); 605 if (ret2 == 0) { 606 (void)SecKeychainAddGenericPassword(NULL, 607 (UInt32)strlen(realm), realm, 608 (UInt32)strlen(name), name, 609 (UInt32)strlen(passwd), passwd, 610 NULL); 611 free(name); 612 } 613 } 614 #endif 615 616 memset(passwd, 0, sizeof(passwd)); 617 618 if (ret && empty_placeholder) { 619 //remove the placeholder cache since it will not be used 620 (void)krb5_cc_destroy(context, ccache); 621 } 622 switch(ret){ 623 case 0: 624 break; 625 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 626 exit(1); 627 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 628 case KRB5KRB_AP_ERR_MODIFIED: 629 case KRB5KDC_ERR_PREAUTH_FAILED: 630 case KRB5_GET_IN_TKT_LOOP: 631 #ifdef __APPLE__ 632 if (passwordItem) 633 SecKeychainItemDelete(passwordItem); 634 #endif 635 krb5_errx(context, 1, N_("Password incorrect", "")); 636 case KRB5KRB_AP_ERR_V4_REPLY: 637 krb5_errx(context, 1, N_("Looks like a Kerberos 4 reply", "")); 638 case KRB5KDC_ERR_KEY_EXPIRED: 639 krb5_errx(context, 1, N_("Password expired", "")); 640 default: 641 krb5_err(context, 1, ret, "krb5_get_init_creds"); 642 } 643 644 ret = krb5_init_creds_get_creds(context, icc, &cred); 645 if (ret) 646 krb5_err(context, 1, ret, "krb5_init_creds_get_creds"); 647 648 krb5_process_last_request(context, opt, icc); 649 650 #ifdef HAVE_XCC 651 if ((ccache->ops == &krb5_xcc_api_ops 652 || ccache->ops == &krb5_xcc_ops 653 || ccache->ops == &krb5_xcc_temp_api_ops)) { 654 ret = krb5_cc_new_unique(context, "XCTEMP", 655 NULL, &tempccache); 656 } else 657 #endif 658 { 659 ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache), 660 NULL, &tempccache); 661 } 662 if (ret) 663 krb5_err (context, 1, ret, "krb5_cc_new_unique"); 664 665 ret = krb5_init_creds_store(context, icc, tempccache); 666 if (ret) 667 krb5_err(context, 1, ret, "krb5_init_creds_store"); 668 669 ret = krb5_init_creds_store_config(context, icc, tempccache); 670 if (ret) 671 krb5_warn(context, ret, "krb5_init_creds_store_config"); 672 673 ret = krb5_init_creds_warn_user(context, icc); 674 if (ret) 675 krb5_warn(context, ret, "krb5_init_creds_warn_user"); 676 677 #ifdef __APPLE__ 678 /* 679 * Set for this case, default to * so that all processes can use 680 * this cache. 681 */ 682 { 683 heim_array_t bundleacl = heim_array_create(); 684 heim_string_t ace; 685 686 if (bundle_acl_strings.num_strings > 0) { 687 int i; 688 for (i = 0; i < bundle_acl_strings.num_strings; i++) { 689 ace = heim_string_create(bundle_acl_strings.strings[i]); 690 heim_array_append_value(bundleacl, ace); 691 heim_release(ace); 692 } 693 } else { 694 ace = heim_string_create("*"); 695 heim_array_append_value(bundleacl, ace); 696 heim_release(ace); 697 } 698 krb5_cc_set_acl(context, tempccache, "kHEIMAttrBundleIdentifierACL", bundleacl); 699 heim_release(bundleacl); 700 } 701 #endif 702 703 ret = krb5_cc_move(context, tempccache, ccache); 704 if (ret) { 705 (void)krb5_cc_destroy(context, tempccache); 706 if (empty_placeholder) { 707 //remove the placeholder cache too 708 (void)krb5_cc_destroy(context, ccache); 709 } 710 krb5_err (context, 1, ret, "krb5_cc_move"); 711 } 712 713 if (switch_cache_flags) 714 krb5_cc_switch(context, ccache); 715 716 if (ok_as_delegate_flag || windows_flag || use_referrals_flag) { 717 unsigned char d = 0; 718 krb5_data data; 719 720 if (ok_as_delegate_flag || windows_flag) 721 d |= 1; 722 if (use_referrals_flag || windows_flag) 723 d |= 2; 724 725 data.length = 1; 726 data.data = &d; 727 728 krb5_cc_set_config(context, ccache, NULL, "realm-config", &data); 729 } 730 731 if (enctype) 732 free(enctype); 733 734 krb5_init_creds_free(context, icc); 735 krb5_get_init_creds_opt_free(context, opt); 736 737 if (kt) 738 krb5_kt_close(context, kt); 739 740 #ifdef __APPLE__ 741 if (passwordItem) 742 CFRelease(passwordItem); 743 #endif 744 745 return 0; 746 } 747 748 static time_t 749 ticket_lifetime(krb5_context context, krb5_ccache cache, 750 krb5_principal client, const char *server) 751 { 752 krb5_creds in_cred, *cred; 753 krb5_error_code ret; 754 time_t timeout; 755 756 memset(&in_cred, 0, sizeof(in_cred)); 757 758 ret = krb5_cc_get_principal(context, cache, &in_cred.client); 759 if(ret) { 760 krb5_warn(context, ret, "krb5_cc_get_principal"); 761 return 0; 762 } 763 ret = get_server(context, in_cred.client, server, &in_cred.server); 764 if(ret) { 765 krb5_free_principal(context, in_cred.client); 766 krb5_warn(context, ret, "get_server"); 767 return 0; 768 } 769 770 ret = krb5_get_credentials(context, KRB5_GC_CACHED, 771 cache, &in_cred, &cred); 772 krb5_free_principal(context, in_cred.client); 773 krb5_free_principal(context, in_cred.server); 774 if(ret) { 775 krb5_warn(context, ret, "krb5_get_credentials"); 776 return 0; 777 } 778 timeout = cred->times.endtime - cred->times.starttime; 779 if (timeout < 0) 780 timeout = 0; 781 krb5_free_creds(context, cred); 782 return timeout; 783 } 784 785 struct renew_ctx { 786 krb5_context context; 787 krb5_ccache ccache; 788 krb5_principal principal; 789 krb5_deltat ticket_life; 790 }; 791 792 static time_t 793 renew_func(void *ptr) 794 { 795 struct renew_ctx *ctx = ptr; 796 krb5_error_code ret; 797 time_t expire; 798 int new_tickets = 0; 799 800 if (renewable_flag) { 801 ret = renew_validate(ctx->context, renewable_flag, validate_flag, 802 ctx->ccache, server_str, ctx->ticket_life); 803 if (ret) 804 new_tickets = 1; 805 } else 806 new_tickets = 1; 807 808 if (new_tickets) 809 get_new_tickets(ctx->context, ctx->principal, 810 ctx->ccache, ctx->ticket_life, 0, false); 811 812 #ifndef NO_AFS 813 if(do_afslog && k_hasafs()) 814 krb5_afslog(ctx->context, ctx->ccache, NULL, NULL); 815 #endif 816 817 expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, 818 server_str) / 2; 819 return expire + 1; 820 } 821 822 int 823 main (int argc, char **argv) 824 { 825 krb5_error_code ret; 826 krb5_context context; 827 krb5_ccache ccache; 828 krb5_principal principal; 829 int optidx = 0; 830 krb5_deltat ticket_life = 0; 831 int parseflags = 0; 832 bool empty_placeholder = false; 833 834 setprogname (argv[0]); 835 836 setlocale (LC_ALL, ""); 837 bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR); 838 textdomain("heimdal_kuser"); 839 840 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 841 usage(1); 842 843 if (help_flag) 844 usage (0); 845 846 if(version_flag) { 847 print_version(NULL); 848 exit(0); 849 } 850 851 argc -= optidx; 852 argv += optidx; 853 854 if (!home_directory_flag) 855 krb5_set_home_dir_access(NULL, FALSE); 856 857 ret = krb5_init_context (&context); 858 if (ret == KRB5_CONFIG_BADFORMAT) 859 errx (1, "krb5_init_context failed to parse configuration file"); 860 else if (ret) 861 errx(1, "krb5_init_context failed: %d", ret); 862 863 if (enterprise_flag) 864 parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE; 865 866 if (pk_enterprise_flag) { 867 #ifdef PKINIT 868 ret = krb5_pk_enterprise_cert(context, pk_user_id, 869 argv[0], &principal, 870 &ent_user_id); 871 if (ret) 872 krb5_err(context, 1, ret, "krb5_pk_enterprise_certs"); 873 pk_user_id = NULL; 874 #endif 875 876 877 } else if (anonymous_flag) { 878 879 ret = krb5_make_principal(context, &principal, argv[0], 880 KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, 881 NULL); 882 if (ret) 883 krb5_err(context, 1, ret, "krb5_make_principal"); 884 krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN); 885 886 } else { 887 if (argv[0]) { 888 ret = krb5_parse_name_flags (context, argv[0], parseflags, 889 &principal); 890 if (ret) 891 krb5_err (context, 1, ret, "krb5_parse_name"); 892 } else { 893 ret = krb5_get_default_principal (context, &principal); 894 if (ret) 895 krb5_err (context, 1, ret, "krb5_get_default_principal"); 896 } 897 } 898 899 if(fcache_version) 900 krb5_set_fcache_version(context, fcache_version); 901 902 if(renewable_flag == -1) 903 /* this seems somewhat pointless, but whatever */ 904 krb5_appdefault_boolean(context, "kinit", 905 krb5_principal_get_realm(context, principal), 906 "renewable", FALSE, &renewable_flag); 907 if(do_afslog == -1) 908 krb5_appdefault_boolean(context, "kinit", 909 krb5_principal_get_realm(context, principal), 910 "afslog", TRUE, &do_afslog); 911 912 if(cred_cache) 913 ret = krb5_cc_resolve(context, cred_cache, &ccache); 914 else { 915 if(argc > 1) { 916 char s[1024]; 917 ret = krb5_cc_new_unique(context, NULL, NULL, &ccache); 918 if(ret) 919 krb5_err(context, 1, ret, "creating cred cache"); 920 snprintf(s, sizeof(s), "%s:%s", 921 krb5_cc_get_type(context, ccache), 922 krb5_cc_get_name(context, ccache)); 923 setenv("KRB5CCNAME", s, 1); 924 } else { 925 ret = krb5_cc_cache_match(context, principal, &ccache); 926 if (ret) { 927 const char *type; 928 ret = krb5_cc_default (context, &ccache); 929 if (ret) 930 krb5_err (context, 1, ret, N_("resolving credentials cache", "")); 931 932 /* 933 * Check if the type support switching, and we do, 934 * then do that instead over overwriting the current 935 * default credential 936 */ 937 type = krb5_cc_get_type(context, ccache); 938 if (krb5_cc_support_switch(context, type)) { 939 krb5_cc_close(context, ccache); 940 ret = krb5_cc_new_unique(context, type, NULL, &ccache); 941 empty_placeholder = true; //if nothing is stored in it, then it should be removed. 942 } 943 } 944 } 945 } 946 if (ret) 947 krb5_err (context, 1, ret, N_("resolving credentials cache", "")); 948 949 #ifndef NO_AFS 950 if(argc > 1 && k_hasafs ()) 951 k_setpag(); 952 #endif 953 954 if (lifetime) { 955 int tmp = parse_time (lifetime, "s"); 956 if (tmp < 0) 957 errx (1, N_("unparsable time: %s", ""), lifetime); 958 959 ticket_life = tmp; 960 } 961 962 if(addrs_flag == 0 && extra_addresses.num_strings > 0) 963 krb5_errx(context, 1, 964 N_("specifying both extra addresses and " 965 "no addresses makes no sense", "")); 966 { 967 int i; 968 krb5_addresses addresses; 969 memset(&addresses, 0, sizeof(addresses)); 970 for(i = 0; i < extra_addresses.num_strings; i++) { 971 ret = krb5_parse_address(context, extra_addresses.strings[i], 972 &addresses); 973 if (ret == 0) { 974 krb5_add_extra_addresses(context, &addresses); 975 krb5_free_addresses(context, &addresses); 976 } 977 } 978 free_getarg_strings(&extra_addresses); 979 } 980 981 if(renew_flag || validate_flag) { 982 ret = renew_validate(context, renew_flag, validate_flag, 983 ccache, server_str, ticket_life); 984 exit(ret != 0); 985 } 986 987 get_new_tickets(context, principal, ccache, ticket_life, 1, empty_placeholder); 988 989 #ifndef NO_AFS 990 if(do_afslog && k_hasafs()) 991 krb5_afslog(context, ccache, NULL, NULL); 992 #endif 993 if (verbose_flag) { 994 char *p; 995 996 ret = krb5_unparse_name(context, principal, &p); 997 if (ret) 998 krb5_err(context, 1, ret, "krb5_unparse_name"); 999 1000 printf("Placing tickets for '%s' in cache '%s:%s'\n", p, 1001 krb5_cc_get_type(context, ccache), 1002 krb5_cc_get_name(context, ccache)); 1003 free(p); 1004 } 1005 1006 if(argc > 1) { 1007 struct renew_ctx ctx; 1008 time_t timeout; 1009 1010 timeout = ticket_lifetime(context, ccache, principal, server_str) / 2; 1011 1012 ctx.context = context; 1013 ctx.ccache = ccache; 1014 ctx.principal = principal; 1015 ctx.ticket_life = ticket_life; 1016 1017 ret = simple_execvp_timed(argv[1], argv+1, 1018 renew_func, &ctx, timeout); 1019 #define EX_NOEXEC 126 1020 #define EX_NOTFOUND 127 1021 if(ret == EX_NOEXEC) 1022 krb5_warnx(context, N_("permission denied: %s", ""), argv[1]); 1023 else if(ret == EX_NOTFOUND) 1024 krb5_warnx(context, N_("command not found: %s", ""), argv[1]); 1025 1026 krb5_cc_destroy(context, ccache); 1027 #ifndef NO_AFS 1028 if(k_hasafs()) 1029 k_unlog(); 1030 #endif 1031 } else { 1032 krb5_cc_close (context, ccache); 1033 ret = 0; 1034 } 1035 krb5_free_principal(context, principal); 1036 krb5_free_context (context); 1037 return ret; 1038 }