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