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