sendmail: Update vendor branch to v8.14.4
[dragonfly.git] / contrib / sendmail-8.14 / libsm / mbdb.c
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>
11 SM_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
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                 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
716 skip:
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
781 static void
782 mbdb_ldap_terminate()
783 {
784         sm_ldap_close(&LDAPLMAP);
785 }
786 # endif /* _LDAP_EXAMPLE_ */
787 #endif /* LDAPMAP */