xref: /titanic_52/usr/src/cmd/krb5/kinit/kinit.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * clients/kinit/kinit.c
10  *
11  * Copyright 1990 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  *
34  * Initialize a credentials cache.
35  */
36 #include <k5-int.h>
37 #include <profile/prof_int.h>
38 #include <com_err.h>
39 #include <libintl.h>
40 
41 #include <krb5.h>
42 
43 #ifdef KRB5_KRB4_COMPAT
44 #include <kerberosIV/krb.h>
45 #define HAVE_KRB524
46 #else
47 #undef HAVE_KRB524
48 #endif /* KRB5_KRB4_COMPAT */
49 
50 #include <string.h>
51 #include <stdio.h>
52 #include <time.h>
53 #include <netdb.h>
54 #include <locale.h>
55 
56 #ifdef GETOPT_LONG
57 #include <getopt.h>
58 #else /* GETOPT_LONG */
59 #ifdef HAVE_UNISTD_H
60 #include <unistd.h>
61 #else /* HAVE_UNISTD_H */
62 extern int optind;
63 extern char *optarg;
64 extern int getopt();
65 #endif /* HAVE_UNISTD_H */
66 #endif /* GETOPT_LONG */
67 
68 #ifndef _WIN32
69 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
70 #else /* _WIN32 */
71 #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
72 #endif /* _WIN32 */
73 
74 #ifdef HAVE_PWD_H
75 #include <pwd.h>
76 char * get_name_from_os()
77 {
78     struct passwd *pw;
79     if (pw = getpwuid((int) getuid()))
80 	return pw->pw_name;
81     return 0;
82 }
83 #else /* HAVE_PWD_H */
84 #ifdef _WIN32
85 char * get_name_from_os()
86 {
87     static char name[1024];
88     DWORD name_size = sizeof(name);
89     if (GetUserName(name, &name_size)) {
90 	name[sizeof(name)-1] = 0; /* Just to be extra safe */
91 	return name;
92     } else {
93 	return 0;
94     }
95 }
96 #else /* _WIN32 */
97 char * get_name_from_os()
98 {
99     return 0;
100 }
101 #endif /* _WIN32 */
102 #endif /* HAVE_PWD_H */
103 
104 static char *progname;
105 
106 static char* progname_v5 = 0;
107 #ifdef KRB5_KRB4_COMPAT
108 static char* progname_v4 = 0;
109 static char* progname_v524 = 0;
110 #endif /* KRB5_KRB4_COMPAT */
111 #include <locale.h>
112 
113 static int got_k5 = 0;
114 static int got_k4 = 0;
115 
116 static int default_k5 = 1;
117 #if defined(KRB5_KRB4_COMPAT) && defined(KINIT_DEFAULT_BOTH)
118 static int default_k4 = 1;
119 #else /* KRB5_KRB4_COMPAT && KINIT_DEFAULT_BOTH */
120 static int default_k4 = 0;
121 #endif /* KRB5_KRB4_COMPAT && KINIT_DEFAULT_BOTH */
122 
123 static int authed_k5 = 0;
124 static int authed_k4 = 0;
125 
126 #define KRB4_BACKUP_DEFAULT_LIFE_SECS 10*60*60 /* 10 hours */
127 #define	ROOT_UNAME	"root"
128 
129 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
130 
131 struct k_opts
132 {
133     /* in seconds */
134     krb5_deltat starttime;
135     krb5_deltat lifetime;
136     krb5_deltat rlife;
137 
138     int forwardable;
139     int proxiable;
140     int addresses;
141 
142     int not_forwardable;
143     int not_proxiable;
144     int no_addresses;
145 
146     int verbose;
147 
148     char* principal_name;
149     char* service_name;
150     char* keytab_name;
151     char* k5_cache_name;
152     char* k4_cache_name;
153 
154     action_type action;
155 };
156 
157 int	forwardable_flag = 0;
158 int	renewable_flag = 0;
159 int	proxiable_flag = 0;
160 int	no_address_flag = 0;
161 profile_options_boolean	config_option[] = {
162 	{ "forwardable",	&forwardable_flag,	0 },
163 	{ "renewable",		&renewable_flag,	0 },
164 	{ "proxiable",		&proxiable_flag,	0 },
165 	{ "no_addresses",	&no_address_flag,	0 },
166 	{ NULL,			NULL,			0 }
167 };
168 
169 char	*renew_timeval=NULL;
170 char	*life_timeval=NULL;
171 int	lifetime_specified;
172 int	renewtime_specified;
173 profile_option_strings	config_times[] = {
174 	{ "max_life",		&life_timeval,	0 },
175 	{ "max_renewable_life",	&renew_timeval,	0 },
176 	{ NULL,			NULL,		0 }
177 };
178 
179 struct k5_data
180 {
181     krb5_context ctx;
182     krb5_ccache cc;
183     krb5_principal me;
184     char* name;
185 };
186 
187 struct k4_data
188 {
189     krb5_deltat lifetime;
190 #ifdef KRB5_KRB4_COMPAT
191     char aname[ANAME_SZ + 1];
192     char inst[INST_SZ + 1];
193     char realm[REALM_SZ + 1];
194     char name[ANAME_SZ + 1 + INST_SZ + 1 + REALM_SZ + 1];
195 #endif /*KRB5_KRB4_COMPAT */
196 };
197 
198 char	*realmdef[] = { "realms", NULL, "kinit", NULL };
199 char	*appdef[] = { "appdefaults", "kinit", NULL };
200 
201 #define	krb_realm		(*(realmdef + 1))
202 
203 #define	lifetime_specified	config_times[0].found
204 #define	renewtime_specified	config_times[1].found
205 
206 /*
207  * Try no preauthentication first; then try the encrypted timestamp
208  */
209 krb5_preauthtype * preauth = NULL;
210 krb5_preauthtype preauth_list[2] = { 0, -1 };
211 
212 static void _kwarnd_add_warning(char *, time_t);
213 static void _kwarnd_del_warning(char *);
214 
215 #ifdef GETOPT_LONG
216 /* if struct[2] == NULL, then long_getopt acts as if the short flag
217    struct[3] was specified.  If struct[2] != NULL, then struct[3] is
218    stored in *(struct[2]), the array index which was specified is
219    stored in *index, and long_getopt() returns 0. */
220 
221 struct option long_options[] = {
222     { "noforwardable", 0, NULL, 'F' },
223     { "noproxiable", 0, NULL, 'P' },
224     { "addresses", 0, NULL, 'a'},
225     { "forwardable", 0, NULL, 'f' },
226     { "proxiable", 0, NULL, 'p' },
227     { "noaddresses", 0, NULL, 'A' },
228     { NULL, 0, NULL, 0 }
229 };
230 
231 #define GETOPT(argc, argv, str) getopt_long(argc, argv, str, long_options, 0)
232 #else /* GETOPT_LONG */
233 #define GETOPT(argc, argv, str) getopt(argc, argv, str)
234 #endif /* GETOPT_LONG */
235 
236 /* Save the program name for the error messages */
237 static char *progname;
238 
239 void
240 usage(void)
241 {
242 #define USAGE_BREAK "\n\t"
243 #ifdef GETOPT_LONG
244 #define USAGE_LONG_FORWARDABLE " | --forwardable | --noforwardable"
245 #define USAGE_LONG_PROXIABLE   " | --proxiable | --noproxiable"
246 #define USAGE_LONG_ADDRESSES   " | --addresses | --noaddresses"
247 #define USAGE_BREAK_LONG       USAGE_BREAK
248 #else /* GETOPT_LONG */
249 #define USAGE_LONG_FORWARDABLE ""
250 #define USAGE_LONG_PROXIABLE   ""
251 #define USAGE_LONG_ADDRESSES   ""
252 #define USAGE_BREAK_LONG       ""
253 #endif /* GETOPT_LONG */
254 
255     fprintf(stderr, "%s : %s  [-V] "
256 	    "[-l lifetime] [-s start_time] "
257 	    USAGE_BREAK
258 	    "[-r renewable_life] "
259 	    "[-f | -F" USAGE_LONG_FORWARDABLE "] "
260 	    USAGE_BREAK_LONG
261 	    "[-p | -P" USAGE_LONG_PROXIABLE "] "
262 	    USAGE_BREAK_LONG
263 	    "[-A" USAGE_LONG_ADDRESSES "] "
264 	    USAGE_BREAK
265 	    "[-v] [-R] "
266 	    "[-k [-t keytab_file]] "
267 	    USAGE_BREAK
268 	    "[-c cachename] "
269 	    "[-S service_name] [principal]"
270 	    "\n\n",
271 	    gettext("Usage"), progname);
272 
273 #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available"))
274 
275 #define OPTTYPE_KRB5   "5"
276 #define OPTTYPE_KRB4   "4"
277 #define OPTTYPE_EITHER "Either 4 or 5"
278 #ifdef HAVE_KRB524
279 #define OPTTYPE_BOTH "5, or both 5 and 4"
280 #else
281 #define OPTTYPE_BOTH "5"
282 #endif
283 
284 #ifdef KRB5_KRB4_COMPAT
285 #define USAGE_OPT_FMT "%s%-50s%s\n"
286 #else
287 #define USAGE_OPT_FMT "%s%s\n"
288 #endif
289 
290 #define ULINE(indent, col1, col2) \
291 fprintf(stderr, USAGE_OPT_FMT, indent, col1, col2)
292 
293     ULINE("    ", "options:", "valid with Kerberos:");
294     fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5));
295     fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4));
296     fprintf(stderr, "\t   (Default behavior is to try %s%s%s%s)\n",
297 	    default_k5?"Kerberos 5":"",
298 	    (default_k5 && default_k4)?gettext(" and "):"",
299 	    default_k4?"Kerberos 4":"",
300 	    (!default_k5 && !default_k4)?gettext("neither"):"");
301     ULINE("\t", gettext("-V verbose"),                   OPTTYPE_EITHER);
302     ULINE("\t", gettext("-l lifetime"),                  OPTTYPE_EITHER);
303     ULINE("\t", gettext("-s start time"),                OPTTYPE_KRB5);
304     ULINE("\t", gettext("-r renewable lifetime"),        OPTTYPE_KRB5);
305     ULINE("\t", gettext("-f forwardable"),               OPTTYPE_KRB5);
306     ULINE("\t", gettext("-F not forwardable"),           OPTTYPE_KRB5);
307     ULINE("\t", gettext("-p proxiable"),                 OPTTYPE_KRB5);
308     ULINE("\t", gettext("-P not proxiable"),             OPTTYPE_KRB5);
309     ULINE("\t", gettext("-A do not include addresses"),  OPTTYPE_KRB5);
310     ULINE("\t", gettext("-v validate"),                  OPTTYPE_KRB5);
311     ULINE("\t", gettext("-R renew"),                     OPTTYPE_BOTH);
312     ULINE("\t", gettext("-k use keytab"),                OPTTYPE_BOTH);
313     ULINE("\t", gettext("-t filename of keytab to use"), OPTTYPE_BOTH);
314     ULINE("\t", gettext("-c Kerberos 5 cache name"),     OPTTYPE_KRB5);
315     /* This options is not yet available: */
316     /* ULINE("\t", "-C Kerberos 4 cache name",     OPTTYPE_KRB4); */
317     ULINE("\t", gettext("-S service"),                   OPTTYPE_BOTH);
318     exit(2);
319 }
320 
321 char *
322 parse_options(argc, argv, opts)
323     int argc;
324     char **argv;
325     struct k_opts* opts;
326 {
327     krb5_error_code code;
328     int errflg = 0;
329     int use_k4 = 0;
330     int use_k5 = 0;
331     int i;
332 
333     while ((i = GETOPT(argc, argv, "r:fpFP54AVl:s:c:kt:RS:v"))
334 	   != -1) {
335 	switch (i) {
336 	case 'V':
337 	    opts->verbose = 1;
338 	    break;
339 	case 'l':
340 	    /* Lifetime */
341 	    code = krb5_string_to_deltat(optarg, &opts->lifetime);
342 	    if (code != 0 || opts->lifetime == 0) {
343 		fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg);
344 		errflg++;
345 	    }
346 	    break;
347 	case 'r':
348 	    /* Renewable Time */
349 	    code = krb5_string_to_deltat(optarg, &opts->rlife);
350 	    if (code != 0 || opts->rlife == 0) {
351 		fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg);
352 		errflg++;
353 	    }
354 	    break;
355 	case 'f':
356 	    opts->forwardable = 1;
357 	    break;
358 	case 'F':
359 	    opts->not_forwardable = 1;
360 	    break;
361 	case 'p':
362 	    opts->proxiable = 1;
363 	    break;
364 	case 'P':
365 	    opts->not_proxiable = 1;
366 	    break;
367 	case 'a':
368 	    /* Note: This is supported only with GETOPT_LONG */
369 	    opts->addresses = 1;
370 	    break;
371 	case 'A':
372 	    opts->no_addresses = 1;
373 	    break;
374        	case 's':
375 	    code = krb5_string_to_deltat(optarg, &opts->starttime);
376 	    if (code != 0 || opts->starttime == 0) {
377 		krb5_timestamp abs_starttime;
378 
379 		code = krb5_string_to_timestamp(optarg, &abs_starttime);
380 		if (code != 0 || abs_starttime == 0) {
381 		    fprintf(stderr, gettext("Bad start time value %s\n"), optarg);
382 		    errflg++;
383 		} else {
384 		    opts->starttime = abs_starttime - time(0);
385 		}
386 	    }
387 	    break;
388 	case 'S':
389 	    opts->service_name = optarg;
390 	    break;
391 	case 'k':
392 	    opts->action = INIT_KT;
393 	    break;
394 	case 't':
395 	    if (opts->keytab_name)
396 	    {
397 		fprintf(stderr, gettext("Only one -t option allowed.\n"));
398 		errflg++;
399 	    } else {
400 		opts->keytab_name = optarg;
401 	    }
402 	    break;
403 	case 'R':
404 	    opts->action = RENEW;
405 	    break;
406 	case 'v':
407 	    opts->action = VALIDATE;
408 	    break;
409        	case 'c':
410 	    if (opts->k5_cache_name)
411 	    {
412 		fprintf(stderr, gettext("Only one -c option allowed\n"));
413 		errflg++;
414 	    } else {
415 		opts->k5_cache_name = optarg;
416 	    }
417 	    break;
418 #if 0
419 	    /*
420 	      A little more work is needed before we can enable this
421 	      option.
422 	    */
423 	case 'C':
424 	    if (opts->k4_cache_name)
425 	    {
426 		fprintf(stderr, "Only one -C option allowed\n");
427 		errflg++;
428 	    } else {
429 		opts->k4_cache_name = optarg;
430 	    }
431 	    break;
432 #endif
433 	case '4':
434 	    if (!got_k4)
435 	    {
436 #ifdef KRB5_KRB4_COMPAT
437 		fprintf(stderr, "Kerberos 4 support could not be loaded\n");
438 #else
439 		fprintf(stderr, gettext("This was not built with Kerberos 4 support\n"));
440 #endif
441 		exit(3);
442 	    }
443 	    use_k4 = 1;
444 	    break;
445 	case '5':
446 	    if (!got_k5)
447 	    {
448 		fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n"));
449 		exit(3);
450 	    }
451 	    use_k5 = 1;
452 	    break;
453 	default:
454 	    errflg++;
455 	    break;
456 	}
457     }
458 
459     if (opts->forwardable && opts->not_forwardable)
460     {
461 	fprintf(stderr, gettext("Only one of -f and -F allowed\n"));
462 	errflg++;
463     }
464     if (opts->proxiable && opts->not_proxiable)
465     {
466 	fprintf(stderr, gettext("Only one of -p and -P allowed\n"));
467 	errflg++;
468     }
469     if (opts->addresses && opts->no_addresses)
470     {
471 	fprintf(stderr, gettext("Only one of -a and -A allowed\n"));
472 	errflg++;
473     }
474 
475     if (argc - optind > 1) {
476 	fprintf(stderr, gettext("Extra arguments (starting with \"%s\").\n"),
477 		argv[optind+1]);
478 	errflg++;
479     }
480 
481     /* At this point, if errorless, we know we only have one option
482        selection */
483     if (!use_k5 && !use_k4) {
484 	use_k5 = default_k5;
485 	use_k4 = default_k4;
486     }
487 
488     /* Now, we encode the OPTTYPE stuff here... */
489     if (!use_k5 &&
490 	(opts->starttime || opts->rlife || opts->forwardable ||
491 	 opts->proxiable || opts->addresses || opts->not_forwardable ||
492 	 opts->not_proxiable || opts->no_addresses ||
493 	 (opts->action == VALIDATE) || opts->k5_cache_name))
494     {
495 	fprintf(stderr, gettext("Specified option that requires Kerberos 5\n"));
496 	errflg++;
497     }
498     if (!use_k4 &&
499 	opts->k4_cache_name)
500     {
501 	fprintf(stderr, gettext("Specified option that require Kerberos 4\n"));
502 	errflg++;
503     }
504     if (
505 #ifdef HAVE_KRB524
506 	!use_k5
507 #else
508 	use_k4
509 #endif
510 	&& (opts->service_name || opts->keytab_name ||
511 	    (opts->action == INIT_KT) || (opts->action == RENEW))
512 	)
513     {
514 	fprintf(stderr, gettext("Specified option that requires Kerberos 5\n"));
515 	errflg++;
516     }
517 
518     if (errflg) {
519 	usage();
520     }
521 
522     got_k5 = got_k5 && use_k5;
523     got_k4 = got_k4 && use_k4;
524 
525     opts->principal_name = (optind == argc-1) ? argv[optind] : 0;
526     return opts->principal_name;
527 }
528 
529 int
530 k5_begin(opts, k5, k4)
531     struct k_opts* opts;
532 struct k5_data* k5;
533 struct k4_data* k4;
534 {
535     char* progname = progname_v5;
536     krb5_error_code code = 0;
537     char* cp;
538 
539     if (!got_k5)
540 	return 0;
541 
542     if (code = krb5_init_context(&k5->ctx)) {
543 	com_err(progname, code, gettext("while initializing Kerberos 5 library"));
544 	return 0;
545     }
546     if (opts->k5_cache_name)
547     {
548 	code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc);
549 	if (code != 0) {
550 	    com_err(progname, code, gettext("resolving ccache %s"),
551 		    opts->k5_cache_name);
552 	    return 0;
553 	}
554     }
555     else
556     {
557 	if ((code = krb5_cc_default(k5->ctx, &k5->cc))) {
558 	    com_err(progname, code, gettext("while getting default ccache"));
559 	    return 0;
560 	}
561     }
562 
563     if (opts->principal_name)
564     {
565 	/* Use specified name */
566 	if ((code = krb5_parse_name(k5->ctx, opts->principal_name,
567 				    &k5->me))) {
568 	    com_err(progname, code, gettext("when parsing name %s"),
569 		    opts->principal_name);
570 	    return 0;
571 	}
572     }
573     else
574     {
575 	/* No principal name specified */
576 	if (opts->action == INIT_KT) {
577 	    /* Use the default host/service name */
578 	    if (code = krb5_sname_to_principal(k5->ctx, NULL, NULL,
579 					       KRB5_NT_SRV_HST, &k5->me)) {
580 		com_err(progname, code, gettext(
581 			"when creating default server principal name"));
582 		return 0;
583 	    }
584 	} else {
585 	/* Get default principal from cache if one exists */
586             if (code = krb5_cc_get_principal(k5->ctx, k5->cc, &k5->me)) {
587                 char *name = get_name_from_os();
588                 if (!name)
589                 {
590                     fprintf(stderr, gettext("Unable to identify user\n"));
591                     return 0;
592                 }
593                 /* use strcmp to ensure only "root" is matched */
594                 if (strcmp(name, ROOT_UNAME) == 0)
595                 {
596                 	if (code = krb5_sname_to_principal(k5->ctx, NULL, ROOT_UNAME,
597 				    KRB5_NT_SRV_HST, &k5->me)) {
598 			    com_err(progname, code, gettext(
599 				"when creating default server principal name"));
600                                 return 0;
601                         }
602                 } else if (code = krb5_parse_name(k5->ctx, name, &k5->me)) {
603 			com_err(progname, code, gettext("when parsing name %s"),
604 				name);
605 			return 0;
606 		}
607             }
608         }
609     }
610     if (code = krb5_unparse_name(k5->ctx, k5->me, &k5->name)) {
611 	com_err(progname, code, gettext("when unparsing name"));
612 	return 0;
613     }
614     opts->principal_name = k5->name;
615 
616 
617 #ifdef KRB5_KRB4_COMPAT
618     if (got_k4)
619     {
620 	/* Translate to a Kerberos 4 principal */
621 	code = krb5_524_conv_principal(k5->ctx, k5->me,
622 				       k4->aname, k4->inst, k4->realm);
623 	if (code) {
624 	    k4->aname[0] = 0;
625 	    k4->inst[0] = 0;
626 	    k4->realm[0] = 0;
627 	}
628     }
629 #endif
630     return 1;
631 }
632 
633 void
634 k5_end(k5)
635     struct k5_data* k5;
636 {
637     if (k5->name)
638 	krb5_free_unparsed_name(k5->ctx, k5->name);
639     if (k5->me)
640 	krb5_free_principal(k5->ctx, k5->me);
641     if (k5->cc)
642 	krb5_cc_close(k5->ctx, k5->cc);
643     if (k5->ctx)
644 	krb5_free_context(k5->ctx);
645     memset(k5, 0, sizeof(*k5));
646 }
647 
648 int
649 k4_begin(opts, k4)
650     struct k_opts* opts;
651     struct k4_data* k4;
652 {
653 #ifdef KRB5_KRB4_COMPAT
654     char* progname = progname_v4;
655     int k_errno = 0;
656 #endif
657 
658     if (!got_k4)
659 	return 0;
660 
661 #ifdef KRB5_KRB4_COMPAT
662     if (k4->aname[0])
663 	goto skip;
664 
665     if (opts->principal_name)
666     {
667 	/* Use specified name */
668 	if (k_errno = kname_parse(k4->aname, k4->inst, k4->realm,
669 				  opts->principal_name))
670 	{
671 	    fprintf(stderr, "%s: %s\n", progname,
672 		    krb_get_err_text(k_errno));
673 	    return 0;
674 	}
675     } else {
676 	/* No principal name specified */
677 	if (opts->action == INIT_KT) {
678 	    /* Use the default host/service name */
679 	    /* XXX - need to add this functionality */
680 	    fprintf(stderr, "%s: Kerberos 4 srvtab support is not "
681 		    "implemented\n", progname);
682 	    return 0;
683 	} else {
684 	    /* Get default principal from cache if one exists */
685 	    if (k_errno = krb_get_tf_fullname(tkt_string(), k4->aname,
686 					      k4->inst, k4->realm))
687 	    {
688 		char *name = get_name_from_os();
689 		if (!name)
690 		{
691 		    fprintf(stderr, "Unable to identify user\n");
692 		    return 0;
693 		}
694 		if (k_errno = kname_parse(k4->aname, k4->inst, k4->realm,
695 					  name))
696 		{
697 		    fprintf(stderr, "%s: %s\n", progname,
698 			    krb_get_err_text(k_errno));
699 		    return 0;
700 		}
701 	    }
702 	}
703     }
704 
705     if (!k4->realm[0])
706 	krb_get_lrealm(k4->realm, 1);
707 
708     if (k4->inst[0])
709 	sprintf(k4->name, "%s.%s@%s", k4->aname, k4->inst, k4->realm);
710     else
711 	sprintf(k4->name, "%s@%s", k4->aname, k4->realm);
712     opts->principal_name = k4->name;
713 
714  skip:
715     if (k4->aname[0] && !k_isname(k4->aname))
716     {
717 	fprintf(stderr, "%s: bad Kerberos 4 name format\n", progname);
718 	return 0;
719     }
720 
721     if (k4->inst[0] && !k_isinst(k4->inst))
722     {
723 	fprintf(stderr, "%s: bad Kerberos 4 instance format\n", progname);
724 	return 0;
725     }
726 
727     if (k4->realm[0] && !k_isrealm(k4->realm))
728     {
729 	fprintf(stderr, "%s: bad Kerberos 4 realm format\n", progname);
730 	return 0;
731     }
732 #endif /* KRB5_KRB4_COMPAT */
733     return 1;
734 }
735 
736 void
737 k4_end(k4)
738     struct k4_data* k4;
739 {
740     memset(k4, 0, sizeof(*k4));
741 }
742 
743 #ifdef KRB5_KRB4_COMPAT
744 static char stash_password[1024];
745 static int got_password = 0;
746 #endif /* KRB5_KRB4_COMPAT */
747 
748 krb5_error_code
749 KRB5_CALLCONV
750 kinit_prompter(
751     krb5_context ctx,
752     void *data,
753     const char *name,
754     const char *banner,
755     int num_prompts,
756     krb5_prompt prompts[]
757     )
758 {
759     int i;
760     krb5_prompt_type *types;
761     krb5_error_code rc =
762 	krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts);
763     if (!rc && (types = krb5_get_prompt_types(ctx)))
764 	for (i = 0; i < num_prompts; i++)
765 	    if ((types[i] == KRB5_PROMPT_TYPE_PASSWORD) ||
766 		(types[i] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN))
767 	    {
768 #ifdef KRB5_KRB4_COMPAT
769 		strncpy(stash_password, prompts[i].reply->data,
770 			sizeof(stash_password));
771 		got_password = 1;
772 #endif
773 	    }
774 
775     return rc;
776 }
777 
778 int
779 k5_kinit(opts, k5)
780     struct k_opts* opts;
781     struct k5_data* k5;
782 {
783     char* progname = progname_v5;
784     int notix = 1;
785     krb5_keytab keytab = 0;
786     krb5_creds my_creds;
787     krb5_error_code code = 0;
788     krb5_get_init_creds_opt options;
789     krb5_timestamp now;
790     krb5_deltat lifetime = 0, rlife = 0, krb5_max_duration;
791 
792     if (!got_k5)
793 	return 0;
794 
795     krb5_get_init_creds_opt_init(&options);
796     memset(&my_creds, 0, sizeof(my_creds));
797 
798     /*
799      * Solaris Kerberos: added support for max_life and max_renewable_life
800      * which should be removed in the next minor release.  See PSARC 2003/545
801      * for more info.
802      *
803      * Also, check krb5.conf for proxiable/forwardable/renewable/no_address
804      * parameter values.
805      */
806     /* If either tkt life or renew life weren't set earlier take common steps to
807      * get the krb5.conf parameter values.
808      */
809 
810     if ((code = krb5_timeofday(k5->ctx, &now))) {
811 	    com_err(progname, code, gettext("while getting time of day"));
812 	    exit(1);
813     }
814     krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60;
815 
816     if (opts->lifetime == 0 || opts->rlife == 0) {
817 
818 	krb_realm = krb5_princ_realm(k5->ctx, k5->me)->data;
819 	/* realm params take precedence */
820 	profile_get_options_string(k5->ctx->profile, realmdef, config_times);
821 	profile_get_options_string(k5->ctx->profile, appdef, config_times);
822 
823 	/* if the input opts doesn't have lifetime set and the krb5.conf
824 	 * parameter has been set, use that.
825 	 */
826 	if (opts->lifetime == 0 && life_timeval != NULL) {
827 	    code = krb5_string_to_deltat(life_timeval, &lifetime);
828 	    if (code != 0 || lifetime == 0 || lifetime > krb5_max_duration) {
829 		fprintf(stderr, gettext("Bad max_life "
830 			    "value in Kerberos config file %s\n"),
831 			life_timeval);
832 		exit(1);
833 	    }
834 	    opts->lifetime = lifetime;
835 	}
836 	if (opts->rlife == 0 && renew_timeval != NULL) {
837 	    code = krb5_string_to_deltat(renew_timeval, &rlife);
838 	    if (code != 0 || rlife == 0 || rlife > krb5_max_duration) {
839 		fprintf(stderr, gettext("Bad max_renewable_life "
840 			    "value in Kerberos config file %s\n"),
841 			renew_timeval);
842 		exit(1);
843 	    }
844 	    opts->rlife = rlife;
845 	}
846     }
847 
848     /*
849      * If lifetime is not set on the cmdline or in the krb5.conf
850      * file, default to max.
851      */
852     if (opts->lifetime == 0)
853 	    opts->lifetime = krb5_max_duration;
854 
855 
856     profile_get_options_boolean(k5->ctx->profile,
857 				realmdef, config_option);
858     profile_get_options_boolean(k5->ctx->profile,
859 				appdef, config_option);
860 
861 
862     /* cmdline opts take precedence over krb5.conf file values */
863     if (!opts->not_proxiable && proxiable_flag) {
864 	    krb5_get_init_creds_opt_set_proxiable(&options, 1);
865     }
866     if (!opts->not_forwardable && forwardable_flag) {
867 	    krb5_get_init_creds_opt_set_forwardable(&options, 1);
868     }
869     if (renewable_flag) {
870 	    /*
871 	     * If this flag is set in krb5.conf, but rlife is 0, then
872 	     * set it to the max (and let the KDC sort it out).
873 	     */
874 	    opts->rlife = opts->rlife ? opts->rlife : krb5_max_duration;
875     }
876     if (no_address_flag) {
877 	    /* cmdline opts will overwrite this below if needbe */
878 	    krb5_get_init_creds_opt_set_address_list(&options, NULL);
879     }
880 
881 
882     /*
883       From this point on, we can goto cleanup because my_creds is
884       initialized.
885     */
886 
887     if (opts->lifetime)
888 	krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime);
889     if (opts->rlife)
890 	krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife);
891     if (opts->forwardable)
892 	krb5_get_init_creds_opt_set_forwardable(&options, 1);
893     if (opts->not_forwardable)
894 	krb5_get_init_creds_opt_set_forwardable(&options, 0);
895     if (opts->proxiable)
896 	krb5_get_init_creds_opt_set_proxiable(&options, 1);
897     if (opts->not_proxiable)
898 	krb5_get_init_creds_opt_set_proxiable(&options, 0);
899     if (opts->addresses)
900     {
901 	krb5_address **addresses = NULL;
902 	code = krb5_os_localaddr(k5->ctx, &addresses);
903 	if (code != 0) {
904 	    com_err(progname, code, gettext("getting local addresses"));
905 	    goto cleanup;
906 	}
907 	krb5_get_init_creds_opt_set_address_list(&options, addresses);
908 	krb5_free_addresses(k5->ctx, addresses);
909     }
910     if (opts->no_addresses)
911 	krb5_get_init_creds_opt_set_address_list(&options, NULL);
912 
913     if ((opts->action == INIT_KT) && opts->keytab_name)
914     {
915 	code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
916 	if (code != 0) {
917 	    com_err(progname, code, gettext("resolving keytab %s"),
918 		    opts->keytab_name);
919 	    goto cleanup;
920 	}
921     }
922 
923 
924 
925     switch (opts->action) {
926     case INIT_PW:
927 	code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
928 					    0, kinit_prompter, 0,
929 					    opts->starttime,
930 					    opts->service_name,
931 					    &options);
932 	break;
933     case INIT_KT:
934 	code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
935 					  keytab,
936 					  opts->starttime,
937 					  opts->service_name,
938 					  &options);
939 	break;
940     case VALIDATE:
941 	code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc,
942 					opts->service_name);
943 	break;
944     case RENEW:
945 	code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->cc,
946 				      opts->service_name);
947 	break;
948     }
949 
950     if (code) {
951 	char *doing = 0;
952 	switch (opts->action) {
953 	case INIT_PW:
954 	case INIT_KT:
955 	    doing = gettext("getting initial credentials");
956 	    break;
957 	case VALIDATE:
958 	    doing = gettext("validating credentials");
959 	    break;
960 	case RENEW:
961 	    doing = gettext("renewing credentials");
962 	    break;
963 	}
964 
965 	/* If got code == KRB5_AP_ERR_V4_REPLY && got_k4, we should
966 	   let the user know that maybe he/she wants -4. */
967 	if (code == KRB5KRB_AP_ERR_V4_REPLY && got_k4)
968 	    com_err(progname, code, "while %s\n"
969 		    "The KDC doesn't support v5.  "
970 		    "You may want the -4 option in the future",
971 		    doing);
972 	else if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
973 	    fprintf(stderr, gettext("%s: Password incorrect while %s\n"), progname,
974 		    doing);
975 	else
976 	    com_err(progname, code, gettext("while %s"), doing);
977 	goto cleanup;
978     }
979 
980     if (!opts->lifetime) {
981 	/* We need to figure out what lifetime to use for Kerberos 4. */
982 	opts->lifetime = my_creds.times.endtime - my_creds.times.authtime;
983     }
984 
985     if (code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me)) {
986 	com_err(progname, code, gettext("when initializing cache %s"),
987 		opts->k5_cache_name?opts->k5_cache_name:"");
988 	goto cleanup;
989     }
990 
991     if (code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds)) {
992 	com_err(progname, code, gettext("while storing credentials"));
993 	goto cleanup;
994     }
995 
996     if (opts->action == RENEW) {
997         _kwarnd_del_warning(opts->principal_name);
998         _kwarnd_add_warning(opts->principal_name, my_creds.times.endtime);
999     } else if ((opts->action == INIT_KT) || (opts->action == INIT_PW)) {
1000         _kwarnd_add_warning(opts->principal_name, my_creds.times.endtime);
1001     }
1002 
1003     notix = 0;
1004 
1005  cleanup:
1006     if (my_creds.client == k5->me) {
1007 	my_creds.client = 0;
1008     }
1009     krb5_free_cred_contents(k5->ctx, &my_creds);
1010     if (keytab)
1011 	krb5_kt_close(k5->ctx, keytab);
1012     return notix?0:1;
1013 }
1014 
1015 int
1016 k4_kinit(opts, k4, ctx)
1017     struct k_opts* opts;
1018     struct k4_data* k4;
1019     krb5_context ctx;
1020 {
1021 #ifdef KRB5_KRB4_COMPAT
1022     char* progname = progname_v4;
1023     int k_errno = 0;
1024 #endif
1025 
1026     if (!got_k4)
1027 	return 0;
1028 
1029     if (opts->starttime)
1030 	return 0;
1031 
1032 #ifdef KRB5_KRB4_COMPAT
1033     if (!k4->lifetime)
1034 	k4->lifetime = opts->lifetime;
1035     if (!k4->lifetime)
1036 	k4->lifetime = KRB4_BACKUP_DEFAULT_LIFE_SECS;
1037 
1038     k4->lifetime /= (5 * 60);
1039     if (k4->lifetime < 1)
1040 	k4->lifetime = 1;
1041     if (k4->lifetime > 255)
1042 	k4->lifetime = 255;
1043 
1044     switch (opts->action)
1045     {
1046     case INIT_PW:
1047 	if (!got_password) {
1048 	    int pwsize = sizeof(stash_password);
1049 	    krb5_error_code code;
1050 	    char prompt[1024];
1051 
1052 	    sprintf(prompt, gettext("Password for %s: "), opts->principal_name);
1053 	    stash_password[0] = 0;
1054 	    /*
1055 	      Note: krb5_read_password does not actually look at the
1056 	      context, so we're ok even if we don't have a context.  If
1057 	      we cannot dynamically load krb5, we can substitute any
1058 	      decent read password function instead of the krb5 one.
1059 	    */
1060 	    code = krb5_read_password(ctx, prompt, 0, stash_password, &pwsize);
1061 	    if (code || pwsize == 0)
1062 	    {
1063 		fprintf(stderr, gettext("Error while reading password for '%s'\n"),
1064 			opts->principal_name);
1065 		memset(stash_password, 0, sizeof(stash_password));
1066 		return 0;
1067 	    }
1068 	    got_password = 1;
1069 	}
1070 	k_errno = krb_get_pw_in_tkt(k4->aname, k4->inst, k4->realm, "krbtgt",
1071 				    k4->realm, k4->lifetime, stash_password);
1072 
1073 	if (k_errno) {
1074 	    fprintf(stderr, "%s: %s\n", progname,
1075 		    krb_get_err_text(k_errno));
1076 	    if (authed_k5)
1077 		fprintf(stderr, gettext("Maybe your KDC does not support v4.  "
1078 			"Try the -5 option next time.\n"));
1079 	    return 0;
1080 	}
1081 	return 1;
1082 #ifndef HAVE_KRB524
1083     case INIT_KT:
1084 	fprintf(stderr, gettext("%s: srvtabs are not supported\n"), progname);
1085 	return 0;
1086     case RENEW:
1087 	fprintf(stderr, gettext("%s: renewal of krb4 tickets is not supported\n"),
1088 		progname);
1089 	return 0;
1090 #endif
1091     }
1092 #endif
1093     return 0;
1094 }
1095 
1096 char*
1097 getvprogname(v)
1098     char *v;
1099 {
1100     int len = strlen(progname) + 2 + strlen(v) + 2;
1101     char *ret = malloc(len);
1102     if (ret)
1103 	sprintf(ret, "%s(v%s)", progname, v);
1104     else
1105 	ret = progname;
1106     return ret;
1107 }
1108 
1109 #ifdef HAVE_KRB524
1110 /* Convert krb5 tickets to krb4. */
1111 int try_convert524(k5)
1112     struct k5_data* k5;
1113 {
1114     char * progname = progname_v524;
1115     krb5_error_code code = 0;
1116     int icode = 0;
1117     krb5_principal kpcserver = 0;
1118     krb5_creds *v5creds = 0;
1119     krb5_creds increds;
1120     CREDENTIALS v4creds;
1121 
1122     if (!got_k4 || !got_k5)
1123 	return 0;
1124 
1125     memset((char *) &increds, 0, sizeof(increds));
1126     /*
1127       From this point on, we can goto cleanup because increds is
1128       initialized.
1129     */
1130 
1131     /* or do this directly with krb524_convert_creds_kdc */
1132     krb524_init_ets(k5->ctx);
1133 
1134     if ((code = krb5_build_principal(k5->ctx,
1135 				     &kpcserver,
1136 				     krb5_princ_realm(k5->ctx, k5->me)->length,
1137 				     krb5_princ_realm(k5->ctx, k5->me)->data,
1138 				     "krbtgt",
1139 				     krb5_princ_realm(k5->ctx, k5->me)->data,
1140 				     NULL))) {
1141 	com_err(progname, code, gettext(
1142 		"while creating service principal name"));
1143 	goto cleanup;
1144     }
1145 
1146     increds.client = k5->me;
1147     increds.server = kpcserver;
1148     /* Prevent duplicate free calls.  */
1149     kpcserver = 0;
1150 
1151     increds.times.endtime = 0;
1152     increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
1153     if ((code = krb5_get_credentials(k5->ctx, 0,
1154 				     k5->cc,
1155 				     &increds,
1156 				     &v5creds))) {
1157 	com_err(progname, code,
1158 		gettext("getting V5 credentials"));
1159 	goto cleanup;
1160     }
1161     if ((icode = krb524_convert_creds_kdc(k5->ctx,
1162 					  v5creds,
1163 					  &v4creds))) {
1164 	com_err(progname, icode,
1165 		gettext("converting to V4 credentials"));
1166 	goto cleanup;
1167     }
1168     /* this is stolen from the v4 kinit */
1169     /* initialize ticket cache */
1170     if ((icode = in_tkt(v4creds.pname, v4creds.pinst)
1171 	 != KSUCCESS)) {
1172 	com_err(progname, icode, gettext(
1173 		"trying to create the V4 ticket file"));
1174 	goto cleanup;
1175     }
1176     /* stash ticket, session key, etc. for future use */
1177     if ((icode = krb_save_credentials(v4creds.service,
1178 				      v4creds.instance,
1179 				      v4creds.realm,
1180 				      v4creds.session,
1181 				      v4creds.lifetime,
1182 				      v4creds.kvno,
1183 				      &(v4creds.ticket_st),
1184 				      v4creds.issue_date))) {
1185 	com_err(progname, icode, gettext(
1186 		"trying to save the V4 ticket"));
1187 	goto cleanup;
1188     }
1189 
1190  cleanup:
1191     memset(&v4creds, 0, sizeof(v4creds));
1192     if (v5creds)
1193 	krb5_free_creds(k5->ctx, v5creds);
1194     increds.client = 0;
1195     krb5_free_cred_contents(k5->ctx, &increds);
1196     if (kpcserver)
1197 	krb5_free_principal(k5->ctx, kpcserver);
1198     return !(code || icode);
1199 }
1200 #endif /* HAVE_KRB524 */
1201 
1202 int
1203 main(argc, argv)
1204     int argc;
1205     char **argv;
1206 {
1207     struct k_opts opts;
1208     struct k5_data k5;
1209     struct k4_data k4;
1210 
1211     (void) setlocale(LC_ALL, "");
1212 
1213 #if !defined(TEXT_DOMAIN)
1214 #define	TEXT_DOMAIN	"SYS_TEST"
1215 #endif
1216 
1217     (void) textdomain(TEXT_DOMAIN);
1218 
1219     progname = GET_PROGNAME(argv[0]);
1220     progname_v5 = getvprogname("5");
1221 #ifdef KRB5_KRB4_COMPAT
1222     progname_v4 = getvprogname("4");
1223     progname_v524 = getvprogname("524");
1224 #endif
1225 
1226     /* Ensure we can be driven from a pipe */
1227     if(!isatty(fileno(stdin)))
1228 	setvbuf(stdin, 0, _IONBF, 0);
1229     if(!isatty(fileno(stdout)))
1230 	setvbuf(stdout, 0, _IONBF, 0);
1231     if(!isatty(fileno(stderr)))
1232 	setvbuf(stderr, 0, _IONBF, 0);
1233 
1234     /*
1235       This is where we would put in code to dynamically load Kerberos
1236       libraries.  Currenlty, we just get them implicitly.
1237     */
1238     got_k5 = 1;
1239 #ifdef KRB5_KRB4_COMPAT
1240     got_k4 = 1;
1241 #endif
1242 
1243     memset(&opts, 0, sizeof(opts));
1244     opts.action = INIT_PW;
1245 
1246     memset(&k5, 0, sizeof(k5));
1247     memset(&k4, 0, sizeof(k4));
1248 
1249     parse_options(argc, argv, &opts);
1250 
1251     got_k5 = k5_begin(&opts, &k5, &k4);
1252     got_k4 = k4_begin(&opts, &k4);
1253 
1254     authed_k5 = k5_kinit(&opts, &k5);
1255 #ifdef HAVE_KRB524
1256     if (authed_k5)
1257 	authed_k4 = try_convert524(&k5);
1258 #endif
1259     if (!authed_k4)
1260 	authed_k4 = k4_kinit(&opts, &k4, k5.ctx);
1261 #ifdef KRB5_KRB4_COMPAT
1262     memset(stash_password, 0, sizeof(stash_password));
1263 #endif
1264 
1265     if (authed_k5 && opts.verbose)
1266 	fprintf(stderr, gettext("Authenticated to Kerberos v5\n"));
1267     if (authed_k4 && opts.verbose)
1268 	fprintf(stderr, gettext("Authenticated to Kerberos v4\n"));
1269 
1270     k5_end(&k5);
1271     k4_end(&k4);
1272 
1273     if ((got_k5 && !authed_k5) || (got_k4 && !authed_k4))
1274 	exit(1);
1275     return 0;
1276 }
1277 
1278 static void
1279 _kwarnd_add_warning(char *me, time_t endtime)
1280 {
1281     if (kwarn_add_warning(me, endtime) != 0)
1282         fprintf(stderr, gettext(
1283             "%s:  no ktkt_warnd warning possible\n"), progname);
1284     return;
1285 }
1286 
1287 
1288 static void
1289 _kwarnd_del_warning(char *me)
1290 {
1291     if (kwarn_del_warning(me) != 0)
1292         fprintf(stderr, gettext(
1293             "%s:  unable to delete ktkt_warnd message for %s\n"),
1294             progname, me);
1295     return;
1296 }
1297