1 /* $NetBSD: kinit.c,v 1.5 2023/06/19 21:41:42 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "kuser_locl.h" 39 40 #ifdef __APPLE__ 41 #include <Security/Security.h> 42 #endif 43 44 #ifndef NO_NTLM 45 #include <krb5/heimntlm.h> 46 #endif 47 48 #ifndef SIGINFO 49 #define SIGINFO SIGUSR1 50 #endif 51 52 int forwardable_flag = -1; 53 int proxiable_flag = -1; 54 int renewable_flag = -1; 55 int renew_flag = 0; 56 int pac_flag = -1; 57 int validate_flag = 0; 58 int version_flag = 0; 59 int help_flag = 0; 60 int addrs_flag = -1; 61 struct getarg_strings extra_addresses; 62 int anonymous_flag = 0; 63 char *lifetime = NULL; 64 char *renew_life = NULL; 65 char *server_str = NULL; 66 char *cred_cache = NULL; 67 char *start_str = NULL; 68 static int switch_cache_flags = 1; 69 struct getarg_strings etype_str; 70 int use_keytab = 0; 71 char *keytab_str = NULL; 72 static krb5_keytab kt = NULL; 73 int do_afslog = -1; 74 int fcache_version; 75 char *password_file = NULL; 76 char *pk_user_id = NULL; 77 int pk_enterprise_flag = 0; 78 struct hx509_certs_data *ent_user_id = NULL; 79 char *pk_x509_anchors = NULL; 80 int pk_use_enckey = 0; 81 static int canonicalize_flag = 0; 82 static int enterprise_flag = 0; 83 static int ok_as_delegate_flag = 0; 84 static char *fast_armor_cache_string = NULL; 85 static int use_referrals_flag = 0; 86 static int windows_flag = 0; 87 #ifndef NO_NTLM 88 static char *ntlm_domain; 89 #endif 90 91 92 static struct getargs args[] = { 93 /* 94 * used by MIT 95 * a: ~A 96 * V: verbose 97 * F: ~f 98 * P: ~p 99 * C: v4 cache name? 100 * 5: 101 * 102 * old flags 103 * 4: 104 * 9: 105 */ 106 { "afslog", 0 , arg_flag, &do_afslog, 107 NP_("obtain afs tokens", ""), NULL }, 108 109 { "cache", 'c', arg_string, &cred_cache, 110 NP_("credentials cache", ""), "cachename" }, 111 112 { "forwardable", 'F', arg_negative_flag, &forwardable_flag, 113 NP_("get tickets not forwardable", ""), NULL }, 114 115 { NULL, 'f', arg_flag, &forwardable_flag, 116 NP_("get forwardable tickets", ""), NULL }, 117 118 { "keytab", 't', arg_string, &keytab_str, 119 NP_("keytab to use", ""), "keytabname" }, 120 121 { "lifetime", 'l', arg_string, &lifetime, 122 NP_("lifetime of tickets", ""), "time" }, 123 124 { "proxiable", 'p', arg_flag, &proxiable_flag, 125 NP_("get proxiable tickets", ""), NULL }, 126 127 { "renew", 'R', arg_flag, &renew_flag, 128 NP_("renew TGT", ""), NULL }, 129 130 { "renewable", 0, arg_flag, &renewable_flag, 131 NP_("get renewable tickets", ""), NULL }, 132 133 { "renewable-life", 'r', arg_string, &renew_life, 134 NP_("renewable lifetime of tickets", ""), "time" }, 135 136 { "server", 'S', arg_string, &server_str, 137 NP_("server to get ticket for", ""), "principal" }, 138 139 { "start-time", 's', arg_string, &start_str, 140 NP_("when ticket gets valid", ""), "time" }, 141 142 { "use-keytab", 'k', arg_flag, &use_keytab, 143 NP_("get key from keytab", ""), NULL }, 144 145 { "validate", 'v', arg_flag, &validate_flag, 146 NP_("validate TGT", ""), NULL }, 147 148 { "enctypes", 'e', arg_strings, &etype_str, 149 NP_("encryption types to use", ""), "enctypes" }, 150 151 { "fcache-version", 0, arg_integer, &fcache_version, 152 NP_("file cache version to create", ""), NULL }, 153 154 { "addresses", 'A', arg_negative_flag, &addrs_flag, 155 NP_("request a ticket with no addresses", ""), NULL }, 156 157 { "extra-addresses",'a', arg_strings, &extra_addresses, 158 NP_("include these extra addresses", ""), "addresses" }, 159 160 { "anonymous", 'n', arg_flag, &anonymous_flag, 161 NP_("request an anonymous ticket", ""), NULL }, 162 163 { "request-pac", 0, arg_flag, &pac_flag, 164 NP_("request a Windows PAC", ""), NULL }, 165 166 { "password-file", 0, arg_string, &password_file, 167 NP_("read the password from a file", ""), NULL }, 168 169 { "canonicalize",0, arg_flag, &canonicalize_flag, 170 NP_("canonicalize client principal", ""), NULL }, 171 172 { "enterprise",0, arg_flag, &enterprise_flag, 173 NP_("parse principal as a KRB5-NT-ENTERPRISE name", ""), NULL }, 174 #ifdef PKINIT 175 { "pk-enterprise", 0, arg_flag, &pk_enterprise_flag, 176 NP_("use enterprise name from certificate", ""), NULL }, 177 178 { "pk-user", 'C', arg_string, &pk_user_id, 179 NP_("principal's public/private/certificate identifier", ""), "id" }, 180 181 { "x509-anchors", 'D', arg_string, &pk_x509_anchors, 182 NP_("directory with CA certificates", ""), "directory" }, 183 184 { "pk-use-enckey", 0, arg_flag, &pk_use_enckey, 185 NP_("Use RSA encrypted reply (instead of DH)", ""), NULL }, 186 #endif 187 #ifndef NO_NTLM 188 { "ntlm-domain", 0, arg_string, &ntlm_domain, 189 NP_("NTLM domain", ""), "domain" }, 190 #endif 191 192 { "change-default", 0, arg_negative_flag, &switch_cache_flags, 193 NP_("switch the default cache to the new credentials cache", ""), NULL }, 194 195 { "ok-as-delegate", 0, arg_flag, &ok_as_delegate_flag, 196 NP_("honor ok-as-delegate on tickets", ""), NULL }, 197 198 { "fast-armor-cache", 0, arg_string, &fast_armor_cache_string, 199 NP_("use this credential cache as FAST armor cache", ""), "cache" }, 200 201 { "use-referrals", 0, arg_flag, &use_referrals_flag, 202 NP_("only use referrals, no dns canalisation", ""), NULL }, 203 204 { "windows", 0, arg_flag, &windows_flag, 205 NP_("get windows behavior", ""), NULL }, 206 207 { "version", 0, arg_flag, &version_flag, NULL, NULL }, 208 { "help", 0, arg_flag, &help_flag, NULL, NULL } 209 }; 210 211 static char * 212 get_default_realm(krb5_context context); 213 214 static void 215 usage(int ret) 216 { 217 arg_printusage_i18n(args, sizeof(args)/sizeof(*args), N_("Usage: ", ""), 218 NULL, "[principal [command]]", getarg_i18n); 219 exit(ret); 220 } 221 222 static krb5_error_code 223 get_server(krb5_context context, 224 krb5_principal client, 225 const char *server, 226 krb5_principal *princ) 227 { 228 krb5_const_realm realm; 229 if (server) 230 return krb5_parse_name(context, server, princ); 231 232 realm = krb5_principal_get_realm(context, client); 233 return krb5_make_principal(context, princ, realm, 234 KRB5_TGS_NAME, realm, NULL); 235 } 236 237 static krb5_error_code 238 copy_configs(krb5_context context, 239 krb5_ccache dst, 240 krb5_ccache src, 241 krb5_principal start_ticket_server) 242 { 243 krb5_error_code ret; 244 const char *cfg_names[] = {"realm-config", "FriendlyName", "anon_pkinit_realm", NULL}; 245 const char *cfg_names_w_pname[] = {"fast_avail", NULL}; 246 krb5_data cfg_data; 247 size_t i; 248 249 for (i = 0; cfg_names[i]; i++) { 250 ret = krb5_cc_get_config(context, src, NULL, cfg_names[i], &cfg_data); 251 if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) { 252 continue; 253 } else if (ret) { 254 krb5_warn(context, ret, "krb5_cc_get_config"); 255 return ret; 256 } 257 ret = krb5_cc_set_config(context, dst, NULL, cfg_names[i], &cfg_data); 258 if (ret) 259 krb5_warn(context, ret, "krb5_cc_set_config"); 260 } 261 for (i = 0; start_ticket_server && cfg_names_w_pname[i]; i++) { 262 ret = krb5_cc_get_config(context, src, start_ticket_server, 263 cfg_names_w_pname[i], &cfg_data); 264 if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) { 265 continue; 266 } else if (ret) { 267 krb5_warn(context, ret, "krb5_cc_get_config"); 268 return ret; 269 } 270 ret = krb5_cc_set_config(context, dst, start_ticket_server, 271 cfg_names_w_pname[i], &cfg_data); 272 if (ret && ret != KRB5_CC_NOTFOUND) 273 krb5_warn(context, ret, "krb5_cc_set_config"); 274 } 275 /* 276 * We don't copy cc configs for any other principals though (mostly 277 * those are per-target time offsets and the like, so it's bad to 278 * lose them, but hardly the end of the world, and as they may not 279 * expire anyways, it's good to let them go). 280 */ 281 return 0; 282 } 283 284 static krb5_error_code 285 get_anon_pkinit_tgs_name(krb5_context context, 286 krb5_ccache ccache, 287 krb5_principal *tgs_name) 288 { 289 krb5_error_code ret; 290 krb5_data data; 291 char *realm; 292 293 ret = krb5_cc_get_config(context, ccache, NULL, "anon_pkinit_realm", &data); 294 if (ret == 0) 295 realm = strndup(data.data, data.length); 296 else 297 realm = get_default_realm(context); 298 299 krb5_data_free(&data); 300 301 if (realm == NULL) 302 return krb5_enomem(context); 303 304 ret = krb5_make_principal(context, tgs_name, realm, 305 KRB5_TGS_NAME, realm, NULL); 306 307 free(realm); 308 309 return ret; 310 } 311 312 static krb5_error_code 313 renew_validate(krb5_context context, 314 int renew, 315 int validate, 316 krb5_ccache cache, 317 const char *server, 318 krb5_deltat life) 319 { 320 krb5_error_code ret; 321 krb5_ccache tempccache = NULL; 322 krb5_creds in, *out = NULL; 323 krb5_kdc_flags flags; 324 325 memset(&in, 0, sizeof(in)); 326 327 ret = krb5_cc_get_principal(context, cache, &in.client); 328 if (ret) { 329 krb5_warn(context, ret, "krb5_cc_get_principal"); 330 return ret; 331 } 332 333 if (server == NULL && 334 krb5_principal_is_anonymous(context, in.client, 335 KRB5_ANON_MATCH_UNAUTHENTICATED)) 336 ret = get_anon_pkinit_tgs_name(context, cache, &in.server); 337 else 338 ret = get_server(context, in.client, server, &in.server); 339 if (ret) { 340 krb5_warn(context, ret, "get_server"); 341 goto out; 342 } 343 344 if (renew) { 345 /* 346 * no need to check the error here, it's only to be 347 * friendly to the user 348 */ 349 krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out); 350 } 351 352 flags.i = 0; 353 flags.b.renewable = flags.b.renew = renew; 354 flags.b.validate = validate; 355 356 if (forwardable_flag != -1) 357 flags.b.forwardable = forwardable_flag; 358 else if (out) 359 flags.b.forwardable = out->flags.b.forwardable; 360 361 if (proxiable_flag != -1) 362 flags.b.proxiable = proxiable_flag; 363 else if (out) 364 flags.b.proxiable = out->flags.b.proxiable; 365 366 if (anonymous_flag) 367 flags.b.request_anonymous = anonymous_flag; 368 if (life) 369 in.times.endtime = time(NULL) + life; 370 371 if (out) { 372 krb5_free_creds(context, out); 373 out = NULL; 374 } 375 376 377 ret = krb5_get_kdc_cred(context, 378 cache, 379 flags, 380 NULL, 381 NULL, 382 &in, 383 &out); 384 if (ret) { 385 krb5_warn(context, ret, "krb5_get_kdc_cred"); 386 goto out; 387 } 388 389 ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, cache), 390 NULL, &tempccache); 391 if (ret) { 392 krb5_warn(context, ret, "krb5_cc_new_unique"); 393 goto out; 394 } 395 396 ret = krb5_cc_initialize(context, tempccache, in.client); 397 if (ret) { 398 krb5_warn(context, ret, "krb5_cc_initialize"); 399 goto out; 400 } 401 402 ret = krb5_cc_store_cred(context, tempccache, out); 403 if (ret) { 404 krb5_warn(context, ret, "krb5_cc_store_cred"); 405 goto out; 406 } 407 408 /* 409 * We want to preserve cc configs as some are security-relevant, and 410 * anyways it's the friendly thing to do. 411 */ 412 ret = copy_configs(context, tempccache, cache, out->server); 413 if (ret) 414 goto out; 415 416 ret = krb5_cc_move(context, tempccache, cache); 417 if (ret) { 418 krb5_warn(context, ret, "krb5_cc_move"); 419 goto out; 420 } 421 tempccache = NULL; 422 423 out: 424 if (tempccache) 425 krb5_cc_destroy(context, tempccache); 426 if (out) 427 krb5_free_creds(context, out); 428 krb5_free_cred_contents(context, &in); 429 return ret; 430 } 431 432 #ifndef NO_NTLM 433 434 static krb5_error_code 435 store_ntlmkey(krb5_context context, krb5_ccache id, 436 const char *domain, struct ntlm_buf *buf) 437 { 438 krb5_error_code ret; 439 krb5_data data; 440 char *name; 441 int aret; 442 443 ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain", &data); 444 if (ret == 0) { 445 krb5_data_free(&data); 446 } else { 447 data.length = strlen(domain); 448 data.data = rk_UNCONST(domain); 449 ret = krb5_cc_set_config(context, id, NULL, "default-ntlm-domain", &data); 450 if (ret != 0) 451 return ret; 452 } 453 454 aret = asprintf(&name, "ntlm-key-%s", domain); 455 if (aret == -1 || name == NULL) 456 return krb5_enomem(context); 457 458 data.length = buf->length; 459 data.data = buf->data; 460 461 ret = krb5_cc_set_config(context, id, NULL, name, &data); 462 free(name); 463 return ret; 464 } 465 #endif 466 467 static krb5_error_code 468 get_new_tickets(krb5_context context, 469 krb5_principal principal, 470 krb5_ccache ccache, 471 krb5_deltat ticket_life, 472 int interactive, 473 int anonymous_pkinit) 474 { 475 krb5_error_code ret; 476 krb5_creds cred; 477 char passwd[256]; 478 krb5_deltat start_time = 0; 479 krb5_deltat renew = 0; 480 const char *renewstr = NULL; 481 krb5_enctype *enctype = NULL; 482 krb5_ccache tempccache = NULL; 483 krb5_init_creds_context ctx = NULL; 484 krb5_get_init_creds_opt *opt = NULL; 485 krb5_prompter_fct prompter = krb5_prompter_posix; 486 #ifndef NO_NTLM 487 struct ntlm_buf ntlmkey; 488 memset(&ntlmkey, 0, sizeof(ntlmkey)); 489 #endif 490 passwd[0] = '\0'; 491 492 if (!interactive) 493 prompter = NULL; 494 495 if (password_file) { 496 FILE *f; 497 498 if (strcasecmp("STDIN", password_file) == 0) 499 f = stdin; 500 else 501 f = fopen(password_file, "r"); 502 if (f == NULL) { 503 krb5_warnx(context, "Failed to open the password file %s", 504 password_file); 505 return errno; 506 } 507 508 if (fgets(passwd, sizeof(passwd), f) == NULL) { 509 krb5_warnx(context, N_("Failed to read password from file %s", ""), 510 password_file); 511 fclose(f); 512 return EINVAL; /* XXX Need a better error */ 513 } 514 if (f != stdin) 515 fclose(f); 516 passwd[strcspn(passwd, "\n")] = '\0'; 517 } 518 519 #ifdef __APPLE__ 520 if (passwd[0] == '\0') { 521 const char *realm; 522 OSStatus osret; 523 UInt32 length; 524 void *buffer; 525 char *name; 526 527 realm = krb5_principal_get_realm(context, principal); 528 529 ret = krb5_unparse_name_flags(context, principal, 530 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); 531 if (ret) 532 goto nopassword; 533 534 osret = SecKeychainFindGenericPassword(NULL, strlen(realm), realm, 535 strlen(name), name, 536 &length, &buffer, NULL); 537 free(name); 538 if (osret == noErr && length < sizeof(passwd) - 1) { 539 memcpy(passwd, buffer, length); 540 passwd[length] = '\0'; 541 } 542 nopassword: 543 do { } while(0); 544 } 545 #endif 546 547 memset(&cred, 0, sizeof(cred)); 548 549 ret = krb5_get_init_creds_opt_alloc(context, &opt); 550 if (ret) { 551 krb5_warn(context, ret, "krb5_get_init_creds_opt_alloc"); 552 goto out; 553 } 554 555 krb5_get_init_creds_opt_set_default_flags(context, "kinit", 556 krb5_principal_get_realm(context, principal), opt); 557 558 if (forwardable_flag != -1) 559 krb5_get_init_creds_opt_set_forwardable(opt, forwardable_flag); 560 if (proxiable_flag != -1) 561 krb5_get_init_creds_opt_set_proxiable(opt, proxiable_flag); 562 if (anonymous_flag) 563 krb5_get_init_creds_opt_set_anonymous(opt, anonymous_flag); 564 if (pac_flag != -1) 565 krb5_get_init_creds_opt_set_pac_request(context, opt, 566 pac_flag ? TRUE : FALSE); 567 if (canonicalize_flag) 568 krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE); 569 if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag) 570 krb5_get_init_creds_opt_set_win2k(context, opt, TRUE); 571 if (pk_user_id || ent_user_id || anonymous_pkinit) { 572 ret = krb5_get_init_creds_opt_set_pkinit(context, opt, 573 principal, 574 pk_user_id, 575 pk_x509_anchors, 576 NULL, 577 NULL, 578 pk_use_enckey ? KRB5_GIC_OPT_PKINIT_USE_ENCKEY : 0 | 579 anonymous_pkinit ? KRB5_GIC_OPT_PKINIT_ANONYMOUS : 0, 580 prompter, 581 NULL, 582 passwd); 583 if (ret) { 584 krb5_warn(context, ret, "krb5_get_init_creds_opt_set_pkinit"); 585 goto out; 586 } 587 if (ent_user_id) 588 krb5_get_init_creds_opt_set_pkinit_user_certs(context, opt, ent_user_id); 589 } 590 591 if (addrs_flag != -1) 592 krb5_get_init_creds_opt_set_addressless(context, opt, 593 addrs_flag ? FALSE : TRUE); 594 595 if (renew_life == NULL && renewable_flag) 596 renewstr = "6 months"; 597 if (renew_life) 598 renewstr = renew_life; 599 if (renewstr) { 600 renew = parse_time(renewstr, "s"); 601 if (renew < 0) 602 errx(1, "unparsable time: %s", renewstr); 603 604 krb5_get_init_creds_opt_set_renew_life(opt, renew); 605 } 606 607 if (ticket_life != 0) 608 krb5_get_init_creds_opt_set_tkt_life(opt, ticket_life); 609 610 if (start_str) { 611 int tmp = parse_time(start_str, "s"); 612 if (tmp < 0) 613 errx(1, N_("unparsable time: %s", ""), start_str); 614 615 start_time = tmp; 616 } 617 618 if (etype_str.num_strings) { 619 int i; 620 621 enctype = malloc(etype_str.num_strings * sizeof(*enctype)); 622 if (enctype == NULL) 623 errx(1, "out of memory"); 624 for(i = 0; i < etype_str.num_strings; i++) { 625 ret = krb5_string_to_enctype(context, 626 etype_str.strings[i], 627 &enctype[i]); 628 if (ret) 629 errx(1, "unrecognized enctype: %s", etype_str.strings[i]); 630 } 631 krb5_get_init_creds_opt_set_etype_list(opt, enctype, 632 etype_str.num_strings); 633 } 634 635 ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx); 636 if (ret) { 637 krb5_warn(context, ret, "krb5_init_creds_init"); 638 goto out; 639 } 640 641 if (server_str) { 642 ret = krb5_init_creds_set_service(context, ctx, server_str); 643 if (ret) { 644 krb5_warn(context, ret, "krb5_init_creds_set_service"); 645 goto out; 646 } 647 } 648 649 if (fast_armor_cache_string) { 650 krb5_ccache fastid; 651 652 ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid); 653 if (ret) { 654 krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)"); 655 goto out; 656 } 657 658 ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid); 659 if (ret) { 660 krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache"); 661 goto out; 662 } 663 } 664 665 if (use_keytab || keytab_str) { 666 ret = krb5_init_creds_set_keytab(context, ctx, kt); 667 if (ret) { 668 krb5_warn(context, ret, "krb5_init_creds_set_keytab"); 669 goto out; 670 } 671 } else if (pk_user_id || ent_user_id || 672 krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY)) { 673 /* nop */; 674 } else if (!interactive && passwd[0] == '\0') { 675 static int already_warned = 0; 676 677 if (!already_warned) 678 krb5_warnx(context, "Not interactive, failed to get " 679 "initial ticket"); 680 krb5_get_init_creds_opt_free(context, opt); 681 already_warned = 1; 682 return 0; 683 } else { 684 685 if (passwd[0] == '\0') { 686 char *p, *prompt; 687 int aret = 0; 688 689 ret = krb5_unparse_name(context, principal, &p); 690 if (ret) 691 errx(1, "failed to generate passwd prompt: not enough memory"); 692 693 aret = asprintf(&prompt, N_("%s's Password: ", ""), p); 694 free(p); 695 if (aret == -1) 696 errx(1, "failed to generate passwd prompt: not enough memory"); 697 698 if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ 699 memset(passwd, 0, sizeof(passwd)); 700 errx(1, "failed to read password"); 701 } 702 free(prompt); 703 } 704 705 if (passwd[0]) { 706 ret = krb5_init_creds_set_password(context, ctx, passwd); 707 if (ret) { 708 krb5_warn(context, ret, "krb5_init_creds_set_password"); 709 goto out; 710 } 711 } 712 } 713 714 ret = krb5_init_creds_get(context, ctx); 715 716 #ifndef NO_NTLM 717 if (ntlm_domain && passwd[0]) 718 heim_ntlm_nt_key(passwd, &ntlmkey); 719 #endif 720 memset_s(passwd, sizeof(passwd), 0, sizeof(passwd)); 721 722 switch(ret){ 723 case 0: 724 break; 725 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 726 exit(1); 727 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 728 case KRB5KRB_AP_ERR_MODIFIED: 729 case KRB5KDC_ERR_PREAUTH_FAILED: 730 case KRB5_GET_IN_TKT_LOOP: 731 krb5_warnx(context, N_("Password incorrect", "")); 732 goto out; 733 case KRB5KRB_AP_ERR_V4_REPLY: 734 krb5_warnx(context, N_("Looks like a Kerberos 4 reply", "")); 735 goto out; 736 case KRB5KDC_ERR_KEY_EXPIRED: 737 krb5_warnx(context, N_("Password expired", "")); 738 goto out; 739 default: 740 krb5_warn(context, ret, "krb5_get_init_creds"); 741 goto out; 742 } 743 744 krb5_process_last_request(context, opt, ctx); 745 746 ret = krb5_init_creds_get_creds(context, ctx, &cred); 747 if (ret) { 748 krb5_warn(context, ret, "krb5_init_creds_get_creds"); 749 goto out; 750 } 751 752 if (ticket_life != 0) { 753 krb5_deltat d = cred.times.endtime - cred.times.starttime; 754 if (llabs(d - ticket_life) > 30) { 755 char life[64]; 756 unparse_time_approx(d, life, sizeof(life)); 757 krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life); 758 } 759 } 760 if (renew_life) { 761 krb5_deltat d = cred.times.renew_till - cred.times.starttime; 762 if (llabs(d - renew) > 30) { 763 char life[64]; 764 unparse_time_approx(d, life, sizeof(life)); 765 krb5_warnx(context, 766 N_("NOTICE: ticket renewable lifetime is %s", ""), 767 life); 768 } 769 } 770 krb5_free_cred_contents(context, &cred); 771 772 ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache), 773 NULL, &tempccache); 774 if (ret) { 775 krb5_warn(context, ret, "krb5_cc_new_unique"); 776 goto out; 777 } 778 779 ret = krb5_init_creds_store(context, ctx, tempccache); 780 if (ret) { 781 krb5_warn(context, ret, "krb5_init_creds_store"); 782 goto out; 783 } 784 785 krb5_init_creds_free(context, ctx); 786 ctx = NULL; 787 788 ret = krb5_cc_move(context, tempccache, ccache); 789 if (ret) { 790 krb5_warn(context, ret, "krb5_cc_move"); 791 goto out; 792 } 793 tempccache = NULL; 794 795 if (switch_cache_flags) 796 krb5_cc_switch(context, ccache); 797 798 #ifndef NO_NTLM 799 if (ntlm_domain && ntlmkey.data) 800 store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey); 801 #endif 802 803 if (ok_as_delegate_flag || windows_flag || use_referrals_flag) { 804 unsigned char d = 0; 805 krb5_data data; 806 807 if (ok_as_delegate_flag || windows_flag) 808 d |= 1; 809 if (use_referrals_flag || windows_flag) 810 d |= 2; 811 812 data.length = 1; 813 data.data = &d; 814 815 krb5_cc_set_config(context, ccache, NULL, "realm-config", &data); 816 } 817 818 if (anonymous_pkinit) { 819 krb5_data data; 820 821 data.length = strlen(principal->realm); 822 data.data = principal->realm; 823 824 krb5_cc_set_config(context, ccache, NULL, "anon_pkinit_realm", &data); 825 } 826 827 out: 828 krb5_get_init_creds_opt_free(context, opt); 829 if (ctx) 830 krb5_init_creds_free(context, ctx); 831 if (tempccache) 832 krb5_cc_destroy(context, tempccache); 833 834 if (enctype) 835 free(enctype); 836 837 return ret; 838 } 839 840 static time_t 841 ticket_lifetime(krb5_context context, krb5_ccache cache, krb5_principal client, 842 const char *server, time_t *renew) 843 { 844 krb5_creds in_cred, *cred; 845 krb5_error_code ret; 846 time_t timeout; 847 time_t curtime; 848 849 memset(&in_cred, 0, sizeof(in_cred)); 850 851 if (renew != NULL) 852 *renew = 0; 853 854 ret = krb5_cc_get_principal(context, cache, &in_cred.client); 855 if (ret) { 856 krb5_warn(context, ret, "krb5_cc_get_principal"); 857 return 0; 858 } 859 ret = get_server(context, in_cred.client, server, &in_cred.server); 860 if (ret) { 861 krb5_free_principal(context, in_cred.client); 862 krb5_warn(context, ret, "get_server"); 863 return 0; 864 } 865 866 ret = krb5_get_credentials(context, KRB5_GC_CACHED, 867 cache, &in_cred, &cred); 868 krb5_free_principal(context, in_cred.client); 869 krb5_free_principal(context, in_cred.server); 870 if (ret) { 871 krb5_warn(context, ret, "krb5_get_credentials"); 872 return 0; 873 } 874 curtime = time(NULL); 875 timeout = cred->times.endtime - curtime; 876 if (timeout < 0) 877 timeout = 0; 878 if (renew) { 879 *renew = cred->times.renew_till - curtime; 880 if (*renew < 0) 881 *renew = 0; 882 } 883 krb5_free_creds(context, cred); 884 return timeout; 885 } 886 887 static time_t expire; 888 889 static char siginfo_msg[1024] = "No credentials\n"; 890 891 static void 892 update_siginfo_msg(time_t exp, const char *srv) 893 { 894 /* Note that exp is relative time */ 895 memset(siginfo_msg, 0, sizeof(siginfo_msg)); 896 memcpy(&siginfo_msg, "Updating...\n", sizeof("Updating...\n")); 897 if (exp) { 898 if (srv == NULL) { 899 snprintf(siginfo_msg, sizeof(siginfo_msg), 900 N_("kinit: TGT expires in %llu seconds\n", ""), 901 (unsigned long long)expire); 902 } else { 903 snprintf(siginfo_msg, sizeof(siginfo_msg), 904 N_("kinit: Ticket for %s expired\n", ""), srv); 905 } 906 return; 907 } 908 909 /* Expired creds */ 910 if (srv == NULL) { 911 snprintf(siginfo_msg, sizeof(siginfo_msg), 912 N_("kinit: TGT expired\n", "")); 913 } else { 914 snprintf(siginfo_msg, sizeof(siginfo_msg), 915 N_("kinit: Ticket for %s expired\n", ""), srv); 916 } 917 } 918 919 #ifdef HAVE_SIGACTION 920 static void 921 handle_siginfo(int sig) 922 { 923 struct iovec iov[2]; 924 925 iov[0].iov_base = rk_UNCONST(siginfo_msg); 926 iov[0].iov_len = strlen(siginfo_msg); 927 iov[1].iov_base = "\n"; 928 iov[1].iov_len = 1; 929 930 writev(STDERR_FILENO, iov, sizeof(iov)/sizeof(iov[0])); 931 } 932 #endif 933 934 struct renew_ctx { 935 krb5_context context; 936 krb5_ccache ccache; 937 krb5_principal principal; 938 krb5_deltat ticket_life; 939 krb5_deltat timeout; 940 }; 941 942 static time_t 943 renew_func(void *ptr) 944 { 945 krb5_error_code ret; 946 struct renew_ctx *ctx = ptr; 947 time_t renew_expire; 948 static time_t exp_delay = 1; 949 950 /* 951 * NOTE: We count on the ccache implementation to notice changes to the 952 * actual ccache filesystem/whatever objects. There should be no ccache 953 * types for which this is not the case, but it might not hurt to 954 * re-krb5_cc_resolve() after each successful renew_validate()/ 955 * get_new_tickets() call. 956 */ 957 958 expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, 959 server_str, &renew_expire); 960 961 /* 962 * When a keytab is available to obtain new tickets, if we are within 963 * half of the original ticket lifetime of the renew limit, get a new 964 * TGT instead of renewing the existing TGT. Note, ctx->ticket_life 965 * is zero by default (without a '-l' option) and cannot be used to 966 * set the time scale on which we decide whether we're "close to the 967 * renew limit". 968 */ 969 if (use_keytab || keytab_str) 970 expire += ctx->timeout; 971 if (renew_expire > expire) { 972 ret = renew_validate(ctx->context, 1, validate_flag, ctx->ccache, 973 server_str, ctx->ticket_life); 974 } else { 975 ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache, 976 ctx->ticket_life, 0, 0); 977 } 978 expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, 979 server_str, &renew_expire); 980 981 #ifndef NO_AFS 982 if (ret == 0 && server_str == NULL && do_afslog && k_hasafs()) 983 krb5_afslog(ctx->context, ctx->ccache, NULL, NULL); 984 #endif 985 986 update_siginfo_msg(expire, server_str); 987 988 /* 989 * If our tickets have expired and we been able to either renew them 990 * or obtain new tickets, then we still call this function but we use 991 * an exponential backoff. This should take care of the case where 992 * we are using stored credentials but the KDC has been unavailable 993 * for some reason... 994 */ 995 996 if (expire < 1) { 997 /* 998 * We can't ask to keep spamming stderr but not syslog, so we warn 999 * only once. 1000 */ 1001 if (exp_delay == 1) { 1002 krb5_warnx(ctx->context, N_("NOTICE: Could not renew/refresh " 1003 "tickets", "")); 1004 } 1005 if (exp_delay < 7200) 1006 exp_delay += exp_delay / 2 + 1; 1007 return exp_delay; 1008 } 1009 exp_delay = 1; 1010 1011 return expire / 2 + 1; 1012 } 1013 1014 static void 1015 set_princ_realm(krb5_context context, 1016 krb5_principal principal, 1017 const char *realm) 1018 { 1019 krb5_error_code ret; 1020 1021 if ((ret = krb5_principal_set_realm(context, principal, realm)) != 0) 1022 krb5_err(context, 1, ret, "krb5_principal_set_realm"); 1023 } 1024 1025 static void 1026 parse_name_realm(krb5_context context, 1027 const char *name, 1028 int flags, 1029 const char *realm, 1030 krb5_principal *princ) 1031 { 1032 krb5_error_code ret; 1033 1034 if (realm) 1035 flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM; 1036 if ((ret = krb5_parse_name_flags(context, name, flags, princ)) != 0) 1037 krb5_err(context, 1, ret, "krb5_parse_name_flags"); 1038 if (realm && krb5_principal_get_realm(context, *princ) == NULL) 1039 set_princ_realm(context, *princ, realm); 1040 } 1041 1042 static char * 1043 get_default_realm(krb5_context context) 1044 { 1045 char *realm; 1046 krb5_error_code ret; 1047 1048 if ((ret = krb5_get_default_realm(context, &realm)) != 0) 1049 krb5_err(context, 1, ret, "krb5_get_default_realm"); 1050 return realm; 1051 } 1052 1053 static void 1054 get_default_principal(krb5_context context, krb5_principal *princ) 1055 { 1056 krb5_error_code ret; 1057 1058 if ((ret = krb5_get_default_principal(context, princ)) != 0) 1059 krb5_err(context, 1, ret, "krb5_get_default_principal"); 1060 } 1061 1062 static char * 1063 get_user_realm(krb5_context context) 1064 { 1065 krb5_error_code ret; 1066 char *user_realm = NULL; 1067 1068 /* 1069 * If memory allocation fails, we don't try to use the wrong realm, 1070 * that will trigger misleading error messages complicate support. 1071 */ 1072 krb5_appdefault_string(context, "kinit", NULL, "user_realm", "", 1073 &user_realm); 1074 if (user_realm == NULL) { 1075 ret = krb5_enomem(context); 1076 krb5_err(context, 1, ret, "krb5_appdefault_string"); 1077 } 1078 1079 if (*user_realm == 0) { 1080 free(user_realm); 1081 user_realm = NULL; 1082 } 1083 1084 return user_realm; 1085 } 1086 1087 static void 1088 get_princ(krb5_context context, krb5_principal *principal, const char *name) 1089 { 1090 krb5_error_code ret; 1091 krb5_principal tmp; 1092 int parseflags = 0; 1093 char *user_realm; 1094 1095 if (name == NULL) { 1096 krb5_ccache ccache; 1097 1098 /* If credential cache provides a client principal, use that. */ 1099 if (krb5_cc_default(context, &ccache) == 0) { 1100 ret = krb5_cc_get_principal(context, ccache, principal); 1101 krb5_cc_close(context, ccache); 1102 if (ret == 0) 1103 return; 1104 } 1105 } 1106 1107 user_realm = get_user_realm(context); 1108 1109 if (name) { 1110 if (canonicalize_flag || enterprise_flag) 1111 parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE; 1112 1113 parse_name_realm(context, name, parseflags, user_realm, &tmp); 1114 1115 if (user_realm && krb5_principal_get_num_comp(context, tmp) > 1) { 1116 /* Principal is instance qualified, reparse with default realm. */ 1117 krb5_free_principal(context, tmp); 1118 parse_name_realm(context, name, parseflags, NULL, principal); 1119 } else { 1120 *principal = tmp; 1121 } 1122 } else { 1123 get_default_principal(context, principal); 1124 if (user_realm) 1125 set_princ_realm(context, *principal, user_realm); 1126 } 1127 1128 if (user_realm) 1129 free(user_realm); 1130 } 1131 1132 static void 1133 get_princ_kt(krb5_context context, 1134 krb5_principal *principal, 1135 char *name) 1136 { 1137 krb5_error_code ret; 1138 krb5_principal tmp; 1139 krb5_ccache ccache; 1140 krb5_kt_cursor cursor; 1141 krb5_keytab_entry entry; 1142 char *def_realm; 1143 1144 if (name == NULL) { 1145 /* 1146 * If the credential cache exists and specifies a client principal, 1147 * use that. 1148 */ 1149 if (krb5_cc_default(context, &ccache) == 0) { 1150 ret = krb5_cc_get_principal(context, ccache, principal); 1151 krb5_cc_close(context, ccache); 1152 if (ret == 0) 1153 return; 1154 } 1155 } 1156 1157 if (name) { 1158 /* If the principal specifies an explicit realm, just use that. */ 1159 int parseflags = KRB5_PRINCIPAL_PARSE_NO_DEF_REALM; 1160 1161 parse_name_realm(context, name, parseflags, NULL, &tmp); 1162 if (krb5_principal_get_realm(context, tmp) != NULL) { 1163 *principal = tmp; 1164 return; 1165 } 1166 } else { 1167 /* Otherwise, search keytab for bare name of the default principal. */ 1168 get_default_principal(context, &tmp); 1169 set_princ_realm(context, tmp, NULL); 1170 } 1171 1172 def_realm = get_default_realm(context); 1173 1174 ret = krb5_kt_start_seq_get(context, kt, &cursor); 1175 if (ret) 1176 krb5_err(context, 1, ret, "krb5_kt_start_seq_get"); 1177 1178 while (ret == 0 && 1179 krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) { 1180 const char *realm; 1181 1182 if (!krb5_principal_compare_any_realm(context, tmp, entry.principal)) 1183 continue; 1184 if (*principal && 1185 krb5_principal_compare(context, *principal, entry.principal)) 1186 continue; 1187 /* The default realm takes precedence */ 1188 realm = krb5_principal_get_realm(context, entry.principal); 1189 if (*principal && strcmp(def_realm, realm) == 0) { 1190 krb5_free_principal(context, *principal); 1191 ret = krb5_copy_principal(context, entry.principal, principal); 1192 break; 1193 } 1194 if (!*principal) 1195 ret = krb5_copy_principal(context, entry.principal, principal); 1196 } 1197 if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0) 1198 krb5_err(context, 1, ret, "get_princ_kt"); 1199 if (!*principal) { 1200 if (name) 1201 parse_name_realm(context, name, 0, NULL, principal); 1202 else 1203 krb5_err(context, 1, KRB5_CC_NOTFOUND, "get_princ_kt"); 1204 } 1205 1206 krb5_free_principal(context, tmp); 1207 free(def_realm); 1208 } 1209 1210 static krb5_error_code 1211 get_switched_ccache(krb5_context context, 1212 const char * type, 1213 krb5_principal principal, 1214 krb5_ccache *ccache) 1215 { 1216 krb5_error_code ret; 1217 1218 #ifdef _WIN32 1219 if (strcmp(type, "API") == 0) { 1220 /* 1221 * Windows stores the default ccache name in the 1222 * registry which is shared across multiple logon 1223 * sessions for the same user. The API credential 1224 * cache provides a unique name space per logon 1225 * session. Therefore there is no need to generate 1226 * a unique ccache name. Instead use the principal 1227 * name. This provides a friendlier user experience. 1228 */ 1229 char * unparsed_name; 1230 char * cred_cache; 1231 1232 ret = krb5_unparse_name(context, principal, 1233 &unparsed_name); 1234 if (ret) 1235 krb5_err(context, 1, ret, 1236 N_("unparsing principal name", "")); 1237 1238 ret = asprintf(&cred_cache, "API:%s", unparsed_name); 1239 krb5_free_unparsed_name(context, unparsed_name); 1240 if (ret == -1 || cred_cache == NULL) 1241 krb5_err(context, 1, ret, 1242 N_("building credential cache name", "")); 1243 1244 ret = krb5_cc_resolve(context, cred_cache, ccache); 1245 free(cred_cache); 1246 } else if (strcmp(type, "MSLSA") == 0) { 1247 /* 1248 * The Windows MSLSA cache when it is writeable 1249 * stores tickets for multiple client principals 1250 * in a single credential cache. 1251 */ 1252 ret = krb5_cc_resolve(context, "MSLSA:", ccache); 1253 } else { 1254 ret = krb5_cc_new_unique(context, type, NULL, ccache); 1255 } 1256 #else /* !_WIN32 */ 1257 ret = krb5_cc_new_unique(context, type, NULL, ccache); 1258 #endif /* _WIN32 */ 1259 1260 return ret; 1261 } 1262 1263 int 1264 main(int argc, char **argv) 1265 { 1266 krb5_error_code ret; 1267 krb5_context context; 1268 krb5_ccache ccache; 1269 krb5_principal principal = NULL; 1270 int optidx = 0; 1271 krb5_deltat ticket_life = 0; 1272 #ifdef HAVE_SIGACTION 1273 struct sigaction sa; 1274 #endif 1275 krb5_boolean unique_ccache = FALSE; 1276 krb5_boolean historical_anon_pkinit = FALSE; 1277 int anonymous_pkinit = FALSE; 1278 1279 setprogname(argv[0]); 1280 1281 setlocale(LC_ALL, ""); 1282 bindtextdomain("heimdal_kuser", HEIMDAL_LOCALEDIR); 1283 textdomain("heimdal_kuser"); 1284 1285 ret = krb5_init_context(&context); 1286 if (ret == KRB5_CONFIG_BADFORMAT) 1287 errx(1, "krb5_init_context failed to parse configuration file"); 1288 else if (ret) 1289 errx(1, "krb5_init_context failed: %d", ret); 1290 1291 if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 1292 usage(1); 1293 1294 if (help_flag) 1295 usage(0); 1296 1297 if (version_flag) { 1298 print_version(NULL); 1299 exit(0); 1300 } 1301 1302 argc -= optidx; 1303 argv += optidx; 1304 1305 krb5_appdefault_boolean(context, "kinit", NULL, "historical_anon_pkinit", 1306 FALSE, &historical_anon_pkinit); 1307 1308 /* 1309 * Open the keytab now, we use the keytab to determine the principal's 1310 * realm when the requested principal has no realm. 1311 */ 1312 if (use_keytab || keytab_str) { 1313 if (keytab_str) 1314 ret = krb5_kt_resolve(context, keytab_str, &kt); 1315 else 1316 ret = krb5_kt_default(context, &kt); 1317 if (ret) 1318 krb5_err(context, 1, ret, "resolving keytab"); 1319 } 1320 1321 if (pk_enterprise_flag) { 1322 ret = krb5_pk_enterprise_cert(context, pk_user_id, 1323 argv[0], &principal, 1324 &ent_user_id); 1325 if (ret) 1326 krb5_err(context, 1, ret, "krb5_pk_enterprise_certs"); 1327 1328 pk_user_id = NULL; 1329 1330 } else if (anonymous_flag && argc && argv[0][0] == '@') { 1331 /* If principal argument as @REALM, try anonymous PKINIT */ 1332 1333 ret = krb5_make_principal(context, &principal, &argv[0][1], 1334 KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, 1335 NULL); 1336 if (ret) 1337 krb5_err(context, 1, ret, "krb5_make_principal"); 1338 krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN); 1339 anonymous_pkinit = TRUE; 1340 } else if (anonymous_flag && historical_anon_pkinit) { 1341 char *realm = argc == 0 ? get_default_realm(context) : 1342 argv[0][0] == '@' ? &argv[0][1] : argv[0]; 1343 1344 ret = krb5_make_principal(context, &principal, realm, 1345 KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); 1346 if (ret) 1347 krb5_err(context, 1, ret, "krb5_make_principal"); 1348 krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN); 1349 anonymous_pkinit = TRUE; 1350 } else if (use_keytab || keytab_str) { 1351 get_princ_kt(context, &principal, argv[0]); 1352 } else { 1353 get_princ(context, &principal, argv[0]); 1354 } 1355 1356 if (fcache_version) 1357 krb5_set_fcache_version(context, fcache_version); 1358 1359 if (renewable_flag == -1) 1360 /* this seems somewhat pointless, but whatever */ 1361 krb5_appdefault_boolean(context, "kinit", 1362 krb5_principal_get_realm(context, principal), 1363 "renewable", FALSE, &renewable_flag); 1364 if (do_afslog == -1) 1365 krb5_appdefault_boolean(context, "kinit", 1366 krb5_principal_get_realm(context, principal), 1367 "afslog", TRUE, &do_afslog); 1368 1369 if (cred_cache) 1370 ret = krb5_cc_resolve(context, cred_cache, &ccache); 1371 else { 1372 if (argc > 1) { 1373 char s[1024]; 1374 ret = krb5_cc_new_unique(context, NULL, NULL, &ccache); 1375 if (ret) 1376 krb5_err(context, 1, ret, "creating cred cache"); 1377 snprintf(s, sizeof(s), "%s:%s", 1378 krb5_cc_get_type(context, ccache), 1379 krb5_cc_get_name(context, ccache)); 1380 setenv("KRB5CCNAME", s, 1); 1381 unique_ccache = TRUE; 1382 } else { 1383 ret = krb5_cc_cache_match(context, principal, &ccache); 1384 if (ret) { 1385 const char *type; 1386 ret = krb5_cc_default(context, &ccache); 1387 if (ret) 1388 krb5_err(context, 1, ret, 1389 N_("resolving credentials cache", "")); 1390 1391 /* 1392 * Check if the type support switching, and we do, 1393 * then do that instead over overwriting the current 1394 * default credential 1395 */ 1396 type = krb5_cc_get_type(context, ccache); 1397 if (krb5_cc_support_switch(context, type)) { 1398 krb5_cc_close(context, ccache); 1399 ret = get_switched_ccache(context, type, principal, 1400 &ccache); 1401 if (ret == 0) 1402 unique_ccache = TRUE; 1403 } 1404 } 1405 } 1406 } 1407 if (ret) 1408 krb5_err(context, 1, ret, N_("resolving credentials cache", "")); 1409 1410 #ifndef NO_AFS 1411 if (argc > 1 && k_hasafs()) 1412 k_setpag(); 1413 #endif 1414 1415 if (lifetime) { 1416 int tmp = parse_time(lifetime, "s"); 1417 if (tmp < 0) 1418 errx(1, N_("unparsable time: %s", ""), lifetime); 1419 1420 ticket_life = tmp; 1421 } 1422 1423 if (addrs_flag == 0 && extra_addresses.num_strings > 0) 1424 krb5_errx(context, 1, 1425 N_("specifying both extra addresses and " 1426 "no addresses makes no sense", "")); 1427 { 1428 int i; 1429 krb5_addresses addresses; 1430 memset(&addresses, 0, sizeof(addresses)); 1431 for(i = 0; i < extra_addresses.num_strings; i++) { 1432 ret = krb5_parse_address(context, extra_addresses.strings[i], 1433 &addresses); 1434 if (ret == 0) { 1435 krb5_add_extra_addresses(context, &addresses); 1436 krb5_free_addresses(context, &addresses); 1437 } 1438 } 1439 free_getarg_strings(&extra_addresses); 1440 } 1441 1442 if (renew_flag || validate_flag) { 1443 ret = renew_validate(context, renew_flag, validate_flag, 1444 ccache, server_str, ticket_life); 1445 1446 #ifndef NO_AFS 1447 if (ret == 0 && server_str == NULL && do_afslog && k_hasafs()) 1448 krb5_afslog(context, ccache, NULL, NULL); 1449 #endif 1450 1451 if (unique_ccache) 1452 krb5_cc_destroy(context, ccache); 1453 exit(ret != 0); 1454 } 1455 1456 ret = get_new_tickets(context, principal, ccache, ticket_life, 1457 1, anonymous_pkinit); 1458 if (ret) { 1459 if (unique_ccache) 1460 krb5_cc_destroy(context, ccache); 1461 exit(1); 1462 } 1463 1464 #ifndef NO_AFS 1465 if (ret == 0 && server_str == NULL && do_afslog && k_hasafs()) 1466 krb5_afslog(context, ccache, NULL, NULL); 1467 #endif 1468 1469 if (argc > 1) { 1470 struct renew_ctx ctx; 1471 time_t timeout; 1472 1473 timeout = ticket_lifetime(context, ccache, principal, 1474 server_str, NULL) / 2; 1475 1476 ctx.context = context; 1477 ctx.ccache = ccache; 1478 ctx.principal = principal; 1479 ctx.ticket_life = ticket_life; 1480 ctx.timeout = timeout; 1481 1482 #ifdef HAVE_SIGACTION 1483 memset(&sa, 0, sizeof(sa)); 1484 sigemptyset(&sa.sa_mask); 1485 sa.sa_handler = handle_siginfo; 1486 1487 sigaction(SIGINFO, &sa, NULL); 1488 #endif 1489 1490 ret = simple_execvp_timed(argv[1], argv+1, 1491 renew_func, &ctx, timeout); 1492 #define EX_NOEXEC 126 1493 #define EX_NOTFOUND 127 1494 if (ret == EX_NOEXEC) 1495 krb5_warnx(context, N_("permission denied: %s", ""), argv[1]); 1496 else if (ret == EX_NOTFOUND) 1497 krb5_warnx(context, N_("command not found: %s", ""), argv[1]); 1498 1499 krb5_cc_destroy(context, ccache); 1500 #ifndef NO_AFS 1501 if (k_hasafs()) 1502 k_unlog(); 1503 #endif 1504 } else { 1505 krb5_cc_close(context, ccache); 1506 ret = 0; 1507 } 1508 krb5_free_principal(context, principal); 1509 if (kt) 1510 krb5_kt_close(context, kt); 1511 krb5_free_context(context); 1512 return ret; 1513 } 1514