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