mirror of
https://github.com/opnsense/src.git
synced 2026-04-25 08:07:28 -04:00
1312 lines
29 KiB
C
1312 lines
29 KiB
C
/*
|
|
* Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
|
|
* All rights reserved.
|
|
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
|
|
* Copyright (c) 1988, 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>
|
|
|
|
#if USERDB
|
|
SM_RCSID("@(#)$Id: udb.c,v 8.153.4.4 2002/12/03 17:57:41 gshapiro Exp $ (with USERDB)")
|
|
#else /* USERDB */
|
|
SM_RCSID("@(#)$Id: udb.c,v 8.153.4.4 2002/12/03 17:57:41 gshapiro Exp $ (without USERDB)")
|
|
#endif /* USERDB */
|
|
|
|
#if USERDB
|
|
|
|
# if NEWDB
|
|
# include "sm/bdb.h"
|
|
# else /* NEWDB */
|
|
# define DBT struct _data_base_thang_
|
|
DBT
|
|
{
|
|
void *data; /* pointer to data */
|
|
size_t size; /* length of data */
|
|
};
|
|
# endif /* NEWDB */
|
|
|
|
/*
|
|
** UDB.C -- interface between sendmail and Berkeley User Data Base.
|
|
**
|
|
** This depends on the 4.4BSD db package.
|
|
*/
|
|
|
|
|
|
struct udbent
|
|
{
|
|
char *udb_spec; /* string version of spec */
|
|
int udb_type; /* type of entry */
|
|
pid_t udb_pid; /* PID of process which opened db */
|
|
char *udb_default; /* default host for outgoing mail */
|
|
union
|
|
{
|
|
# if NETINET || NETINET6
|
|
/* type UE_REMOTE -- do remote call for lookup */
|
|
struct
|
|
{
|
|
SOCKADDR _udb_addr; /* address */
|
|
int _udb_timeout; /* timeout */
|
|
} udb_remote;
|
|
# define udb_addr udb_u.udb_remote._udb_addr
|
|
# define udb_timeout udb_u.udb_remote._udb_timeout
|
|
# endif /* NETINET || NETINET6 */
|
|
|
|
/* type UE_FORWARD -- forward message to remote */
|
|
struct
|
|
{
|
|
char *_udb_fwdhost; /* name of forward host */
|
|
} udb_forward;
|
|
# define udb_fwdhost udb_u.udb_forward._udb_fwdhost
|
|
|
|
# if NEWDB
|
|
/* type UE_FETCH -- lookup in local database */
|
|
struct
|
|
{
|
|
char *_udb_dbname; /* pathname of database */
|
|
DB *_udb_dbp; /* open database ptr */
|
|
} udb_lookup;
|
|
# define udb_dbname udb_u.udb_lookup._udb_dbname
|
|
# define udb_dbp udb_u.udb_lookup._udb_dbp
|
|
# endif /* NEWDB */
|
|
} udb_u;
|
|
};
|
|
|
|
# define UDB_EOLIST 0 /* end of list */
|
|
# define UDB_SKIP 1 /* skip this entry */
|
|
# define UDB_REMOTE 2 /* look up in remote database */
|
|
# define UDB_DBFETCH 3 /* look up in local database */
|
|
# define UDB_FORWARD 4 /* forward to remote host */
|
|
# define UDB_HESIOD 5 /* look up via hesiod */
|
|
|
|
# define MAXUDBENT 10 /* maximum number of UDB entries */
|
|
|
|
|
|
struct udb_option
|
|
{
|
|
char *udbo_name;
|
|
char *udbo_val;
|
|
};
|
|
|
|
# if HESIOD
|
|
static int hes_udb_get __P((DBT *, DBT *));
|
|
# endif /* HESIOD */
|
|
static char *udbmatch __P((char *, char *, SM_RPOOL_T *));
|
|
static int _udbx_init __P((ENVELOPE *));
|
|
static int _udb_parsespec __P((char *, struct udb_option [], int));
|
|
|
|
/*
|
|
** UDBEXPAND -- look up user in database and expand
|
|
**
|
|
** Parameters:
|
|
** a -- address to expand.
|
|
** sendq -- pointer to head of sendq to put the expansions in.
|
|
** aliaslevel -- the current alias nesting depth.
|
|
** e -- the current envelope.
|
|
**
|
|
** Returns:
|
|
** EX_TEMPFAIL -- if something "odd" happened -- probably due
|
|
** to accessing a file on an NFS server that is down.
|
|
** EX_OK -- otherwise.
|
|
**
|
|
** Side Effects:
|
|
** Modifies sendq.
|
|
*/
|
|
|
|
static struct udbent UdbEnts[MAXUDBENT + 1];
|
|
static bool UdbInitialized = false;
|
|
|
|
int
|
|
udbexpand(a, sendq, aliaslevel, e)
|
|
register ADDRESS *a;
|
|
ADDRESS **sendq;
|
|
int aliaslevel;
|
|
register ENVELOPE *e;
|
|
{
|
|
int i;
|
|
DBT key;
|
|
DBT info;
|
|
bool breakout;
|
|
register struct udbent *up;
|
|
int keylen;
|
|
int naddrs;
|
|
char *user;
|
|
char keybuf[MAXKEY];
|
|
|
|
memset(&key, '\0', sizeof key);
|
|
memset(&info, '\0', sizeof info);
|
|
|
|
if (tTd(28, 1))
|
|
sm_dprintf("udbexpand(%s)\n", a->q_paddr);
|
|
|
|
/* make certain we are supposed to send to this address */
|
|
if (!QS_IS_SENDABLE(a->q_state))
|
|
return EX_OK;
|
|
e->e_to = a->q_paddr;
|
|
|
|
/* on first call, locate the database */
|
|
if (!UdbInitialized)
|
|
{
|
|
if (_udbx_init(e) == EX_TEMPFAIL)
|
|
return EX_TEMPFAIL;
|
|
}
|
|
|
|
/* short circuit the process if no chance of a match */
|
|
if (UdbSpec == NULL || UdbSpec[0] == '\0')
|
|
return EX_OK;
|
|
|
|
/* extract user to do userdb matching on */
|
|
user = a->q_user;
|
|
|
|
/* short circuit name begins with '\\' since it can't possibly match */
|
|
/* (might want to treat this as unquoted instead) */
|
|
if (user[0] == '\\')
|
|
return EX_OK;
|
|
|
|
/* if name begins with a colon, it indicates our metadata */
|
|
if (user[0] == ':')
|
|
return EX_OK;
|
|
|
|
keylen = sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop");
|
|
|
|
/* if name is too long, assume it won't match */
|
|
if (keylen > sizeof keybuf)
|
|
return EX_OK;
|
|
|
|
/* build actual database key */
|
|
|
|
breakout = false;
|
|
for (up = UdbEnts; !breakout; up++)
|
|
{
|
|
int usersize;
|
|
int userleft;
|
|
char userbuf[MEMCHUNKSIZE];
|
|
# if HESIOD && HES_GETMAILHOST
|
|
char pobuf[MAXNAME];
|
|
# endif /* HESIOD && HES_GETMAILHOST */
|
|
# if defined(NEWDB) && DB_VERSION_MAJOR > 1
|
|
DBC *dbc = NULL;
|
|
# endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */
|
|
|
|
user = userbuf;
|
|
userbuf[0] = '\0';
|
|
usersize = sizeof userbuf;
|
|
userleft = sizeof userbuf - 1;
|
|
|
|
/*
|
|
** Select action based on entry type.
|
|
**
|
|
** On dropping out of this switch, "class" should
|
|
** explain the type of the data, and "user" should
|
|
** contain the user information.
|
|
*/
|
|
|
|
switch (up->udb_type)
|
|
{
|
|
# if NEWDB
|
|
case UDB_DBFETCH:
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
if (tTd(28, 80))
|
|
sm_dprintf("udbexpand: trying %s (%d) via db\n",
|
|
keybuf, keylen);
|
|
# if DB_VERSION_MAJOR < 2
|
|
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
i = 0;
|
|
if (dbc == NULL &&
|
|
# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
|
|
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
|
|
NULL, &dbc, 0)) != 0)
|
|
# else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
|
|
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
|
|
NULL, &dbc)) != 0)
|
|
# endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
|
|
i = -1;
|
|
if (i != 0 || dbc == NULL ||
|
|
(errno = dbc->c_get(dbc, &key,
|
|
&info, DB_SET)) != 0)
|
|
i = 1;
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (i > 0 || info.size <= 0)
|
|
{
|
|
if (tTd(28, 2))
|
|
sm_dprintf("udbexpand: no match on %s (%d)\n",
|
|
keybuf, keylen);
|
|
# if DB_VERSION_MAJOR > 1
|
|
if (dbc != NULL)
|
|
{
|
|
(void) dbc->c_close(dbc);
|
|
dbc = NULL;
|
|
}
|
|
# endif /* DB_VERSION_MAJOR > 1 */
|
|
break;
|
|
}
|
|
if (tTd(28, 80))
|
|
sm_dprintf("udbexpand: match %.*s: %.*s\n",
|
|
(int) key.size, (char *) key.data,
|
|
(int) info.size, (char *) info.data);
|
|
|
|
a->q_flags &= ~QSELFREF;
|
|
while (i == 0 && key.size == keylen &&
|
|
memcmp(key.data, keybuf, keylen) == 0)
|
|
{
|
|
char *p;
|
|
|
|
if (bitset(EF_VRFYONLY, e->e_flags))
|
|
{
|
|
a->q_state = QS_VERIFIED;
|
|
# if DB_VERSION_MAJOR > 1
|
|
if (dbc != NULL)
|
|
{
|
|
(void) dbc->c_close(dbc);
|
|
dbc = NULL;
|
|
}
|
|
# endif /* DB_VERSION_MAJOR > 1 */
|
|
return EX_OK;
|
|
}
|
|
|
|
breakout = true;
|
|
if (info.size >= userleft - 1)
|
|
{
|
|
char *nuser;
|
|
int size = MEMCHUNKSIZE;
|
|
|
|
if (info.size > MEMCHUNKSIZE)
|
|
size = info.size;
|
|
nuser = sm_malloc_x(usersize + size);
|
|
|
|
memmove(nuser, user, usersize);
|
|
if (user != userbuf)
|
|
sm_free(user); /* XXX */
|
|
user = nuser;
|
|
usersize += size;
|
|
userleft += size;
|
|
}
|
|
p = &user[strlen(user)];
|
|
if (p != user)
|
|
{
|
|
*p++ = ',';
|
|
userleft--;
|
|
}
|
|
memmove(p, info.data, info.size);
|
|
p[info.size] = '\0';
|
|
userleft -= info.size;
|
|
|
|
/* get the next record */
|
|
# if DB_VERSION_MAJOR < 2
|
|
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
i = 0;
|
|
if ((errno = dbc->c_get(dbc, &key,
|
|
&info, DB_NEXT)) != 0)
|
|
i = 1;
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
}
|
|
|
|
# if DB_VERSION_MAJOR > 1
|
|
if (dbc != NULL)
|
|
{
|
|
(void) dbc->c_close(dbc);
|
|
dbc = NULL;
|
|
}
|
|
# endif /* DB_VERSION_MAJOR > 1 */
|
|
|
|
/* if nothing ever matched, try next database */
|
|
if (!breakout)
|
|
break;
|
|
|
|
message("expanded to %s", user);
|
|
if (LogLevel > 10)
|
|
sm_syslog(LOG_INFO, e->e_id,
|
|
"expand %.100s => %s",
|
|
e->e_to,
|
|
shortenstring(user, MAXSHORTSTR));
|
|
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
|
|
if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
|
|
{
|
|
if (tTd(28, 5))
|
|
{
|
|
sm_dprintf("udbexpand: QS_EXPANDED ");
|
|
printaddr(a, false);
|
|
}
|
|
a->q_state = QS_EXPANDED;
|
|
}
|
|
if (i < 0)
|
|
{
|
|
syserr("udbexpand: db-get %.*s stat %d",
|
|
(int) key.size, (char *) key.data, i);
|
|
return EX_TEMPFAIL;
|
|
}
|
|
|
|
/*
|
|
** If this address has a -request address, reflect
|
|
** it into the envelope.
|
|
*/
|
|
|
|
memset(&key, '\0', sizeof key);
|
|
memset(&info, '\0', sizeof info);
|
|
(void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user,
|
|
":mailsender");
|
|
keylen = strlen(keybuf);
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
|
|
# if DB_VERSION_MAJOR < 2
|
|
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
|
|
&key, &info, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (i != 0 || info.size <= 0)
|
|
break;
|
|
a->q_owner = sm_rpool_malloc_x(e->e_rpool,
|
|
info.size + 1);
|
|
memmove(a->q_owner, info.data, info.size);
|
|
a->q_owner[info.size] = '\0';
|
|
|
|
/* announce delivery; NORECEIPT bit set later */
|
|
if (e->e_xfp != NULL)
|
|
{
|
|
(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
|
|
"Message delivered to mailing list %s\n",
|
|
a->q_paddr);
|
|
}
|
|
e->e_flags |= EF_SENDRECEIPT;
|
|
a->q_flags |= QDELIVERED|QEXPANDED;
|
|
break;
|
|
# endif /* NEWDB */
|
|
|
|
# if HESIOD
|
|
case UDB_HESIOD:
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
if (tTd(28, 80))
|
|
sm_dprintf("udbexpand: trying %s (%d) via hesiod\n",
|
|
keybuf, keylen);
|
|
/* look up the key via hesiod */
|
|
i = hes_udb_get(&key, &info);
|
|
if (i < 0)
|
|
{
|
|
syserr("udbexpand: hesiod-get %.*s stat %d",
|
|
(int) key.size, (char *) key.data, i);
|
|
return EX_TEMPFAIL;
|
|
}
|
|
else if (i > 0 || info.size <= 0)
|
|
{
|
|
# if HES_GETMAILHOST
|
|
struct hes_postoffice *hp;
|
|
# endif /* HES_GETMAILHOST */
|
|
|
|
if (tTd(28, 2))
|
|
sm_dprintf("udbexpand: no match on %s (%d)\n",
|
|
(char *) keybuf, (int) keylen);
|
|
# if HES_GETMAILHOST
|
|
if (tTd(28, 8))
|
|
sm_dprintf(" ... trying hes_getmailhost(%s)\n",
|
|
a->q_user);
|
|
hp = hes_getmailhost(a->q_user);
|
|
if (hp == NULL)
|
|
{
|
|
if (hes_error() == HES_ER_NET)
|
|
{
|
|
syserr("udbexpand: hesiod-getmail %s stat %d",
|
|
a->q_user, hes_error());
|
|
return EX_TEMPFAIL;
|
|
}
|
|
if (tTd(28, 2))
|
|
sm_dprintf("hes_getmailhost(%s): %d\n",
|
|
a->q_user, hes_error());
|
|
break;
|
|
}
|
|
if (strlen(hp->po_name) + strlen(hp->po_host) >
|
|
sizeof pobuf - 2)
|
|
{
|
|
if (tTd(28, 2))
|
|
sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
|
|
a->q_user,
|
|
hp->po_name,
|
|
hp->po_host);
|
|
break;
|
|
}
|
|
info.data = pobuf;
|
|
(void) sm_snprintf(pobuf, sizeof pobuf,
|
|
"%s@%s", hp->po_name, hp->po_host);
|
|
info.size = strlen(info.data);
|
|
# else /* HES_GETMAILHOST */
|
|
break;
|
|
# endif /* HES_GETMAILHOST */
|
|
}
|
|
if (tTd(28, 80))
|
|
sm_dprintf("udbexpand: match %.*s: %.*s\n",
|
|
(int) key.size, (char *) key.data,
|
|
(int) info.size, (char *) info.data);
|
|
a->q_flags &= ~QSELFREF;
|
|
|
|
if (bitset(EF_VRFYONLY, e->e_flags))
|
|
{
|
|
a->q_state = QS_VERIFIED;
|
|
return EX_OK;
|
|
}
|
|
|
|
breakout = true;
|
|
if (info.size >= usersize)
|
|
user = sm_malloc_x(info.size + 1);
|
|
memmove(user, info.data, info.size);
|
|
user[info.size] = '\0';
|
|
|
|
message("hesioded to %s", user);
|
|
if (LogLevel > 10)
|
|
sm_syslog(LOG_INFO, e->e_id,
|
|
"hesiod %.100s => %s",
|
|
e->e_to,
|
|
shortenstring(user, MAXSHORTSTR));
|
|
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
|
|
|
|
if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
|
|
{
|
|
if (tTd(28, 5))
|
|
{
|
|
sm_dprintf("udbexpand: QS_EXPANDED ");
|
|
printaddr(a, false);
|
|
}
|
|
a->q_state = QS_EXPANDED;
|
|
}
|
|
|
|
/*
|
|
** If this address has a -request address, reflect
|
|
** it into the envelope.
|
|
*/
|
|
|
|
(void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user,
|
|
":mailsender");
|
|
keylen = strlen(keybuf);
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
i = hes_udb_get(&key, &info);
|
|
if (i != 0 || info.size <= 0)
|
|
break;
|
|
a->q_owner = sm_rpool_malloc_x(e->e_rpool,
|
|
info.size + 1);
|
|
memmove(a->q_owner, info.data, info.size);
|
|
a->q_owner[info.size] = '\0';
|
|
break;
|
|
# endif /* HESIOD */
|
|
|
|
case UDB_REMOTE:
|
|
/* not yet implemented */
|
|
break;
|
|
|
|
case UDB_FORWARD:
|
|
if (bitset(EF_VRFYONLY, e->e_flags))
|
|
{
|
|
a->q_state = QS_VERIFIED;
|
|
return EX_OK;
|
|
}
|
|
i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
|
|
if (i >= usersize)
|
|
{
|
|
usersize = i + 1;
|
|
user = sm_malloc_x(usersize);
|
|
}
|
|
(void) sm_strlcpyn(user, usersize, 3,
|
|
a->q_user, "@", up->udb_fwdhost);
|
|
message("expanded to %s", user);
|
|
a->q_flags &= ~QSELFREF;
|
|
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
|
|
if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
|
|
{
|
|
if (tTd(28, 5))
|
|
{
|
|
sm_dprintf("udbexpand: QS_EXPANDED ");
|
|
printaddr(a, false);
|
|
}
|
|
a->q_state = QS_EXPANDED;
|
|
}
|
|
breakout = true;
|
|
break;
|
|
|
|
case UDB_EOLIST:
|
|
breakout = true;
|
|
break;
|
|
|
|
default:
|
|
/* unknown entry type */
|
|
break;
|
|
}
|
|
/* XXX if an exception occurs, there is a storage leak */
|
|
if (user != userbuf)
|
|
sm_free(user); /* XXX */
|
|
}
|
|
return EX_OK;
|
|
}
|
|
/*
|
|
** UDBSENDER -- return canonical external name of sender, given local name
|
|
**
|
|
** Parameters:
|
|
** sender -- the name of the sender on the local machine.
|
|
** rpool -- resource pool from which to allocate result
|
|
**
|
|
** Returns:
|
|
** The external name for this sender, if derivable from the
|
|
** database. Storage allocated from rpool.
|
|
** NULL -- if nothing is changed from the database.
|
|
**
|
|
** Side Effects:
|
|
** none.
|
|
*/
|
|
|
|
char *
|
|
udbsender(sender, rpool)
|
|
char *sender;
|
|
SM_RPOOL_T *rpool;
|
|
{
|
|
return udbmatch(sender, "mailname", rpool);
|
|
}
|
|
/*
|
|
** UDBMATCH -- match user in field, return result of lookup.
|
|
**
|
|
** Parameters:
|
|
** user -- the name of the user.
|
|
** field -- the field to lookup.
|
|
** rpool -- resource pool from which to allocate result
|
|
**
|
|
** Returns:
|
|
** The external name for this sender, if derivable from the
|
|
** database. Storage allocated from rpool.
|
|
** NULL -- if nothing is changed from the database.
|
|
**
|
|
** Side Effects:
|
|
** none.
|
|
*/
|
|
|
|
static char *
|
|
udbmatch(user, field, rpool)
|
|
char *user;
|
|
char *field;
|
|
SM_RPOOL_T *rpool;
|
|
{
|
|
register char *p;
|
|
register struct udbent *up;
|
|
int i;
|
|
int keylen;
|
|
DBT key, info;
|
|
char keybuf[MAXKEY];
|
|
|
|
if (tTd(28, 1))
|
|
sm_dprintf("udbmatch(%s, %s)\n", user, field);
|
|
|
|
if (!UdbInitialized)
|
|
{
|
|
if (_udbx_init(CurEnv) == EX_TEMPFAIL)
|
|
return NULL;
|
|
}
|
|
|
|
/* short circuit if no spec */
|
|
if (UdbSpec == NULL || UdbSpec[0] == '\0')
|
|
return NULL;
|
|
|
|
/* short circuit name begins with '\\' since it can't possibly match */
|
|
if (user[0] == '\\')
|
|
return NULL;
|
|
|
|
/* long names can never match and are a pain to deal with */
|
|
i = strlen(field);
|
|
if (i < sizeof "maildrop")
|
|
i = sizeof "maildrop";
|
|
if ((strlen(user) + i) > sizeof keybuf - 4)
|
|
return NULL;
|
|
|
|
/* names beginning with colons indicate metadata */
|
|
if (user[0] == ':')
|
|
return NULL;
|
|
|
|
/* build database key */
|
|
(void) sm_strlcpyn(keybuf, sizeof keybuf, 3, user, ":", field);
|
|
keylen = strlen(keybuf);
|
|
|
|
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
|
|
{
|
|
/*
|
|
** Select action based on entry type.
|
|
*/
|
|
|
|
switch (up->udb_type)
|
|
{
|
|
# if NEWDB
|
|
case UDB_DBFETCH:
|
|
memset(&key, '\0', sizeof key);
|
|
memset(&info, '\0', sizeof info);
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
# if DB_VERSION_MAJOR < 2
|
|
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
|
|
&key, &info, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (i != 0 || info.size <= 0)
|
|
{
|
|
if (tTd(28, 2))
|
|
sm_dprintf("udbmatch: no match on %s (%d) via db\n",
|
|
keybuf, keylen);
|
|
continue;
|
|
}
|
|
|
|
p = sm_rpool_malloc_x(rpool, info.size + 1);
|
|
memmove(p, info.data, info.size);
|
|
p[info.size] = '\0';
|
|
if (tTd(28, 1))
|
|
sm_dprintf("udbmatch ==> %s\n", p);
|
|
return p;
|
|
# endif /* NEWDB */
|
|
|
|
# if HESIOD
|
|
case UDB_HESIOD:
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
i = hes_udb_get(&key, &info);
|
|
if (i != 0 || info.size <= 0)
|
|
{
|
|
if (tTd(28, 2))
|
|
sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n",
|
|
keybuf, keylen);
|
|
continue;
|
|
}
|
|
|
|
p = sm_rpool_malloc_x(rpool, info.size + 1);
|
|
memmove(p, info.data, info.size);
|
|
p[info.size] = '\0';
|
|
if (tTd(28, 1))
|
|
sm_dprintf("udbmatch ==> %s\n", p);
|
|
return p;
|
|
# endif /* HESIOD */
|
|
}
|
|
}
|
|
|
|
if (strcmp(field, "mailname") != 0)
|
|
return NULL;
|
|
|
|
/*
|
|
** Nothing yet. Search again for a default case. But only
|
|
** use it if we also have a forward (:maildrop) pointer already
|
|
** in the database.
|
|
*/
|
|
|
|
/* build database key */
|
|
(void) sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop");
|
|
keylen = strlen(keybuf);
|
|
|
|
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
|
|
{
|
|
switch (up->udb_type)
|
|
{
|
|
# if NEWDB
|
|
case UDB_DBFETCH:
|
|
/* get the default case for this database */
|
|
if (up->udb_default == NULL)
|
|
{
|
|
memset(&key, '\0', sizeof key);
|
|
memset(&info, '\0', sizeof info);
|
|
key.data = ":default:mailname";
|
|
key.size = strlen(key.data);
|
|
# if DB_VERSION_MAJOR < 2
|
|
i = (*up->udb_dbp->get)(up->udb_dbp,
|
|
&key, &info, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
i = errno = (*up->udb_dbp->get)(up->udb_dbp,
|
|
NULL, &key,
|
|
&info, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (i != 0 || info.size <= 0)
|
|
{
|
|
/* no default case */
|
|
up->udb_default = "";
|
|
continue;
|
|
}
|
|
|
|
/* save the default case */
|
|
up->udb_default = sm_pmalloc_x(info.size + 1);
|
|
memmove(up->udb_default, info.data, info.size);
|
|
up->udb_default[info.size] = '\0';
|
|
}
|
|
else if (up->udb_default[0] == '\0')
|
|
continue;
|
|
|
|
/* we have a default case -- verify user:maildrop */
|
|
memset(&key, '\0', sizeof key);
|
|
memset(&info, '\0', sizeof info);
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
# if DB_VERSION_MAJOR < 2
|
|
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
|
|
&key, &info, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (i != 0 || info.size <= 0)
|
|
{
|
|
/* nope -- no aliasing for this user */
|
|
continue;
|
|
}
|
|
|
|
/* they exist -- build the actual address */
|
|
i = strlen(user) + strlen(up->udb_default) + 2;
|
|
p = sm_rpool_malloc_x(rpool, i);
|
|
(void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
|
|
if (tTd(28, 1))
|
|
sm_dprintf("udbmatch ==> %s\n", p);
|
|
return p;
|
|
# endif /* NEWDB */
|
|
|
|
# if HESIOD
|
|
case UDB_HESIOD:
|
|
/* get the default case for this database */
|
|
if (up->udb_default == NULL)
|
|
{
|
|
key.data = ":default:mailname";
|
|
key.size = strlen(key.data);
|
|
i = hes_udb_get(&key, &info);
|
|
|
|
if (i != 0 || info.size <= 0)
|
|
{
|
|
/* no default case */
|
|
up->udb_default = "";
|
|
continue;
|
|
}
|
|
|
|
/* save the default case */
|
|
up->udb_default = sm_pmalloc_x(info.size + 1);
|
|
memmove(up->udb_default, info.data, info.size);
|
|
up->udb_default[info.size] = '\0';
|
|
}
|
|
else if (up->udb_default[0] == '\0')
|
|
continue;
|
|
|
|
/* we have a default case -- verify user:maildrop */
|
|
key.data = keybuf;
|
|
key.size = keylen;
|
|
i = hes_udb_get(&key, &info);
|
|
if (i != 0 || info.size <= 0)
|
|
{
|
|
/* nope -- no aliasing for this user */
|
|
continue;
|
|
}
|
|
|
|
/* they exist -- build the actual address */
|
|
i = strlen(user) + strlen(up->udb_default) + 2;
|
|
p = sm_rpool_malloc_x(rpool, i);
|
|
(void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
|
|
if (tTd(28, 1))
|
|
sm_dprintf("udbmatch ==> %s\n", p);
|
|
return p;
|
|
break;
|
|
# endif /* HESIOD */
|
|
}
|
|
}
|
|
|
|
/* still nothing.... too bad */
|
|
return NULL;
|
|
}
|
|
/*
|
|
** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map
|
|
**
|
|
** Parameters:
|
|
** map -- the map being queried.
|
|
** name -- the name to look up.
|
|
** av -- arguments to the map lookup.
|
|
** statp -- to get any error status.
|
|
**
|
|
** Returns:
|
|
** NULL if name not found in map.
|
|
** The rewritten name otherwise.
|
|
*/
|
|
|
|
/* ARGSUSED3 */
|
|
char *
|
|
udb_map_lookup(map, name, av, statp)
|
|
MAP *map;
|
|
char *name;
|
|
char **av;
|
|
int *statp;
|
|
{
|
|
char *val;
|
|
char *key;
|
|
char *SM_NONVOLATILE result = NULL;
|
|
char keybuf[MAXNAME + 1];
|
|
|
|
if (tTd(28, 20) || tTd(38, 20))
|
|
sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
|
|
|
|
if (bitset(MF_NOFOLDCASE, map->map_mflags))
|
|
{
|
|
key = name;
|
|
}
|
|
else
|
|
{
|
|
int keysize = strlen(name);
|
|
|
|
if (keysize > sizeof keybuf - 1)
|
|
keysize = sizeof keybuf - 1;
|
|
memmove(keybuf, name, keysize);
|
|
keybuf[keysize] = '\0';
|
|
makelower(keybuf);
|
|
key = keybuf;
|
|
}
|
|
val = udbmatch(key, map->map_file, NULL);
|
|
if (val == NULL)
|
|
return NULL;
|
|
SM_TRY
|
|
if (bitset(MF_MATCHONLY, map->map_mflags))
|
|
result = map_rewrite(map, name, strlen(name), NULL);
|
|
else
|
|
result = map_rewrite(map, val, strlen(val), av);
|
|
SM_FINALLY
|
|
sm_free(val);
|
|
SM_END_TRY
|
|
return result;
|
|
}
|
|
/*
|
|
** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
|
|
**
|
|
** Parameters:
|
|
** e -- the current envelope.
|
|
**
|
|
** Returns:
|
|
** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
|
|
** database due to a host being down or some similar
|
|
** (recoverable) situation.
|
|
** EX_OK -- otherwise.
|
|
**
|
|
** Side Effects:
|
|
** Fills in the UdbEnts structure from UdbSpec.
|
|
*/
|
|
|
|
# define MAXUDBOPTS 27
|
|
|
|
static int
|
|
_udbx_init(e)
|
|
ENVELOPE *e;
|
|
{
|
|
int ents = 0;
|
|
register char *p;
|
|
register struct udbent *up;
|
|
|
|
if (UdbInitialized)
|
|
return EX_OK;
|
|
|
|
# ifdef UDB_DEFAULT_SPEC
|
|
if (UdbSpec == NULL)
|
|
UdbSpec = UDB_DEFAULT_SPEC;
|
|
# endif /* UDB_DEFAULT_SPEC */
|
|
|
|
p = UdbSpec;
|
|
up = UdbEnts;
|
|
while (p != NULL)
|
|
{
|
|
char *spec;
|
|
int l;
|
|
struct udb_option opts[MAXUDBOPTS + 1];
|
|
|
|
while (*p == ' ' || *p == '\t' || *p == ',')
|
|
p++;
|
|
if (*p == '\0')
|
|
break;
|
|
spec = p;
|
|
p = strchr(p, ',');
|
|
if (p != NULL)
|
|
*p++ = '\0';
|
|
|
|
if (ents >= MAXUDBENT)
|
|
{
|
|
syserr("Maximum number of UDB entries exceeded");
|
|
break;
|
|
}
|
|
|
|
/* extract options */
|
|
(void) _udb_parsespec(spec, opts, MAXUDBOPTS);
|
|
|
|
/*
|
|
** Decode database specification.
|
|
**
|
|
** In the sendmail tradition, the leading character
|
|
** defines the semantics of the rest of the entry.
|
|
**
|
|
** @hostname -- forward email to the indicated host.
|
|
** This should be the last in the list,
|
|
** since it always matches the input.
|
|
** /dbname -- search the named database on the local
|
|
** host using the Berkeley db package.
|
|
** Hesiod -- search the named database with BIND
|
|
** using the MIT Hesiod package.
|
|
*/
|
|
|
|
switch (*spec)
|
|
{
|
|
case '@': /* forward to remote host */
|
|
up->udb_type = UDB_FORWARD;
|
|
up->udb_pid = CurrentPid;
|
|
up->udb_fwdhost = spec + 1;
|
|
ents++;
|
|
up++;
|
|
break;
|
|
|
|
# if HESIOD
|
|
case 'h': /* use hesiod */
|
|
case 'H':
|
|
if (sm_strcasecmp(spec, "hesiod") != 0)
|
|
goto badspec;
|
|
up->udb_type = UDB_HESIOD;
|
|
up->udb_pid = CurrentPid;
|
|
ents++;
|
|
up++;
|
|
break;
|
|
# endif /* HESIOD */
|
|
|
|
# if NEWDB
|
|
case '/': /* look up remote name */
|
|
l = strlen(spec);
|
|
if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
|
|
{
|
|
up->udb_dbname = spec;
|
|
}
|
|
else
|
|
{
|
|
up->udb_dbname = sm_pmalloc_x(l + 4);
|
|
(void) sm_strlcpyn(up->udb_dbname, l + 4, 2,
|
|
spec, ".db");
|
|
}
|
|
errno = 0;
|
|
# if DB_VERSION_MAJOR < 2
|
|
up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY,
|
|
0644, DB_BTREE, NULL);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
{
|
|
int flags = DB_RDONLY;
|
|
# if DB_VERSION_MAJOR > 2
|
|
int ret;
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
|
|
SM_DB_FLAG_ADD(flags);
|
|
up->udb_dbp = NULL;
|
|
# if DB_VERSION_MAJOR > 2
|
|
ret = db_create(&up->udb_dbp, NULL, 0);
|
|
if (ret != 0)
|
|
{
|
|
(void) up->udb_dbp->close(up->udb_dbp,
|
|
0);
|
|
up->udb_dbp = NULL;
|
|
}
|
|
else
|
|
{
|
|
ret = up->udb_dbp->open(up->udb_dbp,
|
|
DBTXN
|
|
up->udb_dbname,
|
|
NULL,
|
|
DB_BTREE,
|
|
flags,
|
|
0644);
|
|
if (ret != 0)
|
|
{
|
|
#ifdef DB_OLD_VERSION
|
|
if (ret == DB_OLD_VERSION)
|
|
ret = EINVAL;
|
|
#endif /* DB_OLD_VERSION */
|
|
(void) up->udb_dbp->close(up->udb_dbp, 0);
|
|
up->udb_dbp = NULL;
|
|
}
|
|
}
|
|
errno = ret;
|
|
# else /* DB_VERSION_MAJOR > 2 */
|
|
errno = db_open(up->udb_dbname, DB_BTREE,
|
|
flags, 0644, NULL,
|
|
NULL, &up->udb_dbp);
|
|
# endif /* DB_VERSION_MAJOR > 2 */
|
|
}
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (up->udb_dbp == NULL)
|
|
{
|
|
if (tTd(28, 1))
|
|
{
|
|
int save_errno = errno;
|
|
|
|
# if DB_VERSION_MAJOR < 2
|
|
sm_dprintf("dbopen(%s): %s\n",
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
sm_dprintf("db_open(%s): %s\n",
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
up->udb_dbname,
|
|
sm_errstring(errno));
|
|
errno = save_errno;
|
|
}
|
|
if (errno != ENOENT && errno != EACCES)
|
|
{
|
|
if (LogLevel > 2)
|
|
sm_syslog(LOG_ERR, e->e_id,
|
|
# if DB_VERSION_MAJOR < 2
|
|
"dbopen(%s): %s",
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
"db_open(%s): %s",
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
up->udb_dbname,
|
|
sm_errstring(errno));
|
|
up->udb_type = UDB_EOLIST;
|
|
if (up->udb_dbname != spec)
|
|
sm_free(up->udb_dbname); /* XXX */
|
|
goto tempfail;
|
|
}
|
|
if (up->udb_dbname != spec)
|
|
sm_free(up->udb_dbname); /* XXX */
|
|
break;
|
|
}
|
|
if (tTd(28, 1))
|
|
{
|
|
# if DB_VERSION_MAJOR < 2
|
|
sm_dprintf("_udbx_init: dbopen(%s)\n",
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
sm_dprintf("_udbx_init: db_open(%s)\n",
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
up->udb_dbname);
|
|
}
|
|
up->udb_type = UDB_DBFETCH;
|
|
up->udb_pid = CurrentPid;
|
|
ents++;
|
|
up++;
|
|
break;
|
|
# endif /* NEWDB */
|
|
|
|
default:
|
|
# if HESIOD
|
|
badspec:
|
|
# endif /* HESIOD */
|
|
syserr("Unknown UDB spec %s", spec);
|
|
break;
|
|
}
|
|
}
|
|
up->udb_type = UDB_EOLIST;
|
|
|
|
if (tTd(28, 4))
|
|
{
|
|
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
|
|
{
|
|
switch (up->udb_type)
|
|
{
|
|
case UDB_REMOTE:
|
|
sm_dprintf("REMOTE: addr %s, timeo %d\n",
|
|
anynet_ntoa((SOCKADDR *) &up->udb_addr),
|
|
up->udb_timeout);
|
|
break;
|
|
|
|
case UDB_DBFETCH:
|
|
# if NEWDB
|
|
sm_dprintf("FETCH: file %s\n",
|
|
up->udb_dbname);
|
|
# else /* NEWDB */
|
|
sm_dprintf("FETCH\n");
|
|
# endif /* NEWDB */
|
|
break;
|
|
|
|
case UDB_FORWARD:
|
|
sm_dprintf("FORWARD: host %s\n",
|
|
up->udb_fwdhost);
|
|
break;
|
|
|
|
case UDB_HESIOD:
|
|
sm_dprintf("HESIOD\n");
|
|
break;
|
|
|
|
default:
|
|
sm_dprintf("UNKNOWN\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
UdbInitialized = true;
|
|
errno = 0;
|
|
return EX_OK;
|
|
|
|
/*
|
|
** On temporary failure, back out anything we've already done
|
|
*/
|
|
|
|
tempfail:
|
|
# if NEWDB
|
|
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
|
|
{
|
|
if (up->udb_type == UDB_DBFETCH)
|
|
{
|
|
# if DB_VERSION_MAJOR < 2
|
|
(*up->udb_dbp->close)(up->udb_dbp);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
if (tTd(28, 1))
|
|
sm_dprintf("_udbx_init: db->close(%s)\n",
|
|
up->udb_dbname);
|
|
}
|
|
}
|
|
# endif /* NEWDB */
|
|
return EX_TEMPFAIL;
|
|
}
|
|
|
|
static int
|
|
_udb_parsespec(udbspec, opt, maxopts)
|
|
char *udbspec;
|
|
struct udb_option opt[];
|
|
int maxopts;
|
|
{
|
|
register char *spec;
|
|
register char *spec_end;
|
|
register int optnum;
|
|
|
|
spec_end = strchr(udbspec, ':');
|
|
for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
|
|
{
|
|
register char *p;
|
|
|
|
while (isascii(*spec) && isspace(*spec))
|
|
spec++;
|
|
spec_end = strchr(spec, ':');
|
|
if (spec_end != NULL)
|
|
*spec_end++ = '\0';
|
|
|
|
opt[optnum].udbo_name = spec;
|
|
opt[optnum].udbo_val = NULL;
|
|
p = strchr(spec, '=');
|
|
if (p != NULL)
|
|
opt[optnum].udbo_val = ++p;
|
|
}
|
|
return optnum;
|
|
}
|
|
/*
|
|
** _UDBX_CLOSE -- close all file based UDB entries.
|
|
**
|
|
** Parameters:
|
|
** none
|
|
**
|
|
** Returns:
|
|
** none
|
|
*/
|
|
void
|
|
_udbx_close()
|
|
{
|
|
struct udbent *up;
|
|
|
|
if (!UdbInitialized)
|
|
return;
|
|
|
|
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
|
|
{
|
|
if (up->udb_pid != CurrentPid)
|
|
continue;
|
|
|
|
# if NEWDB
|
|
if (up->udb_type == UDB_DBFETCH)
|
|
{
|
|
# if DB_VERSION_MAJOR < 2
|
|
(*up->udb_dbp->close)(up->udb_dbp);
|
|
# else /* DB_VERSION_MAJOR < 2 */
|
|
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
|
|
# endif /* DB_VERSION_MAJOR < 2 */
|
|
}
|
|
if (tTd(28, 1))
|
|
sm_dprintf("_udbx_init: db->close(%s)\n",
|
|
up->udb_dbname);
|
|
# endif /* NEWDB */
|
|
}
|
|
}
|
|
|
|
# if HESIOD
|
|
|
|
static int
|
|
hes_udb_get(key, info)
|
|
DBT *key;
|
|
DBT *info;
|
|
{
|
|
char *name, *type;
|
|
char **hp;
|
|
char kbuf[MAXKEY + 1];
|
|
|
|
if (sm_strlcpy(kbuf, key->data, sizeof kbuf) >= sizeof kbuf)
|
|
return 0;
|
|
name = kbuf;
|
|
type = strrchr(name, ':');
|
|
if (type == NULL)
|
|
return 1;
|
|
*type++ = '\0';
|
|
if (strchr(name, '@') != NULL)
|
|
return 1;
|
|
|
|
if (tTd(28, 1))
|
|
sm_dprintf("hes_udb_get(%s, %s)\n", name, type);
|
|
|
|
/* make the hesiod query */
|
|
# ifdef HESIOD_INIT
|
|
if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
|
|
return -1;
|
|
hp = hesiod_resolve(HesiodContext, name, type);
|
|
# else /* HESIOD_INIT */
|
|
hp = hes_resolve(name, type);
|
|
# endif /* HESIOD_INIT */
|
|
*--type = ':';
|
|
# ifdef HESIOD_INIT
|
|
if (hp == NULL)
|
|
return 1;
|
|
if (*hp == NULL)
|
|
{
|
|
hesiod_free_list(HesiodContext, hp);
|
|
if (errno == ECONNREFUSED || errno == EMSGSIZE)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
# else /* HESIOD_INIT */
|
|
if (hp == NULL || hp[0] == NULL)
|
|
{
|
|
/* network problem or timeout */
|
|
if (hes_error() == HES_ER_NET)
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
# endif /* HESIOD_INIT */
|
|
else
|
|
{
|
|
/*
|
|
** If there are multiple matches, just return the
|
|
** first one.
|
|
**
|
|
** XXX These should really be returned; for example,
|
|
** XXX it is legal for :maildrop to be multi-valued.
|
|
*/
|
|
|
|
info->data = hp[0];
|
|
info->size = (size_t) strlen(info->data);
|
|
}
|
|
|
|
if (tTd(28, 80))
|
|
sm_dprintf("hes_udb_get => %s\n", *hp);
|
|
|
|
return 0;
|
|
}
|
|
# endif /* HESIOD */
|
|
|
|
#else /* USERDB */
|
|
|
|
int
|
|
udbexpand(a, sendq, aliaslevel, e)
|
|
ADDRESS *a;
|
|
ADDRESS **sendq;
|
|
int aliaslevel;
|
|
ENVELOPE *e;
|
|
{
|
|
return EX_OK;
|
|
}
|
|
|
|
#endif /* USERDB */
|