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