OpenSolaris_b135/cmd/krb5/kwarn/kwarnd_proc.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *  RPC server procedures for the usermode daemon kwarnd.
 */

#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <strings.h>
#include <string.h>
#include <sys/param.h>
#include <sys/syslog.h>
#include "kwarnd.h"
#include <rpc/rpc.h>
#include <stdlib.h>
#include <syslog.h>
#include <poll.h>
#include <utmpx.h>
#include <pwd.h>
#include <strings.h>
#include <ctype.h>

#include <k5-int.h>
#include <profile/prof_int.h>
#include <com_err.h>
#include <libintl.h>
#include <krb5.h>

extern char progname[];

struct k5_data
{
	krb5_context ctx;
	krb5_ccache cc;
	krb5_principal me;
	char *name;
};


#define	MAIL		"mail"
#define	MAILPATH	"/usr/bin/mail"
#define	DEFAULT_CONFIG	"* terminal 30m"
#define	CONF_FILENAME	"/etc/krb5/warn.conf"

/* warn.conf info */

typedef struct config_entry_s {
	struct config_entry_s		*next;
	int				seconds_to_warn;
	char				*principal;
	char				*where_to;
	char				*email;
	int				renew;
	int				log_success;
	int				log_failure;
} config_entry_list_t;
static config_entry_list_t		*config_entry_list;

/* list of principals to be warned */

typedef struct cred_warning_list_s {
	struct cred_warning_list_s 	*next;
	WARNING_NAME_T			warn_name;
	time_t				cred_exp_time;
	time_t				cred_warn_time;
	mutex_t				cwm;
} cred_warning_list_t;
static cred_warning_list_t		*cred_warning_list;
static rwlock_t				cred_lock = DEFAULTRWLOCK;

static bool_t
del_warning_pvt(char *);

static config_entry_list_t *
find_warning_info(char *);

static bool_t
parseConfigLine(char *buffer);

extern int warn_send(char *, char *);

extern int kwarnd_debug;

cred_warning_list_t *
find_cred_warning(WARNING_NAME_T warn_name)
{
	cred_warning_list_t	*cw;
	if (!cred_warning_list)
		return (NULL);
	for (cw = cred_warning_list; cw != NULL; cw = cw->next) {
		if (strcmp(warn_name, cw->warn_name) != 0)
			continue;
		return (cw);
	}
	return (NULL);
}

/*
 * add a principal to the principal warning list
 */

bool_t
kwarn_add_warning_1_svc(kwarn_add_warning_arg *argp,
			kwarn_add_warning_res *res,
			struct svc_req *rqstp)
{
	cred_warning_list_t	*cred_warning;
	config_entry_list_t *config_entry;

	if (kwarnd_debug) {
		printf("kwarn_add_warning_1_svc start; cWlist=%p\n",
		    cred_warning_list);

		printf("kwarn_add_warning_1_svc: principal %s",
		    argp->warning_name);
		printf(" exp time: %d\n", argp->cred_exp_time);
	}

/*
 *  if there is no entry in the config file that matches the principal to
 *  be added to the warning list, return true because we are not going to
 *  send a warning for this principal.
 */

	if ((config_entry = find_warning_info(argp->warning_name)) == NULL) {
		if (kwarnd_debug)
			printf(
		"kwarn_add_warning_1_svc find_warn_info: fails, cWlist=%p\n",
				cred_warning_list);

		return (TRUE);
	}

/*
 * see if a warning has already been created for this principal, if so
 * update the warning time.
 */

