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