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