	rw_wrlock(&cred_lock);
	if (cred_warning = find_cred_warning(argp->warning_name)) {
		rw_unlock(&cred_lock);
		mutex_lock(&cred_warning->cwm);
		cred_warning->cred_exp_time = argp->cred_exp_time;
		cred_warning->cred_warn_time = argp->cred_exp_time
			- config_entry->seconds_to_warn;
		mutex_unlock(&cred_warning->cwm);
	} else {
		cred_warning = (cred_warning_list_t *)malloc(
				sizeof (*cred_warning_list));
		if (cred_warning == NULL) {
			rw_unlock(&cred_lock);
			res->status = 1;
			return (FALSE);
		}
		(void) memset((char *)cred_warning, 0,
			    sizeof (*cred_warning_list));
		cred_warning->cred_exp_time = argp->cred_exp_time;
		cred_warning->cred_warn_time = argp->cred_exp_time
			- config_entry->seconds_to_warn;
		cred_warning->warn_name = strdup(argp->warning_name);
		if (cred_warning->warn_name == NULL) {
			free(cred_warning);
			rw_unlock(&cred_lock);
			res->status = 1;
			return (FALSE);
		}
		mutex_init(&cred_warning->cwm,  USYNC_THREAD, NULL);
		cred_warning->next = cred_warning_list;
		cred_warning_list = cred_warning;
		rw_unlock(&cred_lock);
	}
	res->status = 0;

	if (kwarnd_debug)
		printf(
		"kwarn_add_warning_1_svc end: returns true; cWlist=%p\n",
		cred_warning_list);

	return (TRUE);
}

/*
 * delete a warning request for a given principal
 */

bool_t
kwarn_del_warning_1_svc(kwarn_del_warning_arg *argp,
			kwarn_del_warning_res *res,
			struct svc_req *rqstp)
{
	if (kwarnd_debug)
		printf(gettext("delete principal %s requested\n"),
		    argp->warning_name);

	if (del_warning_pvt(argp->warning_name) == TRUE) {
		res->status = 0;

		if (kwarnd_debug)
			printf(gettext("delete principal %s completed\n"),
			    argp->warning_name);

		return (TRUE);
	} else {
		res->status = 1;

		if (kwarnd_debug)
			printf(gettext("delete principal %s failed\n"),
				argp->warning_name);

		return (TRUE);
	}
}

static bool_t
del_warning_pvt(char *warning_name)
{
	cred_warning_list_t	*cred_warning, *prev;
	rw_wrlock(&cred_lock);
	for (prev = NULL, cred_warning = cred_warning_list;
		cred_warning != NULL; prev = cred_warning,
		cred_warning = cred_warning->next) {
		if (strcmp(cred_warning->warn_name, warning_name) == 0) {
			if (!prev)
				cred_warning_list = cred_warning->next;
			else
				prev->next = cred_warning->next;

			free(cred_warning->warn_name);
			free(cred_warning);
			rw_unlock(&cred_lock);
			return (TRUE);
		}
	}
	rw_unlock(&cred_lock);
	return (FALSE);
}

/*
 * load the warn.conf file into the config_entry list.
 */

bool_t
loadConfigFile(void)
{
	char	buffer[BUFSIZ];
	FILE	*cfgfile;
	bool_t	retval = TRUE;

	if ((cfgfile = fopen(CONF_FILENAME, "r")) == NULL) {
		syslog(LOG_ERR, gettext(
			"could not open config file \"%s\"\n"),
			CONF_FILENAME);
		syslog(LOG_ERR, gettext(
			"using default options \"%s\"\n"),
			DEFAULT_CONFIG);
		retval = parseConfigLine(DEFAULT_CONFIG);
	} else {
		(void) memset(buffer, 0, sizeof (buffer));
		while ((fgets(buffer, BUFSIZ, cfgfile) != NULL) &&
			(retval == TRUE))
			retval = parseConfigLine(buffer);
		fclose(cfgfile);
	}
	return (retval);
}

/*
 * Return TRUE if we get a valid opt and update flags appro.
 */
static bool_t
cmp_renew_opts(char *opt,
	    int *log_success, /* out */
	    int *log_failure) /* out */
{

	if (strncasecmp(opt, "log",
			sizeof ("log")) == 0) {
		*log_success = *log_failure = 1;
	} else if (strncasecmp(opt, "log-success",
			    sizeof ("log-success")) == 0) {
		*log_success = 1;
	} else if (strncasecmp(opt, "log-failure",
			    sizeof ("log-failure")) == 0) {
		*log_failure = 1;
	} else {
		if (kwarnd_debug)
			printf("cmp_renew_opts: renew bad opt=`%s'\n",
			    opt ? opt : "null");
		return (FALSE);
	}

	return (TRUE);
}

