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