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