/*
 * Make the config_entry item for the config_entry_list, based on
 * buffer.  The formats are
 *
 *    <principal> [renew[:<opt1,...optN>]] syslog|terminal <time>
 *    <principal> [renew[:<opt1,...optN>]] mail <time> <e-mail address>
 *
 * where renew opts will be:
 *
 *     log-success
 *		- Log the result of the renew attempt on success using
 *		  the specified method (syslog|terminal|mail)
 *
 *      log-failure
 *		- Log the result of the renew attempt on failure using
 *		  the specified method (syslog|terminal|mail)
 *
 *      log
 *               - Same as specifing both log-failure and log-success
 *
 *		  Note if no log options are given, there will be no logging.
 *
 */

static bool_t
parseConfigLine(char *buffer)
{
	char *principal, *send_to, *emailid, *ends, *tm;
	char			*exptime;
	int			time_mode;
	time_t			etime;
	config_entry_list_t	*config_entry;
	int renew = 0;
	int log_success = 0;
	int log_failure = 0;

	/* ignore comments */
	if (*buffer == '#')
		return (TRUE);

	if (kwarnd_debug)
		printf("parseconf: buffer=%s", buffer);

	/* find end of principal */
	principal = buffer;
	for (send_to = buffer; *send_to && !isspace(*send_to);
		send_to++);

	/* find first non whitespace after principal (start of send_to) */
	if (*send_to) {
		*send_to = '\0';
		send_to++;
		while (*send_to && isspace(*send_to))
			send_to++;
	}

	/* if no send_to, continue, bad entry */
	if (! *send_to)
		return (TRUE);

	/* find end of send_to */
	for (ends = send_to; *ends && !isspace(*ends);
		ends++);
	if (*ends)
		*ends = '\0';


	if (strchr(send_to, ':')) {
		/* we've got renew opts */
		char *st = NULL, *op = NULL;

		op = strdup(send_to);
		if (!op)
			return (FALSE);
		st = strchr(op, ':');
		*st = '\0';

		if (strncasecmp(op, "renew", sizeof ("renew")) == 0) {
			renew = 1;
		} else {
			free(op);
			/* got a ':' but not preceeded w/renew, badent, skip */
			if (kwarnd_debug)
				printf("parseconf: colon badent, skip\n");
			return (TRUE);
		}
		free(op);
		op = NULL;

		st++;
		if (!st || !*st || isspace(*st)) {
			if (kwarnd_debug)
				printf("parseconf: st badent, skip\n");
			/* bad ent, skip */
			return (TRUE);
		}
		if (renew && strchr(st, ',')) {
			while (1) {
				/* loop thru comma seperated list-o-opts */
				char *comma = NULL, *c = NULL, *l = NULL;

				if (st && (comma = strchr(st, ','))) {
					l = strdup(st);
					if (!l)
						return (FALSE);
					c = strchr(l, ',');
					*c = '\0';
					if (!cmp_renew_opts(l, &log_success,
							    &log_failure)) {
						free(l);
						/* badent, skip */
						return (TRUE);
					}
					free(l);
					l = NULL;

					st = comma;
					st++;
				} else {
					if (st) {
						if (!cmp_renew_opts(st,
							    &log_success,
							    &log_failure)) {
							/* badent, skip */
							return (TRUE);
						}
					}
					break;
				}
			} /* while */
		} else if (st) {
			/* we just have one opt */
			if (!cmp_renew_opts(st, &log_success, &log_failure)) {
				/* badent, skip */
				return (TRUE);
			}
		}

		/* if send_to is "renew", note it and refind send_to */
	} else if (strncasecmp(send_to, "renew",
			    sizeof ("renew")) == 0) {
		renew = 1;

	}

	if (kwarnd_debug) {
		printf("parseconf: renew=%d, log failure=%d, log success=%d\n",
		    renew, log_failure, log_success);
	}

	if (renew) {
		/* find first non whitespace after send_to (start of exptime) */
		for (send_to = ends+1; *send_to && isspace(*send_to);
		    send_to++);

		/* if no send_to, continue, bad entry */
		if (! *send_to) {
			if (kwarnd_debug)
				printf("parseconf: no send_to, badent, skip\n");
			return (TRUE);
		}

		/* find end of send_to */
		for (ends = send_to; *ends && !isspace(*ends);
		    ends++);
		if (*ends)
			*ends = '\0';
	}


	/* find first non whitespace after send_to (start of exptime) */
	for (exptime = ends+1; *exptime && isspace(*exptime);
		exptime++);

	/* if no exptime, continue, bad entry */
	if (! *exptime) {
		if (kwarnd_debug)
			printf("parseconf: no exptime, badent, skip\n");
		return (TRUE);
	}

	/* find end of exptime */
	for (ends = exptime; *ends && !isspace(*ends); ends++);

	tm = ends - 1;
	if (*tm == 's')
		time_mode = 1;
	else if (*tm == 'm')
		time_mode = 2;
	else if (*tm == 'h')
		time_mode = 3;
	else
		time_mode = 1;

	if (*tm)
		*tm = '\0';

	if (kwarnd_debug) {
		printf("parseconf: send_to = '%s', exptime='%s'\n",
		    send_to, exptime);
	}

	/* find first non whitespace after exptime (start of emailid) */
	for (emailid = ends+1; *emailid && isspace(*emailid); emailid++);

	/* find end of emailid */
	if (*emailid) {
		for (ends = emailid; *ends && !isspace(*ends);
			ends++);

		if (*ends)
			*ends = '\0';
	}

	/* if send to mail and no mail address, bad entry */
	if ((strcmp(send_to, "mail") == 0) && (!*emailid)) {
		if (kwarnd_debug)
			printf("parseconf: returns true; no mail addr\n");

		syslog(LOG_ERR, gettext("missing mail address"
			" in config entry: \n%s %s %s "
			" cannot mail warning"), principal,
			send_to, exptime);
		return (TRUE);
	}

	/* create an entry */
	config_entry = (config_entry_list_t *)
		malloc(sizeof (*config_entry_list));
	if (config_entry == NULL)
		return (FALSE);
	(void) memset(config_entry, 0, sizeof (*config_entry_list));
	config_entry->principal = strdup(principal);
	if (config_entry->principal == NULL)
		return (FALSE);
	config_entry->where_to = strdup(send_to);
	if (config_entry->where_to == NULL)
		return (FALSE);
	etime = atol(exptime);
	if (time_mode == 1)
		config_entry->seconds_to_warn = etime;
	else if (time_mode == 2)
		config_entry->seconds_to_warn = etime * 60;
	else if (time_mode == 3)
		config_entry->seconds_to_warn = etime * 60 * 60;

	if (*emailid) {
		config_entry->email = strdup(emailid);
		if (config_entry->email == NULL)
			return (FALSE);
	}

	config_entry->renew = renew;
	config_entry->log_success = log_success;
	config_entry->log_failure = log_failure;
	config_entry->next = config_entry_list;
	config_entry_list = config_entry;
	if (kwarnd_debug)
		printf("parseconf: returns true; celist=%p\n",
		    config_entry_list);

	return (TRUE);
}

