xref: /freebsd/crypto/heimdal/kuser/kinit.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1997-2001 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "kuser_locl.h"
35 RCSID("$Id: kinit.c,v 1.75 2001/05/07 21:08:15 assar Exp $");
36 
37 #ifdef KRB4
38 /* for when the KDC tells us it's a v4 one, we try to talk that */
39 
40 static int
41 key_to_key(const char *user,
42 	   char *instance,
43 	   const char *realm,
44 	   const void *arg,
45 	   des_cblock *key)
46 {
47     memcpy(key, arg, sizeof(des_cblock));
48     return 0;
49 }
50 
51 static int
52 do_v4_fallback (krb5_context context,
53 		const krb5_principal principal,
54 		int lifetime,
55 		int use_srvtab, const char *srvtab_str,
56 		char *passwd, size_t passwd_size)
57 {
58     int ret;
59     krb_principal princ;
60     des_cblock key;
61     krb5_error_code kret;
62 
63     if (lifetime == 0)
64 	lifetime = DEFAULT_TKT_LIFE;
65     else
66 	lifetime = krb_time_to_life (0, lifetime);
67 
68     kret = krb5_524_conv_principal (context, principal,
69 				    princ.name,
70 				    princ.instance,
71 				    princ.realm);
72     if (kret) {
73 	krb5_warn (context, kret, "krb5_524_conv_principal");
74 	return 1;
75     }
76 
77     if (use_srvtab || srvtab_str) {
78 	if (srvtab_str == NULL)
79 	    srvtab_str = KEYFILE;
80 
81 	ret = read_service_key (princ.name, princ.instance, princ.realm,
82 				0, srvtab_str, (char *)&key);
83 	if (ret) {
84 	    warnx ("read_service_key %s: %s", srvtab_str,
85 		   krb_get_err_text (ret));
86 	    return 1;
87 	}
88 	ret = krb_get_in_tkt (princ.name, princ.instance, princ.realm,
89 			      KRB_TICKET_GRANTING_TICKET, princ.realm,
90 			      lifetime, key_to_key, NULL, key);
91     } else {
92 	ret = krb_get_pw_in_tkt2(princ.name, princ.instance, princ.realm,
93 				 KRB_TICKET_GRANTING_TICKET, princ.realm,
94 				 lifetime, passwd, &key);
95     }
96     memset (passwd, 0, passwd_size);
97     memset (key, 0, sizeof(key));
98     if (ret) {
99 	warnx ("%s", krb_get_err_text(ret));
100 	return 1;
101     }
102     if (k_hasafs()) {
103 	if ((ret = krb_afslog(NULL, NULL)) != 0 && ret != KDC_PR_UNKNOWN) {
104 	    if(ret > 0)
105 		warnx ("%s", krb_get_err_text(ret));
106 	    else
107 		warnx ("failed to store AFS token");
108 	}
109     }
110     return 0;
111 }
112 
113 
114 /*
115  * the special version of get_default_principal that takes v4 into account
116  */
117 
118 static krb5_error_code
119 kinit_get_default_principal (krb5_context context,
120 			     krb5_principal *princ)
121 {
122     krb5_error_code ret;
123     krb5_ccache id;
124     krb_principal v4_princ;
125     int kret;
126 
127     ret = krb5_cc_default (context, &id);
128     if (ret == 0) {
129 	ret = krb5_cc_get_principal (context, id, princ);
130 	krb5_cc_close (context, id);
131 	if (ret == 0)
132 	    return 0;
133     }
134 
135     kret = krb_get_tf_fullname (tkt_string(),
136 				v4_princ.name,
137 				v4_princ.instance,
138 				v4_princ.realm);
139     if (kret == KSUCCESS) {
140 	ret = krb5_425_conv_principal (context,
141 				       v4_princ.name,
142 				       v4_princ.instance,
143 				       v4_princ.realm,
144 				       princ);
145 	if (ret == 0)
146 	    return 0;
147     }
148     return krb5_get_default_principal (context, princ);
149 }
150 
151 #else /* !KRB4 */
152 
153 static krb5_error_code
154 kinit_get_default_principal (krb5_context context,
155 			     krb5_principal *princ)
156 {
157     return krb5_get_default_principal (context, princ);
158 }
159 
160 #endif /* !KRB4 */
161 
162 int forwardable_flag	= -1;
163 int proxiable_flag	= -1;
164 int renewable_flag	= -1;
165 int renew_flag		= 0;
166 int validate_flag	= 0;
167 int version_flag	= 0;
168 int help_flag		= 0;
169 int addrs_flag		= 1;
170 int anonymous_flag	= 0;
171 char *lifetime 		= NULL;
172 char *renew_life	= NULL;
173 char *server		= NULL;
174 char *cred_cache	= NULL;
175 char *start_str		= NULL;
176 struct getarg_strings etype_str;
177 int use_keytab		= 0;
178 char *keytab_str	= NULL;
179 #ifdef KRB4
180 extern int do_afslog;
181 extern int get_v4_tgt;
182 #endif
183 int fcache_version;
184 
185 static struct getargs args[] = {
186 #ifdef KRB4
187     { "524init", 	'4', arg_flag, &get_v4_tgt,
188       "obtain version 4 TGT" },
189 
190     { "afslog", 	0  , arg_flag, &do_afslog,
191       "obtain afs tokens"  },
192 #endif
193     { "cache", 		'c', arg_string, &cred_cache,
194       "credentials cache", "cachename" },
195 
196     { "forwardable",	'f', arg_flag, &forwardable_flag,
197       "get forwardable tickets"},
198 
199     { "keytab",         't', arg_string, &keytab_str,
200       "keytab to use", "keytabname" },
201 
202     { "lifetime",	'l', arg_string, &lifetime,
203       "lifetime of tickets", "time"},
204 
205     { "proxiable",	'p', arg_flag, &proxiable_flag,
206       "get proxiable tickets" },
207 
208     { "renew",          'R', arg_flag, &renew_flag,
209       "renew TGT" },
210 
211     { "renewable",	0,   arg_flag, &renewable_flag,
212       "get renewable tickets" },
213 
214     { "renewable-life",	'r', arg_string, &renew_life,
215       "renewable lifetime of tickets", "time" },
216 
217     { "server", 	'S', arg_string, &server,
218       "server to get ticket for", "principal" },
219 
220     { "start-time",	's', arg_string, &start_str,
221       "when ticket gets valid", "time" },
222 
223     { "use-keytab",     'k', arg_flag, &use_keytab,
224       "get key from keytab" },
225 
226     { "validate",	'v', arg_flag, &validate_flag,
227       "validate TGT" },
228 
229     { "enctypes",	'e', arg_strings, &etype_str,
230       "encryption types to use", "enctypes" },
231 
232     { "fcache-version", 0,   arg_integer, &fcache_version,
233       "file cache version to create" },
234 
235     { "addresses",	0,   arg_negative_flag,	&addrs_flag,
236       "request a ticket with no addresses" },
237 
238     { "anonymous",	0,   arg_flag,	&anonymous_flag,
239       "request an anonymous ticket" },
240 
241     { "version", 	0,   arg_flag, &version_flag },
242     { "help",		0,   arg_flag, &help_flag }
243 };
244 
245 static void
246 usage (int ret)
247 {
248     arg_printusage (args,
249 		    sizeof(args)/sizeof(*args),
250 		    NULL,
251 		    "[principal [command]]");
252     exit (ret);
253 }
254 
255 static int
256 renew_validate(krb5_context context,
257 	       int renew,
258 	       int validate,
259 	       krb5_ccache cache,
260 	       const char *server,
261 	       krb5_deltat life)
262 {
263     krb5_error_code ret;
264     krb5_creds in, *out;
265     krb5_kdc_flags flags;
266 
267     memset(&in, 0, sizeof(in));
268 
269     ret = krb5_cc_get_principal(context, cache, &in.client);
270     if(ret) {
271 	krb5_warn(context, ret, "krb5_cc_get_principal");
272 	return ret;
273     }
274     if(server) {
275 	ret = krb5_parse_name(context, server, &in.server);
276 	if(ret) {
277 	    krb5_warn(context, ret, "krb5_parse_name");
278 	    goto out;
279 	}
280     } else {
281 	krb5_realm *client_realm = krb5_princ_realm (context, in.client);
282 
283 	ret = krb5_make_principal(context, &in.server, *client_realm,
284 				  KRB5_TGS_NAME, *client_realm, NULL);
285 	if(ret) {
286 	    krb5_warn(context, ret, "krb5_make_principal");
287 	    goto out;
288 	}
289     }
290     flags.i = 0;
291     flags.b.renewable         = flags.b.renew = renew;
292     flags.b.validate          = validate;
293     if (forwardable_flag != -1)
294 	flags.b.forwardable       = forwardable_flag;
295     if (proxiable_flag != -1)
296 	flags.b.proxiable         = proxiable_flag;
297     if (anonymous_flag != -1)
298 	flags.b.request_anonymous = anonymous_flag;
299     if(life)
300 	in.times.endtime = time(NULL) + life;
301 
302     ret = krb5_get_kdc_cred(context,
303 			    cache,
304 			    flags,
305 			    NULL,
306 			    NULL,
307 			    &in,
308 			    &out);
309     if(ret) {
310 	krb5_warn(context, ret, "krb5_get_kdc_cred");
311 	goto out;
312     }
313     ret = krb5_cc_initialize(context, cache, in.client);
314     if(ret) {
315 	krb5_free_creds (context, out);
316 	krb5_warn(context, ret, "krb5_cc_initialize");
317 	goto out;
318     }
319     ret = krb5_cc_store_cred(context, cache, out);
320     krb5_free_creds (context, out);
321     if(ret) {
322 	krb5_warn(context, ret, "krb5_cc_store_cred");
323 	goto out;
324     }
325 out:
326     krb5_free_creds_contents(context, &in);
327     return ret;
328 }
329 
330 int
331 main (int argc, char **argv)
332 {
333     krb5_error_code ret;
334     krb5_context context;
335     krb5_ccache  ccache;
336     krb5_principal principal;
337     krb5_creds cred;
338     int optind = 0;
339     krb5_get_init_creds_opt opt;
340     krb5_deltat start_time = 0;
341     krb5_deltat ticket_life = 0;
342     krb5_addresses no_addrs;
343     char passwd[256];
344 
345     setprogname (argv[0]);
346     memset(&cred, 0, sizeof(cred));
347 
348     ret = krb5_init_context (&context);
349     if (ret)
350 	errx(1, "krb5_init_context failed: %d", ret);
351 
352     /* XXX no way to figure out if set without explict test */
353     if(krb5_config_get_string(context, NULL, "libdefaults",
354 			      "forwardable", NULL))
355 	forwardable_flag = krb5_config_get_bool (context, NULL,
356 						 "libdefaults",
357 						 "forwardable",
358 						 NULL);
359 
360 #ifdef KRB4
361     get_v4_tgt = krb5_config_get_bool_default (context, NULL,
362 					       get_v4_tgt,
363 					       "libdefaults",
364 					       "krb4_get_tickets",
365 					       NULL);
366 #endif
367 
368     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
369 	usage(1);
370 
371     if (help_flag)
372 	usage (0);
373 
374     if(version_flag) {
375 	print_version(NULL);
376 	exit(0);
377     }
378 
379     argc -= optind;
380     argv += optind;
381 
382     if (argv[0]) {
383 	ret = krb5_parse_name (context, argv[0], &principal);
384 	if (ret)
385 	    krb5_err (context, 1, ret, "krb5_parse_name");
386     } else {
387 	ret = kinit_get_default_principal (context, &principal);
388 	if (ret)
389 	    krb5_err (context, 1, ret, "krb5_get_default_principal");
390     }
391 
392     if(fcache_version)
393 	krb5_set_fcache_version(context, fcache_version);
394 
395     if(cred_cache)
396 	ret = krb5_cc_resolve(context, cred_cache, &ccache);
397     else {
398 	if(argc > 1) {
399 	    char s[1024];
400 	    ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache);
401 	    if(ret)
402 		krb5_err(context, 1, ret, "creating cred cache");
403 	    snprintf(s, sizeof(s), "%s:%s",
404 		     krb5_cc_get_type(context, ccache),
405 		     krb5_cc_get_name(context, ccache));
406 	    setenv("KRB5CCNAME", s, 1);
407 #ifdef KRB4
408 	    snprintf(s, sizeof(s), "%s_XXXXXX", TKT_ROOT);
409 	    close(mkstemp(s));
410 	    setenv("KRBTKFILE", s, 1);
411 	    if (k_hasafs ())
412 		k_setpag();
413 #endif
414 	} else
415 	    ret = krb5_cc_default (context, &ccache);
416     }
417     if (ret)
418 	krb5_err (context, 1, ret, "resolving credentials cache");
419 
420     if (lifetime) {
421 	int tmp = parse_time (lifetime, "s");
422 	if (tmp < 0)
423 	    errx (1, "unparsable time: %s", lifetime);
424 
425 	ticket_life = tmp;
426     }
427     if(renew_flag || validate_flag) {
428 	ret = renew_validate(context, renew_flag, validate_flag,
429 			     ccache, server, ticket_life);
430 	exit(ret != 0);
431     }
432 
433     krb5_get_init_creds_opt_init (&opt);
434 
435     krb5_get_init_creds_opt_set_default_flags(context, "kinit",
436 					      /* XXX */principal->realm, &opt);
437 
438     if(forwardable_flag != -1)
439 	krb5_get_init_creds_opt_set_forwardable (&opt, forwardable_flag);
440     if(proxiable_flag != -1)
441 	krb5_get_init_creds_opt_set_proxiable (&opt, proxiable_flag);
442     if(anonymous_flag != -1)
443 	krb5_get_init_creds_opt_set_anonymous (&opt, anonymous_flag);
444 
445     if (!addrs_flag) {
446 	no_addrs.len = 0;
447 	no_addrs.val = NULL;
448 
449 	krb5_get_init_creds_opt_set_address_list (&opt, &no_addrs);
450     }
451 
452     if(renew_life) {
453 	int tmp = parse_time (renew_life, "s");
454 	if (tmp < 0)
455 	    errx (1, "unparsable time: %s", renew_life);
456 
457 	krb5_get_init_creds_opt_set_renew_life (&opt, tmp);
458     } else if (renewable_flag == 1)
459 	krb5_get_init_creds_opt_set_renew_life (&opt, 1 << 30);
460 
461     if(ticket_life != 0)
462 	krb5_get_init_creds_opt_set_tkt_life (&opt, ticket_life);
463 
464     if(start_str) {
465 	int tmp = parse_time (start_str, "s");
466 	if (tmp < 0)
467 	    errx (1, "unparsable time: %s", start_str);
468 
469 	start_time = tmp;
470     }
471 
472     if(etype_str.num_strings) {
473 	krb5_enctype *enctype = NULL;
474 	int i;
475 	enctype = malloc(etype_str.num_strings * sizeof(*enctype));
476 	if(enctype == NULL)
477 	    errx(1, "out of memory");
478 	for(i = 0; i < etype_str.num_strings; i++) {
479 	    ret = krb5_string_to_enctype(context,
480 					 etype_str.strings[i],
481 					 &enctype[i]);
482 	    if(ret)
483 		errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
484 	}
485 	krb5_get_init_creds_opt_set_etype_list(&opt, enctype,
486 					       etype_str.num_strings);
487     }
488 
489 #ifdef KRB4
490     get_v4_tgt = krb5_config_get_bool_default (context,
491 					       NULL,
492 					       get_v4_tgt,
493 					       "realms",
494 					       krb5_princ_realm(context,
495 								principal),
496 					       "krb4_get_tickets",
497 					       NULL);
498 #endif
499 
500     if(use_keytab || keytab_str) {
501 	krb5_keytab kt;
502 	if(keytab_str)
503 	    ret = krb5_kt_resolve(context, keytab_str, &kt);
504 	else
505 	    ret = krb5_kt_default(context, &kt);
506 	if (ret)
507 	    krb5_err (context, 1, ret, "resolving keytab");
508 	ret = krb5_get_init_creds_keytab (context,
509 					  &cred,
510 					  principal,
511 					  kt,
512 					  start_time,
513 					  server,
514 					  &opt);
515 	krb5_kt_close(context, kt);
516     } else {
517 	char *p, *prompt;
518 
519 	krb5_unparse_name (context, principal, &p);
520 	asprintf (&prompt, "%s's Password: ", p);
521 	free (p);
522 
523 	if (des_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
524 	    memset(passwd, 0, sizeof(passwd));
525 	    exit(1);
526 	}
527 
528 	free (prompt);
529 
530 	ret = krb5_get_init_creds_password (context,
531 					    &cred,
532 					    principal,
533 					    passwd,
534 					    krb5_prompter_posix,
535 					    NULL,
536 					    start_time,
537 					    server,
538 					    &opt);
539     }
540 #ifdef KRB4
541     if (ret == KRB5KRB_AP_ERR_V4_REPLY || ret == KRB5_KDC_UNREACH) {
542 	int exit_val;
543 
544 	exit_val = do_v4_fallback (context, principal, ticket_life,
545 				   use_keytab, keytab_str,
546 				   passwd, sizeof(passwd));
547 	memset(passwd, 0, sizeof(passwd));
548 	if (exit_val == 0 || ret == KRB5KRB_AP_ERR_V4_REPLY) {
549 	    krb5_free_context (context);
550 	    return exit_val;
551 	}
552     }
553 #endif
554     memset(passwd, 0, sizeof(passwd));
555 
556     switch(ret){
557     case 0:
558 	break;
559     case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
560 	memset(passwd, 0, sizeof(passwd));
561 	exit(1);
562     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
563     case KRB5KRB_AP_ERR_MODIFIED:
564 	memset(passwd, 0, sizeof(passwd));
565 	krb5_errx(context, 1, "Password incorrect");
566 	break;
567     default:
568 	krb5_err(context, 1, ret, "krb5_get_init_creds");
569     }
570 
571     ret = krb5_cc_initialize (context, ccache, cred.client);
572     if (ret)
573 	krb5_err (context, 1, ret, "krb5_cc_initialize");
574 
575     ret = krb5_cc_store_cred (context, ccache, &cred);
576     if (ret)
577 	krb5_err (context, 1, ret, "krb5_cc_store_cred");
578 
579 #ifdef KRB4
580     if(get_v4_tgt) {
581 	CREDENTIALS c;
582 	ret = krb524_convert_creds_kdc(context, ccache, &cred, &c);
583 	if(ret)
584 	    krb5_warn(context, ret, "converting creds");
585 	else
586 	    tf_setup(&c, c.pname, c.pinst);
587 	memset(&c, 0, sizeof(c));
588     }
589     if(do_afslog && k_hasafs())
590 	krb5_afslog(context, ccache, NULL, NULL);
591     krb5_free_creds_contents (context, &cred);
592 #endif
593     if(argc > 1) {
594 	simple_execvp(argv[1], argv+1);
595 	krb5_cc_destroy(context, ccache);
596 #ifdef KRB4
597 	dest_tkt();
598 	if(k_hasafs())
599 	    k_unlog();
600 #endif
601     } else
602 	krb5_cc_close (context, ccache);
603     krb5_free_context (context);
604     return 0;
605 }
606