mbdb.c revision 261363
1101043Sdes/*
2228991Suqs * Copyright (c) 2001-2003,2009 Proofpoint, Inc. and its suppliers.
3101043Sdes *      All rights reserved.
4101043Sdes *
5101043Sdes * By using this file, you agree to the terms and conditions set
6101043Sdes * forth in the LICENSE file which can be found at the top level of
7101043Sdes * the sendmail distribution.
8101043Sdes */
9101043Sdes
10101043Sdes#include <sm/gen.h>
11101043SdesSM_RCSID("@(#)$Id: mbdb.c,v 1.43 2014/01/08 17:03:15 ca Exp $")
12101043Sdes
13101043Sdes#include <sys/param.h>
14101043Sdes
15101043Sdes#include <ctype.h>
16101043Sdes#include <errno.h>
17101043Sdes#include <pwd.h>
18101043Sdes#include <stdlib.h>
19101043Sdes#include <setjmp.h>
20101043Sdes#include <unistd.h>
21101043Sdes
22101043Sdes#include <sm/limits.h>
23101043Sdes#include <sm/conf.h>
24101043Sdes#include <sm/assert.h>
25101043Sdes#include <sm/bitops.h>
26101043Sdes#include <sm/errstring.h>
27101043Sdes#include <sm/heap.h>
28101043Sdes#include <sm/mbdb.h>
29101043Sdes#include <sm/string.h>
30101043Sdes# ifdef EX_OK
31101043Sdes#  undef EX_OK			/* for SVr4.2 SMP */
32101043Sdes# endif /* EX_OK */
33101043Sdes#include <sm/sysexits.h>
34101043Sdes
35101043Sdes#if LDAPMAP
36101043Sdes# if _LDAP_EXAMPLE_
37101043Sdes#  include <sm/ldap.h>
38101043Sdes# endif /* _LDAP_EXAMPLE_ */
39101043Sdes#endif /* LDAPMAP */
40101043Sdes
41101043Sdestypedef struct
42101163Sdes{
43101163Sdes	char	*mbdb_typename;
44101043Sdes	int	(*mbdb_initialize) __P((char *));
45101043Sdes	int	(*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
46284441Stuexen	void	(*mbdb_terminate) __P((void));
47101043Sdes} SM_MBDB_TYPE_T;
48294225Stuexen
49294225Stuexenstatic int	mbdb_pw_initialize __P((char *));
50101043Sdesstatic int	mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
51101043Sdesstatic void	mbdb_pw_terminate __P((void));
52101043Sdes
53101043Sdes#if LDAPMAP
54101043Sdes# if _LDAP_EXAMPLE_
55101043Sdesstatic struct sm_ldap_struct LDAPLMAP;
56101043Sdesstatic int	mbdb_ldap_initialize __P((char *));
57336039Sjamiestatic int	mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
58101043Sdesstatic void	mbdb_ldap_terminate __P((void));
59101043Sdes# endif /* _LDAP_EXAMPLE_ */
60101043Sdes#endif /* LDAPMAP */
61101043Sdes
62101043Sdesstatic SM_MBDB_TYPE_T SmMbdbTypes[] =
63101043Sdes{
64101043Sdes	{ "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
65101043Sdes#if LDAPMAP
66285826Shrs# if _LDAP_EXAMPLE_
67285826Shrs	{ "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
68285826Shrs# endif /* _LDAP_EXAMPLE_ */
69285826Shrs#endif /* LDAPMAP */
70285826Shrs	{ NULL, NULL, NULL, NULL }
71101043Sdes};
72101043Sdes
73101043Sdesstatic SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
74235870Sthompsa
75179115Sbms/*
76101043Sdes**  SM_MBDB_INITIALIZE -- specify which mailbox database to use
77294225Stuexen**
78101043Sdes**	If this function is not called, then the "pw" implementation
79101043Sdes**	is used by default; this implementation uses getpwnam().
80101043Sdes**
81164201Skeramida**	Parameters:
82164201Skeramida**		mbdb -- Which mailbox database to use.
83164201Skeramida**			The argument has the form "name" or "name.arg".
84284441Stuexen**			"pw" means use getpwnam().
85285826Shrs**
86164201Skeramida**	Results:
87164201Skeramida**		EX_OK on success, or an EX_* code on failure.
88164201Skeramida*/
89164201Skeramida
90101043Sdesint
91101043Sdessm_mbdb_initialize(mbdb)
92101043Sdes	char *mbdb;
93101043Sdes{
94101043Sdes	size_t namelen;
95101043Sdes	int err;
96284441Stuexen	char *name;
97284441Stuexen	char *arg;
98284441Stuexen	SM_MBDB_TYPE_T *t;
99284441Stuexen
100284441Stuexen	SM_REQUIRE(mbdb != NULL);
101101043Sdes
102101043Sdes	name = mbdb;
103101043Sdes	arg = strchr(mbdb, '.');
104230512Sjilles	if (arg == NULL)
105101043Sdes		namelen = strlen(name);
106101043Sdes	else
107101043Sdes	{
108294225Stuexen		namelen = arg - name;
109101043Sdes		++arg;
110284441Stuexen	}
111284441Stuexen
112101043Sdes	for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
113101043Sdes	{
114101043Sdes		if (strlen(t->mbdb_typename) == namelen &&
115101043Sdes		    strncmp(name, t->mbdb_typename, namelen) == 0)
116101043Sdes		{
117101043Sdes			err = EX_OK;
118101043Sdes			if (t->mbdb_initialize != NULL)
119101043Sdes				err = t->mbdb_initialize(arg);
120101043Sdes			if (err == EX_OK)
121101043Sdes				SmMbdbType = t;
122101043Sdes			return err;
123101043Sdes		}
124101043Sdes	}
125101043Sdes	return EX_UNAVAILABLE;
126101043Sdes}
127101043Sdes
128101043Sdes/*
129101043Sdes**  SM_MBDB_TERMINATE -- terminate connection to the mailbox database
130101043Sdes**
131101043Sdes**	Because this function closes any cached file descriptors that
132101043Sdes**	are being held open for the connection to the mailbox database,
133101043Sdes**	it should be called for security reasons prior to dropping privileges
134101043Sdes**	and execing another process.
135164201Skeramida**
136164201Skeramida**	Parameters:
137164201Skeramida**		none.
138164201Skeramida**
139164201Skeramida**	Results:
140164201Skeramida**		none.
141164201Skeramida*/
142164201Skeramida
143164201Skeramidavoid
144164201Skeramidasm_mbdb_terminate()
145164201Skeramida{
146164201Skeramida	if (SmMbdbType->mbdb_terminate != NULL)
147164201Skeramida		SmMbdbType->mbdb_terminate();
148164201Skeramida}
149164201Skeramida
150164201Skeramida/*
151164201Skeramida**  SM_MBDB_LOOKUP -- look up a local mail recipient, given name
152285826Shrs**
153285826Shrs**	Parameters:
154164201Skeramida**		name -- name of local mail recipient
155164201Skeramida**		user -- pointer to structure to fill in on success
156164201Skeramida**
157164201Skeramida**	Results:
158164201Skeramida**		On success, fill in *user and return EX_OK.
159164201Skeramida**		If the user does not exist, return EX_NOUSER.
160164201Skeramida**		If a temporary failure (eg, a network failure) occurred,
161164201Skeramida**		return EX_TEMPFAIL.  Otherwise return EX_OSERR.
162164201Skeramida*/
163164201Skeramida
164164201Skeramidaint
165164201Skeramidasm_mbdb_lookup(name, user)
166164201Skeramida	char *name;
167164201Skeramida	SM_MBDB_T *user;
168164201Skeramida{
169164201Skeramida	int ret = EX_NOUSER;
170164201Skeramida
171164201Skeramida	if (SmMbdbType->mbdb_lookup != NULL)
172164201Skeramida		ret = SmMbdbType->mbdb_lookup(name, user);
173164201Skeramida	return ret;
174164201Skeramida}
175164201Skeramida
176164201Skeramida/*
177164201Skeramida**  SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T
178164201Skeramida**
179164201Skeramida**	Parameters:
180164201Skeramida**		user -- destination user information structure
181164201Skeramida**		pw -- source passwd structure
182164201Skeramida**
183285826Shrs**	Results:
184164201Skeramida**		none.
185164201Skeramida*/
186164201Skeramida
187164201Skeramidavoid
188164201Skeramidasm_mbdb_frompw(user, pw)
189164201Skeramida	SM_MBDB_T *user;
190164201Skeramida	struct passwd *pw;
191164201Skeramida{
192164201Skeramida	SM_REQUIRE(user != NULL);
193164201Skeramida	(void) sm_strlcpy(user->mbdb_name, pw->pw_name,
194164201Skeramida			  sizeof(user->mbdb_name));
195101043Sdes	user->mbdb_uid = pw->pw_uid;
196101043Sdes	user->mbdb_gid = pw->pw_gid;
197101043Sdes	sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
198101043Sdes		      sizeof(user->mbdb_fullname));
199101043Sdes	(void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
200101043Sdes			  sizeof(user->mbdb_homedir));
201101043Sdes	(void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
202114853Srobert			  sizeof(user->mbdb_shell));
203101043Sdes}
204101043Sdes
205101043Sdes/*
206101043Sdes**  SM_PWFULLNAME -- build full name of user from pw_gecos field.
207101043Sdes**
208101043Sdes**	This routine interprets the strange entry that would appear
209101043Sdes**	in the GECOS field of the password file.
210101043Sdes**
211101043Sdes**	Parameters:
212101043Sdes**		gecos -- name to build.
213101043Sdes**		user -- the login name of this user (for &).
214101043Sdes**		buf -- place to put the result.
215101043Sdes**		buflen -- length of buf.
216101043Sdes**
217101043Sdes**	Returns:
218101043Sdes**		none.
219101043Sdes*/
220101043Sdes
221101043Sdes#if _FFR_HANDLE_ISO8859_GECOS
222101043Sdesstatic char Latin1ToASCII[128] =
223101043Sdes{
224101043Sdes	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
225101043Sdes	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33,
226101043Sdes	99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42,
227101043Sdes	50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65,
228101043Sdes	65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79,
229101043Sdes	79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97,
230101043Sdes	97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110,
231101043Sdes	111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121
232101043Sdes};
233101043Sdes#endif /* _FFR_HANDLE_ISO8859_GECOS */
234101043Sdes
235101043Sdesvoid
236101043Sdessm_pwfullname(gecos, user, buf, buflen)
237101043Sdes	register char *gecos;
238101043Sdes	char *user;
239101043Sdes	char *buf;
240285826Shrs	size_t buflen;
241101043Sdes{
242101043Sdes	register char *p;
243101043Sdes	register char *bp = buf;
244101043Sdes
245285826Shrs	if (*gecos == '*')
246101043Sdes		gecos++;
247101043Sdes
248285826Shrs	/* copy gecos, interpolating & to be full name */
249285826Shrs	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
250101043Sdes	{
251101043Sdes		if (bp >= &buf[buflen - 1])
252101043Sdes		{
253101043Sdes			/* buffer overflow -- just use login name */
254101043Sdes			(void) sm_strlcpy(buf, user, buflen);
255285826Shrs			return;
256285826Shrs		}
257101043Sdes		if (*p == '&')
258101043Sdes		{
259101043Sdes			/* interpolate full name */
260285826Shrs			(void) sm_strlcpy(bp, user, buflen - (bp - buf));
261285826Shrs			*bp = toupper(*bp);
262285826Shrs			bp += strlen(bp);
263285826Shrs		}
264285826Shrs		else
265285826Shrs		{
266101043Sdes#if _FFR_HANDLE_ISO8859_GECOS
267101043Sdes			if ((unsigned char) *p >= 128)
268101043Sdes				*bp++ = Latin1ToASCII[(unsigned char) *p - 128];
269101043Sdes			else
270101043Sdes#endif /* _FFR_HANDLE_ISO8859_GECOS */
271101043Sdes				*bp++ = *p;
272101043Sdes		}
273284694Stuexen	}
274284694Stuexen	*bp = '\0';
275284694Stuexen}
276284694Stuexen
277284694Stuexen/*
278284694Stuexen**  /etc/passwd implementation.
279284694Stuexen*/
280284694Stuexen
281284694Stuexen/*
282284694Stuexen**  MBDB_PW_INITIALIZE -- initialize getpwnam() version
283284694Stuexen**
284284694Stuexen**	Parameters:
285284694Stuexen**		arg -- unused.
286284694Stuexen**
287284694Stuexen**	Results:
288284694Stuexen**		EX_OK.
289284694Stuexen*/
290284694Stuexen
291284694Stuexen/* ARGSUSED0 */
292284694Stuexenstatic int
293284441Stuexenmbdb_pw_initialize(arg)
294284441Stuexen	char *arg;
295284441Stuexen{
296284441Stuexen	return EX_OK;
297284441Stuexen}
298284441Stuexen
299284441Stuexen/*
300284441Stuexen**  MBDB_PW_LOOKUP -- look up a local mail recipient, given name
301284441Stuexen**
302284441Stuexen**	Parameters:
303284441Stuexen**		name -- name of local mail recipient
304284441Stuexen**		user -- pointer to structure to fill in on success
305284441Stuexen**
306284441Stuexen**	Results:
307284441Stuexen**		On success, fill in *user and return EX_OK.
308284441Stuexen**		Failure: EX_NOUSER.
309284441Stuexen*/
310284441Stuexen
311284441Stuexenstatic int
312284441Stuexenmbdb_pw_lookup(name, user)
313284441Stuexen	char *name;
314284441Stuexen	SM_MBDB_T *user;
315284441Stuexen{
316284441Stuexen	struct passwd *pw;
317284441Stuexen
318284441Stuexen#ifdef HESIOD
319284441Stuexen	/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
320284441Stuexen	{
321284441Stuexen		char *p;
322284441Stuexen
323284441Stuexen		for (p = name; *p != '\0'; p++)
324284441Stuexen			if (!isascii(*p) || !isdigit(*p))
325284441Stuexen				break;
326284441Stuexen		if (*p == '\0')
327284441Stuexen			return EX_NOUSER;
328284441Stuexen	}
329284441Stuexen#endif /* HESIOD */
330284441Stuexen
331284441Stuexen	errno = 0;
332284441Stuexen	pw = getpwnam(name);
333284441Stuexen	if (pw == NULL)
334284441Stuexen	{
335284441Stuexen#if _FFR_USE_GETPWNAM_ERRNO
336294232Stuexen		/*
337294231Stuexen		**  Only enable this code iff
338294231Stuexen		**  user unknown <-> getpwnam() == NULL && errno == 0
339294231Stuexen		**  (i.e., errno unchanged); see the POSIX spec.
340284441Stuexen		*/
341284441Stuexen
342284441Stuexen		if (errno != 0)
343284441Stuexen			return EX_TEMPFAIL;
344284441Stuexen#endif /* _FFR_USE_GETPWNAM_ERRNO */
345284441Stuexen		return EX_NOUSER;
346284441Stuexen	}
347284441Stuexen
348284441Stuexen	sm_mbdb_frompw(user, pw);
349284441Stuexen	return EX_OK;
350284441Stuexen}
351284441Stuexen
352284441Stuexen/*
353284441Stuexen**  MBDB_PW_TERMINATE -- terminate connection to the mailbox database
354284441Stuexen**
355284441Stuexen**	Parameters:
356284441Stuexen**		none.
357284441Stuexen**
358284441Stuexen**	Results:
359284441Stuexen**		none.
360284441Stuexen*/
361284441Stuexen
362284441Stuexenstatic void
363284441Stuexenmbdb_pw_terminate()
364284441Stuexen{
365284441Stuexen	endpwent();
366284441Stuexen}
367284441Stuexen
368284441Stuexen#if LDAPMAP
369284441Stuexen# if _LDAP_EXAMPLE_
370284441Stuexen/*
371284441Stuexen**  LDAP example implementation based on RFC 2307, "An Approach for Using
372284441Stuexen**  LDAP as a Network Information Service":
373284441Stuexen**
374284441Stuexen**	( nisSchema.1.0 NAME 'uidNumber'
375284441Stuexen**	  DESC 'An integer uniquely identifying a user in an
376284441Stuexen**		administrative domain'
377293290Sbdrewery**	  EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
378284441Stuexen**
379284441Stuexen**	( nisSchema.1.1 NAME 'gidNumber'
380284441Stuexen**	  DESC 'An integer uniquely identifying a group in an
381284441Stuexen**		administrative domain'
382284441Stuexen**	  EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
383284441Stuexen**
384284441Stuexen**	( nisSchema.1.2 NAME 'gecos'
385284441Stuexen**	  DESC 'The GECOS field; the common name'
386284441Stuexen**	  EQUALITY caseIgnoreIA5Match
387284441Stuexen**	  SUBSTRINGS caseIgnoreIA5SubstringsMatch
388284441Stuexen**	  SYNTAX 'IA5String' SINGLE-VALUE )
389284441Stuexen**
390284441Stuexen**	( nisSchema.1.3 NAME 'homeDirectory'
391284441Stuexen**	  DESC 'The absolute path to the home directory'
392284441Stuexen**	  EQUALITY caseExactIA5Match
393284441Stuexen**	  SYNTAX 'IA5String' SINGLE-VALUE )
394284441Stuexen**
395284441Stuexen**	( nisSchema.1.4 NAME 'loginShell'
396284441Stuexen**	  DESC 'The path to the login shell'
397284441Stuexen**	  EQUALITY caseExactIA5Match
398284441Stuexen**	  SYNTAX 'IA5String' SINGLE-VALUE )
399284441Stuexen**
400284441Stuexen**	( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
401284441Stuexen**	  DESC 'Abstraction of an account with POSIX attributes'
402284441Stuexen**	  MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
403284441Stuexen**	  MAY ( userPassword $ loginShell $ gecos $ description ) )
404284441Stuexen**
405284441Stuexen*/
406284441Stuexen
407284441Stuexen#  define MBDB_LDAP_LABEL		"MailboxDatabase"
408284694Stuexen
409284694Stuexen#  ifndef MBDB_LDAP_FILTER
410284694Stuexen#   define MBDB_LDAP_FILTER		"(&(objectClass=posixAccount)(uid=%0))"
411284694Stuexen#  endif /* MBDB_LDAP_FILTER */
412284694Stuexen
413284694Stuexen#  ifndef MBDB_DEFAULT_LDAP_BASEDN
414284694Stuexen#   define MBDB_DEFAULT_LDAP_BASEDN	NULL
415284694Stuexen#  endif /* MBDB_DEFAULT_LDAP_BASEDN */
416284694Stuexen
417284694Stuexen#  ifndef MBDB_DEFAULT_LDAP_SERVER
418284694Stuexen#   define MBDB_DEFAULT_LDAP_SERVER	NULL
419284441Stuexen#  endif /* MBDB_DEFAULT_LDAP_SERVER */
420284441Stuexen
421284441Stuexen/*
422284441Stuexen**  MBDB_LDAP_INITIALIZE -- initialize LDAP version
423284441Stuexen**
424284441Stuexen**	Parameters:
425284441Stuexen**		arg -- LDAP specification
426284441Stuexen**
427284441Stuexen**	Results:
428284441Stuexen**		EX_OK on success, or an EX_* code on failure.
429294231Stuexen*/
430284441Stuexen
431284441Stuexenstatic int
432284441Stuexenmbdb_ldap_initialize(arg)
433284441Stuexen	char *arg;
434284441Stuexen{
435284441Stuexen	sm_ldap_clear(&LDAPLMAP);
436284441Stuexen	LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
437284441Stuexen	LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
438284441Stuexen	LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
439284441Stuexen
440284441Stuexen	/* Only want one match */
441284441Stuexen	LDAPLMAP.ldap_sizelimit = 1;
442284441Stuexen
443284441Stuexen	/* interpolate new ldap_base and ldap_host from arg if given */
444284441Stuexen	if (arg != NULL && *arg != '\0')
445284441Stuexen	{
446284441Stuexen		char *new;
447284441Stuexen		char *sep;
448284441Stuexen		size_t len;
449284441Stuexen
450284441Stuexen		len = strlen(arg) + 1;
451284441Stuexen		new = sm_malloc(len);
452284441Stuexen		if (new == NULL)
453284441Stuexen			return EX_TEMPFAIL;
454284441Stuexen		(void) sm_strlcpy(new, arg, len);
455284441Stuexen		sep = strrchr(new, '@');
456284441Stuexen		if (sep != NULL)
457284441Stuexen		{
458284441Stuexen			*sep++ = '\0';
459284441Stuexen			LDAPLMAP.ldap_host = sep;
460284441Stuexen		}
461284441Stuexen		LDAPLMAP.ldap_base = new;
462284441Stuexen	}
463284441Stuexen	return EX_OK;
464284441Stuexen}
465284441Stuexen
466284441Stuexen
467284441Stuexen/*
468284441Stuexen**  MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
469284441Stuexen**
470293290Sbdrewery**	Parameters:
471284441Stuexen**		name -- name of local mail recipient
472284441Stuexen**		user -- pointer to structure to fill in on success
473284441Stuexen**
474284441Stuexen**	Results:
475284441Stuexen**		On success, fill in *user and return EX_OK.
476284441Stuexen**		Failure: EX_NOUSER.
477284441Stuexen*/
478284441Stuexen
479284441Stuexen#define NEED_FULLNAME	0x01
480284441Stuexen#define NEED_HOMEDIR	0x02
481284441Stuexen#define NEED_SHELL	0x04
482284441Stuexen#define NEED_UID	0x08
483284441Stuexen#define NEED_GID	0x10
484284441Stuexen
485284441Stuexenstatic int
486284441Stuexenmbdb_ldap_lookup(name, user)
487284441Stuexen	char *name;
488284441Stuexen	SM_MBDB_T *user;
489284441Stuexen{
490284441Stuexen	int msgid;
491284441Stuexen	int need;
492284441Stuexen	int ret;
493284441Stuexen	int save_errno;
494284441Stuexen	LDAPMessage *entry;
495284441Stuexen	BerElement *ber;
496284441Stuexen	char *attr = NULL;
497284441Stuexen
498284441Stuexen	if (strlen(name) >= sizeof(user->mbdb_name))
499284441Stuexen	{
500284441Stuexen		errno = EINVAL;
501284441Stuexen		return EX_NOUSER;
502284441Stuexen	}
503284441Stuexen
504284441Stuexen	if (LDAPLMAP.ldap_filter == NULL)
505284441Stuexen	{
506284441Stuexen		/* map not initialized, but don't have arg here */
507284441Stuexen		errno = EFAULT;
508284441Stuexen		return EX_TEMPFAIL;
509284441Stuexen	}
510284441Stuexen
511284441Stuexen	if (LDAPLMAP.ldap_pid != getpid())
512293290Sbdrewery	{
513284441Stuexen		/* re-open map in this child process */
514284441Stuexen		LDAPLMAP.ldap_ld = NULL;
515284441Stuexen	}
516284441Stuexen
517284441Stuexen	if (LDAPLMAP.ldap_ld == NULL)
518284441Stuexen	{
519284441Stuexen		/* map not open, try to open now */
520284441Stuexen		if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
521284441Stuexen			return EX_TEMPFAIL;
522284694Stuexen	}
523284694Stuexen
524284694Stuexen	sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
525284694Stuexen	msgid = sm_ldap_search(&LDAPLMAP, name);
526284694Stuexen	if (msgid == -1)
527284694Stuexen	{
528284694Stuexen		save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
529284694Stuexen#  ifdef LDAP_SERVER_DOWN
530284441Stuexen		if (errno == LDAP_SERVER_DOWN)
531284441Stuexen		{
532284441Stuexen			/* server disappeared, try reopen on next search */
533284441Stuexen			sm_ldap_close(&LDAPLMAP);
534284441Stuexen		}
535284441Stuexen#  endif /* LDAP_SERVER_DOWN */
536284441Stuexen		errno = save_errno;
537284441Stuexen		return EX_TEMPFAIL;
538284441Stuexen	}
539101043Sdes
540101043Sdes	/* Get results */
541101043Sdes	ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
542101043Sdes			  (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
543101043Sdes			   &(LDAPLMAP.ldap_timeout)),
544101043Sdes			  &(LDAPLMAP.ldap_res));
545101043Sdes
546101043Sdes	if (ret != LDAP_RES_SEARCH_RESULT &&
547284441Stuexen	    ret != LDAP_RES_SEARCH_ENTRY)
548101043Sdes	{
549101043Sdes		if (ret == 0)
550101043Sdes			errno = ETIMEDOUT;
551294230Stuexen		else
552101043Sdes			errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
553294230Stuexen		ret = EX_TEMPFAIL;
554101043Sdes		goto abort;
555101043Sdes	}
556101043Sdes
557101043Sdes	entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
558101043Sdes	if (entry == NULL)
559101043Sdes	{
560101043Sdes		int rc;
561101043Sdes
562101043Sdes		/*
563101043Sdes		**  We may have gotten an LDAP_RES_SEARCH_RESULT response
564101043Sdes		**  with an error inside it, so we have to extract that
565101043Sdes		**  with ldap_parse_result().  This can happen when talking
566101043Sdes		**  to an LDAP proxy whose backend has gone down.
567101043Sdes		*/
568138391Sru
569138391Sru		save_errno = ldap_parse_result(LDAPLMAP.ldap_ld,
570138391Sru					       LDAPLMAP.ldap_res, &rc, NULL,
571138391Sru					       NULL, NULL, NULL, 0);
572101043Sdes		if (save_errno == LDAP_SUCCESS)
573164201Skeramida			save_errno = rc;
574101043Sdes		if (save_errno == LDAP_SUCCESS)
575101043Sdes		{
576101043Sdes			errno = ENOENT;
577101043Sdes			ret = EX_NOUSER;
578101043Sdes		}
579101043Sdes		else
580101043Sdes		{
581101043Sdes			errno = save_errno;
582101043Sdes			ret = EX_TEMPFAIL;
583101043Sdes		}
584101043Sdes		goto abort;
585101043Sdes	}
586138437Sru
587138437Sru# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
588230874Strociny	/*
589101043Sdes	**  Reset value to prevent lingering
590101043Sdes	**  LDAP_DECODING_ERROR due to
591101043Sdes	**  OpenLDAP 1.X's hack (see below)
592101043Sdes	*/
593101220Srobert
594101220Srobert	LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
595101043Sdes# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
596101043Sdes
597101043Sdes	ret = EX_OK;
598101043Sdes	need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
599101043Sdes	for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
600101043Sdes	     attr != NULL;
601101043Sdes	     attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
602101043Sdes	{
603101043Sdes		char **vals;
604101220Srobert
605101043Sdes		vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
606101043Sdes		if (vals == NULL)
607294226Stuexen		{
608294226Stuexen			errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
609101043Sdes			if (errno == LDAP_SUCCESS)
610101043Sdes			{
611285826Shrs				ldap_memfree(attr);
612101043Sdes				continue;
613101043Sdes			}
614101043Sdes
615101043Sdes			/* Must be an error */
616101043Sdes			errno += E_LDAPBASE;
617237263Snp			ret = EX_TEMPFAIL;
618101043Sdes			goto abort;
619101043Sdes		}
620138391Sru
621285826Shrs# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
622101043Sdes		/*
623101043Sdes		**  Reset value to prevent lingering
624101043Sdes		**  LDAP_DECODING_ERROR due to
625101043Sdes		**  OpenLDAP 1.X's hack (see below)
626101043Sdes		*/
627101043Sdes
628101043Sdes		LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
629164201Skeramida# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
630101043Sdes
631101043Sdes		if (vals[0] == NULL || vals[0][0] == '\0')
632101043Sdes			goto skip;
633101144Sdes
634101144Sdes		if (strcasecmp(attr, "gecos") == 0)
635101144Sdes		{
636101144Sdes			if (!bitset(NEED_FULLNAME, need) ||
637179115Sbms			    strlen(vals[0]) >= sizeof(user->mbdb_fullname))
638179115Sbms				goto skip;
639179115Sbms
640179115Sbms			sm_pwfullname(vals[0], name, user->mbdb_fullname,
641179115Sbms				      sizeof(user->mbdb_fullname));
642179115Sbms			need &= ~NEED_FULLNAME;
643179115Sbms		}
644101144Sdes		else if (strcasecmp(attr, "homeDirectory") == 0)
645189637Srwatson		{
646189637Srwatson			if (!bitset(NEED_HOMEDIR, need) ||
647101144Sdes			    strlen(vals[0]) >= sizeof(user->mbdb_homedir))
648179115Sbms				goto skip;
649179115Sbms
650179115Sbms			(void) sm_strlcpy(user->mbdb_homedir, vals[0],
651179115Sbms					  sizeof(user->mbdb_homedir));
652101144Sdes			need &= ~NEED_HOMEDIR;
653101144Sdes		}
654101144Sdes		else if (strcasecmp(attr, "loginShell") == 0)
655101144Sdes		{
656101144Sdes			if (!bitset(NEED_SHELL, need) ||
657285826Shrs			    strlen(vals[0]) >= sizeof(user->mbdb_shell))
658101043Sdes				goto skip;
659284441Stuexen
660284441Stuexen			(void) sm_strlcpy(user->mbdb_shell, vals[0],
661284441Stuexen					  sizeof(user->mbdb_shell));
662284441Stuexen			need &= ~NEED_SHELL;
663101043Sdes		}
664101043Sdes		else if (strcasecmp(attr, "uidNumber") == 0)
665101043Sdes		{
666101043Sdes			char *p;
667284441Stuexen
668101043Sdes			if (!bitset(NEED_UID, need))
669284441Stuexen				goto skip;
670101043Sdes
671101043Sdes			for (p = vals[0]; *p != '\0'; p++)
672101043Sdes			{
673284441Stuexen				/* allow negative numbers */
674189637Srwatson				if (p == vals[0] && *p == '-')
675284441Stuexen				{
676189637Srwatson					/* but not simply '-' */
677101043Sdes					if (*(p + 1) == '\0')
678284441Stuexen						goto skip;
679284441Stuexen				}
680284441Stuexen				else if (!isascii(*p) || !isdigit(*p))
681284441Stuexen					goto skip;
682101043Sdes			}
683294225Stuexen			user->mbdb_uid = atoi(vals[0]);
684294225Stuexen			need &= ~NEED_UID;
685101043Sdes		}
686101043Sdes		else if (strcasecmp(attr, "gidNumber") == 0)
687101043Sdes		{
688101043Sdes			char *p;
689101043Sdes
690101043Sdes			if (!bitset(NEED_GID, need))
691101043Sdes				goto skip;
692101043Sdes
693101043Sdes			for (p = vals[0]; *p != '\0'; p++)
694101043Sdes			{
695101043Sdes				/* allow negative numbers */
696101043Sdes				if (p == vals[0] && *p == '-')
697101043Sdes				{
698101043Sdes					/* but not simply '-' */
699101043Sdes					if (*(p + 1) == '\0')
700284441Stuexen						goto skip;
701101043Sdes				}
702101043Sdes				else if (!isascii(*p) || !isdigit(*p))
703101043Sdes					goto skip;
704101043Sdes			}
705101043Sdes			user->mbdb_gid = atoi(vals[0]);
706101043Sdes			need &= ~NEED_GID;
707101043Sdes		}
708101043Sdes
709101043Sdesskip:
710101043Sdes		ldap_value_free(vals);
711101043Sdes		ldap_memfree(attr);
712101043Sdes	}
713101043Sdes
714101043Sdes	errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
715285826Shrs
716285826Shrs	/*
717285826Shrs	**  We check errno != LDAP_DECODING_ERROR since
718285826Shrs	**  OpenLDAP 1.X has a very ugly *undocumented*
719101043Sdes	**  hack of returning this error code from
720101043Sdes	**  ldap_next_attribute() if the library freed the
721101043Sdes	**  ber attribute.  See:
722101043Sdes	**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
723101043Sdes	*/
724101043Sdes
725101043Sdes	if (errno != LDAP_SUCCESS &&
726101043Sdes	    errno != LDAP_DECODING_ERROR)
727101043Sdes	{
728101043Sdes		/* Must be an error */
729101043Sdes		errno += E_LDAPBASE;
730101043Sdes		ret = EX_TEMPFAIL;
731101043Sdes		goto abort;
732230874Strociny	}
733101043Sdes
734101043Sdes abort:
735101043Sdes	save_errno = errno;
736101043Sdes	if (attr != NULL)
737101220Srobert	{
738285826Shrs		ldap_memfree(attr);
739285826Shrs		attr = NULL;
740285826Shrs	}
741101043Sdes	if (LDAPLMAP.ldap_res != NULL)
742101043Sdes	{
743101043Sdes		ldap_msgfree(LDAPLMAP.ldap_res);
744101043Sdes		LDAPLMAP.ldap_res = NULL;
745101043Sdes	}
746101043Sdes	if (ret == EX_OK)
747101043Sdes	{
748101043Sdes		if (need == 0)
749101043Sdes		{
750101220Srobert			(void) sm_strlcpy(user->mbdb_name, name,
751101043Sdes					  sizeof(user->mbdb_name));
752101043Sdes			save_errno = 0;
753101043Sdes		}
754285826Shrs		else
755101043Sdes		{
756101043Sdes			ret = EX_NOUSER;
757101043Sdes			save_errno = EINVAL;
758101144Sdes		}
759101144Sdes	}
760101144Sdes	errno = save_errno;
761285826Shrs	return ret;
762101043Sdes}
763284441Stuexen
764284441Stuexen/*
765284441Stuexen**  MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
766284441Stuexen**
767101043Sdes**	Parameters:
768101043Sdes**		none.
769101043Sdes**
770101043Sdes**	Results:
771101043Sdes**		none.
772101043Sdes*/
773284441Stuexen
774101220Srobertstatic void
775101043Sdesmbdb_ldap_terminate()
776284441Stuexen{
777284441Stuexen	sm_ldap_close(&LDAPLMAP);
778284441Stuexen}
779284441Stuexen# endif /* _LDAP_EXAMPLE_ */
780284441Stuexen#endif /* LDAPMAP */
781101043Sdes