/*
 * find a specific warn.conf entry.
 */

static config_entry_list_t *
find_warning_info(char *principal)
{
	config_entry_list_t	*config_entry;
	/* look for a specific entry */
	for (config_entry = config_entry_list; config_entry;
		config_entry = config_entry->next) {
		if (strcmp(config_entry->principal, principal) == 0) {
			return (config_entry);
		}
	}
	/* look for a wild card entry */
	for (config_entry = config_entry_list; config_entry;
		config_entry = config_entry->next) {
		if (strcmp(config_entry->principal, "*") == 0) {
			return (config_entry);
		}
	}
	/* nothing found */
	return (NULL);

}

/*
 * create a pipe, fork and exec a command,
 */
static FILE *
safe_popen_w(char *path_to_cmd, char **argv)
{

	int fd[2];
	FILE *fp;
	char *envp[2];

	if (pipe(fd) == -1)
		return (NULL);


	switch (fork()) {
	case -1:
		(void) close(fd[0]);
		(void) close(fd[1]);
		return (NULL);

	case 0:
		close(fd[1]);
		/* fd[0] is the end we read from */
		if (fd[0] != 0) {
			close(0);
			dup(fd[0]);
		}
		close(1);
		close(2);
		envp[0] = "PATH=/usr/bin";
		envp[1] = NULL;
#ifdef	DEBUG
		{
			int fd;
			fd = open("/tmp/kwarn.out", O_WRONLY|O_TRUNC|O_CREAT,
				0666);
			if (fd != 1)
				dup(fd);
			if (fd != 2)
				dup(fd);
		}
#endif
		(void) execve(path_to_cmd, argv, envp);
		syslog(LOG_ERR, "warnd: %m");
		_exit(1);

	default:
		close(fd[0]);
		/* fd[1] is the end we write to */

		fp = fdopen(fd[1], "w");

		if (fp == NULL) {
			(void) close(fd[1]);
			return (NULL);
		}
		return (fp);
	}
}


