map.c revision d25d47ee62f52e470a91221e64abe838a0af786d
/*
* Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
/*
* Copyright 1996-2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sendmail.h>
#if LDAPMAP
#endif /* LDAPMAP */
#if NDBM
# include <ndbm.h>
# ifdef R_FIRST
# endif /* R_FIRST */
#endif /* NDBM */
#if NEWDB
#endif /* NEWDB */
#if NIS
struct dom_binding; /* forward reference needed on IRIX */
# if NDBM
# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
# endif /* NDBM */
#endif /* NIS */
#if NEWDB
# if DB_VERSION_MAJOR < 2
# endif /* DB_VERSION_MAJOR < 2 */
# if DB_VERSION_MAJOR == 2
# endif /* DB_VERSION_MAJOR == 2 */
# if DB_VERSION_MAJOR > 2
# endif /* DB_VERSION_MAJOR > 2 */
#endif /* NEWDB */
static bool extract_canonname __P((char *, char *, char *, char[], int));
#ifdef LDAPMAP
#endif /* LDAPMAP */
#if NISPLUS
static bool nisplus_getcanonname __P((char *, int, int *));
#endif /* NISPLUS */
#if NIS
static bool nis_getcanonname __P((char *, int, int *));
#endif /* NIS */
#if NETINFO
static bool ni_getcanonname __P((char *, int, int *));
#endif /* NETINFO */
static bool text_getcanonname __P((char *, int, int *));
#if SOCKETMAP
/* XXX arbitrary limit for sanity */
# define SOCKETMAP_MAXL 1000000
#endif /* SOCKETMAP */
/* default error message for trying to open a map in write mode */
#ifdef ENOSYS
# define SM_EMAPCANTWRITE ENOSYS
#else /* ENOSYS */
# ifdef EFTYPE
# define SM_EMAPCANTWRITE EFTYPE
# else /* EFTYPE */
# define SM_EMAPCANTWRITE ENXIO
# endif /* EFTYPE */
#endif /* ENOSYS */
/*
** MAP.C -- implementations for various map classes.
**
** Each map class implements a series of functions:
**
** bool map_parse(MAP *map, char *args)
** Parse the arguments from the config file. Return true
** if they were ok, false otherwise. Fill in map with the
** values.
**
** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
** Look up the key in the given map. If found, do any
** rewriting the map wants (including "args" if desired)
** and return the value. Set *pstat to the appropriate status
** on error and return NULL. Args will be NULL if called
** from the alias routines, although this should probably
** not be relied upon. It is suggested you call map_rewrite
** to return the results -- it takes care of null termination
** and uses a dynamically expanded buffer as needed.
**
** void map_store(MAP *map, char *key, char *value)
** Store the key:value pair in the map.
**
** bool map_open(MAP *map, int mode)
** Open the map for the indicated mode. Mode should
** be either O_RDONLY or O_RDWR. Return true if it
** was opened successfully, false otherwise. If the open
** failed and the MF_OPTIONAL flag is not set, it should
** also print an error. If the MF_ALIAS bit is set
** and this map class understands the @:@ convention, it
** should call aliaswait() before returning.
**
** void map_close(MAP *map)
** Close the map.
**
** This file also includes the implementation for getcanonname.
** It is currently implemented in a pretty ad-hoc manner; it ought
** to be more properly integrated into the map structure.
*/
#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
/*
** MAP_PARSEARGS -- parse config line arguments for database lookup
**
** This is a generic version of the map_parse method.
**
** Parameters:
** map -- the map being initialized.
** ap -- a pointer to the args on the config line.
**
** Returns:
** true -- if everything parsed OK.
** false -- otherwise.
**
** Side Effects:
** null terminates the filename; stores it in map
*/
bool
char *ap;
{
register char *p = ap;
/*
** There is no check whether there is really an argument,
** but that's not important enough to warrant extra code.
*/
for (;;)
{
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'N':
break;
case 'O':
break;
case 'o':
break;
case 'f':
break;
case 'm':
break;
case 'A':
break;
case 'q':
break;
case 'a':
break;
case 'T':
break;
case 'k':
continue;
map->map_keycolnm = p;
break;
case 'v':
continue;
map->map_valcolnm = p;
break;
case 'z':
if (*++p != '\\')
map->map_coldelim = *p;
else
{
switch (*++p)
{
case 'n':
break;
case 't':
break;
default:
}
}
break;
case 't':
break;
case 'S':
map->map_spacesub = *++p;
break;
case 'D':
break;
default:
break;
}
p++;
if (*p != '\0')
*p++ = '\0';
}
if (*p != '\0')
{
p++;
if (*p != '\0')
*p++ = '\0';
}
p++;
if (*p != '\0')
{
syserr("No file name for %s map %s",
return false;
}
return true;
}
/*
** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
**
** It also adds the map_app string. It can be used as a utility
** in the map_lookup method.
**
** Parameters:
** map -- the map that causes this.
** s -- the string to rewrite, NOT necessarily null terminated.
** slen -- the length of s.
** av -- arguments to interpolate into buf.
**
** Returns:
** Pointer to rewritten result. This is static data that
** should be copied if it is to be saved!
*/
char *
register const char *s;
char **av;
{
register char *bp;
register char c;
char **avp;
register char *ap;
size_t l;
{
sm_dprintf(" (nullv)");
else
{
}
sm_dprintf("\n");
}
/* count expected size of output (can safely overestimate) */
{
const char *sp = s;
while (l-- > 0 && (c = *sp++) != '\0')
{
if (c != '%')
continue;
if (l-- <= 0)
break;
c = *sp++;
continue;
continue;
continue;
}
}
{
/* need to malloc additional space */
}
{
/* assert(len > slen); */
}
else
{
while (slen-- > 0 && (c = *s++) != '\0')
{
if (c != '%')
{
if (len-- <= 1)
break;
*bp++ = c;
continue;
}
if (slen-- <= 0 || (c = *s++) == '\0')
c = '%';
if (c == '%')
goto pushc;
{
if (len-- <= 1)
break;
*bp++ = '%';
goto pushc;
}
continue;
continue;
/* transliterate argument into output string */
*bp++ = c;
}
}
else
*bp = '\0';
return buf;
}
/*
** INITMAPS -- rebuild alias maps
**
** Parameters:
** none.
**
** Returns:
** none.
*/
void
initmaps()
{
#if XDEBUG
checkfd012("entering initmaps");
#endif /* XDEBUG */
#if XDEBUG
checkfd012("exiting initmaps");
#endif /* XDEBUG */
}
/*
** MAP_INIT -- rebuild a map
**
** Parameters:
** s -- STAB entry: if map: try to rebuild
** unused -- unused variable
**
** Returns:
** none.
**
** Side Effects:
** will close already open rebuildable map.
*/
/* ARGSUSED1 */
static void
register STAB *s;
int unused;
{
/* has to be a map */
return;
return;
sm_dprintf("map_init(%s:%s, %s)\n",
{
sm_dprintf("\tnot rebuildable\n");
return;
}
/* if already open, close it (for nested open) */
{
}
(void) rebuildaliases(map, false);
return;
}
/*
** OPENMAP -- open a map
**
** Parameters:
** map -- map to open (it must not be open).
**
** Returns:
** whether open succeeded.
*/
bool
{
bool restore = false;
bool savequick = QuickAbort;
int saveerrors = Errors;
return false;
/* better safe than sorry... */
return true;
/* Don't send a map open error out via SMTP */
if ((OnlyOneError || QuickAbort) &&
{
restore = true;
HoldErrs = true;
QuickAbort = false;
}
errno = 0;
{
sm_dprintf("openmap()\t%s:%s %s: valid\n",
}
else
{
sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
{
extern MAPCLASS BogusMapClass;
}
else
{
/* don't try again */
}
}
if (restore)
{
Errors = saveerrors;
}
}
/*
** CLOSEMAPS -- close all open maps opened by the current pid.
**
** Parameters:
** bogus -- only close bogus maps.
**
** Returns:
** none.
*/
void
bool bogus;
{
}
/*
** MAP_CLOSE -- close a map opened by the current pid.
**
** Parameters:
** s -- STAB entry: if map: try to close
** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
**
** Returns:
** none.
*/
/* ARGSUSED1 */
static void
register STAB *s;
int bogus; /* int because of stabapply(), used as bool */
{
extern MAPCLASS BogusMapClass;
return;
/*
** close the map iff:
** it is valid and open and opened by this process
** and (!bogus or it's a bogus map or it is not persistent)
** negate this: return iff
** it is not valid or it is not open or not opened by this process
** or (bogus and it's not a bogus map and it's not not-persistent)
*/
return;
sm_dprintf("closemaps: closing %s (%s)\n",
{
}
}
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
extern int getdomainname();
/* this is mainly for backward compatibility in Sun environment */
static char *
{
/*
** Get the domain name from the kernel.
** If it does not start with a leading dot, then remove
** the first component. Since leading dots are funny Unix
** files, we treat a leading "+" the same as a leading dot.
** Finally, force there to be at least one dot in the domain name
** (i.e. top-level domains are not allowed, like "com", must be
** something like "sun.com").
*/
char *period, *autodomain;
return NULL;
if (buf[0] == '\0')
return NULL;
if (tTd(0, 20))
if (buf[0] == '+')
buf[0] = '.';
autodomain = buf;
else
else
return newstr(autodomain);
}
#endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
/*
** GETCANONNAME -- look up name using service switch
**
** Parameters:
** host -- the host name to look up.
** hbsize -- the size of the host buffer.
** trymx -- if set, try MX records.
** pttl -- pointer to return TTL (can be NULL).
**
** Returns:
** true -- if the host was found.
** false -- otherwise.
*/
bool
char *host;
int hbsize;
bool trymx;
int *pttl;
{
int nmaps;
int mapno;
bool found = false;
bool got_tempfail = false;
auto int status;
char *maptype[MAXMAPSTACK];
short mapreturn[MAXMAPACTIONS];
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
bool should_try_nis_domain = false;
static char *nis_domain = NULL;
#endif
if (pttl != 0)
*pttl = SM_DEFAULT_TTL;
{
int i;
sm_dprintf("getcanonname(%s), trying %s\n",
{
}
#if NIS
{
# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
if (nis_domain == NULL)
# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
}
#endif /* NIS */
#if NISPLUS
{
# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
if (nis_domain == NULL)
# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
}
#endif /* NISPLUS */
#if NAMED_BIND
{
}
#endif /* NAMED_BIND */
#if NETINFO
{
}
#endif /* NETINFO */
else
{
found = false;
}
/*
** Heuristic: if $m is not set, we are running during system
** startup. In this case, when a name is apparently found
** but has no dot, treat is as not found. This avoids
** in the service switch.
*/
if (found &&
break;
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
if (found)
should_try_nis_domain = true;
/* but don't break, as we need to try all methods first */
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
/* see if we should continue */
if (status == EX_TEMPFAIL)
{
i = MA_TRYAGAIN;
got_tempfail = true;
}
else if (status == EX_NOTFOUND)
i = MA_NOTFOUND;
else
i = MA_UNAVAIL;
break;
}
if (found)
{
char *d;
/*
** If returned name is still single token, compensate
** by tagging on $m. This is because some sites set
** up their DNS or NIS databases wrong.
*/
{
if (d != NULL &&
{
hbsize);
else
}
else
{
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
if (VendorCode == VENDOR_SUN &&
{
goto try_nis_domain;
}
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
return false;
}
}
return true;
}
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
{
if (nis_domain != NULL &&
{
return true;
}
}
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
status);
if (got_tempfail)
else
return false;
}
/*
**
** Parameters:
** name -- the name against which to match.
** dot -- where to reinsert '.' to get FQDN
** cbuf -- the location to store the result.
** cbuflen -- the size of cbuf.
**
** Returns:
** true -- if the line matched the desired name.
** false -- otherwise.
*/
static bool
char *name;
char *dot;
char *line;
char cbuf[];
int cbuflen;
{
int i;
char *p;
bool found = false;
cbuf[0] = '\0';
if (line[0] == '#')
return false;
for (i = 1; ; i++)
{
if (p == NULL)
break;
if (*p == '\0')
continue;
if (cbuf[0] == '\0' ||
{
}
if (sm_strcasecmp(name, p) == 0)
found = true;
{
/* try looking for the FQDN as well */
*dot = '.';
if (sm_strcasecmp(name, p) == 0)
found = true;
*dot = '\0';
}
}
{
/* try to add a domain on the end of the name */
{
p = &cbuf[i];
*p++ = '.';
}
}
return found;
}
/*
** DNS modules
*/
#if NAMED_BIND
# if DNSMAP
# include "sm_resolve.h"
# endif /* NETINET || NETINET6 */
/*
** DNS_MAP_OPEN -- stub to check proper value for dns map type
*/
bool
int mode;
{
{
/* issue a pseudo-error message */
return false;
}
return true;
}
/*
** DNS_MAP_PARSEARGS -- parse dns map definition args.
**
** Parameters:
** map -- pointer to MAP
** args -- pointer to the args on the config line.
**
** Returns:
** true -- if everything parsed OK.
** false -- otherwise.
*/
# if !_FFR_DNSMAP_MULTI
# endif /* ! _FFR_DNSMAP_MULTI */
# endif /* _FFR_DNSMAP_MULTILIMIT */
# if _FFR_DNSMAP_MULTI
# endif /* _FFR_DNSMAP_MULTILIMIT */
# endif /* _FFR_DNSMAP_MULTI */
struct dns_map
{
int dns_m_type;
};
bool
char *args;
{
register char *p = args;
for (;;)
{
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'N':
break;
case 'O':
break;
case 'o':
break;
case 'f':
break;
case 'm':
break;
case 'A':
break;
case 'q':
break;
case 't':
break;
case 'a':
break;
case 'T':
break;
case 'd':
{
char *h;
++p;
h = strchr(p, ' ');
if (h != NULL)
*h = '\0';
if (h != NULL)
*h = ' ';
}
break;
case 'r':
continue;
break;
# if _FFR_DNSMAP_MULTI
case 'z':
if (*++p != '\\')
map->map_coldelim = *p;
else
{
switch (*++p)
{
case 'n':
break;
case 't':
break;
default:
}
}
break;
case 'Z':
continue;
break;
# endif /* _FFR_DNSMAP_MULTILIMIT */
# endif /* _FFR_DNSMAP_MULTI */
/* Start of dns_map specific args */
case 'R': /* search field */
{
char *h;
continue;
h = strchr(p, ' ');
if (h != NULL)
*h = '\0';
if (h != NULL)
*h = ' ';
if (map_p->dns_m_type < 0)
syserr("dns map %s: wrong type %s",
}
break;
# if _FFR_DNSMAP_BASE
case 'B': /* base domain */
{
char *h;
continue;
h = strchr(p, ' ');
if (h != NULL)
*h = '\0';
/*
** slight abuse of map->map_file; it isn't
** used otherwise in this map type.
*/
if (h != NULL)
*h = ' ';
}
break;
# endif /* _FFR_DNSMAP_BASE */
}
p++;
if (*p != '\0')
*p++ = '\0';
}
if (map_p->dns_m_type < 0)
/*
** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
** Even if this assumption is wrong, we use only one byte,
** so it doesn't really matter.
*/
return true;
}
/*
** DNS_MAP_LOOKUP -- perform dns map lookup.
**
** Parameters:
** map -- pointer to MAP
** name -- name to lookup
** av -- arguments to interpolate into buf.
** statp -- pointer to status (EX_)
**
** Returns:
** result of lookup if succeeded.
** NULL -- otherwise.
*/
char *
char *name;
char **av;
int *statp;
{
# if _FFR_DNSMAP_MULTI
int resnum = 0;
# endif /* _FFR_DNSMAP_MULTILIMIT */
# endif /* _FFR_DNSMAP_MULTI */
DNS_REPLY_T *r = NULL;
# if NETINET6
static char buf6[INET6_ADDRSTRLEN];
# endif /* NETINET6 */
sm_dprintf("dns_map_lookup(%s, %s)\n",
# if _FFR_DNSMAP_BASE
{
char *appdomain;
{
*statp = EX_UNAVAILABLE;
return NULL;
}
}
else
# endif /* _FFR_DNSMAP_BASE */
{
}
if (r == NULL)
{
*statp = EX_TEMPFAIL;
else
*statp = EX_NOTFOUND;
goto cleanup;
}
{
{
case T_NS:
type = "T_NS";
break;
case T_CNAME:
type = "T_CNAME";
break;
case T_AFSDB:
type = "T_AFSDB";
break;
case T_SRV:
type = "T_SRV";
break;
case T_PTR:
type = "T_PTR";
break;
case T_TXT:
type = "T_TXT";
break;
case T_MX:
type = "T_MX";
break;
# if NETINET
case T_A:
type = "T_A";
break;
# endif /* NETINET */
# if NETINET6
case T_AAAA:
type = "T_AAAA";
sizeof buf6);
break;
# endif /* NETINET6 */
}
{
sm_dprintf("\tskipping type %s (%d) value %s\n",
continue;
}
# if NETINET6
{
*statp = EX_DATAERR;
sm_dprintf("\tbad T_AAAA conversion\n");
goto cleanup;
}
# endif /* NETINET6 */
sm_dprintf("\tfound type %s (%d) value %s\n",
# if _FFR_DNSMAP_MULTI
# endif /* _FFR_DNSMAP_MULTILIMIT */
{
/* Only care about the first match */
break;
}
{
/* First result */
}
else
{
/* concatenate the results */
int sz;
char *new;
if (map->map_sizelimit > 0 &&
break;
# endif /* _FFR_DNSMAP_MULTILIMIT */
}
# else /* _FFR_DNSMAP_MULTI */
break;
# endif /* _FFR_DNSMAP_MULTI */
}
{
*statp = EX_NOTFOUND;
sm_dprintf("\tno match found\n");
goto cleanup;
}
# if _FFR_DNSMAP_MULTI
/* Cleanly truncate for rulesets */
# endif /* _FFR_DNSMAP_MULTI */
if (LogLevel > 9)
else
# if _FFR_DNSMAP_MULTI
# endif /* _FFR_DNSMAP_MULTI */
if (r != NULL)
dns_free_data(r);
return result;
}
# endif /* DNSMAP */
#endif /* NAMED_BIND */
/*
** NDBM modules
*/
#if NDBM
/*
** NDBM_MAP_OPEN -- DBM-style map open
*/
bool
int mode;
{
int save_errno;
int dfd;
int pfd;
long sff;
int ret;
char dirfile[MAXPATHLEN];
char pagfile[MAXPATHLEN];
sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
/* do initial file and directory checks */
{
errno = 0;
syserr("dbm map \"%s\": map file %s name too long",
return false;
}
{
sff |= SFF_NOSLINK;
sff |= SFF_NOHLINK;
}
else
{
sff |= SFF_NOWLINK;
}
sff |= SFF_SAFEDIRPATH;
if (ret == 0)
if (ret != 0)
{
char *prob = "unsafe";
/* cannot open this map */
prob = "missing";
syserr("dbm map \"%s\": %s map file %s",
return false;
}
# if LOCK_ON_OPEN
else
# else /* LOCK_ON_OPEN */
{
# if NOFTRUNCATE
/*
** Warning: race condition. Try to lock the file as
** quickly as possible after opening it.
** This may also have security problems on some systems,
** but there isn't anything we can do about it.
*/
# else /* NOFTRUNCATE */
/*
** This ugly code opens the map without truncating it,
** locks the file, then truncates it. Necessary to
** avoid race conditions.
*/
int dirfd;
int pagfd;
sff |= SFF_NOSLINK;
sff |= SFF_NOHLINK;
{
save_errno = errno;
if (dirfd >= 0)
if (pagfd >= 0)
errno = save_errno;
syserr("ndbm_map_open: cannot create database %s",
return false;
}
{
save_errno = errno;
errno = save_errno;
syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
return false;
}
/* if new file, get "before" bits for later filechanged check */
{
save_errno = errno;
errno = save_errno;
syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
return false;
}
/* have to save the lock for the duration (bletch) */
/* twiddle bits for dbm_open */
# endif /* NOFTRUNCATE */
}
# endif /* LOCK_ON_OPEN */
/* open the database */
{
save_errno = errno;
return true;
# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
errno = save_errno;
return false;
}
{
/* heuristic: if files are linked, this is actually gdbm */
# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
errno = 0;
syserr("dbm map \"%s\": cannot support GDBM",
return false;
}
{
save_errno = errno;
# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
errno = save_errno;
syserr("ndbm_map_open(%s): file changed after open",
return false;
}
/*
** Need to set map_mtime before the call to aliaswait()
** as aliaswait() will call map_lookup() which requires
** map_mtime to be set
*/
{
# if LOCK_ON_OPEN
if (dfd >= 0)
if (pfd >= 0)
# endif /* LOCK_ON_OPEN */
return false;
}
else
{
if (geteuid() == 0 && TrustedUid != 0)
{
# if HASFCHOWN
{
"ownership change on %s failed: %s",
message("050 ownership change on %s failed: %s",
}
# else /* HASFCHOWN */
"no fchown(): cannot change ownership on %s",
message("050 no fchown(): cannot change ownership on %s",
# endif /* HASFCHOWN */
}
}
return true;
}
/*
** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
*/
char *
char *name;
char **av;
int *statp;
{
sm_dprintf("ndbm_map_lookup(%s, %s)\n",
{
}
{
/* Reopen the database to sync the cache */
: O_RDONLY;
{
goto lockdbm;
}
else
{
{
extern MAPCLASS BogusMapClass;
*statp = EX_TEMPFAIL;
syserr("Cannot reopen NDBM database %s",
}
return NULL;
}
}
{
}
{
}
return NULL;
else
}
/*
** NDBM_MAP_STORE -- store a datum in the database
*/
void
char *lhs;
char *rhs;
{
int status;
sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
{
}
{
}
if (status > 0)
{
else
{
static int bufsiz = 0;
auto int xstat;
{
{
}
sm_dprintf("ndbm_map_store append=%s\n",
}
}
}
if (status != 0)
}
/*
** NDBM_MAP_CLOSE -- close the database
*/
void
{
sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
{
# ifdef NDBM_YP_COMPAT
bool inclnull;
char buf[MAXHOSTNAMELEN];
{
}
if (inclnull)
# endif /* NDBM_YP_COMPAT */
/* write out the distinguished alias */
}
/* release lock (if needed) */
# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN */
}
#endif /* NDBM */
/*
** NEWDB (Hash and BTree) Modules
*/
#if NEWDB
/*
** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
**
** These do rather bizarre locking. If you can lock on open,
** do that to avoid the condition of opening a database that
** is being rebuilt. If you don't, we'll try to fake it, but
** there will be a race condition. If opening for read-only,
** we immediately release the lock to avoid freezing things up.
** We really ought to hold the lock, but guarantee that we won't
** be pokey about it. That's hard to do.
*/
/* these should be K line arguments */
# if DB_VERSION_MAJOR < 2
# define db_cachesize cachesize
# ifndef DB_CACHE_SIZE
# endif /* ! DB_CACHE_SIZE */
# ifndef DB_HASH_NELEM
# endif /* ! DB_HASH_NELEM */
# endif /* DB_VERSION_MAJOR < 2 */
bool
int mode;
{
# if DB_VERSION_MAJOR < 2
# endif /* DB_VERSION_MAJOR < 2 */
# if DB_VERSION_MAJOR == 2
# endif /* DB_VERSION_MAJOR == 2 */
# if DB_VERSION_MAJOR > 2
# endif /* DB_VERSION_MAJOR > 2 */
sm_dprintf("bt_map_open(%s, %s, %d)\n",
# if DB_VERSION_MAJOR < 3
# ifdef DB_CACHE_SIZE
# endif /* DB_CACHE_SIZE */
# endif /* DB_VERSION_MAJOR < 3 */
}
bool
int mode;
{
# if DB_VERSION_MAJOR < 2
# endif /* DB_VERSION_MAJOR < 2 */
# if DB_VERSION_MAJOR == 2
# endif /* DB_VERSION_MAJOR == 2 */
# if DB_VERSION_MAJOR > 2
# endif /* DB_VERSION_MAJOR > 2 */
sm_dprintf("hash_map_open(%s, %s, %d)\n",
# if DB_VERSION_MAJOR < 3
# ifdef DB_HASH_NELEM
# endif /* DB_HASH_NELEM */
# ifdef DB_CACHE_SIZE
# endif /* DB_CACHE_SIZE */
# endif /* DB_VERSION_MAJOR < 3 */
}
static bool
int mode;
char *mapclassname;
# if DB_VERSION_MAJOR < 2
const void *openinfo;
# endif /* DB_VERSION_MAJOR < 2 */
# if DB_VERSION_MAJOR == 2
# endif /* DB_VERSION_MAJOR == 2 */
# if DB_VERSION_MAJOR > 2
void **openinfo;
# endif /* DB_VERSION_MAJOR > 2 */
{
int i;
int omode;
int fd;
long sff;
int save_errno;
char buf[MAXPATHLEN];
/* do initial file and directory checks */
{
errno = 0;
syserr("map \"%s\": map file %s name too long",
return false;
}
{
{
errno = 0;
syserr("map \"%s\": map file %s name too long",
return false;
}
}
{
sff |= SFF_NOSLINK;
sff |= SFF_NOHLINK;
}
else
{
sff |= SFF_NOWLINK;
}
sff |= SFF_SAFEDIRPATH;
if (i != 0)
{
char *prob = "unsafe";
/* cannot open this map */
if (i == ENOENT)
prob = "missing";
errno = i;
syserr("%s map \"%s\": %s map file %s",
return false;
}
# if LOCK_ON_OPEN
else
# else /* LOCK_ON_OPEN */
/*
** Pre-lock the file to avoid race conditions. In particular,
** since dbopen returns NULL if the file is zero length, we
** must have a locked instance around the dbopen.
*/
if (fd < 0)
{
return false;
}
/* make sure no baddies slipped in just before the open... */
{
save_errno = errno;
errno = save_errno;
return false;
}
/* if new file, get the "before" bits for later filechanged check */
{
save_errno = errno;
errno = save_errno;
syserr("db_map_open(%s): cannot fstat pre-opened file",
buf);
return false;
}
/* actually lock the pre-opened file */
/* set up mode bits for dbopen */
# endif /* LOCK_ON_OPEN */
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
{
int flags = 0;
# if DB_VERSION_MAJOR > 2
int ret;
# endif /* DB_VERSION_MAJOR > 2 */
flags |= DB_TRUNCATE;
# if DB_VERSION_MAJOR > 2
# ifdef DB_CACHE_SIZE
{
if (ret != 0)
{
}
}
# endif /* DB_CACHE_SIZE */
# ifdef DB_HASH_NELEM
{
if (ret != 0)
{
}
}
# endif /* DB_HASH_NELEM */
{
DBTXN /* transaction for DB 4.1 */
if (ret != 0)
{
#ifdef DB_OLD_VERSION
if (ret == DB_OLD_VERSION)
#endif /* DB_OLD_VERSION */
}
}
# else /* DB_VERSION_MAJOR > 2 */
# endif /* DB_VERSION_MAJOR > 2 */
}
# endif /* DB_VERSION_MAJOR < 2 */
save_errno = errno;
# if !LOCK_ON_OPEN
else
# endif /* !LOCK_ON_OPEN */
{
return true;
# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN */
errno = save_errno;
syserr("Cannot open %s database %s",
mapclassname, buf);
return false;
}
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
fd = -1;
# endif /* DB_VERSION_MAJOR < 2 */
{
save_errno = errno;
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
# endif /* DB_VERSION_MAJOR < 2 */
# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN */
errno = save_errno;
return false;
}
# if LOCK_ON_OPEN
{
}
# endif /* LOCK_ON_OPEN */
/* try to make sure that at least the database header is on disk */
{
if (geteuid() == 0 && TrustedUid != 0)
{
# if HASFCHOWN
{
"ownership change on %s failed: %s",
message("050 ownership change on %s failed: %s",
}
# else /* HASFCHOWN */
"no fchown(): cannot change ownership on %s",
message("050 no fchown(): cannot change ownership on %s",
# endif /* HASFCHOWN */
}
}
/*
** Need to set map_mtime before the call to aliaswait()
** as aliaswait() will call map_lookup() which requires
** map_mtime to be set
*/
return false;
return true;
}
/*
** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
*/
char *
char *name;
char **av;
int *statp;
{
int i;
int st;
int save_errno;
int fd;
char buf[MAXPATHLEN];
sm_dprintf("db_map_lookup(%s, %s)\n",
{
errno = 0;
syserr("map \"%s\": map file %s name too long",
return NULL;
}
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
fd = -1;
# endif /* DB_VERSION_MAJOR < 2 */
{
/* Reopen the database to sync the cache */
: O_RDONLY;
{
goto lockdb;
}
else
{
{
extern MAPCLASS BogusMapClass;
*statp = EX_TEMPFAIL;
syserr("Cannot reopen DB database %s",
}
return NULL;
}
}
st = 1;
{
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
switch (errno)
{
case DB_NOTFOUND:
case DB_KEYEMPTY:
st = 1;
break;
case 0:
st = 0;
break;
default:
st = -1;
break;
}
# endif /* DB_VERSION_MAJOR < 2 */
if (st == 0)
}
{
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
switch (errno)
{
case DB_NOTFOUND:
case DB_KEYEMPTY:
st = 1;
break;
case 0:
st = 0;
break;
default:
st = -1;
break;
}
# endif /* DB_VERSION_MAJOR < 2 */
if (st == 0)
}
save_errno = errno;
if (st != 0)
{
errno = save_errno;
if (st < 0)
return NULL;
}
else
}
/*
** DB_MAP_STORE -- store a datum in the NEWDB database
*/
void
char *lhs;
char *rhs;
{
int status;
sm_dprintf("db_map_store(%s, %s, %s)\n",
{
}
{
}
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
switch (errno)
{
case DB_KEYEXIST:
status = 1;
break;
case 0:
status = 0;
break;
default:
status = -1;
break;
}
# endif /* DB_VERSION_MAJOR < 2 */
if (status > 0)
{
else
{
static int bufsiz = 0;
{
{
}
sm_dprintf("db_map_store append=%s\n",
}
}
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
# endif /* DB_VERSION_MAJOR < 2 */
}
if (status != 0)
}
/*
** DB_MAP_CLOSE -- add distinguished entries and close the database
*/
void
{
sm_dprintf("db_map_close(%s, %s, %lx)\n",
{
/* write out the distinguished alias */
}
# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
# endif /* !LOCK_ON_OPEN */
# if DB_VERSION_MAJOR < 2
# else /* DB_VERSION_MAJOR < 2 */
/*
** Berkeley DB can use internal shared memory
** locking for its memory pool. Closing a map
** opened by another process will interfere
** with the shared memory and locks of the parent
** process leaving things in a bad state.
*/
/*
** If this map was not opened by the current
** process, do not close the map but recover
** the file descriptor.
*/
{
int fd = -1;
if (fd >= 0)
return;
}
# endif /* DB_VERSION_MAJOR < 2 */
syserr("db_map_close(%s, %s, %lx): db close failure",
}
#endif /* NEWDB */
/*
** NIS Modules
*/
#if NIS
# ifndef YPERR_BUSY
# define YPERR_BUSY 16
# endif /* ! YPERR_BUSY */
/*
** NIS_MAP_OPEN -- open DBM map
*/
bool
int mode;
{
int yperr;
register char *p;
auto char *vp;
auto int vsize;
sm_dprintf("nis_map_open(%s, %s, %d)\n",
{
/* issue a pseudo-error message */
return false;
}
if (p != NULL)
{
*p++ = '\0';
if (*p != '\0')
map->map_domain = p;
}
{
if (yperr != 0)
{
syserr("451 4.3.5 NIS map %s specified, but NIS not running",
return false;
}
}
/* check to see if this map actually exists */
sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
{
/*
** We ought to be calling aliaswait() here if this is an
** alias file, but powerful HP-UX NIS servers apparently
** don't insert the @:@ token into the alias map when it
** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
*/
# if 0
# endif /* 0 */
return true;
}
{
syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
}
return false;
}
/*
** NIS_MAP_LOOKUP -- look up a datum in a NIS map
*/
/* ARGSUSED3 */
char *
char *name;
char **av;
int *statp;
{
char *vp;
auto int vsize;
int buflen;
int yperr;
sm_dprintf("nis_map_lookup(%s, %s)\n",
{
if (yperr == 0)
}
{
buflen++;
if (yperr == 0)
}
if (yperr != 0)
{
return NULL;
}
else
return result;
}
/*
** NIS_GETCANONNAME -- look up canonical name in NIS
*/
static bool
char *name;
int hbsize;
int *statp;
{
char *vp;
auto int vsize;
int keylen;
int yperr;
static bool try0null = true;
static bool try1null = true;
char host_record[MAXLINE];
{
*statp = EX_UNAVAILABLE;
return false;
}
(void) shorten_hostname(nbuf);
(void) yp_get_default_domain(&yp_domain);
if (try0null)
{
if (yperr == 0)
try1null = false;
}
{
keylen++;
if (yperr == 0)
try0null = false;
}
if (yperr != 0)
{
else if (yperr == YPERR_BUSY)
*statp = EX_TEMPFAIL;
else
*statp = EX_UNAVAILABLE;
return false;
}
*vp = '\0';
{
/* this should not happen, but.... */
return false;
}
{
*statp = EX_UNAVAILABLE;
return false;
}
return true;
}
#endif /* NIS */
/*
** NISPLUS Modules
**
** This code donated by Sun Microsystems.
*/
#if NISPLUS
/*
** NISPLUS_MAP_OPEN -- open nisplus table
*/
bool
int mode;
{
sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
{
return false;
}
{
/* set default NISPLUS Domain to $m */
sm_dprintf("nisplus_map_open(%s): using domain %s\n",
}
{
}
else
{
/* check to see if this map actually exists */
}
retry_cnt = 0;
{
{
case NIS_SUCCESS:
break;
case NIS_TRYAGAIN:
case NIS_RPCERROR:
case NIS_NAMEUNREACHABLE:
if (retry_cnt++ > 4)
{
return false;
}
/* try not to overwhelm hosed server */
sleep(2);
break;
default: /* all other nisplus errors */
# if 0
syserr("451 4.3.5 Cannot find table %s.%s: %s",
# endif /* 0 */
return false;
}
}
{
# if 0
syserr("451 4.3.5 %s.%s: %s is not a table",
# endif /* 0 */
return false;
}
/* default key column is column 0 */
/* verify the key column exist */
for (i = 0; i < max_col; i++)
{
break;
}
if (i == max_col)
{
sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
return false;
}
/* default value column is the last column */
{
return true;
}
for (i = 0; i< max_col; i++)
{
{
map->map_valcolno = i;
return true;
}
}
sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
return false;
}
/*
** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
*/
char *
char *name;
char **av;
int *statp;
{
char *p;
auto int vsize;
char *skp;
int skleft;
sm_dprintf("nisplus_map_lookup(%s, %s)\n",
{
{
}
else
{
*statp = EX_UNAVAILABLE;
return NULL;
}
}
/*
** Copy the name to the key buffer, escaping double quote characters
** by doubling them and quoting "]" and "," to avoid having the
** NIS+ parser choke on them.
*/
skp = search_key;
{
switch (*p)
{
case ']':
case ',':
/* quote the character */
*skp++ = '"';
*skp++ = *p;
*skp++ = '"';
skleft -= 3;
break;
case '"':
/* double the quote */
*skp++ = '"';
skleft--;
/* FALLTHROUGH */
default:
*skp++ = *p;
skleft--;
break;
}
}
*skp = '\0';
/* construct the query */
map->map_domain);
else
{
int count;
char *str;
{
if (LogLevel > 10)
"%s: lookup error, expected 1 entry, got %d",
/* ignore second entry */
sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
}
/* set the length of the result */
if (p == NULL)
p = "";
sm_dprintf("nisplus_map_lookup(%s), found %s\n",
name, p);
else
return str;
}
else
{
*statp = EX_NOTFOUND;
*statp = EX_TEMPFAIL;
else
{
*statp = EX_UNAVAILABLE;
}
}
return NULL;
}
/*
** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
*/
static bool
char *name;
int hbsize;
int *statp;
{
char *vp;
auto int vsize;
char *p;
{
*statp = EX_UNAVAILABLE;
return false;
}
(void) shorten_hostname(nbuf);
if (p == NULL)
{
/* single token */
"[name=%s],hosts.org_dir", nbuf);
}
else if (p[1] != '\0')
{
/* multi token -- take only first token in nbuf */
*p = '\0';
}
else
{
return false;
}
sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
{
int count;
char *domain;
{
if (LogLevel > 10)
"nisplus_getcanonname: lookup error, expected 1 entry, got %d",
count);
/* ignore second entry */
sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
}
sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
sm_dprintf("nisplus_getcanonname(%s), found %s\n",
{
domain = "";
}
else
{
domain = "";
}
{
if (domain[0] == '\0')
else
}
else
return true;
}
else
{
*statp = EX_TEMPFAIL;
else
*statp = EX_UNAVAILABLE;
}
sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
return false;
}
char *
{
char *p;
if (default_domain[0] != '\0')
return default_domain;
p = nis_local_directory();
return default_domain;
}
#endif /* NISPLUS */
/*
** LDAP Modules
*/
/*
** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
*/
# if PH_MAP
# define ph_map_dequote ldapmap_dequote
# endif /* PH_MAP */
static char *ldapmap_dequote __P((char *));
static char *
char *str;
{
char *p;
char *start;
return NULL;
p = str;
if (*p == '"')
{
/* Should probably swallow initial whitespace here */
start = ++p;
}
else
return str;
while (*p != '"' && *p != '\0')
p++;
if (*p != '\0')
*p = '\0';
return start;
}
#endif /* defined(LDAPMAP) || defined(PH_MAP) */
#if LDAPMAP
/*
** LDAPMAP_OPEN -- open LDAP map
**
** Connect to the LDAP server. Re-use existing connections since a
** single server connection to a host (with the same host, port,
** bind DN, and secret) can answer queries for multiple maps.
*/
bool
int mode;
{
STAB *s;
char *id;
#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
if (VendorCode == VENDOR_SUN &&
{
return true;
}
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
/* sendmail doesn't have the ability to write to LDAP (yet) */
{
/* issue a pseudo-error message */
return false;
}
s = ldapmap_findconn(lmap);
{
/* Already have a connection open to this LDAP server */
/* Add this map as head of linked list */
sm_dprintf("using cached connection\n");
return true;
}
sm_dprintf("opening new connection\n");
else
id = "localhost";
/* No connection yet, connect */
{
{
if (LogLevel > 1)
"timeout conning to LDAP server %.100s",
id);
}
{
{
syserr("%s failed to %s in map %s",
# if USE_LDAP_INIT
# else /* USE_LDAP_INIT */
"ldap_open",
# endif /* USE_LDAP_INIT */
}
else
{
syserr("451 4.3.5 %s failed to %s in map %s",
# if USE_LDAP_INIT
# else /* USE_LDAP_INIT */
"ldap_open",
# endif /* USE_LDAP_INIT */
}
}
return false;
}
/* Save connection for reuse */
return true;
}
/*
** LDAPMAP_CLOSE -- close ldap map
*/
void
{
STAB *s;
/* Check if already closed */
return;
/* Close the LDAP connection */
/* Mark all the maps that share the connection as closed */
s = ldapmap_findconn(lmap);
{
sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
}
}
# ifdef SUNET_ID
/*
** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
** This only makes sense at Stanford University.
*/
static char *
char *str;
{
char *p, *p_last;
p = str;
p_last = p;
while (*p != '\0')
{
{
*p_last = *p;
p_last++;
}
else if (isupper(*p))
{
p_last++;
}
++p;
}
if (*p_last != '\0')
*p_last = '\0';
return str;
}
# endif /* SUNET_ID */
/*
** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
*/
char *
char *name;
char **av;
int *statp;
{
int flags;
int plen = 0;
int psize = 0;
int msgid;
int save_errno;
char *vp, *p;
#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
if (VendorCode == VENDOR_SUN &&
{
int rc;
if (rc != 0)
{
sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
*statp = EX_NOTFOUND;
return NULL;
}
answer);
else
return result;
}
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
/* Get ldap struct pointer from map */
{
# ifdef SUNET_ID
# else /* SUNET_ID */
# endif /* SUNET_ID */
}
if (msgid == -1)
{
save_errno = errno;
{
syserr("Error in ldap_search using %s in map %s",
else
syserr("451 4.3.5 Error in ldap_search using %s in map %s",
}
*statp = EX_TEMPFAIL;
switch (save_errno - E_LDAPBASE)
{
# ifdef LDAP_SERVER_DOWN
case LDAP_SERVER_DOWN:
# endif /* LDAP_SERVER_DOWN */
case LDAP_TIMEOUT:
case LDAP_UNAVAILABLE:
/* server disappeared, try reopen on next search */
break;
}
errno = save_errno;
return NULL;
}
*statp = EX_NOTFOUND;
flags = 0;
# if _FFR_LDAP_SINGLEDN
# endif /* _FFR_LDAP_SINGLEDN */
/* Create an rpool for search related memory usage */
p = NULL;
save_errno = errno;
/* Copy result so rpool can be freed */
/* need to restart LDAP connection? */
if (*statp == EX_RESTART)
{
*statp = EX_TEMPFAIL;
}
errno = save_errno;
{
{
syserr("Error getting LDAP results in map %s",
else
syserr("451 4.3.5 Error getting LDAP results in map %s",
}
errno = save_errno;
return NULL;
}
/* Did we match anything? */
return NULL;
{
if (LogLevel > 9)
"ldap %.100s => %s", name,
else
{
/* vp != NULL according to test above */
}
}
return result;
}
/*
** LDAPMAP_FINDCONN -- find an LDAP connection to the server
**
** Cache LDAP connections based on the host, port, bind DN,
** secret, and PID so we don't have multiple connections open to
** the same server for different maps. Need a separate connection
** per PID since a parent process may close the map before the
** child is done with it.
**
** Parameters:
** lmap -- LDAP map information
**
** Returns:
** Symbol table entry for the LDAP connection.
*/
static STAB *
{
char *format;
char *nbuf;
char *id;
else
id = "localhost";
format = "%s%c%d%c%d%c%s%c%s%d";
id,
: lmap->ldap_binddn),
: lmap->ldap_secret),
(int) CurrentPid);
return s;
}
/*
** LDAPMAP_PARSEARGS -- parse ldap map definition args.
*/
static struct lamvalues LDAPAuthMethods[] =
{
{ "none", LDAP_AUTH_NONE },
{ "simple", LDAP_AUTH_SIMPLE },
# ifdef LDAP_AUTH_KRBV4
{ "krbv4", LDAP_AUTH_KRBV4 },
# endif /* LDAP_AUTH_KRBV4 */
{ NULL, 0 }
};
static struct ladvalues LDAPAliasDereference[] =
{
{ "never", LDAP_DEREF_NEVER },
{ "always", LDAP_DEREF_ALWAYS },
{ "search", LDAP_DEREF_SEARCHING },
{ "find", LDAP_DEREF_FINDING },
{ NULL, 0 }
};
static struct lssvalues LDAPSearchScope[] =
{
{ "base", LDAP_SCOPE_BASE },
{ "one", LDAP_SCOPE_ONELEVEL },
{ "sub", LDAP_SCOPE_SUBTREE },
{ NULL, 0 }
};
bool
char *args;
{
bool secretread = true;
bool attrssetup = false;
int i;
register char *p = args;
/* Get ldap struct pointer from map */
/* Check if setting the initial LDAP defaults */
{
/* We need to alloc an SM_LDAP_STRUCT struct */
if (LDAPDefaults == NULL)
else
}
/* there is no check whether there is really an argument */
/* Check if setting up an alias or file class LDAP map */
{
/* Comma separate if used as an alias file */
if (*args == '\0')
{
int n;
char *lc;
char jbuf[MAXHOSTNAMELEN];
/* Get $j */
if (jbuf[0] == '\0')
{
sizeof jbuf);
}
lc = "";
else
{
}
"(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
if (n >= sizeof ldapfilt)
{
syserr("%s: Default LDAP string too long",
return false;
}
/* default args for an alias LDAP entry */
attrssetup = true;
}
}
{
/* Space separate if used as a file class file */
}
for (;;)
{
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'N':
break;
case 'O':
break;
case 'o':
break;
case 'f':
break;
case 'm':
break;
case 'A':
break;
case 'q':
break;
case 'a':
break;
case 'T':
break;
case 't':
break;
case 'S':
map->map_spacesub = *++p;
break;
case 'D':
break;
case 'z':
if (*++p != '\\')
map->map_coldelim = *p;
else
{
switch (*++p)
{
case 'n':
break;
case 't':
break;
default:
}
}
break;
/* Start of ldapmap specific args */
case 'V':
if (*++p != '\\')
lmap->ldap_attrsep = *p;
else
{
switch (*++p)
{
case 'n':
break;
case 't':
break;
default:
}
}
break;
case 'k': /* search field */
continue;
lmap->ldap_filter = p;
break;
case 'v': /* attr to return */
continue;
break;
case '1':
break;
# if _FFR_LDAP_SINGLEDN
case '2':
break;
# endif /* _FFR_LDAP_SINGLEDN */
/* args stolen from ldapsearch.c */
case 'R': /* don't auto chase referrals */
# ifdef LDAP_REFERRALS
# else /* LDAP_REFERRALS */
syserr("compile with -DLDAP_REFERRALS for referral support");
# endif /* LDAP_REFERRALS */
break;
case 'n': /* retrieve attribute names only */
break;
case 'r': /* alias dereferencing */
continue;
p += 11;
for (lad = LDAPAliasDereference;
{
break;
}
else
{
/* bad config line */
if (!bitset(MCF_OPTFILE,
{
char *ptr;
*ptr = '\0';
syserr("Deref must be [never|always|search|find] (not %s) in map %s",
*ptr = ' ';
return false;
}
}
break;
case 's': /* search scope */
continue;
p += 11;
for (lss = LDAPSearchScope;
{
break;
}
else
{
/* bad config line */
if (!bitset(MCF_OPTFILE,
{
char *ptr;
*ptr = '\0';
syserr("Scope must be [base|one|sub] (not %s) in map %s",
*ptr = ' ';
return false;
}
}
break;
case 'h': /* ldap host */
continue;
{
syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
return false;
}
break;
case 'b': /* search base */
continue;
break;
case 'p': /* ldap port */
continue;
break;
case 'l': /* time limit */
continue;
break;
case 'Z':
continue;
break;
case 'd': /* Dn to bind to server as */
continue;
lmap->ldap_binddn = p;
break;
case 'M': /* Method for binding */
continue;
p += 10;
for (lam = LDAPAuthMethods;
{
break;
}
else
{
/* bad config line */
if (!bitset(MCF_OPTFILE,
{
char *ptr;
*ptr = '\0';
syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
*ptr = ' ';
return false;
}
}
break;
/*
** This is a string that is dependent on the
** method used defined above.
*/
case 'P': /* Secret password for binding */
continue;
lmap->ldap_secret = p;
secretread = false;
break;
case 'H': /* Use LDAP URI */
# if !USE_LDAP_INIT
syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
return false;
# else /* !USE_LDAP_INIT */
{
syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
return false;
}
continue;
break;
# endif /* !USE_LDAP_INIT */
case 'w':
/* -w should be for passwd, -P should be for version */
continue;
# ifdef LDAP_VERSION_MAX
{
syserr("LDAP version %d exceeds max of %d in map %s",
return false;
}
# endif /* LDAP_VERSION_MAX */
# ifdef LDAP_VERSION_MIN
{
syserr("LDAP version %d is lower than min of %d in map %s",
return false;
}
# endif /* LDAP_VERSION_MIN */
break;
default:
break;
}
/* need to account for quoted strings here */
{
if (*p == '"')
{
while (*++p != '"' && *p != '\0')
continue;
if (*p != '\0')
p++;
}
else
p++;
}
if (*p != '\0')
*p++ = '\0';
}
/*
** We need to swallow up all the stuff into a struct
** and dump it into map->map_dbptr1
*/
(LDAPDefaults == NULL ||
LDAPDefaults == lmap ||
(LDAPDefaults == NULL ||
LDAPDefaults == lmap ||
(LDAPDefaults == NULL ||
LDAPDefaults == lmap ||
(LDAPDefaults == NULL ||
LDAPDefaults == lmap ||
{
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
/* need to use method to map secret to passwd string */
switch (lmap->ldap_method)
{
case LDAP_AUTH_NONE:
/* Do nothing */
break;
case LDAP_AUTH_SIMPLE:
/*
** Secret is the name of a file with
** the first line as the password.
*/
/* Already read in the secret? */
if (secretread)
break;
{
syserr("LDAP map: cannot open secret %s",
return false;
}
"ldapmap_parseargs");
{
syserr("LDAP map: secret in %s too long",
return false;
}
{
/* chomp newline */
}
break;
# ifdef LDAP_AUTH_KRBV4
case LDAP_AUTH_KRBV4:
/*
** Secret is where the ticket file is
** stashed
*/
"KRBTKFILE=%s",
break;
# endif /* LDAP_AUTH_KRBV4 */
default: /* Should NEVER get here */
syserr("LDAP map: Illegal value in lmap method");
return false;
/* NOTREACHED */
break;
}
}
(LDAPDefaults == NULL ||
LDAPDefaults == lmap ||
(LDAPDefaults == NULL ||
LDAPDefaults == lmap ||
/*
** Save the server from extra work. If request is for a single
** match, tell the server to only return enough records to
** determine if there is a single match or not. This can not
** be one since the server would only return one and we wouldn't
** know if there were others available.
*/
/* If setting defaults, don't process ldap_filter and ldap_attr */
if (lmap == LDAPDefaults)
return true;
else
{
{
return false;
}
}
{
bool recurse = false;
bool normalseen = false;
i = 0;
/* Prime the attr list with the objectClass attribute */
i++;
while (p != NULL)
{
char *v;
p++;
if (*p == '\0')
break;
v = p;
p = strchr(v, ',');
if (p != NULL)
*p++ = '\0';
if (i >= LDAPMAP_MAX_ATTR)
{
syserr("Too many return attributes in %s (max %d)",
return false;
}
if (*v != '\0')
{
int j;
int use;
char *type;
char *needobjclass;
{
*type++ = '\0';
if (needobjclass != NULL)
*needobjclass++ = '\0';
}
else
{
needobjclass = NULL;
}
use = i;
/* allow override on "objectClass" type */
if (sm_strcasecmp(v, "objectClass") == 0 &&
{
use = 0;
}
else
{
/*
** Don't add something to attribute
** list twice.
*/
for (j = 1; j < i; j++)
{
{
syserr("Duplicate attribute (%s) in %s",
return false;
}
}
if (needobjclass != NULL &&
*needobjclass != '\0' &&
*needobjclass != '*')
{
}
else
{
}
}
{
{
recurse = true;
}
{
recurse = true;
}
{
recurse = true;
}
{
normalseen = true;
}
else
{
syserr("Unknown attribute type (%s) in %s",
return false;
}
}
else
{
normalseen = true;
}
i++;
}
}
/* Set in case needed in future code */
attrssetup = true;
if (recurse && !normalseen)
{
syserr("LDAP recursion requested in %s but no returnable attribute given",
return false;
}
{
syserr("LDAP recursion requested in %s can not be used with -n",
return false;
}
}
return true;
}
/*
** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
**
** Parameters:
** spec -- map argument string from LDAPDefaults option
**
** Returns:
** None.
*/
void
char *spec;
{
/* Allocate and set the default values */
if (LDAPDefaults == NULL)
/* look up the class */
{
syserr("readcf: LDAPDefaultSpec: class ldap not available");
return;
}
/* These should never be set in LDAPDefaults */
{
syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
}
{
syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
/* don't free, it isn't malloc'ed in parseargs */
}
{
syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
/* don't free, they aren't malloc'ed in parseargs */
}
}
#endif /* LDAPMAP */
/*
** PH map
*/
#if PH_MAP
/*
** This code is intended to replace the so-called "ph mailer".
** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
*/
/* what version of the ph map code we're running */
static char phmap_id[128];
/* sendmail version for phmap id string */
extern const char Version[];
/* assume we're using nph-1.2.x if not specified */
# ifndef NPH_VERSION
# define NPH_VERSION 10200
# endif
/* compatibility for versions older than nph-1.2.0 */
# if NPH_VERSION < 10200
# define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
# define PH_OPEN_DONTID PH_DONTID
# define PH_CLOSE_FAST PH_FASTCLOSE
# define PH_ERR_DATAERR PH_DATAERR
# define PH_ERR_NOMATCH PH_NOMATCH
# endif /* NPH_VERSION < 10200 */
/*
** PH_MAP_PARSEARGS -- parse ph map definition args.
*/
bool
char *args;
{
register bool done;
register char *p = args;
/* initialize version string */
"sendmail-%s phmap-20010529 libphclient-%s",
/* defaults */
pmap->ph_timeout = 0;
pmap->ph_fastclose = 0;
for (;;)
{
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'N':
break;
case 'O':
break;
case 'o':
break;
case 'f':
break;
case 'm':
break;
case 'A':
break;
case 'q':
break;
case 't':
break;
case 'a':
break;
case 'T':
break;
case 'l':
continue;
break;
case 'S':
map->map_spacesub = *++p;
break;
case 'D':
break;
case 'h': /* PH server list */
continue;
pmap->ph_servers = p;
break;
case 'k': /* fields to search for */
continue;
pmap->ph_field_list = p;
break;
default:
syserr("ph_map_parseargs: unknown option -%c", *p);
}
/* try to account for quoted strings */
while (*p != '\0' && !done)
{
if (*p == '"')
{
while (*++p != '"' && *p != '\0')
continue;
if (*p != '\0')
p++;
}
else
p++;
}
if (*p != '\0')
*p++ = '\0';
}
else
{
syserr("ph_map_parseargs: -h flag is required");
return false;
}
return true;
}
/*
** PH_MAP_CLOSE -- close the connection to the ph server
*/
void
{
sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
{
}
}
/* ARGSUSED */
static void
int unused;
{
/*
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
** DOING.
*/
}
static void
#if NPH_VERSION >= 10200
void *appdata;
#else
#endif
char *text;
{
if (LogLevel > 9)
"ph_map_send_debug: ==> %s", text);
}
static void
#if NPH_VERSION >= 10200
void *appdata;
#else
#endif
char *text;
{
if (LogLevel > 10)
"ph_map_recv_debug: <== %s", text);
}
/*
** PH_MAP_OPEN -- sub for opening PH map
*/
bool
int mode;
{
int save_errno = 0;
{
/* issue a pseudo-error message */
return false;
}
{
sm_dprintf("ph_map_open(%s) => DEFERRED\n",
/*
** Unset MF_DEFER here so that map_lookup() returns
** a temporary failure using the bogus map and
** map->map_tapp instead of the default permanent error.
*/
return false;
}
/* try each host in the list */
{
/* set timeout */
if (pmap->ph_timeout != 0)
{
{
if (LogLevel > 1)
"timeout connecting to PH server %.100s",
host);
goto ph_map_open_abort;
}
}
/* open connection to server */
#if NPH_VERSION >= 10200
, NULL
#endif
) == 0
{
return true;
}
save_errno = errno;
errno = save_errno;
}
{
if (errno == 0)
syserr("ph_map_open: %s: cannot connect to PH server",
}
"ph_map_open: %s: cannot connect to PH server",
return false;
}
/*
** PH_MAP_LOOKUP -- look up key from ph server
*/
char *
char *key;
char **args;
int *pstat;
{
int i, save_errno = 0;
/* set timeout */
if (pmap->ph_timeout != 0)
{
{
if (LogLevel > 1)
"timeout during PH lookup of %.100s",
key);
*pstat = EX_TEMPFAIL;
goto ph_map_lookup_abort;
}
}
/* perform lookup */
if (i == -1)
*pstat = EX_TEMPFAIL;
else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
*pstat = EX_UNAVAILABLE;
/*
** Close the connection if the timer popped
** or we got a temporary PH error
*/
if (*pstat == EX_TEMPFAIL)
{
save_errno = errno;
errno = save_errno;
}
{
else
}
return NULL;
}
#endif /* PH_MAP */
/*
** syslog map
*/
/*
** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
*/
bool
char *args;
{
char *p = args;
/* there is no check whether there is really an argument */
while (*p != '\0')
{
p++;
if (*p != '-')
break;
++p;
if (*p == 'D')
{
++p;
}
else if (*p == 'S')
{
map->map_spacesub = *++p;
if (*p != '\0')
p++;
}
else if (*p == 'L')
{
continue;
if (*p == '\0')
break;
priority = p;
p++;
if (*p != '\0')
*p++ = '\0';
}
else
{
syserr("Illegal option %c map syslog", *p);
++p;
}
}
else
{
priority += 4;
#ifdef LOG_EMERG
else
#endif /* LOG_EMERG */
#ifdef LOG_ALERT
else
#endif /* LOG_ALERT */
#ifdef LOG_CRIT
else
#endif /* LOG_CRIT */
#ifdef LOG_ERR
else
#endif /* LOG_ERR */
#ifdef LOG_WARNING
else
#endif /* LOG_WARNING */
#ifdef LOG_NOTICE
else
#endif /* LOG_NOTICE */
#ifdef LOG_INFO
else
#endif /* LOG_INFO */
#ifdef LOG_DEBUG
else
#endif /* LOG_DEBUG */
{
syserr("syslog_map_parseargs: Unknown priority %s",
priority);
return false;
}
}
return true;
}
/*
** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
*/
char *
char *string;
char **args;
int *statp;
{
{
sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
}
return "";
}
/*
** HESIOD Modules
*/
#if HESIOD
bool
int mode;
{
sm_dprintf("hes_map_open(%s, %s, %d)\n",
{
/* issue a pseudo-error message */
return false;
}
# ifdef HESIOD_INIT
return true;
syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
return false;
# else /* HESIOD_INIT */
if (hes_error() == HES_ER_UNINIT)
hes_init();
switch (hes_error())
{
case HES_ER_OK:
case HES_ER_NOTFOUND:
return true;
}
return false;
# endif /* HESIOD_INIT */
}
char *
char *name;
char **av;
int *statp;
{
char **hp;
if (name[0] == '\\')
{
char *np;
int nl;
int save_errno;
else
np[0] = '\\';
# ifdef HESIOD_INIT
# else /* HESIOD_INIT */
# endif /* HESIOD_INIT */
save_errno = errno;
errno = save_errno;
}
else
{
# ifdef HESIOD_INIT
# else /* HESIOD_INIT */
# endif /* HESIOD_INIT */
}
# ifdef HESIOD_INIT
{
switch (errno)
{
case ENOENT:
*statp = EX_NOTFOUND;
break;
case ECONNREFUSED:
*statp = EX_TEMPFAIL;
break;
case EMSGSIZE:
case ENOMEM:
default:
*statp = EX_UNAVAILABLE;
break;
}
return NULL;
}
# else /* HESIOD_INIT */
{
switch (hes_error())
{
case HES_ER_OK:
break;
case HES_ER_NOTFOUND:
*statp = EX_NOTFOUND;
break;
case HES_ER_CONFIG:
*statp = EX_UNAVAILABLE;
break;
case HES_ER_NET:
*statp = EX_TEMPFAIL;
break;
}
return NULL;
}
# endif /* HESIOD_INIT */
else
}
/*
** HES_MAP_CLOSE -- free the Hesiod context
*/
void
{
# ifdef HESIOD_INIT
/* Free the hesiod context */
if (HesiodContext != NULL)
{
}
# endif /* HESIOD_INIT */
}
#endif /* HESIOD */
/*
** NeXT NETINFO Modules
*/
#if NETINFO
# define NETINFO_DEFAULT_DIR "/aliases"
# define NETINFO_DEFAULT_PROPERTY "members"
/*
** NI_MAP_OPEN -- open NetInfo Aliases
*/
bool
int mode;
{
sm_dprintf("ni_map_open(%s, %s, %d)\n",
{
}
return true;
}
/*
** NI_MAP_LOOKUP -- look up a datum in NetInfo
*/
char *
char *name;
char **av;
int *statp;
{
char *res;
char *propval;
return NULL;
else
return res;
}
static bool
char *name;
int hbsize;
int *statp;
{
char *vptr;
char *ptr;
{
*statp = EX_UNAVAILABLE;
return false;
}
(void) shorten_hostname(nbuf);
/* we only accept single token search key */
{
return false;
}
/* Do the search */
{
return false;
}
/* Only want the first machine name */
*ptr = '\0';
{
*statp = EX_UNAVAILABLE;
return true;
}
return false;
}
#endif /* NETINFO */
/*
** TEXT (unindexed text file) Modules
**
** This code donated by Sun Microsystems.
*/
/*
** TEXT_MAP_OPEN -- open text table
*/
bool
int mode;
{
long sff;
int i;
sm_dprintf("text_map_open(%s, %s, %d)\n",
{
return false;
}
{
syserr("text map \"%s\": file name required",
return false;
}
{
syserr("text map \"%s\": file name must be fully qualified",
return false;
}
sff |= SFF_NOWLINK;
sff |= SFF_SAFEDIRPATH;
{
int save_errno = errno;
/* cannot open this map */
sm_dprintf("\tunsafe map file: %d\n", i);
errno = save_errno;
syserr("text map \"%s\": unsafe map file %s",
return false;
}
map->map_keycolno = 0;
else
{
{
syserr("text map \"%s\", file %s: -k should specify a number, not %s",
map->map_keycolnm);
return false;
}
}
map->map_valcolno = 0;
else
{
{
syserr("text map \"%s\", file %s: -v should specify a number, not %s",
map->map_valcolnm);
return false;
}
}
{
sm_dprintf("text_map_open(%s, %s): delimiter = ",
sm_dprintf("(white space)\n");
else
}
return true;
}
/*
** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
*/
char *
char *name;
char **av;
int *statp;
{
char *vp;
auto int vsize;
int buflen;
SM_FILE_T *f;
char delim;
int key_idx;
bool found_it;
found_it = false;
if (f == NULL)
{
*statp = EX_UNAVAILABLE;
return NULL;
}
while (sm_io_fgets(f, SM_TIME_DEFAULT,
{
char *p;
/* skip comment line */
if (linebuf[0] == '#')
continue;
if (p != NULL)
*p = '\0';
{
found_it = true;
break;
}
}
(void) sm_io_close(f, SM_TIME_DEFAULT);
if (!found_it)
{
*statp = EX_NOTFOUND;
return NULL;
}
{
*statp = EX_NOTFOUND;
return NULL;
}
else
}
/*
** TEXT_GETCANONNAME -- look up canonical name in hosts file
*/
static bool
char *name;
int hbsize;
int *statp;
{
bool found;
char *dot;
SM_FILE_T *f;
{
*statp = EX_UNAVAILABLE;
return false;
}
NULL);
if (f == NULL)
{
*statp = EX_UNAVAILABLE;
return false;
}
found = false;
while (!found &&
{
if (p != NULL)
*p = '\0';
if (linebuf[0] != '\0')
}
(void) sm_io_close(f, SM_TIME_DEFAULT);
if (!found)
{
return false;
}
{
*statp = EX_UNAVAILABLE;
return false;
}
return true;
}
/*
** STAB (Symbol Table) Modules
*/
/*
** STAB_MAP_LOOKUP -- look up alias in symbol table
*/
/* ARGSUSED2 */
char *
char *name;
char **av;
int *pstat;
{
register STAB *s;
sm_dprintf("stab_lookup(%s, %s)\n",
if (s == NULL)
return NULL;
else
}
/*
** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
*/
void
char *lhs;
char *rhs;
{
register STAB *s;
}
/*
** STAB_MAP_OPEN -- initialize (reads data file)
**
** This is a wierd case -- it is only intended as a fallback for
** aliases. For this reason, opens for write (only during a
** "newaliases") always fails, and opens for read open the
** actual underlying text file instead of the database.
*/
bool
int mode;
{
long sff;
sm_dprintf("stab_map_open(%s, %s, %d)\n",
{
return false;
}
sff |= SFF_NOWLINK;
sff |= SFF_SAFEDIRPATH;
return false;
return true;
}
/*
** Implicit Modules
**
** Tries several types. For back compatibility of aliases.
*/
/*
** IMPL_MAP_LOOKUP -- lookup in best open database
*/
char *
char *name;
char **av;
int *pstat;
{
sm_dprintf("impl_map_lookup(%s, %s)\n",
#if NEWDB
#endif /* NEWDB */
#if NDBM
#endif /* NDBM */
}
/*
** IMPL_MAP_STORE -- store in open databases
*/
void
char *lhs;
char *rhs;
{
sm_dprintf("impl_map_store(%s, %s, %s)\n",
#if NEWDB
#endif /* NEWDB */
#if NDBM
#endif /* NDBM */
}
/*
** IMPL_MAP_OPEN -- implicit database open
*/
bool
int mode;
{
sm_dprintf("impl_map_open(%s, %s, %d)\n",
#if NEWDB
{
# ifdef NDBM_YP_COMPAT
# endif /* NDBM_YP_COMPAT */
return true;
}
else
#endif /* NEWDB */
#if NDBM
{
return true;
}
else
#endif /* NDBM */
if (Verbose)
message("WARNING: cannot open alias database %s%s",
#else /* defined(NEWDB) || defined(NDBM) */
usrerr("Cannot rebuild aliases: no database format defined");
#endif /* defined(NEWDB) || defined(NDBM) */
else
return false;
}
/*
** IMPL_MAP_CLOSE -- close any open database(s)
*/
void
{
sm_dprintf("impl_map_close(%s, %s, %lx)\n",
#if NEWDB
{
}
#endif /* NEWDB */
#if NDBM
{
}
#endif /* NDBM */
}
/*
** User map class.
**
** Provides access to the system password file.
*/
/*
** USER_MAP_OPEN -- open user map
**
** Really just binds field names to field numbers.
*/
bool
int mode;
{
sm_dprintf("user_map_open(%s, %d)\n",
{
/* issue a pseudo-error message */
return false;
}
/* EMPTY */
/* nothing */ ;
else
{
syserr("User map %s: unknown column name %s",
return false;
}
return true;
}
/*
** USER_MAP_LOOKUP -- look up a user in the passwd file.
*/
/* ARGSUSED3 */
char *
char *key;
char **av;
int *statp;
{
auto bool fuzzy;
sm_dprintf("user_map_lookup(%s, %s)\n",
return NULL;
else
{
char buf[30];
switch (map->map_valcolno)
{
case 0:
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
default:
syserr("user_map %s: bogus field %d",
return NULL;
}
}
}
/*
** Program map type.
**
** This provides access to arbitrary programs. It should be used
** only very sparingly, since there is no way to bound the cost
** of invoking an arbitrary program.
*/
char *
char *name;
char **av;
int *statp;
{
int i;
int save_errno;
int fd;
int status;
register char *p;
char *rval;
sm_dprintf("prog_map_lookup(%s, %s) %s\n",
i = 0;
{
{
if (i >= MAXPV - 1)
break;
argv[i++] = p;
}
}
{
sm_dprintf("prog_open:");
sm_dprintf("\n");
}
(void) sm_blocksignal(SIGCHLD);
if (pid < 0)
{
syserr("prog_map_lookup(%s) failed (%s) -- closing",
sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
return NULL;
}
if (i < 0)
{
syserr("prog_map_lookup(%s): read error %s",
}
else if (i == 0)
{
sm_dprintf("prog_map_lookup(%s): empty answer\n",
}
else
{
buf[i] = '\0';
if (p != NULL)
*p = '\0';
/* collect the return value */
else
/* now flush any additional output */
continue;
}
/* wait for the process to terminate */
save_errno = errno;
(void) sm_releasesignal(SIGCHLD);
errno = save_errno;
if (status == -1)
{
syserr("prog_map_lookup(%s): wait error %s",
*statp = EX_SOFTWARE;
}
{
}
else
{
syserr("prog_map_lookup(%s): child died on signal %d",
*statp = EX_UNAVAILABLE;
}
return rval;
}
/*
** Sequenced map type.
**
** Tries each map in order until something matches, much like
** implicit. Stores go to the first map in the list that can
** support storing.
**
** This is slightly unusual in that there are two interfaces.
** The "sequence" interface lets you stack maps arbitrarily.
** The "switch" interface builds a sequence map by looking
** at a system-dependent configuration file such as
** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
**
** We don't need an explicit open, since all maps are
** opened on demand.
*/
/*
** SEQ_MAP_PARSE -- Sequenced map parsing
*/
bool
char *ap;
{
int maxmap;
maxmap = 0;
while (*ap != '\0')
{
register char *p;
STAB *s;
/* find beginning of map name */
ap++;
for (p = ap;
p++)
continue;
if (*p != '\0')
*p++ = '\0';
p++;
if (*ap == '\0')
{
ap = p;
continue;
}
if (s == NULL)
{
syserr("Sequence map %s: unknown member map %s",
}
else if (maxmap >= MAXMAPSTACK)
{
syserr("Sequence map %s: too many member maps (%d max)",
maxmap++;
}
else if (maxmap < MAXMAPSTACK)
{
}
ap = p;
}
return true;
}
/*
** SWITCH_MAP_OPEN -- open a switched map
**
** This looks at the system-dependent configuration and builds
** a sequence map that does the same thing.
**
** Every system must define a switch_map_find routine in conf.c
** that will return the list of service types associated with a
** given service class.
*/
bool
int mode;
{
int mapno;
int nmaps;
char *maptype[MAXMAPSTACK];
sm_dprintf("switch_map_open(%s, %s, %d)\n",
{
}
return false;
{
register STAB *s;
continue;
if (s == NULL)
{
syserr("Switch map %s: unknown member map %s",
}
else
{
sm_dprintf("\tmap_stack[%d] = %s:%s\n",
nbuf);
}
}
return true;
}
#if 0
/*
** SEQ_MAP_CLOSE -- close all underlying maps
*/
void
{
int mapno;
{
continue;
}
}
#endif /* 0 */
/*
** SEQ_MAP_LOOKUP -- sequenced map lookup
*/
char *
char *key;
char **args;
int *pstat;
{
int mapno;
int mapbit = 0x01;
bool tempfail = false;
{
char *rv;
continue;
{
{
*pstat = EX_UNAVAILABLE;
return NULL;
}
continue;
}
return rv;
if (*pstat == EX_TEMPFAIL)
{
return NULL;
tempfail = true;
}
break;
}
if (tempfail)
*pstat = EX_TEMPFAIL;
*pstat = EX_NOTFOUND;
return NULL;
}
/*
** SEQ_MAP_STORE -- sequenced map store
*/
void
char *key;
char *val;
{
int mapno;
sm_dprintf("seq_map_store(%s, %s, %s)\n",
{
continue;
return;
}
syserr("seq_map_store(%s, %s, %s): no writable map",
}
/*
** NULL stubs
*/
/* ARGSUSED */
bool
int mode;
{
return true;
}
/* ARGSUSED */
void
{
return;
}
char *
char *key;
char **args;
int *pstat;
{
*pstat = EX_NOTFOUND;
return NULL;
}
/* ARGSUSED */
void
char *key;
char *val;
{
return;
}
/*
** BOGUS stubs
*/
char *
char *key;
char **args;
int *pstat;
{
*pstat = EX_TEMPFAIL;
return NULL;
}
{
"bogus-map", NULL, 0,
};
/*
** MACRO modules
*/
char *
char *name;
char **av;
int *statp;
{
int mid;
*name == '\0' ||
{
return NULL;
}
else
return "";
}
/*
** REGEX modules
*/
#if MAP_REGEX
# include <regex.h>
# define DEFAULT_DELIM CONDELSE
# define END_OF_FIELDS -1
# define ERRBUF_SIZE 80
# define MAX_MATCH 32
struct regex_map
{
int *regex_subfields; /* move to type MAP */
char *regex_delim; /* move to type MAP */
};
static int parse_fields __P((char *, int *, int, int));
static int
char *s;
int *ibuf; /* array */
int blen; /* number of elements in ibuf */
int nr_substrings; /* number of substrings in the pattern */
{
register char *cp;
int i = 0;
bool lastone = false;
blen--; /* for terminating END_OF_FIELDS */
cp = s;
do
{
for (;; cp++)
{
if (*cp == ',')
{
*cp = '\0';
break;
}
if (*cp == '\0')
{
lastone = true;
break;
}
}
if (i < blen)
{
{
syserr("field (%d) out of range, only %d substrings in pattern",
val, nr_substrings);
return -1;
}
}
else
{
return -1;
}
s = ++cp;
} while (!lastone);
ibuf[i] = END_OF_FIELDS;
return i;
}
bool
char *ap;
{
int regerr;
register char *p;
int pflags;
sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
p = ap;
for (;;)
{
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'n': /* not */
break;
case 'f': /* case sensitive */
break;
case 'b': /* basic regular expressions */
pflags &= ~REG_EXTENDED;
break;
case 's': /* substring match () syntax */
sub_param = ++p;
break;
case 'd': /* delimiter */
map_p->regex_delim = ++p;
break;
case 'a': /* map append */
break;
case 'm': /* matchonly */
break;
case 'q':
break;
case 'S':
map->map_spacesub = *++p;
break;
case 'D':
break;
}
p++;
if (*p != '\0')
*p++ = '\0';
}
{
/* Errorhandling */
char errbuf[ERRBUF_SIZE];
return false;
}
else
{
/* substring matching */
int substrings;
sm_dprintf("regex_map_init: nr of substrings %d\n",
if (substrings >= MAX_MATCH)
{
return false;
}
{
/* optional parameter -sfields */
return false;
}
else
{
int i;
/* set default fields */
for (i = 0; i < substrings; i++)
fields[i] = i;
fields[i] = END_OF_FIELDS;
}
{
int *ip;
sm_dprintf("regex_map_init: subfields");
sm_dprintf("\n");
}
}
return true;
}
static char *
const char *s;
char **av;
{
else
}
char *
char *name;
char **av;
int *statp;
{
int reg_res;
{
char **cpp;
}
{
/* option -n */
if (reg_res == REG_NOMATCH)
else
return NULL;
}
if (reg_res == REG_NOMATCH)
return NULL;
{
/* option -s */
bool first = true;
bool quotemode = false, bslashmode = false;
int *ip;
{
{
return NULL;
}
}
else
{
if (!first)
{
{
}
}
else
first = false;
continue;
{
{
if (bslashmode)
{
bslashmode = false;
}
*sp != '\\')
{
}
{
case '\\':
bslashmode = true;
break;
case '(':
cmntcnt++;
break;
case ')':
cmntcnt--;
break;
case '<':
anglecnt++;
break;
case '>':
anglecnt--;
break;
case ' ':
spacecnt++;
break;
case '"':
break;
}
}
}
}
bslashmode || spacecnt != 0)
{
"Warning: regex may cause prescan() failure map=%s lookup=%s",
return NULL;
}
*dp = '\0';
}
}
#endif /* MAP_REGEX */
/*
** NSD modules
*/
#if MAP_NSD
# include <ndbm.h>
# define _DATUM_DEFINED
# include <ns_api.h>
typedef struct ns_map_list
{
char *mapname;
struct ns_map_list *next;
static ns_map_t *
char *mapname;
{
/* walk the list of maps looking for the correctly named map */
{
break;
}
/* if we are looking at a NULL ns_map_list_t, then create a new one */
{
}
}
char *
char *name;
char **av;
int *statp;
{
int buflen, r;
char *p;
{
sm_dprintf("nsd_map_t_find failed\n");
*statp = EX_UNAVAILABLE;
return NULL;
}
if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
{
*statp = EX_TEMPFAIL;
return NULL;
}
if (r == NS_BADREQ
# ifdef NS_NOPERM
|| r == NS_NOPERM
# endif /* NS_NOPERM */
)
{
return NULL;
}
if (r != NS_SUCCESS)
{
*statp = EX_NOTFOUND;
return NULL;
}
/* Null out trailing \n */
*p = '\0';
}
#endif /* MAP_NSD */
char *
char *name;
char **av;
int *statp;
{
long r;
long v[2];
bool res = false;
bool boolres;
static char result[16];
char **cpp;
{
}
r = 0;
boolres = false;
/*
** read arguments for arith map
** - no check is made whether they are really numbers
** - just ignores args after the second
*/
/* operator and (at least) two operands given? */
{
switch (*name)
{
case '|':
r = v[0] | v[1];
break;
case '&':
r = v[0] & v[1];
break;
case '%':
if (v[1] == 0)
return NULL;
r = v[0] % v[1];
break;
case '+':
r = v[0] + v[1];
break;
case '-':
r = v[0] - v[1];
break;
case '*':
r = v[0] * v[1];
break;
case '/':
if (v[1] == 0)
return NULL;
r = v[0] / v[1];
break;
case 'l':
res = v[0] < v[1];
boolres = true;
break;
case '=':
res = v[0] == v[1];
boolres = true;
break;
default:
/* XXX */
if (LogLevel > 10)
"arith_map: unknown operator %c",
return NULL;
}
if (boolres)
else
return result;
}
return NULL;
}
#if SOCKETMAP
# endif /* NETINET || NETINET6 */
# define socket_map_next map_stack[0]
/*
** SOCKET_MAP_OPEN -- open socket table
*/
bool
int mode;
{
STAB *s;
int sock = 0;
SOCKADDR_LEN_T addrlen = 0;
int addrno = 0;
int save_errno;
char *p;
char *colon;
char *at;
sm_dprintf("socket_map_open(%s, %s, %d)\n",
/* sendmail doesn't have the ability to write to SOCKET (yet) */
{
/* issue a pseudo-error message */
return false;
}
{
syserr("socket map \"%s\": empty or missing socket information",
return false;
}
if (s->s_socketmap != NULL)
{
/* Copy open connection */
/* Add this map as head of linked list */
s->s_socketmap = map;
sm_dprintf("using cached connection\n");
return true;
}
sm_dprintf("opening new connection\n");
/* following code is ripped from milter.c */
/* XXX It should be put in a library... */
/* protocol:filename or protocol:port@host */
{
*colon = '\0';
if (*p == '\0')
{
# if NETUNIX
/* default to AF_UNIX */
# else /* NETUNIX */
# if NETINET
/* default to AF_INET */
# else /* NETINET */
# if NETINET6
/* default to AF_INET6 */
# else /* NETINET6 */
/* no protocols available */
syserr("socket map \"%s\": no valid socket protocols available",
return false;
# endif /* NETINET6 */
# endif /* NETINET */
# endif /* NETUNIX */
}
# if NETUNIX
else if (sm_strcasecmp(p, "unix") == 0 ||
sm_strcasecmp(p, "local") == 0)
# endif /* NETUNIX */
# if NETINET
else if (sm_strcasecmp(p, "inet") == 0)
# endif /* NETINET */
# if NETINET6
else if (sm_strcasecmp(p, "inet6") == 0)
# endif /* NETINET6 */
else
{
# ifdef EPROTONOSUPPORT
# else /* EPROTONOSUPPORT */
# endif /* EPROTONOSUPPORT */
syserr("socket map \"%s\": unknown socket type %s",
return false;
}
*colon++ = ':';
}
else
{
colon = p;
#if NETUNIX
/* default to AF_UNIX */
#else /* NETUNIX */
# if NETINET
/* default to AF_INET */
# else /* NETINET */
# if NETINET6
/* default to AF_INET6 */
# else /* NETINET6 */
syserr("socket map \"%s\": unknown socket type %s",
return false;
# endif /* NETINET6 */
# endif /* NETINET */
#endif /* NETUNIX */
}
# if NETUNIX
{
{
syserr("socket map \"%s\": local socket name %s too long",
return false;
}
if (errno != 0)
{
/* if not safe, don't create */
syserr("socket map \"%s\": local socket name %s unsafe",
return false;
}
addrlen = sizeof (struct sockaddr_un);
}
else
# endif /* NETUNIX */
if (false
# if NETINET
# endif /* NETINET */
# if NETINET6
# endif /* NETINET6 */
)
{
unsigned short port;
/* Parse port@host */
{
syserr("socket map \"%s\": bad address %s (expected port@host)",
return false;
}
*at = '\0';
else
{
# ifdef NO_GETSERVBYNAME
syserr("socket map \"%s\": invalid port number %s",
return false;
# else /* NO_GETSERVBYNAME */
{
syserr("socket map \"%s\": unknown port name %s",
return false;
}
# endif /* NO_GETSERVBYNAME */
}
*at++ = '@';
if (*at == '[')
{
char *end;
{
bool found = false;
# if NETINET
unsigned long hid = INADDR_NONE;
# endif /* NETINET */
# if NETINET6
struct sockaddr_in6 hid6;
# endif /* NETINET6 */
*end = '\0';
# if NETINET
{
found = true;
}
# endif /* NETINET */
# if NETINET6
{
found = true;
}
# endif /* NETINET6 */
*end = ']';
if (!found)
{
syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
return false;
}
}
else
{
syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
return false;
}
}
else
{
{
syserr("socket map \"%s\": Unknown host name %s",
return false;
}
switch (hp->h_addrtype)
{
# if NETINET
case AF_INET:
addrlen = sizeof (struct sockaddr_in);
addrno = 1;
break;
# endif /* NETINET */
# if NETINET6
case AF_INET6:
addrlen = sizeof (struct sockaddr_in6);
addrno = 1;
break;
# endif /* NETINET6 */
default:
syserr("socket map \"%s\": Unknown protocol for %s (%d)",
# if NETINET6
# endif /* NETINET6 */
return false;
}
}
}
else
# endif /* NETINET || NETINET6 */
{
syserr("socket map \"%s\": unknown socket protocol",
return false;
}
/* nope, actually connecting */
for (;;)
{
if (sock < 0)
{
save_errno = errno;
sm_dprintf("socket map \"%s\": error creating socket: %s\n",
# if NETINET6
# endif /* NETINET6 */
return false;
}
break;
/* couldn't connect.... try next address */
save_errno = errno;
p = CurHostName;
CurHostName = at;
sm_dprintf("socket_open (%s): open %s failed: %s\n",
CurHostName = p;
/* try next address */
{
{
# if NETINET
case AF_INET:
INADDRSZ);
break;
# endif /* NETINET */
# if NETINET6
case AF_INET6:
break;
# endif /* NETINET6 */
default:
sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
hp->h_addrtype);
# if NETINET6
# endif /* NETINET6 */
return false;
}
continue;
}
p = CurHostName;
CurHostName = at;
sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
CurHostName = p;
# if NETINET6
# endif /* NETINET6 */
return false;
}
# if NETINET6
{
}
# endif /* NETINET6 */
(void *) &sock,
{
sm_dprintf("socket_open (%s): failed to create stream: %s\n",
return false;
}
/* Save connection for reuse */
s->s_socketmap = map;
return true;
}
/*
** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
**
** Cache SOCKET connections based on the connection specifier
** and PID so we don't have multiple connections open to
** the same server for different maps. Need a separate connection
** per PID since a parent process may close the map before the
** child is done with it.
**
** Parameters:
** conn -- SOCKET map connection specifier
**
** Returns:
** Symbol table entry for the SOCKET connection.
*/
static STAB *
const char *conn;
{
char *nbuf;
return s;
}
/*
** SOCKET_MAP_CLOSE -- close the socket
*/
void
{
STAB *s;
(long) CurrentPid);
/* Check if already closed */
{
sm_dprintf("socket_map_close(%s) already closed\n",
return;
}
/* Mark all the maps that share the connection as closed */
smap = s->s_socketmap;
{
sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
}
s->s_socketmap = NULL;
}
/*
** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
*/
char *
char *name;
char **av;
int *statp;
{
SM_FILE_T *f;
sm_dprintf("socket_map_lookup(%s, %s) %s\n",
{
}
else
(sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
(sm_io_error(f)))
{
syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
*statp = EX_TEMPFAIL;
goto errcl;
}
{
syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
*statp = EX_TEMPFAIL;
goto errcl;
}
if (replylen > SOCKETMAP_MAXL)
{
syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
*statp = EX_TEMPFAIL;
goto errcl;
}
{
syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
*statp = EX_TEMPFAIL;
goto error;
}
{
syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
goto error;
}
{
syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
*statp = EX_TEMPFAIL;
goto errcl;
}
{
syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
*statp = EX_TEMPFAIL;
goto errcl;
}
{
*value = '\0';
value++;
}
{
/* collect the return value */
else
}
{
*statp = EX_NOTFOUND;
sm_dprintf("socket_map_lookup(%s): %s not found\n",
}
else
{
sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
*statp = EX_TEMPFAIL;
*statp = EX_UNAVAILABLE;
else
*statp = EX_PROTOCOL;
}
return rval;
return rval;
}
#endif /* SOCKETMAP */