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