|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T f
Length: 20901 (0x51a5)
Types: TextFile
Names: »fwdfile.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/directors/fwdfile.c«
/* @(#)fwdfile.c 3.20 3/17/89 17:17:04 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* forward.c:
* direct mail using forward files. In the future, the name of
* the file involved will be configurable. Now, for each addr
* structure representing a local user, an attempt is made to
* open a .forward file in that user's home directory. If the
* attempt succeeds then the contents are read in and transformed
* by process_field into a list of addr structures.
*
* Specifications for the forwardfile directing driver:
*
* private attribute data:
* file (string): specifies the template for the name of the
* forward file. expand_string will be used to build the
* actual name. If this does not begin with `/', it will
* be referenced relative to the smail_lib_dir directory.
*
* modemask (number): specifies bits that are not allowed
* to be set. If some of these bits are set, the
* ALIAS_SECURE flag is not set for the resultant
* addr structures.
*
* caution (string): colon-separated list of users and
* directories considered not secure. Directory names
* can begin with ~user. Secure addresses will not have
* the ADDR_CAUTION bit set in the addr structure.
*
* unsecure (string): colon-separated list of users and
* directories considered very unsecure. Unsecure
* addresses will have the ADDR_UNSECURE bit set
* in the addr structure.
*
* owners (string): list of possible owners for the file.
* For files owned by others, the ADDR_CAUTION bit is
* set in the resultant addr structures.
*
* owngroups (string): like the `owners' attribute except
* that it applies to groups.
*
* prefix (string): a string which must appear at the
* beginning of each address to be matched by this
* director. The prefix is not included in any
* expansions of the $user variable. The prefix is also
* compared independently of case.
*
* suffix (string): a string which must appear at the end of
* each address to be matched by this director. The
* suffix is not included in any expansions of the $user
* variable. The suffix is also compared independently
* of case.
*
* private attribute flags:
* checkowner: if set, the user associated with the forward
* file (remainder from the addr structure) is allowed
* as a possible owner for the file.
*
* forwardto: if set, this is actually a Forward-to driver.
* In this case, the forward file is used only if the
* first line begins with "Forward to " and only the
* first line is actually used.
*
* NOTE: if none of the attributes `owners', `owngroups' or
* `checkowner' is set, no checks are made for ownership
* restrictions.
*/
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "defs.h"
#ifdef UNIX_BSD
# include <sys/file.h>
#endif
#include "../smail.h"
#include "../smailconf.h"
#include "../parse.h"
#include "../addr.h"
#include "../field.h"
#include "../log.h"
#include "../direct.h"
#include "../dys.h"
#include "../exitcodes.h"
#include "fwdfile.h"
#ifndef DEPEND
# include "../extern.h"
# include "../error.h"
# include "../debug.h"
#endif
\f
/*
* dtd_forwardfile - direct using per-user forward files
*/
/*ARGSUSED*/
struct addr *
dtd_forwardfile(dp, in, out, new, defer, fail)
struct director *dp; /* director entry */
struct addr *in; /* input local-form addrs */
struct addr **out; /* output resolved addrs */
struct addr **new; /* output new addrs to resolve */
struct addr **defer; /* addrs to defer to a later time */
struct addr **fail; /* unresolvable addrs */
{
register struct addr *cur; /* temp for processing input */
struct addr *pass = NULL; /* addrs to pass to next director */
struct addr *next; /* next value for cur */
DEBUG(DBG_DRIVER_HI, "dtd_forwardfile called\n");
/* loop over all of the input addrs */
for (cur = in; cur; cur = next) {
next = cur->succ;
if (cur->remainder[0] == '.' || index(cur->remainder, '/')) {
/* do not match hidden files or files in subdirectories */
cur->succ = pass;
pass = cur;
continue;
}
/* fill in any user information */
director_user_info(cur);
/* finish up */
if (finish_forward(dp, cur, new, defer, fail) == FAIL) {
/* if there was no forward file, pass it to the next director */
DEBUG2(DBG_DRIVER_MID, "director %s did not match name %s\n",
dp->name, cur->remainder);
cur->succ = pass;
pass = cur;
}
}
return pass; /* return addrs for next director */
}
/*
* dtv_forwardfile - verify using forward file
*/
/*ARGSUSED*/
void
dtv_forwardfile(dp, in, retry, okay, defer, fail)
struct director *dp; /* director entry */
struct addr *in; /* input local-form addrs */
struct addr **retry; /* output list of unmatched addrs */
struct addr **okay; /* output list of verified addrs */
struct addr **defer; /* temporariliy unverifiable addrs */
struct addr **fail; /* unverified addrs */
{
register struct addr *cur; /* temp for processing input */
struct addr *next; /* next value for cur */
register struct forwardfile_private *priv;
priv = (struct forwardfile_private *)dp->private;
DEBUG(DBG_DRIVER_HI, "dtv_forwardfile called\n");
/* loop over all of the input addrs */
for (cur = in; cur; cur = next) {
char *fn; /* forward filename */
struct stat statbuf; /* stat of forward file */
char *remainder = cur->remainder;
int c_save; /* saved start of suffix */
char *suffixp = NULL; /* start of suffix in remainder */
next = cur->succ;
if (cur->remainder[0] == '.' || index(cur->remainder, '/')) {
/* do not match hidden files or files in subdirectories */
cur->succ = *retry;
*retry = cur;
continue;
}
/* fill in any user information */
director_user_info(cur);
/* remove prefix and suffix from the user name */
if (priv->prefix) {
int len = strlen(priv->prefix);
if (strncmpic(priv->prefix, remainder, len) == 0) {
remainder += len;
} else {
cur->succ = *retry;
*retry = cur;
continue;
}
}
if (priv->suffix) {
int len = strlen(priv->suffix);
int rem_len = strlen(remainder);
if (rem_len > len &&
EQIC(remainder + rem_len - len, priv->suffix))
{
suffixp = remainder + rem_len - len;
c_save = *suffixp;
*suffixp = '\0';
} else {
cur->succ = *retry;
*retry = cur;
continue;
}
}
/* compute the filename */
fn = expand_string(priv->file, (struct addr *)NULL,
cur->flags&ADDR_ISUSER? cur->home: (char *)NULL,
remainder);
if (suffixp) {
*suffixp = c_save;
}
if (fn == NULL) {
DEBUG2(DBG_DRIVER_MID,
"dtv_forwardfile: director %s failed to expand %s\n",
dp->name, priv->file);
cur->succ = *retry;
*retry = cur;
continue;
}
if (fn[0] == '/') {
fn = COPY_STRING(fn);
} else {
fn = make_lib_fn(fn);
}
if (fn == NULL) {
/*
* ERR_114 - forward file not an absolute path, and no
* smail_lib_dir defined
*
* DESCRIPTION
* A path from / (root) was not returned by expand_string()
* for the file attribute, and no smail_lib_dir is defined.
* Since the current directory for smail is not generally
* useful, this can be considered a configuration error.
*
* ACTIONS
* Defer the address as a configuration error.
*
* RESOLUTION
* The postmaster should check the director to ensure that the
* forward-file director will always either fail to expand
* (if a $-variable is not defined in some context) or produce
* an absolute pathname, or that the smail_lib_dir be defined.
*/
cur->error =
note_error(ERR_CONFERR|ERR_114,
xprintf("director %s: %s not an absolute path",
dp->name, priv->file));
cur->succ = *defer;
*defer = cur;
continue;
}
/*
* make sure that the file exists and is a regular non-zero
* length file.
*/
if (stat(fn, &statbuf) < 0 ||
(statbuf.st_mode&S_IFMT) != S_IFREG ||
statbuf.st_size == 0)
{
DEBUG2(DBG_DRIVER_MID,
"dtv_forwardfile: director %s does not match %s\n",
dp->name, cur->remainder);
xfree(fn);
cur->succ = *retry;
*retry = cur;
continue;
}
xfree(fn);
/* address was matched */
cur->succ = *okay;
*okay = cur;
}
}
/*
* dtb_forwardfile - read the configuration file attributes
*/
char *
dtb_forwardfile(dp, attrs)
struct director *dp; /* director entry being defined */
struct attribute *attrs; /* list of per-driver attributes */
{
char *error;
static struct attr_table forwardfile_attributes[] = {
{ "file", t_string, NULL, NULL, OFFSET(forwardfile_private, file) },
{ "modemask", t_int, NULL, NULL,
OFFSET(forwardfile_private, modemask) },
{ "caution", t_string, NULL, NULL,
OFFSET(forwardfile_private, caution) },
{ "unsecure", t_string, NULL, NULL,
OFFSET(forwardfile_private, unsecure) },
{ "owners", t_string, NULL, NULL,
OFFSET(forwardfile_private, owners) },
{ "owngroups", t_string, NULL, NULL,
OFFSET(forwardfile_private, owngroups) },
{ "prefix", t_string, NULL, NULL,
OFFSET(forwardfile_private, prefix) },
{ "suffix", t_string, NULL, NULL,
OFFSET(forwardfile_private, suffix) },
{ "checkowner", t_boolean, NULL, NULL, FWD_CHECKOWNER },
{ "forwardto", t_boolean, NULL, NULL, FWD_FORWARDTO },
};
static struct attr_table *end_forwardfile_attributes =
ENDTABLE(forwardfile_attributes);
static struct forwardfile_private forwardfile_template = {
NULL, /* file */
000, /* modemask */
NULL, /* caution */
NULL, /* unsecure */
NULL, /* owners */
NULL, /* owngroups */
};
struct forwardfile_private *priv; /* new forwardfile_private structure */
/* copy the template private data */
priv = (struct forwardfile_private *)xmalloc(sizeof(*priv));
(void) memcpy((char *)priv, (char *)&forwardfile_template, sizeof(*priv));
dp->private = (char *)priv;
/* fill in the attributes of the private data */
error = fill_attributes((char *)priv,
attrs,
&dp->flags,
forwardfile_attributes,
end_forwardfile_attributes);
if (error) {
return error;
} else {
return NULL;
}
}
/*
* finish_forward - do the real work of finding and processing forward file
*
* return SUCCEED, if a forward file was found and processed,
* FAIL if it was not found or processed. If SUCCEED, `new' will
* be properly filled in.
*/
static int
finish_forward(dp, addr, new, defer, fail)
struct director *dp; /* director entry */
struct addr *addr; /* specific addr structure */
struct addr **new; /* output new addrs to resolve */
struct addr **defer; /* addrs to defer to a later time */
struct addr **fail; /* unresolvable addrs */
{
register struct forwardfile_private *priv;
char *fn; /* name of forward file */
int fd; /* open forward file */
struct stat statbuf; /* buf for fstat call */
char *buf; /* buffer for file contents */
struct addr *cur; /* addrs from process_field */
struct addr *next; /* next addr from process_field */
int flags = ADDR_FWDTYPE; /* addr structure flags value */
char *error; /* error message */
struct str fwdtobuf;
char *remainder = addr->remainder;
int c_save; /* saved start of suffix */
char *suffixp = NULL; /* start of suffix in remainder */
priv = (struct forwardfile_private *)dp->private;
/* remove prefix and suffix from the user name */
if (priv->prefix) {
int len = strlen(priv->prefix);
if (strncmpic(priv->prefix, remainder, len) == 0) {
remainder += len;
} else {
return FAIL;
}
}
if (priv->suffix) {
int len = strlen(priv->suffix);
int rem_len = strlen(remainder);
if (rem_len > len &&
EQIC(remainder + rem_len - len, priv->suffix))
{
suffixp = remainder + rem_len - len;
c_save = *suffixp;
*suffixp = '\0';
} else {
return FAIL;
}
}
/* compute the file name */
fn = expand_string(priv->file, (struct addr *)NULL,
addr->flags&ADDR_ISUSER? addr->home: (char *)NULL,
remainder);
if (suffixp) {
*suffixp = c_save;
}
if (fn == NULL) {
DEBUG2(DBG_DRIVER_MID,
"dtd_forwardfile: director %s failed to expand %s\n",
dp->name, priv->file);
return FAIL;
}
if (fn[0] == '/') {
fn = COPY_STRING(fn);
} else {
fn = make_lib_fn(fn);
}
if (fn == NULL) {
/*
* ERR_114 - forward file not an absolute path, and no
* smail_lib_dir defined
*
* DESCRIPTION
* A path from / (root) was not returned by expand_string()
* for the file attribute, and no smail_lib_dir is defined.
* Since the current directory for smail is not generally
* useful, this can be considered a configuration error.
*
* ACTIONS
* Defer the address as a configuration error.
*
* RESOLUTION
* The postmaster should check the director to ensure that the
* forward-file director will always either fail to expand
* (if a $-variable is not defined in some context) or produce
* an absolute pathname, or that the smail_lib_dir be defined.
*/
addr->error =
note_error(ERR_CONFERR|ERR_114,
xprintf("director %s: %s not an absolute path",
dp->name, priv->file));
addr->succ = *defer;
*defer = addr;
return SUCCEED;
}
DEBUG1(DBG_DRIVER_MID, "dtd_forwardfile: opening forward file %s\n", fn);
fd = open(fn, 0);
if (fd < 0) {
xfree(fn);
DEBUG1(DBG_DRIVER_HI, "dtd_forwardfile: no forward file for %s\n",
addr->remainder);
return FAIL;
}
/* find out how big the file is, malloc that much and read the file */
#ifdef lock_fd_rd_wait
lock_fd_rd_wait(fd);
#endif
(void) fstat(fd, &statbuf);
/* ignore non-files and ignore zero-length forward files */
if ((statbuf.st_mode & S_IFMT) != S_IFREG || statbuf.st_size == 0) {
(void) close(fd);
xfree(fn);
return FAIL;
}
/* set security bits appropriately */
flags |= forward_secure(dp, addr, &statbuf, fn);
if (dp->flags & FWD_FORWARDTO) {
register FILE *f = fdopen(fd, "r");
register int c;
STR_INIT(&fwdtobuf);
while ((c = getc(f)) != EOF && c != '\n') {
STR_NEXT(&fwdtobuf, c);
}
if (ferror(f)) {
write_log(LOG_SYS, "read failed on %s, director=%s", fn, dp->name);
(void) fclose(f);
xfree(fn);
STR_FREE(&fwdtobuf);
return FAIL;
}
(void) fclose(f);
STR_NEXT(&fwdtobuf, '\0');
if (strncmpic(fwdtobuf.p, "forward to ",
sizeof("forward to ") - 1) != 0) {
STR_FREE(&fwdtobuf);
xfree(fn);
return FAIL;
}
buf = fwdtobuf.p + sizeof("forward to ") - 1;
} else {
buf = xmalloc(statbuf.st_size + 1); /* leave room for trailing nul */
if (read(fd, buf, statbuf.st_size) < statbuf.st_size) {
write_log(LOG_SYS, "read failed on %s, director=%s", fn, dp->name);
(void) close(fd);
xfree(buf);
xfree(fn);
return FAIL;
}
(void) close(fd); /* don't need the open file anymore */
buf[statbuf.st_size] = '\0'; /* terminate the buffer */
DEBUG1(DBG_DRIVER_HI, "read %d bytes\n", statbuf.st_size);
}
/* matched a forward file */
DEBUG2(DBG_DRIVER_LO, "director %s: matched %s",
dp->name, addr->remainder);
DEBUG1(DBG_DRIVER_HI, ", forwarded to %s", buf);
DEBUG(DBG_DRIVER_LO, "\n");
addr->director = dp;
/*
* extract addr structures from the file contents, then
* mark them all as coming from the forward director
*/
cur = NULL;
error = NULL;
(void) process_field((char *)NULL, buf, (char *)NULL,
(char *)NULL, &cur, F_ALIAS, &error);
if (error) {
/*
* ERR_115 - forward file parse error
*
* DESCRIPTION
* process_field() found an error while parsing a forward file.
* The specific error is stored in `error'.
*
* ACTIONS
* Fail the address and send to the owner or to the postmaster.
*
* RESOLUTION
* The postmaster or forward file owner should correct the
* forward file.
*/
cur->error = note_error(ERR_NPOWNER|ERR_115,
xprintf("director %s: error parsing %s: %s",
dp->name, error));
cur->succ = *fail;
*fail = cur;
return SUCCEED;
}
if (dp->flags & FWD_FORWARDTO) {
STR_FREE(&fwdtobuf);
} else {
xfree(buf); /* don't need buf anymore */
}
for (; cur; cur = next) {
next = cur->succ;
cur->parent = addr;
cur->flags = flags;
cur->home = addr->home;
cur->uid = addr->uid;
cur->gid = addr->gid;
cur->succ = *new;
*new = cur;
}
DEBUG2(DBG_DRIVER_LO, "director <%s> matched <%s>\n",
dp->name, addr->remainder);
xfree(fn);
return SUCCEED;
}
/*
* forward_secure - determine if a forward file is secure
*
* return ADDR_UNSECURE in the event that the `unsecure' attribute
* affects the forward file, unset ADDR_CAUTION if other constraints
* do not state that the file is not secure.
*/
static int
forward_secure(dp, addr, statp, fn)
struct director *dp; /* source director */
struct addr *addr; /* source addr structure */
struct stat *statp; /* source of file data */
char *fn; /* source of filename data */
{
register struct forwardfile_private *priv;
priv = (struct forwardfile_private *)dp->private;
/* `unsecure' has priority */
if (priv->unsecure) {
char *temp = priv->unsecure;
int found = FALSE;
char *save_dir = rindex(fn, '/'); /* temp put a nul byte here */
*save_dir = '\0';
for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) {
if (secure_match(temp, addr->remainder, fn)) {
found = TRUE;
break;
}
}
*save_dir = '/';
if (found) {
return ADDR_UNSECURE;
}
}
/* check the mode bits */
if (priv->modemask & statp->st_mode) {
return ADDR_CAUTION; /* not secure */
}
/* check the list of caution sources */
if (priv->caution) {
char *temp = priv->caution;
int found = FALSE;
char *save_dir = rindex(fn, '/'); /* temp put a nul byte here */
/* pass the directory name to secure_match */
*save_dir = '\0';
for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) {
if (secure_match(temp, addr->remainder, fn)) {
found = TRUE;
break;
}
}
*save_dir = '/';
if (found) {
return ADDR_CAUTION; /* not secure */
}
}
if (priv->owners) {
if ((dp->flags & FWD_CHECKOWNER) == 0 || addr->uid != statp->st_uid) {
/* checkowner fails and owners attribute exists */
char *temp = priv->owners;
int found = FALSE;
/* check against passwd file uid */
for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) {
struct passwd *pw = getpwbyname(temp);
if (pw == NULL) {
write_log(LOG_SYS, "dtd_forwardfile: no such owner %s\n",
temp);
} else {
if (statp->st_uid == pw->pw_uid) {
found = TRUE;
break;
}
}
}
if (!found) {
return ADDR_CAUTION; /* not secure */
}
}
} else if (dp->flags & FWD_CHECKOWNER) {
/* no owners attribute, so checkowner attribute is the final word */
if (addr->uid != statp->st_uid) {
return ADDR_CAUTION; /* not secure */
}
}
if (priv->owngroups) {
/* verify owning group */
char *temp = priv->owngroups;
int found = FALSE;
/* check against passwd file uid */
for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) {
struct group *gr = getgrbyname(temp);
if (gr == NULL) {
write_log(LOG_SYS, "dtd_forwardfile: no such owngroup %s\n",
temp);
} else {
if (statp->st_gid == gr->gr_gid) {
found = TRUE;
break;
}
}
}
if (!found) {
return ADDR_CAUTION; /* not secure */
}
}
return 0; /* secure by default */
}
/*
* secure_match - determine if a string matches a user or parent directory
*
* return TRUE if the given string matches the user associated with the
* forward file, or the parent directory of the forward file.
*/
static int
secure_match(string, user, dirn)
char *string; /* string to be tested */
char *user; /* associated user name */
char *dirn; /* directory to verify against */
{
if (string[0] == '~' || string[0] == '/') {
/* hmm, looks sort of like a directory name, expand it */
char *dir = expand_string(string, (struct addr *)NULL,
(char *)NULL, (char *)NULL);
if (dir == NULL) {
/* didn't expand, ignore */
return FALSE;
}
/* match if string matches directory */
return EQ(dir, dirn);
}
/* check against the user */
return EQ(string, user);
}