1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 *  RPC server procedures for the usermode daemon kwarnd.
30 */
31
32#include <stdio.h>
33#include <unistd.h>
34#include <pwd.h>
35#include <grp.h>
36#include <strings.h>
37#include <string.h>
38#include <sys/param.h>
39#include <sys/syslog.h>
40#include "kwarnd.h"
41#include <rpc/rpc.h>
42#include <stdlib.h>
43#include <syslog.h>
44#include <poll.h>
45#include <utmpx.h>
46#include <pwd.h>
47#include <strings.h>
48#include <ctype.h>
49
50#include <k5-int.h>
51#include <profile/prof_int.h>
52#include <com_err.h>
53#include <libintl.h>
54#include <krb5.h>
55
56extern char progname[];
57
58struct 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
74typedef 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;
84static config_entry_list_t		*config_entry_list;
85
86/* list of principals to be warned */
87
88typedef 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;
95static cred_warning_list_t		*cred_warning_list;
96static rwlock_t				cred_lock = DEFAULTRWLOCK;
97
98static bool_t
99del_warning_pvt(char *);
100
101static config_entry_list_t *
102find_warning_info(char *);
103
104static bool_t
105parseConfigLine(char *buffer);
106
107extern int warn_send(char *, char *);
108
109extern int kwarnd_debug;
110
111cred_warning_list_t *
112find_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
129bool_t
130kwarn_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
213bool_t
214kwarn_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
241static bool_t
242del_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
269bool_t
270loadConfigFile(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 */
297static bool_t
298cmp_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
346static bool_t
347parseConfigLine(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
590static config_entry_list_t *
591find_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 */
616static FILE *
617safe_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
675static uid_t krb5_cc_uid;
676
677void
678set_warnd_uid(uid_t uid)
679{
680	/*
681	 * set the value of krb5_cc_uid, so it can be retrieved when
682	 * app_krb5_user_uid() is called by the underlying mechanism libraries.
683	 */
684	if (kwarnd_debug)
685		printf("set_warnd_uid called with uid = %d\n", uid);
686	krb5_cc_uid = uid;
687}
688
689uid_t
690app_krb5_user_uid(void)
691{
692
693	/*
694	 * return the value set when one of the kwarnd procedures was
695	 * entered. This is the value of the uid under which the
696	 * underlying mechanism library must operate in order to
697	 * get the user's credentials. This call is necessary since
698	 * kwarnd runs as root and credentials are many times stored
699	 * in files and directories specific to the user
700	 */
701	if (kwarnd_debug)
702		printf("app_krb5_user_uid called and returning uid = %d\n",
703		    krb5_cc_uid);
704	return (krb5_cc_uid);
705}
706
707
708static bool_t
709getpruid(char *pr, uid_t *uid)
710{
711	char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
712	struct passwd *pw;
713
714	rcp1 = strdup(pr);
715	if (!rcp1)
716		return (FALSE);
717	rcp2 = strtok(rcp1, "@");
718	rcp3 = strtok(rcp2, "/");
719
720	if (rcp3) {
721		pw = getpwnam(rcp3);
722		*uid = pw->pw_uid;
723		free(rcp1);
724		return (TRUE);
725	}
726
727	free(rcp1);
728	return (FALSE);
729}
730
731
732static krb5_error_code
733renew_creds(
734	char *princ,
735	time_t *new_exp_time) /* out */
736{
737	krb5_creds my_creds;
738	krb5_error_code code = 0;
739	struct k5_data k5;
740
741	uid_t saved_u = app_krb5_user_uid();
742	uid_t u;
743
744	if (kwarnd_debug)
745		printf("renew start: uid=%d\n", app_krb5_user_uid());
746
747	if (!getpruid(princ, &u)) {
748		if (kwarnd_debug)
749			printf("renew: getpruid failed, princ='%s'\n",
750			    princ ? princ : "<null>");
751
752		return (-1); /* better err num? */
753	}
754
755	set_warnd_uid(u);
756
757	(void) memset(&my_creds, 0, sizeof (my_creds));
758	(void) memset(&k5, 0, sizeof (k5));
759
760	if (code = krb5_init_context(&k5.ctx)) {
761		com_err(progname, code,
762			gettext("while initializing Kerberos 5 library"));
763		goto out;
764	}
765
766	if ((code = krb5_cc_default(k5.ctx, &k5.cc))) {
767		com_err(progname, code,
768			gettext("while getting default ccache"));
769		goto out;
770
771	}
772
773	if ((code = krb5_parse_name(k5.ctx, princ,
774				    &k5.me))) {
775		com_err(progname, code, gettext("when parsing name %s"),
776			princ);
777		goto out;
778	}
779
780	if ((code = krb5_get_renewed_creds(k5.ctx, &my_creds, k5.me, k5.cc,
781					NULL))) {
782		com_err(progname, code, gettext("while renewing creds"));
783		goto out;
784	}
785
786	if (code = krb5_cc_initialize(k5.ctx, k5.cc, k5.me)) {
787		com_err(progname, code, gettext("when initializing cache %s"),
788			"defcc");
789		goto out;
790	}
791
792	if (code = krb5_cc_store_cred(k5.ctx, k5.cc, &my_creds)) {
793		com_err(progname, code, gettext("while storing credentials"));
794		goto out;
795	}
796
797	/* "return" new expire time */
798	*new_exp_time = my_creds.times.endtime;
799
800out:
801	krb5_free_cred_contents(k5.ctx, &my_creds);
802
803	if (k5.name)
804		krb5_free_unparsed_name(k5.ctx, k5.name);
805	if (k5.me)
806		krb5_free_principal(k5.ctx, k5.me);
807	if (k5.cc)
808		krb5_cc_close(k5.ctx, k5.cc);
809	if (k5.ctx)
810		krb5_free_context(k5.ctx);
811
812	set_warnd_uid(saved_u);
813
814	if (kwarnd_debug)
815		printf("renew end: code=%s, uid=%d\n", error_message(code),
816		    app_krb5_user_uid());
817
818	return (code);
819}
820
821static bool_t
822loggedon(char *name)
823{
824	register struct utmpx *ubuf;
825	char    *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
826
827	/*
828	 * strip any realm or instance from principal so we can match
829	 * against unix userid.
830	 */
831	rcp1 = strdup(name);
832	if (!rcp1)
833		return (FALSE);
834	rcp2 = strtok(rcp1, "@");
835	rcp3 = strtok(rcp2, "/");
836
837	/*
838	 * Scan through the "utmpx" file for the
839	 * entry for the person we want to send to.
840	 */
841
842	setutxent();
843	while ((ubuf = getutxent()) != NULL) {
844		if (ubuf->ut_type == USER_PROCESS) {
845			if (strncmp(rcp3, ubuf->ut_user,
846				    sizeof (ubuf->ut_user)) == 0) {
847				free(rcp1);
848				endutxent();
849				return (TRUE);
850
851			}
852		}
853	}
854	free(rcp1);
855	endutxent();
856
857	if (kwarnd_debug)
858		printf("loggedon: returning false for user `%s'\n", rcp1);
859
860	return (FALSE);
861}
862
863/*
864 * main loop to check the cred warning list and send the warnings
865 * the appropriate location based on warn.conf or auto-renew creds.
866 */
867
868void
869kwarnd_check_warning_list(void)
870{ /* func */
871	cred_warning_list_t	*cw;  /* cred warning */
872	config_entry_list_t	*ce;  /* config entry */
873	time_t			now;
874	int			minutes;
875	char			buff[256];
876	char			cmdline[256];
877	FILE			*fp;
878	char			*subj = "Kerberos credentials expiring";
879	char			*renew_subj = "Kerberos credentials renewed";
880
881	if (kwarnd_debug)
882		printf("check list: start: uid=%d, cw list=%p\n",
883		    app_krb5_user_uid(), cred_warning_list);
884
885	while (1) {
886		(void) poll(NULL, NULL, 60000);
887
888		for (cw = cred_warning_list;
889			cw != NULL;
890			cw = cw->next) {
891			int send_msg = 0;
892
893			time(&now);
894			if (now >= cw->cred_warn_time) {
895				int renew_attempted = 0;
896				int renew_failed = 0;
897				int renew_tooclose = 0;
898
899				if (kwarnd_debug)
900					printf("checklist: now >= warn_t\n");
901
902				ce = find_warning_info(cw->warn_name);
903				minutes = (cw->cred_exp_time -
904					now + 59) / 60;
905
906				if (kwarnd_debug)
907					printf("checklist: where_to=%s\n",
908					    ce->where_to ?
909					    ce->where_to : "null");
910
911				if (ce->renew &&
912				    loggedon(cw->warn_name)) {
913					krb5_error_code code;
914					time_t new_exp_time;
915
916					renew_attempted = 1;
917					code = renew_creds(
918						cw->warn_name,
919						&new_exp_time);
920					if (!code) {
921						/* krb5 api renew success */
922
923						/*
924						 * So we had api success
925						 * but the new exp time
926						 * is same as current one
927						 * so we are too close
928						 * to Renewable_life time.
929						 */
930						if (cw->cred_exp_time
931						    == new_exp_time) {
932							renew_tooclose = 1;
933							if (kwarnd_debug)
934								printf(
935		"checklist: new expire time same as old expire time\n");
936
937							if (ce->log_failure) {
938								send_msg = 1;
939								snprintf(buff,
940								sizeof (buff),
941					gettext("%s:\r\nYour kerberos"
942					" credentials have not been renewed"
943					" (too close to Renewable_life).\r\n"
944					"Please run kinit(1).\r\n"),
945								cw->warn_name);
946							}
947						} else {
948							/* update times */
949							cw->cred_exp_time =
950								new_exp_time;
951							cw->cred_warn_time =
952							    new_exp_time -
953							    ce->seconds_to_warn;
954						}
955
956						if (kwarnd_debug)
957							printf(
958						    "check list: new_w_t=%d\n",
959						    cw->cred_warn_time);
960
961						if (!renew_tooclose &&
962						    ce->log_success) {
963							if (kwarnd_debug)
964								printf(
965						"check list: log success\n");
966
967							send_msg = 1;
968							snprintf(buff,
969								sizeof (buff),
970						gettext("%s:\r\nYour kerberos"
971					" credentials have been renewed.\r\n"),
972								cw->warn_name);
973						}
974
975					}  /* !(code) */
976
977					if (!renew_tooclose && code &&
978					    ce->log_failure) {
979						if (kwarnd_debug)
980							printf(
981						"check list: log FAIL\n");
982
983						send_msg = 1;
984						snprintf(buff,
985							sizeof (buff),
986					    gettext("%s:\r\nYour kerberos"
987				" credentials failed to be renewed (%s).\r\n"),
988							cw->warn_name,
989							error_message(code));
990					}
991					renew_failed = code ? 1 : 0;
992
993				} else if (minutes > 0) {
994					send_msg = 1;
995					snprintf(buff, sizeof (buff),
996					gettext("%s:\r\nyour kerberos"
997					" credentials expire in less than"
998					" %d minutes.\r\n"),
999					cw->warn_name,
1000					minutes);
1001				} else {
1002					send_msg = 1;
1003					snprintf(buff, sizeof (buff),
1004					gettext("%s:\r\nyour kerberos"
1005					" credentials have expired.\r\n"),
1006					cw->warn_name);
1007				}
1008
1009				if (kwarnd_debug)
1010					printf("checklist: send_msg=%d\n",
1011					    send_msg);
1012				if (!send_msg)
1013					goto del_warning;
1014
1015				if (strncmp(ce->where_to,
1016					    "mail", sizeof ("mail")) == 0) {
1017					char *argv[3];
1018
1019					argv[0] = MAIL;
1020					(void) snprintf(cmdline,
1021							sizeof (cmdline),
1022							"%s",
1023							ce->email);
1024					argv[1] = cmdline;
1025					argv[2] = NULL;
1026
1027					fp = safe_popen_w(MAILPATH, argv);
1028
1029					if (fp) {
1030
1031						(void) fprintf(fp,
1032						"To: %s\nSubject: %s\n\n%s\n",
1033							    ce->email,
1034							    renew_attempted
1035							    ? renew_subj : subj,
1036							    buff);
1037
1038					    fclose(fp);
1039					} else {
1040					    syslog(LOG_ERR,
1041						gettext("could not fork "
1042						"mail program to e-mail "
1043						"warning to %s\n"),
1044						cmdline);
1045					}
1046
1047				} else if (strncmp(ce->where_to,
1048						"terminal",
1049						sizeof ("terminal")) == 0) {
1050
1051					warn_send(cw->warn_name,
1052						buff);
1053
1054				} else if (send_msg && strncmp(ce->where_to,
1055							    "syslog",
1056						sizeof ("syslog")) == 0) {
1057					syslog(LOG_NOTICE|LOG_AUTH,
1058					    "%s",
1059					    buff);
1060#if 0
1061				} else if (strncmp(ce->where_to,
1062						"snmp",
1063						sizeof ("snmp")) == 0) {
1064#endif
1065				} else {
1066					if (kwarnd_debug)
1067						printf(
1068						"unknown msg method=`%s'\n",
1069						ce->where_to);
1070
1071					exit(1);
1072				}
1073
1074			del_warning:
1075				if (!renew_attempted || renew_failed ||
1076				    renew_tooclose) {
1077					if (del_warning_pvt(cw->warn_name)
1078					    == TRUE) {
1079
1080						if (kwarnd_debug)
1081							printf(
1082						"check list: del warn succ\n");
1083
1084						break;
1085					} else {
1086						if (kwarnd_debug)
1087							printf(
1088						"could not delete warning\n");
1089
1090						syslog(LOG_ERR, gettext(
1091						"could not delete warning"));
1092
1093						exit(1);
1094					    }
1095					}
1096
1097				} /* if (now) */
1098		} /* for */
1099	} /* while */
1100}  /* func */
1101