static uid_t krb5_cc_uid;

void
set_warnd_uid(uid_t uid)
{
	/*
	 * set the value of krb5_cc_uid, so it can be retrieved when
	 * app_krb5_user_uid() is called by the underlying mechanism libraries.
	 */
	if (kwarnd_debug)
		printf("set_warnd_uid called with uid = %d\n", uid);
	krb5_cc_uid = uid;
}

uid_t
app_krb5_user_uid(void)
{

	/*
	 * return the value set when one of the kwarnd procedures was
	 * entered. This is the value of the uid under which the
	 * underlying mechanism library must operate in order to
	 * get the user's credentials. This call is necessary since
	 * kwarnd runs as root and credentials are many times stored
	 * in files and directories specific to the user
	 */
	if (kwarnd_debug)
		printf("app_krb5_user_uid called and returning uid = %d\n",
		    krb5_cc_uid);
	return (krb5_cc_uid);
}


static bool_t
getpruid(char *pr, uid_t *uid)
{
	char *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;
	struct passwd *pw;

	rcp1 = strdup(pr);
	if (!rcp1)
		return (FALSE);
	rcp2 = strtok(rcp1, "@");
	rcp3 = strtok(rcp2, "/");

	if (rcp3) {
		pw = getpwnam(rcp3);
		*uid = pw->pw_uid;
		free(rcp1);
		return (TRUE);
	}

	free(rcp1);
	return (FALSE);
}


static krb5_error_code
renew_creds(
	char *princ,
	time_t *new_exp_time) /* out */
{
	krb5_creds my_creds;
	krb5_error_code code = 0;
	struct k5_data k5;

	uid_t saved_u = app_krb5_user_uid();
	uid_t u;

	if (kwarnd_debug)
		printf("renew start: uid=%d\n", app_krb5_user_uid());

	if (!getpruid(princ, &u)) {
		if (kwarnd_debug)
			printf("renew: getpruid failed, princ='%s'\n",
			    princ ? princ : "<null>");

		return (-1); /* better err num? */
	}

	set_warnd_uid(u);

	(void) memset(&my_creds, 0, sizeof (my_creds));
	(void) memset(&k5, 0, sizeof (k5));

	if (code = krb5_init_context(&k5.ctx)) {
		com_err(progname, code,
			gettext("while initializing Kerberos 5 library"));
		goto out;
	}

	if ((code = krb5_cc_default(k5.ctx, &k5.cc))) {
		com_err(progname, code,
			gettext("while getting default ccache"));
		goto out;

	}

	if ((code = krb5_parse_name(k5.ctx, princ,
				    &k5.me))) {
		com_err(progname, code, gettext("when parsing name %s"),
			princ);
		goto out;
	}

	if ((code = krb5_get_renewed_creds(k5.ctx, &my_creds, k5.me, k5.cc,
					NULL))) {
		com_err(progname, code, gettext("while renewing creds"));
		goto out;
	}

	if (code = krb5_cc_initialize(k5.ctx, k5.cc, k5.me)) {
		com_err(progname, code, gettext("when initializing cache %s"),
			"defcc");
		goto out;
	}

	if (code = krb5_cc_store_cred(k5.ctx, k5.cc, &my_creds)) {
		com_err(progname, code, gettext("while storing credentials"));
		goto out;
	}

	/* "return" new expire time */
	*new_exp_time = my_creds.times.endtime;

out:
	krb5_free_cred_contents(k5.ctx, &my_creds);

	if (k5.name)
		krb5_free_unparsed_name(k5.ctx, k5.name);
	if (k5.me)
		krb5_free_principal(k5.ctx, k5.me);
	if (k5.cc)
		krb5_cc_close(k5.ctx, k5.cc);
	if (k5.ctx)
		krb5_free_context(k5.ctx);

	set_warnd_uid(saved_u);

	if (kwarnd_debug)
		printf("renew end: code=%s, uid=%d\n", error_message(code),
		    app_krb5_user_uid());

	return (code);
}

