xref: /illumos-gate/usr/src/cmd/krb5/kwarn/kwarnd_proc.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  *  RPC server procedures for the usermode daemon kwarnd.
30  */
31 
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <sys/param.h>
39 #include <sys/syslog.h>
40 #include "kwarnd.h"
41 #include <rpc/rpc.h>
42 #include <stdlib.h>
43 #include <syslog.h>
44 #include <poll.h>
45 #include <utmpx.h>
46 #include <pwd.h>
47 #include <strings.h>
48 #include <ctype.h>
49 
50 #include <k5-int.h>
51 #include <profile/prof_int.h>
52 #include <com_err.h>
53 #include <libintl.h>
54 #include <krb5.h>
55 
56 extern char progname[];
57 
58 struct k5_data
59 {
60 	krb5_context ctx;
61 	krb5_ccache cc;
62 	krb5_principal me;
63 	char *name;
64 };
65 
66 
67 #define	MAIL		"mail"
68 #define	MAILPATH	"/usr/bin/mail"
69 #define	DEFAULT_CONFIG	"* terminal 30m"
70 #define	CONF_FILENAME	"/etc/krb5/warn.conf"
71 
72 /* warn.conf info */
73 
74 typedef struct config_entry_s {
75 	struct config_entry_s		*next;
76 	int				seconds_to_warn;
77 	char				*principal;
78 	char				*where_to;
79 	char				*email;
80 	int				renew;
81 	int				log_success;
82 	int				log_failure;
83 } config_entry_list_t;
84 static config_entry_list_t		*config_entry_list;
85 
86 /* list of principals to be warned */
87 
88 typedef struct cred_warning_list_s {
89 	struct cred_warning_list_s 	*next;
90 	WARNING_NAME_T			warn_name;
91 	time_t				cred_exp_time;
92 	time_t				cred_warn_time;
93 	mutex_t				cwm;
94 } cred_warning_list_t;
95 static cred_warning_list_t		*cred_warning_list;
96 static rwlock_t				cred_lock = DEFAULTRWLOCK;
97 
98 static bool_t
99 del_warning_pvt(char *);
100 
101 static config_entry_list_t *
102 find_warning_info(char *);
103 
104 static bool_t
105 parseConfigLine(char *buffer);
106 
107 extern int warn_send(char *, char *);
108 
109 extern int kwarnd_debug;
110 
111 cred_warning_list_t *
112 find_cred_warning(WARNING_NAME_T warn_name)
113 {
114 	cred_warning_list_t	*cw;
115 	if (!cred_warning_list)
116 		return (NULL);
117 	for (cw = cred_warning_list; cw != NULL; cw = cw->next) {
118 		if (strcmp(warn_name, cw->warn_name) != 0)
119 			continue;
120 		return (cw);
121 	}
122 	return (NULL);
123 }
124 
125 /*
126  * add a principal to the principal warning list
127  */
128 
129 bool_t
130 kwarn_add_warning_1_svc(kwarn_add_warning_arg *argp,
131 			kwarn_add_warning_res *res,
132 			struct svc_req *rqstp)
133 {
134 	cred_warning_list_t	*cred_warning;
135 	config_entry_list_t *config_entry;
136 
137 	if (kwarnd_debug) {
138 		printf("kwarn_add_warning_1_svc start; cWlist=%p\n",
139 		    cred_warning_list);
140 
141 		printf("kwarn_add_warning_1_svc: principal %s",
142 		    argp->warning_name);
143 		printf(" exp time: %d\n", argp->cred_exp_time);
144 	}
145 
146 /*
147  *  if there is no entry in the config file that matches the principal to
148  *  be added to the warning list, return true because we are not going to
149  *  send a warning for this principal.
150  */
151 
152 	if ((config_entry = find_warning_info(argp->warning_name)) == NULL) {
153 		if (kwarnd_debug)
154 			printf(
155 		"kwarn_add_warning_1_svc find_warn_info: fails, cWlist=%p\n",
156 				cred_warning_list);
157 
158 		return (TRUE);
159 	}
160 
161 /*
162  * see if a warning has already been created for this principal, if so
163  * update the warning time.
164  */
165 
166 	rw_wrlock(&cred_lock);
167 	if (cred_warning = find_cred_warning(argp->warning_name)) {
168 		rw_unlock(&cred_lock);
169 		mutex_lock(&cred_warning->cwm);
170 		cred_warning->cred_exp_time = argp->cred_exp_time;
171 		cred_warning->cred_warn_time = argp->cred_exp_time
172 			- config_entry->seconds_to_warn;
173 		mutex_unlock(&cred_warning->cwm);
174 	} else {
175 		cred_warning = (cred_warning_list_t *)malloc(
176 				sizeof (*cred_warning_list));
177 		if (cred_warning == NULL) {
178 			rw_unlock(&cred_lock);
179 			res->status = 1;
180 			return (FALSE);
181 		}
182 		(void) memset((char *)cred_warning, 0,
183 			    sizeof (*cred_warning_list));
184 		cred_warning->cred_exp_time = argp->cred_exp_time;
185 		cred_warning->cred_warn_time = argp->cred_exp_time
186 			- config_entry->seconds_to_warn;
187 		cred_warning->warn_name = strdup(argp->warning_name);
188 		if (cred_warning->warn_name == NULL) {
189 			free(cred_warning);
190 			rw_unlock(&cred_lock);
191 			res->status = 1;
192 			return (FALSE);
193 		}
194 		mutex_init(&cred_warning->cwm,  USYNC_THREAD, NULL);
195 		cred_warning->next = cred_warning_list;
196 		cred_warning_list = cred_warning;
197 		rw_unlock(&cred_lock);
198 	}
199 	res->status = 0;
200 
201 	if (kwarnd_debug)
202 		printf(
203 		"kwarn_add_warning_1_svc end: returns true; cWlist=%p\n",
204 		cred_warning_list);
205 
206 	return (TRUE);
207 }
208 
209 /*
210  * delete a warning request for a given principal
211  */
212 
213 bool_t
214 kwarn_del_warning_1_svc(kwarn_del_warning_arg *argp,
215 			kwarn_del_warning_res *res,
216 			struct svc_req *rqstp)
217 {
218 	if (kwarnd_debug)
219 		printf(gettext("delete principal %s requested\n"),
220 		    argp->warning_name);
221 
222 	if (del_warning_pvt(argp->warning_name) == TRUE) {
223 		res->status = 0;
224 
225 		if (kwarnd_debug)
226 			printf(gettext("delete principal %s completed\n"),
227 			    argp->warning_name);
228 
229 		return (TRUE);
230 	} else {
231 		res->status = 1;
232 
233 		if (kwarnd_debug)
234 			printf(gettext("delete principal %s failed\n"),
235 				argp->warning_name);
236 
237 		return (TRUE);
238 	}
239 }
240 
241 static bool_t
242 del_warning_pvt(char *warning_name)
243 {
244 	cred_warning_list_t	*cred_warning, *prev;
245 	rw_wrlock(&cred_lock);
246 	for (prev = NULL, cred_warning = cred_warning_list;
247 		cred_warning != NULL; prev = cred_warning,
248 		cred_warning = cred_warning->next) {
249 		if (strcmp(cred_warning->warn_name, warning_name) == 0) {
250 			if (!prev)
251 				cred_warning_list = cred_warning->next;
252 			else
253 				prev->next = cred_warning->next;
254 
255 			free(cred_warning->warn_name);
256 			free(cred_warning);
257 			rw_unlock(&cred_lock);
258 			return (TRUE);
259 		}
260 	}
261 	rw_unlock(&cred_lock);
262 	return (FALSE);
263 }
264 
265 /*
266  * load the warn.conf file into the config_entry list.
267  */
268 
269 bool_t
270 loadConfigFile(void)
271 {
272 	char	buffer[BUFSIZ];
273 	FILE	*cfgfile;
274 	bool_t	retval = TRUE;
275 
276 	if ((cfgfile = fopen(CONF_FILENAME, "r")) == NULL) {
277 		syslog(LOG_ERR, gettext(
278 			"could not open config file \"%s\"\n"),
279 			CONF_FILENAME);
280 		syslog(LOG_ERR, gettext(
281 			"using default options \"%s\"\n"),
282 			DEFAULT_CONFIG);
283 		retval = parseConfigLine(DEFAULT_CONFIG);
284 	} else {
285 		(void) memset(buffer, 0, sizeof (buffer));
286 		while ((fgets(buffer, BUFSIZ, cfgfile) != NULL) &&
287 			(retval == TRUE))
288 			retval = parseConfigLine(buffer);
289 		fclose(cfgfile);
290 	}
291 	return (retval);
292 }
293 
294 /*
295  * Return TRUE if we get a valid opt and update flags appro.
296  */
297 static bool_t
298 cmp_renew_opts(char *opt,
299 	    int *log_success, /* out */
300 	    int *log_failure) /* out */
301 {
302 
303 	if (strncasecmp(opt, "log",
304 			sizeof ("log")) == 0) {
305 		*log_success = *log_failure = 1;
306 	} else if (strncasecmp(opt, "log-success",
307 			    sizeof ("log-success")) == 0) {
308 		*log_success = 1;
309 	} else if (strncasecmp(opt, "log-failure",
310 			    sizeof ("log-failure")) == 0) {
311 		*log_failure = 1;
312 	} else {
313 		if (kwarnd_debug)
314 			printf("cmp_renew_opts: renew bad opt=`%s'\n",
315 			    opt ? opt : "null");
316 		return (FALSE);
317 	}
318 
319 	return (TRUE);
320 }
321 
322 /*
323  * Make the config_entry item for the config_entry_list, based on
324  * buffer.  The formats are
325  *
326  *    <principal> [renew[:<opt1,...optN>]] syslog|terminal <time>
327  *    <principal> [renew[:<opt1,...optN>]] mail <time> <e-mail address>
328  *
329  * where renew opts will be:
330  *
331  *     log-success
332  *		- Log the result of the renew attempt on success using
333  *		  the specified method (syslog|terminal|mail)
334  *
335  *      log-failure
336  *		- Log the result of the renew attempt on failure using
337  *		  the specified method (syslog|terminal|mail)
338  *
339  *      log
340  *               - Same as specifing both log-failure and log-success
341  *
342  *		  Note if no log options are given, there will be no logging.
343  *
344  */
345 
346 static bool_t
347 parseConfigLine(char *buffer)
348 {
349 	char *principal, *send_to, *emailid, *ends, *tm;
350 	char			*exptime;
351 	int			time_mode;
352 	time_t			etime;
353 	config_entry_list_t	*config_entry;
354 	int renew = 0;
355 	int log_success = 0;
356 	int log_failure = 0;
357 
358 	/* ignore comments */
359 	if (*buffer == '#')
360 		return (TRUE);
361 
362 	if (kwarnd_debug)
363 		printf("parseconf: buffer=%s", buffer);
364 
365 	/* find end of principal */
366 	principal = buffer;
367 	for (send_to = buffer; *send_to && !isspace(*send_to);
368 		send_to++);
369 
370 	/* find first non whitespace after principal (start of send_to) */
371 	if (*send_to) {
372 		*send_to = '\0';
373 		send_to++;
374 		while (*send_to && isspace(*send_to))
375 			send_to++;
376 	}
377 
378 	/* if no send_to, continue, bad entry */
379 	if (! *send_to)
380 		return (TRUE);
381 
382 	/* find end of send_to */
383 	for (ends = send_to; *ends && !isspace(*ends);
384 		ends++);
385 	if (*ends)
386 		*ends = '\0';
387 
388 
389 	if (strchr(send_to, ':')) {
390 		/* we've got renew opts */
391 		char *st = NULL, *op = NULL;
392 
393 		op = strdup(send_to);
394 		if (!op)
395 			return (FALSE);
396 		st = strchr(op, ':');
397 		*st = '\0';
398 
399 		if (strncasecmp(op, "renew", sizeof ("renew")) == 0) {
400 			renew = 1;
401 		} else {
402 			free(op);
403 			/* got a ':' but not preceeded w/renew, badent, skip */
404 			if (kwarnd_debug)
405 				printf("parseconf: colon badent, skip\n");
406 			return (TRUE);
407 		}
408 		free(op);
409 		op = NULL;
410 
411 		st++;
412 		if (!st || !*st || isspace(*st)) {
413 			if (kwarnd_debug)
414 				printf("parseconf: st badent, skip\n");
415 			/* bad ent, skip */
416 			return (TRUE);
417 		}
418 		if (renew && strchr(st, ',')) {
419 			while (1) {
420 				/* loop thru comma seperated list-o-opts */
421 				char *comma = NULL, *c = NULL, *l = NULL;
422 
423 				if (st && (comma = strchr(st, ','))) {
424 					l = strdup(st);
425 					if (!l)
426 						return (FALSE);
427 					c = strchr(l, ',');
428 					*c = '\0';
429 					if (!cmp_renew_opts(l, &log_success,
430 							    &log_failure)) {
431 						free(l);
432 						/* badent, skip */
433 						return (TRUE);
434 					}
435 					free(l);
436 					l = NULL;
437 
438 					st = comma;
439 					st++;
440 				} else {
441 					if (st) {
442 						if (!cmp_renew_opts(st,
443 							    &log_success,
444 							    &log_failure)) {
445 							/* badent, skip */
446 							return (TRUE);
447 						}
448 					}
449 					break;
450 				}
451 			} /* while */
452 		} else if (st) {
453 			/* we just have one opt */
454 			if (!cmp_renew_opts(st, &log_success, &log_failure)) {
455 				/* badent, skip */
456 				return (TRUE);
457 			}
458 		}
459 
460 		/* if send_to is "renew", note it and refind send_to */
461 	} else if (strncasecmp(send_to, "renew",
462 			    sizeof ("renew")) == 0) {
463 		renew = 1;
464 
465 	}
466 
467 	if (kwarnd_debug) {
468 		printf("parseconf: renew=%d, log failure=%d, log success=%d\n",
469 		    renew, log_failure, log_success);
470 	}
471 
472 	if (renew) {
473 		/* find first non whitespace after send_to (start of exptime) */
474 		for (send_to = ends+1; *send_to && isspace(*send_to);
475 		    send_to++);
476 
477 		/* if no send_to, continue, bad entry */
478 		if (! *send_to) {
479 			if (kwarnd_debug)
480 				printf("parseconf: no send_to, badent, skip\n");
481 			return (TRUE);
482 		}
483 
484 		/* find end of send_to */
485 		for (ends = send_to; *ends && !isspace(*ends);
486 		    ends++);
487 		if (*ends)
488 			*ends = '\0';
489 	}
490 
491 
492 	/* find first non whitespace after send_to (start of exptime) */
493 	for (exptime = ends+1; *exptime && isspace(*exptime);
494 		exptime++);
495 
496 	/* if no exptime, continue, bad entry */
497 	if (! *exptime) {
498 		if (kwarnd_debug)
499 			printf("parseconf: no exptime, badent, skip\n");
500 		return (TRUE);
501 	}
502 
503 	/* find end of exptime */
504 	for (ends = exptime; *ends && !isspace(*ends); ends++);
505 
506 	tm = ends - 1;
507 	if (*tm == 's')
508 		time_mode = 1;
509 	else if (*tm == 'm')
510 		time_mode = 2;
511 	else if (*tm == 'h')
512 		time_mode = 3;
513 	else
514 		time_mode = 1;
515 
516 	if (*tm)
517 		*tm = '\0';
518 
519 	if (kwarnd_debug) {
520 		printf("parseconf: send_to = '%s', exptime='%s'\n",
521 		    send_to, exptime);
522 	}
523 
524 	/* find first non whitespace after exptime (start of emailid) */
525 	for (emailid = ends+1; *emailid && isspace(*emailid); emailid++);
526 
527 	/* find end of emailid */
528 	if (*emailid) {
529 		for (ends = emailid; *ends && !isspace(*ends);
530 			ends++);
531 
532 		if (*ends)
533 			*ends = '\0';
534 	}
535 
536 	/* if send to mail and no mail address, bad entry */
537 	if ((strcmp(send_to, "mail") == 0) && (!*emailid)) {
538 		if (kwarnd_debug)
539 			printf("parseconf: returns true; no mail addr\n");
540 
541 		syslog(LOG_ERR, gettext("missing mail address"
542 			" in config entry: \n%s %s %s "
543 			" cannot mail warning"), principal,
544 			send_to, exptime);
545 		return (TRUE);
546 	}
547 
548 	/* create an entry */
549 	config_entry = (config_entry_list_t *)
550 		malloc(sizeof (*config_entry_list));
551 	if (config_entry == NULL)
552 		return (FALSE);
553 	(void) memset(config_entry, 0, sizeof (*config_entry_list));
554 	config_entry->principal = strdup(principal);
555 	if (config_entry->principal == NULL)
556 		return (FALSE);
557 	config_entry->where_to = strdup(send_to);
558 	if (config_entry->where_to == NULL)
559 		return (FALSE);
560 	etime = atol(exptime);
561 	if (time_mode == 1)
562 		config_entry->seconds_to_warn = etime;
563 	else if (time_mode == 2)
564 		config_entry->seconds_to_warn = etime * 60;
565 	else if (time_mode == 3)
566 		config_entry->seconds_to_warn = etime * 60 * 60;
567 
568 	if (*emailid) {
569 		config_entry->email = strdup(emailid);
570 		if (config_entry->email == NULL)
571 			return (FALSE);
572 	}
573 
574 	config_entry->renew = renew;
575 	config_entry->log_success = log_success;
576 	config_entry->log_failure = log_failure;
577 	config_entry->next = config_entry_list;
578 	config_entry_list = config_entry;
579 	if (kwarnd_debug)
580 		printf("parseconf: returns true; celist=%p\n",
581 		    config_entry_list);
582 
583 	return (TRUE);
584 }
585 
586 /*
587  * find a specific warn.conf entry.
588  */
589 
590 static config_entry_list_t *
591 find_warning_info(char *principal)
592 {
593 	config_entry_list_t	*config_entry;
594 	/* look for a specific entry */
595 	for (config_entry = config_entry_list; config_entry;
596 		config_entry = config_entry->next) {
597 		if (strcmp(config_entry->principal, principal) == 0) {
598 			return (config_entry);
599 		}
600 	}
601 	/* look for a wild card entry */
602 	for (config_entry = config_entry_list; config_entry;
603 		config_entry = config_entry->next) {
604 		if (strcmp(config_entry->principal, "*") == 0) {
605 			return (config_entry);
606 		}
607 	}
608 	/* nothing found */
609 	return (NULL);
610 
611 }
612 
613 /*
614  * create a pipe, fork and exec a command,
615  */
616 static FILE *
617 safe_popen_w(char *path_to_cmd, char **argv)
618 {
619 
620 	int fd[2];
621 	FILE *fp;
622 	char *envp[2];
623 
624 	if (pipe(fd) == -1)
625 		return (NULL);
626 
627 
628 	switch (fork()) {
629 	case -1:
630 		(void) close(fd[0]);
631 		(void) close(fd[1]);
632 		return (NULL);
633 
634 	case 0:
635 		close(fd[1]);
636 		/* fd[0] is the end we read from */
637 		if (fd[0] != 0) {
638 			close(0);
639 			dup(fd[0]);
640 		}
641 		close(1);
642 		close(2);
643 		envp[0] = "PATH=/usr/bin";
644 		envp[1] = NULL;
645 #ifdef	DEBUG
646 		{
647 			int fd;
648 			fd = open("/tmp/kwarn.out", O_WRONLY|O_TRUNC|O_CREAT,
649 				0666);
650 			if (fd != 1)
651 				dup(fd);
652 			if (fd != 2)
653 				dup(fd);
654 		}
655 #endif
656 		(void) execve(path_to_cmd, argv, envp);
657 		syslog(LOG_ERR, "warnd: %m");
658 		_exit(1);
659 
660 	default:
661 		close(fd[0]);
662 		/* fd[1] is the end we write to */
663 
664 		fp = fdopen(fd[1], "w");
665 
666 		if (fp == NULL) {
667 			(void) close(fd[1]);
668 			return (NULL);
669 		}
670 		return (fp);
671 	}
672 }
673 
674 
675 static uid_t krb5_cc_uid;
676 
677 void
678 set_warnd_uid(uid_t uid)
679 {
680 	/*
681 	 * set the value of krb5_cc_uid, so it can be retrieved when
682 	 * app_krb5_user_uid() is called by the underlying mechanism libraries.
683 	 */
684 	if (kwarnd_debug)
685 		printf("set_warnd_uid called with uid = %d\n", uid);
686 	krb5_cc_uid = uid;
687 }
688 
689 uid_t
690 app_krb5_user_uid(void)
691 {
692 
693 	/*
694 	 * return the value set when one of the kwarnd procedures was
695 	 * entered. This is the value of the uid under which the
696 	 * underlying mechanism library must operate in order to
697 	 * get the user's credentials. This call is necessary since
698 	 * kwarnd runs as root and credentials are many times stored
699 	 * in files and directories specific to the user
700 	 */
701 	if (kwarnd_debug)
702 		printf("app_krb5_user_uid called and returning uid = %d\n",
703 		    krb5_cc_uid);
704 	return (krb5_cc_uid);
705 }
706 
707 
708 static bool_t
709 getpruid(char *pr, uid_t *uid)
710 {
711 	char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
712 	struct passwd *pw;
713 
714 	rcp1 = strdup(pr);
715 	if (!rcp1)
716 		return (FALSE);
717 	rcp2 = strtok(rcp1, "@");
718 	rcp3 = strtok(rcp2, "/");
719 
720 	if (rcp3) {
721 		pw = getpwnam(rcp3);
722 		*uid = pw->pw_uid;
723 		free(rcp1);
724 		return (TRUE);
725 	}
726 
727 	free(rcp1);
728 	return (FALSE);
729 }
730 
731 
732 static krb5_error_code
733 renew_creds(
734 	char *princ,
735 	time_t *new_exp_time) /* out */
736 {
737 	krb5_creds my_creds;
738 	krb5_error_code code = 0;
739 	struct k5_data k5;
740 
741 	uid_t saved_u = app_krb5_user_uid();
742 	uid_t u;
743 
744 	if (kwarnd_debug)
745 		printf("renew start: uid=%d\n", app_krb5_user_uid());
746 
747 	if (!getpruid(princ, &u)) {
748 		if (kwarnd_debug)
749 			printf("renew: getpruid failed, princ='%s'\n",
750 			    princ ? princ : "<null>");
751 
752 		return (-1); /* better err num? */
753 	}
754 
755 	set_warnd_uid(u);
756 
757 	(void) memset(&my_creds, 0, sizeof (my_creds));
758 	(void) memset(&k5, 0, sizeof (k5));
759 
760 	if (code = krb5_init_context(&k5.ctx)) {
761 		com_err(progname, code,
762 			gettext("while initializing Kerberos 5 library"));
763 		goto out;
764 	}
765 
766 	if ((code = krb5_cc_default(k5.ctx, &k5.cc))) {
767 		com_err(progname, code,
768 			gettext("while getting default ccache"));
769 		goto out;
770 
771 	}
772 
773 	if ((code = krb5_parse_name(k5.ctx, princ,
774 				    &k5.me))) {
775 		com_err(progname, code, gettext("when parsing name %s"),
776 			princ);
777 		goto out;
778 	}
779 
780 	if ((code = krb5_get_renewed_creds(k5.ctx, &my_creds, k5.me, k5.cc,
781 					NULL))) {
782 		com_err(progname, code, gettext("while renewing creds"));
783 		goto out;
784 	}
785 
786 	if (code = krb5_cc_initialize(k5.ctx, k5.cc, k5.me)) {
787 		com_err(progname, code, gettext("when initializing cache %s"),
788 			"defcc");
789 		goto out;
790 	}
791 
792 	if (code = krb5_cc_store_cred(k5.ctx, k5.cc, &my_creds)) {
793 		com_err(progname, code, gettext("while storing credentials"));
794 		goto out;
795 	}
796 
797 	/* "return" new expire time */
798 	*new_exp_time = my_creds.times.endtime;
799 
800 out:
801 	krb5_free_cred_contents(k5.ctx, &my_creds);
802 
803 	if (k5.name)
804 		krb5_free_unparsed_name(k5.ctx, k5.name);
805 	if (k5.me)
806 		krb5_free_principal(k5.ctx, k5.me);
807 	if (k5.cc)
808 		krb5_cc_close(k5.ctx, k5.cc);
809 	if (k5.ctx)
810 		krb5_free_context(k5.ctx);
811 
812 	set_warnd_uid(saved_u);
813 
814 	if (kwarnd_debug)
815 		printf("renew end: code=%s, uid=%d\n", error_message(code),
816 		    app_krb5_user_uid());
817 
818 	return (code);
819 }
820 
821 static bool_t
822 loggedon(char *name)
823 {
824 	register struct utmpx *ubuf;
825 	char    *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
826 
827 	/*
828 	 * strip any realm or instance from principal so we can match
829 	 * against unix userid.
830 	 */
831 	rcp1 = strdup(name);
832 	if (!rcp1)
833 		return (FALSE);
834 	rcp2 = strtok(rcp1, "@");
835 	rcp3 = strtok(rcp2, "/");
836 
837 	/*
838 	 * Scan through the "utmpx" file for the
839 	 * entry for the person we want to send to.
840 	 */
841 
842 	setutxent();
843 	while ((ubuf = getutxent()) != NULL) {
844 		if (ubuf->ut_type == USER_PROCESS) {
845 			if (strncmp(rcp3, ubuf->ut_user,
846 				    sizeof (ubuf->ut_user)) == 0) {
847 				free(rcp1);
848 				endutxent();
849 				return (TRUE);
850 
851 			}
852 		}
853 	}
854 	free(rcp1);
855 	endutxent();
856 
857 	if (kwarnd_debug)
858 		printf("loggedon: returning false for user `%s'\n", rcp1);
859 
860 	return (FALSE);
861 }
862 
863 /*
864  * main loop to check the cred warning list and send the warnings
865  * the appropriate location based on warn.conf or auto-renew creds.
866  */
867 
868 void
869 kwarnd_check_warning_list(void)
870 { /* func */
871 	cred_warning_list_t	*cw;  /* cred warning */
872 	config_entry_list_t	*ce;  /* config entry */
873 	time_t			now;
874 	int			minutes;
875 	char			buff[256];
876 	char			cmdline[256];
877 	FILE			*fp;
878 	char			*subj = "Kerberos credentials expiring";
879 	char			*renew_subj = "Kerberos credentials renewed";
880 
881 	if (kwarnd_debug)
882 		printf("check list: start: uid=%d, cw list=%p\n",
883 		    app_krb5_user_uid(), cred_warning_list);
884 
885 	while (1) {
886 		(void) poll(NULL, NULL, 60000);
887 
888 		for (cw = cred_warning_list;
889 			cw != NULL;
890 			cw = cw->next) {
891 			int send_msg = 0;
892 
893 			time(&now);
894 			if (now >= cw->cred_warn_time) {
895 				int renew_attempted = 0;
896 				int renew_failed = 0;
897 				int renew_tooclose = 0;
898 
899 				if (kwarnd_debug)
900 					printf("checklist: now >= warn_t\n");
901 
902 				ce = find_warning_info(cw->warn_name);
903 				minutes = (cw->cred_exp_time -
904 					now + 59) / 60;
905 
906 				if (kwarnd_debug)
907 					printf("checklist: where_to=%s\n",
908 					    ce->where_to ?
909 					    ce->where_to : "null");
910 
911 				if (ce->renew &&
912 				    loggedon(cw->warn_name)) {
913 					krb5_error_code code;
914 					time_t new_exp_time;
915 
916 					renew_attempted = 1;
917 					code = renew_creds(
918 						cw->warn_name,
919 						&new_exp_time);
920 					if (!code) {
921 						/* krb5 api renew success */
922 
923 						/*
924 						 * So we had api success
925 						 * but the new exp time
926 						 * is same as current one
927 						 * so we are too close
928 						 * to Renewable_life time.
929 						 */
930 						if (cw->cred_exp_time
931 						    == new_exp_time) {
932 							renew_tooclose = 1;
933 							if (kwarnd_debug)
934 								printf(
935 		"checklist: new expire time same as old expire time\n");
936 
937 							if (ce->log_failure) {
938 								send_msg = 1;
939 								snprintf(buff,
940 								sizeof (buff),
941 					gettext("%s:\r\nYour kerberos"
942 					" credentials have not been renewed"
943 					" (too close to Renewable_life).\r\n"
944 					"Please run kinit(1).\r\n"),
945 								cw->warn_name);
946 							}
947 						} else {
948 							/* update times */
949 							cw->cred_exp_time =
950 								new_exp_time;
951 							cw->cred_warn_time =
952 							    new_exp_time -
953 							    ce->seconds_to_warn;
954 						}
955 
956 						if (kwarnd_debug)
957 							printf(
958 						    "check list: new_w_t=%d\n",
959 						    cw->cred_warn_time);
960 
961 						if (!renew_tooclose &&
962 						    ce->log_success) {
963 							if (kwarnd_debug)
964 								printf(
965 						"check list: log success\n");
966 
967 							send_msg = 1;
968 							snprintf(buff,
969 								sizeof (buff),
970 						gettext("%s:\r\nYour kerberos"
971 					" credentials have been renewed.\r\n"),
972 								cw->warn_name);
973 						}
974 
975 					}  /* !(code) */
976 
977 					if (!renew_tooclose && code &&
978 					    ce->log_failure) {
979 						if (kwarnd_debug)
980 							printf(
981 						"check list: log FAIL\n");
982 
983 						send_msg = 1;
984 						snprintf(buff,
985 							sizeof (buff),
986 					    gettext("%s:\r\nYour kerberos"
987 				" credentials failed to be renewed (%s).\r\n"),
988 							cw->warn_name,
989 							error_message(code));
990 					}
991 					renew_failed = code ? 1 : 0;
992 
993 				} else if (minutes > 0) {
994 					send_msg = 1;
995 					snprintf(buff, sizeof (buff),
996 					gettext("%s:\r\nyour kerberos"
997 					" credentials expire in less than"
998 					" %d minutes.\r\n"),
999 					cw->warn_name,
1000 					minutes);
1001 				} else {
1002 					send_msg = 1;
1003 					snprintf(buff, sizeof (buff),
1004 					gettext("%s:\r\nyour kerberos"
1005 					" credentials have expired.\r\n"),
1006 					cw->warn_name);
1007 				}
1008 
1009 				if (kwarnd_debug)
1010 					printf("checklist: send_msg=%d\n",
1011 					    send_msg);
1012 				if (!send_msg)
1013 					goto del_warning;
1014 
1015 				if (strncmp(ce->where_to,
1016 					    "mail", sizeof ("mail")) == 0) {
1017 					char *argv[3];
1018 
1019 					argv[0] = MAIL;
1020 					(void) snprintf(cmdline,
1021 							sizeof (cmdline),
1022 							"%s",
1023 							ce->email);
1024 					argv[1] = cmdline;
1025 					argv[2] = NULL;
1026 
1027 					fp = safe_popen_w(MAILPATH, argv);
1028 
1029 					if (fp) {
1030 
1031 						(void) fprintf(fp,
1032 						"To: %s\nSubject: %s\n\n%s\n",
1033 							    ce->email,
1034 							    renew_attempted
1035 							    ? renew_subj : subj,
1036 							    buff);
1037 
1038 					    fclose(fp);
1039 					} else {
1040 					    syslog(LOG_ERR,
1041 						gettext("could not fork "
1042 						"mail program to e-mail "
1043 						"warning to %s\n"),
1044 						cmdline);
1045 					}
1046 
1047 				} else if (strncmp(ce->where_to,
1048 						"terminal",
1049 						sizeof ("terminal")) == 0) {
1050 
1051 					warn_send(cw->warn_name,
1052 						buff);
1053 
1054 				} else if (send_msg && strncmp(ce->where_to,
1055 							    "syslog",
1056 						sizeof ("syslog")) == 0) {
1057 					syslog(LOG_NOTICE|LOG_AUTH,
1058 					    "%s",
1059 					    buff);
1060 #if 0
1061 				} else if (strncmp(ce->where_to,
1062 						"snmp",
1063 						sizeof ("snmp")) == 0) {
1064 #endif
1065 				} else {
1066 					if (kwarnd_debug)
1067 						printf(
1068 						"unknown msg method=`%s'\n",
1069 						ce->where_to);
1070 
1071 					exit(1);
1072 				}
1073 
1074 			del_warning:
1075 				if (!renew_attempted || renew_failed ||
1076 				    renew_tooclose) {
1077 					if (del_warning_pvt(cw->warn_name)
1078 					    == TRUE) {
1079 
1080 						if (kwarnd_debug)
1081 							printf(
1082 						"check list: del warn succ\n");
1083 
1084 						break;
1085 					} else {
1086 						if (kwarnd_debug)
1087 							printf(
1088 						"could not delete warning\n");
1089 
1090 						syslog(LOG_ERR, gettext(
1091 						"could not delete warning"));
1092 
1093 						exit(1);
1094 					    }
1095 					}
1096 
1097 				} /* if (now) */
1098 		} /* for */
1099 	} /* while */
1100 }  /* func */
1101