xref: /titanic_44/usr/src/cmd/krb5/kwarn/kwarnd_proc.c (revision 9760e1c48d45ca95f530c947bf07c62061b86c18)
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 2006 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 gssd_uid;
676 
677 void
678 set_warnd_uid(uid_t uid)
679 {
680 
681 	/*
682 	 * set the value of gssd_uid, so it can be retrieved when getuid()
683 	 * is called by the underlying mechanism libraries
684 	 */
685 	if (kwarnd_debug)
686 		printf("set_warnd_uid called with uid = %d\n", uid);
687 
688 	gssd_uid = uid;
689 }
690 
691 uid_t
692 getuid(void)
693 
694 {
695 
696 	/*
697 	 * return the value set when one of the gssd procedures was
698 	 * entered. This is the value of the uid under which the
699 	 * underlying mechanism library must operate in order to
700 	 * get the user's credentials. This call is necessary since
701 	 * gssd runs as root and credentials are many times stored
702 	 * in files and directories specific to the user
703 	 */
704 	if (kwarnd_debug)
705 		printf("getuid called and returning gsssd_uid = %d\n",
706 		    gssd_uid);
707 
708 	return (gssd_uid);
709 }
710 
711 
712 static bool_t
713 getpruid(char *pr, uid_t *uid)
714 {
715 	char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
716 	struct passwd *pw;
717 
718 	rcp1 = strdup(pr);
719 	if (!rcp1)
720 		return (FALSE);
721 	rcp2 = strtok(rcp1, "@");
722 	rcp3 = strtok(rcp2, "/");
723 
724 	if (rcp3) {
725 		pw = getpwnam(rcp3);
726 		*uid = pw->pw_uid;
727 		free(rcp1);
728 		return (TRUE);
729 	}
730 
731 	free(rcp1);
732 	return (FALSE);
733 }
734 
735 
736 static krb5_error_code
737 renew_creds(
738 	char *princ,
739 	time_t *new_exp_time) /* out */
740 {
741 	krb5_creds my_creds;
742 	krb5_error_code code = 0;
743 	struct k5_data k5;
744 
745 	uid_t saved_u = getuid();
746 	uid_t u;
747 
748 	if (kwarnd_debug)
749 		printf("renew start: uid=%d\n", getuid());
750 
751 	if (!getpruid(princ, &u)) {
752 		if (kwarnd_debug)
753 			printf("renew: getpruid failed, princ='%s'\n",
754 			    princ ? princ : "<null>");
755 
756 		return (-1); /* better err num? */
757 	}
758 
759 	set_warnd_uid(u);
760 
761 	(void) memset(&my_creds, 0, sizeof (my_creds));
762 	(void) memset(&k5, 0, sizeof (k5));
763 
764 	if (code = krb5_init_context(&k5.ctx)) {
765 		com_err(progname, code,
766 			gettext("while initializing Kerberos 5 library"));
767 		goto out;
768 	}
769 
770 	if ((code = krb5_cc_default(k5.ctx, &k5.cc))) {
771 		com_err(progname, code,
772 			gettext("while getting default ccache"));
773 		goto out;
774 
775 	}
776 
777 	if ((code = krb5_parse_name(k5.ctx, princ,
778 				    &k5.me))) {
779 		com_err(progname, code, gettext("when parsing name %s"),
780 			princ);
781 		goto out;
782 	}
783 
784 	if ((code = krb5_get_renewed_creds(k5.ctx, &my_creds, k5.me, k5.cc,
785 					NULL))) {
786 		com_err(progname, code, gettext("while renewing creds"));
787 		goto out;
788 	}
789 
790 	if (code = krb5_cc_initialize(k5.ctx, k5.cc, k5.me)) {
791 		com_err(progname, code, gettext("when initializing cache %s"),
792 			"defcc");
793 		goto out;
794 	}
795 
796 	if (code = krb5_cc_store_cred(k5.ctx, k5.cc, &my_creds)) {
797 		com_err(progname, code, gettext("while storing credentials"));
798 		goto out;
799 	}
800 
801 	/* "return" new expire time */
802 	*new_exp_time = my_creds.times.endtime;
803 
804 out:
805 	krb5_free_cred_contents(k5.ctx, &my_creds);
806 
807 	if (k5.name)
808 		krb5_free_unparsed_name(k5.ctx, k5.name);
809 	if (k5.me)
810 		krb5_free_principal(k5.ctx, k5.me);
811 	if (k5.cc)
812 		krb5_cc_close(k5.ctx, k5.cc);
813 	if (k5.ctx)
814 		krb5_free_context(k5.ctx);
815 
816 	set_warnd_uid(saved_u);
817 
818 	if (kwarnd_debug)
819 		printf("renew end: code=%s, uid=%d\n", error_message(code),
820 		    getuid());
821 
822 	return (code);
823 }
824 
825 static bool_t
826 loggedon(char *name)
827 {
828 	register struct utmpx *ubuf;
829 	char    *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
830 
831 	/*
832 	 * strip any realm or instance from principal so we can match
833 	 * against unix userid.
834 	 */
835 	rcp1 = strdup(name);
836 	if (!rcp1)
837 		return (FALSE);
838 	rcp2 = strtok(rcp1, "@");
839 	rcp3 = strtok(rcp2, "/");
840 
841 	/*
842 	 * Scan through the "utmpx" file for the
843 	 * entry for the person we want to send to.
844 	 */
845 
846 	setutxent();
847 	while ((ubuf = getutxent()) != NULL) {
848 		if (ubuf->ut_type == USER_PROCESS) {
849 			if (strncmp(rcp3, ubuf->ut_user,
850 				    sizeof (ubuf->ut_user)) == 0) {
851 				free(rcp1);
852 				endutxent();
853 				return (TRUE);
854 
855 			}
856 		}
857 	}
858 	free(rcp1);
859 	endutxent();
860 
861 	if (kwarnd_debug)
862 		printf("loggedon: returning false for user `%s'\n", rcp1);
863 
864 	return (FALSE);
865 }
866 
867 /*
868  * main loop to check the cred warning list and send the warnings
869  * the appropriate location based on warn.conf or auto-renew creds.
870  */
871 
872 void
873 kwarnd_check_warning_list(void)
874 { /* func */
875 	cred_warning_list_t	*cw;  /* cred warning */
876 	config_entry_list_t	*ce;  /* config entry */
877 	time_t			now;
878 	int			minutes;
879 	char			buff[256];
880 	char			cmdline[256];
881 	FILE			*fp;
882 	char			*subj = "Kerberos credentials expiring";
883 	char			*renew_subj = "Kerberos credentials renewed";
884 
885 	if (kwarnd_debug)
886 		printf("check list: start: getuid=%d, cw list=%p\n", getuid(),
887 			cred_warning_list);
888 
889 	while (1) {
890 		(void) poll(NULL, NULL, 60000);
891 
892 		for (cw = cred_warning_list;
893 			cw != NULL;
894 			cw = cw->next) {
895 			int send_msg = 0;
896 
897 			time(&now);
898 			if (now >= cw->cred_warn_time) {
899 				int renew_attempted = 0;
900 				int renew_failed = 0;
901 				int renew_tooclose = 0;
902 
903 				if (kwarnd_debug)
904 					printf("checklist: now >= warn_t\n");
905 
906 				ce = find_warning_info(cw->warn_name);
907 				minutes = (cw->cred_exp_time -
908 					now + 59) / 60;
909 
910 				if (kwarnd_debug)
911 					printf("checklist: where_to=%s\n",
912 					    ce->where_to ?
913 					    ce->where_to : "null");
914 
915 				if (ce->renew &&
916 				    loggedon(cw->warn_name)) {
917 					krb5_error_code code;
918 					time_t new_exp_time;
919 
920 					renew_attempted = 1;
921 					code = renew_creds(
922 						cw->warn_name,
923 						&new_exp_time);
924 					if (!code) {
925 						/* krb5 api renew success */
926 
927 						/*
928 						 * So we had api success
929 						 * but the new exp time
930 						 * is same as current one
931 						 * so we are too close
932 						 * to Renewable_life time.
933 						 */
934 						if (cw->cred_exp_time
935 						    == new_exp_time) {
936 							renew_tooclose = 1;
937 							if (kwarnd_debug)
938 								printf(
939 		"checklist: new expire time same as old expire time\n");
940 
941 							if (ce->log_failure) {
942 								send_msg = 1;
943 								snprintf(buff,
944 								sizeof (buff),
945 					gettext("%s:\r\nYour kerberos"
946 					" credentials have not been renewed"
947 					" (too close to Renewable_life).\r\n"
948 					"Please run kinit(1).\r\n"),
949 								cw->warn_name);
950 							}
951 						} else {
952 							/* update times */
953 							cw->cred_exp_time =
954 								new_exp_time;
955 							cw->cred_warn_time =
956 							    new_exp_time -
957 							    ce->seconds_to_warn;
958 						}
959 
960 						if (kwarnd_debug)
961 							printf(
962 						    "check list: new_w_t=%d\n",
963 						    cw->cred_warn_time);
964 
965 						if (!renew_tooclose &&
966 						    ce->log_success) {
967 							if (kwarnd_debug)
968 								printf(
969 						"check list: log success\n");
970 
971 							send_msg = 1;
972 							snprintf(buff,
973 								sizeof (buff),
974 						gettext("%s:\r\nYour kerberos"
975 					" credentials have been renewed.\r\n"),
976 								cw->warn_name);
977 						}
978 
979 					}  /* !(code) */
980 
981 					if (!renew_tooclose && code &&
982 					    ce->log_failure) {
983 						if (kwarnd_debug)
984 							printf(
985 						"check list: log FAIL\n");
986 
987 						send_msg = 1;
988 						snprintf(buff,
989 							sizeof (buff),
990 					    gettext("%s:\r\nYour kerberos"
991 				" credentials failed to be renewed (%s).\r\n"),
992 							cw->warn_name,
993 							error_message(code));
994 					}
995 					renew_failed = code ? 1 : 0;
996 
997 				} else if (minutes > 0) {
998 					send_msg = 1;
999 					snprintf(buff, sizeof (buff),
1000 					gettext("%s:\r\nyour kerberos"
1001 					" credentials expire in less than"
1002 					" %d minutes.\r\n"),
1003 					cw->warn_name,
1004 					minutes);
1005 				} else {
1006 					send_msg = 1;
1007 					snprintf(buff, sizeof (buff),
1008 					gettext("%s:\r\nyour kerberos"
1009 					" credentials have expired.\r\n"),
1010 					cw->warn_name);
1011 				}
1012 
1013 				if (kwarnd_debug)
1014 					printf("checklist: send_msg=%d\n",
1015 					    send_msg);
1016 				if (!send_msg)
1017 					goto del_warning;
1018 
1019 				if (strncmp(ce->where_to,
1020 					    "mail", sizeof ("mail")) == 0) {
1021 					char *argv[3];
1022 
1023 					argv[0] = MAIL;
1024 					(void) snprintf(cmdline,
1025 							sizeof (cmdline),
1026 							"%s",
1027 							ce->email);
1028 					argv[1] = cmdline;
1029 					argv[2] = NULL;
1030 
1031 					fp = safe_popen_w(MAILPATH, argv);
1032 
1033 					if (fp) {
1034 
1035 						(void) fprintf(fp,
1036 						"To: %s\nSubject: %s\n\n%s\n",
1037 							    ce->email,
1038 							    renew_attempted
1039 							    ? renew_subj : subj,
1040 							    buff);
1041 
1042 					    fclose(fp);
1043 					} else {
1044 					    syslog(LOG_ERR,
1045 						gettext("could not fork "
1046 						"mail program to e-mail "
1047 						"warning to %s\n"),
1048 						cmdline);
1049 					}
1050 
1051 				} else if (strncmp(ce->where_to,
1052 						"terminal",
1053 						sizeof ("terminal")) == 0) {
1054 
1055 					warn_send(cw->warn_name,
1056 						buff);
1057 
1058 				} else if (send_msg && strncmp(ce->where_to,
1059 							    "syslog",
1060 						sizeof ("syslog")) == 0) {
1061 					syslog(LOG_NOTICE|LOG_AUTH,
1062 					    "%s",
1063 					    buff);
1064 #if 0
1065 				} else if (strncmp(ce->where_to,
1066 						"snmp",
1067 						sizeof ("snmp")) == 0) {
1068 #endif
1069 				} else {
1070 					if (kwarnd_debug)
1071 						printf(
1072 						"unknown msg method=`%s'\n",
1073 						ce->where_to);
1074 
1075 					exit(1);
1076 				}
1077 
1078 			del_warning:
1079 				if (!renew_attempted || renew_failed ||
1080 				    renew_tooclose) {
1081 					if (del_warning_pvt(cw->warn_name)
1082 					    == TRUE) {
1083 
1084 						if (kwarnd_debug)
1085 							printf(
1086 						"check list: del warn succ\n");
1087 
1088 						break;
1089 					} else {
1090 						if (kwarnd_debug)
1091 							printf(
1092 						"could not delete warning\n");
1093 
1094 						syslog(LOG_ERR, gettext(
1095 						"could not delete warning"));
1096 
1097 						exit(1);
1098 					    }
1099 					}
1100 
1101 				} /* if (now) */
1102 		} /* for */
1103 	} /* while */
1104 }  /* func */
1105