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