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