190792Sgshapiro/*
2261363Sgshapiro * Copyright (c) 2001-2003,2009 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro *
590792Sgshapiro * By using this file, you agree to the terms and conditions set
690792Sgshapiro * forth in the LICENSE file which can be found at the top level of
790792Sgshapiro * the sendmail distribution.
890792Sgshapiro */
990792Sgshapiro
1090792Sgshapiro#include <sm/gen.h>
11266692SgshapiroSM_RCSID("@(#)$Id: mbdb.c,v 1.43 2014-01-08 17:03:15 ca Exp $")
1290792Sgshapiro
1390792Sgshapiro#include <sys/param.h>
1490792Sgshapiro
1590792Sgshapiro#include <ctype.h>
1690792Sgshapiro#include <errno.h>
1790792Sgshapiro#include <pwd.h>
1890792Sgshapiro#include <stdlib.h>
1990792Sgshapiro#include <setjmp.h>
20110560Sgshapiro#include <unistd.h>
2190792Sgshapiro
2290792Sgshapiro#include <sm/limits.h>
2390792Sgshapiro#include <sm/conf.h>
2490792Sgshapiro#include <sm/assert.h>
2590792Sgshapiro#include <sm/bitops.h>
2690792Sgshapiro#include <sm/errstring.h>
2790792Sgshapiro#include <sm/heap.h>
2890792Sgshapiro#include <sm/mbdb.h>
2990792Sgshapiro#include <sm/string.h>
3094334Sgshapiro# ifdef EX_OK
3194334Sgshapiro#  undef EX_OK			/* for SVr4.2 SMP */
3294334Sgshapiro# endif /* EX_OK */
3390792Sgshapiro#include <sm/sysexits.h>
3490792Sgshapiro
3590792Sgshapiro#if LDAPMAP
3690792Sgshapiro# if _LDAP_EXAMPLE_
3790792Sgshapiro#  include <sm/ldap.h>
3890792Sgshapiro# endif /* _LDAP_EXAMPLE_ */
3990792Sgshapiro#endif /* LDAPMAP */
4090792Sgshapiro
4190792Sgshapirotypedef struct
4290792Sgshapiro{
4390792Sgshapiro	char	*mbdb_typename;
4490792Sgshapiro	int	(*mbdb_initialize) __P((char *));
4590792Sgshapiro	int	(*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
4690792Sgshapiro	void	(*mbdb_terminate) __P((void));
4790792Sgshapiro} SM_MBDB_TYPE_T;
4890792Sgshapiro
4990792Sgshapirostatic int	mbdb_pw_initialize __P((char *));
5090792Sgshapirostatic int	mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
5190792Sgshapirostatic void	mbdb_pw_terminate __P((void));
5290792Sgshapiro
5390792Sgshapiro#if LDAPMAP
5490792Sgshapiro# if _LDAP_EXAMPLE_
5590792Sgshapirostatic struct sm_ldap_struct LDAPLMAP;
5690792Sgshapirostatic int	mbdb_ldap_initialize __P((char *));
5790792Sgshapirostatic int	mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
5890792Sgshapirostatic void	mbdb_ldap_terminate __P((void));
5990792Sgshapiro# endif /* _LDAP_EXAMPLE_ */
6090792Sgshapiro#endif /* LDAPMAP */
6190792Sgshapiro
6290792Sgshapirostatic SM_MBDB_TYPE_T SmMbdbTypes[] =
6390792Sgshapiro{
6490792Sgshapiro	{ "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
6590792Sgshapiro#if LDAPMAP
6690792Sgshapiro# if _LDAP_EXAMPLE_
6790792Sgshapiro	{ "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
6890792Sgshapiro# endif /* _LDAP_EXAMPLE_ */
6990792Sgshapiro#endif /* LDAPMAP */
7090792Sgshapiro	{ NULL, NULL, NULL, NULL }
7190792Sgshapiro};
7290792Sgshapiro
7390792Sgshapirostatic SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
7490792Sgshapiro
7590792Sgshapiro/*
7690792Sgshapiro**  SM_MBDB_INITIALIZE -- specify which mailbox database to use
7790792Sgshapiro**
7890792Sgshapiro**	If this function is not called, then the "pw" implementation
7990792Sgshapiro**	is used by default; this implementation uses getpwnam().
8090792Sgshapiro**
8190792Sgshapiro**	Parameters:
8290792Sgshapiro**		mbdb -- Which mailbox database to use.
8390792Sgshapiro**			The argument has the form "name" or "name.arg".
8490792Sgshapiro**			"pw" means use getpwnam().
8590792Sgshapiro**
8690792Sgshapiro**	Results:
8790792Sgshapiro**		EX_OK on success, or an EX_* code on failure.
8890792Sgshapiro*/
8990792Sgshapiro
9090792Sgshapiroint
9190792Sgshapirosm_mbdb_initialize(mbdb)
9290792Sgshapiro	char *mbdb;
9390792Sgshapiro{
9490792Sgshapiro	size_t namelen;
9590792Sgshapiro	int err;
9690792Sgshapiro	char *name;
9790792Sgshapiro	char *arg;
9890792Sgshapiro	SM_MBDB_TYPE_T *t;
9990792Sgshapiro
10090792Sgshapiro	SM_REQUIRE(mbdb != NULL);
10190792Sgshapiro
10290792Sgshapiro	name = mbdb;
10390792Sgshapiro	arg = strchr(mbdb, '.');
10490792Sgshapiro	if (arg == NULL)
10590792Sgshapiro		namelen = strlen(name);
10690792Sgshapiro	else
10790792Sgshapiro	{
10890792Sgshapiro		namelen = arg - name;
10990792Sgshapiro		++arg;
11090792Sgshapiro	}
11190792Sgshapiro
11290792Sgshapiro	for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
11390792Sgshapiro	{
11490792Sgshapiro		if (strlen(t->mbdb_typename) == namelen &&
11590792Sgshapiro		    strncmp(name, t->mbdb_typename, namelen) == 0)
11690792Sgshapiro		{
11798121Sgshapiro			err = EX_OK;
11898121Sgshapiro			if (t->mbdb_initialize != NULL)
11998121Sgshapiro				err = t->mbdb_initialize(arg);
12090792Sgshapiro			if (err == EX_OK)
12190792Sgshapiro				SmMbdbType = t;
12290792Sgshapiro			return err;
12390792Sgshapiro		}
12490792Sgshapiro	}
12590792Sgshapiro	return EX_UNAVAILABLE;
12690792Sgshapiro}
12790792Sgshapiro
12890792Sgshapiro/*
12990792Sgshapiro**  SM_MBDB_TERMINATE -- terminate connection to the mailbox database
13090792Sgshapiro**
13190792Sgshapiro**	Because this function closes any cached file descriptors that
13290792Sgshapiro**	are being held open for the connection to the mailbox database,
13390792Sgshapiro**	it should be called for security reasons prior to dropping privileges
13490792Sgshapiro**	and execing another process.
13590792Sgshapiro**
13690792Sgshapiro**	Parameters:
13790792Sgshapiro**		none.
13890792Sgshapiro**
13990792Sgshapiro**	Results:
14090792Sgshapiro**		none.
14190792Sgshapiro*/
14290792Sgshapiro
14390792Sgshapirovoid
14490792Sgshapirosm_mbdb_terminate()
14590792Sgshapiro{
14698121Sgshapiro	if (SmMbdbType->mbdb_terminate != NULL)
14798121Sgshapiro		SmMbdbType->mbdb_terminate();
14890792Sgshapiro}
14990792Sgshapiro
15090792Sgshapiro/*
15190792Sgshapiro**  SM_MBDB_LOOKUP -- look up a local mail recipient, given name
15290792Sgshapiro**
15390792Sgshapiro**	Parameters:
15490792Sgshapiro**		name -- name of local mail recipient
15590792Sgshapiro**		user -- pointer to structure to fill in on success
15690792Sgshapiro**
15790792Sgshapiro**	Results:
15890792Sgshapiro**		On success, fill in *user and return EX_OK.
15990792Sgshapiro**		If the user does not exist, return EX_NOUSER.
16090792Sgshapiro**		If a temporary failure (eg, a network failure) occurred,
16190792Sgshapiro**		return EX_TEMPFAIL.  Otherwise return EX_OSERR.
16290792Sgshapiro*/
16390792Sgshapiro
16490792Sgshapiroint
16590792Sgshapirosm_mbdb_lookup(name, user)
16690792Sgshapiro	char *name;
16790792Sgshapiro	SM_MBDB_T *user;
16890792Sgshapiro{
16998121Sgshapiro	int ret = EX_NOUSER;
17098121Sgshapiro
17198121Sgshapiro	if (SmMbdbType->mbdb_lookup != NULL)
17298121Sgshapiro		ret = SmMbdbType->mbdb_lookup(name, user);
17398121Sgshapiro	return ret;
17490792Sgshapiro}
17590792Sgshapiro
17690792Sgshapiro/*
17790792Sgshapiro**  SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T
17890792Sgshapiro**
17990792Sgshapiro**	Parameters:
18090792Sgshapiro**		user -- destination user information structure
18190792Sgshapiro**		pw -- source passwd structure
18290792Sgshapiro**
18390792Sgshapiro**	Results:
18490792Sgshapiro**		none.
18590792Sgshapiro*/
18690792Sgshapiro
18790792Sgshapirovoid
18890792Sgshapirosm_mbdb_frompw(user, pw)
18990792Sgshapiro	SM_MBDB_T *user;
19090792Sgshapiro	struct passwd *pw;
19190792Sgshapiro{
19290792Sgshapiro	SM_REQUIRE(user != NULL);
19390792Sgshapiro	(void) sm_strlcpy(user->mbdb_name, pw->pw_name,
19490792Sgshapiro			  sizeof(user->mbdb_name));
19590792Sgshapiro	user->mbdb_uid = pw->pw_uid;
19690792Sgshapiro	user->mbdb_gid = pw->pw_gid;
19790792Sgshapiro	sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
19890792Sgshapiro		      sizeof(user->mbdb_fullname));
19990792Sgshapiro	(void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
20090792Sgshapiro			  sizeof(user->mbdb_homedir));
20190792Sgshapiro	(void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
20290792Sgshapiro			  sizeof(user->mbdb_shell));
20390792Sgshapiro}
20490792Sgshapiro
20590792Sgshapiro/*
20690792Sgshapiro**  SM_PWFULLNAME -- build full name of user from pw_gecos field.
20790792Sgshapiro**
20890792Sgshapiro**	This routine interprets the strange entry that would appear
20990792Sgshapiro**	in the GECOS field of the password file.
21090792Sgshapiro**
21190792Sgshapiro**	Parameters:
21290792Sgshapiro**		gecos -- name to build.
21390792Sgshapiro**		user -- the login name of this user (for &).
21490792Sgshapiro**		buf -- place to put the result.
21590792Sgshapiro**		buflen -- length of buf.
21690792Sgshapiro**
21790792Sgshapiro**	Returns:
21890792Sgshapiro**		none.
21990792Sgshapiro*/
22090792Sgshapiro
22194334Sgshapiro#if _FFR_HANDLE_ISO8859_GECOS
22294334Sgshapirostatic char Latin1ToASCII[128] =
22394334Sgshapiro{
22494334Sgshapiro	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
22594334Sgshapiro	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33,
22694334Sgshapiro	99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42,
22794334Sgshapiro	50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65,
22894334Sgshapiro	65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79,
22994334Sgshapiro	79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97,
23094334Sgshapiro	97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110,
23194334Sgshapiro	111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121
23294334Sgshapiro};
23394334Sgshapiro#endif /* _FFR_HANDLE_ISO8859_GECOS */
23494334Sgshapiro
23590792Sgshapirovoid
23690792Sgshapirosm_pwfullname(gecos, user, buf, buflen)
23790792Sgshapiro	register char *gecos;
23890792Sgshapiro	char *user;
23990792Sgshapiro	char *buf;
24090792Sgshapiro	size_t buflen;
24190792Sgshapiro{
24290792Sgshapiro	register char *p;
24390792Sgshapiro	register char *bp = buf;
24490792Sgshapiro
24590792Sgshapiro	if (*gecos == '*')
24690792Sgshapiro		gecos++;
24790792Sgshapiro
24890792Sgshapiro	/* copy gecos, interpolating & to be full name */
24990792Sgshapiro	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
25090792Sgshapiro	{
25190792Sgshapiro		if (bp >= &buf[buflen - 1])
25290792Sgshapiro		{
25390792Sgshapiro			/* buffer overflow -- just use login name */
25490792Sgshapiro			(void) sm_strlcpy(buf, user, buflen);
25590792Sgshapiro			return;
25690792Sgshapiro		}
25790792Sgshapiro		if (*p == '&')
25890792Sgshapiro		{
25990792Sgshapiro			/* interpolate full name */
26090792Sgshapiro			(void) sm_strlcpy(bp, user, buflen - (bp - buf));
26190792Sgshapiro			*bp = toupper(*bp);
26290792Sgshapiro			bp += strlen(bp);
26390792Sgshapiro		}
26490792Sgshapiro		else
26594334Sgshapiro		{
26694334Sgshapiro#if _FFR_HANDLE_ISO8859_GECOS
26794334Sgshapiro			if ((unsigned char) *p >= 128)
26894334Sgshapiro				*bp++ = Latin1ToASCII[(unsigned char) *p - 128];
26994334Sgshapiro			else
27094334Sgshapiro#endif /* _FFR_HANDLE_ISO8859_GECOS */
27194334Sgshapiro				*bp++ = *p;
27294334Sgshapiro		}
27390792Sgshapiro	}
27490792Sgshapiro	*bp = '\0';
27590792Sgshapiro}
27690792Sgshapiro
27790792Sgshapiro/*
27890792Sgshapiro**  /etc/passwd implementation.
27990792Sgshapiro*/
28090792Sgshapiro
28190792Sgshapiro/*
28290792Sgshapiro**  MBDB_PW_INITIALIZE -- initialize getpwnam() version
28390792Sgshapiro**
28490792Sgshapiro**	Parameters:
28590792Sgshapiro**		arg -- unused.
28690792Sgshapiro**
28790792Sgshapiro**	Results:
28890792Sgshapiro**		EX_OK.
28990792Sgshapiro*/
29090792Sgshapiro
29190792Sgshapiro/* ARGSUSED0 */
29290792Sgshapirostatic int
29390792Sgshapirombdb_pw_initialize(arg)
29490792Sgshapiro	char *arg;
29590792Sgshapiro{
29690792Sgshapiro	return EX_OK;
29790792Sgshapiro}
29890792Sgshapiro
29990792Sgshapiro/*
30090792Sgshapiro**  MBDB_PW_LOOKUP -- look up a local mail recipient, given name
30190792Sgshapiro**
30290792Sgshapiro**	Parameters:
30390792Sgshapiro**		name -- name of local mail recipient
30490792Sgshapiro**		user -- pointer to structure to fill in on success
30590792Sgshapiro**
30690792Sgshapiro**	Results:
30790792Sgshapiro**		On success, fill in *user and return EX_OK.
30890792Sgshapiro**		Failure: EX_NOUSER.
30990792Sgshapiro*/
31090792Sgshapiro
31190792Sgshapirostatic int
31290792Sgshapirombdb_pw_lookup(name, user)
31390792Sgshapiro	char *name;
31490792Sgshapiro	SM_MBDB_T *user;
31590792Sgshapiro{
31690792Sgshapiro	struct passwd *pw;
31790792Sgshapiro
31890792Sgshapiro#ifdef HESIOD
31990792Sgshapiro	/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
32090792Sgshapiro	{
32190792Sgshapiro		char *p;
32290792Sgshapiro
32390792Sgshapiro		for (p = name; *p != '\0'; p++)
32490792Sgshapiro			if (!isascii(*p) || !isdigit(*p))
32590792Sgshapiro				break;
32690792Sgshapiro		if (*p == '\0')
32790792Sgshapiro			return EX_NOUSER;
32890792Sgshapiro	}
32990792Sgshapiro#endif /* HESIOD */
33090792Sgshapiro
33190792Sgshapiro	errno = 0;
33290792Sgshapiro	pw = getpwnam(name);
33390792Sgshapiro	if (pw == NULL)
33490792Sgshapiro	{
335261363Sgshapiro#if _FFR_USE_GETPWNAM_ERRNO
33690792Sgshapiro		/*
337261363Sgshapiro		**  Only enable this code iff
338261363Sgshapiro		**  user unknown <-> getpwnam() == NULL && errno == 0
339261363Sgshapiro		**  (i.e., errno unchanged); see the POSIX spec.
34090792Sgshapiro		*/
341261363Sgshapiro
342261363Sgshapiro		if (errno != 0)
34390792Sgshapiro			return EX_TEMPFAIL;
344261363Sgshapiro#endif /* _FFR_USE_GETPWNAM_ERRNO */
34590792Sgshapiro		return EX_NOUSER;
34690792Sgshapiro	}
34790792Sgshapiro
34890792Sgshapiro	sm_mbdb_frompw(user, pw);
34990792Sgshapiro	return EX_OK;
35090792Sgshapiro}
35190792Sgshapiro
35290792Sgshapiro/*
35390792Sgshapiro**  MBDB_PW_TERMINATE -- terminate connection to the mailbox database
35490792Sgshapiro**
35590792Sgshapiro**	Parameters:
35690792Sgshapiro**		none.
35790792Sgshapiro**
35890792Sgshapiro**	Results:
35990792Sgshapiro**		none.
36090792Sgshapiro*/
36190792Sgshapiro
36290792Sgshapirostatic void
36390792Sgshapirombdb_pw_terminate()
36490792Sgshapiro{
36590792Sgshapiro	endpwent();
36690792Sgshapiro}
36790792Sgshapiro
36890792Sgshapiro#if LDAPMAP
36990792Sgshapiro# if _LDAP_EXAMPLE_
37090792Sgshapiro/*
37190792Sgshapiro**  LDAP example implementation based on RFC 2307, "An Approach for Using
37290792Sgshapiro**  LDAP as a Network Information Service":
37390792Sgshapiro**
37490792Sgshapiro**	( nisSchema.1.0 NAME 'uidNumber'
37590792Sgshapiro**	  DESC 'An integer uniquely identifying a user in an
37690792Sgshapiro**		administrative domain'
37790792Sgshapiro**	  EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
37890792Sgshapiro**
37990792Sgshapiro**	( nisSchema.1.1 NAME 'gidNumber'
38090792Sgshapiro**	  DESC 'An integer uniquely identifying a group in an
38190792Sgshapiro**		administrative domain'
38290792Sgshapiro**	  EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
38390792Sgshapiro**
38490792Sgshapiro**	( nisSchema.1.2 NAME 'gecos'
38590792Sgshapiro**	  DESC 'The GECOS field; the common name'
38690792Sgshapiro**	  EQUALITY caseIgnoreIA5Match
38790792Sgshapiro**	  SUBSTRINGS caseIgnoreIA5SubstringsMatch
38890792Sgshapiro**	  SYNTAX 'IA5String' SINGLE-VALUE )
38990792Sgshapiro**
39090792Sgshapiro**	( nisSchema.1.3 NAME 'homeDirectory'
39190792Sgshapiro**	  DESC 'The absolute path to the home directory'
39290792Sgshapiro**	  EQUALITY caseExactIA5Match
39390792Sgshapiro**	  SYNTAX 'IA5String' SINGLE-VALUE )
39490792Sgshapiro**
39590792Sgshapiro**	( nisSchema.1.4 NAME 'loginShell'
39690792Sgshapiro**	  DESC 'The path to the login shell'
39790792Sgshapiro**	  EQUALITY caseExactIA5Match
39890792Sgshapiro**	  SYNTAX 'IA5String' SINGLE-VALUE )
39990792Sgshapiro**
40090792Sgshapiro**	( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
40190792Sgshapiro**	  DESC 'Abstraction of an account with POSIX attributes'
40290792Sgshapiro**	  MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
40390792Sgshapiro**	  MAY ( userPassword $ loginShell $ gecos $ description ) )
40490792Sgshapiro**
40590792Sgshapiro*/
40690792Sgshapiro
40790792Sgshapiro#  define MBDB_LDAP_LABEL		"MailboxDatabase"
40890792Sgshapiro
40990792Sgshapiro#  ifndef MBDB_LDAP_FILTER
41090792Sgshapiro#   define MBDB_LDAP_FILTER		"(&(objectClass=posixAccount)(uid=%0))"
41190792Sgshapiro#  endif /* MBDB_LDAP_FILTER */
41290792Sgshapiro
41390792Sgshapiro#  ifndef MBDB_DEFAULT_LDAP_BASEDN
41490792Sgshapiro#   define MBDB_DEFAULT_LDAP_BASEDN	NULL
41590792Sgshapiro#  endif /* MBDB_DEFAULT_LDAP_BASEDN */
41690792Sgshapiro
41790792Sgshapiro#  ifndef MBDB_DEFAULT_LDAP_SERVER
41890792Sgshapiro#   define MBDB_DEFAULT_LDAP_SERVER	NULL
41990792Sgshapiro#  endif /* MBDB_DEFAULT_LDAP_SERVER */
42090792Sgshapiro
42190792Sgshapiro/*
42290792Sgshapiro**  MBDB_LDAP_INITIALIZE -- initialize LDAP version
42390792Sgshapiro**
42490792Sgshapiro**	Parameters:
42590792Sgshapiro**		arg -- LDAP specification
42690792Sgshapiro**
42790792Sgshapiro**	Results:
42890792Sgshapiro**		EX_OK on success, or an EX_* code on failure.
42990792Sgshapiro*/
43090792Sgshapiro
43190792Sgshapirostatic int
43290792Sgshapirombdb_ldap_initialize(arg)
43390792Sgshapiro	char *arg;
43490792Sgshapiro{
43590792Sgshapiro	sm_ldap_clear(&LDAPLMAP);
43690792Sgshapiro	LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
437132943Sgshapiro	LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
43890792Sgshapiro	LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
43990792Sgshapiro
44090792Sgshapiro	/* Only want one match */
44190792Sgshapiro	LDAPLMAP.ldap_sizelimit = 1;
44290792Sgshapiro
443132943Sgshapiro	/* interpolate new ldap_base and ldap_host from arg if given */
44490792Sgshapiro	if (arg != NULL && *arg != '\0')
44590792Sgshapiro	{
44690792Sgshapiro		char *new;
44790792Sgshapiro		char *sep;
44890792Sgshapiro		size_t len;
44990792Sgshapiro
45090792Sgshapiro		len = strlen(arg) + 1;
45190792Sgshapiro		new = sm_malloc(len);
45290792Sgshapiro		if (new == NULL)
45390792Sgshapiro			return EX_TEMPFAIL;
45490792Sgshapiro		(void) sm_strlcpy(new, arg, len);
45590792Sgshapiro		sep = strrchr(new, '@');
45690792Sgshapiro		if (sep != NULL)
45790792Sgshapiro		{
45890792Sgshapiro			*sep++ = '\0';
459132943Sgshapiro			LDAPLMAP.ldap_host = sep;
46090792Sgshapiro		}
46190792Sgshapiro		LDAPLMAP.ldap_base = new;
46290792Sgshapiro	}
46390792Sgshapiro	return EX_OK;
46490792Sgshapiro}
46590792Sgshapiro
46690792Sgshapiro
46790792Sgshapiro/*
46890792Sgshapiro**  MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
46990792Sgshapiro**
47090792Sgshapiro**	Parameters:
47190792Sgshapiro**		name -- name of local mail recipient
47290792Sgshapiro**		user -- pointer to structure to fill in on success
47390792Sgshapiro**
47490792Sgshapiro**	Results:
47590792Sgshapiro**		On success, fill in *user and return EX_OK.
47690792Sgshapiro**		Failure: EX_NOUSER.
47790792Sgshapiro*/
47890792Sgshapiro
47990792Sgshapiro#define NEED_FULLNAME	0x01
48090792Sgshapiro#define NEED_HOMEDIR	0x02
48190792Sgshapiro#define NEED_SHELL	0x04
48290792Sgshapiro#define NEED_UID	0x08
48390792Sgshapiro#define NEED_GID	0x10
48490792Sgshapiro
48590792Sgshapirostatic int
48690792Sgshapirombdb_ldap_lookup(name, user)
48790792Sgshapiro	char *name;
48890792Sgshapiro	SM_MBDB_T *user;
48990792Sgshapiro{
49090792Sgshapiro	int msgid;
49190792Sgshapiro	int need;
49290792Sgshapiro	int ret;
49390792Sgshapiro	int save_errno;
49490792Sgshapiro	LDAPMessage *entry;
49590792Sgshapiro	BerElement *ber;
49690792Sgshapiro	char *attr = NULL;
49790792Sgshapiro
49890792Sgshapiro	if (strlen(name) >= sizeof(user->mbdb_name))
49990792Sgshapiro	{
50090792Sgshapiro		errno = EINVAL;
50190792Sgshapiro		return EX_NOUSER;
50290792Sgshapiro	}
50390792Sgshapiro
50490792Sgshapiro	if (LDAPLMAP.ldap_filter == NULL)
50590792Sgshapiro	{
50690792Sgshapiro		/* map not initialized, but don't have arg here */
50790792Sgshapiro		errno = EFAULT;
50890792Sgshapiro		return EX_TEMPFAIL;
50990792Sgshapiro	}
51090792Sgshapiro
511110560Sgshapiro	if (LDAPLMAP.ldap_pid != getpid())
512110560Sgshapiro	{
513110560Sgshapiro		/* re-open map in this child process */
514110560Sgshapiro		LDAPLMAP.ldap_ld = NULL;
515110560Sgshapiro	}
516110560Sgshapiro
51790792Sgshapiro	if (LDAPLMAP.ldap_ld == NULL)
51890792Sgshapiro	{
51990792Sgshapiro		/* map not open, try to open now */
52090792Sgshapiro		if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
52190792Sgshapiro			return EX_TEMPFAIL;
52290792Sgshapiro	}
52390792Sgshapiro
52490792Sgshapiro	sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
52590792Sgshapiro	msgid = sm_ldap_search(&LDAPLMAP, name);
52690792Sgshapiro	if (msgid == -1)
52790792Sgshapiro	{
52890792Sgshapiro		save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
52990792Sgshapiro#  ifdef LDAP_SERVER_DOWN
53090792Sgshapiro		if (errno == LDAP_SERVER_DOWN)
53190792Sgshapiro		{
53290792Sgshapiro			/* server disappeared, try reopen on next search */
53390792Sgshapiro			sm_ldap_close(&LDAPLMAP);
53490792Sgshapiro		}
53590792Sgshapiro#  endif /* LDAP_SERVER_DOWN */
53690792Sgshapiro		errno = save_errno;
53790792Sgshapiro		return EX_TEMPFAIL;
53890792Sgshapiro	}
53990792Sgshapiro
54090792Sgshapiro	/* Get results */
54190792Sgshapiro	ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
54290792Sgshapiro			  (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
54390792Sgshapiro			   &(LDAPLMAP.ldap_timeout)),
54490792Sgshapiro			  &(LDAPLMAP.ldap_res));
54590792Sgshapiro
54690792Sgshapiro	if (ret != LDAP_RES_SEARCH_RESULT &&
54790792Sgshapiro	    ret != LDAP_RES_SEARCH_ENTRY)
54890792Sgshapiro	{
54990792Sgshapiro		if (ret == 0)
55090792Sgshapiro			errno = ETIMEDOUT;
55190792Sgshapiro		else
55290792Sgshapiro			errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
55390792Sgshapiro		ret = EX_TEMPFAIL;
55490792Sgshapiro		goto abort;
55590792Sgshapiro	}
55690792Sgshapiro
55790792Sgshapiro	entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
55890792Sgshapiro	if (entry == NULL)
55990792Sgshapiro	{
560203004Sgshapiro		int rc;
561203004Sgshapiro
562203004Sgshapiro		/*
563203004Sgshapiro		**  We may have gotten an LDAP_RES_SEARCH_RESULT response
564203004Sgshapiro		**  with an error inside it, so we have to extract that
565203004Sgshapiro		**  with ldap_parse_result().  This can happen when talking
566203004Sgshapiro		**  to an LDAP proxy whose backend has gone down.
567203004Sgshapiro		*/
568203004Sgshapiro
569203004Sgshapiro		save_errno = ldap_parse_result(LDAPLMAP.ldap_ld,
570203004Sgshapiro					       LDAPLMAP.ldap_res, &rc, NULL,
571203004Sgshapiro					       NULL, NULL, NULL, 0);
57290792Sgshapiro		if (save_errno == LDAP_SUCCESS)
573203004Sgshapiro			save_errno = rc;
574203004Sgshapiro		if (save_errno == LDAP_SUCCESS)
57590792Sgshapiro		{
57690792Sgshapiro			errno = ENOENT;
57790792Sgshapiro			ret = EX_NOUSER;
57890792Sgshapiro		}
57990792Sgshapiro		else
58090792Sgshapiro		{
58190792Sgshapiro			errno = save_errno;
58290792Sgshapiro			ret = EX_TEMPFAIL;
58390792Sgshapiro		}
58490792Sgshapiro		goto abort;
58590792Sgshapiro	}
58690792Sgshapiro
58790792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
58890792Sgshapiro	/*
58990792Sgshapiro	**  Reset value to prevent lingering
59090792Sgshapiro	**  LDAP_DECODING_ERROR due to
59190792Sgshapiro	**  OpenLDAP 1.X's hack (see below)
59290792Sgshapiro	*/
59390792Sgshapiro
59490792Sgshapiro	LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
59590792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
59690792Sgshapiro
59790792Sgshapiro	ret = EX_OK;
59890792Sgshapiro	need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
59990792Sgshapiro	for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
60090792Sgshapiro	     attr != NULL;
60190792Sgshapiro	     attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
60290792Sgshapiro	{
60390792Sgshapiro		char **vals;
60490792Sgshapiro
60590792Sgshapiro		vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
60690792Sgshapiro		if (vals == NULL)
60790792Sgshapiro		{
60890792Sgshapiro			errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
60990792Sgshapiro			if (errno == LDAP_SUCCESS)
61090792Sgshapiro			{
61190792Sgshapiro				ldap_memfree(attr);
61290792Sgshapiro				continue;
61390792Sgshapiro			}
61490792Sgshapiro
61590792Sgshapiro			/* Must be an error */
61690792Sgshapiro			errno += E_LDAPBASE;
61790792Sgshapiro			ret = EX_TEMPFAIL;
61890792Sgshapiro			goto abort;
61990792Sgshapiro		}
62090792Sgshapiro
62190792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
62290792Sgshapiro		/*
62390792Sgshapiro		**  Reset value to prevent lingering
62490792Sgshapiro		**  LDAP_DECODING_ERROR due to
62590792Sgshapiro		**  OpenLDAP 1.X's hack (see below)
62690792Sgshapiro		*/
62790792Sgshapiro
62890792Sgshapiro		LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
62990792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
63090792Sgshapiro
63190792Sgshapiro		if (vals[0] == NULL || vals[0][0] == '\0')
63290792Sgshapiro			goto skip;
63390792Sgshapiro
63490792Sgshapiro		if (strcasecmp(attr, "gecos") == 0)
63590792Sgshapiro		{
63690792Sgshapiro			if (!bitset(NEED_FULLNAME, need) ||
63790792Sgshapiro			    strlen(vals[0]) >= sizeof(user->mbdb_fullname))
63890792Sgshapiro				goto skip;
63990792Sgshapiro
64090792Sgshapiro			sm_pwfullname(vals[0], name, user->mbdb_fullname,
64190792Sgshapiro				      sizeof(user->mbdb_fullname));
64290792Sgshapiro			need &= ~NEED_FULLNAME;
64390792Sgshapiro		}
64490792Sgshapiro		else if (strcasecmp(attr, "homeDirectory") == 0)
64590792Sgshapiro		{
64690792Sgshapiro			if (!bitset(NEED_HOMEDIR, need) ||
64790792Sgshapiro			    strlen(vals[0]) >= sizeof(user->mbdb_homedir))
64890792Sgshapiro				goto skip;
64990792Sgshapiro
65090792Sgshapiro			(void) sm_strlcpy(user->mbdb_homedir, vals[0],
65190792Sgshapiro					  sizeof(user->mbdb_homedir));
65290792Sgshapiro			need &= ~NEED_HOMEDIR;
65390792Sgshapiro		}
65490792Sgshapiro		else if (strcasecmp(attr, "loginShell") == 0)
65590792Sgshapiro		{
65690792Sgshapiro			if (!bitset(NEED_SHELL, need) ||
65790792Sgshapiro			    strlen(vals[0]) >= sizeof(user->mbdb_shell))
65890792Sgshapiro				goto skip;
65990792Sgshapiro
66090792Sgshapiro			(void) sm_strlcpy(user->mbdb_shell, vals[0],
66190792Sgshapiro					  sizeof(user->mbdb_shell));
66290792Sgshapiro			need &= ~NEED_SHELL;
66390792Sgshapiro		}
66490792Sgshapiro		else if (strcasecmp(attr, "uidNumber") == 0)
66590792Sgshapiro		{
66690792Sgshapiro			char *p;
66790792Sgshapiro
66890792Sgshapiro			if (!bitset(NEED_UID, need))
66990792Sgshapiro				goto skip;
67090792Sgshapiro
67190792Sgshapiro			for (p = vals[0]; *p != '\0'; p++)
67290792Sgshapiro			{
67390792Sgshapiro				/* allow negative numbers */
67490792Sgshapiro				if (p == vals[0] && *p == '-')
67590792Sgshapiro				{
67690792Sgshapiro					/* but not simply '-' */
67790792Sgshapiro					if (*(p + 1) == '\0')
67890792Sgshapiro						goto skip;
67990792Sgshapiro				}
68090792Sgshapiro				else if (!isascii(*p) || !isdigit(*p))
68190792Sgshapiro					goto skip;
68290792Sgshapiro			}
68390792Sgshapiro			user->mbdb_uid = atoi(vals[0]);
68490792Sgshapiro			need &= ~NEED_UID;
68590792Sgshapiro		}
68690792Sgshapiro		else if (strcasecmp(attr, "gidNumber") == 0)
68790792Sgshapiro		{
68890792Sgshapiro			char *p;
68990792Sgshapiro
69090792Sgshapiro			if (!bitset(NEED_GID, need))
69190792Sgshapiro				goto skip;
69290792Sgshapiro
69390792Sgshapiro			for (p = vals[0]; *p != '\0'; p++)
69490792Sgshapiro			{
69590792Sgshapiro				/* allow negative numbers */
69690792Sgshapiro				if (p == vals[0] && *p == '-')
69790792Sgshapiro				{
69890792Sgshapiro					/* but not simply '-' */
69990792Sgshapiro					if (*(p + 1) == '\0')
70090792Sgshapiro						goto skip;
70190792Sgshapiro				}
70290792Sgshapiro				else if (!isascii(*p) || !isdigit(*p))
70390792Sgshapiro					goto skip;
70490792Sgshapiro			}
70590792Sgshapiro			user->mbdb_gid = atoi(vals[0]);
70690792Sgshapiro			need &= ~NEED_GID;
70790792Sgshapiro		}
70890792Sgshapiro
70990792Sgshapiroskip:
71090792Sgshapiro		ldap_value_free(vals);
71190792Sgshapiro		ldap_memfree(attr);
71290792Sgshapiro	}
71390792Sgshapiro
71490792Sgshapiro	errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
71590792Sgshapiro
71690792Sgshapiro	/*
71790792Sgshapiro	**  We check errno != LDAP_DECODING_ERROR since
71890792Sgshapiro	**  OpenLDAP 1.X has a very ugly *undocumented*
71990792Sgshapiro	**  hack of returning this error code from
72090792Sgshapiro	**  ldap_next_attribute() if the library freed the
72190792Sgshapiro	**  ber attribute.  See:
72290792Sgshapiro	**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
72390792Sgshapiro	*/
72490792Sgshapiro
72590792Sgshapiro	if (errno != LDAP_SUCCESS &&
72690792Sgshapiro	    errno != LDAP_DECODING_ERROR)
72790792Sgshapiro	{
72890792Sgshapiro		/* Must be an error */
72990792Sgshapiro		errno += E_LDAPBASE;
73090792Sgshapiro		ret = EX_TEMPFAIL;
73190792Sgshapiro		goto abort;
73290792Sgshapiro	}
73390792Sgshapiro
73490792Sgshapiro abort:
73590792Sgshapiro	save_errno = errno;
73690792Sgshapiro	if (attr != NULL)
73790792Sgshapiro	{
73890792Sgshapiro		ldap_memfree(attr);
73990792Sgshapiro		attr = NULL;
74090792Sgshapiro	}
74190792Sgshapiro	if (LDAPLMAP.ldap_res != NULL)
74290792Sgshapiro	{
74390792Sgshapiro		ldap_msgfree(LDAPLMAP.ldap_res);
74490792Sgshapiro		LDAPLMAP.ldap_res = NULL;
74590792Sgshapiro	}
74690792Sgshapiro	if (ret == EX_OK)
74790792Sgshapiro	{
74890792Sgshapiro		if (need == 0)
74990792Sgshapiro		{
75090792Sgshapiro			(void) sm_strlcpy(user->mbdb_name, name,
75190792Sgshapiro					  sizeof(user->mbdb_name));
75290792Sgshapiro			save_errno = 0;
75390792Sgshapiro		}
75490792Sgshapiro		else
75590792Sgshapiro		{
75690792Sgshapiro			ret = EX_NOUSER;
75790792Sgshapiro			save_errno = EINVAL;
75890792Sgshapiro		}
75990792Sgshapiro	}
76090792Sgshapiro	errno = save_errno;
76190792Sgshapiro	return ret;
76290792Sgshapiro}
76390792Sgshapiro
76490792Sgshapiro/*
76590792Sgshapiro**  MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
76690792Sgshapiro**
76790792Sgshapiro**	Parameters:
76890792Sgshapiro**		none.
76990792Sgshapiro**
77090792Sgshapiro**	Results:
77190792Sgshapiro**		none.
77290792Sgshapiro*/
77390792Sgshapiro
77490792Sgshapirostatic void
77590792Sgshapirombdb_ldap_terminate()
77690792Sgshapiro{
77790792Sgshapiro	sm_ldap_close(&LDAPLMAP);
77890792Sgshapiro}
77990792Sgshapiro# endif /* _LDAP_EXAMPLE_ */
78090792Sgshapiro#endif /* LDAPMAP */
781