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
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 /* _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
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 #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
mbdb_pw_terminate()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
mbdb_ldap_initialize(arg)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
mbdb_ldap_lookup(name,user)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
mbdb_ldap_terminate()782 mbdb_ldap_terminate()
783 {
784 sm_ldap_close(&LDAPLMAP);
785 }
786 # endif /* _LDAP_EXAMPLE_ */
787 #endif /* LDAPMAP */
788