1 /* 2 * Copyright (c) 2001-2002 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: ldap.c,v 1.18 2002/01/11 22:06:51 gshapiro Exp $") 12 13 #if LDAPMAP 14 # include <sys/types.h> 15 # include <errno.h> 16 # include <setjmp.h> 17 # include <stdlib.h> 18 # include <unistd.h> 19 20 # include <sm/bitops.h> 21 # include <sm/clock.h> 22 # include <sm/conf.h> 23 # include <sm/debug.h> 24 # include <sm/errstring.h> 25 # include <sm/ldap.h> 26 # include <sm/string.h> 27 # include <sm/sysexits.h> 28 29 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap", 30 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $"); 31 32 static void ldaptimeout __P((int)); 33 34 /* 35 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT 36 ** 37 ** Parameters: 38 ** lmap -- pointer to SM_LDAP_STRUCT to clear 39 ** 40 ** Returns: 41 ** None. 42 ** 43 */ 44 45 void 46 sm_ldap_clear(lmap) 47 SM_LDAP_STRUCT *lmap; 48 { 49 if (lmap == NULL) 50 return; 51 52 lmap->ldap_host = NULL; 53 lmap->ldap_port = LDAP_PORT; 54 lmap->ldap_deref = LDAP_DEREF_NEVER; 55 lmap->ldap_timelimit = LDAP_NO_LIMIT; 56 lmap->ldap_sizelimit = LDAP_NO_LIMIT; 57 # ifdef LDAP_REFERRALS 58 lmap->ldap_options = LDAP_OPT_REFERRALS; 59 # else /* LDAP_REFERRALS */ 60 lmap->ldap_options = 0; 61 # endif /* LDAP_REFERRALS */ 62 lmap->ldap_attrsep = '\0'; 63 lmap->ldap_binddn = NULL; 64 lmap->ldap_secret = NULL; 65 lmap->ldap_method = LDAP_AUTH_SIMPLE; 66 lmap->ldap_base = NULL; 67 lmap->ldap_scope = LDAP_SCOPE_SUBTREE; 68 lmap->ldap_attrsonly = LDAPMAP_FALSE; 69 lmap->ldap_timeout.tv_sec = 0; 70 lmap->ldap_timeout.tv_usec = 0; 71 lmap->ldap_ld = NULL; 72 lmap->ldap_filter = NULL; 73 lmap->ldap_attr[0] = NULL; 74 #if _FFR_LDAP_RECURSION 75 lmap->ldap_attr_type[0] = LDAPMAP_ATTR_NORMAL; 76 lmap->ldap_attr_final[0] = NULL; 77 #endif /* _FFR_LDAP_RECURSION */ 78 lmap->ldap_res = NULL; 79 lmap->ldap_next = NULL; 80 lmap->ldap_pid = 0; 81 } 82 83 /* 84 ** SM_LDAP_START -- actually connect to an LDAP server 85 ** 86 ** Parameters: 87 ** name -- name of map for debug output. 88 ** lmap -- the LDAP map being opened. 89 ** 90 ** Returns: 91 ** true if connection is successful, false otherwise. 92 ** 93 ** Side Effects: 94 ** Populates lmap->ldap_ld. 95 */ 96 97 static jmp_buf LDAPTimeout; 98 99 #define SM_LDAP_SETTIMEOUT(to) \ 100 do \ 101 { \ 102 if (to != 0) \ 103 { \ 104 if (setjmp(LDAPTimeout) != 0) \ 105 { \ 106 errno = ETIMEDOUT; \ 107 return false; \ 108 } \ 109 ev = sm_setevent(to, ldaptimeout, 0); \ 110 } \ 111 } while (0) 112 113 #define SM_LDAP_CLEARTIMEOUT() \ 114 do \ 115 { \ 116 if (ev != NULL) \ 117 sm_clrevent(ev); \ 118 } while (0) 119 120 bool 121 sm_ldap_start(name, lmap) 122 char *name; 123 SM_LDAP_STRUCT *lmap; 124 { 125 int bind_result; 126 int save_errno; 127 SM_EVENT *ev = NULL; 128 LDAP *ld; 129 130 if (sm_debug_active(&SmLDAPTrace, 2)) 131 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name); 132 133 if (sm_debug_active(&SmLDAPTrace, 9)) 134 sm_dprintf("ldapmap_start(%s, %d)\n", 135 lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host, 136 lmap->ldap_port); 137 138 # if USE_LDAP_INIT 139 ld = ldap_init(lmap->ldap_host, lmap->ldap_port); 140 save_errno = errno; 141 # else /* USE_LDAP_INIT */ 142 /* 143 ** If using ldap_open(), the actual connection to the server 144 ** happens now so we need the timeout here. For ldap_init(), 145 ** the connection happens at bind time. 146 */ 147 148 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec); 149 ld = ldap_open(lmap->ldap_host, lmap->ldap_port); 150 save_errno = errno; 151 152 /* clear the event if it has not sprung */ 153 SM_LDAP_CLEARTIMEOUT(); 154 # endif /* USE_LDAP_INIT */ 155 156 errno = save_errno; 157 if (ld == NULL) 158 return false; 159 160 sm_ldap_setopts(ld, lmap); 161 162 # if USE_LDAP_INIT 163 /* 164 ** If using ldap_init(), the actual connection to the server 165 ** happens at ldap_bind_s() so we need the timeout here. 166 */ 167 168 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec); 169 # endif /* USE_LDAP_INIT */ 170 171 # ifdef LDAP_AUTH_KRBV4 172 if (lmap->ldap_method == LDAP_AUTH_KRBV4 && 173 lmap->ldap_secret != NULL) 174 { 175 /* 176 ** Need to put ticket in environment here instead of 177 ** during parseargs as there may be different tickets 178 ** for different LDAP connections. 179 */ 180 181 (void) putenv(lmap->ldap_secret); 182 } 183 # endif /* LDAP_AUTH_KRBV4 */ 184 185 bind_result = ldap_bind_s(ld, lmap->ldap_binddn, 186 lmap->ldap_secret, lmap->ldap_method); 187 188 # if USE_LDAP_INIT 189 /* clear the event if it has not sprung */ 190 SM_LDAP_CLEARTIMEOUT(); 191 # endif /* USE_LDAP_INIT */ 192 193 if (bind_result != LDAP_SUCCESS) 194 { 195 errno = bind_result + E_LDAPBASE; 196 return false; 197 } 198 199 /* Save PID to make sure only this PID closes the LDAP connection */ 200 lmap->ldap_pid = getpid(); 201 lmap->ldap_ld = ld; 202 return true; 203 } 204 205 /* ARGSUSED */ 206 static void 207 ldaptimeout(unused) 208 int unused; 209 { 210 /* 211 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 212 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 213 ** DOING. 214 */ 215 216 errno = ETIMEDOUT; 217 longjmp(LDAPTimeout, 1); 218 } 219 220 /* 221 ** SM_LDAP_SEARCH -- iniate LDAP search 222 ** 223 ** Initiate an LDAP search, return the msgid. 224 ** The calling function must collect the results. 225 ** 226 ** Parameters: 227 ** lmap -- LDAP map information 228 ** key -- key to substitute in LDAP filter 229 ** 230 ** Returns: 231 ** -1 on failure, msgid on success 232 ** 233 */ 234 235 int 236 sm_ldap_search(lmap, key) 237 SM_LDAP_STRUCT *lmap; 238 char *key; 239 { 240 int msgid; 241 char *fp, *p, *q; 242 char filter[LDAPMAP_MAX_FILTER + 1]; 243 244 /* substitute key into filter, perhaps multiple times */ 245 memset(filter, '\0', sizeof filter); 246 fp = filter; 247 p = lmap->ldap_filter; 248 while ((q = strchr(p, '%')) != NULL) 249 { 250 if (q[1] == 's') 251 { 252 (void) sm_snprintf(fp, SPACELEFT(filter, fp), 253 "%.*s%s", (int) (q - p), p, key); 254 fp += strlen(fp); 255 p = q + 2; 256 } 257 else if (q[1] == '0') 258 { 259 char *k = key; 260 261 (void) sm_snprintf(fp, SPACELEFT(filter, fp), 262 "%.*s", (int) (q - p), p); 263 fp += strlen(fp); 264 p = q + 2; 265 266 /* Properly escape LDAP special characters */ 267 while (SPACELEFT(filter, fp) > 0 && 268 *k != '\0') 269 { 270 if (*k == '*' || *k == '(' || 271 *k == ')' || *k == '\\') 272 { 273 (void) sm_strlcat(fp, 274 (*k == '*' ? "\\2A" : 275 (*k == '(' ? "\\28" : 276 (*k == ')' ? "\\29" : 277 (*k == '\\' ? "\\5C" : 278 "\00")))), 279 SPACELEFT(filter, fp)); 280 fp += strlen(fp); 281 k++; 282 } 283 else 284 *fp++ = *k++; 285 } 286 } 287 else 288 { 289 (void) sm_snprintf(fp, SPACELEFT(filter, fp), 290 "%.*s", (int) (q - p + 1), p); 291 p = q + (q[1] == '%' ? 2 : 1); 292 fp += strlen(fp); 293 } 294 } 295 (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp)); 296 if (sm_debug_active(&SmLDAPTrace, 20)) 297 sm_dprintf("ldap search filter=%s\n", filter); 298 299 lmap->ldap_res = NULL; 300 msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, lmap->ldap_scope, 301 filter, 302 (lmap->ldap_attr[0] == NULL ? NULL : 303 lmap->ldap_attr), 304 lmap->ldap_attrsonly); 305 return msgid; 306 } 307 308 # if _FFR_LDAP_RECURSION 309 /* 310 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result 311 ** 312 ** Parameters: 313 ** lmap -- pointer to SM_LDAP_STRUCT in use 314 ** msgid -- msgid returned by sm_ldap_search() 315 ** flags -- flags for the lookup 316 ** delim -- delimiter for result concatenation 317 ** rpool -- memory pool for storage 318 ** result -- return string 319 ** recurse -- recursion list 320 ** 321 ** Returns: 322 ** status (sysexit) 323 */ 324 325 # define LDAPMAP_ERROR_CLEANUP() \ 326 { \ 327 if (lmap->ldap_res != NULL) \ 328 { \ 329 ldap_msgfree(lmap->ldap_res); \ 330 lmap->ldap_res = NULL; \ 331 } \ 332 (void) ldap_abandon(lmap->ldap_ld, msgid); \ 333 } 334 335 static int 336 ldapmap_add_recurse(top, item, type, rpool) 337 SM_LDAP_RECURSE_LIST **top; 338 char *item; 339 int type; 340 SM_RPOOL_T *rpool; 341 { 342 SM_LDAP_RECURSE_LIST *p; 343 SM_LDAP_RECURSE_LIST *last; 344 345 last = NULL; 346 for (p = *top; p != NULL; p = p->lr_next) 347 { 348 if (strcasecmp(item, p->lr_search) == 0 && 349 type == p->lr_type) 350 { 351 /* already on list */ 352 return 1; 353 } 354 last = p; 355 } 356 357 /* not on list, add it */ 358 p = sm_rpool_malloc_x(rpool, sizeof *p); 359 p->lr_search = sm_rpool_strdup_x(rpool, item); 360 p->lr_type = type; 361 p->lr_next = NULL; 362 if (last == NULL) 363 *top = p; 364 else 365 last->lr_next = p; 366 return 0; 367 } 368 369 int 370 sm_ldap_results(lmap, msgid, flags, delim, rpool, result, recurse) 371 SM_LDAP_STRUCT *lmap; 372 int msgid; 373 int flags; 374 char delim; 375 SM_RPOOL_T *rpool; 376 char **result; 377 SM_LDAP_RECURSE_LIST *recurse; 378 { 379 bool toplevel; 380 int i; 381 int entries = 0; 382 int statp; 383 int vsize; 384 int ret; 385 int save_errno; 386 char *p; 387 388 /* Are we the top top level of the search? */ 389 toplevel = (recurse == NULL); 390 391 /* Get results */ 392 statp = EX_NOTFOUND; 393 while ((ret = ldap_result(lmap->ldap_ld, msgid, 0, 394 (lmap->ldap_timeout.tv_sec == 0 ? NULL : 395 &(lmap->ldap_timeout)), 396 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY) 397 { 398 LDAPMessage *entry; 399 400 if (bitset(SM_LDAP_SINGLEMATCH, flags)) 401 { 402 entries += ldap_count_entries(lmap->ldap_ld, 403 lmap->ldap_res); 404 if (entries > 1) 405 { 406 LDAPMAP_ERROR_CLEANUP(); 407 errno = ENOENT; 408 return EX_NOTFOUND; 409 } 410 } 411 412 /* If we don't want multiple values and we have one, break */ 413 if (delim == '\0' && *result != NULL) 414 break; 415 416 /* Cycle through all entries */ 417 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); 418 entry != NULL; 419 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) 420 { 421 BerElement *ber; 422 char *attr; 423 char **vals = NULL; 424 char *dn; 425 426 /* 427 ** If matching only and found an entry, 428 ** no need to spin through attributes 429 */ 430 431 if (statp == EX_OK && 432 bitset(SM_LDAP_MATCHONLY, flags)) 433 continue; 434 435 /* record completed DN's to prevent loops */ 436 dn = ldap_get_dn(lmap->ldap_ld, entry); 437 if (dn == NULL) 438 { 439 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 440 save_errno += E_LDAPBASE; 441 LDAPMAP_ERROR_CLEANUP(); 442 errno = save_errno; 443 return EX_OSERR; 444 } 445 446 switch (ldapmap_add_recurse(&recurse, dn, 447 LDAPMAP_ATTR_NORMAL, 448 rpool)) 449 { 450 case -1: 451 /* error adding */ 452 ldap_memfree(dn); 453 LDAPMAP_ERROR_CLEANUP(); 454 errno = ENOMEM; 455 return EX_OSERR; 456 457 case 1: 458 /* already on list, skip it */ 459 ldap_memfree(dn); 460 continue; 461 } 462 ldap_memfree(dn); 463 464 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 465 /* 466 ** Reset value to prevent lingering 467 ** LDAP_DECODING_ERROR due to 468 ** OpenLDAP 1.X's hack (see below) 469 */ 470 471 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 472 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 473 474 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 475 &ber); 476 attr != NULL; 477 attr = ldap_next_attribute(lmap->ldap_ld, entry, 478 ber)) 479 { 480 char *tmp, *vp_tmp; 481 int type; 482 483 for (i = 0; lmap->ldap_attr[i] != NULL; i++) 484 { 485 if (sm_strcasecmp(lmap->ldap_attr[i], 486 attr) == 0) 487 { 488 type = lmap->ldap_attr_type[i]; 489 break; 490 } 491 } 492 if (lmap->ldap_attr[i] == NULL) 493 { 494 /* attribute not requested */ 495 # if USING_NETSCAPE_LDAP 496 ldap_memfree(attr); 497 # endif /* USING_NETSCAPE_LDAP */ 498 LDAPMAP_ERROR_CLEANUP(); 499 errno = EFAULT; 500 return EX_SOFTWARE; 501 } 502 503 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 504 { 505 vals = ldap_get_values(lmap->ldap_ld, 506 entry, 507 attr); 508 if (vals == NULL) 509 { 510 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 511 if (save_errno == LDAP_SUCCESS) 512 { 513 # if USING_NETSCAPE_LDAP 514 ldap_memfree(attr); 515 # endif /* USING_NETSCAPE_LDAP */ 516 continue; 517 } 518 519 /* Must be an error */ 520 save_errno += E_LDAPBASE; 521 # if USING_NETSCAPE_LDAP 522 ldap_memfree(attr); 523 # endif /* USING_NETSCAPE_LDAP */ 524 LDAPMAP_ERROR_CLEANUP(); 525 errno = save_errno; 526 return EX_TEMPFAIL; 527 } 528 } 529 530 statp = EX_OK; 531 532 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 533 /* 534 ** Reset value to prevent lingering 535 ** LDAP_DECODING_ERROR due to 536 ** OpenLDAP 1.X's hack (see below) 537 */ 538 539 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 540 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 541 542 /* 543 ** If matching only, 544 ** no need to spin through entries 545 */ 546 547 if (bitset(SM_LDAP_MATCHONLY, flags)) 548 { 549 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 550 ldap_value_free(vals); 551 552 # if USING_NETSCAPE_LDAP 553 ldap_memfree(attr); 554 # endif /* USING_NETSCAPE_LDAP */ 555 continue; 556 } 557 558 /* 559 ** If we don't want multiple values, 560 ** return first found. 561 */ 562 563 if (delim == '\0') 564 { 565 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 566 { 567 *result = sm_rpool_strdup_x(rpool, 568 attr); 569 # if USING_NETSCAPE_LDAP 570 ldap_memfree(attr); 571 # endif /* USING_NETSCAPE_LDAP */ 572 break; 573 } 574 575 if (vals[0] == NULL) 576 { 577 ldap_value_free(vals); 578 # if USING_NETSCAPE_LDAP 579 ldap_memfree(attr); 580 # endif /* USING_NETSCAPE_LDAP */ 581 continue; 582 } 583 584 vsize = strlen(vals[0]) + 1; 585 if (lmap->ldap_attrsep != '\0') 586 vsize += strlen(attr) + 1; 587 *result = sm_rpool_malloc_x(rpool, 588 vsize); 589 if (lmap->ldap_attrsep != '\0') 590 sm_snprintf(*result, vsize, 591 "%s%c%s", 592 attr, 593 lmap->ldap_attrsep, 594 vals[0]); 595 else 596 sm_strlcpy(*result, vals[0], 597 vsize); 598 ldap_value_free(vals); 599 # if USING_NETSCAPE_LDAP 600 ldap_memfree(attr); 601 # endif /* USING_NETSCAPE_LDAP */ 602 break; 603 } 604 605 /* attributes only */ 606 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 607 { 608 if (*result == NULL) 609 *result = sm_rpool_strdup_x(rpool, 610 attr); 611 else 612 { 613 vsize = strlen(*result) + 614 strlen(attr) + 2; 615 tmp = sm_rpool_malloc_x(rpool, 616 vsize); 617 (void) sm_snprintf(tmp, 618 vsize, "%s%c%s", 619 *result, delim, 620 attr); 621 *result = tmp; 622 } 623 # if USING_NETSCAPE_LDAP 624 ldap_memfree(attr); 625 # endif /* USING_NETSCAPE_LDAP */ 626 continue; 627 } 628 629 /* 630 ** If there is more than one, 631 ** munge then into a map_coldelim 632 ** separated string 633 */ 634 635 vsize = 0; 636 for (i = 0; vals[i] != NULL; i++) 637 { 638 if (type == LDAPMAP_ATTR_DN || 639 type == LDAPMAP_ATTR_FILTER || 640 type == LDAPMAP_ATTR_URL) 641 { 642 if (ldapmap_add_recurse(&recurse, 643 vals[i], 644 type) < 0) 645 { 646 LDAPMAP_ERROR_CLEANUP(); 647 errno = ENOMEM; 648 return EX_OSERR; 649 } 650 } 651 if (type != LDAPMAP_ATTR_NORMAL) 652 { 653 # if USING_NETSCAPE_LDAP 654 ldap_memfree(attr); 655 # endif /* USING_NETSCAPE_LDAP */ 656 continue; 657 } 658 vsize += strlen(vals[i]) + 1; 659 if (lmap->ldap_attrsep != '\0') 660 vsize += strlen(attr) + 1; 661 } 662 vp_tmp = sm_rpool_malloc_x(rpool, vsize); 663 *vp_tmp = '\0'; 664 665 p = vp_tmp; 666 for (i = 0; vals[i] != NULL; i++) 667 { 668 if (lmap->ldap_attrsep != '\0') 669 { 670 p += sm_strlcpy(p, attr, 671 vsize - (p - vp_tmp)); 672 *p++ = lmap->ldap_attrsep; 673 } 674 p += sm_strlcpy(p, vals[i], 675 vsize - (p - vp_tmp)); 676 if (p >= vp_tmp + vsize) 677 { 678 /* Internal error: buffer too small for LDAP values */ 679 LDAPMAP_ERROR_CLEANUP(); 680 errno = ENOMEM; 681 return EX_OSERR; 682 } 683 if (vals[i + 1] != NULL) 684 *p++ = delim; 685 } 686 687 ldap_value_free(vals); 688 # if USING_NETSCAPE_LDAP 689 ldap_memfree(attr); 690 # endif /* USING_NETSCAPE_LDAP */ 691 if (*result == NULL) 692 { 693 *result = vp_tmp; 694 continue; 695 } 696 vsize = strlen(*result) + strlen(vp_tmp) + 2; 697 tmp = sm_rpool_malloc_x(rpool, vsize); 698 (void) sm_snprintf(tmp, vsize, "%s%c%s", 699 *result, delim, vp_tmp); 700 *result = tmp; 701 } 702 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 703 704 /* 705 ** We check save_errno != LDAP_DECODING_ERROR since 706 ** OpenLDAP 1.X has a very ugly *undocumented* 707 ** hack of returning this error code from 708 ** ldap_next_attribute() if the library freed the 709 ** ber attribute. See: 710 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 711 */ 712 713 if (save_errno != LDAP_SUCCESS && 714 save_errno != LDAP_DECODING_ERROR) 715 { 716 /* Must be an error */ 717 save_errno += E_LDAPBASE; 718 LDAPMAP_ERROR_CLEANUP(); 719 errno = save_errno; 720 return EX_TEMPFAIL; 721 } 722 723 /* We don't want multiple values and we have one */ 724 if (delim == '\0' && *result != NULL) 725 break; 726 } 727 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 728 if (save_errno != LDAP_SUCCESS && 729 save_errno != LDAP_DECODING_ERROR) 730 { 731 /* Must be an error */ 732 save_errno += E_LDAPBASE; 733 LDAPMAP_ERROR_CLEANUP(); 734 errno = save_errno; 735 return EX_TEMPFAIL; 736 } 737 ldap_msgfree(lmap->ldap_res); 738 lmap->ldap_res = NULL; 739 } 740 741 if (ret == 0) 742 save_errno = ETIMEDOUT; 743 else 744 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 745 if (save_errno != LDAP_SUCCESS) 746 { 747 statp = EX_TEMPFAIL; 748 if (ret != 0) 749 { 750 switch (save_errno) 751 { 752 #ifdef LDAP_SERVER_DOWN 753 case LDAP_SERVER_DOWN: 754 #endif /* LDAP_SERVER_DOWN */ 755 case LDAP_TIMEOUT: 756 case LDAP_UNAVAILABLE: 757 /* server disappeared, try reopen on next search */ 758 statp = EX_RESTART; 759 break; 760 } 761 save_errno += E_LDAPBASE; 762 } 763 LDAPMAP_ERROR_CLEANUP(); 764 errno = save_errno; 765 return statp; 766 } 767 768 if (lmap->ldap_res != NULL) 769 { 770 ldap_msgfree(lmap->ldap_res); 771 lmap->ldap_res = NULL; 772 } 773 774 if (toplevel) 775 { 776 SM_LDAP_RECURSE_LIST *rl; 777 778 /* 779 ** Spin through the built-up recurse list at the top 780 ** of the recursion. Since new items are added at the 781 ** end of the shared list, we actually only ever get 782 ** one level of recursion before things pop back to the 783 ** top. Any items added to the list during that recursion 784 ** will be expanded by the top level. 785 */ 786 787 for (rl = recurse; rl != NULL; rl = rl->lr_next) 788 { 789 int sid; 790 int status; 791 792 if (rl->lr_type == LDAPMAP_ATTR_NORMAL) 793 { 794 /* already expanded */ 795 continue; 796 } 797 else if (rl->lr_type == LDAPMAP_ATTR_DN) 798 { 799 /* do DN search */ 800 sid = ldap_search(lmap->ldap_ld, 801 rl->lr_search, 802 lmap->ldap_scope, 803 "(objectClass=*)", 804 lmap->ldap_attr_final, 805 lmap->ldap_attrsonly); 806 } 807 else if (rl->lr_type == LDAPMAP_ATTR_FILTER) 808 { 809 /* do new search */ 810 sid = ldap_search(lmap->ldap_ld, 811 lmap->ldap_base, 812 lmap->ldap_scope, 813 rl->lr_search, 814 lmap->ldap_attr_final, 815 lmap->ldap_attrsonly); 816 } 817 else if (rl->lr_type == LDAPMAP_ATTR_URL) 818 { 819 /* do new URL search */ 820 sid = ldap_url_search(lmap->ldap_ld, 821 rl->lr_search, 822 lmap->ldap_attrsonly); 823 } 824 else 825 { 826 /* unknown or illegal attribute type */ 827 errno = EFAULT; 828 return EX_SOFTWARE; 829 } 830 831 /* Collect results */ 832 if (sid == -1) 833 { 834 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 835 statp = EX_TEMPFAIL; 836 switch (save_errno) 837 { 838 #ifdef LDAP_SERVER_DOWN 839 case LDAP_SERVER_DOWN: 840 #endif /* LDAP_SERVER_DOWN */ 841 case LDAP_TIMEOUT: 842 case LDAP_UNAVAILABLE: 843 /* server disappeared, try reopen on next search */ 844 statp = EX_RESTART; 845 break; 846 } 847 errno = save_errno + E_LDAPBASE; 848 return statp; 849 } 850 851 status = sm_ldap_results(lmap, sid, flags, delim, 852 rpool, result, recurse); 853 save_errno = errno; 854 if (status != EX_OK && status != EX_NOTFOUND) 855 { 856 errno = save_errno; 857 return status; 858 } 859 860 /* Mark as done */ 861 rl->lr_type = LDAPMAP_ATTR_NORMAL; 862 } 863 } 864 return statp; 865 } 866 #endif /* _FFR_LDAP_RECURSION */ 867 868 /* 869 ** SM_LDAP_CLOSE -- close LDAP connection 870 ** 871 ** Parameters: 872 ** lmap -- LDAP map information 873 ** 874 ** Returns: 875 ** None. 876 ** 877 */ 878 879 void 880 sm_ldap_close(lmap) 881 SM_LDAP_STRUCT *lmap; 882 { 883 if (lmap->ldap_ld == NULL) 884 return; 885 886 if (lmap->ldap_pid == getpid()) 887 ldap_unbind(lmap->ldap_ld); 888 lmap->ldap_ld = NULL; 889 lmap->ldap_pid = 0; 890 } 891 892 /* 893 ** SM_LDAP_SETOPTS -- set LDAP options 894 ** 895 ** Parameters: 896 ** ld -- LDAP session handle 897 ** lmap -- LDAP map information 898 ** 899 ** Returns: 900 ** None. 901 ** 902 */ 903 904 void 905 sm_ldap_setopts(ld, lmap) 906 LDAP *ld; 907 SM_LDAP_STRUCT *lmap; 908 { 909 # if USE_LDAP_SET_OPTION 910 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 911 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 912 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 913 else 914 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 915 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 916 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 917 # else /* USE_LDAP_SET_OPTION */ 918 /* From here on in we can use ldap internal timelimits */ 919 ld->ld_deref = lmap->ldap_deref; 920 ld->ld_options = lmap->ldap_options; 921 ld->ld_sizelimit = lmap->ldap_sizelimit; 922 ld->ld_timelimit = lmap->ldap_timelimit; 923 # endif /* USE_LDAP_SET_OPTION */ 924 } 925 926 /* 927 ** SM_LDAP_GETERRNO -- get ldap errno value 928 ** 929 ** Parameters: 930 ** ld -- LDAP session handle 931 ** 932 ** Returns: 933 ** LDAP errno. 934 ** 935 */ 936 937 int 938 sm_ldap_geterrno(ld) 939 LDAP *ld; 940 { 941 int err = LDAP_SUCCESS; 942 943 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 944 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 945 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 946 # ifdef LDAP_OPT_SIZELIMIT 947 err = ldap_get_lderrno(ld, NULL, NULL); 948 # else /* LDAP_OPT_SIZELIMIT */ 949 err = ld->ld_errno; 950 951 /* 952 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 953 ** OpenLDAP 1.X's hack (see above) 954 */ 955 956 ld->ld_errno = LDAP_SUCCESS; 957 # endif /* LDAP_OPT_SIZELIMIT */ 958 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 959 return err; 960 } 961 # endif /* LDAPMAP */ 962