/*
* 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
* 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 "kwarnd.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
{
char *name;
};
/* warn.conf info */
typedef struct config_entry_s {
int seconds_to_warn;
char *principal;
char *where_to;
char *email;
int renew;
int log_success;
int log_failure;
/* list of principals to be warned */
typedef struct cred_warning_list_s {
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;
{
if (!cred_warning_list)
return (NULL);
continue;
return (cw);
}
return (NULL);
}
/*
* add a principal to the principal warning list
*/
{
if (kwarnd_debug) {
printf("kwarn_add_warning_1_svc start; cWlist=%p\n",
printf("kwarn_add_warning_1_svc: principal %s",
argp->warning_name);
}
/*
* 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 (kwarnd_debug)
"kwarn_add_warning_1_svc find_warn_info: fails, cWlist=%p\n",
return (TRUE);
}
/*
* see if a warning has already been created for this principal, if so
* update the warning time.
*/
} else {
sizeof (*cred_warning_list));
if (cred_warning == NULL) {
return (FALSE);
}
(void) memset((char *)cred_warning, 0,
sizeof (*cred_warning_list));
return (FALSE);
}
}
if (kwarnd_debug)
"kwarn_add_warning_1_svc end: returns true; cWlist=%p\n",
return (TRUE);
}
/*
* delete a warning request for a given principal
*/
{
if (kwarnd_debug)
argp->warning_name);
if (kwarnd_debug)
argp->warning_name);
return (TRUE);
} else {
if (kwarnd_debug)
argp->warning_name);
return (TRUE);
}
}
static bool_t
{
if (!prev)
else
return (TRUE);
}
}
return (FALSE);
}
/*
* load the warn.conf file into the config_entry list.
*/
loadConfigFile(void)
{
"could not open config file \"%s\"\n"),
"using default options \"%s\"\n"),
} else {
}
return (retval);
}
/*
* Return TRUE if we get a valid opt and update flags appro.
*/
static bool_t
int *log_success, /* out */
int *log_failure) /* out */
{
sizeof ("log")) == 0) {
sizeof ("log-success")) == 0) {
*log_success = 1;
sizeof ("log-failure")) == 0) {
*log_failure = 1;
} else {
if (kwarnd_debug)
printf("cmp_renew_opts: renew bad opt=`%s'\n",
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
{
char *exptime;
int time_mode;
int renew = 0;
int log_success = 0;
int log_failure = 0;
/* ignore comments */
if (*buffer == '#')
return (TRUE);
if (kwarnd_debug)
/* find end of principal */
send_to++);
/* find first non whitespace after principal (start of send_to) */
if (*send_to) {
*send_to = '\0';
send_to++;
send_to++;
}
/* if no send_to, continue, bad entry */
if (! *send_to)
return (TRUE);
/* find end of send_to */
ends++);
if (*ends)
*ends = '\0';
/* we've got renew opts */
if (!op)
return (FALSE);
*st = '\0';
renew = 1;
} else {
if (kwarnd_debug)
printf("parseconf: colon badent, skip\n");
return (TRUE);
}
st++;
if (kwarnd_debug)
printf("parseconf: st badent, skip\n");
/* bad ent, skip */
return (TRUE);
}
while (1) {
/* loop thru comma seperated list-o-opts */
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++;
} else {
if (st) {
if (!cmp_renew_opts(st,
&log_failure)) {
/* badent, skip */
return (TRUE);
}
}
break;
}
} /* while */
} else if (st) {
/* we just have one opt */
/* badent, skip */
return (TRUE);
}
}
/* if send_to is "renew", note it and refind send_to */
sizeof ("renew")) == 0) {
renew = 1;
}
if (kwarnd_debug) {
printf("parseconf: renew=%d, log failure=%d, log success=%d\n",
}
if (renew) {
/* find first non whitespace after send_to (start of exptime) */
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 */
ends++);
if (*ends)
*ends = '\0';
}
/* find first non whitespace after send_to (start of 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 */
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",
}
/* find first non whitespace after exptime (start of emailid) */
/* find end of emailid */
if (*emailid) {
ends++);
if (*ends)
*ends = '\0';
}
/* if send to mail and no mail address, bad entry */
if (kwarnd_debug)
printf("parseconf: returns true; no mail addr\n");
" in config entry: \n%s %s %s "
" cannot mail warning"), principal,
return (TRUE);
}
/* create an entry */
malloc(sizeof (*config_entry_list));
if (config_entry == NULL)
return (FALSE);
return (FALSE);
return (FALSE);
if (time_mode == 1)
else if (time_mode == 2)
else if (time_mode == 3)
if (*emailid) {
return (FALSE);
}
if (kwarnd_debug)
printf("parseconf: returns true; celist=%p\n",
return (TRUE);
}
/*
* find a specific warn.conf entry.
*/
static config_entry_list_t *
{
/* look for a specific entry */
return (config_entry);
}
}
/* look for a wild card entry */
return (config_entry);
}
}
/* nothing found */
return (NULL);
}
/*
* create a pipe, fork and exec a command,
*/
static FILE *
{
return (NULL);
switch (fork()) {
case -1:
return (NULL);
case 0:
/* fd[0] is the end we read from */
if (fd[0] != 0) {
close(0);
}
close(1);
close(2);
#ifdef DEBUG
{
int fd;
0666);
if (fd != 1)
if (fd != 2)
}
#endif
_exit(1);
default:
/* fd[1] is the end we write to */
return (NULL);
}
return (fp);
}
}
void
{
/*
* 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)
krb5_cc_uid = uid;
}
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",
return (krb5_cc_uid);
}
static bool_t
{
if (!rcp1)
return (FALSE);
if (rcp3) {
return (TRUE);
}
return (FALSE);
}
static krb5_error_code
char *princ,
{
uid_t u;
if (kwarnd_debug)
if (kwarnd_debug)
printf("renew: getpruid failed, princ='%s'\n",
return (-1); /* better err num? */
}
set_warnd_uid(u);
gettext("while initializing Kerberos 5 library"));
goto out;
}
gettext("while getting default ccache"));
goto out;
}
princ);
goto out;
}
NULL))) {
goto out;
}
"defcc");
goto out;
}
goto out;
}
/* "return" new expire time */
out:
if (kwarnd_debug)
return (code);
}
static bool_t
{
/*
* strip any realm or instance from principal so we can match
* against unix userid.
*/
if (!rcp1)
return (FALSE);
/*
* Scan through the "utmpx" file for the
* entry for the person we want to send to.
*/
setutxent();
endutxent();
return (TRUE);
}
}
}
endutxent();
if (kwarnd_debug)
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
{ /* func */
int minutes;
if (kwarnd_debug)
printf("check list: start: uid=%d, cw list=%p\n",
while (1) {
for (cw = cred_warning_list;
int send_msg = 0;
int renew_attempted = 0;
int renew_failed = 0;
int renew_tooclose = 0;
if (kwarnd_debug)
printf("checklist: now >= warn_t\n");
if (kwarnd_debug)
printf("checklist: where_to=%s\n",
renew_attempted = 1;
code = renew_creds(
&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)
"checklist: new expire time same as old expire time\n");
if (ce->log_failure) {
send_msg = 1;
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"),
}
} else {
/* update times */
cw->cred_exp_time =
cw->cred_warn_time =
}
if (kwarnd_debug)
"check list: new_w_t=%d\n",
cw->cred_warn_time);
if (!renew_tooclose &&
ce->log_success) {
if (kwarnd_debug)
"check list: log success\n");
send_msg = 1;
sizeof (buff),
gettext("%s:\r\nYour kerberos"
" credentials have been renewed.\r\n"),
}
} /* !(code) */
if (!renew_tooclose && code &&
ce->log_failure) {
if (kwarnd_debug)
"check list: log FAIL\n");
send_msg = 1;
sizeof (buff),
gettext("%s:\r\nYour kerberos"
" credentials failed to be renewed (%s).\r\n"),
}
} else if (minutes > 0) {
send_msg = 1;
gettext("%s:\r\nyour kerberos"
" credentials expire in less than"
" %d minutes.\r\n"),
minutes);
} else {
send_msg = 1;
gettext("%s:\r\nyour kerberos"
" credentials have expired.\r\n"),
}
if (kwarnd_debug)
printf("checklist: send_msg=%d\n",
send_msg);
if (!send_msg)
goto del_warning;
"mail", sizeof ("mail")) == 0) {
sizeof (cmdline),
"%s",
if (fp) {
"To: %s\nSubject: %s\n\n%s\n",
? renew_subj : subj,
buff);
} else {
gettext("could not fork "
"mail program to e-mail "
"warning to %s\n"),
cmdline);
}
"terminal",
sizeof ("terminal")) == 0) {
buff);
"syslog",
sizeof ("syslog")) == 0) {
"%s",
buff);
#if 0
"snmp",
sizeof ("snmp")) == 0) {
#endif
} else {
if (kwarnd_debug)
"unknown msg method=`%s'\n",
exit(1);
}
if (!renew_attempted || renew_failed ||
== TRUE) {
if (kwarnd_debug)
"check list: del warn succ\n");
break;
} else {
if (kwarnd_debug)
"could not delete warning\n");
"could not delete warning"));
exit(1);
}
}
} /* if (now) */
} /* for */
} /* while */
} /* func */