da7d7b9c86
MFC after: 3 days
8126 lines
168 KiB
C
8126 lines
168 KiB
C
/*
|
|
* Copyright (c) 1998-2008 Proofpoint, 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.
|
|
*
|
|
*/
|
|
|
|
#include <sendmail.h>
|
|
|
|
SM_RCSID("@(#)$Id: map.c,v 8.713 2013-11-22 20:51:55 ca Exp $")
|
|
|
|
#if LDAPMAP
|
|
# include <sm/ldap.h>
|
|
#endif /* LDAPMAP */
|
|
|
|
#if NDBM
|
|
# include <ndbm.h>
|
|
# ifdef R_FIRST
|
|
ERROR README: You are running the Berkeley DB version of ndbm.h. See
|
|
ERROR README: the README file about tweaking Berkeley DB so it can
|
|
ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
|
|
ERROR README: and use -DNEWDB instead.
|
|
# endif /* R_FIRST */
|
|
#endif /* NDBM */
|
|
#if NEWDB
|
|
# include "sm/bdb.h"
|
|
#endif /* NEWDB */
|
|
#if NIS
|
|
struct dom_binding; /* forward reference needed on IRIX */
|
|
# include <rpcsvc/ypclnt.h>
|
|
# if NDBM
|
|
# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
|
|
# endif /* NDBM */
|
|
#endif /* NIS */
|
|
|
|
#include "map.h"
|
|
|
|
#if NEWDB
|
|
# if DB_VERSION_MAJOR < 2
|
|
static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
# if DB_VERSION_MAJOR == 2
|
|
static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
|
|
# endif /* DB_VERSION_MAJOR == 2 */
|
|
# if DB_VERSION_MAJOR > 2
|
|
static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
#endif /* NEWDB */
|
|
static bool extract_canonname __P((char *, char *, char *, char[], int));
|
|
static void map_close __P((STAB *, int));
|
|
static void map_init __P((STAB *, int));
|
|
#ifdef LDAPMAP
|
|
static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
|
|
#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
|
|
static STAB *socket_map_findconn __P((const char*));
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
|
|
# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
|
|
#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
|
|
map_parseargs(map, ap)
|
|
MAP *map;
|
|
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.
|
|
*/
|
|
|
|
map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
|
|
map->map_spacesub = SpaceSub; /* default value */
|
|
for (;;)
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
switch (*++p)
|
|
{
|
|
case 'N':
|
|
map->map_mflags |= MF_INCLNULL;
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
break;
|
|
|
|
case 'O':
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
break;
|
|
|
|
case 'o':
|
|
map->map_mflags |= MF_OPTIONAL;
|
|
break;
|
|
|
|
case 'f':
|
|
map->map_mflags |= MF_NOFOLDCASE;
|
|
break;
|
|
|
|
case 'm':
|
|
map->map_mflags |= MF_MATCHONLY;
|
|
break;
|
|
|
|
case 'A':
|
|
map->map_mflags |= MF_APPEND;
|
|
break;
|
|
|
|
case 'q':
|
|
map->map_mflags |= MF_KEEPQUOTES;
|
|
break;
|
|
|
|
case 'a':
|
|
map->map_app = ++p;
|
|
break;
|
|
|
|
case 'd':
|
|
{
|
|
char *h;
|
|
|
|
++p;
|
|
h = strchr(p, ' ');
|
|
if (h != NULL)
|
|
*h = '\0';
|
|
map->map_timeout = convtime(p, 's');
|
|
if (h != NULL)
|
|
*h = ' ';
|
|
}
|
|
break;
|
|
|
|
case 'T':
|
|
map->map_tapp = ++p;
|
|
break;
|
|
|
|
case 'k':
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
map->map_keycolnm = p;
|
|
break;
|
|
|
|
case 'v':
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
map->map_valcolnm = p;
|
|
break;
|
|
|
|
case 'z':
|
|
if (*++p != '\\')
|
|
map->map_coldelim = *p;
|
|
else
|
|
{
|
|
switch (*++p)
|
|
{
|
|
case 'n':
|
|
map->map_coldelim = '\n';
|
|
break;
|
|
|
|
case 't':
|
|
map->map_coldelim = '\t';
|
|
break;
|
|
|
|
default:
|
|
map->map_coldelim = '\\';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
map->map_mflags |= MF_NODEFER;
|
|
break;
|
|
|
|
|
|
case 'S':
|
|
map->map_spacesub = *++p;
|
|
break;
|
|
|
|
case 'D':
|
|
map->map_mflags |= MF_DEFER;
|
|
break;
|
|
|
|
default:
|
|
syserr("Illegal option %c map %s", *p, map->map_mname);
|
|
break;
|
|
}
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
p++;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
if (map->map_app != NULL)
|
|
map->map_app = newstr(map->map_app);
|
|
if (map->map_tapp != NULL)
|
|
map->map_tapp = newstr(map->map_tapp);
|
|
if (map->map_keycolnm != NULL)
|
|
map->map_keycolnm = newstr(map->map_keycolnm);
|
|
if (map->map_valcolnm != NULL)
|
|
map->map_valcolnm = newstr(map->map_valcolnm);
|
|
|
|
if (*p != '\0')
|
|
{
|
|
map->map_file = p;
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
p++;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
map->map_file = newstr(map->map_file);
|
|
}
|
|
|
|
while (*p != '\0' && isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '\0')
|
|
map->map_rebuild = newstr(p);
|
|
|
|
if (map->map_file == NULL &&
|
|
!bitset(MCF_OPTFILE, map->map_class->map_cflags))
|
|
{
|
|
syserr("No file name for %s map %s",
|
|
map->map_class->map_cname, map->map_mname);
|
|
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 *
|
|
map_rewrite(map, s, slen, av)
|
|
register MAP *map;
|
|
register const char *s;
|
|
size_t slen;
|
|
char **av;
|
|
{
|
|
register char *bp;
|
|
register char c;
|
|
char **avp;
|
|
register char *ap;
|
|
size_t l;
|
|
size_t len;
|
|
static size_t buflen = 0;
|
|
static char *buf = NULL;
|
|
|
|
if (tTd(39, 1))
|
|
{
|
|
sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
|
|
if (av == NULL)
|
|
sm_dprintf(" (nullv)");
|
|
else
|
|
{
|
|
for (avp = av; *avp != NULL; avp++)
|
|
sm_dprintf("\n\t%s", *avp);
|
|
}
|
|
sm_dprintf("\n");
|
|
}
|
|
|
|
/* count expected size of output (can safely overestimate) */
|
|
l = len = slen;
|
|
if (av != NULL)
|
|
{
|
|
const char *sp = s;
|
|
|
|
while (l-- > 0 && (c = *sp++) != '\0')
|
|
{
|
|
if (c != '%')
|
|
continue;
|
|
if (l-- <= 0)
|
|
break;
|
|
c = *sp++;
|
|
if (!(isascii(c) && isdigit(c)))
|
|
continue;
|
|
for (avp = av; --c >= '0' && *avp != NULL; avp++)
|
|
continue;
|
|
if (*avp == NULL)
|
|
continue;
|
|
len += strlen(*avp);
|
|
}
|
|
}
|
|
if (map->map_app != NULL)
|
|
len += strlen(map->map_app);
|
|
if (buflen < ++len)
|
|
{
|
|
/* need to malloc additional space */
|
|
buflen = len;
|
|
if (buf != NULL)
|
|
sm_free(buf);
|
|
buf = sm_pmalloc_x(buflen);
|
|
}
|
|
|
|
bp = buf;
|
|
if (av == NULL)
|
|
{
|
|
memmove(bp, s, slen);
|
|
bp += slen;
|
|
|
|
/* assert(len > slen); */
|
|
len -= slen;
|
|
}
|
|
else
|
|
{
|
|
while (slen-- > 0 && (c = *s++) != '\0')
|
|
{
|
|
if (c != '%')
|
|
{
|
|
pushc:
|
|
if (len-- <= 1)
|
|
break;
|
|
*bp++ = c;
|
|
continue;
|
|
}
|
|
if (slen-- <= 0 || (c = *s++) == '\0')
|
|
c = '%';
|
|
if (c == '%')
|
|
goto pushc;
|
|
if (!(isascii(c) && isdigit(c)))
|
|
{
|
|
if (len-- <= 1)
|
|
break;
|
|
*bp++ = '%';
|
|
goto pushc;
|
|
}
|
|
for (avp = av; --c >= '0' && *avp != NULL; avp++)
|
|
continue;
|
|
if (*avp == NULL)
|
|
continue;
|
|
|
|
/* transliterate argument into output string */
|
|
for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
|
|
*bp++ = c;
|
|
}
|
|
}
|
|
if (map->map_app != NULL && len > 0)
|
|
(void) sm_strlcpy(bp, map->map_app, len);
|
|
else
|
|
*bp = '\0';
|
|
if (tTd(39, 1))
|
|
sm_dprintf("map_rewrite => %s\n", buf);
|
|
return buf;
|
|
}
|
|
/*
|
|
** INITMAPS -- rebuild alias maps
|
|
**
|
|
** Parameters:
|
|
** none.
|
|
**
|
|
** Returns:
|
|
** none.
|
|
*/
|
|
|
|
void
|
|
initmaps()
|
|
{
|
|
#if XDEBUG
|
|
checkfd012("entering initmaps");
|
|
#endif /* XDEBUG */
|
|
stabapply(map_init, 0);
|
|
#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
|
|
map_init(s, unused)
|
|
register STAB *s;
|
|
int unused;
|
|
{
|
|
register MAP *map;
|
|
|
|
/* has to be a map */
|
|
if (s->s_symtype != ST_MAP)
|
|
return;
|
|
|
|
map = &s->s_map;
|
|
if (!bitset(MF_VALID, map->map_mflags))
|
|
return;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("map_init(%s:%s, %s)\n",
|
|
map->map_class->map_cname == NULL ? "NULL" :
|
|
map->map_class->map_cname,
|
|
map->map_mname == NULL ? "NULL" : map->map_mname,
|
|
map->map_file == NULL ? "NULL" : map->map_file);
|
|
|
|
if (!bitset(MF_ALIAS, map->map_mflags) ||
|
|
!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
|
|
{
|
|
if (tTd(38, 3))
|
|
sm_dprintf("\tnot rebuildable\n");
|
|
return;
|
|
}
|
|
|
|
/* if already open, close it (for nested open) */
|
|
if (bitset(MF_OPEN, map->map_mflags))
|
|
{
|
|
map->map_mflags |= MF_CLOSING;
|
|
map->map_class->map_close(map);
|
|
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
|
|
}
|
|
|
|
(void) rebuildaliases(map, false);
|
|
return;
|
|
}
|
|
/*
|
|
** OPENMAP -- open a map
|
|
**
|
|
** Parameters:
|
|
** map -- map to open (it must not be open).
|
|
**
|
|
** Returns:
|
|
** whether open succeeded.
|
|
*/
|
|
|
|
bool
|
|
openmap(map)
|
|
MAP *map;
|
|
{
|
|
bool restore = false;
|
|
bool savehold = HoldErrs;
|
|
bool savequick = QuickAbort;
|
|
int saveerrors = Errors;
|
|
|
|
if (!bitset(MF_VALID, map->map_mflags))
|
|
return false;
|
|
|
|
/* better safe than sorry... */
|
|
if (bitset(MF_OPEN, map->map_mflags))
|
|
return true;
|
|
|
|
/* Don't send a map open error out via SMTP */
|
|
if ((OnlyOneError || QuickAbort) &&
|
|
(OpMode == MD_SMTP || OpMode == MD_DAEMON))
|
|
{
|
|
restore = true;
|
|
HoldErrs = true;
|
|
QuickAbort = false;
|
|
}
|
|
|
|
errno = 0;
|
|
if (map->map_class->map_open(map, O_RDONLY))
|
|
{
|
|
if (tTd(38, 4))
|
|
sm_dprintf("openmap()\t%s:%s %s: valid\n",
|
|
map->map_class->map_cname == NULL ? "NULL" :
|
|
map->map_class->map_cname,
|
|
map->map_mname == NULL ? "NULL" :
|
|
map->map_mname,
|
|
map->map_file == NULL ? "NULL" :
|
|
map->map_file);
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
}
|
|
else
|
|
{
|
|
if (tTd(38, 4))
|
|
sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
|
|
map->map_class->map_cname == NULL ? "NULL" :
|
|
map->map_class->map_cname,
|
|
map->map_mname == NULL ? "NULL" :
|
|
map->map_mname,
|
|
map->map_file == NULL ? "NULL" :
|
|
map->map_file,
|
|
errno == 0 ? "" : ": ",
|
|
errno == 0 ? "" : sm_errstring(errno));
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
extern MAPCLASS BogusMapClass;
|
|
|
|
map->map_orgclass = map->map_class;
|
|
map->map_class = &BogusMapClass;
|
|
map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
|
|
map->map_pid = CurrentPid;
|
|
}
|
|
else
|
|
{
|
|
/* don't try again */
|
|
map->map_mflags &= ~MF_VALID;
|
|
}
|
|
}
|
|
|
|
if (restore)
|
|
{
|
|
Errors = saveerrors;
|
|
HoldErrs = savehold;
|
|
QuickAbort = savequick;
|
|
}
|
|
|
|
return bitset(MF_OPEN, map->map_mflags);
|
|
}
|
|
/*
|
|
** CLOSEMAPS -- close all open maps opened by the current pid.
|
|
**
|
|
** Parameters:
|
|
** bogus -- only close bogus maps.
|
|
**
|
|
** Returns:
|
|
** none.
|
|
*/
|
|
|
|
void
|
|
closemaps(bogus)
|
|
bool bogus;
|
|
{
|
|
stabapply(map_close, 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
|
|
map_close(s, bogus)
|
|
register STAB *s;
|
|
int bogus; /* int because of stabapply(), used as bool */
|
|
{
|
|
MAP *map;
|
|
extern MAPCLASS BogusMapClass;
|
|
|
|
if (s->s_symtype != ST_MAP)
|
|
return;
|
|
|
|
map = &s->s_map;
|
|
|
|
/*
|
|
** 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)
|
|
*/
|
|
|
|
if (!bitset(MF_VALID, map->map_mflags) ||
|
|
!bitset(MF_OPEN, map->map_mflags) ||
|
|
bitset(MF_CLOSING, map->map_mflags) ||
|
|
map->map_pid != CurrentPid ||
|
|
(bogus && map->map_class != &BogusMapClass &&
|
|
!bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
|
|
return;
|
|
|
|
if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
|
|
map->map_orgclass != &BogusMapClass)
|
|
map->map_class = map->map_orgclass;
|
|
if (tTd(38, 5))
|
|
sm_dprintf("closemaps: closing %s (%s)\n",
|
|
map->map_mname == NULL ? "NULL" : map->map_mname,
|
|
map->map_file == NULL ? "NULL" : map->map_file);
|
|
|
|
if (!bitset(MF_OPENBOGUS, map->map_mflags))
|
|
{
|
|
map->map_mflags |= MF_CLOSING;
|
|
map->map_class->map_close(map);
|
|
}
|
|
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
|
|
}
|
|
|
|
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
|
|
extern int getdomainname();
|
|
|
|
/* this is mainly for backward compatibility in Sun environment */
|
|
static char *
|
|
sun_init_domain()
|
|
{
|
|
/*
|
|
** 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 buf[MAXNAME];
|
|
char *period, *autodomain;
|
|
|
|
if (getdomainname(buf, sizeof buf) < 0)
|
|
return NULL;
|
|
|
|
if (buf[0] == '\0')
|
|
return NULL;
|
|
|
|
if (tTd(0, 20))
|
|
printf("domainname = %s\n", buf);
|
|
|
|
if (buf[0] == '+')
|
|
buf[0] = '.';
|
|
period = strchr(buf, '.');
|
|
if (period == NULL)
|
|
autodomain = buf;
|
|
else
|
|
autodomain = period + 1;
|
|
if (strchr(autodomain, '.') == NULL)
|
|
return newstr(buf);
|
|
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
|
|
getcanonname(host, hbsize, trymx, pttl)
|
|
char *host;
|
|
int hbsize;
|
|
bool trymx;
|
|
int *pttl;
|
|
{
|
|
int nmaps;
|
|
int mapno;
|
|
bool found = false;
|
|
bool got_tempfail = false;
|
|
auto int status = EX_UNAVAILABLE;
|
|
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
|
|
|
|
nmaps = switch_map_find("hosts", maptype, mapreturn);
|
|
if (pttl != 0)
|
|
*pttl = SM_DEFAULT_TTL;
|
|
for (mapno = 0; mapno < nmaps; mapno++)
|
|
{
|
|
int i;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("getcanonname(%s), trying %s\n",
|
|
host, maptype[mapno]);
|
|
if (strcmp("files", maptype[mapno]) == 0)
|
|
{
|
|
found = text_getcanonname(host, hbsize, &status);
|
|
}
|
|
#if NIS
|
|
else if (strcmp("nis", maptype[mapno]) == 0)
|
|
{
|
|
found = nis_getcanonname(host, hbsize, &status);
|
|
# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
|
|
if (nis_domain == NULL)
|
|
nis_domain = sun_init_domain();
|
|
# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
|
|
}
|
|
#endif /* NIS */
|
|
#if NISPLUS
|
|
else if (strcmp("nisplus", maptype[mapno]) == 0)
|
|
{
|
|
found = nisplus_getcanonname(host, hbsize, &status);
|
|
# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
|
|
if (nis_domain == NULL)
|
|
nis_domain = sun_init_domain();
|
|
# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
|
|
}
|
|
#endif /* NISPLUS */
|
|
#if NAMED_BIND
|
|
else if (strcmp("dns", maptype[mapno]) == 0)
|
|
{
|
|
found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
|
|
}
|
|
#endif /* NAMED_BIND */
|
|
#if NETINFO
|
|
else if (strcmp("netinfo", maptype[mapno]) == 0)
|
|
{
|
|
found = ni_getcanonname(host, hbsize, &status);
|
|
}
|
|
#endif /* NETINFO */
|
|
else
|
|
{
|
|
found = false;
|
|
status = EX_UNAVAILABLE;
|
|
}
|
|
|
|
/*
|
|
** 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
|
|
** problems if /etc/hosts has no FQDN but is listed first
|
|
** in the service switch.
|
|
*/
|
|
|
|
if (found &&
|
|
(macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
|
|
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;
|
|
if (bitset(1 << mapno, mapreturn[i]))
|
|
break;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
char *d;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("getcanonname(%s), found\n", host);
|
|
|
|
/*
|
|
** 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 = strchr(host, '.')) == NULL || d[1] == '\0')
|
|
{
|
|
d = macvalue('m', CurEnv);
|
|
if (d != NULL &&
|
|
hbsize > (int) (strlen(host) + strlen(d) + 1))
|
|
{
|
|
if (host[strlen(host) - 1] != '.')
|
|
(void) sm_strlcat2(host, ".", d,
|
|
hbsize);
|
|
else
|
|
(void) sm_strlcat(host, d, hbsize);
|
|
}
|
|
else
|
|
{
|
|
#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
|
|
if (VendorCode == VENDOR_SUN &&
|
|
should_try_nis_domain)
|
|
{
|
|
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 (VendorCode == VENDOR_SUN && should_try_nis_domain)
|
|
{
|
|
try_nis_domain:
|
|
if (nis_domain != NULL &&
|
|
strlen(nis_domain) + strlen(host) + 1 < hbsize)
|
|
{
|
|
(void) sm_strlcat2(host, ".", nis_domain, hbsize);
|
|
return true;
|
|
}
|
|
}
|
|
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
|
|
status);
|
|
|
|
if (got_tempfail)
|
|
SM_SET_H_ERRNO(TRY_AGAIN);
|
|
else
|
|
SM_SET_H_ERRNO(HOST_NOT_FOUND);
|
|
|
|
return false;
|
|
}
|
|
/*
|
|
** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
|
|
**
|
|
** Parameters:
|
|
** name -- the name against which to match.
|
|
** dot -- where to reinsert '.' to get FQDN
|
|
** line -- the /etc/hosts line.
|
|
** 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
|
|
extract_canonname(name, dot, line, cbuf, cbuflen)
|
|
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++)
|
|
{
|
|
char nbuf[MAXNAME + 1];
|
|
|
|
p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
|
|
if (p == NULL)
|
|
break;
|
|
if (*p == '\0')
|
|
continue;
|
|
if (cbuf[0] == '\0' ||
|
|
(strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
|
|
{
|
|
(void) sm_strlcpy(cbuf, p, cbuflen);
|
|
}
|
|
if (sm_strcasecmp(name, p) == 0)
|
|
found = true;
|
|
else if (dot != NULL)
|
|
{
|
|
/* try looking for the FQDN as well */
|
|
*dot = '.';
|
|
if (sm_strcasecmp(name, p) == 0)
|
|
found = true;
|
|
*dot = '\0';
|
|
}
|
|
}
|
|
if (found && strchr(cbuf, '.') == NULL)
|
|
{
|
|
/* try to add a domain on the end of the name */
|
|
char *domain = macvalue('m', CurEnv);
|
|
|
|
if (domain != NULL &&
|
|
strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
|
|
{
|
|
p = &cbuf[i];
|
|
*p++ = '.';
|
|
(void) sm_strlcpy(p, domain, cbuflen - i - 1);
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/*
|
|
** DNS modules
|
|
*/
|
|
|
|
#if NAMED_BIND
|
|
# if DNSMAP
|
|
|
|
# include "sm_resolve.h"
|
|
# if NETINET || NETINET6
|
|
# include <arpa/inet.h>
|
|
# endif /* NETINET || NETINET6 */
|
|
|
|
/*
|
|
** DNS_MAP_OPEN -- stub to check proper value for dns map type
|
|
*/
|
|
|
|
bool
|
|
dns_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
if (tTd(38,2))
|
|
sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
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.
|
|
*/
|
|
|
|
#define map_sizelimit map_lockfd /* overload field */
|
|
|
|
struct dns_map
|
|
{
|
|
int dns_m_type;
|
|
};
|
|
|
|
bool
|
|
dns_map_parseargs(map,args)
|
|
MAP *map;
|
|
char *args;
|
|
{
|
|
register char *p = args;
|
|
struct dns_map *map_p;
|
|
|
|
map_p = (struct dns_map *) xalloc(sizeof(*map_p));
|
|
map_p->dns_m_type = -1;
|
|
map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
|
|
|
|
for (;;)
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
switch (*++p)
|
|
{
|
|
case 'N':
|
|
map->map_mflags |= MF_INCLNULL;
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
break;
|
|
|
|
case 'O':
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
break;
|
|
|
|
case 'o':
|
|
map->map_mflags |= MF_OPTIONAL;
|
|
break;
|
|
|
|
case 'f':
|
|
map->map_mflags |= MF_NOFOLDCASE;
|
|
break;
|
|
|
|
case 'm':
|
|
map->map_mflags |= MF_MATCHONLY;
|
|
break;
|
|
|
|
case 'A':
|
|
map->map_mflags |= MF_APPEND;
|
|
break;
|
|
|
|
case 'q':
|
|
map->map_mflags |= MF_KEEPQUOTES;
|
|
break;
|
|
|
|
case 't':
|
|
map->map_mflags |= MF_NODEFER;
|
|
break;
|
|
|
|
case 'a':
|
|
map->map_app = ++p;
|
|
break;
|
|
|
|
case 'T':
|
|
map->map_tapp = ++p;
|
|
break;
|
|
|
|
case 'd':
|
|
{
|
|
char *h;
|
|
|
|
++p;
|
|
h = strchr(p, ' ');
|
|
if (h != NULL)
|
|
*h = '\0';
|
|
map->map_timeout = convtime(p, 's');
|
|
if (h != NULL)
|
|
*h = ' ';
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
map->map_retry = atoi(p);
|
|
break;
|
|
|
|
case 'z':
|
|
if (*++p != '\\')
|
|
map->map_coldelim = *p;
|
|
else
|
|
{
|
|
switch (*++p)
|
|
{
|
|
case 'n':
|
|
map->map_coldelim = '\n';
|
|
break;
|
|
|
|
case 't':
|
|
map->map_coldelim = '\t';
|
|
break;
|
|
|
|
default:
|
|
map->map_coldelim = '\\';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'Z':
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
map->map_sizelimit = atoi(p);
|
|
break;
|
|
|
|
/* Start of dns_map specific args */
|
|
case 'R': /* search field */
|
|
{
|
|
char *h;
|
|
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
h = strchr(p, ' ');
|
|
if (h != NULL)
|
|
*h = '\0';
|
|
map_p->dns_m_type = dns_string_to_type(p);
|
|
if (h != NULL)
|
|
*h = ' ';
|
|
if (map_p->dns_m_type < 0)
|
|
syserr("dns map %s: wrong type %s",
|
|
map->map_mname, p);
|
|
}
|
|
break;
|
|
|
|
case 'B': /* base domain */
|
|
{
|
|
char *h;
|
|
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
h = strchr(p, ' ');
|
|
if (h != NULL)
|
|
*h = '\0';
|
|
|
|
/*
|
|
** slight abuse of map->map_file; it isn't
|
|
** used otherwise in this map type.
|
|
*/
|
|
|
|
map->map_file = newstr(p);
|
|
if (h != NULL)
|
|
*h = ' ';
|
|
}
|
|
break;
|
|
}
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
p++;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
if (map_p->dns_m_type < 0)
|
|
syserr("dns map %s: missing -R type", map->map_mname);
|
|
if (map->map_app != NULL)
|
|
map->map_app = newstr(map->map_app);
|
|
if (map->map_tapp != NULL)
|
|
map->map_tapp = newstr(map->map_tapp);
|
|
|
|
/*
|
|
** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
|
|
** Even if this assumption is wrong, we use only one byte,
|
|
** so it doesn't really matter.
|
|
*/
|
|
|
|
map->map_db1 = (ARBPTR_T) map_p;
|
|
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 *
|
|
dns_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int resnum = 0;
|
|
char *vp = NULL, *result = NULL;
|
|
size_t vsize;
|
|
struct dns_map *map_p;
|
|
RESOURCE_RECORD_T *rr = NULL;
|
|
DNS_REPLY_T *r = NULL;
|
|
# if NETINET6
|
|
static char buf6[INET6_ADDRSTRLEN];
|
|
# endif /* NETINET6 */
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("dns_map_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
map_p = (struct dns_map *)(map->map_db1);
|
|
if (map->map_file != NULL && *map->map_file != '\0')
|
|
{
|
|
size_t len;
|
|
char *appdomain;
|
|
|
|
len = strlen(map->map_file) + strlen(name) + 2;
|
|
appdomain = (char *) sm_malloc(len);
|
|
if (appdomain == NULL)
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return NULL;
|
|
}
|
|
(void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
|
|
r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
|
|
map->map_timeout, map->map_retry);
|
|
sm_free(appdomain);
|
|
}
|
|
else
|
|
{
|
|
r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
|
|
map->map_timeout, map->map_retry);
|
|
}
|
|
|
|
if (r == NULL)
|
|
{
|
|
result = NULL;
|
|
if (h_errno == TRY_AGAIN || transienterror(errno))
|
|
*statp = EX_TEMPFAIL;
|
|
else
|
|
*statp = EX_NOTFOUND;
|
|
goto cleanup;
|
|
}
|
|
*statp = EX_OK;
|
|
for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
|
|
{
|
|
char *type = NULL;
|
|
char *value = NULL;
|
|
|
|
switch (rr->rr_type)
|
|
{
|
|
case T_NS:
|
|
type = "T_NS";
|
|
value = rr->rr_u.rr_txt;
|
|
break;
|
|
case T_CNAME:
|
|
type = "T_CNAME";
|
|
value = rr->rr_u.rr_txt;
|
|
break;
|
|
case T_AFSDB:
|
|
type = "T_AFSDB";
|
|
value = rr->rr_u.rr_mx->mx_r_domain;
|
|
break;
|
|
case T_SRV:
|
|
type = "T_SRV";
|
|
value = rr->rr_u.rr_srv->srv_r_target;
|
|
break;
|
|
case T_PTR:
|
|
type = "T_PTR";
|
|
value = rr->rr_u.rr_txt;
|
|
break;
|
|
case T_TXT:
|
|
type = "T_TXT";
|
|
value = rr->rr_u.rr_txt;
|
|
break;
|
|
case T_MX:
|
|
type = "T_MX";
|
|
value = rr->rr_u.rr_mx->mx_r_domain;
|
|
break;
|
|
# if NETINET
|
|
case T_A:
|
|
type = "T_A";
|
|
value = inet_ntoa(*(rr->rr_u.rr_a));
|
|
break;
|
|
# endif /* NETINET */
|
|
# if NETINET6
|
|
case T_AAAA:
|
|
type = "T_AAAA";
|
|
value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
|
|
sizeof(buf6));
|
|
break;
|
|
# endif /* NETINET6 */
|
|
}
|
|
|
|
(void) strreplnonprt(value, 'X');
|
|
if (map_p->dns_m_type != rr->rr_type)
|
|
{
|
|
if (tTd(38, 40))
|
|
sm_dprintf("\tskipping type %s (%d) value %s\n",
|
|
type != NULL ? type : "<UNKNOWN>",
|
|
rr->rr_type,
|
|
value != NULL ? value : "<NO VALUE>");
|
|
continue;
|
|
}
|
|
|
|
# if NETINET6
|
|
if (rr->rr_type == T_AAAA && value == NULL)
|
|
{
|
|
result = NULL;
|
|
*statp = EX_DATAERR;
|
|
if (tTd(38, 40))
|
|
sm_dprintf("\tbad T_AAAA conversion\n");
|
|
goto cleanup;
|
|
}
|
|
# endif /* NETINET6 */
|
|
if (tTd(38, 40))
|
|
sm_dprintf("\tfound type %s (%d) value %s\n",
|
|
type != NULL ? type : "<UNKNOWN>",
|
|
rr->rr_type,
|
|
value != NULL ? value : "<NO VALUE>");
|
|
if (value != NULL &&
|
|
(map->map_coldelim == '\0' ||
|
|
map->map_sizelimit == 1 ||
|
|
bitset(MF_MATCHONLY, map->map_mflags)))
|
|
{
|
|
/* Only care about the first match */
|
|
vp = newstr(value);
|
|
break;
|
|
}
|
|
else if (vp == NULL)
|
|
{
|
|
/* First result */
|
|
vp = newstr(value);
|
|
}
|
|
else
|
|
{
|
|
/* concatenate the results */
|
|
int sz;
|
|
char *new;
|
|
|
|
sz = strlen(vp) + strlen(value) + 2;
|
|
new = xalloc(sz);
|
|
(void) sm_snprintf(new, sz, "%s%c%s",
|
|
vp, map->map_coldelim, value);
|
|
sm_free(vp);
|
|
vp = new;
|
|
if (map->map_sizelimit > 0 &&
|
|
++resnum >= map->map_sizelimit)
|
|
break;
|
|
}
|
|
}
|
|
if (vp == NULL)
|
|
{
|
|
result = NULL;
|
|
*statp = EX_NOTFOUND;
|
|
if (tTd(38, 40))
|
|
sm_dprintf("\tno match found\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Cleanly truncate for rulesets */
|
|
truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
|
|
|
|
vsize = strlen(vp);
|
|
|
|
if (LogLevel > 9)
|
|
sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
|
|
name, vp);
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
result = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
result = map_rewrite(map, vp, vsize, av);
|
|
|
|
cleanup:
|
|
if (vp != NULL)
|
|
sm_free(vp);
|
|
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
|
|
ndbm_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
register DBM *dbm;
|
|
int save_errno;
|
|
int dfd;
|
|
int pfd;
|
|
long sff;
|
|
int ret;
|
|
int smode = S_IREAD;
|
|
char dirfile[MAXPATHLEN];
|
|
char pagfile[MAXPATHLEN];
|
|
struct stat st;
|
|
struct stat std, stp;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
map->map_lockfd = -1;
|
|
mode &= O_ACCMODE;
|
|
|
|
/* do initial file and directory checks */
|
|
if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
|
|
map->map_file, ".dir") >= sizeof(dirfile) ||
|
|
sm_strlcpyn(pagfile, sizeof(pagfile), 2,
|
|
map->map_file, ".pag") >= sizeof(pagfile))
|
|
{
|
|
errno = 0;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("dbm map \"%s\": map file %s name too long",
|
|
map->map_mname, map->map_file);
|
|
return false;
|
|
}
|
|
sff = SFF_ROOTOK|SFF_REGONLY;
|
|
if (mode == O_RDWR)
|
|
{
|
|
sff |= SFF_CREAT;
|
|
if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
|
|
sff |= SFF_NOSLINK;
|
|
if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
|
|
sff |= SFF_NOHLINK;
|
|
smode = S_IWRITE;
|
|
}
|
|
else
|
|
{
|
|
if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
|
|
sff |= SFF_NOWLINK;
|
|
}
|
|
if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
|
|
sff |= SFF_SAFEDIRPATH;
|
|
ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
|
|
sff, smode, &std);
|
|
if (ret == 0)
|
|
ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
|
|
sff, smode, &stp);
|
|
|
|
if (ret != 0)
|
|
{
|
|
char *prob = "unsafe";
|
|
|
|
/* cannot open this map */
|
|
if (ret == ENOENT)
|
|
prob = "missing";
|
|
if (tTd(38, 2))
|
|
sm_dprintf("\t%s map file: %d\n", prob, ret);
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("dbm map \"%s\": %s map file %s",
|
|
map->map_mname, prob, map->map_file);
|
|
return false;
|
|
}
|
|
if (std.st_mode == ST_MODE_NOFILE)
|
|
mode |= O_CREAT|O_EXCL;
|
|
|
|
# if LOCK_ON_OPEN
|
|
if (mode == O_RDONLY)
|
|
mode |= O_SHLOCK;
|
|
else
|
|
mode |= O_TRUNC|O_EXLOCK;
|
|
# else /* LOCK_ON_OPEN */
|
|
if ((mode & O_ACCMODE) == O_RDWR)
|
|
{
|
|
# 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.
|
|
*/
|
|
|
|
mode |= O_TRUNC;
|
|
# 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;
|
|
long sff = SFF_CREAT|SFF_OPENASROOT;
|
|
|
|
if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
|
|
sff |= SFF_NOSLINK;
|
|
if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
|
|
sff |= SFF_NOHLINK;
|
|
|
|
dirfd = safeopen(dirfile, mode, DBMMODE, sff);
|
|
pagfd = safeopen(pagfile, mode, DBMMODE, sff);
|
|
|
|
if (dirfd < 0 || pagfd < 0)
|
|
{
|
|
save_errno = errno;
|
|
if (dirfd >= 0)
|
|
(void) close(dirfd);
|
|
if (pagfd >= 0)
|
|
(void) close(pagfd);
|
|
errno = save_errno;
|
|
syserr("ndbm_map_open: cannot create database %s",
|
|
map->map_file);
|
|
return false;
|
|
}
|
|
if (ftruncate(dirfd, (off_t) 0) < 0 ||
|
|
ftruncate(pagfd, (off_t) 0) < 0)
|
|
{
|
|
save_errno = errno;
|
|
(void) close(dirfd);
|
|
(void) close(pagfd);
|
|
errno = save_errno;
|
|
syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
|
|
map->map_file);
|
|
return false;
|
|
}
|
|
|
|
/* if new file, get "before" bits for later filechanged check */
|
|
if (std.st_mode == ST_MODE_NOFILE &&
|
|
(fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
|
|
{
|
|
save_errno = errno;
|
|
(void) close(dirfd);
|
|
(void) close(pagfd);
|
|
errno = save_errno;
|
|
syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
|
|
map->map_file);
|
|
return false;
|
|
}
|
|
|
|
/* have to save the lock for the duration (bletch) */
|
|
map->map_lockfd = dirfd;
|
|
(void) close(pagfd);
|
|
|
|
/* twiddle bits for dbm_open */
|
|
mode &= ~(O_CREAT|O_EXCL);
|
|
# endif /* NOFTRUNCATE */
|
|
}
|
|
# endif /* LOCK_ON_OPEN */
|
|
|
|
/* open the database */
|
|
dbm = dbm_open(map->map_file, mode, DBMMODE);
|
|
if (dbm == NULL)
|
|
{
|
|
save_errno = errno;
|
|
if (bitset(MF_ALIAS, map->map_mflags) &&
|
|
aliaswait(map, ".pag", false))
|
|
return true;
|
|
# if !LOCK_ON_OPEN && !NOFTRUNCATE
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
|
|
errno = save_errno;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("Cannot open DBM database %s", map->map_file);
|
|
return false;
|
|
}
|
|
dfd = dbm_dirfno(dbm);
|
|
pfd = dbm_pagfno(dbm);
|
|
if (dfd == pfd)
|
|
{
|
|
/* heuristic: if files are linked, this is actually gdbm */
|
|
dbm_close(dbm);
|
|
# if !LOCK_ON_OPEN && !NOFTRUNCATE
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
|
|
errno = 0;
|
|
syserr("dbm map \"%s\": cannot support GDBM",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
|
|
if (filechanged(dirfile, dfd, &std) ||
|
|
filechanged(pagfile, pfd, &stp))
|
|
{
|
|
save_errno = errno;
|
|
dbm_close(dbm);
|
|
# if !LOCK_ON_OPEN && !NOFTRUNCATE
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
|
|
errno = save_errno;
|
|
syserr("ndbm_map_open(%s): file changed after open",
|
|
map->map_file);
|
|
return false;
|
|
}
|
|
|
|
map->map_db1 = (ARBPTR_T) dbm;
|
|
|
|
/*
|
|
** Need to set map_mtime before the call to aliaswait()
|
|
** as aliaswait() will call map_lookup() which requires
|
|
** map_mtime to be set
|
|
*/
|
|
|
|
if (fstat(pfd, &st) >= 0)
|
|
map->map_mtime = st.st_mtime;
|
|
|
|
if (mode == O_RDONLY)
|
|
{
|
|
# if LOCK_ON_OPEN
|
|
if (dfd >= 0)
|
|
(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
|
|
if (pfd >= 0)
|
|
(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
|
|
# endif /* LOCK_ON_OPEN */
|
|
if (bitset(MF_ALIAS, map->map_mflags) &&
|
|
!aliaswait(map, ".pag", true))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
map->map_mflags |= MF_LOCKED;
|
|
if (geteuid() == 0 && TrustedUid != 0)
|
|
{
|
|
# if HASFCHOWN
|
|
if (fchown(dfd, TrustedUid, -1) < 0 ||
|
|
fchown(pfd, TrustedUid, -1) < 0)
|
|
{
|
|
int err = errno;
|
|
|
|
sm_syslog(LOG_ALERT, NOQID,
|
|
"ownership change on %s failed: %s",
|
|
map->map_file, sm_errstring(err));
|
|
message("050 ownership change on %s failed: %s",
|
|
map->map_file, sm_errstring(err));
|
|
}
|
|
# else /* HASFCHOWN */
|
|
sm_syslog(LOG_ALERT, NOQID,
|
|
"no fchown(): cannot change ownership on %s",
|
|
map->map_file);
|
|
message("050 no fchown(): cannot change ownership on %s",
|
|
map->map_file);
|
|
# endif /* HASFCHOWN */
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
|
|
*/
|
|
|
|
char *
|
|
ndbm_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
datum key, val;
|
|
int dfd, pfd;
|
|
char keybuf[MAXNAME + 1];
|
|
struct stat stbuf;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("ndbm_map_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
key.dptr = name;
|
|
key.dsize = strlen(name);
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
{
|
|
if (key.dsize > sizeof(keybuf) - 1)
|
|
key.dsize = sizeof(keybuf) - 1;
|
|
memmove(keybuf, key.dptr, key.dsize);
|
|
keybuf[key.dsize] = '\0';
|
|
makelower(keybuf);
|
|
key.dptr = keybuf;
|
|
}
|
|
lockdbm:
|
|
dfd = dbm_dirfno((DBM *) map->map_db1);
|
|
if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
|
|
(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
|
|
pfd = dbm_pagfno((DBM *) map->map_db1);
|
|
if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
|
|
stbuf.st_mtime > map->map_mtime)
|
|
{
|
|
/* Reopen the database to sync the cache */
|
|
int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
|
|
: O_RDONLY;
|
|
|
|
if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
|
|
(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
|
|
map->map_mflags |= MF_CLOSING;
|
|
map->map_class->map_close(map);
|
|
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
|
|
if (map->map_class->map_open(map, omode))
|
|
{
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
if ((omode & O_ACCMODE) == O_RDWR)
|
|
map->map_mflags |= MF_WRITABLE;
|
|
goto lockdbm;
|
|
}
|
|
else
|
|
{
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
extern MAPCLASS BogusMapClass;
|
|
|
|
*statp = EX_TEMPFAIL;
|
|
map->map_orgclass = map->map_class;
|
|
map->map_class = &BogusMapClass;
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
syserr("Cannot reopen NDBM database %s",
|
|
map->map_file);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
val.dptr = NULL;
|
|
if (bitset(MF_TRY0NULL, map->map_mflags))
|
|
{
|
|
val = dbm_fetch((DBM *) map->map_db1, key);
|
|
if (val.dptr != NULL)
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
}
|
|
if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
|
|
{
|
|
key.dsize++;
|
|
val = dbm_fetch((DBM *) map->map_db1, key);
|
|
if (val.dptr != NULL)
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
}
|
|
if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
|
|
(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
|
|
if (val.dptr == NULL)
|
|
return NULL;
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
return map_rewrite(map, val.dptr, val.dsize, av);
|
|
}
|
|
|
|
|
|
/*
|
|
** NDBM_MAP_STORE -- store a datum in the database
|
|
*/
|
|
|
|
void
|
|
ndbm_map_store(map, lhs, rhs)
|
|
register MAP *map;
|
|
char *lhs;
|
|
char *rhs;
|
|
{
|
|
datum key;
|
|
datum data;
|
|
int status;
|
|
char keybuf[MAXNAME + 1];
|
|
|
|
if (tTd(38, 12))
|
|
sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
|
|
map->map_mname, lhs, rhs);
|
|
|
|
key.dsize = strlen(lhs);
|
|
key.dptr = lhs;
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
{
|
|
if (key.dsize > sizeof(keybuf) - 1)
|
|
key.dsize = sizeof(keybuf) - 1;
|
|
memmove(keybuf, key.dptr, key.dsize);
|
|
keybuf[key.dsize] = '\0';
|
|
makelower(keybuf);
|
|
key.dptr = keybuf;
|
|
}
|
|
|
|
data.dsize = strlen(rhs);
|
|
data.dptr = rhs;
|
|
|
|
if (bitset(MF_INCLNULL, map->map_mflags))
|
|
{
|
|
key.dsize++;
|
|
data.dsize++;
|
|
}
|
|
|
|
status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
|
|
if (status > 0)
|
|
{
|
|
if (!bitset(MF_APPEND, map->map_mflags))
|
|
message("050 Warning: duplicate alias name %s", lhs);
|
|
else
|
|
{
|
|
static char *buf = NULL;
|
|
static int bufsiz = 0;
|
|
auto int xstat;
|
|
datum old;
|
|
|
|
old.dptr = ndbm_map_lookup(map, key.dptr,
|
|
(char **) NULL, &xstat);
|
|
if (old.dptr != NULL && *(char *) old.dptr != '\0')
|
|
{
|
|
old.dsize = strlen(old.dptr);
|
|
if (data.dsize + old.dsize + 2 > bufsiz)
|
|
{
|
|
if (buf != NULL)
|
|
(void) sm_free(buf);
|
|
bufsiz = data.dsize + old.dsize + 2;
|
|
buf = sm_pmalloc_x(bufsiz);
|
|
}
|
|
(void) sm_strlcpyn(buf, bufsiz, 3,
|
|
data.dptr, ",", old.dptr);
|
|
data.dsize = data.dsize + old.dsize + 1;
|
|
data.dptr = buf;
|
|
if (tTd(38, 9))
|
|
sm_dprintf("ndbm_map_store append=%s\n",
|
|
(char *)data.dptr);
|
|
}
|
|
}
|
|
status = dbm_store((DBM *) map->map_db1,
|
|
key, data, DBM_REPLACE);
|
|
}
|
|
if (status != 0)
|
|
syserr("readaliases: dbm put (%s): %d", lhs, status);
|
|
}
|
|
|
|
|
|
/*
|
|
** NDBM_MAP_CLOSE -- close the database
|
|
*/
|
|
|
|
void
|
|
ndbm_map_close(map)
|
|
register MAP *map;
|
|
{
|
|
if (tTd(38, 9))
|
|
sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
|
|
map->map_mname, map->map_file, map->map_mflags);
|
|
|
|
if (bitset(MF_WRITABLE, map->map_mflags))
|
|
{
|
|
# ifdef NDBM_YP_COMPAT
|
|
bool inclnull;
|
|
char buf[MAXHOSTNAMELEN];
|
|
|
|
inclnull = bitset(MF_INCLNULL, map->map_mflags);
|
|
map->map_mflags &= ~MF_INCLNULL;
|
|
|
|
if (strstr(map->map_file, "/yp/") != NULL)
|
|
{
|
|
long save_mflags = map->map_mflags;
|
|
|
|
map->map_mflags |= MF_NOFOLDCASE;
|
|
|
|
(void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
|
|
ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
|
|
|
|
(void) gethostname(buf, sizeof(buf));
|
|
ndbm_map_store(map, "YP_MASTER_NAME", buf);
|
|
|
|
map->map_mflags = save_mflags;
|
|
}
|
|
|
|
if (inclnull)
|
|
map->map_mflags |= MF_INCLNULL;
|
|
# endif /* NDBM_YP_COMPAT */
|
|
|
|
/* write out the distinguished alias */
|
|
ndbm_map_store(map, "@", "@");
|
|
}
|
|
dbm_close((DBM *) map->map_db1);
|
|
|
|
/* release lock (if needed) */
|
|
# if !LOCK_ON_OPEN
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# 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
|
|
# define h_nelem nelem
|
|
# ifndef DB_CACHE_SIZE
|
|
# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
|
|
# endif /* ! DB_CACHE_SIZE */
|
|
# ifndef DB_HASH_NELEM
|
|
# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
|
|
# endif /* ! DB_HASH_NELEM */
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
|
|
bool
|
|
bt_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
# if DB_VERSION_MAJOR < 2
|
|
BTREEINFO btinfo;
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
# if DB_VERSION_MAJOR == 2
|
|
DB_INFO btinfo;
|
|
# endif /* DB_VERSION_MAJOR == 2 */
|
|
# if DB_VERSION_MAJOR > 2
|
|
void *btinfo = NULL;
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("bt_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
# if DB_VERSION_MAJOR < 3
|
|
memset(&btinfo, '\0', sizeof(btinfo));
|
|
# ifdef DB_CACHE_SIZE
|
|
btinfo.db_cachesize = DB_CACHE_SIZE;
|
|
# endif /* DB_CACHE_SIZE */
|
|
# endif /* DB_VERSION_MAJOR < 3 */
|
|
|
|
return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
|
|
}
|
|
|
|
bool
|
|
hash_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
# if DB_VERSION_MAJOR < 2
|
|
HASHINFO hinfo;
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
# if DB_VERSION_MAJOR == 2
|
|
DB_INFO hinfo;
|
|
# endif /* DB_VERSION_MAJOR == 2 */
|
|
# if DB_VERSION_MAJOR > 2
|
|
void *hinfo = NULL;
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("hash_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
# if DB_VERSION_MAJOR < 3
|
|
memset(&hinfo, '\0', sizeof(hinfo));
|
|
# ifdef DB_HASH_NELEM
|
|
hinfo.h_nelem = DB_HASH_NELEM;
|
|
# endif /* DB_HASH_NELEM */
|
|
# ifdef DB_CACHE_SIZE
|
|
hinfo.db_cachesize = DB_CACHE_SIZE;
|
|
# endif /* DB_CACHE_SIZE */
|
|
# endif /* DB_VERSION_MAJOR < 3 */
|
|
|
|
return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
|
|
}
|
|
|
|
static bool
|
|
db_map_open(map, mode, mapclassname, dbtype, openinfo)
|
|
MAP *map;
|
|
int mode;
|
|
char *mapclassname;
|
|
DBTYPE dbtype;
|
|
# if DB_VERSION_MAJOR < 2
|
|
const void *openinfo;
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
# if DB_VERSION_MAJOR == 2
|
|
DB_INFO *openinfo;
|
|
# endif /* DB_VERSION_MAJOR == 2 */
|
|
# if DB_VERSION_MAJOR > 2
|
|
void **openinfo;
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
{
|
|
DB *db = NULL;
|
|
int i;
|
|
int omode;
|
|
int smode = S_IREAD;
|
|
int fd;
|
|
long sff;
|
|
int save_errno;
|
|
struct stat st;
|
|
char buf[MAXPATHLEN];
|
|
|
|
/* do initial file and directory checks */
|
|
if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
|
|
{
|
|
errno = 0;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("map \"%s\": map file %s name too long",
|
|
map->map_mname, map->map_file);
|
|
return false;
|
|
}
|
|
i = strlen(buf);
|
|
if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
|
|
{
|
|
if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
|
|
{
|
|
errno = 0;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("map \"%s\": map file %s name too long",
|
|
map->map_mname, map->map_file);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mode &= O_ACCMODE;
|
|
omode = mode;
|
|
|
|
sff = SFF_ROOTOK|SFF_REGONLY;
|
|
if (mode == O_RDWR)
|
|
{
|
|
sff |= SFF_CREAT;
|
|
if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
|
|
sff |= SFF_NOSLINK;
|
|
if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
|
|
sff |= SFF_NOHLINK;
|
|
smode = S_IWRITE;
|
|
}
|
|
else
|
|
{
|
|
if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
|
|
sff |= SFF_NOWLINK;
|
|
}
|
|
if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
|
|
sff |= SFF_SAFEDIRPATH;
|
|
i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
|
|
|
|
if (i != 0)
|
|
{
|
|
char *prob = "unsafe";
|
|
|
|
/* cannot open this map */
|
|
if (i == ENOENT)
|
|
prob = "missing";
|
|
if (tTd(38, 2))
|
|
sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
|
|
errno = i;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("%s map \"%s\": %s map file %s",
|
|
mapclassname, map->map_mname, prob, buf);
|
|
return false;
|
|
}
|
|
if (st.st_mode == ST_MODE_NOFILE)
|
|
omode |= O_CREAT|O_EXCL;
|
|
|
|
map->map_lockfd = -1;
|
|
|
|
# if LOCK_ON_OPEN
|
|
if (mode == O_RDWR)
|
|
omode |= O_TRUNC|O_EXLOCK;
|
|
else
|
|
omode |= O_SHLOCK;
|
|
# 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.
|
|
*/
|
|
|
|
fd = open(buf, omode, DBMMODE);
|
|
if (fd < 0)
|
|
{
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("db_map_open: cannot pre-open database %s", buf);
|
|
return false;
|
|
}
|
|
|
|
/* make sure no baddies slipped in just before the open... */
|
|
if (filechanged(buf, fd, &st))
|
|
{
|
|
save_errno = errno;
|
|
(void) close(fd);
|
|
errno = save_errno;
|
|
syserr("db_map_open(%s): file changed after pre-open", buf);
|
|
return false;
|
|
}
|
|
|
|
/* if new file, get the "before" bits for later filechanged check */
|
|
if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
|
|
{
|
|
save_errno = errno;
|
|
(void) close(fd);
|
|
errno = save_errno;
|
|
syserr("db_map_open(%s): cannot fstat pre-opened file",
|
|
buf);
|
|
return false;
|
|
}
|
|
|
|
/* actually lock the pre-opened file */
|
|
if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
|
|
syserr("db_map_open: cannot lock %s", buf);
|
|
|
|
/* set up mode bits for dbopen */
|
|
if (mode == O_RDWR)
|
|
omode |= O_TRUNC;
|
|
omode &= ~(O_EXCL|O_CREAT);
|
|
# endif /* LOCK_ON_OPEN */
|
|
|
|
# if DB_VERSION_MAJOR < 2
|
|
db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
{
|
|
int flags = 0;
|
|
# if DB_VERSION_MAJOR > 2
|
|
int ret;
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
|
|
if (mode == O_RDONLY)
|
|
flags |= DB_RDONLY;
|
|
if (bitset(O_CREAT, omode))
|
|
flags |= DB_CREATE;
|
|
if (bitset(O_TRUNC, omode))
|
|
flags |= DB_TRUNCATE;
|
|
SM_DB_FLAG_ADD(flags);
|
|
|
|
# if DB_VERSION_MAJOR > 2
|
|
ret = db_create(&db, NULL, 0);
|
|
# ifdef DB_CACHE_SIZE
|
|
if (ret == 0 && db != NULL)
|
|
{
|
|
ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
|
|
if (ret != 0)
|
|
{
|
|
(void) db->close(db, 0);
|
|
db = NULL;
|
|
}
|
|
}
|
|
# endif /* DB_CACHE_SIZE */
|
|
# ifdef DB_HASH_NELEM
|
|
if (dbtype == DB_HASH && ret == 0 && db != NULL)
|
|
{
|
|
ret = db->set_h_nelem(db, DB_HASH_NELEM);
|
|
if (ret != 0)
|
|
{
|
|
(void) db->close(db, 0);
|
|
db = NULL;
|
|
}
|
|
}
|
|
# endif /* DB_HASH_NELEM */
|
|
if (ret == 0 && db != NULL)
|
|
{
|
|
ret = db->open(db,
|
|
DBTXN /* transaction for DB 4.1 */
|
|
buf, NULL, dbtype, flags, DBMMODE);
|
|
if (ret != 0)
|
|
{
|
|
#ifdef DB_OLD_VERSION
|
|
if (ret == DB_OLD_VERSION)
|
|
ret = EINVAL;
|
|
#endif /* DB_OLD_VERSION */
|
|
(void) db->close(db, 0);
|
|
db = NULL;
|
|
}
|
|
}
|
|
errno = ret;
|
|
# else /* DB_VERSION_MAJOR > 2 */
|
|
errno = db_open(buf, dbtype, flags, DBMMODE,
|
|
NULL, openinfo, &db);
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
}
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
save_errno = errno;
|
|
|
|
# if !LOCK_ON_OPEN
|
|
if (mode == O_RDWR)
|
|
map->map_lockfd = fd;
|
|
else
|
|
(void) close(fd);
|
|
# endif /* !LOCK_ON_OPEN */
|
|
|
|
if (db == NULL)
|
|
{
|
|
if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
|
|
aliaswait(map, ".db", false))
|
|
return true;
|
|
# if !LOCK_ON_OPEN
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# endif /* !LOCK_ON_OPEN */
|
|
errno = save_errno;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("Cannot open %s database %s",
|
|
mapclassname, buf);
|
|
return false;
|
|
}
|
|
|
|
# if DB_VERSION_MAJOR < 2
|
|
fd = db->fd(db);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
fd = -1;
|
|
errno = db->fd(db, &fd);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (filechanged(buf, fd, &st))
|
|
{
|
|
save_errno = errno;
|
|
# if DB_VERSION_MAJOR < 2
|
|
(void) db->close(db);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
errno = db->close(db, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
# if !LOCK_ON_OPEN
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# endif /* !LOCK_ON_OPEN */
|
|
errno = save_errno;
|
|
syserr("db_map_open(%s): file changed after open", buf);
|
|
return false;
|
|
}
|
|
|
|
if (mode == O_RDWR)
|
|
map->map_mflags |= MF_LOCKED;
|
|
# if LOCK_ON_OPEN
|
|
if (fd >= 0 && mode == O_RDONLY)
|
|
{
|
|
(void) lockfile(fd, buf, NULL, LOCK_UN);
|
|
}
|
|
# endif /* LOCK_ON_OPEN */
|
|
|
|
/* try to make sure that at least the database header is on disk */
|
|
if (mode == O_RDWR)
|
|
{
|
|
(void) db->sync(db, 0);
|
|
if (geteuid() == 0 && TrustedUid != 0)
|
|
{
|
|
# if HASFCHOWN
|
|
if (fchown(fd, TrustedUid, -1) < 0)
|
|
{
|
|
int err = errno;
|
|
|
|
sm_syslog(LOG_ALERT, NOQID,
|
|
"ownership change on %s failed: %s",
|
|
buf, sm_errstring(err));
|
|
message("050 ownership change on %s failed: %s",
|
|
buf, sm_errstring(err));
|
|
}
|
|
# else /* HASFCHOWN */
|
|
sm_syslog(LOG_ALERT, NOQID,
|
|
"no fchown(): cannot change ownership on %s",
|
|
map->map_file);
|
|
message("050 no fchown(): cannot change ownership on %s",
|
|
map->map_file);
|
|
# endif /* HASFCHOWN */
|
|
}
|
|
}
|
|
|
|
map->map_db2 = (ARBPTR_T) db;
|
|
|
|
/*
|
|
** Need to set map_mtime before the call to aliaswait()
|
|
** as aliaswait() will call map_lookup() which requires
|
|
** map_mtime to be set
|
|
*/
|
|
|
|
if (fd >= 0 && fstat(fd, &st) >= 0)
|
|
map->map_mtime = st.st_mtime;
|
|
|
|
if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
|
|
!aliaswait(map, ".db", true))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
|
|
*/
|
|
|
|
char *
|
|
db_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
DBT key, val;
|
|
register DB *db = (DB *) map->map_db2;
|
|
int i;
|
|
int st;
|
|
int save_errno;
|
|
int fd;
|
|
struct stat stbuf;
|
|
char keybuf[MAXNAME + 1];
|
|
char buf[MAXPATHLEN];
|
|
|
|
memset(&key, '\0', sizeof(key));
|
|
memset(&val, '\0', sizeof(val));
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("db_map_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
|
|
{
|
|
errno = 0;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("map \"%s\": map file %s name too long",
|
|
map->map_mname, map->map_file);
|
|
return NULL;
|
|
}
|
|
i = strlen(buf);
|
|
if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
|
|
buf[i - 3] = '\0';
|
|
|
|
key.size = strlen(name);
|
|
if (key.size > sizeof(keybuf) - 1)
|
|
key.size = sizeof(keybuf) - 1;
|
|
key.data = keybuf;
|
|
memmove(keybuf, name, key.size);
|
|
keybuf[key.size] = '\0';
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
makelower(keybuf);
|
|
lockdb:
|
|
# if DB_VERSION_MAJOR < 2
|
|
fd = db->fd(db);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
fd = -1;
|
|
errno = db->fd(db, &fd);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
|
|
(void) lockfile(fd, buf, ".db", LOCK_SH);
|
|
if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
|
|
{
|
|
/* Reopen the database to sync the cache */
|
|
int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
|
|
: O_RDONLY;
|
|
|
|
if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
|
|
(void) lockfile(fd, buf, ".db", LOCK_UN);
|
|
map->map_mflags |= MF_CLOSING;
|
|
map->map_class->map_close(map);
|
|
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
|
|
if (map->map_class->map_open(map, omode))
|
|
{
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
if ((omode & O_ACCMODE) == O_RDWR)
|
|
map->map_mflags |= MF_WRITABLE;
|
|
db = (DB *) map->map_db2;
|
|
goto lockdb;
|
|
}
|
|
else
|
|
{
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
extern MAPCLASS BogusMapClass;
|
|
|
|
*statp = EX_TEMPFAIL;
|
|
map->map_orgclass = map->map_class;
|
|
map->map_class = &BogusMapClass;
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
syserr("Cannot reopen DB database %s",
|
|
map->map_file);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
st = 1;
|
|
if (bitset(MF_TRY0NULL, map->map_mflags))
|
|
{
|
|
# if DB_VERSION_MAJOR < 2
|
|
st = db->get(db, &key, &val, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
errno = db->get(db, NULL, &key, &val, 0);
|
|
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)
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
}
|
|
if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
|
|
{
|
|
key.size++;
|
|
# if DB_VERSION_MAJOR < 2
|
|
st = db->get(db, &key, &val, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
errno = db->get(db, NULL, &key, &val, 0);
|
|
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)
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
}
|
|
save_errno = errno;
|
|
if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
|
|
(void) lockfile(fd, buf, ".db", LOCK_UN);
|
|
if (st != 0)
|
|
{
|
|
errno = save_errno;
|
|
if (st < 0)
|
|
syserr("db_map_lookup: get (%s)", name);
|
|
return NULL;
|
|
}
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
return map_rewrite(map, val.data, val.size, av);
|
|
}
|
|
|
|
|
|
/*
|
|
** DB_MAP_STORE -- store a datum in the NEWDB database
|
|
*/
|
|
|
|
void
|
|
db_map_store(map, lhs, rhs)
|
|
register MAP *map;
|
|
char *lhs;
|
|
char *rhs;
|
|
{
|
|
int status;
|
|
DBT key;
|
|
DBT data;
|
|
register DB *db = map->map_db2;
|
|
char keybuf[MAXNAME + 1];
|
|
|
|
memset(&key, '\0', sizeof(key));
|
|
memset(&data, '\0', sizeof(data));
|
|
|
|
if (tTd(38, 12))
|
|
sm_dprintf("db_map_store(%s, %s, %s)\n",
|
|
map->map_mname, lhs, rhs);
|
|
|
|
key.size = strlen(lhs);
|
|
key.data = lhs;
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
{
|
|
if (key.size > sizeof(keybuf) - 1)
|
|
key.size = sizeof(keybuf) - 1;
|
|
memmove(keybuf, key.data, key.size);
|
|
keybuf[key.size] = '\0';
|
|
makelower(keybuf);
|
|
key.data = keybuf;
|
|
}
|
|
|
|
data.size = strlen(rhs);
|
|
data.data = rhs;
|
|
|
|
if (bitset(MF_INCLNULL, map->map_mflags))
|
|
{
|
|
key.size++;
|
|
data.size++;
|
|
}
|
|
|
|
# if DB_VERSION_MAJOR < 2
|
|
status = db->put(db, &key, &data, R_NOOVERWRITE);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
|
|
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)
|
|
{
|
|
if (!bitset(MF_APPEND, map->map_mflags))
|
|
message("050 Warning: duplicate alias name %s", lhs);
|
|
else
|
|
{
|
|
static char *buf = NULL;
|
|
static int bufsiz = 0;
|
|
DBT old;
|
|
|
|
memset(&old, '\0', sizeof(old));
|
|
|
|
old.data = db_map_lookup(map, key.data,
|
|
(char **) NULL, &status);
|
|
if (old.data != NULL)
|
|
{
|
|
old.size = strlen(old.data);
|
|
if (data.size + old.size + 2 > (size_t) bufsiz)
|
|
{
|
|
if (buf != NULL)
|
|
sm_free(buf);
|
|
bufsiz = data.size + old.size + 2;
|
|
buf = sm_pmalloc_x(bufsiz);
|
|
}
|
|
(void) sm_strlcpyn(buf, bufsiz, 3,
|
|
(char *) data.data, ",",
|
|
(char *) old.data);
|
|
data.size = data.size + old.size + 1;
|
|
data.data = buf;
|
|
if (tTd(38, 9))
|
|
sm_dprintf("db_map_store append=%s\n",
|
|
(char *) data.data);
|
|
}
|
|
}
|
|
# if DB_VERSION_MAJOR < 2
|
|
status = db->put(db, &key, &data, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
status = errno = db->put(db, NULL, &key, &data, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
}
|
|
if (status != 0)
|
|
syserr("readaliases: db put (%s)", lhs);
|
|
}
|
|
|
|
|
|
/*
|
|
** DB_MAP_CLOSE -- add distinguished entries and close the database
|
|
*/
|
|
|
|
void
|
|
db_map_close(map)
|
|
MAP *map;
|
|
{
|
|
register DB *db = map->map_db2;
|
|
|
|
if (tTd(38, 9))
|
|
sm_dprintf("db_map_close(%s, %s, %lx)\n",
|
|
map->map_mname, map->map_file, map->map_mflags);
|
|
|
|
if (bitset(MF_WRITABLE, map->map_mflags))
|
|
{
|
|
/* write out the distinguished alias */
|
|
db_map_store(map, "@", "@");
|
|
}
|
|
|
|
(void) db->sync(db, 0);
|
|
|
|
# if !LOCK_ON_OPEN
|
|
if (map->map_lockfd >= 0)
|
|
(void) close(map->map_lockfd);
|
|
# endif /* !LOCK_ON_OPEN */
|
|
|
|
# if DB_VERSION_MAJOR < 2
|
|
if (db->close(db) != 0)
|
|
# 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.
|
|
*/
|
|
|
|
if (map->map_pid != CurrentPid)
|
|
{
|
|
int fd = -1;
|
|
|
|
errno = db->fd(db, &fd);
|
|
if (fd >= 0)
|
|
(void) close(fd);
|
|
return;
|
|
}
|
|
|
|
if ((errno = db->close(db, 0)) != 0)
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
syserr("db_map_close(%s, %s, %lx): db close failure",
|
|
map->map_mname, map->map_file, map->map_mflags);
|
|
}
|
|
#endif /* NEWDB */
|
|
/*
|
|
** NIS Modules
|
|
*/
|
|
|
|
#if NIS
|
|
|
|
# ifndef YPERR_BUSY
|
|
# define YPERR_BUSY 16
|
|
# endif /* ! YPERR_BUSY */
|
|
|
|
/*
|
|
** NIS_MAP_OPEN -- open DBM map
|
|
*/
|
|
|
|
bool
|
|
nis_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
int yperr;
|
|
register char *p;
|
|
auto char *vp;
|
|
auto int vsize;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("nis_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
return false;
|
|
}
|
|
|
|
p = strchr(map->map_file, '@');
|
|
if (p != NULL)
|
|
{
|
|
*p++ = '\0';
|
|
if (*p != '\0')
|
|
map->map_domain = p;
|
|
}
|
|
|
|
if (*map->map_file == '\0')
|
|
map->map_file = "mail.aliases";
|
|
|
|
if (map->map_domain == NULL)
|
|
{
|
|
yperr = yp_get_default_domain(&map->map_domain);
|
|
if (yperr != 0)
|
|
{
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("451 4.3.5 NIS map %s specified, but NIS not running",
|
|
map->map_file);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* check to see if this map actually exists */
|
|
vp = NULL;
|
|
yperr = yp_match(map->map_domain, map->map_file, "@", 1,
|
|
&vp, &vsize);
|
|
if (tTd(38, 10))
|
|
sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
|
|
map->map_domain, map->map_file, yperr_string(yperr));
|
|
if (vp != NULL)
|
|
sm_free(vp);
|
|
|
|
if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
|
|
{
|
|
/*
|
|
** 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
|
|
if (!bitset(MF_ALIAS, map->map_mflags) ||
|
|
aliaswait(map, NULL, true))
|
|
# endif /* 0 */
|
|
return true;
|
|
}
|
|
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
|
|
map->map_file, map->map_domain, yperr_string(yperr));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
** NIS_MAP_LOOKUP -- look up a datum in a NIS map
|
|
*/
|
|
|
|
/* ARGSUSED3 */
|
|
char *
|
|
nis_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
char *vp;
|
|
auto int vsize;
|
|
int buflen;
|
|
int yperr;
|
|
char keybuf[MAXNAME + 1];
|
|
char *SM_NONVOLATILE result = NULL;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nis_map_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
buflen = strlen(name);
|
|
if (buflen > sizeof(keybuf) - 1)
|
|
buflen = sizeof(keybuf) - 1;
|
|
memmove(keybuf, name, buflen);
|
|
keybuf[buflen] = '\0';
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
makelower(keybuf);
|
|
yperr = YPERR_KEY;
|
|
vp = NULL;
|
|
if (bitset(MF_TRY0NULL, map->map_mflags))
|
|
{
|
|
yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
|
|
&vp, &vsize);
|
|
if (yperr == 0)
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
}
|
|
if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
|
|
{
|
|
SM_FREE_CLR(vp);
|
|
buflen++;
|
|
yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
|
|
&vp, &vsize);
|
|
if (yperr == 0)
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
}
|
|
if (yperr != 0)
|
|
{
|
|
if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
|
|
map->map_mflags &= ~(MF_VALID|MF_OPEN);
|
|
if (vp != NULL)
|
|
sm_free(vp);
|
|
return NULL;
|
|
}
|
|
SM_TRY
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
result = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
result = map_rewrite(map, vp, vsize, av);
|
|
SM_FINALLY
|
|
if (vp != NULL)
|
|
sm_free(vp);
|
|
SM_END_TRY
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
** NIS_GETCANONNAME -- look up canonical name in NIS
|
|
*/
|
|
|
|
static bool
|
|
nis_getcanonname(name, hbsize, statp)
|
|
char *name;
|
|
int hbsize;
|
|
int *statp;
|
|
{
|
|
char *vp;
|
|
auto int vsize;
|
|
int keylen;
|
|
int yperr;
|
|
static bool try0null = true;
|
|
static bool try1null = true;
|
|
static char *yp_domain = NULL;
|
|
char host_record[MAXLINE];
|
|
char cbuf[MAXNAME];
|
|
char nbuf[MAXNAME + 1];
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nis_getcanonname(%s)\n", name);
|
|
|
|
if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
(void) shorten_hostname(nbuf);
|
|
keylen = strlen(nbuf);
|
|
|
|
if (yp_domain == NULL)
|
|
(void) yp_get_default_domain(&yp_domain);
|
|
makelower(nbuf);
|
|
yperr = YPERR_KEY;
|
|
vp = NULL;
|
|
if (try0null)
|
|
{
|
|
yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
|
|
&vp, &vsize);
|
|
if (yperr == 0)
|
|
try1null = false;
|
|
}
|
|
if (yperr == YPERR_KEY && try1null)
|
|
{
|
|
SM_FREE_CLR(vp);
|
|
keylen++;
|
|
yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
|
|
&vp, &vsize);
|
|
if (yperr == 0)
|
|
try0null = false;
|
|
}
|
|
if (yperr != 0)
|
|
{
|
|
if (yperr == YPERR_KEY)
|
|
*statp = EX_NOHOST;
|
|
else if (yperr == YPERR_BUSY)
|
|
*statp = EX_TEMPFAIL;
|
|
else
|
|
*statp = EX_UNAVAILABLE;
|
|
if (vp != NULL)
|
|
sm_free(vp);
|
|
return false;
|
|
}
|
|
(void) sm_strlcpy(host_record, vp, sizeof(host_record));
|
|
sm_free(vp);
|
|
if (tTd(38, 44))
|
|
sm_dprintf("got record `%s'\n", host_record);
|
|
vp = strpbrk(host_record, "#\n");
|
|
if (vp != NULL)
|
|
*vp = '\0';
|
|
if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
|
|
{
|
|
/* this should not happen, but.... */
|
|
*statp = EX_NOHOST;
|
|
return false;
|
|
}
|
|
if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
*statp = EX_OK;
|
|
return true;
|
|
}
|
|
|
|
#endif /* NIS */
|
|
/*
|
|
** NISPLUS Modules
|
|
**
|
|
** This code donated by Sun Microsystems.
|
|
*/
|
|
|
|
#if NISPLUS
|
|
|
|
# undef NIS /* symbol conflict in nis.h */
|
|
# undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
|
|
# include <rpcsvc/nis.h>
|
|
# include <rpcsvc/nislib.h>
|
|
# ifndef NIS_TABLE_OBJ
|
|
# define NIS_TABLE_OBJ TABLE_OBJ
|
|
# endif /* NIS_TABLE_OBJ */
|
|
|
|
# define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
|
|
# define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
|
|
# define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
|
|
# define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
|
|
|
|
/*
|
|
** NISPLUS_MAP_OPEN -- open nisplus table
|
|
*/
|
|
|
|
bool
|
|
nisplus_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
nis_result *res = NULL;
|
|
int retry_cnt, max_col, i;
|
|
char qbuf[MAXLINE + NIS_MAXNAMELEN];
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
errno = EPERM;
|
|
return false;
|
|
}
|
|
|
|
if (*map->map_file == '\0')
|
|
map->map_file = "mail_aliases.org_dir";
|
|
|
|
if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
|
|
{
|
|
/* set default NISPLUS Domain to $m */
|
|
map->map_domain = newstr(nisplus_default_domain());
|
|
if (tTd(38, 2))
|
|
sm_dprintf("nisplus_map_open(%s): using domain %s\n",
|
|
map->map_file, map->map_domain);
|
|
}
|
|
if (!PARTIAL_NAME(map->map_file))
|
|
{
|
|
map->map_domain = newstr("");
|
|
(void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
|
|
}
|
|
else
|
|
{
|
|
/* check to see if this map actually exists */
|
|
(void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
|
|
map->map_file, ".", map->map_domain);
|
|
}
|
|
|
|
retry_cnt = 0;
|
|
while (res == NULL || res->status != NIS_SUCCESS)
|
|
{
|
|
res = nis_lookup(qbuf, FOLLOW_LINKS);
|
|
switch (res->status)
|
|
{
|
|
case NIS_SUCCESS:
|
|
break;
|
|
|
|
case NIS_TRYAGAIN:
|
|
case NIS_RPCERROR:
|
|
case NIS_NAMEUNREACHABLE:
|
|
if (retry_cnt++ > 4)
|
|
{
|
|
errno = EAGAIN;
|
|
return false;
|
|
}
|
|
/* try not to overwhelm hosed server */
|
|
sleep(2);
|
|
break;
|
|
|
|
default: /* all other nisplus errors */
|
|
# if 0
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("451 4.3.5 Cannot find table %s.%s: %s",
|
|
map->map_file, map->map_domain,
|
|
nis_sperrno(res->status));
|
|
# endif /* 0 */
|
|
errno = EAGAIN;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (NIS_RES_NUMOBJ(res) != 1 ||
|
|
(NIS_RES_OBJECT(res)->zo_data.zo_type != NIS_TABLE_OBJ))
|
|
{
|
|
if (tTd(38, 10))
|
|
sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
|
|
# if 0
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("451 4.3.5 %s.%s: %s is not a table",
|
|
map->map_file, map->map_domain,
|
|
nis_sperrno(res->status));
|
|
# endif /* 0 */
|
|
errno = EBADF;
|
|
return false;
|
|
}
|
|
/* default key column is column 0 */
|
|
if (map->map_keycolnm == NULL)
|
|
map->map_keycolnm = newstr(COL_NAME(res,0));
|
|
|
|
max_col = COL_MAX(res);
|
|
|
|
/* verify the key column exist */
|
|
for (i = 0; i < max_col; i++)
|
|
{
|
|
if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
|
|
break;
|
|
}
|
|
if (i == max_col)
|
|
{
|
|
if (tTd(38, 2))
|
|
sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
|
|
map->map_file, map->map_keycolnm);
|
|
errno = ENOENT;
|
|
return false;
|
|
}
|
|
|
|
/* default value column is the last column */
|
|
if (map->map_valcolnm == NULL)
|
|
{
|
|
map->map_valcolno = max_col - 1;
|
|
return true;
|
|
}
|
|
|
|
for (i = 0; i< max_col; i++)
|
|
{
|
|
if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
|
|
{
|
|
map->map_valcolno = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
|
|
map->map_file, map->map_keycolnm);
|
|
errno = ENOENT;
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
|
|
*/
|
|
|
|
char *
|
|
nisplus_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
char *p;
|
|
auto int vsize;
|
|
char *skp;
|
|
int skleft;
|
|
char search_key[MAXNAME + 4];
|
|
char qbuf[MAXLINE + NIS_MAXNAMELEN];
|
|
nis_result *result;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_map_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
if (!bitset(MF_OPEN, map->map_mflags))
|
|
{
|
|
if (nisplus_map_open(map, O_RDONLY))
|
|
{
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
}
|
|
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.
|
|
*/
|
|
|
|
skleft = sizeof(search_key) - 4;
|
|
skp = search_key;
|
|
for (p = name; *p != '\0' && skleft > 0; p++)
|
|
{
|
|
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';
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
makelower(search_key);
|
|
|
|
/* construct the query */
|
|
if (PARTIAL_NAME(map->map_file))
|
|
(void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
|
|
map->map_keycolnm, search_key, map->map_file,
|
|
map->map_domain);
|
|
else
|
|
(void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
|
|
map->map_keycolnm, search_key, map->map_file);
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("qbuf=%s\n", qbuf);
|
|
result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
|
|
if (result->status == NIS_SUCCESS)
|
|
{
|
|
int count;
|
|
char *str;
|
|
|
|
if ((count = NIS_RES_NUMOBJ(result)) != 1)
|
|
{
|
|
if (LogLevel > 10)
|
|
sm_syslog(LOG_WARNING, CurEnv->e_id,
|
|
"%s: lookup error, expected 1 entry, got %d",
|
|
map->map_file, count);
|
|
|
|
/* ignore second entry */
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
|
|
name, count);
|
|
}
|
|
|
|
p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
|
|
/* set the length of the result */
|
|
if (p == NULL)
|
|
p = "";
|
|
vsize = strlen(p);
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_map_lookup(%s), found %s\n",
|
|
name, p);
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
str = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
str = map_rewrite(map, p, vsize, av);
|
|
nis_freeresult(result);
|
|
*statp = EX_OK;
|
|
return str;
|
|
}
|
|
else
|
|
{
|
|
if (result->status == NIS_NOTFOUND)
|
|
*statp = EX_NOTFOUND;
|
|
else if (result->status == NIS_TRYAGAIN)
|
|
*statp = EX_TEMPFAIL;
|
|
else
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
map->map_mflags &= ~(MF_VALID|MF_OPEN);
|
|
}
|
|
}
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
|
|
nis_freeresult(result);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
|
|
*/
|
|
|
|
static bool
|
|
nisplus_getcanonname(name, hbsize, statp)
|
|
char *name;
|
|
int hbsize;
|
|
int *statp;
|
|
{
|
|
char *vp;
|
|
auto int vsize;
|
|
nis_result *result;
|
|
char *p;
|
|
char nbuf[MAXNAME + 1];
|
|
char qbuf[MAXLINE + NIS_MAXNAMELEN];
|
|
|
|
if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
(void) shorten_hostname(nbuf);
|
|
|
|
p = strchr(nbuf, '.');
|
|
if (p == NULL)
|
|
{
|
|
/* single token */
|
|
(void) sm_snprintf(qbuf, sizeof(qbuf),
|
|
"[name=%s],hosts.org_dir", nbuf);
|
|
}
|
|
else if (p[1] != '\0')
|
|
{
|
|
/* multi token -- take only first token in nbuf */
|
|
*p = '\0';
|
|
(void) sm_snprintf(qbuf, sizeof(qbuf),
|
|
"[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
|
|
}
|
|
else
|
|
{
|
|
*statp = EX_NOHOST;
|
|
return false;
|
|
}
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
|
|
name, qbuf);
|
|
|
|
result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
|
|
NULL, NULL);
|
|
|
|
if (result->status == NIS_SUCCESS)
|
|
{
|
|
int count;
|
|
char *domain;
|
|
|
|
if ((count = NIS_RES_NUMOBJ(result)) != 1)
|
|
{
|
|
if (LogLevel > 10)
|
|
sm_syslog(LOG_WARNING, CurEnv->e_id,
|
|
"nisplus_getcanonname: lookup error, expected 1 entry, got %d",
|
|
count);
|
|
|
|
/* ignore second entry */
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
|
|
name, count);
|
|
}
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
|
|
name, (NIS_RES_OBJECT(result))->zo_domain);
|
|
|
|
|
|
vp = ((NIS_RES_OBJECT(result))->EN_col(0));
|
|
vsize = strlen(vp);
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_getcanonname(%s), found %s\n",
|
|
name, vp);
|
|
if (strchr(vp, '.') != NULL)
|
|
{
|
|
domain = "";
|
|
}
|
|
else
|
|
{
|
|
domain = macvalue('m', CurEnv);
|
|
if (domain == NULL)
|
|
domain = "";
|
|
}
|
|
if (hbsize > vsize + (int) strlen(domain) + 1)
|
|
{
|
|
if (domain[0] == '\0')
|
|
(void) sm_strlcpy(name, vp, hbsize);
|
|
else
|
|
(void) sm_snprintf(name, hbsize,
|
|
"%s.%s", vp, domain);
|
|
*statp = EX_OK;
|
|
}
|
|
else
|
|
*statp = EX_NOHOST;
|
|
nis_freeresult(result);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (result->status == NIS_NOTFOUND)
|
|
*statp = EX_NOHOST;
|
|
else if (result->status == NIS_TRYAGAIN)
|
|
*statp = EX_TEMPFAIL;
|
|
else
|
|
*statp = EX_UNAVAILABLE;
|
|
}
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
|
|
name, result->status, *statp);
|
|
nis_freeresult(result);
|
|
return false;
|
|
}
|
|
|
|
char *
|
|
nisplus_default_domain()
|
|
{
|
|
static char default_domain[MAXNAME + 1] = "";
|
|
char *p;
|
|
|
|
if (default_domain[0] != '\0')
|
|
return default_domain;
|
|
|
|
p = nis_local_directory();
|
|
(void) sm_strlcpy(default_domain, p, sizeof(default_domain));
|
|
return default_domain;
|
|
}
|
|
|
|
#endif /* NISPLUS */
|
|
/*
|
|
** LDAP Modules
|
|
*/
|
|
|
|
/*
|
|
** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
|
|
*/
|
|
|
|
#if defined(LDAPMAP) || defined(PH_MAP)
|
|
|
|
# if PH_MAP
|
|
# define ph_map_dequote ldapmap_dequote
|
|
# endif /* PH_MAP */
|
|
|
|
static char *ldapmap_dequote __P((char *));
|
|
|
|
static char *
|
|
ldapmap_dequote(str)
|
|
char *str;
|
|
{
|
|
char *p;
|
|
char *start;
|
|
|
|
if (str == NULL)
|
|
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
|
|
|
|
static SM_LDAP_STRUCT *LDAPDefaults = NULL;
|
|
|
|
/*
|
|
** 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
|
|
ldapmap_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
SM_LDAP_STRUCT *lmap;
|
|
STAB *s;
|
|
char *id;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
|
|
|
|
#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
|
|
HASLDAPGETALIASBYNAME
|
|
if (VendorCode == VENDOR_SUN &&
|
|
strcmp(map->map_mname, "aliases.ldap") == 0)
|
|
{
|
|
return true;
|
|
}
|
|
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
|
|
|
|
mode &= O_ACCMODE;
|
|
|
|
/* sendmail doesn't have the ability to write to LDAP (yet) */
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
return false;
|
|
}
|
|
|
|
lmap = (SM_LDAP_STRUCT *) map->map_db1;
|
|
|
|
s = ldapmap_findconn(lmap);
|
|
if (s->s_lmap != NULL)
|
|
{
|
|
/* Already have a connection open to this LDAP server */
|
|
lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
|
|
lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
|
|
|
|
/* Add this map as head of linked list */
|
|
lmap->ldap_next = s->s_lmap;
|
|
s->s_lmap = map;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("using cached connection\n");
|
|
return true;
|
|
}
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("opening new connection\n");
|
|
|
|
if (lmap->ldap_host != NULL)
|
|
id = lmap->ldap_host;
|
|
else if (lmap->ldap_uri != NULL)
|
|
id = lmap->ldap_uri;
|
|
else
|
|
id = "localhost";
|
|
|
|
if (tTd(74, 104))
|
|
{
|
|
extern MAPCLASS NullMapClass;
|
|
|
|
/* debug mode: don't actually open an LDAP connection */
|
|
map->map_orgclass = map->map_class;
|
|
map->map_class = &NullMapClass;
|
|
map->map_mflags |= MF_OPEN;
|
|
map->map_pid = CurrentPid;
|
|
return true;
|
|
}
|
|
|
|
/* No connection yet, connect */
|
|
if (!sm_ldap_start(map->map_mname, lmap))
|
|
{
|
|
if (errno == ETIMEDOUT)
|
|
{
|
|
if (LogLevel > 1)
|
|
sm_syslog(LOG_NOTICE, CurEnv->e_id,
|
|
"timeout connecting to LDAP server %.100s",
|
|
id);
|
|
}
|
|
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
if (bitset(MF_NODEFER, map->map_mflags))
|
|
{
|
|
syserr("%s failed to %s in map %s",
|
|
# if USE_LDAP_INIT
|
|
"ldap_init/ldap_bind",
|
|
# else /* USE_LDAP_INIT */
|
|
"ldap_open",
|
|
# endif /* USE_LDAP_INIT */
|
|
id, map->map_mname);
|
|
}
|
|
else
|
|
{
|
|
syserr("451 4.3.5 %s failed to %s in map %s",
|
|
# if USE_LDAP_INIT
|
|
"ldap_init/ldap_bind",
|
|
# else /* USE_LDAP_INIT */
|
|
"ldap_open",
|
|
# endif /* USE_LDAP_INIT */
|
|
id, map->map_mname);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Save connection for reuse */
|
|
s->s_lmap = map;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** LDAPMAP_CLOSE -- close ldap map
|
|
*/
|
|
|
|
void
|
|
ldapmap_close(map)
|
|
MAP *map;
|
|
{
|
|
SM_LDAP_STRUCT *lmap;
|
|
STAB *s;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
|
|
|
|
lmap = (SM_LDAP_STRUCT *) map->map_db1;
|
|
|
|
/* Check if already closed */
|
|
if (lmap->ldap_ld == NULL)
|
|
return;
|
|
|
|
/* Close the LDAP connection */
|
|
sm_ldap_close(lmap);
|
|
|
|
/* Mark all the maps that share the connection as closed */
|
|
s = ldapmap_findconn(lmap);
|
|
|
|
while (s->s_lmap != NULL)
|
|
{
|
|
MAP *smap = s->s_lmap;
|
|
|
|
if (tTd(38, 2) && smap != map)
|
|
sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
|
|
map->map_mname, smap->map_mname);
|
|
smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
|
|
lmap = (SM_LDAP_STRUCT *) smap->map_db1;
|
|
lmap->ldap_ld = NULL;
|
|
s->s_lmap = lmap->ldap_next;
|
|
lmap->ldap_next = NULL;
|
|
}
|
|
}
|
|
|
|
# ifdef SUNET_ID
|
|
/*
|
|
** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
|
|
** This only makes sense at Stanford University.
|
|
*/
|
|
|
|
static char *
|
|
sunet_id_hash(str)
|
|
char *str;
|
|
{
|
|
char *p, *p_last;
|
|
|
|
p = str;
|
|
p_last = p;
|
|
while (*p != '\0')
|
|
{
|
|
if (isascii(*p) && (islower(*p) || isdigit(*p)))
|
|
{
|
|
*p_last = *p;
|
|
p_last++;
|
|
}
|
|
else if (isascii(*p) && isupper(*p))
|
|
{
|
|
*p_last = tolower(*p);
|
|
p_last++;
|
|
}
|
|
++p;
|
|
}
|
|
if (*p_last != '\0')
|
|
*p_last = '\0';
|
|
return str;
|
|
}
|
|
# define SM_CONVERT_ID(str) sunet_id_hash(str)
|
|
# else /* SUNET_ID */
|
|
# define SM_CONVERT_ID(str) makelower(str)
|
|
# endif /* SUNET_ID */
|
|
|
|
/*
|
|
** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
|
|
*/
|
|
|
|
char *
|
|
ldapmap_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int flags;
|
|
int i;
|
|
int plen = 0;
|
|
int psize = 0;
|
|
int msgid;
|
|
int save_errno;
|
|
char *vp, *p;
|
|
char *result = NULL;
|
|
SM_RPOOL_T *rpool;
|
|
SM_LDAP_STRUCT *lmap = NULL;
|
|
char *argv[SM_LDAP_ARGS];
|
|
char keybuf[MAXKEY];
|
|
#if SM_LDAP_ARGS != MAX_MAP_ARGS
|
|
# ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
|
|
#endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
|
|
|
|
#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
|
|
HASLDAPGETALIASBYNAME
|
|
if (VendorCode == VENDOR_SUN &&
|
|
strcmp(map->map_mname, "aliases.ldap") == 0)
|
|
{
|
|
int rc;
|
|
#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
|
|
extern char *__getldapaliasbyname();
|
|
char *answer;
|
|
|
|
answer = __getldapaliasbyname(name, &rc);
|
|
#else
|
|
char answer[MAXNAME + 1];
|
|
|
|
rc = __getldapaliasbyname(name, answer, sizeof(answer));
|
|
#endif
|
|
if (rc != 0)
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
|
|
name, errno);
|
|
*statp = EX_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
*statp = EX_OK;
|
|
if (tTd(38, 20))
|
|
sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
|
|
answer);
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
result = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
result = map_rewrite(map, answer, strlen(answer), av);
|
|
#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
|
|
free(answer);
|
|
#endif
|
|
return result;
|
|
}
|
|
#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
|
|
|
|
/* Get ldap struct pointer from map */
|
|
lmap = (SM_LDAP_STRUCT *) map->map_db1;
|
|
sm_ldap_setopts(lmap->ldap_ld, lmap);
|
|
|
|
if (lmap->ldap_multi_args)
|
|
{
|
|
SM_REQUIRE(av != NULL);
|
|
memset(argv, '\0', sizeof(argv));
|
|
for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
|
|
{
|
|
argv[i] = sm_strdup(av[i]);
|
|
if (argv[i] == NULL)
|
|
{
|
|
int save_errno, j;
|
|
|
|
save_errno = errno;
|
|
for (j = 0; j < i && argv[j] != NULL; j++)
|
|
SM_FREE(argv[j]);
|
|
*statp = EX_TEMPFAIL;
|
|
errno = save_errno;
|
|
return NULL;
|
|
}
|
|
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
SM_CONVERT_ID(av[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(void) sm_strlcpy(keybuf, name, sizeof(keybuf));
|
|
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
SM_CONVERT_ID(keybuf);
|
|
}
|
|
|
|
if (tTd(38, 20))
|
|
{
|
|
if (lmap->ldap_multi_args)
|
|
{
|
|
sm_dprintf("ldapmap_lookup(%s, argv)\n",
|
|
map->map_mname);
|
|
for (i = 0; i < SM_LDAP_ARGS; i++)
|
|
{
|
|
sm_dprintf(" argv[%d] = %s\n", i,
|
|
argv[i] == NULL ? "NULL" : argv[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_dprintf("ldapmap_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
}
|
|
}
|
|
|
|
if (lmap->ldap_multi_args)
|
|
{
|
|
msgid = sm_ldap_search_m(lmap, argv);
|
|
|
|
/* free the argv array and its content, no longer needed */
|
|
for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
|
|
SM_FREE(argv[i]);
|
|
}
|
|
else
|
|
msgid = sm_ldap_search(lmap, keybuf);
|
|
if (msgid == SM_LDAP_ERR)
|
|
{
|
|
errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
|
|
save_errno = errno;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
/*
|
|
** Do not include keybuf as this error may be shown
|
|
** to outsiders.
|
|
*/
|
|
|
|
if (bitset(MF_NODEFER, map->map_mflags))
|
|
syserr("Error in ldap_search in map %s",
|
|
map->map_mname);
|
|
else
|
|
syserr("451 4.3.5 Error in ldap_search in map %s",
|
|
map->map_mname);
|
|
}
|
|
*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 */
|
|
ldapmap_close(map);
|
|
break;
|
|
}
|
|
errno = save_errno;
|
|
return NULL;
|
|
}
|
|
#if SM_LDAP_ERROR_ON_MISSING_ARGS
|
|
else if (msgid == SM_LDAP_ERR_ARG_MISS)
|
|
{
|
|
if (bitset(MF_NODEFER, map->map_mflags))
|
|
syserr("Error in ldap_search in map %s, too few arguments",
|
|
map->map_mname);
|
|
else
|
|
syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
|
|
map->map_mname);
|
|
*statp = EX_CONFIG;
|
|
return NULL;
|
|
}
|
|
#endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
|
|
|
|
*statp = EX_NOTFOUND;
|
|
vp = NULL;
|
|
|
|
flags = 0;
|
|
if (bitset(MF_SINGLEMATCH, map->map_mflags))
|
|
flags |= SM_LDAP_SINGLEMATCH;
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
flags |= SM_LDAP_MATCHONLY;
|
|
# if _FFR_LDAP_SINGLEDN
|
|
if (bitset(MF_SINGLEDN, map->map_mflags))
|
|
flags |= SM_LDAP_SINGLEDN;
|
|
# endif /* _FFR_LDAP_SINGLEDN */
|
|
|
|
/* Create an rpool for search related memory usage */
|
|
rpool = sm_rpool_new_x(NULL);
|
|
|
|
p = NULL;
|
|
*statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
|
|
rpool, &p, &plen, &psize, NULL);
|
|
save_errno = errno;
|
|
|
|
/* Copy result so rpool can be freed */
|
|
if (*statp == EX_OK && p != NULL)
|
|
vp = newstr(p);
|
|
sm_rpool_free(rpool);
|
|
|
|
/* need to restart LDAP connection? */
|
|
if (*statp == EX_RESTART)
|
|
{
|
|
*statp = EX_TEMPFAIL;
|
|
ldapmap_close(map);
|
|
}
|
|
|
|
errno = save_errno;
|
|
if (*statp != EX_OK && *statp != EX_NOTFOUND)
|
|
{
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
{
|
|
if (bitset(MF_NODEFER, map->map_mflags))
|
|
syserr("Error getting LDAP results, map=%s, name=%s",
|
|
map->map_mname, name);
|
|
else
|
|
syserr("451 4.3.5 Error getting LDAP results, map=%s, name=%s",
|
|
map->map_mname, name);
|
|
}
|
|
errno = save_errno;
|
|
return NULL;
|
|
}
|
|
|
|
/* Did we match anything? */
|
|
if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
|
|
return NULL;
|
|
|
|
if (*statp == EX_OK)
|
|
{
|
|
if (LogLevel > 9)
|
|
sm_syslog(LOG_INFO, CurEnv->e_id,
|
|
"ldap=%s, %.100s=>%s", map->map_mname, name,
|
|
vp == NULL ? "<NULL>" : vp);
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
result = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
{
|
|
/* vp != NULL according to test above */
|
|
result = map_rewrite(map, vp, strlen(vp), av);
|
|
}
|
|
if (vp != NULL)
|
|
sm_free(vp); /* XXX */
|
|
}
|
|
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 *
|
|
ldapmap_findconn(lmap)
|
|
SM_LDAP_STRUCT *lmap;
|
|
{
|
|
char *format;
|
|
char *nbuf;
|
|
char *id;
|
|
STAB *SM_NONVOLATILE s = NULL;
|
|
|
|
if (lmap->ldap_host != NULL)
|
|
id = lmap->ldap_host;
|
|
else if (lmap->ldap_uri != NULL)
|
|
id = lmap->ldap_uri;
|
|
else
|
|
id = "localhost";
|
|
|
|
format = "%s%c%d%c%d%c%s%c%s%d";
|
|
nbuf = sm_stringf_x(format,
|
|
id,
|
|
CONDELSE,
|
|
lmap->ldap_port,
|
|
CONDELSE,
|
|
lmap->ldap_version,
|
|
CONDELSE,
|
|
(lmap->ldap_binddn == NULL ? ""
|
|
: lmap->ldap_binddn),
|
|
CONDELSE,
|
|
(lmap->ldap_secret == NULL ? ""
|
|
: lmap->ldap_secret),
|
|
(int) CurrentPid);
|
|
SM_TRY
|
|
s = stab(nbuf, ST_LMAP, ST_ENTER);
|
|
SM_FINALLY
|
|
sm_free(nbuf);
|
|
SM_END_TRY
|
|
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
|
|
ldapmap_parseargs(map, args)
|
|
MAP *map;
|
|
char *args;
|
|
{
|
|
bool secretread = true;
|
|
bool attrssetup = false;
|
|
int i;
|
|
register char *p = args;
|
|
SM_LDAP_STRUCT *lmap;
|
|
struct lamvalues *lam;
|
|
struct ladvalues *lad;
|
|
struct lssvalues *lss;
|
|
char ldapfilt[MAXLINE];
|
|
char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
|
|
|
|
/* Get ldap struct pointer from map */
|
|
lmap = (SM_LDAP_STRUCT *) map->map_db1;
|
|
|
|
/* Check if setting the initial LDAP defaults */
|
|
if (lmap == NULL || lmap != LDAPDefaults)
|
|
{
|
|
/* We need to alloc an SM_LDAP_STRUCT struct */
|
|
lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
|
|
if (LDAPDefaults == NULL)
|
|
sm_ldap_clear(lmap);
|
|
else
|
|
STRUCTCOPY(*LDAPDefaults, *lmap);
|
|
}
|
|
|
|
/* there is no check whether there is really an argument */
|
|
map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
|
|
map->map_spacesub = SpaceSub; /* default value */
|
|
|
|
/* Check if setting up an alias or file class LDAP map */
|
|
if (bitset(MF_ALIAS, map->map_mflags))
|
|
{
|
|
/* Comma separate if used as an alias file */
|
|
map->map_coldelim = ',';
|
|
if (*args == '\0')
|
|
{
|
|
int n;
|
|
char *lc;
|
|
char jbuf[MAXHOSTNAMELEN];
|
|
char lcbuf[MAXLINE];
|
|
|
|
/* Get $j */
|
|
expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
|
|
if (jbuf[0] == '\0')
|
|
{
|
|
(void) sm_strlcpy(jbuf, "localhost",
|
|
sizeof(jbuf));
|
|
}
|
|
|
|
lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
|
|
if (lc == NULL)
|
|
lc = "";
|
|
else
|
|
{
|
|
expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
|
|
lc = lcbuf;
|
|
}
|
|
|
|
n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
|
|
"(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
|
|
lc, jbuf);
|
|
if (n >= sizeof(ldapfilt))
|
|
{
|
|
syserr("%s: Default LDAP string too long",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
|
|
/* default args for an alias LDAP entry */
|
|
lmap->ldap_filter = ldapfilt;
|
|
lmap->ldap_attr[0] = "objectClass";
|
|
lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
|
|
lmap->ldap_attr_needobjclass[0] = NULL;
|
|
lmap->ldap_attr[1] = "sendmailMTAAliasValue";
|
|
lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
|
|
lmap->ldap_attr_needobjclass[1] = NULL;
|
|
lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
|
|
lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
|
|
lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
|
|
lmap->ldap_attr[3] = "sendmailMTAAliasURL";
|
|
lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
|
|
lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
|
|
lmap->ldap_attr[4] = NULL;
|
|
lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
|
|
lmap->ldap_attr_needobjclass[4] = NULL;
|
|
attrssetup = true;
|
|
}
|
|
}
|
|
else if (bitset(MF_FILECLASS, map->map_mflags))
|
|
{
|
|
/* Space separate if used as a file class file */
|
|
map->map_coldelim = ' ';
|
|
}
|
|
|
|
# if _FFR_LDAP_NETWORK_TIMEOUT
|
|
lmap->ldap_networktmo = 120;
|
|
# endif /* _FFR_LDAP_NETWORK_TIMEOUT */
|
|
|
|
for (;;)
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
switch (*++p)
|
|
{
|
|
case 'A':
|
|
map->map_mflags |= MF_APPEND;
|
|
break;
|
|
|
|
case 'a':
|
|
map->map_app = ++p;
|
|
break;
|
|
|
|
case 'D':
|
|
map->map_mflags |= MF_DEFER;
|
|
break;
|
|
|
|
case 'f':
|
|
map->map_mflags |= MF_NOFOLDCASE;
|
|
break;
|
|
|
|
case 'm':
|
|
map->map_mflags |= MF_MATCHONLY;
|
|
break;
|
|
|
|
case 'N':
|
|
map->map_mflags |= MF_INCLNULL;
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
break;
|
|
|
|
case 'O':
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
break;
|
|
|
|
case 'o':
|
|
map->map_mflags |= MF_OPTIONAL;
|
|
break;
|
|
|
|
case 'q':
|
|
map->map_mflags |= MF_KEEPQUOTES;
|
|
break;
|
|
|
|
case 'S':
|
|
map->map_spacesub = *++p;
|
|
break;
|
|
|
|
case 'T':
|
|
map->map_tapp = ++p;
|
|
break;
|
|
|
|
case 't':
|
|
map->map_mflags |= MF_NODEFER;
|
|
break;
|
|
|
|
case 'z':
|
|
if (*++p != '\\')
|
|
map->map_coldelim = *p;
|
|
else
|
|
{
|
|
switch (*++p)
|
|
{
|
|
case 'n':
|
|
map->map_coldelim = '\n';
|
|
break;
|
|
|
|
case 't':
|
|
map->map_coldelim = '\t';
|
|
break;
|
|
|
|
default:
|
|
map->map_coldelim = '\\';
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* Start of ldapmap specific args */
|
|
case '1':
|
|
map->map_mflags |= MF_SINGLEMATCH;
|
|
break;
|
|
|
|
# if _FFR_LDAP_SINGLEDN
|
|
case '2':
|
|
map->map_mflags |= MF_SINGLEDN;
|
|
break;
|
|
# endif /* _FFR_LDAP_SINGLEDN */
|
|
|
|
case 'b': /* search base */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_base = p;
|
|
break;
|
|
|
|
# if _FFR_LDAP_NETWORK_TIMEOUT
|
|
case 'c': /* network (connect) timeout */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_networktmo = atoi(p);
|
|
break;
|
|
# endif /* _FFR_LDAP_NETWORK_TIMEOUT */
|
|
|
|
case 'd': /* Dn to bind to server as */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_binddn = p;
|
|
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",
|
|
map->map_mname);
|
|
return false;
|
|
# else /* !USE_LDAP_INIT */
|
|
if (lmap->ldap_host != NULL)
|
|
{
|
|
syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_uri = p;
|
|
break;
|
|
# endif /* !USE_LDAP_INIT */
|
|
|
|
case 'h': /* ldap host */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
if (lmap->ldap_uri != NULL)
|
|
{
|
|
syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
lmap->ldap_host = p;
|
|
break;
|
|
|
|
case 'K':
|
|
lmap->ldap_multi_args = true;
|
|
break;
|
|
|
|
case 'k': /* search field */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_filter = p;
|
|
break;
|
|
|
|
case 'l': /* time limit */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_timelimit = atoi(p);
|
|
lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
|
|
break;
|
|
|
|
case 'M': /* Method for binding */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
|
|
if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
|
|
p += 10;
|
|
|
|
for (lam = LDAPAuthMethods;
|
|
lam != NULL && lam->lam_name != NULL; lam++)
|
|
{
|
|
if (sm_strncasecmp(p, lam->lam_name,
|
|
strlen(lam->lam_name)) == 0)
|
|
break;
|
|
}
|
|
if (lam->lam_name != NULL)
|
|
lmap->ldap_method = lam->lam_code;
|
|
else
|
|
{
|
|
/* bad config line */
|
|
if (!bitset(MCF_OPTFILE,
|
|
map->map_class->map_cflags))
|
|
{
|
|
char *ptr;
|
|
|
|
if ((ptr = strchr(p, ' ')) != NULL)
|
|
*ptr = '\0';
|
|
syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
|
|
p, map->map_mname);
|
|
if (ptr != NULL)
|
|
*ptr = ' ';
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'n': /* retrieve attribute names only */
|
|
lmap->ldap_attrsonly = LDAPMAP_TRUE;
|
|
break;
|
|
|
|
/*
|
|
** This is a string that is dependent on the
|
|
** method used defined by 'M'.
|
|
*/
|
|
|
|
case 'P': /* Secret password for binding */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_secret = p;
|
|
secretread = false;
|
|
break;
|
|
|
|
case 'p': /* ldap port */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_port = atoi(p);
|
|
break;
|
|
|
|
/* args stolen from ldapsearch.c */
|
|
case 'R': /* don't auto chase referrals */
|
|
# ifdef LDAP_REFERRALS
|
|
lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
|
|
# else /* LDAP_REFERRALS */
|
|
syserr("compile with -DLDAP_REFERRALS for referral support");
|
|
# endif /* LDAP_REFERRALS */
|
|
break;
|
|
|
|
case 'r': /* alias dereferencing */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
|
|
if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
|
|
p += 11;
|
|
|
|
for (lad = LDAPAliasDereference;
|
|
lad != NULL && lad->lad_name != NULL; lad++)
|
|
{
|
|
if (sm_strncasecmp(p, lad->lad_name,
|
|
strlen(lad->lad_name)) == 0)
|
|
break;
|
|
}
|
|
if (lad->lad_name != NULL)
|
|
lmap->ldap_deref = lad->lad_code;
|
|
else
|
|
{
|
|
/* bad config line */
|
|
if (!bitset(MCF_OPTFILE,
|
|
map->map_class->map_cflags))
|
|
{
|
|
char *ptr;
|
|
|
|
if ((ptr = strchr(p, ' ')) != NULL)
|
|
*ptr = '\0';
|
|
syserr("Deref must be [never|always|search|find] (not %s) in map %s",
|
|
p, map->map_mname);
|
|
if (ptr != NULL)
|
|
*ptr = ' ';
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 's': /* search scope */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
|
|
if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
|
|
p += 11;
|
|
|
|
for (lss = LDAPSearchScope;
|
|
lss != NULL && lss->lss_name != NULL; lss++)
|
|
{
|
|
if (sm_strncasecmp(p, lss->lss_name,
|
|
strlen(lss->lss_name)) == 0)
|
|
break;
|
|
}
|
|
if (lss->lss_name != NULL)
|
|
lmap->ldap_scope = lss->lss_code;
|
|
else
|
|
{
|
|
/* bad config line */
|
|
if (!bitset(MCF_OPTFILE,
|
|
map->map_class->map_cflags))
|
|
{
|
|
char *ptr;
|
|
|
|
if ((ptr = strchr(p, ' ')) != NULL)
|
|
*ptr = '\0';
|
|
syserr("Scope must be [base|one|sub] (not %s) in map %s",
|
|
p, map->map_mname);
|
|
if (ptr != NULL)
|
|
*ptr = ' ';
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'V':
|
|
if (*++p != '\\')
|
|
lmap->ldap_attrsep = *p;
|
|
else
|
|
{
|
|
switch (*++p)
|
|
{
|
|
case 'n':
|
|
lmap->ldap_attrsep = '\n';
|
|
break;
|
|
|
|
case 't':
|
|
lmap->ldap_attrsep = '\t';
|
|
break;
|
|
|
|
default:
|
|
lmap->ldap_attrsep = '\\';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'v': /* attr to return */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_attr[0] = p;
|
|
lmap->ldap_attr[1] = NULL;
|
|
break;
|
|
|
|
case 'w':
|
|
/* -w should be for passwd, -P should be for version */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_version = atoi(p);
|
|
# ifdef LDAP_VERSION_MAX
|
|
if (lmap->ldap_version > LDAP_VERSION_MAX)
|
|
{
|
|
syserr("LDAP version %d exceeds max of %d in map %s",
|
|
lmap->ldap_version, LDAP_VERSION_MAX,
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
# endif /* LDAP_VERSION_MAX */
|
|
# ifdef LDAP_VERSION_MIN
|
|
if (lmap->ldap_version < LDAP_VERSION_MIN)
|
|
{
|
|
syserr("LDAP version %d is lower than min of %d in map %s",
|
|
lmap->ldap_version, LDAP_VERSION_MIN,
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
# endif /* LDAP_VERSION_MIN */
|
|
break;
|
|
|
|
case 'Z':
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
lmap->ldap_sizelimit = atoi(p);
|
|
break;
|
|
|
|
default:
|
|
syserr("Illegal option %c map %s", *p, map->map_mname);
|
|
break;
|
|
}
|
|
|
|
/* need to account for quoted strings here */
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
{
|
|
if (*p == '"')
|
|
{
|
|
while (*++p != '"' && *p != '\0')
|
|
continue;
|
|
if (*p != '\0')
|
|
p++;
|
|
}
|
|
else
|
|
p++;
|
|
}
|
|
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
|
|
if (map->map_app != NULL)
|
|
map->map_app = newstr(ldapmap_dequote(map->map_app));
|
|
if (map->map_tapp != NULL)
|
|
map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
|
|
|
|
/*
|
|
** We need to swallow up all the stuff into a struct
|
|
** and dump it into map->map_dbptr1
|
|
*/
|
|
|
|
if (lmap->ldap_host != NULL &&
|
|
(LDAPDefaults == NULL ||
|
|
LDAPDefaults == lmap ||
|
|
LDAPDefaults->ldap_host != lmap->ldap_host))
|
|
lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
|
|
map->map_domain = lmap->ldap_host;
|
|
|
|
if (lmap->ldap_uri != NULL &&
|
|
(LDAPDefaults == NULL ||
|
|
LDAPDefaults == lmap ||
|
|
LDAPDefaults->ldap_uri != lmap->ldap_uri))
|
|
lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
|
|
map->map_domain = lmap->ldap_uri;
|
|
|
|
if (lmap->ldap_binddn != NULL &&
|
|
(LDAPDefaults == NULL ||
|
|
LDAPDefaults == lmap ||
|
|
LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
|
|
lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
|
|
|
|
if (lmap->ldap_secret != NULL &&
|
|
(LDAPDefaults == NULL ||
|
|
LDAPDefaults == lmap ||
|
|
LDAPDefaults->ldap_secret != lmap->ldap_secret))
|
|
{
|
|
SM_FILE_T *sfd;
|
|
long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
|
|
|
|
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;
|
|
|
|
sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
|
|
O_RDONLY, 0, sff);
|
|
if (sfd == NULL)
|
|
{
|
|
syserr("LDAP map: cannot open secret %s",
|
|
ldapmap_dequote(lmap->ldap_secret));
|
|
return false;
|
|
}
|
|
lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
|
|
sfd, TimeOuts.to_fileopen,
|
|
"ldapmap_parseargs");
|
|
(void) sm_io_close(sfd, SM_TIME_DEFAULT);
|
|
if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
|
|
{
|
|
syserr("LDAP map: secret in %s too long",
|
|
ldapmap_dequote(lmap->ldap_secret));
|
|
return false;
|
|
}
|
|
if (lmap->ldap_secret != NULL &&
|
|
strlen(m_tmp) > 0)
|
|
{
|
|
/* chomp newline */
|
|
if (m_tmp[strlen(m_tmp) - 1] == '\n')
|
|
m_tmp[strlen(m_tmp) - 1] = '\0';
|
|
|
|
lmap->ldap_secret = m_tmp;
|
|
}
|
|
break;
|
|
|
|
# ifdef LDAP_AUTH_KRBV4
|
|
case LDAP_AUTH_KRBV4:
|
|
|
|
/*
|
|
** Secret is where the ticket file is
|
|
** stashed
|
|
*/
|
|
|
|
(void) sm_snprintf(m_tmp, sizeof(m_tmp),
|
|
"KRBTKFILE=%s",
|
|
ldapmap_dequote(lmap->ldap_secret));
|
|
lmap->ldap_secret = m_tmp;
|
|
break;
|
|
# endif /* LDAP_AUTH_KRBV4 */
|
|
|
|
default: /* Should NEVER get here */
|
|
syserr("LDAP map: Illegal value in lmap method");
|
|
return false;
|
|
/* NOTREACHED */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lmap->ldap_secret != NULL &&
|
|
(LDAPDefaults == NULL ||
|
|
LDAPDefaults == lmap ||
|
|
LDAPDefaults->ldap_secret != lmap->ldap_secret))
|
|
lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
|
|
|
|
if (lmap->ldap_base != NULL &&
|
|
(LDAPDefaults == NULL ||
|
|
LDAPDefaults == lmap ||
|
|
LDAPDefaults->ldap_base != lmap->ldap_base))
|
|
lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
|
|
|
|
/*
|
|
** 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 (bitset(MF_SINGLEMATCH, map->map_mflags))
|
|
lmap->ldap_sizelimit = 2;
|
|
|
|
/* If setting defaults, don't process ldap_filter and ldap_attr */
|
|
if (lmap == LDAPDefaults)
|
|
return true;
|
|
|
|
if (lmap->ldap_filter != NULL)
|
|
lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
|
|
else
|
|
{
|
|
if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
|
|
{
|
|
syserr("No filter given in map %s", map->map_mname);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!attrssetup && lmap->ldap_attr[0] != NULL)
|
|
{
|
|
bool recurse = false;
|
|
bool normalseen = false;
|
|
|
|
i = 0;
|
|
p = ldapmap_dequote(lmap->ldap_attr[0]);
|
|
lmap->ldap_attr[0] = NULL;
|
|
|
|
/* Prime the attr list with the objectClass attribute */
|
|
lmap->ldap_attr[i] = "objectClass";
|
|
lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
|
|
lmap->ldap_attr_needobjclass[i] = NULL;
|
|
i++;
|
|
|
|
while (p != NULL)
|
|
{
|
|
char *v;
|
|
|
|
while (isascii(*p) && isspace(*p))
|
|
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)",
|
|
map->map_mname, LDAPMAP_MAX_ATTR);
|
|
return false;
|
|
}
|
|
if (*v != '\0')
|
|
{
|
|
int j;
|
|
int use;
|
|
char *type;
|
|
char *needobjclass;
|
|
|
|
type = strchr(v, ':');
|
|
if (type != NULL)
|
|
{
|
|
*type++ = '\0';
|
|
needobjclass = strchr(type, ':');
|
|
if (needobjclass != NULL)
|
|
*needobjclass++ = '\0';
|
|
}
|
|
else
|
|
{
|
|
needobjclass = NULL;
|
|
}
|
|
|
|
use = i;
|
|
|
|
/* allow override on "objectClass" type */
|
|
if (sm_strcasecmp(v, "objectClass") == 0 &&
|
|
lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
|
|
{
|
|
use = 0;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** Don't add something to attribute
|
|
** list twice.
|
|
*/
|
|
|
|
for (j = 1; j < i; j++)
|
|
{
|
|
if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
|
|
{
|
|
syserr("Duplicate attribute (%s) in %s",
|
|
v, map->map_mname);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
lmap->ldap_attr[use] = newstr(v);
|
|
if (needobjclass != NULL &&
|
|
*needobjclass != '\0' &&
|
|
*needobjclass != '*')
|
|
{
|
|
lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
|
|
}
|
|
else
|
|
{
|
|
lmap->ldap_attr_needobjclass[use] = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
if (type != NULL && *type != '\0')
|
|
{
|
|
if (sm_strcasecmp(type, "dn") == 0)
|
|
{
|
|
recurse = true;
|
|
lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
|
|
}
|
|
else if (sm_strcasecmp(type, "filter") == 0)
|
|
{
|
|
recurse = true;
|
|
lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
|
|
}
|
|
else if (sm_strcasecmp(type, "url") == 0)
|
|
{
|
|
recurse = true;
|
|
lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
|
|
}
|
|
else if (sm_strcasecmp(type, "normal") == 0)
|
|
{
|
|
lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
|
|
normalseen = true;
|
|
}
|
|
else
|
|
{
|
|
syserr("Unknown attribute type (%s) in %s",
|
|
type, map->map_mname);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
|
|
normalseen = true;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
lmap->ldap_attr[i] = NULL;
|
|
|
|
/* Set in case needed in future code */
|
|
attrssetup = true;
|
|
|
|
if (recurse && !normalseen)
|
|
{
|
|
syserr("LDAP recursion requested in %s but no returnable attribute given",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
|
|
{
|
|
syserr("LDAP recursion requested in %s can not be used with -n",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
}
|
|
map->map_db1 = (ARBPTR_T) lmap;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
|
|
**
|
|
** Parameters:
|
|
** spec -- map argument string from LDAPDefaults option
|
|
**
|
|
** Returns:
|
|
** None.
|
|
*/
|
|
|
|
void
|
|
ldapmap_set_defaults(spec)
|
|
char *spec;
|
|
{
|
|
STAB *class;
|
|
MAP map;
|
|
|
|
/* Allocate and set the default values */
|
|
if (LDAPDefaults == NULL)
|
|
LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
|
|
sm_ldap_clear(LDAPDefaults);
|
|
|
|
memset(&map, '\0', sizeof(map));
|
|
|
|
/* look up the class */
|
|
class = stab("ldap", ST_MAPCLASS, ST_FIND);
|
|
if (class == NULL)
|
|
{
|
|
syserr("readcf: LDAPDefaultSpec: class ldap not available");
|
|
return;
|
|
}
|
|
map.map_class = &class->s_mapclass;
|
|
map.map_db1 = (ARBPTR_T) LDAPDefaults;
|
|
map.map_mname = "O LDAPDefaultSpec";
|
|
|
|
(void) ldapmap_parseargs(&map, spec);
|
|
|
|
/* These should never be set in LDAPDefaults */
|
|
if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
|
|
map.map_spacesub != SpaceSub ||
|
|
map.map_app != NULL ||
|
|
map.map_tapp != NULL)
|
|
{
|
|
syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
|
|
SM_FREE_CLR(map.map_app);
|
|
SM_FREE_CLR(map.map_tapp);
|
|
}
|
|
|
|
if (LDAPDefaults->ldap_filter != NULL)
|
|
{
|
|
syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
|
|
|
|
/* don't free, it isn't malloc'ed in parseargs */
|
|
LDAPDefaults->ldap_filter = NULL;
|
|
}
|
|
|
|
if (LDAPDefaults->ldap_attr[0] != NULL)
|
|
{
|
|
syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
|
|
/* don't free, they aren't malloc'ed in parseargs */
|
|
LDAPDefaults->ldap_attr[0] = NULL;
|
|
}
|
|
}
|
|
#endif /* LDAPMAP */
|
|
/*
|
|
** PH map
|
|
*/
|
|
|
|
#if PH_MAP
|
|
|
|
/*
|
|
** Support for the CCSO Nameserver (ph/qi).
|
|
** This code is intended to replace the so-called "ph mailer".
|
|
** Contributed by Mark D. Roth. 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
|
|
ph_map_parseargs(map, args)
|
|
MAP *map;
|
|
char *args;
|
|
{
|
|
register bool done;
|
|
register char *p = args;
|
|
PH_MAP_STRUCT *pmap = NULL;
|
|
|
|
/* initialize version string */
|
|
(void) sm_snprintf(phmap_id, sizeof(phmap_id),
|
|
"sendmail-%s phmap-20010529 libphclient-%s",
|
|
Version, libphclient_version);
|
|
|
|
pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
|
|
|
|
/* defaults */
|
|
pmap->ph_servers = NULL;
|
|
pmap->ph_field_list = NULL;
|
|
pmap->ph = NULL;
|
|
pmap->ph_timeout = 0;
|
|
pmap->ph_fastclose = 0;
|
|
|
|
map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
|
|
for (;;)
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
switch (*++p)
|
|
{
|
|
case 'N':
|
|
map->map_mflags |= MF_INCLNULL;
|
|
map->map_mflags &= ~MF_TRY0NULL;
|
|
break;
|
|
|
|
case 'O':
|
|
map->map_mflags &= ~MF_TRY1NULL;
|
|
break;
|
|
|
|
case 'o':
|
|
map->map_mflags |= MF_OPTIONAL;
|
|
break;
|
|
|
|
case 'f':
|
|
map->map_mflags |= MF_NOFOLDCASE;
|
|
break;
|
|
|
|
case 'm':
|
|
map->map_mflags |= MF_MATCHONLY;
|
|
break;
|
|
|
|
case 'A':
|
|
map->map_mflags |= MF_APPEND;
|
|
break;
|
|
|
|
case 'q':
|
|
map->map_mflags |= MF_KEEPQUOTES;
|
|
break;
|
|
|
|
case 't':
|
|
map->map_mflags |= MF_NODEFER;
|
|
break;
|
|
|
|
case 'a':
|
|
map->map_app = ++p;
|
|
break;
|
|
|
|
case 'T':
|
|
map->map_tapp = ++p;
|
|
break;
|
|
|
|
case 'l':
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
pmap->ph_timeout = atoi(p);
|
|
break;
|
|
|
|
case 'S':
|
|
map->map_spacesub = *++p;
|
|
break;
|
|
|
|
case 'D':
|
|
map->map_mflags |= MF_DEFER;
|
|
break;
|
|
|
|
case 'h': /* PH server list */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
pmap->ph_servers = p;
|
|
break;
|
|
|
|
case 'k': /* fields to search for */
|
|
while (isascii(*++p) && isspace(*p))
|
|
continue;
|
|
pmap->ph_field_list = p;
|
|
break;
|
|
|
|
default:
|
|
syserr("ph_map_parseargs: unknown option -%c", *p);
|
|
}
|
|
|
|
/* try to account for quoted strings */
|
|
done = isascii(*p) && isspace(*p);
|
|
while (*p != '\0' && !done)
|
|
{
|
|
if (*p == '"')
|
|
{
|
|
while (*++p != '"' && *p != '\0')
|
|
continue;
|
|
if (*p != '\0')
|
|
p++;
|
|
}
|
|
else
|
|
p++;
|
|
done = isascii(*p) && isspace(*p);
|
|
}
|
|
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
|
|
if (map->map_app != NULL)
|
|
map->map_app = newstr(ph_map_dequote(map->map_app));
|
|
if (map->map_tapp != NULL)
|
|
map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
|
|
|
|
if (pmap->ph_field_list != NULL)
|
|
pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
|
|
|
|
if (pmap->ph_servers != NULL)
|
|
pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
|
|
else
|
|
{
|
|
syserr("ph_map_parseargs: -h flag is required");
|
|
return false;
|
|
}
|
|
|
|
map->map_db1 = (ARBPTR_T) pmap;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** PH_MAP_CLOSE -- close the connection to the ph server
|
|
*/
|
|
|
|
void
|
|
ph_map_close(map)
|
|
MAP *map;
|
|
{
|
|
PH_MAP_STRUCT *pmap;
|
|
|
|
pmap = (PH_MAP_STRUCT *)map->map_db1;
|
|
if (tTd(38, 9))
|
|
sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
|
|
map->map_mname, pmap->ph_fastclose);
|
|
|
|
|
|
if (pmap->ph != NULL)
|
|
{
|
|
ph_set_sendhook(pmap->ph, NULL);
|
|
ph_set_recvhook(pmap->ph, NULL);
|
|
ph_close(pmap->ph, pmap->ph_fastclose);
|
|
}
|
|
|
|
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
|
|
}
|
|
|
|
static jmp_buf PHTimeout;
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
ph_timeout(unused)
|
|
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.
|
|
*/
|
|
|
|
errno = ETIMEDOUT;
|
|
longjmp(PHTimeout, 1);
|
|
}
|
|
|
|
static void
|
|
#if NPH_VERSION >= 10200
|
|
ph_map_send_debug(appdata, text)
|
|
void *appdata;
|
|
#else
|
|
ph_map_send_debug(text)
|
|
#endif
|
|
char *text;
|
|
{
|
|
if (LogLevel > 9)
|
|
sm_syslog(LOG_NOTICE, CurEnv->e_id,
|
|
"ph_map_send_debug: ==> %s", text);
|
|
if (tTd(38, 20))
|
|
sm_dprintf("ph_map_send_debug: ==> %s\n", text);
|
|
}
|
|
|
|
static void
|
|
#if NPH_VERSION >= 10200
|
|
ph_map_recv_debug(appdata, text)
|
|
void *appdata;
|
|
#else
|
|
ph_map_recv_debug(text)
|
|
#endif
|
|
char *text;
|
|
{
|
|
if (LogLevel > 10)
|
|
sm_syslog(LOG_NOTICE, CurEnv->e_id,
|
|
"ph_map_recv_debug: <== %s", text);
|
|
if (tTd(38, 21))
|
|
sm_dprintf("ph_map_recv_debug: <== %s\n", text);
|
|
}
|
|
|
|
/*
|
|
** PH_MAP_OPEN -- sub for opening PH map
|
|
*/
|
|
bool
|
|
ph_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
PH_MAP_STRUCT *pmap;
|
|
register SM_EVENT *ev = NULL;
|
|
int save_errno = 0;
|
|
char *hostlist, *host;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("ph_map_open(%s)\n", map->map_mname);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
return false;
|
|
}
|
|
|
|
if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
|
|
bitset(MF_DEFER, map->map_mflags))
|
|
{
|
|
if (tTd(9, 1))
|
|
sm_dprintf("ph_map_open(%s) => DEFERRED\n",
|
|
map->map_mname);
|
|
|
|
/*
|
|
** 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.
|
|
*/
|
|
|
|
map->map_mflags &= ~MF_DEFER;
|
|
return false;
|
|
}
|
|
|
|
pmap = (PH_MAP_STRUCT *)map->map_db1;
|
|
pmap->ph_fastclose = 0; /* refresh field for reopen */
|
|
|
|
/* try each host in the list */
|
|
hostlist = newstr(pmap->ph_servers);
|
|
for (host = strtok(hostlist, " ");
|
|
host != NULL;
|
|
host = strtok(NULL, " "))
|
|
{
|
|
/* set timeout */
|
|
if (pmap->ph_timeout != 0)
|
|
{
|
|
if (setjmp(PHTimeout) != 0)
|
|
{
|
|
ev = NULL;
|
|
if (LogLevel > 1)
|
|
sm_syslog(LOG_NOTICE, CurEnv->e_id,
|
|
"timeout connecting to PH server %.100s",
|
|
host);
|
|
errno = ETIMEDOUT;
|
|
goto ph_map_open_abort;
|
|
}
|
|
ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
|
|
}
|
|
|
|
/* open connection to server */
|
|
if (ph_open(&(pmap->ph), host,
|
|
PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
|
|
ph_map_send_debug, ph_map_recv_debug
|
|
#if NPH_VERSION >= 10200
|
|
, NULL
|
|
#endif
|
|
) == 0
|
|
&& ph_id(pmap->ph, phmap_id) == 0)
|
|
{
|
|
if (ev != NULL)
|
|
sm_clrevent(ev);
|
|
sm_free(hostlist); /* XXX */
|
|
return true;
|
|
}
|
|
|
|
ph_map_open_abort:
|
|
save_errno = errno;
|
|
if (ev != NULL)
|
|
sm_clrevent(ev);
|
|
pmap->ph_fastclose = PH_CLOSE_FAST;
|
|
ph_map_close(map);
|
|
errno = save_errno;
|
|
}
|
|
|
|
if (bitset(MF_NODEFER, map->map_mflags))
|
|
{
|
|
if (errno == 0)
|
|
errno = EAGAIN;
|
|
syserr("ph_map_open: %s: cannot connect to PH server",
|
|
map->map_mname);
|
|
}
|
|
else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
|
|
sm_syslog(LOG_NOTICE, CurEnv->e_id,
|
|
"ph_map_open: %s: cannot connect to PH server",
|
|
map->map_mname);
|
|
sm_free(hostlist); /* XXX */
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
** PH_MAP_LOOKUP -- look up key from ph server
|
|
*/
|
|
|
|
char *
|
|
ph_map_lookup(map, key, args, pstat)
|
|
MAP *map;
|
|
char *key;
|
|
char **args;
|
|
int *pstat;
|
|
{
|
|
int i, save_errno = 0;
|
|
register SM_EVENT *ev = NULL;
|
|
PH_MAP_STRUCT *pmap;
|
|
char *value = NULL;
|
|
|
|
pmap = (PH_MAP_STRUCT *)map->map_db1;
|
|
|
|
*pstat = EX_OK;
|
|
|
|
/* set timeout */
|
|
if (pmap->ph_timeout != 0)
|
|
{
|
|
if (setjmp(PHTimeout) != 0)
|
|
{
|
|
ev = NULL;
|
|
if (LogLevel > 1)
|
|
sm_syslog(LOG_NOTICE, CurEnv->e_id,
|
|
"timeout during PH lookup of %.100s",
|
|
key);
|
|
errno = ETIMEDOUT;
|
|
*pstat = EX_TEMPFAIL;
|
|
goto ph_map_lookup_abort;
|
|
}
|
|
ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
|
|
}
|
|
|
|
/* perform lookup */
|
|
i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
|
|
if (i == -1)
|
|
*pstat = EX_TEMPFAIL;
|
|
else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
|
|
*pstat = EX_UNAVAILABLE;
|
|
|
|
ph_map_lookup_abort:
|
|
if (ev != NULL)
|
|
sm_clrevent(ev);
|
|
|
|
/*
|
|
** Close the connection if the timer popped
|
|
** or we got a temporary PH error
|
|
*/
|
|
|
|
if (*pstat == EX_TEMPFAIL)
|
|
{
|
|
save_errno = errno;
|
|
pmap->ph_fastclose = PH_CLOSE_FAST;
|
|
ph_map_close(map);
|
|
errno = save_errno;
|
|
}
|
|
|
|
if (*pstat == EX_OK)
|
|
{
|
|
if (tTd(38,20))
|
|
sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
|
|
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, key, strlen(key), NULL);
|
|
else
|
|
return map_rewrite(map, value, strlen(value), args);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* PH_MAP */
|
|
|
|
/*
|
|
** syslog map
|
|
*/
|
|
|
|
#define map_prio map_lockfd /* overload field */
|
|
|
|
/*
|
|
** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
|
|
*/
|
|
|
|
bool
|
|
syslog_map_parseargs(map, args)
|
|
MAP *map;
|
|
char *args;
|
|
{
|
|
char *p = args;
|
|
char *priority = NULL;
|
|
|
|
/* there is no check whether there is really an argument */
|
|
while (*p != '\0')
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
++p;
|
|
if (*p == 'D')
|
|
{
|
|
map->map_mflags |= MF_DEFER;
|
|
++p;
|
|
}
|
|
else if (*p == 'S')
|
|
{
|
|
map->map_spacesub = *++p;
|
|
if (*p != '\0')
|
|
p++;
|
|
}
|
|
else if (*p == 'L')
|
|
{
|
|
while (*++p != '\0' && isascii(*p) && isspace(*p))
|
|
continue;
|
|
if (*p == '\0')
|
|
break;
|
|
priority = p;
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
p++;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
else
|
|
{
|
|
syserr("Illegal option %c map syslog", *p);
|
|
++p;
|
|
}
|
|
}
|
|
|
|
if (priority == NULL)
|
|
map->map_prio = LOG_INFO;
|
|
else
|
|
{
|
|
if (sm_strncasecmp("LOG_", priority, 4) == 0)
|
|
priority += 4;
|
|
|
|
#ifdef LOG_EMERG
|
|
if (sm_strcasecmp("EMERG", priority) == 0)
|
|
map->map_prio = LOG_EMERG;
|
|
else
|
|
#endif /* LOG_EMERG */
|
|
#ifdef LOG_ALERT
|
|
if (sm_strcasecmp("ALERT", priority) == 0)
|
|
map->map_prio = LOG_ALERT;
|
|
else
|
|
#endif /* LOG_ALERT */
|
|
#ifdef LOG_CRIT
|
|
if (sm_strcasecmp("CRIT", priority) == 0)
|
|
map->map_prio = LOG_CRIT;
|
|
else
|
|
#endif /* LOG_CRIT */
|
|
#ifdef LOG_ERR
|
|
if (sm_strcasecmp("ERR", priority) == 0)
|
|
map->map_prio = LOG_ERR;
|
|
else
|
|
#endif /* LOG_ERR */
|
|
#ifdef LOG_WARNING
|
|
if (sm_strcasecmp("WARNING", priority) == 0)
|
|
map->map_prio = LOG_WARNING;
|
|
else
|
|
#endif /* LOG_WARNING */
|
|
#ifdef LOG_NOTICE
|
|
if (sm_strcasecmp("NOTICE", priority) == 0)
|
|
map->map_prio = LOG_NOTICE;
|
|
else
|
|
#endif /* LOG_NOTICE */
|
|
#ifdef LOG_INFO
|
|
if (sm_strcasecmp("INFO", priority) == 0)
|
|
map->map_prio = LOG_INFO;
|
|
else
|
|
#endif /* LOG_INFO */
|
|
#ifdef LOG_DEBUG
|
|
if (sm_strcasecmp("DEBUG", priority) == 0)
|
|
map->map_prio = 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 *
|
|
syslog_map_lookup(map, string, args, statp)
|
|
MAP *map;
|
|
char *string;
|
|
char **args;
|
|
int *statp;
|
|
{
|
|
char *ptr = map_rewrite(map, string, strlen(string), args);
|
|
|
|
if (ptr != NULL)
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
|
|
map->map_mname, map->map_prio, ptr);
|
|
|
|
sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
|
|
}
|
|
|
|
*statp = EX_OK;
|
|
return "";
|
|
}
|
|
|
|
#if _FFR_DPRINTF_MAP
|
|
/*
|
|
** dprintf map
|
|
*/
|
|
|
|
#define map_dbg_level map_lockfd /* overload field */
|
|
|
|
/*
|
|
** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
|
|
*/
|
|
|
|
bool
|
|
dprintf_map_parseargs(map, args)
|
|
MAP *map;
|
|
char *args;
|
|
{
|
|
char *p = args;
|
|
char *dbg_level = NULL;
|
|
|
|
/* there is no check whether there is really an argument */
|
|
while (*p != '\0')
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
++p;
|
|
if (*p == 'D')
|
|
{
|
|
map->map_mflags |= MF_DEFER;
|
|
++p;
|
|
}
|
|
else if (*p == 'S')
|
|
{
|
|
map->map_spacesub = *++p;
|
|
if (*p != '\0')
|
|
p++;
|
|
}
|
|
else if (*p == 'd')
|
|
{
|
|
while (*++p != '\0' && isascii(*p) && isspace(*p))
|
|
continue;
|
|
if (*p == '\0')
|
|
break;
|
|
dbg_level = p;
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
p++;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
else
|
|
{
|
|
syserr("Illegal option %c map dprintf", *p);
|
|
++p;
|
|
}
|
|
}
|
|
|
|
if (dbg_level == NULL)
|
|
map->map_dbg_level = 0;
|
|
else
|
|
{
|
|
if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
|
|
{
|
|
syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
|
|
map->map_mname, map->map_file,
|
|
dbg_level);
|
|
return false;
|
|
}
|
|
map->map_dbg_level = atoi(dbg_level);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
|
|
*/
|
|
|
|
char *
|
|
dprintf_map_lookup(map, string, args, statp)
|
|
MAP *map;
|
|
char *string;
|
|
char **args;
|
|
int *statp;
|
|
{
|
|
char *ptr = map_rewrite(map, string, strlen(string), args);
|
|
|
|
if (ptr != NULL && tTd(85, map->map_dbg_level))
|
|
sm_dprintf("%s\n", ptr);
|
|
*statp = EX_OK;
|
|
return "";
|
|
}
|
|
#endif /* _FFR_DPRINTF_MAP */
|
|
|
|
/*
|
|
** HESIOD Modules
|
|
*/
|
|
|
|
#if HESIOD
|
|
|
|
bool
|
|
hes_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
if (tTd(38, 2))
|
|
sm_dprintf("hes_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
return false;
|
|
}
|
|
|
|
# ifdef HESIOD_INIT
|
|
if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
|
|
return true;
|
|
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
|
|
sm_errstring(errno));
|
|
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;
|
|
}
|
|
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
|
|
|
|
return false;
|
|
# endif /* HESIOD_INIT */
|
|
}
|
|
|
|
char *
|
|
hes_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
char **hp;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
|
|
|
|
if (name[0] == '\\')
|
|
{
|
|
char *np;
|
|
int nl;
|
|
int save_errno;
|
|
char nbuf[MAXNAME];
|
|
|
|
nl = strlen(name);
|
|
if (nl < sizeof(nbuf) - 1)
|
|
np = nbuf;
|
|
else
|
|
np = xalloc(strlen(name) + 2);
|
|
np[0] = '\\';
|
|
(void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
|
|
# ifdef HESIOD_INIT
|
|
hp = hesiod_resolve(HesiodContext, np, map->map_file);
|
|
# else /* HESIOD_INIT */
|
|
hp = hes_resolve(np, map->map_file);
|
|
# endif /* HESIOD_INIT */
|
|
save_errno = errno;
|
|
if (np != nbuf)
|
|
sm_free(np); /* XXX */
|
|
errno = save_errno;
|
|
}
|
|
else
|
|
{
|
|
# ifdef HESIOD_INIT
|
|
hp = hesiod_resolve(HesiodContext, name, map->map_file);
|
|
# else /* HESIOD_INIT */
|
|
hp = hes_resolve(name, map->map_file);
|
|
# endif /* HESIOD_INIT */
|
|
}
|
|
# ifdef HESIOD_INIT
|
|
if (hp == NULL || *hp == NULL)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case ENOENT:
|
|
*statp = EX_NOTFOUND;
|
|
break;
|
|
case ECONNREFUSED:
|
|
*statp = EX_TEMPFAIL;
|
|
break;
|
|
case EMSGSIZE:
|
|
case ENOMEM:
|
|
default:
|
|
*statp = EX_UNAVAILABLE;
|
|
break;
|
|
}
|
|
if (hp != NULL)
|
|
hesiod_free_list(HesiodContext, hp);
|
|
return NULL;
|
|
}
|
|
# else /* HESIOD_INIT */
|
|
if (hp == NULL || hp[0] == NULL)
|
|
{
|
|
switch (hes_error())
|
|
{
|
|
case HES_ER_OK:
|
|
*statp = EX_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 */
|
|
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
return map_rewrite(map, hp[0], strlen(hp[0]), av);
|
|
}
|
|
|
|
/*
|
|
** HES_MAP_CLOSE -- free the Hesiod context
|
|
*/
|
|
|
|
void
|
|
hes_map_close(map)
|
|
MAP *map;
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("hes_map_close(%s)\n", map->map_file);
|
|
|
|
# ifdef HESIOD_INIT
|
|
/* Free the hesiod context */
|
|
if (HesiodContext != NULL)
|
|
{
|
|
hesiod_end(HesiodContext);
|
|
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
|
|
ni_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
if (tTd(38, 2))
|
|
sm_dprintf("ni_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
mode &= O_ACCMODE;
|
|
|
|
if (*map->map_file == '\0')
|
|
map->map_file = NETINFO_DEFAULT_DIR;
|
|
|
|
if (map->map_valcolnm == NULL)
|
|
map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
|
|
|
|
if (map->map_coldelim == '\0')
|
|
{
|
|
if (bitset(MF_ALIAS, map->map_mflags))
|
|
map->map_coldelim = ',';
|
|
else if (bitset(MF_FILECLASS, map->map_mflags))
|
|
map->map_coldelim = ' ';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
** NI_MAP_LOOKUP -- look up a datum in NetInfo
|
|
*/
|
|
|
|
char *
|
|
ni_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
char *res;
|
|
char *propval;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
|
|
|
|
propval = ni_propval(map->map_file, map->map_keycolnm, name,
|
|
map->map_valcolnm, map->map_coldelim);
|
|
|
|
if (propval == NULL)
|
|
return NULL;
|
|
|
|
SM_TRY
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
res = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
res = map_rewrite(map, propval, strlen(propval), av);
|
|
SM_FINALLY
|
|
sm_free(propval);
|
|
SM_END_TRY
|
|
return res;
|
|
}
|
|
|
|
|
|
static bool
|
|
ni_getcanonname(name, hbsize, statp)
|
|
char *name;
|
|
int hbsize;
|
|
int *statp;
|
|
{
|
|
char *vptr;
|
|
char *ptr;
|
|
char nbuf[MAXNAME + 1];
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("ni_getcanonname(%s)\n", name);
|
|
|
|
if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
(void) shorten_hostname(nbuf);
|
|
|
|
/* we only accept single token search key */
|
|
if (strchr(nbuf, '.'))
|
|
{
|
|
*statp = EX_NOHOST;
|
|
return false;
|
|
}
|
|
|
|
/* Do the search */
|
|
vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
|
|
|
|
if (vptr == NULL)
|
|
{
|
|
*statp = EX_NOHOST;
|
|
return false;
|
|
}
|
|
|
|
/* Only want the first machine name */
|
|
if ((ptr = strchr(vptr, '\n')) != NULL)
|
|
*ptr = '\0';
|
|
|
|
if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
|
|
{
|
|
sm_free(vptr);
|
|
*statp = EX_UNAVAILABLE;
|
|
return true;
|
|
}
|
|
sm_free(vptr);
|
|
*statp = EX_OK;
|
|
return false;
|
|
}
|
|
#endif /* NETINFO */
|
|
/*
|
|
** TEXT (unindexed text file) Modules
|
|
**
|
|
** This code donated by Sun Microsystems.
|
|
*/
|
|
|
|
#define map_sff map_lockfd /* overload field */
|
|
|
|
|
|
/*
|
|
** TEXT_MAP_OPEN -- open text table
|
|
*/
|
|
|
|
bool
|
|
text_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
long sff;
|
|
int i;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("text_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
errno = EPERM;
|
|
return false;
|
|
}
|
|
|
|
if (*map->map_file == '\0')
|
|
{
|
|
syserr("text map \"%s\": file name required",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
|
|
if (map->map_file[0] != '/')
|
|
{
|
|
syserr("text map \"%s\": file name must be fully qualified",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
|
|
sff = SFF_ROOTOK|SFF_REGONLY;
|
|
if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
|
|
sff |= SFF_NOWLINK;
|
|
if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
|
|
sff |= SFF_SAFEDIRPATH;
|
|
if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
|
|
sff, S_IRUSR, NULL)) != 0)
|
|
{
|
|
int save_errno = errno;
|
|
|
|
/* cannot open this map */
|
|
if (tTd(38, 2))
|
|
sm_dprintf("\tunsafe map file: %d\n", i);
|
|
errno = save_errno;
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("text map \"%s\": unsafe map file %s",
|
|
map->map_mname, map->map_file);
|
|
return false;
|
|
}
|
|
|
|
if (map->map_keycolnm == NULL)
|
|
map->map_keycolno = 0;
|
|
else
|
|
{
|
|
if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
|
|
{
|
|
syserr("text map \"%s\", file %s: -k should specify a number, not %s",
|
|
map->map_mname, map->map_file,
|
|
map->map_keycolnm);
|
|
return false;
|
|
}
|
|
map->map_keycolno = atoi(map->map_keycolnm);
|
|
}
|
|
|
|
if (map->map_valcolnm == NULL)
|
|
map->map_valcolno = 0;
|
|
else
|
|
{
|
|
if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
|
|
{
|
|
syserr("text map \"%s\", file %s: -v should specify a number, not %s",
|
|
map->map_mname, map->map_file,
|
|
map->map_valcolnm);
|
|
return false;
|
|
}
|
|
map->map_valcolno = atoi(map->map_valcolnm);
|
|
}
|
|
|
|
if (tTd(38, 2))
|
|
{
|
|
sm_dprintf("text_map_open(%s, %s): delimiter = ",
|
|
map->map_mname, map->map_file);
|
|
if (map->map_coldelim == '\0')
|
|
sm_dprintf("(white space)\n");
|
|
else
|
|
sm_dprintf("%c\n", map->map_coldelim);
|
|
}
|
|
|
|
map->map_sff = sff;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
|
|
*/
|
|
|
|
char *
|
|
text_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
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;
|
|
long sff = map->map_sff;
|
|
char search_key[MAXNAME + 1];
|
|
char linebuf[MAXLINE];
|
|
char buf[MAXNAME + 1];
|
|
|
|
found_it = false;
|
|
if (tTd(38, 20))
|
|
sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
|
|
|
|
buflen = strlen(name);
|
|
if (buflen > sizeof(search_key) - 1)
|
|
buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
|
|
memmove(search_key, name, buflen);
|
|
search_key[buflen] = '\0';
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
makelower(search_key);
|
|
|
|
f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
|
|
if (f == NULL)
|
|
{
|
|
map->map_mflags &= ~(MF_VALID|MF_OPEN);
|
|
*statp = EX_UNAVAILABLE;
|
|
return NULL;
|
|
}
|
|
key_idx = map->map_keycolno;
|
|
delim = map->map_coldelim;
|
|
while (sm_io_fgets(f, SM_TIME_DEFAULT,
|
|
linebuf, sizeof(linebuf)) >= 0)
|
|
{
|
|
char *p;
|
|
|
|
/* skip comment line */
|
|
if (linebuf[0] == '#')
|
|
continue;
|
|
p = strchr(linebuf, '\n');
|
|
if (p != NULL)
|
|
*p = '\0';
|
|
p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
|
|
if (p != NULL && sm_strcasecmp(search_key, p) == 0)
|
|
{
|
|
found_it = true;
|
|
break;
|
|
}
|
|
}
|
|
(void) sm_io_close(f, SM_TIME_DEFAULT);
|
|
if (!found_it)
|
|
{
|
|
*statp = EX_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
|
|
if (vp == NULL)
|
|
{
|
|
*statp = EX_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
vsize = strlen(vp);
|
|
*statp = EX_OK;
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
return map_rewrite(map, vp, vsize, av);
|
|
}
|
|
|
|
/*
|
|
** TEXT_GETCANONNAME -- look up canonical name in hosts file
|
|
*/
|
|
|
|
static bool
|
|
text_getcanonname(name, hbsize, statp)
|
|
char *name;
|
|
int hbsize;
|
|
int *statp;
|
|
{
|
|
bool found;
|
|
char *dot;
|
|
SM_FILE_T *f;
|
|
char linebuf[MAXLINE];
|
|
char cbuf[MAXNAME + 1];
|
|
char nbuf[MAXNAME + 1];
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("text_getcanonname(%s)\n", name);
|
|
|
|
if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
dot = shorten_hostname(nbuf);
|
|
|
|
f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
|
|
NULL);
|
|
if (f == NULL)
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
found = false;
|
|
while (!found &&
|
|
sm_io_fgets(f, SM_TIME_DEFAULT,
|
|
linebuf, sizeof(linebuf)) >= 0)
|
|
{
|
|
char *p = strpbrk(linebuf, "#\n");
|
|
|
|
if (p != NULL)
|
|
*p = '\0';
|
|
if (linebuf[0] != '\0')
|
|
found = extract_canonname(nbuf, dot, linebuf,
|
|
cbuf, sizeof(cbuf));
|
|
}
|
|
(void) sm_io_close(f, SM_TIME_DEFAULT);
|
|
if (!found)
|
|
{
|
|
*statp = EX_NOHOST;
|
|
return false;
|
|
}
|
|
|
|
if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
|
|
{
|
|
*statp = EX_UNAVAILABLE;
|
|
return false;
|
|
}
|
|
*statp = EX_OK;
|
|
return true;
|
|
}
|
|
/*
|
|
** STAB (Symbol Table) Modules
|
|
*/
|
|
|
|
|
|
/*
|
|
** STAB_MAP_LOOKUP -- look up alias in symbol table
|
|
*/
|
|
|
|
/* ARGSUSED2 */
|
|
char *
|
|
stab_map_lookup(map, name, av, pstat)
|
|
register MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *pstat;
|
|
{
|
|
register STAB *s;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("stab_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
s = stab(name, ST_ALIAS, ST_FIND);
|
|
if (s == NULL)
|
|
return NULL;
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
|
|
}
|
|
|
|
/*
|
|
** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
|
|
*/
|
|
|
|
void
|
|
stab_map_store(map, lhs, rhs)
|
|
register MAP *map;
|
|
char *lhs;
|
|
char *rhs;
|
|
{
|
|
register STAB *s;
|
|
|
|
s = stab(lhs, ST_ALIAS, ST_ENTER);
|
|
s->s_alias = newstr(rhs);
|
|
}
|
|
|
|
|
|
/*
|
|
** STAB_MAP_OPEN -- initialize (reads data file)
|
|
**
|
|
** This is a weird 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
|
|
stab_map_open(map, mode)
|
|
register MAP *map;
|
|
int mode;
|
|
{
|
|
SM_FILE_T *af;
|
|
long sff;
|
|
struct stat st;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("stab_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
errno = EPERM;
|
|
return false;
|
|
}
|
|
|
|
sff = SFF_ROOTOK|SFF_REGONLY;
|
|
if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
|
|
sff |= SFF_NOWLINK;
|
|
if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
|
|
sff |= SFF_SAFEDIRPATH;
|
|
af = safefopen(map->map_file, O_RDONLY, 0444, sff);
|
|
if (af == NULL)
|
|
return false;
|
|
readaliases(map, af, false, false);
|
|
|
|
if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
|
|
map->map_mtime = st.st_mtime;
|
|
(void) sm_io_close(af, SM_TIME_DEFAULT);
|
|
|
|
return true;
|
|
}
|
|
/*
|
|
** Implicit Modules
|
|
**
|
|
** Tries several types. For back compatibility of aliases.
|
|
*/
|
|
|
|
|
|
/*
|
|
** IMPL_MAP_LOOKUP -- lookup in best open database
|
|
*/
|
|
|
|
char *
|
|
impl_map_lookup(map, name, av, pstat)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *pstat;
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("impl_map_lookup(%s, %s)\n",
|
|
map->map_mname, name);
|
|
|
|
#if NEWDB
|
|
if (bitset(MF_IMPL_HASH, map->map_mflags))
|
|
return db_map_lookup(map, name, av, pstat);
|
|
#endif /* NEWDB */
|
|
#if NDBM
|
|
if (bitset(MF_IMPL_NDBM, map->map_mflags))
|
|
return ndbm_map_lookup(map, name, av, pstat);
|
|
#endif /* NDBM */
|
|
return stab_map_lookup(map, name, av, pstat);
|
|
}
|
|
|
|
/*
|
|
** IMPL_MAP_STORE -- store in open databases
|
|
*/
|
|
|
|
void
|
|
impl_map_store(map, lhs, rhs)
|
|
MAP *map;
|
|
char *lhs;
|
|
char *rhs;
|
|
{
|
|
if (tTd(38, 12))
|
|
sm_dprintf("impl_map_store(%s, %s, %s)\n",
|
|
map->map_mname, lhs, rhs);
|
|
#if NEWDB
|
|
if (bitset(MF_IMPL_HASH, map->map_mflags))
|
|
db_map_store(map, lhs, rhs);
|
|
#endif /* NEWDB */
|
|
#if NDBM
|
|
if (bitset(MF_IMPL_NDBM, map->map_mflags))
|
|
ndbm_map_store(map, lhs, rhs);
|
|
#endif /* NDBM */
|
|
stab_map_store(map, lhs, rhs);
|
|
}
|
|
|
|
/*
|
|
** IMPL_MAP_OPEN -- implicit database open
|
|
*/
|
|
|
|
bool
|
|
impl_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
if (tTd(38, 2))
|
|
sm_dprintf("impl_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
#if NEWDB
|
|
map->map_mflags |= MF_IMPL_HASH;
|
|
if (hash_map_open(map, mode))
|
|
{
|
|
# ifdef NDBM_YP_COMPAT
|
|
if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
|
|
# endif /* NDBM_YP_COMPAT */
|
|
return true;
|
|
}
|
|
else
|
|
map->map_mflags &= ~MF_IMPL_HASH;
|
|
#endif /* NEWDB */
|
|
#if NDBM
|
|
map->map_mflags |= MF_IMPL_NDBM;
|
|
if (ndbm_map_open(map, mode))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
map->map_mflags &= ~MF_IMPL_NDBM;
|
|
#endif /* NDBM */
|
|
|
|
#if defined(NEWDB) || defined(NDBM)
|
|
if (Verbose)
|
|
message("WARNING: cannot open alias database %s%s",
|
|
map->map_file,
|
|
mode == O_RDONLY ? "; reading text version" : "");
|
|
#else /* defined(NEWDB) || defined(NDBM) */
|
|
if (mode != O_RDONLY)
|
|
usrerr("Cannot rebuild aliases: no database format defined");
|
|
#endif /* defined(NEWDB) || defined(NDBM) */
|
|
|
|
if (mode == O_RDONLY)
|
|
return stab_map_open(map, mode);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
** IMPL_MAP_CLOSE -- close any open database(s)
|
|
*/
|
|
|
|
void
|
|
impl_map_close(map)
|
|
MAP *map;
|
|
{
|
|
if (tTd(38, 9))
|
|
sm_dprintf("impl_map_close(%s, %s, %lx)\n",
|
|
map->map_mname, map->map_file, map->map_mflags);
|
|
#if NEWDB
|
|
if (bitset(MF_IMPL_HASH, map->map_mflags))
|
|
{
|
|
db_map_close(map);
|
|
map->map_mflags &= ~MF_IMPL_HASH;
|
|
}
|
|
#endif /* NEWDB */
|
|
|
|
#if NDBM
|
|
if (bitset(MF_IMPL_NDBM, map->map_mflags))
|
|
{
|
|
ndbm_map_close(map);
|
|
map->map_mflags &= ~MF_IMPL_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
|
|
user_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
if (tTd(38, 2))
|
|
sm_dprintf("user_map_open(%s, %d)\n",
|
|
map->map_mname, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
return false;
|
|
}
|
|
if (map->map_valcolnm == NULL)
|
|
/* EMPTY */
|
|
/* nothing */ ;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
|
|
map->map_valcolno = 1;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
|
|
map->map_valcolno = 2;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
|
|
map->map_valcolno = 3;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
|
|
map->map_valcolno = 4;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
|
|
map->map_valcolno = 5;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
|
|
map->map_valcolno = 6;
|
|
else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
|
|
map->map_valcolno = 7;
|
|
else
|
|
{
|
|
syserr("User map %s: unknown column name %s",
|
|
map->map_mname, map->map_valcolnm);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
** USER_MAP_LOOKUP -- look up a user in the passwd file.
|
|
*/
|
|
|
|
/* ARGSUSED3 */
|
|
char *
|
|
user_map_lookup(map, key, av, statp)
|
|
MAP *map;
|
|
char *key;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
auto bool fuzzy;
|
|
SM_MBDB_T user;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("user_map_lookup(%s, %s)\n",
|
|
map->map_mname, key);
|
|
|
|
*statp = finduser(key, &fuzzy, &user);
|
|
if (*statp != EX_OK)
|
|
return NULL;
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, key, strlen(key), NULL);
|
|
else
|
|
{
|
|
char *rwval = NULL;
|
|
char buf[30];
|
|
|
|
switch (map->map_valcolno)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
rwval = user.mbdb_name;
|
|
break;
|
|
|
|
case 2:
|
|
rwval = "x"; /* passwd no longer supported */
|
|
break;
|
|
|
|
case 3:
|
|
(void) sm_snprintf(buf, sizeof(buf), "%d",
|
|
(int) user.mbdb_uid);
|
|
rwval = buf;
|
|
break;
|
|
|
|
case 4:
|
|
(void) sm_snprintf(buf, sizeof(buf), "%d",
|
|
(int) user.mbdb_gid);
|
|
rwval = buf;
|
|
break;
|
|
|
|
case 5:
|
|
rwval = user.mbdb_fullname;
|
|
break;
|
|
|
|
case 6:
|
|
rwval = user.mbdb_homedir;
|
|
break;
|
|
|
|
case 7:
|
|
rwval = user.mbdb_shell;
|
|
break;
|
|
default:
|
|
syserr("user_map %s: bogus field %d",
|
|
map->map_mname, map->map_valcolno);
|
|
return NULL;
|
|
}
|
|
return map_rewrite(map, rwval, strlen(rwval), av);
|
|
}
|
|
}
|
|
/*
|
|
** 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 *
|
|
prog_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int i;
|
|
int save_errno;
|
|
int fd;
|
|
int status;
|
|
auto pid_t pid;
|
|
register char *p;
|
|
char *rval;
|
|
char *argv[MAXPV + 1];
|
|
char buf[MAXLINE];
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("prog_map_lookup(%s, %s) %s\n",
|
|
map->map_mname, name, map->map_file);
|
|
|
|
i = 0;
|
|
argv[i++] = map->map_file;
|
|
if (map->map_rebuild != NULL)
|
|
{
|
|
(void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
|
|
for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
|
|
{
|
|
if (i >= MAXPV - 1)
|
|
break;
|
|
argv[i++] = p;
|
|
}
|
|
}
|
|
argv[i++] = name;
|
|
argv[i] = NULL;
|
|
if (tTd(38, 21))
|
|
{
|
|
sm_dprintf("prog_open:");
|
|
for (i = 0; argv[i] != NULL; i++)
|
|
sm_dprintf(" %s", argv[i]);
|
|
sm_dprintf("\n");
|
|
}
|
|
(void) sm_blocksignal(SIGCHLD);
|
|
pid = prog_open(argv, &fd, CurEnv);
|
|
if (pid < 0)
|
|
{
|
|
if (!bitset(MF_OPTIONAL, map->map_mflags))
|
|
syserr("prog_map_lookup(%s) failed (%s) -- closing",
|
|
map->map_mname, sm_errstring(errno));
|
|
else if (tTd(38, 9))
|
|
sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
|
|
map->map_mname, sm_errstring(errno));
|
|
map->map_mflags &= ~(MF_VALID|MF_OPEN);
|
|
*statp = EX_OSFILE;
|
|
return NULL;
|
|
}
|
|
i = read(fd, buf, sizeof(buf) - 1);
|
|
if (i < 0)
|
|
{
|
|
syserr("prog_map_lookup(%s): read error %s",
|
|
map->map_mname, sm_errstring(errno));
|
|
rval = NULL;
|
|
}
|
|
else if (i == 0)
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("prog_map_lookup(%s): empty answer\n",
|
|
map->map_mname);
|
|
rval = NULL;
|
|
}
|
|
else
|
|
{
|
|
buf[i] = '\0';
|
|
p = strchr(buf, '\n');
|
|
if (p != NULL)
|
|
*p = '\0';
|
|
|
|
/* collect the return value */
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
rval = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
rval = map_rewrite(map, buf, strlen(buf), av);
|
|
|
|
/* now flush any additional output */
|
|
while ((i = read(fd, buf, sizeof(buf))) > 0)
|
|
continue;
|
|
}
|
|
|
|
/* wait for the process to terminate */
|
|
(void) close(fd);
|
|
status = waitfor(pid);
|
|
save_errno = errno;
|
|
(void) sm_releasesignal(SIGCHLD);
|
|
errno = save_errno;
|
|
|
|
if (status == -1)
|
|
{
|
|
syserr("prog_map_lookup(%s): wait error %s",
|
|
map->map_mname, sm_errstring(errno));
|
|
*statp = EX_SOFTWARE;
|
|
rval = NULL;
|
|
}
|
|
else if (WIFEXITED(status))
|
|
{
|
|
if ((*statp = WEXITSTATUS(status)) != EX_OK)
|
|
rval = NULL;
|
|
}
|
|
else
|
|
{
|
|
syserr("prog_map_lookup(%s): child died on signal %d",
|
|
map->map_mname, status);
|
|
*statp = EX_UNAVAILABLE;
|
|
rval = NULL;
|
|
}
|
|
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
|
|
seq_map_parse(map, ap)
|
|
MAP *map;
|
|
char *ap;
|
|
{
|
|
int maxmap;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
|
|
maxmap = 0;
|
|
while (*ap != '\0')
|
|
{
|
|
register char *p;
|
|
STAB *s;
|
|
|
|
/* find beginning of map name */
|
|
while (isascii(*ap) && isspace(*ap))
|
|
ap++;
|
|
for (p = ap;
|
|
(isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
|
|
p++)
|
|
continue;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
|
|
p++;
|
|
if (*ap == '\0')
|
|
{
|
|
ap = p;
|
|
continue;
|
|
}
|
|
s = stab(ap, ST_MAP, ST_FIND);
|
|
if (s == NULL)
|
|
{
|
|
syserr("Sequence map %s: unknown member map %s",
|
|
map->map_mname, ap);
|
|
}
|
|
else if (maxmap >= MAXMAPSTACK)
|
|
{
|
|
syserr("Sequence map %s: too many member maps (%d max)",
|
|
map->map_mname, MAXMAPSTACK);
|
|
maxmap++;
|
|
}
|
|
else if (maxmap < MAXMAPSTACK)
|
|
{
|
|
map->map_stack[maxmap++] = &s->s_map;
|
|
}
|
|
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
|
|
switch_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
int mapno;
|
|
int nmaps;
|
|
char *maptype[MAXMAPSTACK];
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("switch_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
nmaps = switch_map_find(map->map_file, maptype, map->map_return);
|
|
if (tTd(38, 19))
|
|
{
|
|
sm_dprintf("\tswitch_map_find => %d\n", nmaps);
|
|
for (mapno = 0; mapno < nmaps; mapno++)
|
|
sm_dprintf("\t\t%s\n", maptype[mapno]);
|
|
}
|
|
if (nmaps <= 0 || nmaps > MAXMAPSTACK)
|
|
return false;
|
|
|
|
for (mapno = 0; mapno < nmaps; mapno++)
|
|
{
|
|
register STAB *s;
|
|
char nbuf[MAXNAME + 1];
|
|
|
|
if (maptype[mapno] == NULL)
|
|
continue;
|
|
(void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
|
|
map->map_mname, ".", maptype[mapno]);
|
|
s = stab(nbuf, ST_MAP, ST_FIND);
|
|
if (s == NULL)
|
|
{
|
|
syserr("Switch map %s: unknown member map %s",
|
|
map->map_mname, nbuf);
|
|
}
|
|
else
|
|
{
|
|
map->map_stack[mapno] = &s->s_map;
|
|
if (tTd(38, 4))
|
|
sm_dprintf("\tmap_stack[%d] = %s:%s\n",
|
|
mapno,
|
|
s->s_map.map_class->map_cname,
|
|
nbuf);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
** SEQ_MAP_CLOSE -- close all underlying maps
|
|
*/
|
|
|
|
void
|
|
seq_map_close(map)
|
|
MAP *map;
|
|
{
|
|
int mapno;
|
|
|
|
if (tTd(38, 9))
|
|
sm_dprintf("seq_map_close(%s)\n", map->map_mname);
|
|
|
|
for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
|
|
{
|
|
MAP *mm = map->map_stack[mapno];
|
|
|
|
if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
|
|
continue;
|
|
mm->map_mflags |= MF_CLOSING;
|
|
mm->map_class->map_close(mm);
|
|
mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
|
|
}
|
|
}
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
** SEQ_MAP_LOOKUP -- sequenced map lookup
|
|
*/
|
|
|
|
char *
|
|
seq_map_lookup(map, key, args, pstat)
|
|
MAP *map;
|
|
char *key;
|
|
char **args;
|
|
int *pstat;
|
|
{
|
|
int mapno;
|
|
int mapbit = 0x01;
|
|
bool tempfail = false;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
|
|
|
|
for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
|
|
{
|
|
MAP *mm = map->map_stack[mapno];
|
|
char *rv;
|
|
|
|
if (mm == NULL)
|
|
continue;
|
|
if (!bitset(MF_OPEN, mm->map_mflags) &&
|
|
!openmap(mm))
|
|
{
|
|
if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
|
|
{
|
|
*pstat = EX_UNAVAILABLE;
|
|
return NULL;
|
|
}
|
|
continue;
|
|
}
|
|
*pstat = EX_OK;
|
|
rv = mm->map_class->map_lookup(mm, key, args, pstat);
|
|
if (rv != NULL)
|
|
return rv;
|
|
if (*pstat == EX_TEMPFAIL)
|
|
{
|
|
if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
|
|
return NULL;
|
|
tempfail = true;
|
|
}
|
|
else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
|
|
break;
|
|
}
|
|
if (tempfail)
|
|
*pstat = EX_TEMPFAIL;
|
|
else if (*pstat == EX_OK)
|
|
*pstat = EX_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** SEQ_MAP_STORE -- sequenced map store
|
|
*/
|
|
|
|
void
|
|
seq_map_store(map, key, val)
|
|
MAP *map;
|
|
char *key;
|
|
char *val;
|
|
{
|
|
int mapno;
|
|
|
|
if (tTd(38, 12))
|
|
sm_dprintf("seq_map_store(%s, %s, %s)\n",
|
|
map->map_mname, key, val);
|
|
|
|
for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
|
|
{
|
|
MAP *mm = map->map_stack[mapno];
|
|
|
|
if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
|
|
continue;
|
|
|
|
mm->map_class->map_store(mm, key, val);
|
|
return;
|
|
}
|
|
syserr("seq_map_store(%s, %s, %s): no writable map",
|
|
map->map_mname, key, val);
|
|
}
|
|
/*
|
|
** NULL stubs
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
bool
|
|
null_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
void
|
|
null_map_close(map)
|
|
MAP *map;
|
|
{
|
|
return;
|
|
}
|
|
|
|
char *
|
|
null_map_lookup(map, key, args, pstat)
|
|
MAP *map;
|
|
char *key;
|
|
char **args;
|
|
int *pstat;
|
|
{
|
|
*pstat = EX_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
void
|
|
null_map_store(map, key, val)
|
|
MAP *map;
|
|
char *key;
|
|
char *val;
|
|
{
|
|
return;
|
|
}
|
|
|
|
MAPCLASS NullMapClass =
|
|
{
|
|
"null-map", NULL, 0,
|
|
NULL, null_map_lookup, null_map_store,
|
|
null_map_open, null_map_close,
|
|
};
|
|
|
|
/*
|
|
** BOGUS stubs
|
|
*/
|
|
|
|
char *
|
|
bogus_map_lookup(map, key, args, pstat)
|
|
MAP *map;
|
|
char *key;
|
|
char **args;
|
|
int *pstat;
|
|
{
|
|
*pstat = EX_TEMPFAIL;
|
|
return NULL;
|
|
}
|
|
|
|
MAPCLASS BogusMapClass =
|
|
{
|
|
"bogus-map", NULL, 0,
|
|
NULL, bogus_map_lookup, null_map_store,
|
|
null_map_open, null_map_close,
|
|
};
|
|
/*
|
|
** MACRO modules
|
|
*/
|
|
|
|
char *
|
|
macro_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int mid;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
|
|
name == NULL ? "NULL" : name);
|
|
|
|
if (name == NULL ||
|
|
*name == '\0' ||
|
|
(mid = macid(name)) == 0)
|
|
{
|
|
*statp = EX_CONFIG;
|
|
return NULL;
|
|
}
|
|
|
|
if (av[1] == NULL)
|
|
macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
|
|
else
|
|
macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
|
|
|
|
*statp = EX_OK;
|
|
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
|
|
|
|
# define xnalloc(s) memset(xalloc(s), '\0', s);
|
|
|
|
struct regex_map
|
|
{
|
|
regex_t *regex_pattern_buf; /* xalloc it */
|
|
int *regex_subfields; /* move to type MAP */
|
|
char *regex_delim; /* move to type MAP */
|
|
};
|
|
|
|
static int parse_fields __P((char *, int *, int, int));
|
|
static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
|
|
|
|
static int
|
|
parse_fields(s, ibuf, blen, nr_substrings)
|
|
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)
|
|
{
|
|
int val = atoi(s);
|
|
|
|
if (val < 0 || val >= nr_substrings)
|
|
{
|
|
syserr("field (%d) out of range, only %d substrings in pattern",
|
|
val, nr_substrings);
|
|
return -1;
|
|
}
|
|
ibuf[i++] = val;
|
|
}
|
|
else
|
|
{
|
|
syserr("too many fields, %d max", blen);
|
|
return -1;
|
|
}
|
|
s = ++cp;
|
|
} while (!lastone);
|
|
ibuf[i] = END_OF_FIELDS;
|
|
return i;
|
|
}
|
|
|
|
bool
|
|
regex_map_init(map, ap)
|
|
MAP *map;
|
|
char *ap;
|
|
{
|
|
int regerr;
|
|
struct regex_map *map_p;
|
|
register char *p;
|
|
char *sub_param = NULL;
|
|
int pflags;
|
|
static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
|
|
map->map_mname, ap);
|
|
|
|
pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
|
|
p = ap;
|
|
map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
|
|
map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
|
|
|
|
for (;;)
|
|
{
|
|
while (isascii(*p) && isspace(*p))
|
|
p++;
|
|
if (*p != '-')
|
|
break;
|
|
switch (*++p)
|
|
{
|
|
case 'n': /* not */
|
|
map->map_mflags |= MF_REGEX_NOT;
|
|
break;
|
|
|
|
case 'f': /* case sensitive */
|
|
map->map_mflags |= MF_NOFOLDCASE;
|
|
pflags &= ~REG_ICASE;
|
|
break;
|
|
|
|
case 'b': /* basic regular expressions */
|
|
pflags &= ~REG_EXTENDED;
|
|
break;
|
|
|
|
case 's': /* substring match () syntax */
|
|
sub_param = ++p;
|
|
pflags &= ~REG_NOSUB;
|
|
break;
|
|
|
|
case 'd': /* delimiter */
|
|
map_p->regex_delim = ++p;
|
|
break;
|
|
|
|
case 'a': /* map append */
|
|
map->map_app = ++p;
|
|
break;
|
|
|
|
case 'm': /* matchonly */
|
|
map->map_mflags |= MF_MATCHONLY;
|
|
break;
|
|
|
|
case 'q':
|
|
map->map_mflags |= MF_KEEPQUOTES;
|
|
break;
|
|
|
|
case 'S':
|
|
map->map_spacesub = *++p;
|
|
break;
|
|
|
|
case 'D':
|
|
map->map_mflags |= MF_DEFER;
|
|
break;
|
|
|
|
}
|
|
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
|
|
p++;
|
|
if (*p != '\0')
|
|
*p++ = '\0';
|
|
}
|
|
if (tTd(38, 3))
|
|
sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
|
|
|
|
if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
|
|
{
|
|
/* Errorhandling */
|
|
char errbuf[ERRBUF_SIZE];
|
|
|
|
(void) regerror(regerr, map_p->regex_pattern_buf,
|
|
errbuf, sizeof(errbuf));
|
|
syserr("pattern-compile-error: %s", errbuf);
|
|
sm_free(map_p->regex_pattern_buf); /* XXX */
|
|
sm_free(map_p); /* XXX */
|
|
return false;
|
|
}
|
|
|
|
if (map->map_app != NULL)
|
|
map->map_app = newstr(map->map_app);
|
|
if (map_p->regex_delim != NULL)
|
|
map_p->regex_delim = newstr(map_p->regex_delim);
|
|
else
|
|
map_p->regex_delim = defdstr;
|
|
|
|
if (!bitset(REG_NOSUB, pflags))
|
|
{
|
|
/* substring matching */
|
|
int substrings;
|
|
int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
|
|
|
|
substrings = map_p->regex_pattern_buf->re_nsub + 1;
|
|
|
|
if (tTd(38, 3))
|
|
sm_dprintf("regex_map_init: nr of substrings %d\n",
|
|
substrings);
|
|
|
|
if (substrings >= MAX_MATCH)
|
|
{
|
|
syserr("too many substrings, %d max", MAX_MATCH);
|
|
sm_free(map_p->regex_pattern_buf); /* XXX */
|
|
sm_free(map_p); /* XXX */
|
|
return false;
|
|
}
|
|
if (sub_param != NULL && sub_param[0] != '\0')
|
|
{
|
|
/* optional parameter -sfields */
|
|
if (parse_fields(sub_param, fields,
|
|
MAX_MATCH + 1, substrings) == -1)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
/* set default fields */
|
|
for (i = 0; i < substrings; i++)
|
|
fields[i] = i;
|
|
fields[i] = END_OF_FIELDS;
|
|
}
|
|
map_p->regex_subfields = fields;
|
|
if (tTd(38, 3))
|
|
{
|
|
int *ip;
|
|
|
|
sm_dprintf("regex_map_init: subfields");
|
|
for (ip = fields; *ip != END_OF_FIELDS; ip++)
|
|
sm_dprintf(" %d", *ip);
|
|
sm_dprintf("\n");
|
|
}
|
|
}
|
|
map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
|
|
return true;
|
|
}
|
|
|
|
static char *
|
|
regex_map_rewrite(map, s, slen, av)
|
|
MAP *map;
|
|
const char *s;
|
|
size_t slen;
|
|
char **av;
|
|
{
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
return map_rewrite(map, av[0], strlen(av[0]), NULL);
|
|
else
|
|
return map_rewrite(map, s, slen, av);
|
|
}
|
|
|
|
char *
|
|
regex_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int reg_res;
|
|
struct regex_map *map_p;
|
|
regmatch_t pmatch[MAX_MATCH];
|
|
|
|
if (tTd(38, 20))
|
|
{
|
|
char **cpp;
|
|
|
|
sm_dprintf("regex_map_lookup: key '%s'\n", name);
|
|
for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
|
|
sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
|
|
}
|
|
|
|
map_p = (struct regex_map *)(map->map_db1);
|
|
reg_res = regexec(map_p->regex_pattern_buf,
|
|
name, MAX_MATCH, pmatch, 0);
|
|
|
|
if (bitset(MF_REGEX_NOT, map->map_mflags))
|
|
{
|
|
/* option -n */
|
|
if (reg_res == REG_NOMATCH)
|
|
return regex_map_rewrite(map, "", (size_t) 0, av);
|
|
else
|
|
return NULL;
|
|
}
|
|
if (reg_res == REG_NOMATCH)
|
|
return NULL;
|
|
|
|
if (map_p->regex_subfields != NULL)
|
|
{
|
|
/* option -s */
|
|
static char retbuf[MAXNAME];
|
|
int fields[MAX_MATCH + 1];
|
|
bool first = true;
|
|
int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
|
|
bool quotemode = false, bslashmode = false;
|
|
register char *dp, *sp;
|
|
char *endp, *ldp;
|
|
int *ip;
|
|
|
|
dp = retbuf;
|
|
ldp = retbuf + sizeof(retbuf) - 1;
|
|
|
|
if (av[1] != NULL)
|
|
{
|
|
if (parse_fields(av[1], fields, MAX_MATCH + 1,
|
|
(int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
|
|
{
|
|
*statp = EX_CONFIG;
|
|
return NULL;
|
|
}
|
|
ip = fields;
|
|
}
|
|
else
|
|
ip = map_p->regex_subfields;
|
|
|
|
for ( ; *ip != END_OF_FIELDS; ip++)
|
|
{
|
|
if (!first)
|
|
{
|
|
for (sp = map_p->regex_delim; *sp; sp++)
|
|
{
|
|
if (dp < ldp)
|
|
*dp++ = *sp;
|
|
}
|
|
}
|
|
else
|
|
first = false;
|
|
|
|
if (*ip >= MAX_MATCH ||
|
|
pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
|
|
continue;
|
|
|
|
sp = name + pmatch[*ip].rm_so;
|
|
endp = name + pmatch[*ip].rm_eo;
|
|
for (; endp > sp; sp++)
|
|
{
|
|
if (dp < ldp)
|
|
{
|
|
if (bslashmode)
|
|
{
|
|
*dp++ = *sp;
|
|
bslashmode = false;
|
|
}
|
|
else if (quotemode && *sp != '"' &&
|
|
*sp != '\\')
|
|
{
|
|
*dp++ = *sp;
|
|
}
|
|
else switch (*dp++ = *sp)
|
|
{
|
|
case '\\':
|
|
bslashmode = true;
|
|
break;
|
|
|
|
case '(':
|
|
cmntcnt++;
|
|
break;
|
|
|
|
case ')':
|
|
cmntcnt--;
|
|
break;
|
|
|
|
case '<':
|
|
anglecnt++;
|
|
break;
|
|
|
|
case '>':
|
|
anglecnt--;
|
|
break;
|
|
|
|
case ' ':
|
|
spacecnt++;
|
|
break;
|
|
|
|
case '"':
|
|
quotemode = !quotemode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
|
|
bslashmode || spacecnt != 0)
|
|
{
|
|
sm_syslog(LOG_WARNING, NOQID,
|
|
"Warning: regex may cause prescan() failure map=%s lookup=%s",
|
|
map->map_mname, name);
|
|
return NULL;
|
|
}
|
|
|
|
*dp = '\0';
|
|
|
|
return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
|
|
}
|
|
return regex_map_rewrite(map, "", (size_t)0, av);
|
|
}
|
|
#endif /* MAP_REGEX */
|
|
/*
|
|
** NSD modules
|
|
*/
|
|
#if MAP_NSD
|
|
|
|
# include <ndbm.h>
|
|
# define _DATUM_DEFINED
|
|
# include <ns_api.h>
|
|
|
|
typedef struct ns_map_list
|
|
{
|
|
ns_map_t *map; /* XXX ns_ ? */
|
|
char *mapname;
|
|
struct ns_map_list *next;
|
|
} ns_map_list_t;
|
|
|
|
static ns_map_t *
|
|
ns_map_t_find(mapname)
|
|
char *mapname;
|
|
{
|
|
static ns_map_list_t *ns_maps = NULL;
|
|
ns_map_list_t *ns_map;
|
|
|
|
/* walk the list of maps looking for the correctly named map */
|
|
for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
|
|
{
|
|
if (strcmp(ns_map->mapname, mapname) == 0)
|
|
break;
|
|
}
|
|
|
|
/* if we are looking at a NULL ns_map_list_t, then create a new one */
|
|
if (ns_map == NULL)
|
|
{
|
|
ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
|
|
ns_map->mapname = newstr(mapname);
|
|
ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
|
|
memset(ns_map->map, '\0', sizeof(*ns_map->map));
|
|
ns_map->next = ns_maps;
|
|
ns_maps = ns_map;
|
|
}
|
|
return ns_map->map;
|
|
}
|
|
|
|
char *
|
|
nsd_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int buflen, r;
|
|
char *p;
|
|
ns_map_t *ns_map;
|
|
char keybuf[MAXNAME + 1];
|
|
char buf[MAXLINE];
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
|
|
|
|
buflen = strlen(name);
|
|
if (buflen > sizeof(keybuf) - 1)
|
|
buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
|
|
memmove(keybuf, name, buflen);
|
|
keybuf[buflen] = '\0';
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
makelower(keybuf);
|
|
|
|
ns_map = ns_map_t_find(map->map_file);
|
|
if (ns_map == NULL)
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("nsd_map_t_find failed\n");
|
|
*statp = EX_UNAVAILABLE;
|
|
return NULL;
|
|
}
|
|
r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
|
|
buf, sizeof(buf));
|
|
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 */
|
|
)
|
|
{
|
|
*statp = EX_CONFIG;
|
|
return NULL;
|
|
}
|
|
if (r != NS_SUCCESS)
|
|
{
|
|
*statp = EX_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
|
|
*statp = EX_OK;
|
|
|
|
/* Null out trailing \n */
|
|
if ((p = strchr(buf, '\n')) != NULL)
|
|
*p = '\0';
|
|
|
|
return map_rewrite(map, buf, strlen(buf), av);
|
|
}
|
|
#endif /* MAP_NSD */
|
|
|
|
char *
|
|
arith_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
long r;
|
|
long v[2];
|
|
bool res = false;
|
|
bool boolres;
|
|
static char result[16];
|
|
char **cpp;
|
|
|
|
if (tTd(38, 2))
|
|
{
|
|
sm_dprintf("arith_map_lookup: key '%s'\n", name);
|
|
for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
|
|
sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
|
|
}
|
|
r = 0;
|
|
boolres = false;
|
|
cpp = av;
|
|
*statp = EX_OK;
|
|
|
|
/*
|
|
** read arguments for arith map
|
|
** - no check is made whether they are really numbers
|
|
** - just ignores args after the second
|
|
*/
|
|
|
|
for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
|
|
v[r++] = strtol(*cpp, NULL, 0);
|
|
|
|
/* operator and (at least) two operands given? */
|
|
if (name != NULL && r == 2)
|
|
{
|
|
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;
|
|
|
|
case 'r':
|
|
r = v[1] - v[0] + 1;
|
|
if (r <= 0)
|
|
return NULL;
|
|
r = get_random() % r + v[0];
|
|
break;
|
|
|
|
default:
|
|
/* XXX */
|
|
*statp = EX_CONFIG;
|
|
if (LogLevel > 10)
|
|
sm_syslog(LOG_WARNING, NOQID,
|
|
"arith_map: unknown operator %c",
|
|
(isascii(*name) && isprint(*name)) ?
|
|
*name : '?');
|
|
return NULL;
|
|
}
|
|
if (boolres)
|
|
(void) sm_snprintf(result, sizeof(result),
|
|
res ? "TRUE" : "FALSE");
|
|
else
|
|
(void) sm_snprintf(result, sizeof(result), "%ld", r);
|
|
return result;
|
|
}
|
|
*statp = EX_CONFIG;
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
arpa_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
int r;
|
|
char *rval;
|
|
char result[128]; /* IPv6: 64 + 10 + 1 would be enough */
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("arpa_map_lookup: key '%s'\n", name);
|
|
*statp = EX_DATAERR;
|
|
r = 1;
|
|
memset(result, '\0', sizeof(result));
|
|
rval = NULL;
|
|
|
|
# if NETINET6
|
|
if (sm_strncasecmp(name, "IPv6:", 5) == 0)
|
|
{
|
|
struct in6_addr in6_addr;
|
|
|
|
r = anynet_pton(AF_INET6, name, &in6_addr);
|
|
if (r == 1)
|
|
{
|
|
static char hex_digits[] =
|
|
{ '0', '1', '2', '3', '4', '5', '6', '7', '8',
|
|
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
|
|
unsigned char *src;
|
|
char *dst;
|
|
int i;
|
|
|
|
src = (unsigned char *) &in6_addr;
|
|
dst = result;
|
|
for (i = 15; i >= 0; i--) {
|
|
*dst++ = hex_digits[src[i] & 0x0f];
|
|
*dst++ = '.';
|
|
*dst++ = hex_digits[(src[i] >> 4) & 0x0f];
|
|
if (i > 0)
|
|
*dst++ = '.';
|
|
}
|
|
*statp = EX_OK;
|
|
}
|
|
}
|
|
else
|
|
# endif /* NETINET6 */
|
|
# if NETINET
|
|
{
|
|
struct in_addr in_addr;
|
|
|
|
r = inet_pton(AF_INET, name, &in_addr);
|
|
if (r == 1)
|
|
{
|
|
unsigned char *src;
|
|
|
|
src = (unsigned char *) &in_addr;
|
|
(void) snprintf(result, sizeof(result),
|
|
"%u.%u.%u.%u",
|
|
src[3], src[2], src[1], src[0]);
|
|
*statp = EX_OK;
|
|
}
|
|
}
|
|
# endif /* NETINET */
|
|
if (r < 0)
|
|
*statp = EX_UNAVAILABLE;
|
|
if (tTd(38, 2))
|
|
sm_dprintf("arpa_map_lookup: r=%d, result='%s'\n", r, result);
|
|
if (*statp == EX_OK)
|
|
{
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
rval = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
rval = map_rewrite(map, result, strlen(result), av);
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
#if SOCKETMAP
|
|
|
|
# if NETINET || NETINET6
|
|
# include <arpa/inet.h>
|
|
# endif /* NETINET || NETINET6 */
|
|
|
|
# define socket_map_next map_stack[0]
|
|
|
|
/*
|
|
** SOCKET_MAP_OPEN -- open socket table
|
|
*/
|
|
|
|
bool
|
|
socket_map_open(map, mode)
|
|
MAP *map;
|
|
int mode;
|
|
{
|
|
STAB *s;
|
|
int sock = 0;
|
|
int tmo;
|
|
SOCKADDR_LEN_T addrlen = 0;
|
|
int addrno = 0;
|
|
int save_errno;
|
|
char *p;
|
|
char *colon;
|
|
char *at;
|
|
struct hostent *hp = NULL;
|
|
SOCKADDR addr;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("socket_map_open(%s, %s, %d)\n",
|
|
map->map_mname, map->map_file, mode);
|
|
|
|
mode &= O_ACCMODE;
|
|
|
|
/* sendmail doesn't have the ability to write to SOCKET (yet) */
|
|
if (mode != O_RDONLY)
|
|
{
|
|
/* issue a pseudo-error message */
|
|
errno = SM_EMAPCANTWRITE;
|
|
return false;
|
|
}
|
|
|
|
if (*map->map_file == '\0')
|
|
{
|
|
syserr("socket map \"%s\": empty or missing socket information",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
|
|
s = socket_map_findconn(map->map_file);
|
|
if (s->s_socketmap != NULL)
|
|
{
|
|
/* Copy open connection */
|
|
map->map_db1 = s->s_socketmap->map_db1;
|
|
|
|
/* Add this map as head of linked list */
|
|
map->socket_map_next = s->s_socketmap;
|
|
s->s_socketmap = map;
|
|
|
|
if (tTd(38, 2))
|
|
sm_dprintf("using cached connection\n");
|
|
return true;
|
|
}
|
|
|
|
if (tTd(38, 2))
|
|
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 */
|
|
memset(&addr, '\0', sizeof(addr));
|
|
p = map->map_file;
|
|
colon = strchr(p, ':');
|
|
if (colon != NULL)
|
|
{
|
|
*colon = '\0';
|
|
|
|
if (*p == '\0')
|
|
{
|
|
# if NETUNIX
|
|
/* default to AF_UNIX */
|
|
addr.sa.sa_family = AF_UNIX;
|
|
# else /* NETUNIX */
|
|
# if NETINET
|
|
/* default to AF_INET */
|
|
addr.sa.sa_family = AF_INET;
|
|
# else /* NETINET */
|
|
# if NETINET6
|
|
/* default to AF_INET6 */
|
|
addr.sa.sa_family = AF_INET6;
|
|
# else /* NETINET6 */
|
|
/* no protocols available */
|
|
syserr("socket map \"%s\": no valid socket protocols available",
|
|
map->map_mname);
|
|
return false;
|
|
# endif /* NETINET6 */
|
|
# endif /* NETINET */
|
|
# endif /* NETUNIX */
|
|
}
|
|
# if NETUNIX
|
|
else if (sm_strcasecmp(p, "unix") == 0 ||
|
|
sm_strcasecmp(p, "local") == 0)
|
|
addr.sa.sa_family = AF_UNIX;
|
|
# endif /* NETUNIX */
|
|
# if NETINET
|
|
else if (sm_strcasecmp(p, "inet") == 0)
|
|
addr.sa.sa_family = AF_INET;
|
|
# endif /* NETINET */
|
|
# if NETINET6
|
|
else if (sm_strcasecmp(p, "inet6") == 0)
|
|
addr.sa.sa_family = AF_INET6;
|
|
# endif /* NETINET6 */
|
|
else
|
|
{
|
|
# ifdef EPROTONOSUPPORT
|
|
errno = EPROTONOSUPPORT;
|
|
# else /* EPROTONOSUPPORT */
|
|
errno = EINVAL;
|
|
# endif /* EPROTONOSUPPORT */
|
|
syserr("socket map \"%s\": unknown socket type %s",
|
|
map->map_mname, p);
|
|
return false;
|
|
}
|
|
*colon++ = ':';
|
|
}
|
|
else
|
|
{
|
|
colon = p;
|
|
#if NETUNIX
|
|
/* default to AF_UNIX */
|
|
addr.sa.sa_family = AF_UNIX;
|
|
#else /* NETUNIX */
|
|
# if NETINET
|
|
/* default to AF_INET */
|
|
addr.sa.sa_family = AF_INET;
|
|
# else /* NETINET */
|
|
# if NETINET6
|
|
/* default to AF_INET6 */
|
|
addr.sa.sa_family = AF_INET6;
|
|
# else /* NETINET6 */
|
|
syserr("socket map \"%s\": unknown socket type %s",
|
|
map->map_mname, p);
|
|
return false;
|
|
# endif /* NETINET6 */
|
|
# endif /* NETINET */
|
|
#endif /* NETUNIX */
|
|
}
|
|
|
|
# if NETUNIX
|
|
if (addr.sa.sa_family == AF_UNIX)
|
|
{
|
|
long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
|
|
|
|
at = colon;
|
|
if (strlen(colon) >= sizeof(addr.sunix.sun_path))
|
|
{
|
|
syserr("socket map \"%s\": local socket name %s too long",
|
|
map->map_mname, colon);
|
|
return false;
|
|
}
|
|
errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
|
|
S_IRUSR|S_IWUSR, NULL);
|
|
|
|
if (errno != 0)
|
|
{
|
|
/* if not safe, don't create */
|
|
syserr("socket map \"%s\": local socket name %s unsafe",
|
|
map->map_mname, colon);
|
|
return false;
|
|
}
|
|
|
|
(void) sm_strlcpy(addr.sunix.sun_path, colon,
|
|
sizeof(addr.sunix.sun_path));
|
|
addrlen = sizeof(struct sockaddr_un);
|
|
}
|
|
else
|
|
# endif /* NETUNIX */
|
|
# if NETINET || NETINET6
|
|
if (false
|
|
# if NETINET
|
|
|| addr.sa.sa_family == AF_INET
|
|
# endif /* NETINET */
|
|
# if NETINET6
|
|
|| addr.sa.sa_family == AF_INET6
|
|
# endif /* NETINET6 */
|
|
)
|
|
{
|
|
unsigned short port;
|
|
|
|
/* Parse port@host */
|
|
at = strchr(colon, '@');
|
|
if (at == NULL)
|
|
{
|
|
syserr("socket map \"%s\": bad address %s (expected port@host)",
|
|
map->map_mname, colon);
|
|
return false;
|
|
}
|
|
*at = '\0';
|
|
if (isascii(*colon) && isdigit(*colon))
|
|
port = htons((unsigned short) atoi(colon));
|
|
else
|
|
{
|
|
# ifdef NO_GETSERVBYNAME
|
|
syserr("socket map \"%s\": invalid port number %s",
|
|
map->map_mname, colon);
|
|
return false;
|
|
# else /* NO_GETSERVBYNAME */
|
|
register struct servent *sp;
|
|
|
|
sp = getservbyname(colon, "tcp");
|
|
if (sp == NULL)
|
|
{
|
|
syserr("socket map \"%s\": unknown port name %s",
|
|
map->map_mname, colon);
|
|
return false;
|
|
}
|
|
port = sp->s_port;
|
|
# endif /* NO_GETSERVBYNAME */
|
|
}
|
|
*at++ = '@';
|
|
if (*at == '[')
|
|
{
|
|
char *end;
|
|
|
|
end = strchr(at, ']');
|
|
if (end != NULL)
|
|
{
|
|
bool found = false;
|
|
# if NETINET
|
|
unsigned long hid = INADDR_NONE;
|
|
# endif /* NETINET */
|
|
# if NETINET6
|
|
struct sockaddr_in6 hid6;
|
|
# endif /* NETINET6 */
|
|
|
|
*end = '\0';
|
|
# if NETINET
|
|
if (addr.sa.sa_family == AF_INET &&
|
|
(hid = inet_addr(&at[1])) != INADDR_NONE)
|
|
{
|
|
addr.sin.sin_addr.s_addr = hid;
|
|
addr.sin.sin_port = port;
|
|
found = true;
|
|
}
|
|
# endif /* NETINET */
|
|
# if NETINET6
|
|
(void) memset(&hid6, '\0', sizeof(hid6));
|
|
if (addr.sa.sa_family == AF_INET6 &&
|
|
anynet_pton(AF_INET6, &at[1],
|
|
&hid6.sin6_addr) == 1)
|
|
{
|
|
addr.sin6.sin6_addr = hid6.sin6_addr;
|
|
addr.sin6.sin6_port = port;
|
|
found = true;
|
|
}
|
|
# endif /* NETINET6 */
|
|
*end = ']';
|
|
if (!found)
|
|
{
|
|
syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
|
|
map->map_mname, at);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
|
|
map->map_mname, at);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hp = sm_gethostbyname(at, addr.sa.sa_family);
|
|
if (hp == NULL)
|
|
{
|
|
syserr("socket map \"%s\": Unknown host name %s",
|
|
map->map_mname, at);
|
|
return false;
|
|
}
|
|
addr.sa.sa_family = hp->h_addrtype;
|
|
switch (hp->h_addrtype)
|
|
{
|
|
# if NETINET
|
|
case AF_INET:
|
|
memmove(&addr.sin.sin_addr,
|
|
hp->h_addr, INADDRSZ);
|
|
addr.sin.sin_port = port;
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
addrno = 1;
|
|
break;
|
|
# endif /* NETINET */
|
|
|
|
# if NETINET6
|
|
case AF_INET6:
|
|
memmove(&addr.sin6.sin6_addr,
|
|
hp->h_addr, IN6ADDRSZ);
|
|
addr.sin6.sin6_port = port;
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
addrno = 1;
|
|
break;
|
|
# endif /* NETINET6 */
|
|
|
|
default:
|
|
syserr("socket map \"%s\": Unknown protocol for %s (%d)",
|
|
map->map_mname, at, hp->h_addrtype);
|
|
# if NETINET6
|
|
freehostent(hp);
|
|
# endif /* NETINET6 */
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
# endif /* NETINET || NETINET6 */
|
|
{
|
|
syserr("socket map \"%s\": unknown socket protocol",
|
|
map->map_mname);
|
|
return false;
|
|
}
|
|
|
|
/* nope, actually connecting */
|
|
for (;;)
|
|
{
|
|
sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
save_errno = errno;
|
|
if (tTd(38, 5))
|
|
sm_dprintf("socket map \"%s\": error creating socket: %s\n",
|
|
map->map_mname,
|
|
sm_errstring(save_errno));
|
|
# if NETINET6
|
|
if (hp != NULL)
|
|
freehostent(hp);
|
|
# endif /* NETINET6 */
|
|
return false;
|
|
}
|
|
|
|
if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
|
|
break;
|
|
|
|
/* couldn't connect.... try next address */
|
|
save_errno = errno;
|
|
p = CurHostName;
|
|
CurHostName = at;
|
|
if (tTd(38, 5))
|
|
sm_dprintf("socket_open (%s): open %s failed: %s\n",
|
|
map->map_mname, at, sm_errstring(save_errno));
|
|
CurHostName = p;
|
|
(void) close(sock);
|
|
|
|
/* try next address */
|
|
if (hp != NULL && hp->h_addr_list[addrno] != NULL)
|
|
{
|
|
switch (addr.sa.sa_family)
|
|
{
|
|
# if NETINET
|
|
case AF_INET:
|
|
memmove(&addr.sin.sin_addr,
|
|
hp->h_addr_list[addrno++],
|
|
INADDRSZ);
|
|
break;
|
|
# endif /* NETINET */
|
|
|
|
# if NETINET6
|
|
case AF_INET6:
|
|
memmove(&addr.sin6.sin6_addr,
|
|
hp->h_addr_list[addrno++],
|
|
IN6ADDRSZ);
|
|
break;
|
|
# endif /* NETINET6 */
|
|
|
|
default:
|
|
if (tTd(38, 5))
|
|
sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
|
|
map->map_mname, at,
|
|
hp->h_addrtype);
|
|
# if NETINET6
|
|
freehostent(hp);
|
|
# endif /* NETINET6 */
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
p = CurHostName;
|
|
CurHostName = at;
|
|
if (tTd(38, 5))
|
|
sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
|
|
map->map_mname, sm_errstring(save_errno));
|
|
CurHostName = p;
|
|
# if NETINET6
|
|
if (hp != NULL)
|
|
freehostent(hp);
|
|
# endif /* NETINET6 */
|
|
return false;
|
|
}
|
|
# if NETINET6
|
|
if (hp != NULL)
|
|
{
|
|
freehostent(hp);
|
|
hp = NULL;
|
|
}
|
|
# endif /* NETINET6 */
|
|
if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
|
|
SM_TIME_DEFAULT,
|
|
(void *) &sock,
|
|
SM_IO_RDWR,
|
|
NULL)) == NULL)
|
|
{
|
|
close(sock);
|
|
if (tTd(38, 2))
|
|
sm_dprintf("socket_open (%s): failed to create stream: %s\n",
|
|
map->map_mname, sm_errstring(errno));
|
|
return false;
|
|
}
|
|
|
|
tmo = map->map_timeout;
|
|
if (tmo == 0)
|
|
tmo = 30000; /* default: 30s */
|
|
else
|
|
tmo *= 1000; /* s -> ms */
|
|
sm_io_setinfo(map->map_db1, SM_IO_WHAT_TIMEOUT, &tmo);
|
|
|
|
/* 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 *
|
|
socket_map_findconn(conn)
|
|
const char *conn;
|
|
{
|
|
char *nbuf;
|
|
STAB *SM_NONVOLATILE s = NULL;
|
|
|
|
nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
|
|
SM_TRY
|
|
s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
|
|
SM_FINALLY
|
|
sm_free(nbuf);
|
|
SM_END_TRY
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
** SOCKET_MAP_CLOSE -- close the socket
|
|
*/
|
|
|
|
void
|
|
socket_map_close(map)
|
|
MAP *map;
|
|
{
|
|
STAB *s;
|
|
MAP *smap;
|
|
|
|
if (tTd(38, 20))
|
|
sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
|
|
(long) CurrentPid);
|
|
|
|
/* Check if already closed */
|
|
if (map->map_db1 == NULL)
|
|
{
|
|
if (tTd(38, 20))
|
|
sm_dprintf("socket_map_close(%s) already closed\n",
|
|
map->map_file);
|
|
return;
|
|
}
|
|
sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
|
|
|
|
/* Mark all the maps that share the connection as closed */
|
|
s = socket_map_findconn(map->map_file);
|
|
smap = s->s_socketmap;
|
|
while (smap != NULL)
|
|
{
|
|
MAP *next;
|
|
|
|
if (tTd(38, 2) && smap != map)
|
|
sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
|
|
map->map_mname, smap->map_mname);
|
|
|
|
smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
|
|
smap->map_db1 = NULL;
|
|
next = smap->socket_map_next;
|
|
smap->socket_map_next = NULL;
|
|
smap = next;
|
|
}
|
|
s->s_socketmap = NULL;
|
|
}
|
|
|
|
/*
|
|
** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
|
|
*/
|
|
|
|
char *
|
|
socket_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
unsigned int nettolen, replylen, recvlen;
|
|
char *replybuf, *rval, *value, *status, *key;
|
|
SM_FILE_T *f;
|
|
char keybuf[MAXNAME + 1];
|
|
|
|
replybuf = NULL;
|
|
rval = NULL;
|
|
f = (SM_FILE_T *)map->map_db1;
|
|
if (tTd(38, 20))
|
|
sm_dprintf("socket_map_lookup(%s, %s) %s\n",
|
|
map->map_mname, name, map->map_file);
|
|
|
|
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
{
|
|
nettolen = strlen(name);
|
|
if (nettolen > sizeof(keybuf) - 1)
|
|
nettolen = sizeof(keybuf) - 1;
|
|
memmove(keybuf, name, nettolen);
|
|
keybuf[nettolen] = '\0';
|
|
makelower(keybuf);
|
|
key = keybuf;
|
|
}
|
|
else
|
|
key = name;
|
|
|
|
nettolen = strlen(map->map_mname) + 1 + strlen(key);
|
|
SM_ASSERT(nettolen > strlen(map->map_mname));
|
|
SM_ASSERT(nettolen > strlen(key));
|
|
if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
|
|
nettolen, map->map_mname, key) == SM_IO_EOF) ||
|
|
(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",
|
|
map->map_mname);
|
|
*statp = EX_TEMPFAIL;
|
|
goto errcl;
|
|
}
|
|
|
|
if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
|
|
{
|
|
if (errno == EAGAIN)
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): read timeout",
|
|
map->map_mname);
|
|
}
|
|
else
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply %d",
|
|
map->map_mname, errno);
|
|
}
|
|
*statp = EX_TEMPFAIL;
|
|
goto errcl;
|
|
}
|
|
if (replylen > SOCKETMAP_MAXL)
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
|
|
map->map_mname, replylen);
|
|
*statp = EX_TEMPFAIL;
|
|
goto errcl;
|
|
}
|
|
if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
|
|
map->map_mname);
|
|
*statp = EX_TEMPFAIL;
|
|
goto error;
|
|
}
|
|
|
|
replybuf = (char *) sm_malloc(replylen + 1);
|
|
if (replybuf == NULL)
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
|
|
map->map_mname, replylen + 1);
|
|
*statp = EX_OSERR;
|
|
goto error;
|
|
}
|
|
|
|
recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
|
|
if (recvlen < replylen)
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
|
|
map->map_mname, recvlen, replylen);
|
|
*statp = EX_TEMPFAIL;
|
|
goto errcl;
|
|
}
|
|
if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
|
|
{
|
|
syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
|
|
map->map_mname);
|
|
*statp = EX_TEMPFAIL;
|
|
goto errcl;
|
|
}
|
|
status = replybuf;
|
|
replybuf[recvlen] = '\0';
|
|
value = strchr(replybuf, ' ');
|
|
if (value != NULL)
|
|
{
|
|
*value = '\0';
|
|
value++;
|
|
}
|
|
if (strcmp(status, "OK") == 0)
|
|
{
|
|
*statp = EX_OK;
|
|
|
|
/* collect the return value */
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
rval = map_rewrite(map, key, strlen(key), NULL);
|
|
else
|
|
rval = map_rewrite(map, value, strlen(value), av);
|
|
}
|
|
else if (strcmp(status, "NOTFOUND") == 0)
|
|
{
|
|
*statp = EX_NOTFOUND;
|
|
if (tTd(38, 20))
|
|
sm_dprintf("socket_map_lookup(%s): %s not found\n",
|
|
map->map_mname, key);
|
|
}
|
|
else
|
|
{
|
|
if (tTd(38, 5))
|
|
sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
|
|
map->map_mname, key, status,
|
|
value ? value : "");
|
|
if ((strcmp(status, "TEMP") == 0) ||
|
|
(strcmp(status, "TIMEOUT") == 0))
|
|
*statp = EX_TEMPFAIL;
|
|
else if(strcmp(status, "PERM") == 0)
|
|
*statp = EX_UNAVAILABLE;
|
|
else
|
|
*statp = EX_PROTOCOL;
|
|
}
|
|
|
|
if (replybuf != NULL)
|
|
sm_free(replybuf);
|
|
return rval;
|
|
|
|
errcl:
|
|
socket_map_close(map);
|
|
error:
|
|
if (replybuf != NULL)
|
|
sm_free(replybuf);
|
|
return rval;
|
|
}
|
|
#endif /* SOCKETMAP */
|