xref: /freebsd/crypto/heimdal/kuser/kinit.c (revision 5e9cd1ae3e10592ed70e7575551cba1bbab04d84)
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.69 2001/01/05 16:32:55 joda 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]");
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     flags.b.forwardable       = forwardable_flag;
294     flags.b.proxiable         = proxiable_flag;
295     flags.b.request_anonymous = anonymous_flag;
296     if(life)
297 	in.times.endtime = time(NULL) + life;
298 
299     ret = krb5_get_kdc_cred(context,
300 			    cache,
301 			    flags,
302 			    NULL,
303 			    NULL,
304 			    &in,
305 			    &out);
306     if(ret) {
307 	krb5_warn(context, ret, "krb5_get_kdc_cred");
308 	goto out;
309     }
310     ret = krb5_cc_initialize(context, cache, in.client);
311     if(ret) {
312 	krb5_free_creds (context, out);
313 	krb5_warn(context, ret, "krb5_cc_initialize");
314 	goto out;
315     }
316     ret = krb5_cc_store_cred(context, cache, out);
317     krb5_free_creds (context, out);
318     if(ret) {
319 	krb5_warn(context, ret, "krb5_cc_store_cred");
320 	goto out;
321     }
322 out:
323     krb5_free_creds_contents(context, &in);
324     return ret;
325 }
326 
327 int
328 main (int argc, char **argv)
329 {
330     krb5_error_code ret;
331     krb5_context context;
332     krb5_ccache  ccache;
333     krb5_principal principal;
334     krb5_creds cred;
335     int optind = 0;
336     krb5_get_init_creds_opt opt;
337     krb5_deltat start_time = 0;
338     krb5_deltat ticket_life = 0;
339     krb5_addresses no_addrs;
340     char passwd[256];
341 
342     set_progname (argv[0]);
343     memset(&cred, 0, sizeof(cred));
344 
345     ret = krb5_init_context (&context);
346     if (ret)
347 	errx(1, "krb5_init_context failed: %d", ret);
348 
349     /* XXX no way to figure out if set without explict test */
350     if(krb5_config_get_string(context, NULL, "libdefaults",
351 			      "forwardable", NULL))
352 	forwardable_flag = krb5_config_get_bool (context, NULL,
353 						 "libdefaults",
354 						 "forwardable",
355 						 NULL);
356 
357 #ifdef KRB4
358     get_v4_tgt = krb5_config_get_bool_default (context, NULL,
359 					       get_v4_tgt,
360 					       "libdefaults",
361 					       "krb4_get_tickets",
362 					       NULL);
363 #endif
364 
365     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
366 	usage(1);
367 
368     if (help_flag)
369 	usage (0);
370 
371     if(version_flag) {
372 	print_version(NULL);
373 	exit(0);
374     }
375 
376     argc -= optind;
377     argv += optind;
378 
379     if (argc > 1)
380 	usage (1);
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 	ret = krb5_cc_default (context, &ccache);
399     if (ret)
400 	krb5_err (context, 1, ret, "resolving credentials cache");
401 
402     if (lifetime) {
403 	int tmp = parse_time (lifetime, "s");
404 	if (tmp < 0)
405 	    errx (1, "unparsable time: %s", lifetime);
406 
407 	ticket_life = tmp;
408     }
409     if(renew_flag || validate_flag) {
410 	ret = renew_validate(context, renew_flag, validate_flag,
411 			     ccache, server, ticket_life);
412 	exit(ret != 0);
413     }
414 
415     krb5_get_init_creds_opt_init (&opt);
416 
417     krb5_get_init_creds_opt_set_default_flags(context, "kinit",
418 					      /* XXX */principal->realm, &opt);
419 
420     if(forwardable_flag != -1)
421 	krb5_get_init_creds_opt_set_forwardable (&opt, forwardable_flag);
422     if(proxiable_flag != -1)
423 	krb5_get_init_creds_opt_set_proxiable (&opt, proxiable_flag);
424     if(anonymous_flag != -1)
425 	krb5_get_init_creds_opt_set_anonymous (&opt, anonymous_flag);
426 
427     if (!addrs_flag) {
428 	no_addrs.len = 0;
429 	no_addrs.val = NULL;
430 
431 	krb5_get_init_creds_opt_set_address_list (&opt, &no_addrs);
432     }
433 
434     if(renew_life) {
435 	int tmp = parse_time (renew_life, "s");
436 	if (tmp < 0)
437 	    errx (1, "unparsable time: %s", renew_life);
438 
439 	krb5_get_init_creds_opt_set_renew_life (&opt, tmp);
440     } else if (renewable_flag)
441 	krb5_get_init_creds_opt_set_renew_life (&opt, 1 << 30);
442 
443     if(ticket_life != 0)
444 	krb5_get_init_creds_opt_set_tkt_life (&opt, ticket_life);
445 
446     if(start_str) {
447 	int tmp = parse_time (start_str, "s");
448 	if (tmp < 0)
449 	    errx (1, "unparsable time: %s", start_str);
450 
451 	start_time = tmp;
452     }
453 
454     if(etype_str.num_strings) {
455 	krb5_enctype *enctype = NULL;
456 	int i;
457 	enctype = malloc(etype_str.num_strings * sizeof(*enctype));
458 	if(enctype == NULL)
459 	    errx(1, "out of memory");
460 	for(i = 0; i < etype_str.num_strings; i++) {
461 	    ret = krb5_string_to_enctype(context,
462 					 etype_str.strings[i],
463 					 &enctype[i]);
464 	    if(ret)
465 		errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
466 	}
467 	krb5_get_init_creds_opt_set_etype_list(&opt, enctype,
468 					       etype_str.num_strings);
469     }
470 
471 #ifdef KRB4
472     get_v4_tgt = krb5_config_get_bool_default (context,
473 					       NULL,
474 					       get_v4_tgt,
475 					       "realms",
476 					       krb5_princ_realm(context,
477 								principal),
478 					       "krb4_get_tickets",
479 					       NULL);
480 #endif
481 
482     if(use_keytab || keytab_str) {
483 	krb5_keytab kt;
484 	if(keytab_str)
485 	    ret = krb5_kt_resolve(context, keytab_str, &kt);
486 	else
487 	    ret = krb5_kt_default(context, &kt);
488 	if (ret)
489 	    krb5_err (context, 1, ret, "resolving keytab");
490 	ret = krb5_get_init_creds_keytab (context,
491 					  &cred,
492 					  principal,
493 					  kt,
494 					  start_time,
495 					  server,
496 					  &opt);
497 	krb5_kt_close(context, kt);
498     } else {
499 	char *p, *prompt;
500 
501 	krb5_unparse_name (context, principal, &p);
502 	asprintf (&prompt, "%s's Password: ", p);
503 	free (p);
504 
505 	if (des_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
506 	    memset(passwd, 0, sizeof(passwd));
507 	    exit(1);
508 	}
509 
510 	free (prompt);
511 
512 	ret = krb5_get_init_creds_password (context,
513 					    &cred,
514 					    principal,
515 					    passwd,
516 					    krb5_prompter_posix,
517 					    NULL,
518 					    start_time,
519 					    server,
520 					    &opt);
521     }
522 #ifdef KRB4
523     if (ret == KRB5KRB_AP_ERR_V4_REPLY || ret == KRB5_KDC_UNREACH) {
524 	int exit_val;
525 
526 	exit_val = do_v4_fallback (context, principal, ticket_life,
527 				   use_keytab, keytab_str,
528 				   passwd, sizeof(passwd));
529 	memset(passwd, 0, sizeof(passwd));
530 	if (exit_val == 0 || ret == KRB5KRB_AP_ERR_V4_REPLY) {
531 	    krb5_free_context (context);
532 	    return exit_val;
533 	}
534     }
535 #endif
536     memset(passwd, 0, sizeof(passwd));
537 
538     switch(ret){
539     case 0:
540 	break;
541     case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
542 	memset(passwd, 0, sizeof(passwd));
543 	exit(1);
544     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
545     case KRB5KRB_AP_ERR_MODIFIED:
546 	memset(passwd, 0, sizeof(passwd));
547 	krb5_errx(context, 1, "Password incorrect");
548 	break;
549     default:
550 	krb5_err(context, 1, ret, "krb5_get_init_creds");
551     }
552 
553     ret = krb5_cc_initialize (context, ccache, cred.client);
554     if (ret)
555 	krb5_err (context, 1, ret, "krb5_cc_initialize");
556 
557     ret = krb5_cc_store_cred (context, ccache, &cred);
558     if (ret)
559 	krb5_err (context, 1, ret, "krb5_cc_store_cred");
560 
561 #ifdef KRB4
562     if(get_v4_tgt) {
563 	CREDENTIALS c;
564 	ret = krb524_convert_creds_kdc(context, ccache, &cred, &c);
565 	if(ret)
566 	    krb5_warn(context, ret, "converting creds");
567 	else
568 	    tf_setup(&c, c.pname, c.pinst);
569 	memset(&c, 0, sizeof(c));
570     }
571     if(do_afslog && k_hasafs())
572 	krb5_afslog(context, ccache, NULL, NULL);
573 #endif
574     krb5_free_creds_contents (context, &cred);
575     krb5_cc_close (context, ccache);
576     krb5_free_context (context);
577     return 0;
578 }
579