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