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