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.44 2002/02/22 21:54:02 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 # ifdef EX_OK 28 # undef EX_OK /* for SVr4.2 SMP */ 29 # endif /* EX_OK */ 30 # include <sm/sysexits.h> 31 32 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap", 33 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $"); 34 35 static void ldaptimeout __P((int)); 36 37 /* 38 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT 39 ** 40 ** Parameters: 41 ** lmap -- pointer to SM_LDAP_STRUCT to clear 42 ** 43 ** Returns: 44 ** None. 45 ** 46 */ 47 48 void 49 sm_ldap_clear(lmap) 50 SM_LDAP_STRUCT *lmap; 51 { 52 if (lmap == NULL) 53 return; 54 55 lmap->ldap_target = NULL; 56 lmap->ldap_port = LDAP_PORT; 57 #if _FFR_LDAP_URI 58 lmap->ldap_uri = false; 59 #endif /* _FFR_LDAP_URI */ 60 # if _FFR_LDAP_SETVERSION 61 lmap->ldap_version = 0; 62 # endif /* _FFR_LDAP_SETVERSION */ 63 lmap->ldap_deref = LDAP_DEREF_NEVER; 64 lmap->ldap_timelimit = LDAP_NO_LIMIT; 65 lmap->ldap_sizelimit = LDAP_NO_LIMIT; 66 # ifdef LDAP_REFERRALS 67 lmap->ldap_options = LDAP_OPT_REFERRALS; 68 # else /* LDAP_REFERRALS */ 69 lmap->ldap_options = 0; 70 # endif /* LDAP_REFERRALS */ 71 lmap->ldap_attrsep = '\0'; 72 lmap->ldap_binddn = NULL; 73 lmap->ldap_secret = NULL; 74 lmap->ldap_method = LDAP_AUTH_SIMPLE; 75 lmap->ldap_base = NULL; 76 lmap->ldap_scope = LDAP_SCOPE_SUBTREE; 77 lmap->ldap_attrsonly = LDAPMAP_FALSE; 78 lmap->ldap_timeout.tv_sec = 0; 79 lmap->ldap_timeout.tv_usec = 0; 80 lmap->ldap_ld = NULL; 81 lmap->ldap_filter = NULL; 82 lmap->ldap_attr[0] = NULL; 83 #if _FFR_LDAP_RECURSION 84 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE; 85 lmap->ldap_attr_needobjclass[0] = NULL; 86 #endif /* _FFR_LDAP_RECURSION */ 87 lmap->ldap_res = NULL; 88 lmap->ldap_next = NULL; 89 lmap->ldap_pid = 0; 90 } 91 92 /* 93 ** SM_LDAP_START -- actually connect to an LDAP server 94 ** 95 ** Parameters: 96 ** name -- name of map for debug output. 97 ** lmap -- the LDAP map being opened. 98 ** 99 ** Returns: 100 ** true if connection is successful, false otherwise. 101 ** 102 ** Side Effects: 103 ** Populates lmap->ldap_ld. 104 */ 105 106 static jmp_buf LDAPTimeout; 107 108 #define SM_LDAP_SETTIMEOUT(to) \ 109 do \ 110 { \ 111 if (to != 0) \ 112 { \ 113 if (setjmp(LDAPTimeout) != 0) \ 114 { \ 115 errno = ETIMEDOUT; \ 116 return false; \ 117 } \ 118 ev = sm_setevent(to, ldaptimeout, 0); \ 119 } \ 120 } while (0) 121 122 #define SM_LDAP_CLEARTIMEOUT() \ 123 do \ 124 { \ 125 if (ev != NULL) \ 126 sm_clrevent(ev); \ 127 } while (0) 128 129 bool 130 sm_ldap_start(name, lmap) 131 char *name; 132 SM_LDAP_STRUCT *lmap; 133 { 134 int bind_result; 135 int save_errno; 136 SM_EVENT *ev = NULL; 137 LDAP *ld; 138 139 if (sm_debug_active(&SmLDAPTrace, 2)) 140 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name); 141 142 if (sm_debug_active(&SmLDAPTrace, 9)) 143 sm_dprintf("ldapmap_start(%s, %d)\n", 144 lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target, 145 lmap->ldap_port); 146 147 # if USE_LDAP_INIT 148 # if _FFR_LDAP_URI 149 if (lmap->ldap_uri) 150 errno = ldap_initialize(&ld, lmap->ldap_target); 151 else 152 # endif /* _FFR_LDAP_URI */ 153 ld = ldap_init(lmap->ldap_target, lmap->ldap_port); 154 save_errno = errno; 155 # else /* USE_LDAP_INIT */ 156 /* 157 ** If using ldap_open(), the actual connection to the server 158 ** happens now so we need the timeout here. For ldap_init(), 159 ** the connection happens at bind time. 160 */ 161 162 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec); 163 ld = ldap_open(lmap->ldap_target, lmap->ldap_port); 164 save_errno = errno; 165 166 /* clear the event if it has not sprung */ 167 SM_LDAP_CLEARTIMEOUT(); 168 # endif /* USE_LDAP_INIT */ 169 170 errno = save_errno; 171 if (ld == NULL) 172 return false; 173 174 sm_ldap_setopts(ld, lmap); 175 176 # if USE_LDAP_INIT 177 /* 178 ** If using ldap_init(), the actual connection to the server 179 ** happens at ldap_bind_s() so we need the timeout here. 180 */ 181 182 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec); 183 # endif /* USE_LDAP_INIT */ 184 185 # ifdef LDAP_AUTH_KRBV4 186 if (lmap->ldap_method == LDAP_AUTH_KRBV4 && 187 lmap->ldap_secret != NULL) 188 { 189 /* 190 ** Need to put ticket in environment here instead of 191 ** during parseargs as there may be different tickets 192 ** for different LDAP connections. 193 */ 194 195 (void) putenv(lmap->ldap_secret); 196 } 197 # endif /* LDAP_AUTH_KRBV4 */ 198 199 bind_result = ldap_bind_s(ld, lmap->ldap_binddn, 200 lmap->ldap_secret, lmap->ldap_method); 201 202 # if USE_LDAP_INIT 203 /* clear the event if it has not sprung */ 204 SM_LDAP_CLEARTIMEOUT(); 205 # endif /* USE_LDAP_INIT */ 206 207 if (bind_result != LDAP_SUCCESS) 208 { 209 errno = bind_result + E_LDAPBASE; 210 return false; 211 } 212 213 /* Save PID to make sure only this PID closes the LDAP connection */ 214 lmap->ldap_pid = getpid(); 215 lmap->ldap_ld = ld; 216 return true; 217 } 218 219 /* ARGSUSED */ 220 static void 221 ldaptimeout(unused) 222 int unused; 223 { 224 /* 225 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 226 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 227 ** DOING. 228 */ 229 230 errno = ETIMEDOUT; 231 longjmp(LDAPTimeout, 1); 232 } 233 234 /* 235 ** SM_LDAP_SEARCH -- iniate LDAP search 236 ** 237 ** Initiate an LDAP search, return the msgid. 238 ** The calling function must collect the results. 239 ** 240 ** Parameters: 241 ** lmap -- LDAP map information 242 ** key -- key to substitute in LDAP filter 243 ** 244 ** Returns: 245 ** -1 on failure, msgid on success 246 ** 247 */ 248 249 int 250 sm_ldap_search(lmap, key) 251 SM_LDAP_STRUCT *lmap; 252 char *key; 253 { 254 int msgid; 255 char *fp, *p, *q; 256 char filter[LDAPMAP_MAX_FILTER + 1]; 257 258 /* substitute key into filter, perhaps multiple times */ 259 memset(filter, '\0', sizeof filter); 260 fp = filter; 261 p = lmap->ldap_filter; 262 while ((q = strchr(p, '%')) != NULL) 263 { 264 if (q[1] == 's') 265 { 266 (void) sm_snprintf(fp, SPACELEFT(filter, fp), 267 "%.*s%s", (int) (q - p), p, key); 268 fp += strlen(fp); 269 p = q + 2; 270 } 271 else if (q[1] == '0') 272 { 273 char *k = key; 274 275 (void) sm_snprintf(fp, SPACELEFT(filter, fp), 276 "%.*s", (int) (q - p), p); 277 fp += strlen(fp); 278 p = q + 2; 279 280 /* Properly escape LDAP special characters */ 281 while (SPACELEFT(filter, fp) > 0 && 282 *k != '\0') 283 { 284 if (*k == '*' || *k == '(' || 285 *k == ')' || *k == '\\') 286 { 287 (void) sm_strlcat(fp, 288 (*k == '*' ? "\\2A" : 289 (*k == '(' ? "\\28" : 290 (*k == ')' ? "\\29" : 291 (*k == '\\' ? "\\5C" : 292 "\00")))), 293 SPACELEFT(filter, fp)); 294 fp += strlen(fp); 295 k++; 296 } 297 else 298 *fp++ = *k++; 299 } 300 } 301 else 302 { 303 (void) sm_snprintf(fp, SPACELEFT(filter, fp), 304 "%.*s", (int) (q - p + 1), p); 305 p = q + (q[1] == '%' ? 2 : 1); 306 fp += strlen(fp); 307 } 308 } 309 (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp)); 310 if (sm_debug_active(&SmLDAPTrace, 20)) 311 sm_dprintf("ldap search filter=%s\n", filter); 312 313 lmap->ldap_res = NULL; 314 msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, 315 lmap->ldap_scope, filter, 316 (lmap->ldap_attr[0] == NULL ? NULL : 317 lmap->ldap_attr), 318 lmap->ldap_attrsonly); 319 return msgid; 320 } 321 322 # if _FFR_LDAP_RECURSION 323 /* 324 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a 325 ** particular objectClass 326 ** 327 ** Parameters: 328 ** lmap -- pointer to SM_LDAP_STRUCT in use 329 ** entry -- current LDAP entry struct 330 ** ocvalue -- particular objectclass in question. 331 ** may be of form (fee|foo|fum) meaning 332 ** any entry can be part of either fee, 333 ** foo or fum objectclass 334 ** 335 ** Returns: 336 ** true if item has that objectClass 337 */ 338 339 static bool 340 sm_ldap_has_objectclass(lmap, entry, ocvalue) 341 SM_LDAP_STRUCT *lmap; 342 LDAPMessage *entry; 343 char *ocvalue; 344 { 345 char **vals = NULL; 346 int i; 347 348 if (ocvalue == NULL) 349 return false; 350 351 vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass"); 352 if (vals == NULL) 353 return false; 354 355 for (i = 0; vals[i] != NULL; i++) 356 { 357 char *p; 358 char *q; 359 360 p = q = ocvalue; 361 while (*p != '\0') 362 { 363 while (*p != '\0' && *p != '|') 364 p++; 365 366 if ((p - q) == strlen(vals[i]) && 367 sm_strncasecmp(vals[i], q, p - q) == 0) 368 { 369 ldap_value_free(vals); 370 return true; 371 } 372 373 while (*p == '|') 374 p++; 375 q = p; 376 } 377 } 378 379 ldap_value_free(vals); 380 return false; 381 } 382 383 /* 384 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result 385 ** 386 ** Parameters: 387 ** lmap -- pointer to SM_LDAP_STRUCT in use 388 ** msgid -- msgid returned by sm_ldap_search() 389 ** flags -- flags for the lookup 390 ** delim -- delimiter for result concatenation 391 ** rpool -- memory pool for storage 392 ** result -- return string 393 ** recurse -- recursion list 394 ** 395 ** Returns: 396 ** status (sysexit) 397 */ 398 399 # define SM_LDAP_ERROR_CLEANUP() \ 400 { \ 401 if (lmap->ldap_res != NULL) \ 402 { \ 403 ldap_msgfree(lmap->ldap_res); \ 404 lmap->ldap_res = NULL; \ 405 } \ 406 (void) ldap_abandon(lmap->ldap_ld, msgid); \ 407 } 408 409 static SM_LDAP_RECURSE_ENTRY * 410 sm_ldap_add_recurse(top, item, type, rpool) 411 SM_LDAP_RECURSE_LIST **top; 412 char *item; 413 int type; 414 SM_RPOOL_T *rpool; 415 { 416 int n; 417 int m; 418 int p; 419 int insertat; 420 int moveb; 421 int oldsizeb; 422 int rc; 423 SM_LDAP_RECURSE_ENTRY *newe; 424 SM_LDAP_RECURSE_ENTRY **olddata; 425 426 /* 427 ** This code will maintain a list of 428 ** SM_LDAP_RECURSE_ENTRY structures 429 ** in ascending order. 430 */ 431 432 if (*top == NULL) 433 { 434 /* Allocate an initial SM_LDAP_RECURSE_LIST struct */ 435 *top = sm_rpool_malloc_x(rpool, sizeof **top); 436 (*top)->lr_cnt = 0; 437 (*top)->lr_size = 0; 438 (*top)->lr_data = NULL; 439 } 440 441 if ((*top)->lr_cnt >= (*top)->lr_size) 442 { 443 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */ 444 olddata = (*top)->lr_data; 445 if ((*top)->lr_size == 0) 446 { 447 oldsizeb = 0; 448 (*top)->lr_size = 256; 449 } 450 else 451 { 452 oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data); 453 (*top)->lr_size *= 2; 454 } 455 (*top)->lr_data = sm_rpool_malloc_x(rpool, 456 (*top)->lr_size * sizeof *((*top)->lr_data)); 457 if (oldsizeb > 0) 458 memcpy((*top)->lr_data, olddata, oldsizeb); 459 } 460 461 /* 462 ** Binary search/insert item:type into list. 463 ** Return current entry pointer if already exists. 464 */ 465 466 n = 0; 467 m = (*top)->lr_cnt - 1; 468 if (m < 0) 469 insertat = 0; 470 else 471 insertat = -1; 472 473 while (insertat == -1) 474 { 475 p = (m + n) / 2; 476 477 rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search); 478 if (rc == 0) 479 rc = type - (*top)->lr_data[p]->lr_type; 480 481 if (rc < 0) 482 m = p - 1; 483 else if (rc > 0) 484 n = p + 1; 485 else 486 return (*top)->lr_data[p]; 487 488 if (m == -1) 489 insertat = 0; 490 else if (n >= (*top)->lr_cnt) 491 insertat = (*top)->lr_cnt; 492 else if (m < n) 493 insertat = m + 1; 494 } 495 496 /* 497 ** Not found in list, make room 498 ** at insert point and add it. 499 */ 500 501 newe = sm_rpool_malloc_x(rpool, sizeof *newe); 502 if (newe != NULL) 503 { 504 moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data); 505 if (moveb > 0) 506 memmove(&((*top)->lr_data[insertat + 1]), 507 &((*top)->lr_data[insertat]), 508 moveb); 509 510 newe->lr_search = sm_rpool_strdup_x(rpool, item); 511 newe->lr_type = type; 512 newe->lr_done = false; 513 514 ((*top)->lr_data)[insertat] = newe; 515 (*top)->lr_cnt++; 516 } 517 return newe; 518 } 519 520 int 521 sm_ldap_results(lmap, msgid, flags, delim, rpool, result, 522 resultln, resultsz, recurse) 523 SM_LDAP_STRUCT *lmap; 524 int msgid; 525 int flags; 526 int delim; 527 SM_RPOOL_T *rpool; 528 char **result; 529 int *resultln; 530 int *resultsz; 531 SM_LDAP_RECURSE_LIST *recurse; 532 { 533 bool toplevel; 534 int i; 535 int statp; 536 int vsize; 537 int ret; 538 int save_errno; 539 char *p; 540 SM_LDAP_RECURSE_ENTRY *rl; 541 542 /* Are we the top top level of the search? */ 543 toplevel = (recurse == NULL); 544 545 /* Get results */ 546 statp = EX_NOTFOUND; 547 while ((ret = ldap_result(lmap->ldap_ld, msgid, 0, 548 (lmap->ldap_timeout.tv_sec == 0 ? NULL : 549 &(lmap->ldap_timeout)), 550 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY) 551 { 552 LDAPMessage *entry; 553 554 /* If we don't want multiple values and we have one, break */ 555 if ((char) delim == '\0' && *result != NULL) 556 break; 557 558 /* Cycle through all entries */ 559 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); 560 entry != NULL; 561 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) 562 { 563 BerElement *ber; 564 char *attr; 565 char **vals = NULL; 566 char *dn; 567 568 /* 569 ** If matching only and found an entry, 570 ** no need to spin through attributes 571 */ 572 573 if (statp == EX_OK && 574 bitset(SM_LDAP_MATCHONLY, flags)) 575 continue; 576 577 /* record completed DN's to prevent loops */ 578 dn = ldap_get_dn(lmap->ldap_ld, entry); 579 if (dn == NULL) 580 { 581 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 582 save_errno += E_LDAPBASE; 583 SM_LDAP_ERROR_CLEANUP(); 584 errno = save_errno; 585 return EX_OSERR; 586 } 587 588 rl = sm_ldap_add_recurse(&recurse, dn, 589 SM_LDAP_ATTR_DN, 590 rpool); 591 592 if (rl == NULL) 593 { 594 ldap_memfree(dn); 595 SM_LDAP_ERROR_CLEANUP(); 596 errno = ENOMEM; 597 return EX_OSERR; 598 } 599 else if (rl->lr_done) 600 { 601 /* already on list, skip it */ 602 ldap_memfree(dn); 603 continue; 604 } 605 ldap_memfree(dn); 606 607 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 608 /* 609 ** Reset value to prevent lingering 610 ** LDAP_DECODING_ERROR due to 611 ** OpenLDAP 1.X's hack (see below) 612 */ 613 614 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 615 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 616 617 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 618 &ber); 619 attr != NULL; 620 attr = ldap_next_attribute(lmap->ldap_ld, entry, 621 ber)) 622 { 623 char *tmp, *vp_tmp; 624 int type; 625 char *needobjclass = NULL; 626 627 type = SM_LDAP_ATTR_NONE; 628 for (i = 0; lmap->ldap_attr[i] != NULL; i++) 629 { 630 if (sm_strcasecmp(lmap->ldap_attr[i], 631 attr) == 0) 632 { 633 type = lmap->ldap_attr_type[i]; 634 needobjclass = lmap->ldap_attr_needobjclass[i]; 635 break; 636 } 637 } 638 639 if (bitset(SM_LDAP_USE_ALLATTR, flags) && 640 type == SM_LDAP_ATTR_NONE) 641 { 642 /* URL lookups specify attrs to use */ 643 type = SM_LDAP_ATTR_NORMAL; 644 needobjclass = NULL; 645 } 646 647 if (type == SM_LDAP_ATTR_NONE) 648 { 649 /* attribute not requested */ 650 ldap_memfree(attr); 651 SM_LDAP_ERROR_CLEANUP(); 652 errno = EFAULT; 653 return EX_SOFTWARE; 654 } 655 656 /* 657 ** For recursion on a particular attribute, 658 ** we may need to see if this entry is 659 ** part of a particular objectclass. 660 ** Also, ignore objectClass attribute. 661 ** Otherwise we just ignore this attribute. 662 */ 663 664 if (type == SM_LDAP_ATTR_OBJCLASS || 665 (needobjclass != NULL && 666 !sm_ldap_has_objectclass(lmap, entry, 667 needobjclass))) 668 { 669 ldap_memfree(attr); 670 continue; 671 } 672 673 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 674 { 675 vals = ldap_get_values(lmap->ldap_ld, 676 entry, 677 attr); 678 if (vals == NULL) 679 { 680 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 681 if (save_errno == LDAP_SUCCESS) 682 { 683 ldap_memfree(attr); 684 continue; 685 } 686 687 /* Must be an error */ 688 save_errno += E_LDAPBASE; 689 ldap_memfree(attr); 690 SM_LDAP_ERROR_CLEANUP(); 691 errno = save_errno; 692 return EX_TEMPFAIL; 693 } 694 } 695 696 statp = EX_OK; 697 698 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 699 /* 700 ** Reset value to prevent lingering 701 ** LDAP_DECODING_ERROR due to 702 ** OpenLDAP 1.X's hack (see below) 703 */ 704 705 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 706 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 707 708 /* 709 ** If matching only, 710 ** no need to spin through entries 711 */ 712 713 if (bitset(SM_LDAP_MATCHONLY, flags)) 714 { 715 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 716 ldap_value_free(vals); 717 ldap_memfree(attr); 718 continue; 719 } 720 721 /* 722 ** If we don't want multiple values, 723 ** return first found. 724 */ 725 726 if ((char) delim == '\0') 727 { 728 if (*result != NULL) 729 { 730 /* already have a value */ 731 break; 732 } 733 734 if (bitset(SM_LDAP_SINGLEMATCH, 735 flags) && 736 *result != NULL) 737 { 738 /* only wanted one match */ 739 SM_LDAP_ERROR_CLEANUP(); 740 errno = ENOENT; 741 return EX_NOTFOUND; 742 } 743 744 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 745 { 746 *result = sm_rpool_strdup_x(rpool, 747 attr); 748 ldap_memfree(attr); 749 break; 750 } 751 752 if (vals[0] == NULL) 753 { 754 ldap_value_free(vals); 755 ldap_memfree(attr); 756 continue; 757 } 758 759 vsize = strlen(vals[0]) + 1; 760 if (lmap->ldap_attrsep != '\0') 761 vsize += strlen(attr) + 1; 762 *result = sm_rpool_malloc_x(rpool, 763 vsize); 764 if (lmap->ldap_attrsep != '\0') 765 sm_snprintf(*result, vsize, 766 "%s%c%s", 767 attr, 768 lmap->ldap_attrsep, 769 vals[0]); 770 else 771 sm_strlcpy(*result, vals[0], 772 vsize); 773 ldap_value_free(vals); 774 ldap_memfree(attr); 775 break; 776 } 777 778 /* attributes only */ 779 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 780 { 781 if (*result == NULL) 782 *result = sm_rpool_strdup_x(rpool, 783 attr); 784 else 785 { 786 if (bitset(SM_LDAP_SINGLEMATCH, 787 flags) && 788 *result != NULL) 789 { 790 /* only wanted one match */ 791 SM_LDAP_ERROR_CLEANUP(); 792 errno = ENOENT; 793 return EX_NOTFOUND; 794 } 795 796 vsize = strlen(*result) + 797 strlen(attr) + 2; 798 tmp = sm_rpool_malloc_x(rpool, 799 vsize); 800 (void) sm_snprintf(tmp, 801 vsize, "%s%c%s", 802 *result, (char) delim, 803 attr); 804 *result = tmp; 805 } 806 ldap_memfree(attr); 807 continue; 808 } 809 810 /* 811 ** If there is more than one, munge then 812 ** into a map_coldelim separated string. 813 ** If we are recursing we may have an entry 814 ** with no 'normal' values to put in the 815 ** string. 816 ** This is not an error. 817 */ 818 819 if (type == SM_LDAP_ATTR_NORMAL && 820 bitset(SM_LDAP_SINGLEMATCH, flags) && 821 *result != NULL) 822 { 823 /* only wanted one match */ 824 SM_LDAP_ERROR_CLEANUP(); 825 errno = ENOENT; 826 return EX_NOTFOUND; 827 } 828 829 vsize = 0; 830 for (i = 0; vals[i] != NULL; i++) 831 { 832 if (type == SM_LDAP_ATTR_DN || 833 type == SM_LDAP_ATTR_FILTER || 834 type == SM_LDAP_ATTR_URL) 835 { 836 /* add to recursion */ 837 if (sm_ldap_add_recurse(&recurse, 838 vals[i], 839 type, 840 rpool) == NULL) 841 { 842 SM_LDAP_ERROR_CLEANUP(); 843 errno = ENOMEM; 844 return EX_OSERR; 845 } 846 continue; 847 } 848 849 vsize += strlen(vals[i]) + 1; 850 if (lmap->ldap_attrsep != '\0') 851 vsize += strlen(attr) + 1; 852 } 853 854 /* 855 ** Create/Append to string any normal 856 ** attribute values. Otherwise, just free 857 ** memory and move on to the next 858 ** attribute in this entry. 859 */ 860 861 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0) 862 { 863 char *pe; 864 865 /* Grow result string if needed */ 866 if ((*resultln + vsize) >= *resultsz) 867 { 868 while ((*resultln + vsize) >= *resultsz) 869 { 870 if (*resultsz == 0) 871 *resultsz = 1024; 872 else 873 *resultsz *= 2; 874 } 875 876 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz); 877 *vp_tmp = '\0'; 878 879 if (*result != NULL) 880 sm_strlcpy(vp_tmp, 881 *result, 882 *resultsz); 883 *result = vp_tmp; 884 } 885 886 p = *result + *resultln; 887 pe = *result + *resultsz; 888 889 for (i = 0; vals[i] != NULL; i++) 890 { 891 if (*resultln > 0) 892 *p++ = (char) delim; 893 894 if (lmap->ldap_attrsep != '\0') 895 { 896 p += sm_strlcpy(p, attr, 897 pe - p); 898 if (p < pe) 899 *p++ = lmap->ldap_attrsep; 900 } 901 902 p += sm_strlcpy(p, vals[i], 903 pe - p); 904 *resultln = p - (*result); 905 if (p >= pe) 906 { 907 /* Internal error: buffer too small for LDAP values */ 908 SM_LDAP_ERROR_CLEANUP(); 909 errno = ENOMEM; 910 return EX_OSERR; 911 } 912 } 913 } 914 915 ldap_value_free(vals); 916 ldap_memfree(attr); 917 } 918 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 919 920 /* 921 ** We check save_errno != LDAP_DECODING_ERROR since 922 ** OpenLDAP 1.X has a very ugly *undocumented* 923 ** hack of returning this error code from 924 ** ldap_next_attribute() if the library freed the 925 ** ber attribute. See: 926 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 927 */ 928 929 if (save_errno != LDAP_SUCCESS && 930 save_errno != LDAP_DECODING_ERROR) 931 { 932 /* Must be an error */ 933 save_errno += E_LDAPBASE; 934 SM_LDAP_ERROR_CLEANUP(); 935 errno = save_errno; 936 return EX_TEMPFAIL; 937 } 938 939 /* mark this DN as done */ 940 rl->lr_done = true; 941 942 /* We don't want multiple values and we have one */ 943 if ((char) delim == '\0' && *result != NULL) 944 break; 945 } 946 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 947 if (save_errno != LDAP_SUCCESS && 948 save_errno != LDAP_DECODING_ERROR) 949 { 950 /* Must be an error */ 951 save_errno += E_LDAPBASE; 952 SM_LDAP_ERROR_CLEANUP(); 953 errno = save_errno; 954 return EX_TEMPFAIL; 955 } 956 ldap_msgfree(lmap->ldap_res); 957 lmap->ldap_res = NULL; 958 } 959 960 if (ret == 0) 961 save_errno = ETIMEDOUT; 962 else 963 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 964 if (save_errno != LDAP_SUCCESS) 965 { 966 statp = EX_TEMPFAIL; 967 if (ret != 0) 968 { 969 switch (save_errno) 970 { 971 #ifdef LDAP_SERVER_DOWN 972 case LDAP_SERVER_DOWN: 973 #endif /* LDAP_SERVER_DOWN */ 974 case LDAP_TIMEOUT: 975 case LDAP_UNAVAILABLE: 976 977 /* 978 ** server disappeared, 979 ** try reopen on next search 980 */ 981 982 statp = EX_RESTART; 983 break; 984 } 985 save_errno += E_LDAPBASE; 986 } 987 SM_LDAP_ERROR_CLEANUP(); 988 errno = save_errno; 989 return statp; 990 } 991 992 if (lmap->ldap_res != NULL) 993 { 994 ldap_msgfree(lmap->ldap_res); 995 lmap->ldap_res = NULL; 996 } 997 998 if (toplevel) 999 { 1000 int rlidx; 1001 1002 /* 1003 ** Spin through the built-up recurse list at the top 1004 ** of the recursion. Since new items are added at the 1005 ** end of the shared list, we actually only ever get 1006 ** one level of recursion before things pop back to the 1007 ** top. Any items added to the list during that recursion 1008 ** will be expanded by the top level. 1009 */ 1010 1011 for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++) 1012 { 1013 int newflags; 1014 int sid; 1015 int status; 1016 1017 rl = recurse->lr_data[rlidx]; 1018 1019 newflags = flags; 1020 if (rl->lr_done) 1021 { 1022 /* already expanded */ 1023 continue; 1024 } 1025 1026 if (rl->lr_type == SM_LDAP_ATTR_DN) 1027 { 1028 /* do DN search */ 1029 sid = ldap_search(lmap->ldap_ld, 1030 rl->lr_search, 1031 lmap->ldap_scope, 1032 "(objectClass=*)", 1033 (lmap->ldap_attr[0] == NULL ? 1034 NULL : lmap->ldap_attr), 1035 lmap->ldap_attrsonly); 1036 } 1037 else if (rl->lr_type == SM_LDAP_ATTR_FILTER) 1038 { 1039 /* do new search */ 1040 sid = ldap_search(lmap->ldap_ld, 1041 lmap->ldap_base, 1042 lmap->ldap_scope, 1043 rl->lr_search, 1044 (lmap->ldap_attr[0] == NULL ? 1045 NULL : lmap->ldap_attr), 1046 lmap->ldap_attrsonly); 1047 } 1048 else if (rl->lr_type == SM_LDAP_ATTR_URL) 1049 { 1050 /* do new URL search */ 1051 sid = ldap_url_search(lmap->ldap_ld, 1052 rl->lr_search, 1053 lmap->ldap_attrsonly); 1054 newflags |= SM_LDAP_USE_ALLATTR; 1055 } 1056 else 1057 { 1058 /* unknown or illegal attribute type */ 1059 errno = EFAULT; 1060 return EX_SOFTWARE; 1061 } 1062 1063 /* Collect results */ 1064 if (sid == -1) 1065 { 1066 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1067 statp = EX_TEMPFAIL; 1068 switch (save_errno) 1069 { 1070 #ifdef LDAP_SERVER_DOWN 1071 case LDAP_SERVER_DOWN: 1072 #endif /* LDAP_SERVER_DOWN */ 1073 case LDAP_TIMEOUT: 1074 case LDAP_UNAVAILABLE: 1075 1076 /* 1077 ** server disappeared, 1078 ** try reopen on next search 1079 */ 1080 1081 statp = EX_RESTART; 1082 break; 1083 } 1084 errno = save_errno + E_LDAPBASE; 1085 return statp; 1086 } 1087 1088 status = sm_ldap_results(lmap, sid, newflags, delim, 1089 rpool, result, resultln, 1090 resultsz, recurse); 1091 save_errno = errno; 1092 if (status != EX_OK && status != EX_NOTFOUND) 1093 { 1094 errno = save_errno; 1095 return status; 1096 } 1097 1098 /* Mark as done */ 1099 rl->lr_done = true; 1100 1101 /* Reset rlidx as new items may have been added */ 1102 rlidx = -1; 1103 } 1104 } 1105 return statp; 1106 } 1107 #endif /* _FFR_LDAP_RECURSION */ 1108 1109 /* 1110 ** SM_LDAP_CLOSE -- close LDAP connection 1111 ** 1112 ** Parameters: 1113 ** lmap -- LDAP map information 1114 ** 1115 ** Returns: 1116 ** None. 1117 ** 1118 */ 1119 1120 void 1121 sm_ldap_close(lmap) 1122 SM_LDAP_STRUCT *lmap; 1123 { 1124 if (lmap->ldap_ld == NULL) 1125 return; 1126 1127 if (lmap->ldap_pid == getpid()) 1128 ldap_unbind(lmap->ldap_ld); 1129 lmap->ldap_ld = NULL; 1130 lmap->ldap_pid = 0; 1131 } 1132 1133 /* 1134 ** SM_LDAP_SETOPTS -- set LDAP options 1135 ** 1136 ** Parameters: 1137 ** ld -- LDAP session handle 1138 ** lmap -- LDAP map information 1139 ** 1140 ** Returns: 1141 ** None. 1142 ** 1143 */ 1144 1145 void 1146 sm_ldap_setopts(ld, lmap) 1147 LDAP *ld; 1148 SM_LDAP_STRUCT *lmap; 1149 { 1150 # if USE_LDAP_SET_OPTION 1151 # if _FFR_LDAP_SETVERSION 1152 if (lmap->ldap_version != 0) 1153 { 1154 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 1155 &lmap->ldap_version); 1156 } 1157 # endif /* _FFR_LDAP_SETVERSION */ 1158 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 1159 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 1160 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 1161 else 1162 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1163 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 1164 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 1165 # else /* USE_LDAP_SET_OPTION */ 1166 /* From here on in we can use ldap internal timelimits */ 1167 ld->ld_deref = lmap->ldap_deref; 1168 ld->ld_options = lmap->ldap_options; 1169 ld->ld_sizelimit = lmap->ldap_sizelimit; 1170 ld->ld_timelimit = lmap->ldap_timelimit; 1171 # endif /* USE_LDAP_SET_OPTION */ 1172 } 1173 1174 /* 1175 ** SM_LDAP_GETERRNO -- get ldap errno value 1176 ** 1177 ** Parameters: 1178 ** ld -- LDAP session handle 1179 ** 1180 ** Returns: 1181 ** LDAP errno. 1182 ** 1183 */ 1184 1185 int 1186 sm_ldap_geterrno(ld) 1187 LDAP *ld; 1188 { 1189 int err = LDAP_SUCCESS; 1190 1191 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 1192 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 1193 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1194 # ifdef LDAP_OPT_SIZELIMIT 1195 err = ldap_get_lderrno(ld, NULL, NULL); 1196 # else /* LDAP_OPT_SIZELIMIT */ 1197 err = ld->ld_errno; 1198 1199 /* 1200 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 1201 ** OpenLDAP 1.X's hack (see above) 1202 */ 1203 1204 ld->ld_errno = LDAP_SUCCESS; 1205 # endif /* LDAP_OPT_SIZELIMIT */ 1206 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1207 return err; 1208 } 1209 # endif /* LDAPMAP */ 1210