Default kdump's data limit to 64 bytes and document how it can be disabled.
[dragonfly.git] / contrib / sendmail-8.13.7 / libsm / mbdb.c
1 /*
2  * Copyright (c) 2001-2002 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>
11 SM_RCSID("@(#)$Id: mbdb.c,v 1.40 2003/12/10 03:19:07 gshapiro 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
41 typedef 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
49 static int      mbdb_pw_initialize __P((char *));
50 static int      mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
51 static void     mbdb_pw_terminate __P((void));
52
53 #if LDAPMAP
54 # if _LDAP_EXAMPLE_
55 static struct sm_ldap_struct LDAPLMAP;
56 static int      mbdb_ldap_initialize __P((char *));
57 static int      mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
58 static void     mbdb_ldap_terminate __P((void));
59 # endif /* _LDAP_EXAMPLE_ */
60 #endif /* LDAPMAP */
61
62 static 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
73 static 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
90 int
91 sm_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
143 void
144 sm_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
164 int
165 sm_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
187 void
188 sm_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
222 static 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
235 void
236 sm_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 */
292 static int
293 mbdb_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
311 static int
312 mbdb_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
369 static void
370 mbdb_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
438 static int
439 mbdb_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
492 static int
493 mbdb_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                 save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
568                 if (save_errno == LDAP_SUCCESS)
569                 {
570                         errno = ENOENT;
571                         ret = EX_NOUSER;
572                 }
573                 else
574                 {
575                         errno = save_errno;
576                         ret = EX_TEMPFAIL;
577                 }
578                 goto abort;
579         }
580
581 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
582         /*
583         **  Reset value to prevent lingering
584         **  LDAP_DECODING_ERROR due to
585         **  OpenLDAP 1.X's hack (see below)
586         */
587
588         LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
589 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
590
591         ret = EX_OK;
592         need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
593         for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
594              attr != NULL;
595              attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
596         {
597                 char **vals;
598
599                 vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
600                 if (vals == NULL)
601                 {
602                         errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
603                         if (errno == LDAP_SUCCESS)
604                         {
605                                 ldap_memfree(attr);
606                                 continue;
607                         }
608
609                         /* Must be an error */
610                         errno += E_LDAPBASE;
611                         ret = EX_TEMPFAIL;
612                         goto abort;
613                 }
614
615 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
616                 /*
617                 **  Reset value to prevent lingering
618                 **  LDAP_DECODING_ERROR due to
619                 **  OpenLDAP 1.X's hack (see below)
620                 */
621
622                 LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
623 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
624
625                 if (vals[0] == NULL || vals[0][0] == '\0')
626                         goto skip;
627
628                 if (strcasecmp(attr, "gecos") == 0)
629                 {
630                         if (!bitset(NEED_FULLNAME, need) ||
631                             strlen(vals[0]) >= sizeof(user->mbdb_fullname))
632                                 goto skip;
633
634                         sm_pwfullname(vals[0], name, user->mbdb_fullname,
635                                       sizeof(user->mbdb_fullname));
636                         need &= ~NEED_FULLNAME;
637                 }
638                 else if (strcasecmp(attr, "homeDirectory") == 0)
639                 {
640                         if (!bitset(NEED_HOMEDIR, need) ||
641                             strlen(vals[0]) >= sizeof(user->mbdb_homedir))
642                                 goto skip;
643
644                         (void) sm_strlcpy(user->mbdb_homedir, vals[0],
645                                           sizeof(user->mbdb_homedir));
646                         need &= ~NEED_HOMEDIR;
647                 }
648                 else if (strcasecmp(attr, "loginShell") == 0)
649                 {
650                         if (!bitset(NEED_SHELL, need) ||
651                             strlen(vals[0]) >= sizeof(user->mbdb_shell))
652                                 goto skip;
653
654                         (void) sm_strlcpy(user->mbdb_shell, vals[0],
655                                           sizeof(user->mbdb_shell));
656                         need &= ~NEED_SHELL;
657                 }
658                 else if (strcasecmp(attr, "uidNumber") == 0)
659                 {
660                         char *p;
661
662                         if (!bitset(NEED_UID, need))
663                                 goto skip;
664
665                         for (p = vals[0]; *p != '\0'; p++)
666                         {
667                                 /* allow negative numbers */
668                                 if (p == vals[0] && *p == '-')
669                                 {
670                                         /* but not simply '-' */
671                                         if (*(p + 1) == '\0')
672                                                 goto skip;
673                                 }
674                                 else if (!isascii(*p) || !isdigit(*p))
675                                         goto skip;
676                         }
677                         user->mbdb_uid = atoi(vals[0]);
678                         need &= ~NEED_UID;
679                 }
680                 else if (strcasecmp(attr, "gidNumber") == 0)
681                 {
682                         char *p;
683
684                         if (!bitset(NEED_GID, need))
685                                 goto skip;
686
687                         for (p = vals[0]; *p != '\0'; p++)
688                         {
689                                 /* allow negative numbers */
690                                 if (p == vals[0] && *p == '-')
691                                 {
692                                         /* but not simply '-' */
693                                         if (*(p + 1) == '\0')
694                                                 goto skip;
695                                 }
696                                 else if (!isascii(*p) || !isdigit(*p))
697                                         goto skip;
698                         }
699                         user->mbdb_gid = atoi(vals[0]);
700                         need &= ~NEED_GID;
701                 }
702
703 skip:
704                 ldap_value_free(vals);
705                 ldap_memfree(attr);
706         }
707
708         errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
709
710         /*
711         **  We check errno != LDAP_DECODING_ERROR since
712         **  OpenLDAP 1.X has a very ugly *undocumented*
713         **  hack of returning this error code from
714         **  ldap_next_attribute() if the library freed the
715         **  ber attribute.  See:
716         **  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
717         */
718
719         if (errno != LDAP_SUCCESS &&
720             errno != LDAP_DECODING_ERROR)
721         {
722                 /* Must be an error */
723                 errno += E_LDAPBASE;
724                 ret = EX_TEMPFAIL;
725                 goto abort;
726         }
727
728  abort:
729         save_errno = errno;
730         if (attr != NULL)
731         {
732                 ldap_memfree(attr);
733                 attr = NULL;
734         }
735         if (LDAPLMAP.ldap_res != NULL)
736         {
737                 ldap_msgfree(LDAPLMAP.ldap_res);
738                 LDAPLMAP.ldap_res = NULL;
739         }
740         if (ret == EX_OK)
741         {
742                 if (need == 0)
743                 {
744                         (void) sm_strlcpy(user->mbdb_name, name,
745                                           sizeof(user->mbdb_name));
746                         save_errno = 0;
747                 }
748                 else
749                 {
750                         ret = EX_NOUSER;
751                         save_errno = EINVAL;
752                 }
753         }
754         errno = save_errno;
755         return ret;
756 }
757
758 /*
759 **  MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
760 **
761 **      Parameters:
762 **              none.
763 **
764 **      Results:
765 **              none.
766 */
767
768 static void
769 mbdb_ldap_terminate()
770 {
771         sm_ldap_close(&LDAPLMAP);
772 }
773 # endif /* _LDAP_EXAMPLE_ */
774 #endif /* LDAPMAP */