root/usr/src/cmd/krb5/kwarn/kwarnd_proc.c
/*
 * 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.
 */

/*
 *  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, 0, 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 */