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 # ifdef EX_OK
31 # undef EX_OK /* for SVr4.2 SMP */
32 # endif
33 #include <sm/sysexits.h>
34
35 #if LDAPMAP
36 # if _LDAP_EXAMPLE_
37 # include <sm/ldap.h>
38 # endif
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
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
sm_mbdb_initialize(mbdb)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
sm_mbdb_terminate()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
sm_mbdb_lookup(name,user)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
sm_mbdb_frompw(user,pw)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
sm_pwfullname(gecos,user,buf,buflen)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
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
mbdb_pw_initialize(arg)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
mbdb_pw_lookup(name,user)312 mbdb_pw_lookup(name, user)
313 char *name;
314 SM_MBDB_T *user;
315 {
316 struct passwd *pw;
317
318 #if HESIOD && !HESIOD_ALLOW_NUMERIC_LOGIN
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 && !HESIOD_ALLOW_NUMERIC_LOGIN */
330
331 errno = 0;
332 pw = getpwnam(name);
333 if (pw == NULL)
334 {
335 #if _FFR_USE_GETPWNAM_ERRNO
336 /*
337 ** Only enable this code iff
338 ** user unknown <-> getpwnam() == NULL && errno == 0
339 ** (i.e., errno unchanged); see the POSIX spec.
340 */
341
342 if (errno != 0)
343 return EX_TEMPFAIL;
344 #endif /* _FFR_USE_GETPWNAM_ERRNO */
345 return EX_NOUSER;
346 }
347
348 sm_mbdb_frompw(user, pw);
349 return EX_OK;
350 }
351
352 /*
353 ** MBDB_PW_TERMINATE -- terminate connection to the mailbox database
354 **
355 ** Parameters:
356 ** none.
357 **
358 ** Results:
359 ** none.
360 */
361
362 static void
mbdb_pw_terminate()363 mbdb_pw_terminate()
364 {
365 endpwent();
366 }
367
368 #if LDAPMAP
369 # if _LDAP_EXAMPLE_
370 /*
371 ** LDAP example implementation based on RFC 2307, "An Approach for Using
372 ** LDAP as a Network Information Service":
373 **
374 ** ( nisSchema.1.0 NAME 'uidNumber'
375 ** DESC 'An integer uniquely identifying a user in an
376 ** administrative domain'
377 ** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
378 **
379 ** ( nisSchema.1.1 NAME 'gidNumber'
380 ** DESC 'An integer uniquely identifying a group in an
381 ** administrative domain'
382 ** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
383 **
384 ** ( nisSchema.1.2 NAME 'gecos'
385 ** DESC 'The GECOS field; the common name'
386 ** EQUALITY caseIgnoreIA5Match
387 ** SUBSTRINGS caseIgnoreIA5SubstringsMatch
388 ** SYNTAX 'IA5String' SINGLE-VALUE )
389 **
390 ** ( nisSchema.1.3 NAME 'homeDirectory'
391 ** DESC 'The absolute path to the home directory'
392 ** EQUALITY caseExactIA5Match
393 ** SYNTAX 'IA5String' SINGLE-VALUE )
394 **
395 ** ( nisSchema.1.4 NAME 'loginShell'
396 ** DESC 'The path to the login shell'
397 ** EQUALITY caseExactIA5Match
398 ** SYNTAX 'IA5String' SINGLE-VALUE )
399 **
400 ** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
401 ** DESC 'Abstraction of an account with POSIX attributes'
402 ** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
403 ** MAY ( userPassword $ loginShell $ gecos $ description ) )
404 **
405 */
406
407 # define MBDB_LDAP_LABEL "MailboxDatabase"
408
409 # ifndef MBDB_LDAP_FILTER
410 # define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))"
411 # endif
412
413 # ifndef MBDB_DEFAULT_LDAP_BASEDN
414 # define MBDB_DEFAULT_LDAP_BASEDN NULL
415 # endif
416
417 # ifndef MBDB_DEFAULT_LDAP_SERVER
418 # define MBDB_DEFAULT_LDAP_SERVER NULL
419 # endif
420
421 /*
422 ** MBDB_LDAP_INITIALIZE -- initialize LDAP version
423 **
424 ** Parameters:
425 ** arg -- LDAP specification
426 **
427 ** Results:
428 ** EX_OK on success, or an EX_* code on failure.
429 */
430
431 static int
mbdb_ldap_initialize(arg)432 mbdb_ldap_initialize(arg)
433 char *arg;
434 {
435 sm_ldap_clear(&LDAPLMAP);
436 LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
437 LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
438 LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
439
440 /* Only want one match */
441 LDAPLMAP.ldap_sizelimit = 1;
442
443 /* interpolate new ldap_base and ldap_host from arg if given */
444 if (arg != NULL && *arg != '\0')
445 {
446 char *new;
447 char *sep;
448 size_t len;
449
450 len = strlen(arg) + 1;
451 new = sm_malloc(len);
452 if (new == NULL)
453 return EX_TEMPFAIL;
454 (void) sm_strlcpy(new, arg, len);
455 sep = strrchr(new, '@');
456 if (sep != NULL)
457 {
458 *sep++ = '\0';
459 LDAPLMAP.ldap_host = sep;
460 }
461 LDAPLMAP.ldap_base = new;
462 }
463 return EX_OK;
464 }
465
466
467 /*
468 ** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
469 **
470 ** Parameters:
471 ** name -- name of local mail recipient
472 ** user -- pointer to structure to fill in on success
473 **
474 ** Results:
475 ** On success, fill in *user and return EX_OK.
476 ** Failure: EX_NOUSER.
477 */
478
479 #define NEED_FULLNAME 0x01
480 #define NEED_HOMEDIR 0x02
481 #define NEED_SHELL 0x04
482 #define NEED_UID 0x08
483 #define NEED_GID 0x10
484
485 static int
mbdb_ldap_lookup(name,user)486 mbdb_ldap_lookup(name, user)
487 char *name;
488 SM_MBDB_T *user;
489 {
490 int msgid;
491 int need;
492 int ret;
493 int save_errno;
494 LDAPMessage *entry;
495 BerElement *ber;
496 char *attr = NULL;
497
498 if (strlen(name) >= sizeof(user->mbdb_name))
499 {
500 errno = EINVAL;
501 return EX_NOUSER;
502 }
503
504 if (LDAPLMAP.ldap_filter == NULL)
505 {
506 /* map not initialized, but don't have arg here */
507 errno = EFAULT;
508 return EX_TEMPFAIL;
509 }
510
511 if (LDAPLMAP.ldap_pid != getpid())
512 {
513 /* re-open map in this child process */
514 LDAPLMAP.ldap_ld = NULL;
515 }
516
517 if (LDAPLMAP.ldap_ld == NULL)
518 {
519 /* map not open, try to open now */
520 if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
521 return EX_TEMPFAIL;
522 }
523
524 sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
525 msgid = sm_ldap_search(&LDAPLMAP, name);
526 if (msgid == -1)
527 {
528 save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
529 # ifdef LDAP_SERVER_DOWN
530 if (errno == LDAP_SERVER_DOWN)
531 {
532 /* server disappeared, try reopen on next search */
533 sm_ldap_close(&LDAPLMAP);
534 }
535 # endif /* LDAP_SERVER_DOWN */
536 errno = save_errno;
537 return EX_TEMPFAIL;
538 }
539
540 /* Get results */
541 ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
542 (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
543 &(LDAPLMAP.ldap_timeout)),
544 &(LDAPLMAP.ldap_res));
545
546 if (ret != LDAP_RES_SEARCH_RESULT &&
547 ret != LDAP_RES_SEARCH_ENTRY)
548 {
549 if (ret == 0)
550 errno = ETIMEDOUT;
551 else
552 errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
553 ret = EX_TEMPFAIL;
554 goto abort;
555 }
556
557 entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
558 if (entry == NULL)
559 {
560 int rc;
561
562 /*
563 ** We may have gotten an LDAP_RES_SEARCH_RESULT response
564 ** with an error inside it, so we have to extract that
565 ** with ldap_parse_result(). This can happen when talking
566 ** to an LDAP proxy whose backend has gone down.
567 */
568
569 save_errno = ldap_parse_result(LDAPLMAP.ldap_ld,
570 LDAPLMAP.ldap_res, &rc, NULL,
571 NULL, NULL, NULL, 0);
572 if (save_errno == LDAP_SUCCESS)
573 save_errno = rc;
574 if (save_errno == LDAP_SUCCESS)
575 {
576 errno = ENOENT;
577 ret = EX_NOUSER;
578 }
579 else
580 {
581 errno = save_errno;
582 ret = EX_TEMPFAIL;
583 }
584 goto abort;
585 }
586
587 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
588 /*
589 ** Reset value to prevent lingering
590 ** LDAP_DECODING_ERROR due to
591 ** OpenLDAP 1.X's hack (see below)
592 */
593
594 LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
595 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
596
597 ret = EX_OK;
598 need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
599 for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
600 attr != NULL;
601 attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
602 {
603 char **vals;
604
605 vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
606 if (vals == NULL)
607 {
608 errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
609 if (errno == LDAP_SUCCESS)
610 {
611 ldap_memfree(attr);
612 continue;
613 }
614
615 /* Must be an error */
616 errno += E_LDAPBASE;
617 ret = EX_TEMPFAIL;
618 goto abort;
619 }
620
621 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
622 /*
623 ** Reset value to prevent lingering
624 ** LDAP_DECODING_ERROR due to
625 ** OpenLDAP 1.X's hack (see below)
626 */
627
628 LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
629 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
630
631 if (vals[0] == NULL || vals[0][0] == '\0')
632 goto skip;
633
634 if (strcasecmp(attr, "gecos") == 0)
635 {
636 if (!bitset(NEED_FULLNAME, need) ||
637 strlen(vals[0]) >= sizeof(user->mbdb_fullname))
638 goto skip;
639
640 sm_pwfullname(vals[0], name, user->mbdb_fullname,
641 sizeof(user->mbdb_fullname));
642 need &= ~NEED_FULLNAME;
643 }
644 else if (strcasecmp(attr, "homeDirectory") == 0)
645 {
646 if (!bitset(NEED_HOMEDIR, need) ||
647 strlen(vals[0]) >= sizeof(user->mbdb_homedir))
648 goto skip;
649
650 (void) sm_strlcpy(user->mbdb_homedir, vals[0],
651 sizeof(user->mbdb_homedir));
652 need &= ~NEED_HOMEDIR;
653 }
654 else if (strcasecmp(attr, "loginShell") == 0)
655 {
656 if (!bitset(NEED_SHELL, need) ||
657 strlen(vals[0]) >= sizeof(user->mbdb_shell))
658 goto skip;
659
660 (void) sm_strlcpy(user->mbdb_shell, vals[0],
661 sizeof(user->mbdb_shell));
662 need &= ~NEED_SHELL;
663 }
664 else if (strcasecmp(attr, "uidNumber") == 0)
665 {
666 char *p;
667
668 if (!bitset(NEED_UID, need))
669 goto skip;
670
671 for (p = vals[0]; *p != '\0'; p++)
672 {
673 /* allow negative numbers */
674 if (p == vals[0] && *p == '-')
675 {
676 /* but not simply '-' */
677 if (*(p + 1) == '\0')
678 goto skip;
679 }
680 else if (!isascii(*p) || !isdigit(*p))
681 goto skip;
682 }
683 user->mbdb_uid = atoi(vals[0]);
684 need &= ~NEED_UID;
685 }
686 else if (strcasecmp(attr, "gidNumber") == 0)
687 {
688 char *p;
689
690 if (!bitset(NEED_GID, need))
691 goto skip;
692
693 for (p = vals[0]; *p != '\0'; p++)
694 {
695 /* allow negative numbers */
696 if (p == vals[0] && *p == '-')
697 {
698 /* but not simply '-' */
699 if (*(p + 1) == '\0')
700 goto skip;
701 }
702 else if (!isascii(*p) || !isdigit(*p))
703 goto skip;
704 }
705 user->mbdb_gid = atoi(vals[0]);
706 need &= ~NEED_GID;
707 }
708
709 skip:
710 ldap_value_free(vals);
711 ldap_memfree(attr);
712 }
713
714 errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
715
716 /*
717 ** We check errno != LDAP_DECODING_ERROR since
718 ** OpenLDAP 1.X has a very ugly *undocumented*
719 ** hack of returning this error code from
720 ** ldap_next_attribute() if the library freed the
721 ** ber attribute. See:
722 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
723 */
724
725 if (errno != LDAP_SUCCESS &&
726 errno != LDAP_DECODING_ERROR)
727 {
728 /* Must be an error */
729 errno += E_LDAPBASE;
730 ret = EX_TEMPFAIL;
731 goto abort;
732 }
733
734 abort:
735 save_errno = errno;
736 if (attr != NULL)
737 {
738 ldap_memfree(attr);
739 attr = NULL;
740 }
741 if (LDAPLMAP.ldap_res != NULL)
742 {
743 ldap_msgfree(LDAPLMAP.ldap_res);
744 LDAPLMAP.ldap_res = NULL;
745 }
746 if (ret == EX_OK)
747 {
748 if (need == 0)
749 {
750 (void) sm_strlcpy(user->mbdb_name, name,
751 sizeof(user->mbdb_name));
752 save_errno = 0;
753 }
754 else
755 {
756 ret = EX_NOUSER;
757 save_errno = EINVAL;
758 }
759 }
760 errno = save_errno;
761 return ret;
762 }
763
764 /*
765 ** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
766 **
767 ** Parameters:
768 ** none.
769 **
770 ** Results:
771 ** none.
772 */
773
774 static void
mbdb_ldap_terminate()775 mbdb_ldap_terminate()
776 {
777 sm_ldap_close(&LDAPLMAP);
778 }
779 # endif /* _LDAP_EXAMPLE_ */
780 #endif /* LDAPMAP */
781