summaryrefslogtreecommitdiffstats
path: root/sendmail/src/udb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sendmail/src/udb.c')
-rw-r--r--sendmail/src/udb.c1314
1 files changed, 1314 insertions, 0 deletions
diff --git a/sendmail/src/udb.c b/sendmail/src/udb.c
new file mode 100644
index 0000000..29470e7
--- /dev/null
+++ b/sendmail/src/udb.c
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (c) 1998-2003, 2006 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>
+#include "map.h"
+
+#if USERDB
+SM_RCSID("@(#)$Id: udb.c,v 8.164 2006/12/19 19:49:51 ca Exp $ (with USERDB)")
+#else /* USERDB */
+SM_RCSID("@(#)$Id: udb.c,v 8.164 2006/12/19 19:49:51 ca Exp $ (without USERDB)")
+#endif /* USERDB */
+
+#if USERDB
+
+#include <sm/sendmail.h>
+# 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[MAXUDBKEY];
+
+ 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(sm_debug_file(), 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(sm_debug_file(), 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(sm_debug_file(), 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[MAXUDBKEY];
+
+ 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[MAXUDBKEY + 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 */
OpenPOWER on IntegriCloud