static bool_t
loggedon(char *name)
{
	register struct utmpx *ubuf;
	char    *rcp1 = NULL, *rcp2 = NULL, *rcp3 = NULL;

	/*
	 * strip any realm or instance from principal so we can match
	 * against unix userid.
	 */
	rcp1 = strdup(name);
	if (!rcp1)
		return (FALSE);
	rcp2 = strtok(rcp1, "@");
	rcp3 = strtok(rcp2, "/");

	/*
	 * Scan through the "utmpx" file for the
	 * entry for the person we want to send to.
	 */

	setutxent();
	while ((ubuf = getutxent()) != NULL) {
		if (ubuf->ut_type == USER_PROCESS) {
			if (strncmp(rcp3, ubuf->ut_user,
				    sizeof (ubuf->ut_user)) == 0) {
				free(rcp1);
				endutxent();
				return (TRUE);

			}
		}
	}
	free(rcp1);
	endutxent();

	if (kwarnd_debug)
		printf("loggedon: returning false for user `%s'\n", rcp1);

	return (FALSE);
}

/*
 * main loop to check the cred warning list and send the warnings
 * the appropriate location based on warn.conf or auto-renew creds.
 */

void
kwarnd_check_warning_list(void)
{ /* func */
	cred_warning_list_t	*cw;  /* cred warning */
	config_entry_list_t	*ce;  /* config entry */
	time_t			now;
	int			minutes;
	char			buff[256];
	char			cmdline[256];
	FILE			*fp;
	char			*subj = "Kerberos credentials expiring";
	char			*renew_subj = "Kerberos credentials renewed";

	if (kwarnd_debug)
		printf("check list: start: uid=%d, cw list=%p\n",
		    app_krb5_user_uid(), cred_warning_list);

	while (1) {
		(void) poll(NULL, NULL, 60000);

		for (cw = cred_warning_list;
			cw != NULL;
			cw = cw->next) {
			int send_msg = 0;

			time(&now);
			if (now >= cw->cred_warn_time) {
				int renew_attempted = 0;
				int renew_failed = 0;
				int renew_tooclose = 0;

				if (kwarnd_debug)
					printf("checklist: now >= warn_t\n");

				ce = find_warning_info(cw->warn_name);
				minutes = (cw->cred_exp_time -
					now + 59) / 60;

				if (kwarnd_debug)
					printf("checklist: where_to=%s\n",
					    ce->where_to ?
					    ce->where_to : "null");

				if (ce->renew &&
				    loggedon(cw->warn_name)) {
					krb5_error_code code;
					time_t new_exp_time;

					renew_attempted = 1;
					code = renew_creds(
						cw->warn_name,
						&new_exp_time);
					if (!code) {
						/* krb5 api renew success */

						/*
						 * So we had api success
						 * but the new exp time
						 * is same as current one
						 * so we are too close
						 * to Renewable_life time.
						 */
						if (cw->cred_exp_time
						    == new_exp_time) {
							renew_tooclose = 1;
							if (kwarnd_debug)
								printf(
		"checklist: new expire time same as old expire time\n");

							if (ce->log_failure) {
								send_msg = 1;
								snprintf(buff,
								sizeof (buff),
					gettext("%s:\r\nYour kerberos"
					" credentials have not been renewed"
					" (too close to Renewable_life).\r\n"
					"Please run kinit(1).\r\n"),
								cw->warn_name);
							}
						} else {
							/* update times */
							cw->cred_exp_time =
								new_exp_time;
							cw->cred_warn_time =
							    new_exp_time -
							    ce->seconds_to_warn;
						}

						if (kwarnd_debug)
							printf(
						    "check list: new_w_t=%d\n",
						    cw->cred_warn_time);

						if (!renew_tooclose &&
						    ce->log_success) {
							if (kwarnd_debug)
								printf(
						"check list: log success\n");

							send_msg = 1;
							snprintf(buff,
								sizeof (buff),
						gettext("%s:\r\nYour kerberos"
					" credentials have been renewed.\r\n"),
								cw->warn_name);
						}

					}  /* !(code) */

					if (!renew_tooclose && code &&
					    ce->log_failure) {
						if (kwarnd_debug)
							printf(
						"check list: log FAIL\n");

						send_msg = 1;
						snprintf(buff,
							sizeof (buff),
					    gettext("%s:\r\nYour kerberos"
				" credentials failed to be renewed (%s).\r\n"),
							cw->warn_name,
							error_message(code));
					}
					renew_failed = code ? 1 : 0;

				} else if (minutes > 0) {
					send_msg = 1;
					snprintf(buff, sizeof (buff),
					gettext("%s:\r\nyour kerberos"
					" credentials expire in less than"
					" %d minutes.\r\n"),
					cw->warn_name,
					minutes);
				} else {
					send_msg = 1;
					snprintf(buff, sizeof (buff),
					gettext("%s:\r\nyour kerberos"
					" credentials have expired.\r\n"),
					cw->warn_name);
				}

				if (kwarnd_debug)
					printf("checklist: send_msg=%d\n",
					    send_msg);
				if (!send_msg)
					goto del_warning;

				if (strncmp(ce->where_to,
					    "mail", sizeof ("mail")) == 0) {
					char *argv[3];

					argv[0] = MAIL;
					(void) snprintf(cmdline,
							sizeof (cmdline),
							"%s",
							ce->email);
					argv[1] = cmdline;
					argv[2] = NULL;

					fp = safe_popen_w(MAILPATH, argv);

					if (fp) {

						(void) fprintf(fp,
						"To: %s\nSubject: %s\n\n%s\n",
							    ce->email,
							    renew_attempted
							    ? renew_subj : subj,
							    buff);

					    fclose(fp);
					} else {
					    syslog(LOG_ERR,
						gettext("could not fork "
						"mail program to e-mail "
						"warning to %s\n"),
						cmdline);
					}

				} else if (strncmp(ce->where_to,
						"terminal",
						sizeof ("terminal")) == 0) {

					warn_send(cw->warn_name,
						buff);

				} else if (send_msg && strncmp(ce->where_to,
							    "syslog",
						sizeof ("syslog")) == 0) {
					syslog(LOG_NOTICE|LOG_AUTH,
					    "%s",
					    buff);
#if 0
				} else if (strncmp(ce->where_to,
						"snmp",
						sizeof ("snmp")) == 0) {
#endif
				} else {
					if (kwarnd_debug)
						printf(
						"unknown msg method=`%s'\n",
						ce->where_to);

					exit(1);
				}

			del_warning:
				if (!renew_attempted || renew_failed ||
				    renew_tooclose) {
					if (del_warning_pvt(cw->warn_name)
					    == TRUE) {

						if (kwarnd_debug)
							printf(
						"check list: del warn succ\n");

						break;
					} else {
						if (kwarnd_debug)
							printf(
						"could not delete warning\n");

						syslog(LOG_ERR, gettext(
						"could not delete warning"));

						exit(1);
					    }
					}

				} /* if (now) */
		} /* for */
	} /* while */
}  /* func */