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