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