1 /* 2 * Copyright (c) 2001-2003 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.2.5 2003/12/23 21:21:56 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 (bitset(SM_LDAP_MATCHONLY, flags)) 574 { 575 statp = EX_OK; 576 continue; 577 } 578 579 /* record completed DN's to prevent loops */ 580 dn = ldap_get_dn(lmap->ldap_ld, entry); 581 if (dn == NULL) 582 { 583 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 584 save_errno += E_LDAPBASE; 585 SM_LDAP_ERROR_CLEANUP(); 586 errno = save_errno; 587 return EX_TEMPFAIL; 588 } 589 590 rl = sm_ldap_add_recurse(&recurse, dn, 591 SM_LDAP_ATTR_DN, 592 rpool); 593 594 if (rl == NULL) 595 { 596 ldap_memfree(dn); 597 SM_LDAP_ERROR_CLEANUP(); 598 errno = ENOMEM; 599 return EX_OSERR; 600 } 601 else if (rl->lr_done) 602 { 603 /* already on list, skip it */ 604 ldap_memfree(dn); 605 continue; 606 } 607 ldap_memfree(dn); 608 609 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 610 /* 611 ** Reset value to prevent lingering 612 ** LDAP_DECODING_ERROR due to 613 ** OpenLDAP 1.X's hack (see below) 614 */ 615 616 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 617 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 618 619 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 620 &ber); 621 attr != NULL; 622 attr = ldap_next_attribute(lmap->ldap_ld, entry, 623 ber)) 624 { 625 char *tmp, *vp_tmp; 626 int type; 627 char *needobjclass = NULL; 628 629 type = SM_LDAP_ATTR_NONE; 630 for (i = 0; lmap->ldap_attr[i] != NULL; i++) 631 { 632 if (sm_strcasecmp(lmap->ldap_attr[i], 633 attr) == 0) 634 { 635 type = lmap->ldap_attr_type[i]; 636 needobjclass = lmap->ldap_attr_needobjclass[i]; 637 break; 638 } 639 } 640 641 if (bitset(SM_LDAP_USE_ALLATTR, flags) && 642 type == SM_LDAP_ATTR_NONE) 643 { 644 /* URL lookups specify attrs to use */ 645 type = SM_LDAP_ATTR_NORMAL; 646 needobjclass = NULL; 647 } 648 649 if (type == SM_LDAP_ATTR_NONE) 650 { 651 /* attribute not requested */ 652 ldap_memfree(attr); 653 SM_LDAP_ERROR_CLEANUP(); 654 errno = EFAULT; 655 return EX_SOFTWARE; 656 } 657 658 /* 659 ** For recursion on a particular attribute, 660 ** we may need to see if this entry is 661 ** part of a particular objectclass. 662 ** Also, ignore objectClass attribute. 663 ** Otherwise we just ignore this attribute. 664 */ 665 666 if (type == SM_LDAP_ATTR_OBJCLASS || 667 (needobjclass != NULL && 668 !sm_ldap_has_objectclass(lmap, entry, 669 needobjclass))) 670 { 671 ldap_memfree(attr); 672 continue; 673 } 674 675 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 676 { 677 vals = ldap_get_values(lmap->ldap_ld, 678 entry, 679 attr); 680 if (vals == NULL) 681 { 682 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 683 if (save_errno == LDAP_SUCCESS) 684 { 685 ldap_memfree(attr); 686 continue; 687 } 688 689 /* Must be an error */ 690 save_errno += E_LDAPBASE; 691 ldap_memfree(attr); 692 SM_LDAP_ERROR_CLEANUP(); 693 errno = save_errno; 694 return EX_TEMPFAIL; 695 } 696 } 697 698 statp = EX_OK; 699 700 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 701 /* 702 ** Reset value to prevent lingering 703 ** LDAP_DECODING_ERROR due to 704 ** OpenLDAP 1.X's hack (see below) 705 */ 706 707 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 708 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 709 710 /* 711 ** If matching only, 712 ** no need to spin through entries 713 */ 714 715 if (bitset(SM_LDAP_MATCHONLY, flags)) 716 { 717 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 718 ldap_value_free(vals); 719 ldap_memfree(attr); 720 continue; 721 } 722 723 /* 724 ** If we don't want multiple values, 725 ** return first found. 726 */ 727 728 if ((char) delim == '\0') 729 { 730 if (*result != NULL) 731 { 732 /* already have a value */ 733 break; 734 } 735 736 if (bitset(SM_LDAP_SINGLEMATCH, 737 flags) && 738 *result != NULL) 739 { 740 /* only wanted one match */ 741 SM_LDAP_ERROR_CLEANUP(); 742 errno = ENOENT; 743 return EX_NOTFOUND; 744 } 745 746 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 747 { 748 *result = sm_rpool_strdup_x(rpool, 749 attr); 750 ldap_memfree(attr); 751 break; 752 } 753 754 if (vals[0] == NULL) 755 { 756 ldap_value_free(vals); 757 ldap_memfree(attr); 758 continue; 759 } 760 761 vsize = strlen(vals[0]) + 1; 762 if (lmap->ldap_attrsep != '\0') 763 vsize += strlen(attr) + 1; 764 *result = sm_rpool_malloc_x(rpool, 765 vsize); 766 if (lmap->ldap_attrsep != '\0') 767 sm_snprintf(*result, vsize, 768 "%s%c%s", 769 attr, 770 lmap->ldap_attrsep, 771 vals[0]); 772 else 773 sm_strlcpy(*result, vals[0], 774 vsize); 775 ldap_value_free(vals); 776 ldap_memfree(attr); 777 break; 778 } 779 780 /* attributes only */ 781 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 782 { 783 if (*result == NULL) 784 *result = sm_rpool_strdup_x(rpool, 785 attr); 786 else 787 { 788 if (bitset(SM_LDAP_SINGLEMATCH, 789 flags) && 790 *result != NULL) 791 { 792 /* only wanted one match */ 793 SM_LDAP_ERROR_CLEANUP(); 794 errno = ENOENT; 795 return EX_NOTFOUND; 796 } 797 798 vsize = strlen(*result) + 799 strlen(attr) + 2; 800 tmp = sm_rpool_malloc_x(rpool, 801 vsize); 802 (void) sm_snprintf(tmp, 803 vsize, "%s%c%s", 804 *result, (char) delim, 805 attr); 806 *result = tmp; 807 } 808 ldap_memfree(attr); 809 continue; 810 } 811 812 /* 813 ** If there is more than one, munge then 814 ** into a map_coldelim separated string. 815 ** If we are recursing we may have an entry 816 ** with no 'normal' values to put in the 817 ** string. 818 ** This is not an error. 819 */ 820 821 if (type == SM_LDAP_ATTR_NORMAL && 822 bitset(SM_LDAP_SINGLEMATCH, flags) && 823 *result != NULL) 824 { 825 /* only wanted one match */ 826 SM_LDAP_ERROR_CLEANUP(); 827 errno = ENOENT; 828 return EX_NOTFOUND; 829 } 830 831 vsize = 0; 832 for (i = 0; vals[i] != NULL; i++) 833 { 834 if (type == SM_LDAP_ATTR_DN || 835 type == SM_LDAP_ATTR_FILTER || 836 type == SM_LDAP_ATTR_URL) 837 { 838 /* add to recursion */ 839 if (sm_ldap_add_recurse(&recurse, 840 vals[i], 841 type, 842 rpool) == NULL) 843 { 844 SM_LDAP_ERROR_CLEANUP(); 845 errno = ENOMEM; 846 return EX_OSERR; 847 } 848 continue; 849 } 850 851 vsize += strlen(vals[i]) + 1; 852 if (lmap->ldap_attrsep != '\0') 853 vsize += strlen(attr) + 1; 854 } 855 856 /* 857 ** Create/Append to string any normal 858 ** attribute values. Otherwise, just free 859 ** memory and move on to the next 860 ** attribute in this entry. 861 */ 862 863 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0) 864 { 865 char *pe; 866 867 /* Grow result string if needed */ 868 if ((*resultln + vsize) >= *resultsz) 869 { 870 while ((*resultln + vsize) >= *resultsz) 871 { 872 if (*resultsz == 0) 873 *resultsz = 1024; 874 else 875 *resultsz *= 2; 876 } 877 878 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz); 879 *vp_tmp = '\0'; 880 881 if (*result != NULL) 882 sm_strlcpy(vp_tmp, 883 *result, 884 *resultsz); 885 *result = vp_tmp; 886 } 887 888 p = *result + *resultln; 889 pe = *result + *resultsz; 890 891 for (i = 0; vals[i] != NULL; i++) 892 { 893 if (*resultln > 0 && 894 p < pe) 895 *p++ = (char) delim; 896 897 if (lmap->ldap_attrsep != '\0') 898 { 899 p += sm_strlcpy(p, attr, 900 pe - p); 901 if (p < pe) 902 *p++ = lmap->ldap_attrsep; 903 } 904 905 p += sm_strlcpy(p, vals[i], 906 pe - p); 907 *resultln = p - (*result); 908 if (p >= pe) 909 { 910 /* Internal error: buffer too small for LDAP values */ 911 SM_LDAP_ERROR_CLEANUP(); 912 errno = ENOMEM; 913 return EX_OSERR; 914 } 915 } 916 } 917 918 ldap_value_free(vals); 919 ldap_memfree(attr); 920 } 921 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 922 923 /* 924 ** We check save_errno != LDAP_DECODING_ERROR since 925 ** OpenLDAP 1.X has a very ugly *undocumented* 926 ** hack of returning this error code from 927 ** ldap_next_attribute() if the library freed the 928 ** ber attribute. See: 929 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 930 */ 931 932 if (save_errno != LDAP_SUCCESS && 933 save_errno != LDAP_DECODING_ERROR) 934 { 935 /* Must be an error */ 936 save_errno += E_LDAPBASE; 937 SM_LDAP_ERROR_CLEANUP(); 938 errno = save_errno; 939 return EX_TEMPFAIL; 940 } 941 942 /* mark this DN as done */ 943 rl->lr_done = true; 944 945 /* We don't want multiple values and we have one */ 946 if ((char) delim == '\0' && *result != NULL) 947 break; 948 } 949 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 950 if (save_errno != LDAP_SUCCESS && 951 save_errno != LDAP_DECODING_ERROR) 952 { 953 /* Must be an error */ 954 save_errno += E_LDAPBASE; 955 SM_LDAP_ERROR_CLEANUP(); 956 errno = save_errno; 957 return EX_TEMPFAIL; 958 } 959 ldap_msgfree(lmap->ldap_res); 960 lmap->ldap_res = NULL; 961 } 962 963 if (ret == 0) 964 save_errno = ETIMEDOUT; 965 else 966 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 967 if (save_errno != LDAP_SUCCESS) 968 { 969 statp = EX_TEMPFAIL; 970 if (ret != 0) 971 { 972 switch (save_errno) 973 { 974 #ifdef LDAP_SERVER_DOWN 975 case LDAP_SERVER_DOWN: 976 #endif /* LDAP_SERVER_DOWN */ 977 case LDAP_TIMEOUT: 978 case LDAP_UNAVAILABLE: 979 980 /* 981 ** server disappeared, 982 ** try reopen on next search 983 */ 984 985 statp = EX_RESTART; 986 break; 987 } 988 save_errno += E_LDAPBASE; 989 } 990 SM_LDAP_ERROR_CLEANUP(); 991 errno = save_errno; 992 return statp; 993 } 994 995 if (lmap->ldap_res != NULL) 996 { 997 ldap_msgfree(lmap->ldap_res); 998 lmap->ldap_res = NULL; 999 } 1000 1001 if (toplevel) 1002 { 1003 int rlidx; 1004 1005 /* 1006 ** Spin through the built-up recurse list at the top 1007 ** of the recursion. Since new items are added at the 1008 ** end of the shared list, we actually only ever get 1009 ** one level of recursion before things pop back to the 1010 ** top. Any items added to the list during that recursion 1011 ** will be expanded by the top level. 1012 */ 1013 1014 for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++) 1015 { 1016 int newflags; 1017 int sid; 1018 int status; 1019 1020 rl = recurse->lr_data[rlidx]; 1021 1022 newflags = flags; 1023 if (rl->lr_done) 1024 { 1025 /* already expanded */ 1026 continue; 1027 } 1028 1029 if (rl->lr_type == SM_LDAP_ATTR_DN) 1030 { 1031 /* do DN search */ 1032 sid = ldap_search(lmap->ldap_ld, 1033 rl->lr_search, 1034 lmap->ldap_scope, 1035 "(objectClass=*)", 1036 (lmap->ldap_attr[0] == NULL ? 1037 NULL : lmap->ldap_attr), 1038 lmap->ldap_attrsonly); 1039 } 1040 else if (rl->lr_type == SM_LDAP_ATTR_FILTER) 1041 { 1042 /* do new search */ 1043 sid = ldap_search(lmap->ldap_ld, 1044 lmap->ldap_base, 1045 lmap->ldap_scope, 1046 rl->lr_search, 1047 (lmap->ldap_attr[0] == NULL ? 1048 NULL : lmap->ldap_attr), 1049 lmap->ldap_attrsonly); 1050 } 1051 else if (rl->lr_type == SM_LDAP_ATTR_URL) 1052 { 1053 /* do new URL search */ 1054 sid = ldap_url_search(lmap->ldap_ld, 1055 rl->lr_search, 1056 lmap->ldap_attrsonly); 1057 newflags |= SM_LDAP_USE_ALLATTR; 1058 } 1059 else 1060 { 1061 /* unknown or illegal attribute type */ 1062 errno = EFAULT; 1063 return EX_SOFTWARE; 1064 } 1065 1066 /* Collect results */ 1067 if (sid == -1) 1068 { 1069 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1070 statp = EX_TEMPFAIL; 1071 switch (save_errno) 1072 { 1073 #ifdef LDAP_SERVER_DOWN 1074 case LDAP_SERVER_DOWN: 1075 #endif /* LDAP_SERVER_DOWN */ 1076 case LDAP_TIMEOUT: 1077 case LDAP_UNAVAILABLE: 1078 1079 /* 1080 ** server disappeared, 1081 ** try reopen on next search 1082 */ 1083 1084 statp = EX_RESTART; 1085 break; 1086 } 1087 errno = save_errno + E_LDAPBASE; 1088 return statp; 1089 } 1090 1091 status = sm_ldap_results(lmap, sid, newflags, delim, 1092 rpool, result, resultln, 1093 resultsz, recurse); 1094 save_errno = errno; 1095 if (status != EX_OK && status != EX_NOTFOUND) 1096 { 1097 errno = save_errno; 1098 return status; 1099 } 1100 1101 /* Mark as done */ 1102 rl->lr_done = true; 1103 1104 /* Reset rlidx as new items may have been added */ 1105 rlidx = -1; 1106 } 1107 } 1108 return statp; 1109 } 1110 #endif /* _FFR_LDAP_RECURSION */ 1111 1112 /* 1113 ** SM_LDAP_CLOSE -- close LDAP connection 1114 ** 1115 ** Parameters: 1116 ** lmap -- LDAP map information 1117 ** 1118 ** Returns: 1119 ** None. 1120 ** 1121 */ 1122 1123 void 1124 sm_ldap_close(lmap) 1125 SM_LDAP_STRUCT *lmap; 1126 { 1127 if (lmap->ldap_ld == NULL) 1128 return; 1129 1130 if (lmap->ldap_pid == getpid()) 1131 ldap_unbind(lmap->ldap_ld); 1132 lmap->ldap_ld = NULL; 1133 lmap->ldap_pid = 0; 1134 } 1135 1136 /* 1137 ** SM_LDAP_SETOPTS -- set LDAP options 1138 ** 1139 ** Parameters: 1140 ** ld -- LDAP session handle 1141 ** lmap -- LDAP map information 1142 ** 1143 ** Returns: 1144 ** None. 1145 ** 1146 */ 1147 1148 void 1149 sm_ldap_setopts(ld, lmap) 1150 LDAP *ld; 1151 SM_LDAP_STRUCT *lmap; 1152 { 1153 # if USE_LDAP_SET_OPTION 1154 # if _FFR_LDAP_SETVERSION 1155 if (lmap->ldap_version != 0) 1156 { 1157 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 1158 &lmap->ldap_version); 1159 } 1160 # endif /* _FFR_LDAP_SETVERSION */ 1161 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 1162 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 1163 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 1164 else 1165 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1166 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 1167 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 1168 # ifdef LDAP_OPT_RESTART 1169 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1170 # endif /* LDAP_OPT_RESTART */ 1171 # else /* USE_LDAP_SET_OPTION */ 1172 /* From here on in we can use ldap internal timelimits */ 1173 ld->ld_deref = lmap->ldap_deref; 1174 ld->ld_options = lmap->ldap_options; 1175 ld->ld_sizelimit = lmap->ldap_sizelimit; 1176 ld->ld_timelimit = lmap->ldap_timelimit; 1177 # endif /* USE_LDAP_SET_OPTION */ 1178 } 1179 1180 /* 1181 ** SM_LDAP_GETERRNO -- get ldap errno value 1182 ** 1183 ** Parameters: 1184 ** ld -- LDAP session handle 1185 ** 1186 ** Returns: 1187 ** LDAP errno. 1188 ** 1189 */ 1190 1191 int 1192 sm_ldap_geterrno(ld) 1193 LDAP *ld; 1194 { 1195 int err = LDAP_SUCCESS; 1196 1197 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 1198 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 1199 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1200 # ifdef LDAP_OPT_SIZELIMIT 1201 err = ldap_get_lderrno(ld, NULL, NULL); 1202 # else /* LDAP_OPT_SIZELIMIT */ 1203 err = ld->ld_errno; 1204 1205 /* 1206 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 1207 ** OpenLDAP 1.X's hack (see above) 1208 */ 1209 1210 ld->ld_errno = LDAP_SUCCESS; 1211 # endif /* LDAP_OPT_SIZELIMIT */ 1212 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1213 return err; 1214 } 1215 # endif /* LDAPMAP */ 1216