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 /* 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 _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 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 /* MBDB_LDAP_FILTER */ 412 413 # ifndef MBDB_DEFAULT_LDAP_BASEDN 414 # define MBDB_DEFAULT_LDAP_BASEDN NULL 415 # endif /* MBDB_DEFAULT_LDAP_BASEDN */ 416 417 # ifndef MBDB_DEFAULT_LDAP_SERVER 418 # define MBDB_DEFAULT_LDAP_SERVER NULL 419 # endif /* MBDB_DEFAULT_LDAP_SERVER */ 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 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 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 775 mbdb_ldap_terminate() 776 { 777 sm_ldap_close(&LDAPLMAP); 778 } 779 # endif /* _LDAP_EXAMPLE_ */ 780 #endif /* LDAPMAP */ 781