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