1/*
2 * Copyright (c) 2001-2003,2009 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 */
9
10#include <sm/gen.h>
11SM_RCSID("@(#)$Id: mbdb.c,v 1.41 2009/06/19 22:02:26 guenther Exp $")
12
13#include <sys/param.h>
14
15#include <ctype.h>
16#include <errno.h>
17#include <pwd.h>
18#include <stdlib.h>
19#include <setjmp.h>
20#include <unistd.h>
21
22#include <sm/limits.h>
23#include <sm/conf.h>
24#include <sm/assert.h>
25#include <sm/bitops.h>
26#include <sm/errstring.h>
27#include <sm/heap.h>
28#include <sm/mbdb.h>
29#include <sm/string.h>
30# ifdef EX_OK
31#  undef EX_OK			/* for SVr4.2 SMP */
32# endif /* EX_OK */
33#include <sm/sysexits.h>
34
35#if LDAPMAP
36# if _LDAP_EXAMPLE_
37#  include <sm/ldap.h>
38# endif /* _LDAP_EXAMPLE_ */
39#endif /* LDAPMAP */
40
41typedef struct
42{
43	char	*mbdb_typename;
44	int	(*mbdb_initialize) __P((char *));
45	int	(*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
46	void	(*mbdb_terminate) __P((void));
47} SM_MBDB_TYPE_T;
48
49static int	mbdb_pw_initialize __P((char *));
50static int	mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
51static void	mbdb_pw_terminate __P((void));
52
53#if LDAPMAP
54# if _LDAP_EXAMPLE_
55static struct sm_ldap_struct LDAPLMAP;
56static int	mbdb_ldap_initialize __P((char *));
57static int	mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
58static void	mbdb_ldap_terminate __P((void));
59# endif /* _LDAP_EXAMPLE_ */
60#endif /* LDAPMAP */
61
62static SM_MBDB_TYPE_T SmMbdbTypes[] =
63{
64	{ "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
65#if LDAPMAP
66# if _LDAP_EXAMPLE_
67	{ "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
68# endif /* _LDAP_EXAMPLE_ */
69#endif /* LDAPMAP */
70	{ NULL, NULL, NULL, NULL }
71};
72
73static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
74
75/*
76**  SM_MBDB_INITIALIZE -- specify which mailbox database to use
77**
78**	If this function is not called, then the "pw" implementation
79**	is used by default; this implementation uses getpwnam().
80**
81**	Parameters:
82**		mbdb -- Which mailbox database to use.
83**			The argument has the form "name" or "name.arg".
84**			"pw" means use getpwnam().
85**
86**	Results:
87**		EX_OK on success, or an EX_* code on failure.
88*/
89
90int
91sm_mbdb_initialize(mbdb)
92	char *mbdb;
93{
94	size_t namelen;
95	int err;
96	char *name;
97	char *arg;
98	SM_MBDB_TYPE_T *t;
99
100	SM_REQUIRE(mbdb != NULL);
101
102	name = mbdb;
103	arg = strchr(mbdb, '.');
104	if (arg == NULL)
105		namelen = strlen(name);
106	else
107	{
108		namelen = arg - name;
109		++arg;
110	}
111
112	for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
113	{
114		if (strlen(t->mbdb_typename) == namelen &&
115		    strncmp(name, t->mbdb_typename, namelen) == 0)
116		{
117			err = EX_OK;
118			if (t->mbdb_initialize != NULL)
119				err = t->mbdb_initialize(arg);
120			if (err == EX_OK)
121				SmMbdbType = t;
122			return err;
123		}
124	}
125	return EX_UNAVAILABLE;
126}
127
128/*
129**  SM_MBDB_TERMINATE -- terminate connection to the mailbox database
130**
131**	Because this function closes any cached file descriptors that
132**	are being held open for the connection to the mailbox database,
133**	it should be called for security reasons prior to dropping privileges
134**	and execing another process.
135**
136**	Parameters:
137**		none.
138**
139**	Results:
140**		none.
141*/
142
143void
144sm_mbdb_terminate()
145{
146	if (SmMbdbType->mbdb_terminate != NULL)
147		SmMbdbType->mbdb_terminate();
148}
149
150/*
151**  SM_MBDB_LOOKUP -- look up a local mail recipient, given name
152**
153**	Parameters:
154**		name -- name of local mail recipient
155**		user -- pointer to structure to fill in on success
156**
157**	Results:
158**		On success, fill in *user and return EX_OK.
159**		If the user does not exist, return EX_NOUSER.
160**		If a temporary failure (eg, a network failure) occurred,
161**		return EX_TEMPFAIL.  Otherwise return EX_OSERR.
162*/
163
164int
165sm_mbdb_lookup(name, user)
166	char *name;
167	SM_MBDB_T *user;
168{
169	int ret = EX_NOUSER;
170
171	if (SmMbdbType->mbdb_lookup != NULL)
172		ret = SmMbdbType->mbdb_lookup(name, user);
173	return ret;
174}
175
176/*
177**  SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T
178**
179**	Parameters:
180**		user -- destination user information structure
181**		pw -- source passwd structure
182**
183**	Results:
184**		none.
185*/
186
187void
188sm_mbdb_frompw(user, pw)
189	SM_MBDB_T *user;
190	struct passwd *pw;
191{
192	SM_REQUIRE(user != NULL);
193	(void) sm_strlcpy(user->mbdb_name, pw->pw_name,
194			  sizeof(user->mbdb_name));
195	user->mbdb_uid = pw->pw_uid;
196	user->mbdb_gid = pw->pw_gid;
197	sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
198		      sizeof(user->mbdb_fullname));
199	(void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
200			  sizeof(user->mbdb_homedir));
201	(void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
202			  sizeof(user->mbdb_shell));
203}
204
205/*
206**  SM_PWFULLNAME -- build full name of user from pw_gecos field.
207**
208**	This routine interprets the strange entry that would appear
209**	in the GECOS field of the password file.
210**
211**	Parameters:
212**		gecos -- name to build.
213**		user -- the login name of this user (for &).
214**		buf -- place to put the result.
215**		buflen -- length of buf.
216**
217**	Returns:
218**		none.
219*/
220
221#if _FFR_HANDLE_ISO8859_GECOS
222static char Latin1ToASCII[128] =
223{
224	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
225	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33,
226	99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42,
227	50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65,
228	65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79,
229	79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97,
230	97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110,
231	111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121
232};
233#endif /* _FFR_HANDLE_ISO8859_GECOS */
234
235void
236sm_pwfullname(gecos, user, buf, buflen)
237	register char *gecos;
238	char *user;
239	char *buf;
240	size_t buflen;
241{
242	register char *p;
243	register char *bp = buf;
244
245	if (*gecos == '*')
246		gecos++;
247
248	/* copy gecos, interpolating & to be full name */
249	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
250	{
251		if (bp >= &buf[buflen - 1])
252		{
253			/* buffer overflow -- just use login name */
254			(void) sm_strlcpy(buf, user, buflen);
255			return;
256		}
257		if (*p == '&')
258		{
259			/* interpolate full name */
260			(void) sm_strlcpy(bp, user, buflen - (bp - buf));
261			*bp = toupper(*bp);
262			bp += strlen(bp);
263		}
264		else
265		{
266#if _FFR_HANDLE_ISO8859_GECOS
267			if ((unsigned char) *p >= 128)
268				*bp++ = Latin1ToASCII[(unsigned char) *p - 128];
269			else
270#endif /* _FFR_HANDLE_ISO8859_GECOS */
271				*bp++ = *p;
272		}
273	}
274	*bp = '\0';
275}
276
277/*
278**  /etc/passwd implementation.
279*/
280
281/*
282**  MBDB_PW_INITIALIZE -- initialize getpwnam() version
283**
284**	Parameters:
285**		arg -- unused.
286**
287**	Results:
288**		EX_OK.
289*/
290
291/* ARGSUSED0 */
292static int
293mbdb_pw_initialize(arg)
294	char *arg;
295{
296	return EX_OK;
297}
298
299/*
300**  MBDB_PW_LOOKUP -- look up a local mail recipient, given name
301**
302**	Parameters:
303**		name -- name of local mail recipient
304**		user -- pointer to structure to fill in on success
305**
306**	Results:
307**		On success, fill in *user and return EX_OK.
308**		Failure: EX_NOUSER.
309*/
310
311static int
312mbdb_pw_lookup(name, user)
313	char *name;
314	SM_MBDB_T *user;
315{
316	struct passwd *pw;
317
318#ifdef HESIOD
319	/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
320	{
321		char *p;
322
323		for (p = name; *p != '\0'; p++)
324			if (!isascii(*p) || !isdigit(*p))
325				break;
326		if (*p == '\0')
327			return EX_NOUSER;
328	}
329#endif /* HESIOD */
330
331	errno = 0;
332	pw = getpwnam(name);
333	if (pw == NULL)
334	{
335#if 0
336		/*
337		**  getpwnam() isn't advertised as setting errno.
338		**  In fact, under FreeBSD, non-root getpwnam() on
339		**  non-existant users returns NULL with errno = EPERM.
340		**  This test won't work.
341		*/
342		switch (errno)
343		{
344		  case 0:
345			return EX_NOUSER;
346		  case EIO:
347			return EX_OSERR;
348		  default:
349			return EX_TEMPFAIL;
350		}
351#endif /* 0 */
352		return EX_NOUSER;
353	}
354
355	sm_mbdb_frompw(user, pw);
356	return EX_OK;
357}
358
359/*
360**  MBDB_PW_TERMINATE -- terminate connection to the mailbox database
361**
362**	Parameters:
363**		none.
364**
365**	Results:
366**		none.
367*/
368
369static void
370mbdb_pw_terminate()
371{
372	endpwent();
373}
374
375#if LDAPMAP
376# if _LDAP_EXAMPLE_
377/*
378**  LDAP example implementation based on RFC 2307, "An Approach for Using
379**  LDAP as a Network Information Service":
380**
381**	( nisSchema.1.0 NAME 'uidNumber'
382**	  DESC 'An integer uniquely identifying a user in an
383**		administrative domain'
384**	  EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
385**
386**	( nisSchema.1.1 NAME 'gidNumber'
387**	  DESC 'An integer uniquely identifying a group in an
388**		administrative domain'
389**	  EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
390**
391**	( nisSchema.1.2 NAME 'gecos'
392**	  DESC 'The GECOS field; the common name'
393**	  EQUALITY caseIgnoreIA5Match
394**	  SUBSTRINGS caseIgnoreIA5SubstringsMatch
395**	  SYNTAX 'IA5String' SINGLE-VALUE )
396**
397**	( nisSchema.1.3 NAME 'homeDirectory'
398**	  DESC 'The absolute path to the home directory'
399**	  EQUALITY caseExactIA5Match
400**	  SYNTAX 'IA5String' SINGLE-VALUE )
401**
402**	( nisSchema.1.4 NAME 'loginShell'
403**	  DESC 'The path to the login shell'
404**	  EQUALITY caseExactIA5Match
405**	  SYNTAX 'IA5String' SINGLE-VALUE )
406**
407**	( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
408**	  DESC 'Abstraction of an account with POSIX attributes'
409**	  MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
410**	  MAY ( userPassword $ loginShell $ gecos $ description ) )
411**
412*/
413
414#  define MBDB_LDAP_LABEL		"MailboxDatabase"
415
416#  ifndef MBDB_LDAP_FILTER
417#   define MBDB_LDAP_FILTER		"(&(objectClass=posixAccount)(uid=%0))"
418#  endif /* MBDB_LDAP_FILTER */
419
420#  ifndef MBDB_DEFAULT_LDAP_BASEDN
421#   define MBDB_DEFAULT_LDAP_BASEDN	NULL
422#  endif /* MBDB_DEFAULT_LDAP_BASEDN */
423
424#  ifndef MBDB_DEFAULT_LDAP_SERVER
425#   define MBDB_DEFAULT_LDAP_SERVER	NULL
426#  endif /* MBDB_DEFAULT_LDAP_SERVER */
427
428/*
429**  MBDB_LDAP_INITIALIZE -- initialize LDAP version
430**
431**	Parameters:
432**		arg -- LDAP specification
433**
434**	Results:
435**		EX_OK on success, or an EX_* code on failure.
436*/
437
438static int
439mbdb_ldap_initialize(arg)
440	char *arg;
441{
442	sm_ldap_clear(&LDAPLMAP);
443	LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
444	LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
445	LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
446
447	/* Only want one match */
448	LDAPLMAP.ldap_sizelimit = 1;
449
450	/* interpolate new ldap_base and ldap_host from arg if given */
451	if (arg != NULL && *arg != '\0')
452	{
453		char *new;
454		char *sep;
455		size_t len;
456
457		len = strlen(arg) + 1;
458		new = sm_malloc(len);
459		if (new == NULL)
460			return EX_TEMPFAIL;
461		(void) sm_strlcpy(new, arg, len);
462		sep = strrchr(new, '@');
463		if (sep != NULL)
464		{
465			*sep++ = '\0';
466			LDAPLMAP.ldap_host = sep;
467		}
468		LDAPLMAP.ldap_base = new;
469	}
470	return EX_OK;
471}
472
473
474/*
475**  MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
476**
477**	Parameters:
478**		name -- name of local mail recipient
479**		user -- pointer to structure to fill in on success
480**
481**	Results:
482**		On success, fill in *user and return EX_OK.
483**		Failure: EX_NOUSER.
484*/
485
486#define NEED_FULLNAME	0x01
487#define NEED_HOMEDIR	0x02
488#define NEED_SHELL	0x04
489#define NEED_UID	0x08
490#define NEED_GID	0x10
491
492static int
493mbdb_ldap_lookup(name, user)
494	char *name;
495	SM_MBDB_T *user;
496{
497	int msgid;
498	int need;
499	int ret;
500	int save_errno;
501	LDAPMessage *entry;
502	BerElement *ber;
503	char *attr = NULL;
504
505	if (strlen(name) >= sizeof(user->mbdb_name))
506	{
507		errno = EINVAL;
508		return EX_NOUSER;
509	}
510
511	if (LDAPLMAP.ldap_filter == NULL)
512	{
513		/* map not initialized, but don't have arg here */
514		errno = EFAULT;
515		return EX_TEMPFAIL;
516	}
517
518	if (LDAPLMAP.ldap_pid != getpid())
519	{
520		/* re-open map in this child process */
521		LDAPLMAP.ldap_ld = NULL;
522	}
523
524	if (LDAPLMAP.ldap_ld == NULL)
525	{
526		/* map not open, try to open now */
527		if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
528			return EX_TEMPFAIL;
529	}
530
531	sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
532	msgid = sm_ldap_search(&LDAPLMAP, name);
533	if (msgid == -1)
534	{
535		save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
536#  ifdef LDAP_SERVER_DOWN
537		if (errno == LDAP_SERVER_DOWN)
538		{
539			/* server disappeared, try reopen on next search */
540			sm_ldap_close(&LDAPLMAP);
541		}
542#  endif /* LDAP_SERVER_DOWN */
543		errno = save_errno;
544		return EX_TEMPFAIL;
545	}
546
547	/* Get results */
548	ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
549			  (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
550			   &(LDAPLMAP.ldap_timeout)),
551			  &(LDAPLMAP.ldap_res));
552
553	if (ret != LDAP_RES_SEARCH_RESULT &&
554	    ret != LDAP_RES_SEARCH_ENTRY)
555	{
556		if (ret == 0)
557			errno = ETIMEDOUT;
558		else
559			errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
560		ret = EX_TEMPFAIL;
561		goto abort;
562	}
563
564	entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
565	if (entry == NULL)
566	{
567		int rc;
568
569		/*
570		**  We may have gotten an LDAP_RES_SEARCH_RESULT response
571		**  with an error inside it, so we have to extract that
572		**  with ldap_parse_result().  This can happen when talking
573		**  to an LDAP proxy whose backend has gone down.
574		*/
575
576		save_errno = ldap_parse_result(LDAPLMAP.ldap_ld,
577					       LDAPLMAP.ldap_res, &rc, NULL,
578					       NULL, NULL, NULL, 0);
579		if (save_errno == LDAP_SUCCESS)
580			save_errno = rc;
581		if (save_errno == LDAP_SUCCESS)
582		{
583			errno = ENOENT;
584			ret = EX_NOUSER;
585		}
586		else
587		{
588			errno = save_errno;
589			ret = EX_TEMPFAIL;
590		}
591		goto abort;
592	}
593
594# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
595	/*
596	**  Reset value to prevent lingering
597	**  LDAP_DECODING_ERROR due to
598	**  OpenLDAP 1.X's hack (see below)
599	*/
600
601	LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
602# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
603
604	ret = EX_OK;
605	need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
606	for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
607	     attr != NULL;
608	     attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
609	{
610		char **vals;
611
612		vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
613		if (vals == NULL)
614		{
615			errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
616			if (errno == LDAP_SUCCESS)
617			{
618				ldap_memfree(attr);
619				continue;
620			}
621
622			/* Must be an error */
623			errno += E_LDAPBASE;
624			ret = EX_TEMPFAIL;
625			goto abort;
626		}
627
628# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
629		/*
630		**  Reset value to prevent lingering
631		**  LDAP_DECODING_ERROR due to
632		**  OpenLDAP 1.X's hack (see below)
633		*/
634
635		LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
636# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
637
638		if (vals[0] == NULL || vals[0][0] == '\0')
639			goto skip;
640
641		if (strcasecmp(attr, "gecos") == 0)
642		{
643			if (!bitset(NEED_FULLNAME, need) ||
644			    strlen(vals[0]) >= sizeof(user->mbdb_fullname))
645				goto skip;
646
647			sm_pwfullname(vals[0], name, user->mbdb_fullname,
648				      sizeof(user->mbdb_fullname));
649			need &= ~NEED_FULLNAME;
650		}
651		else if (strcasecmp(attr, "homeDirectory") == 0)
652		{
653			if (!bitset(NEED_HOMEDIR, need) ||
654			    strlen(vals[0]) >= sizeof(user->mbdb_homedir))
655				goto skip;
656
657			(void) sm_strlcpy(user->mbdb_homedir, vals[0],
658					  sizeof(user->mbdb_homedir));
659			need &= ~NEED_HOMEDIR;
660		}
661		else if (strcasecmp(attr, "loginShell") == 0)
662		{
663			if (!bitset(NEED_SHELL, need) ||
664			    strlen(vals[0]) >= sizeof(user->mbdb_shell))
665				goto skip;
666
667			(void) sm_strlcpy(user->mbdb_shell, vals[0],
668					  sizeof(user->mbdb_shell));
669			need &= ~NEED_SHELL;
670		}
671		else if (strcasecmp(attr, "uidNumber") == 0)
672		{
673			char *p;
674
675			if (!bitset(NEED_UID, need))
676				goto skip;
677
678			for (p = vals[0]; *p != '\0'; p++)
679			{
680				/* allow negative numbers */
681				if (p == vals[0] && *p == '-')
682				{
683					/* but not simply '-' */
684					if (*(p + 1) == '\0')
685						goto skip;
686				}
687				else if (!isascii(*p) || !isdigit(*p))
688					goto skip;
689			}
690			user->mbdb_uid = atoi(vals[0]);
691			need &= ~NEED_UID;
692		}
693		else if (strcasecmp(attr, "gidNumber") == 0)
694		{
695			char *p;
696
697			if (!bitset(NEED_GID, need))
698				goto skip;
699
700			for (p = vals[0]; *p != '\0'; p++)
701			{
702				/* allow negative numbers */
703				if (p == vals[0] && *p == '-')
704				{
705					/* but not simply '-' */
706					if (*(p + 1) == '\0')
707						goto skip;
708				}
709				else if (!isascii(*p) || !isdigit(*p))
710					goto skip;
711			}
712			user->mbdb_gid = atoi(vals[0]);
713			need &= ~NEED_GID;
714		}
715
716skip:
717		ldap_value_free(vals);
718		ldap_memfree(attr);
719	}
720
721	errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
722
723	/*
724	**  We check errno != LDAP_DECODING_ERROR since
725	**  OpenLDAP 1.X has a very ugly *undocumented*
726	**  hack of returning this error code from
727	**  ldap_next_attribute() if the library freed the
728	**  ber attribute.  See:
729	**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
730	*/
731
732	if (errno != LDAP_SUCCESS &&
733	    errno != LDAP_DECODING_ERROR)
734	{
735		/* Must be an error */
736		errno += E_LDAPBASE;
737		ret = EX_TEMPFAIL;
738		goto abort;
739	}
740
741 abort:
742	save_errno = errno;
743	if (attr != NULL)
744	{
745		ldap_memfree(attr);
746		attr = NULL;
747	}
748	if (LDAPLMAP.ldap_res != NULL)
749	{
750		ldap_msgfree(LDAPLMAP.ldap_res);
751		LDAPLMAP.ldap_res = NULL;
752	}
753	if (ret == EX_OK)
754	{
755		if (need == 0)
756		{
757			(void) sm_strlcpy(user->mbdb_name, name,
758					  sizeof(user->mbdb_name));
759			save_errno = 0;
760		}
761		else
762		{
763			ret = EX_NOUSER;
764			save_errno = EINVAL;
765		}
766	}
767	errno = save_errno;
768	return ret;
769}
770
771/*
772**  MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
773**
774**	Parameters:
775**		none.
776**
777**	Results:
778**		none.
779*/
780
781static void
782mbdb_ldap_terminate()
783{
784	sm_ldap_close(&LDAPLMAP);
785}
786# endif /* _LDAP_EXAMPLE_ */
787#endif /* LDAPMAP */
788