summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libsm/mbdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/libsm/mbdb.c')
-rw-r--r--contrib/sendmail/libsm/mbdb.c746
1 files changed, 746 insertions, 0 deletions
diff --git a/contrib/sendmail/libsm/mbdb.c b/contrib/sendmail/libsm/mbdb.c
new file mode 100644
index 0000000..fcb5117
--- /dev/null
+++ b/contrib/sendmail/libsm/mbdb.c
@@ -0,0 +1,746 @@
+/*
+ * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers.
+ * 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 <sm/gen.h>
+SM_RCSID("@(#)$Id: mbdb.c,v 1.28 2002/01/07 23:29:43 gshapiro Exp $")
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+#include <sm/limits.h>
+#include <sm/conf.h>
+#include <sm/assert.h>
+#include <sm/bitops.h>
+#include <sm/errstring.h>
+#include <sm/heap.h>
+#include <sm/mbdb.h>
+#include <sm/string.h>
+#include <sm/sysexits.h>
+
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+# include <sm/ldap.h>
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
+
+typedef struct
+{
+ char *mbdb_typename;
+ int (*mbdb_initialize) __P((char *));
+ int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
+ void (*mbdb_terminate) __P((void));
+} SM_MBDB_TYPE_T;
+
+static int mbdb_pw_initialize __P((char *));
+static int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
+static void mbdb_pw_terminate __P((void));
+
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+static struct sm_ldap_struct LDAPLMAP;
+static int mbdb_ldap_initialize __P((char *));
+static int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
+static void mbdb_ldap_terminate __P((void));
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
+
+static SM_MBDB_TYPE_T SmMbdbTypes[] =
+{
+ { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+ { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
+ { NULL, NULL, NULL, NULL }
+};
+
+static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
+
+/*
+** SM_MBDB_INITIALIZE -- specify which mailbox database to use
+**
+** If this function is not called, then the "pw" implementation
+** is used by default; this implementation uses getpwnam().
+**
+** Parameters:
+** mbdb -- Which mailbox database to use.
+** The argument has the form "name" or "name.arg".
+** "pw" means use getpwnam().
+**
+** Results:
+** EX_OK on success, or an EX_* code on failure.
+*/
+
+int
+sm_mbdb_initialize(mbdb)
+ char *mbdb;
+{
+ size_t namelen;
+ int err;
+ char *name;
+ char *arg;
+ SM_MBDB_TYPE_T *t;
+
+ SM_REQUIRE(mbdb != NULL);
+
+ name = mbdb;
+ arg = strchr(mbdb, '.');
+ if (arg == NULL)
+ namelen = strlen(name);
+ else
+ {
+ namelen = arg - name;
+ ++arg;
+ }
+
+ for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
+ {
+ if (strlen(t->mbdb_typename) == namelen &&
+ strncmp(name, t->mbdb_typename, namelen) == 0)
+ {
+ err = t->mbdb_initialize(arg);
+ if (err == EX_OK)
+ SmMbdbType = t;
+ return err;
+ }
+ }
+ return EX_UNAVAILABLE;
+}
+
+/*
+** SM_MBDB_TERMINATE -- terminate connection to the mailbox database
+**
+** Because this function closes any cached file descriptors that
+** are being held open for the connection to the mailbox database,
+** it should be called for security reasons prior to dropping privileges
+** and execing another process.
+**
+** Parameters:
+** none.
+**
+** Results:
+** none.
+*/
+
+void
+sm_mbdb_terminate()
+{
+ SmMbdbType->mbdb_terminate();
+}
+
+/*
+** SM_MBDB_LOOKUP -- look up a local mail recipient, given name
+**
+** Parameters:
+** name -- name of local mail recipient
+** user -- pointer to structure to fill in on success
+**
+** Results:
+** On success, fill in *user and return EX_OK.
+** If the user does not exist, return EX_NOUSER.
+** If a temporary failure (eg, a network failure) occurred,
+** return EX_TEMPFAIL. Otherwise return EX_OSERR.
+*/
+
+int
+sm_mbdb_lookup(name, user)
+ char *name;
+ SM_MBDB_T *user;
+{
+ return SmMbdbType->mbdb_lookup(name, user);
+}
+
+/*
+** SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T
+**
+** Parameters:
+** user -- destination user information structure
+** pw -- source passwd structure
+**
+** Results:
+** none.
+*/
+
+void
+sm_mbdb_frompw(user, pw)
+ SM_MBDB_T *user;
+ struct passwd *pw;
+{
+ SM_REQUIRE(user != NULL);
+ (void) sm_strlcpy(user->mbdb_name, pw->pw_name,
+ sizeof(user->mbdb_name));
+ user->mbdb_uid = pw->pw_uid;
+ user->mbdb_gid = pw->pw_gid;
+ sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
+ sizeof(user->mbdb_fullname));
+ (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
+ sizeof(user->mbdb_homedir));
+ (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
+ sizeof(user->mbdb_shell));
+}
+
+/*
+** SM_PWFULLNAME -- build full name of user from pw_gecos field.
+**
+** This routine interprets the strange entry that would appear
+** in the GECOS field of the password file.
+**
+** Parameters:
+** gecos -- name to build.
+** user -- the login name of this user (for &).
+** buf -- place to put the result.
+** buflen -- length of buf.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_pwfullname(gecos, user, buf, buflen)
+ register char *gecos;
+ char *user;
+ char *buf;
+ size_t buflen;
+{
+ register char *p;
+ register char *bp = buf;
+
+ if (*gecos == '*')
+ gecos++;
+
+ /* copy gecos, interpolating & to be full name */
+ for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
+ {
+ if (bp >= &buf[buflen - 1])
+ {
+ /* buffer overflow -- just use login name */
+ (void) sm_strlcpy(buf, user, buflen);
+ return;
+ }
+ if (*p == '&')
+ {
+ /* interpolate full name */
+ (void) sm_strlcpy(bp, user, buflen - (bp - buf));
+ *bp = toupper(*bp);
+ bp += strlen(bp);
+ }
+ else
+ *bp++ = *p;
+ }
+ *bp = '\0';
+}
+
+/*
+** /etc/passwd implementation.
+*/
+
+/*
+** MBDB_PW_INITIALIZE -- initialize getpwnam() version
+**
+** Parameters:
+** arg -- unused.
+**
+** Results:
+** EX_OK.
+*/
+
+/* ARGSUSED0 */
+static int
+mbdb_pw_initialize(arg)
+ char *arg;
+{
+ return EX_OK;
+}
+
+/*
+** MBDB_PW_LOOKUP -- look up a local mail recipient, given name
+**
+** Parameters:
+** name -- name of local mail recipient
+** user -- pointer to structure to fill in on success
+**
+** Results:
+** On success, fill in *user and return EX_OK.
+** Failure: EX_NOUSER.
+*/
+
+static int
+mbdb_pw_lookup(name, user)
+ char *name;
+ SM_MBDB_T *user;
+{
+ struct passwd *pw;
+
+#ifdef HESIOD
+ /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
+ {
+ char *p;
+
+ for (p = name; *p != '\0'; p++)
+ if (!isascii(*p) || !isdigit(*p))
+ break;
+ if (*p == '\0')
+ return EX_NOUSER;
+ }
+#endif /* HESIOD */
+
+ errno = 0;
+ pw = getpwnam(name);
+ if (pw == NULL)
+ {
+#if 0
+ /*
+ ** getpwnam() isn't advertised as setting errno.
+ ** In fact, under FreeBSD, non-root getpwnam() on
+ ** non-existant users returns NULL with errno = EPERM.
+ ** This test won't work.
+ */
+ switch (errno)
+ {
+ case 0:
+ return EX_NOUSER;
+ case EIO:
+ return EX_OSERR;
+ default:
+ return EX_TEMPFAIL;
+ }
+#endif /* 0 */
+ return EX_NOUSER;
+ }
+
+ sm_mbdb_frompw(user, pw);
+ return EX_OK;
+}
+
+/*
+** MBDB_PW_TERMINATE -- terminate connection to the mailbox database
+**
+** Parameters:
+** none.
+**
+** Results:
+** none.
+*/
+
+static void
+mbdb_pw_terminate()
+{
+ endpwent();
+}
+
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+/*
+** LDAP example implementation based on RFC 2307, "An Approach for Using
+** LDAP as a Network Information Service":
+**
+** ( nisSchema.1.0 NAME 'uidNumber'
+** DESC 'An integer uniquely identifying a user in an
+** administrative domain'
+** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
+**
+** ( nisSchema.1.1 NAME 'gidNumber'
+** DESC 'An integer uniquely identifying a group in an
+** administrative domain'
+** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
+**
+** ( nisSchema.1.2 NAME 'gecos'
+** DESC 'The GECOS field; the common name'
+** EQUALITY caseIgnoreIA5Match
+** SUBSTRINGS caseIgnoreIA5SubstringsMatch
+** SYNTAX 'IA5String' SINGLE-VALUE )
+**
+** ( nisSchema.1.3 NAME 'homeDirectory'
+** DESC 'The absolute path to the home directory'
+** EQUALITY caseExactIA5Match
+** SYNTAX 'IA5String' SINGLE-VALUE )
+**
+** ( nisSchema.1.4 NAME 'loginShell'
+** DESC 'The path to the login shell'
+** EQUALITY caseExactIA5Match
+** SYNTAX 'IA5String' SINGLE-VALUE )
+**
+** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
+** DESC 'Abstraction of an account with POSIX attributes'
+** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+** MAY ( userPassword $ loginShell $ gecos $ description ) )
+**
+*/
+
+# define MBDB_LDAP_LABEL "MailboxDatabase"
+
+# ifndef MBDB_LDAP_FILTER
+# define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))"
+# endif /* MBDB_LDAP_FILTER */
+
+# ifndef MBDB_DEFAULT_LDAP_BASEDN
+# define MBDB_DEFAULT_LDAP_BASEDN NULL
+# endif /* MBDB_DEFAULT_LDAP_BASEDN */
+
+# ifndef MBDB_DEFAULT_LDAP_SERVER
+# define MBDB_DEFAULT_LDAP_SERVER NULL
+# endif /* MBDB_DEFAULT_LDAP_SERVER */
+
+/*
+** MBDB_LDAP_INITIALIZE -- initialize LDAP version
+**
+** Parameters:
+** arg -- LDAP specification
+**
+** Results:
+** EX_OK on success, or an EX_* code on failure.
+*/
+
+static int
+mbdb_ldap_initialize(arg)
+ char *arg;
+{
+ sm_ldap_clear(&LDAPLMAP);
+ LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
+ LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
+ LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
+
+ /* Only want one match */
+ LDAPLMAP.ldap_sizelimit = 1;
+
+ /* interpolate new ldap_base and ldap_host from arg if given */
+ if (arg != NULL && *arg != '\0')
+ {
+ char *new;
+ char *sep;
+ size_t len;
+
+ len = strlen(arg) + 1;
+ new = sm_malloc(len);
+ if (new == NULL)
+ return EX_TEMPFAIL;
+ (void) sm_strlcpy(new, arg, len);
+ sep = strrchr(new, '@');
+ if (sep != NULL)
+ {
+ *sep++ = '\0';
+ LDAPLMAP.ldap_host = sep;
+ }
+ LDAPLMAP.ldap_base = new;
+ }
+
+ /* No connection yet, connect */
+ if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
+ return EX_UNAVAILABLE;
+ return EX_OK;
+}
+
+
+/*
+** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
+**
+** Parameters:
+** name -- name of local mail recipient
+** user -- pointer to structure to fill in on success
+**
+** Results:
+** On success, fill in *user and return EX_OK.
+** Failure: EX_NOUSER.
+*/
+
+#define NEED_FULLNAME 0x01
+#define NEED_HOMEDIR 0x02
+#define NEED_SHELL 0x04
+#define NEED_UID 0x08
+#define NEED_GID 0x10
+
+static int
+mbdb_ldap_lookup(name, user)
+ char *name;
+ SM_MBDB_T *user;
+{
+ int msgid;
+ int need;
+ int ret;
+ int save_errno;
+ LDAPMessage *entry;
+ BerElement *ber;
+ char *attr = NULL;
+
+ if (strlen(name) >= sizeof(user->mbdb_name))
+ {
+ errno = EINVAL;
+ return EX_NOUSER;
+ }
+
+ if (LDAPLMAP.ldap_filter == NULL)
+ {
+ /* map not initialized, but don't have arg here */
+ errno = EFAULT;
+ return EX_TEMPFAIL;
+ }
+
+ if (LDAPLMAP.ldap_ld == NULL)
+ {
+ /* map not open, try to open now */
+ if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
+ return EX_TEMPFAIL;
+ }
+
+ sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
+ msgid = sm_ldap_search(&LDAPLMAP, name);
+ if (msgid == -1)
+ {
+ save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
+# ifdef LDAP_SERVER_DOWN
+ if (errno == LDAP_SERVER_DOWN)
+ {
+ /* server disappeared, try reopen on next search */
+ sm_ldap_close(&LDAPLMAP);
+ }
+# endif /* LDAP_SERVER_DOWN */
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* Get results */
+ ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
+ (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
+ &(LDAPLMAP.ldap_timeout)),
+ &(LDAPLMAP.ldap_res));
+
+ if (ret != LDAP_RES_SEARCH_RESULT &&
+ ret != LDAP_RES_SEARCH_ENTRY)
+ {
+ if (ret == 0)
+ errno = ETIMEDOUT;
+ else
+ errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+ ret = EX_TEMPFAIL;
+ goto abort;
+ }
+
+ entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
+ if (entry == NULL)
+ {
+ save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+ if (save_errno == LDAP_SUCCESS)
+ {
+ errno = ENOENT;
+ ret = EX_NOUSER;
+ }
+ else
+ {
+ errno = save_errno;
+ ret = EX_TEMPFAIL;
+ }
+ goto abort;
+ }
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ ret = EX_OK;
+ need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
+ for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
+ attr != NULL;
+ attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
+ {
+ char **vals;
+
+ vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
+ if (vals == NULL)
+ {
+ errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+ if (errno == LDAP_SUCCESS)
+ {
+# if USING_NETSCAPE_LDAP
+ ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ continue;
+ }
+
+ /* Must be an error */
+ errno += E_LDAPBASE;
+ ret = EX_TEMPFAIL;
+ goto abort;
+ }
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ if (vals[0] == NULL || vals[0][0] == '\0')
+ goto skip;
+
+ if (strcasecmp(attr, "gecos") == 0)
+ {
+ if (!bitset(NEED_FULLNAME, need) ||
+ strlen(vals[0]) >= sizeof(user->mbdb_fullname))
+ goto skip;
+
+ sm_pwfullname(vals[0], name, user->mbdb_fullname,
+ sizeof(user->mbdb_fullname));
+ need &= ~NEED_FULLNAME;
+ }
+ else if (strcasecmp(attr, "homeDirectory") == 0)
+ {
+ if (!bitset(NEED_HOMEDIR, need) ||
+ strlen(vals[0]) >= sizeof(user->mbdb_homedir))
+ goto skip;
+
+ (void) sm_strlcpy(user->mbdb_homedir, vals[0],
+ sizeof(user->mbdb_homedir));
+ need &= ~NEED_HOMEDIR;
+ }
+ else if (strcasecmp(attr, "loginShell") == 0)
+ {
+ if (!bitset(NEED_SHELL, need) ||
+ strlen(vals[0]) >= sizeof(user->mbdb_shell))
+ goto skip;
+
+ (void) sm_strlcpy(user->mbdb_shell, vals[0],
+ sizeof(user->mbdb_shell));
+ need &= ~NEED_SHELL;
+ }
+ else if (strcasecmp(attr, "uidNumber") == 0)
+ {
+ char *p;
+
+ if (!bitset(NEED_UID, need))
+ goto skip;
+
+ for (p = vals[0]; *p != '\0'; p++)
+ {
+ /* allow negative numbers */
+ if (p == vals[0] && *p == '-')
+ {
+ /* but not simply '-' */
+ if (*(p + 1) == '\0')
+ goto skip;
+ }
+ else if (!isascii(*p) || !isdigit(*p))
+ goto skip;
+ }
+ user->mbdb_uid = atoi(vals[0]);
+ need &= ~NEED_UID;
+ }
+ else if (strcasecmp(attr, "gidNumber") == 0)
+ {
+ char *p;
+
+ if (!bitset(NEED_GID, need))
+ goto skip;
+
+ for (p = vals[0]; *p != '\0'; p++)
+ {
+ /* allow negative numbers */
+ if (p == vals[0] && *p == '-')
+ {
+ /* but not simply '-' */
+ if (*(p + 1) == '\0')
+ goto skip;
+ }
+ else if (!isascii(*p) || !isdigit(*p))
+ goto skip;
+ }
+ user->mbdb_gid = atoi(vals[0]);
+ need &= ~NEED_GID;
+ }
+
+skip:
+ ldap_value_free(vals);
+# if USING_NETSCAPE_LDAP
+ ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ }
+
+ errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+
+ /*
+ ** We check errno != LDAP_DECODING_ERROR since
+ ** OpenLDAP 1.X has a very ugly *undocumented*
+ ** hack of returning this error code from
+ ** ldap_next_attribute() if the library freed the
+ ** ber attribute. See:
+ ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
+ */
+
+ if (errno != LDAP_SUCCESS &&
+ errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ errno += E_LDAPBASE;
+ ret = EX_TEMPFAIL;
+ goto abort;
+ }
+
+ abort:
+ save_errno = errno;
+ if (attr != NULL)
+ {
+# if USING_NETSCAPE_LDAP
+ ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ attr = NULL;
+ }
+ if (LDAPLMAP.ldap_res != NULL)
+ {
+ ldap_msgfree(LDAPLMAP.ldap_res);
+ LDAPLMAP.ldap_res = NULL;
+ }
+ if (ret == EX_OK)
+ {
+ if (need == 0)
+ {
+ (void) sm_strlcpy(user->mbdb_name, name,
+ sizeof(user->mbdb_name));
+ save_errno = 0;
+ }
+ else
+ {
+ ret = EX_NOUSER;
+ save_errno = EINVAL;
+ }
+ }
+ errno = save_errno;
+ return ret;
+}
+
+/*
+** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
+**
+** Parameters:
+** none.
+**
+** Results:
+** none.
+*/
+
+static void
+mbdb_ldap_terminate()
+{
+ sm_ldap_close(&LDAPLMAP);
+}
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
OpenPOWER on IntegriCloud