|
|
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 a
Length: 16775 (0x4187)
Types: TextFile
Names: »aliasfile.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/directors/aliasfile.c«
/* @(#)aliasfile.c 3.16 12/17/88 21:18:18 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* aliasfile.c:
* direct mail using an aliases database stored as key/value pairs
* where the key is a local address and the value is a string
* which can be run through process_field to extract addr
* structures.
*
* Specifications for the aliasfile directing driver:
*
* private attribute data:
* file (string): the name of a file which contains the
* key/value association database.
*
* proto (name): specifies the protocol used in accessing
* the database. Can be one of:
*
* lsearch - performs a linear serach of an ASCII file.
* bsearch - performs a straightforward binary search
* on a sorted file of text lines.
* dbm - use V7/BSD DBM library to perform search.
*
* 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.
*
* 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.
*
* retries (number): specifies how many retries should be
* attempted in opening the file. Retries are useful
* in for a UN*X system that does not have an atomic
* rename system call, where unlink/link must be used
* rendering the database file nonexistent for some
* small period of time.
*
* interval (number): specifies the retry interval. Sleep
* is called with this number between each retry to open
* the database.
*
* private attribute flags:
* reopen: if set, then reopen the database for each call
* to the directing driver, and close before each return.
* This is necessary for systems that would not otherwise
* have a sufficient number of available file descriptors
* for all of their routing and directing needs.
* optional: if set, then if the open fails, assume an empty
* alias file.
* tryagain: if set, then if the open fails, try again on a
* later spool directory queue run.
*/
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "defs.h"
#include "../smail.h"
#include "../dys.h"
#include "../smailconf.h"
#include "../parse.h"
#include "../addr.h"
#include "../field.h"
#include "../log.h"
#include "../direct.h"
#include "../exitcodes.h"
#include "../lookup.h"
#include "aliasfile.h"
#ifndef DEPEND
# include "../extern.h"
# include "../error.h"
# include "../debug.h"
#endif
/* functions local to this file */
static int alias_secure();
\f
/*
* dtd_aliasfile - direct using aliases database
*/
/*ARGSUSED*/
struct addr *
dtd_aliasfile(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 */
struct aliasfile_private *priv; /* private data area */
char *error; /* error message */
if (! do_aliasing) {
return in;
}
priv = (struct aliasfile_private *)dp->private;
DEBUG(DBG_DRIVER_HI, "dtd_aliasfile called\n");
if (priv->database == NULL) {
struct stat statbuf; /* get stat of database */
int ret;
if (dp->flags & ALIAS_OPENFAIL) {
ret = FILE_FAIL;
} else if (dp->flags & ALIAS_OPENAGAIN) {
ret = FILE_AGAIN;
} else {
if (priv->error_text) {
xfree(priv->error_text);
}
ret = open_database(priv->file, priv->proto,
priv->retries, priv->interval,
&statbuf, &priv->database, &error);
}
if (ret != FILE_SUCCEED) {
struct error *err;
if (ret == FILE_FAIL) {
dp->flags |= ALIAS_OPENFAIL;
}
if (ret == FILE_NOMATCH) {
dp->flags |= ALIAS_OPENFAIL;
error = "Database not found";
}
if (ret == FILE_AGAIN) {
dp->flags |= ALIAS_OPENAGAIN;
} else if (dp->flags & ALIAS_OPTIONAL) {
return in; /* optional and not found */
}
if (! priv->error_text) {
priv->error_text = COPY_STRING(error);
}
/*
* ERR_112 - failed to open alias database
*
* DESCRIPTION
* open_database() failed to open an alias database. The
* error encountered should be stored in errno.
*
* ACTIONS
* Defer all of the input addresses as configuration
* errors. Do not call it a configuration error if
* `tryagain' is set or open_database returned DB_AGAIN.
*
* RESOLUTION
* The postmaster should check the director entry against
* the database he wishes to use.
*/
error = xprintf("director %s: alias database %s: %s",
dp->name, priv->file, priv->error_text);
err = note_error((dp->flags & ALIAS_TRYAGAIN) || ret == DB_AGAIN?
ERR_CONFERR|ERR_112:
ERR_112,
error);
insert_addr_list(in, defer, err);
return NULL;
}
if (alias_secure(priv, &statbuf)) {
/* check succeeded */
priv->flags_set = ADDR_ALIASTYPE;
} else {
/* security check failed, don't secure secure flag */
priv->flags_set = ADDR_ALIASTYPE|ADDR_CAUTION;
}
}
for (cur = in; cur; cur = next) {
char *value; /* value from lookup_database */
struct addr *list; /* list returned by lookup */
int ret;
next = cur->succ;
ret = lookup_database(priv->database, cur->remainder, &value, &error);
switch (ret) {
struct addr *next_list;
case DB_NOMATCH:
DEBUG2(DBG_DRIVER_MID, "director <%s> did not match name <%s>\n",
dp->name, cur->remainder);
cur->succ = pass;
pass = cur;
break;
case DB_AGAIN:
case DB_FAIL:
case FILE_AGAIN:
case FILE_FAIL:
/*
* ERR_158 - aliasfile lookup error
*
* DESCRIPTION
* database_lookup() returned an error, rather than
* success or not found.
*
* ACTIONS
* Defer the address with a configuration error, for
* DB_FAIL, or without a configuration error, for
* DB_AGAIN.
*
* RESOLUTION
* For DB_FAIL, the postmaster will need to look into
* why the database lookup failed. For DB_AGAIN, a
* later queue run will hopefully take care of the
* problem.
*/
DEBUG3(DBG_DRIVER_LO, "director %s: lookup failure on %s: %s\n",
dp->name, cur->remainder, error);
error = xprintf("director %s: lookup failure on %s: %s",
dp->name, cur->remainder, error);
cur->error =
note_error(ret == DB_AGAIN || ret == FILE_AGAIN?
ERR_CONFERR|ERR_158:
ERR_158,
error);
cur->succ = *defer;
*defer = cur;
break;
case DB_SUCCEED:
DEBUG3(DBG_DRIVER_LO, "director %s: matched %s, aliased to %s\n",
dp->name, cur->remainder, value);
cur->director = dp; /* set the director which matched */
/* get the addr structures */
list = NULL;
error = NULL;
(void) process_field((char *)NULL, value, (char *)NULL,
(char *)NULL, &list, F_ALIAS, &error);
if (error) {
/*
* ERR_113 - alias parsing error
*
* DESCRIPTION
* process_field() found an error while parsing an
* entry from an alias database. The specific error is
* stored in `error'.
*
* ACTIONS
* Fail the address and send to the owner or to the
* postmaster.
*
* RESOLUTION
* The postmaster or alias owner should correct the
* alias database entry.
*/
cur->error =
note_error(ERR_NPOWNER|ERR_113,
xprintf("director %s: error parsing alias: %s",
dp->name, error));
cur->succ = *fail;
*fail = cur;
break;
}
for (; list; list = next_list) {
next_list = list->succ;
list->parent = cur;
list->flags = priv->flags_set;
list->succ = *new;
*new = list;
}
break;
}
}
if (dp->flags&ALIAS_REOPEN) {
close_database(priv->database);
priv->database = NULL;
}
return pass; /* return addrs for next director */
}
/*
* dtv_aliasfile - verify using aliases database
*/
/*ARGSUSED*/
void
dtv_aliasfile(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 addr to process */
struct aliasfile_private *priv; /* private data area */
char *error; /* error message */
if (! do_aliasing) {
/* no aliasing, return all of them */
insert_addr_list(in, retry, (struct error *)NULL);
return;
}
priv = (struct aliasfile_private *)dp->private;
DEBUG(DBG_DRIVER_HI, "dtv_aliasfile called\n");
/* open the database, if it is not already open */
if (priv->database == NULL) {
int ret;
if (dp->flags & ALIAS_OPENFAIL) {
ret = FILE_FAIL;
} else if (dp->flags & ALIAS_OPENAGAIN) {
ret = FILE_AGAIN;
} else {
if (priv->error_text) {
xfree(priv->error_text);
}
ret = open_database(priv->file, priv->proto,
priv->retries, priv->interval,
(struct stat *)NULL, &priv->database,
&error);
}
if (ret != FILE_SUCCEED) {
struct error *err;
if (ret == FILE_FAIL) {
dp->flags |= ALIAS_OPENFAIL;
}
if (ret == FILE_NOMATCH) {
error = "Database not found";
dp->flags |= ALIAS_OPENFAIL;
}
if (ret == FILE_AGAIN) {
dp->flags |= ALIAS_OPENAGAIN;
} else if (dp->flags & ALIAS_OPTIONAL) {
/* optional and not found */
insert_addr_list(in, retry, (struct error *)NULL);
return;
}
if (! priv->error_text) {
priv->error_text = COPY_STRING(error);
}
/*
* ERR_112 - failed to open alias database
*
* DESCRIPTION
* open_database() failed to open an alias database. The
* error encountered should be stored in errno.
*
* ACTIONS
* Defer all of the input addresses as configuration
* errors. Do not call it a configuration error if
* `tryagain' is set or open_database returned DB_AGAIN.
*
* RESOLUTION
* The postmaster should check the director entry against
* the database he wishes to use.
*/
error = xprintf("director %s: alias database %s: %s",
dp->name, priv->file, error);
err = note_error((dp->flags & ALIAS_TRYAGAIN) || ret == DB_AGAIN?
ERR_112:
ERR_CONFERR|ERR_112,
error);
insert_addr_list(in, defer, err);
return;
}
}
/* loop through all of the input addrs */
for (cur = in; cur; cur = next) {
int ret;
char *value;
next = cur->succ;
/* check for a match, without actually using the value */
ret = lookup_database(priv->database, cur->remainder, &value, &error);
switch (ret) {
case DB_NOMATCH:
DEBUG2(DBG_DRIVER_MID, "director <%s> did not match name <%s>\n",
dp->name, cur->remainder);
/* no match */
cur->succ = *retry;
*retry = cur;
break;
case DB_SUCCEED:
DEBUG2(DBG_DRIVER_LO, "director <%s> matched <%s>\n",
dp->name, cur->remainder);
/* match */
cur->succ = *okay;
*okay = cur;
break;
case DB_AGAIN:
case DB_FAIL:
case FILE_AGAIN:
case FILE_FAIL:
/*
* ERR_158 - aliasfile lookup error
*
* DESCRIPTION
* database_lookup() returned an error, rather than
* success or not found.
*
* ACTIONS
* Defer the address with a configuration error, for
* DB_FAIL, or without a configuration error, for
* DB_AGAIN.
*
* RESOLUTION
* For DB_FAIL, the postmaster will need to look into
* why the database lookup failed. For DB_AGAIN, a
* later queue run will hopefully take care of the
* problem.
*/
DEBUG3(DBG_DRIVER_LO, "director %s: lookup failure on %s: %s\n",
dp->name, cur->remainder, error);
error = xprintf("director %s: lookup failure on %s: %s",
dp->name, cur->remainder, error);
cur->error =
note_error(ret == DB_AGAIN || ret == FILE_AGAIN?
ERR_CONFERR|ERR_158:
ERR_158,
error);
cur->succ = *defer;
*defer = cur;
break;
}
}
if (dp->flags&ALIAS_REOPEN) {
close_database(priv->database);
priv->database = NULL;
}
}
/*
* dtb_aliasfile - read the configuration file attributes
*/
char *
dtb_aliasfile(dp, attrs)
struct director *dp; /* director entry being defined */
struct attribute *attrs; /* list of per-driver attributes */
{
char *error;
static struct attr_table aliasfile_attributes[] = {
{ "file", t_string, NULL, NULL, OFFSET(aliasfile_private, file) },
{ "proto", t_string, NULL, NULL, OFFSET(aliasfile_private, proto) },
{ "modemask", t_int, NULL, NULL, OFFSET(aliasfile_private, modemask) },
{ "owners", t_string, NULL, NULL, OFFSET(aliasfile_private, owners) },
{ "owngroups", t_string, NULL, NULL,
OFFSET(aliasfile_private, owngroups) },
{ "retries", t_int, NULL, NULL, OFFSET(aliasfile_private, retries) },
{ "interval", t_int, NULL, NULL, OFFSET(aliasfile_private, interval) },
{ "reopen", t_boolean, NULL, NULL, ALIAS_REOPEN },
{ "optional", t_boolean, NULL, NULL, ALIAS_OPTIONAL },
{ "tryagain", t_boolean, NULL, NULL, ALIAS_TRYAGAIN },
};
static struct attr_table *end_aliasfile_attributes =
ENDTABLE(aliasfile_attributes);
static struct aliasfile_private aliasfile_template = {
NULL, /* file */
"bsearch", /* proto */
000, /* modemask */
NULL, /* owners */
NULL, /* owngroups */
#ifdef HAVE_RENAME
0, /* retries */
#else /* not HAVE_RENAME */
1, /* retries */
#endif /* not HAVE_RENAME */
2, /* interval */
0, /* flags_set -- for internal use */
NULL, /* database -- for internal use */
};
struct aliasfile_private *priv; /* new aliasfile_private structure */
/* copy the template private data */
priv = (struct aliasfile_private *)xmalloc(sizeof(*priv));
(void) memcpy((char *)priv, (char *)&aliasfile_template, sizeof(*priv));
dp->private = (char *)priv;
/* fill in the attributes of the private data */
error = fill_attributes((char *)priv,
attrs,
&dp->flags,
aliasfile_attributes,
end_aliasfile_attributes);
if (error) {
return error;
} else {
return NULL;
}
}
\f
/*
* alias_secure - determine if an alias file is secure
*
* return TRUE if an aliasfile is secure, given the stat structure
* and the constraints in the private structure.
*/
static int
alias_secure(priv, statp)
struct aliasfile_private *priv; /* source of constraints */
struct stat *statp; /* source of data */
{
/* first constraint, bits in modemask must not be set in st_mode */
if (statp->st_mode & priv->modemask) {
return FALSE;
}
/* look through the list of acceptible owners */
if (priv->owners && priv->owners[0]) {
char *temp = priv->owners;
int found = FALSE;
/* step through all of the owners */
for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) {
struct passwd *pw = getpwbyname(temp);
/* ignore names not in the passwd file */
if (pw == NULL) {
continue;
}
/* otherwise check for a match */
if (pw->pw_uid == statp->st_uid) {
found = TRUE;
break;
}
}
if (!found) {
return FALSE;
}
}
/* check list of allowable owning groups */
if (priv->owners && priv->owners[0]) {
char *temp = priv->owngroups;
int found = FALSE;
/* step through all of the owners */
for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) {
struct group *gr = getgrbyname(temp);
/* ignore names not in the passwd file */
if (gr == NULL) {
continue;
}
/* otherwise check for a match */
if (gr->gr_gid == statp->st_gid) {
found = TRUE;
break;
}
}
if (!found) {
return FALSE;
}
}
return TRUE; /* checks out, by default */
}