1 /* 2 * Copyright (c) 2001-2004 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.60 2004/08/03 20:42:21 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' && *result != NULL) 593 break; 594 595 /* Cycle through all entries */ 596 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); 597 entry != NULL; 598 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) 599 { 600 BerElement *ber; 601 char *attr; 602 char **vals = NULL; 603 char *dn; 604 605 /* 606 ** If matching only and found an entry, 607 ** no need to spin through attributes 608 */ 609 610 if (bitset(SM_LDAP_MATCHONLY, flags)) 611 { 612 statp = EX_OK; 613 continue; 614 } 615 616 /* record completed DN's to prevent loops */ 617 dn = ldap_get_dn(lmap->ldap_ld, entry); 618 if (dn == NULL) 619 { 620 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 621 save_errno += E_LDAPBASE; 622 SM_LDAP_ERROR_CLEANUP(); 623 errno = save_errno; 624 return EX_TEMPFAIL; 625 } 626 627 rl = sm_ldap_add_recurse(&recurse, dn, 628 SM_LDAP_ATTR_DN, 629 rpool); 630 631 if (rl == NULL) 632 { 633 ldap_memfree(dn); 634 SM_LDAP_ERROR_CLEANUP(); 635 errno = ENOMEM; 636 return EX_OSERR; 637 } 638 else if (rl->lr_done) 639 { 640 /* already on list, skip it */ 641 ldap_memfree(dn); 642 continue; 643 } 644 ldap_memfree(dn); 645 646 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 647 /* 648 ** Reset value to prevent lingering 649 ** LDAP_DECODING_ERROR due to 650 ** OpenLDAP 1.X's hack (see below) 651 */ 652 653 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 654 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 655 656 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 657 &ber); 658 attr != NULL; 659 attr = ldap_next_attribute(lmap->ldap_ld, entry, 660 ber)) 661 { 662 char *tmp, *vp_tmp; 663 int type; 664 char *needobjclass = NULL; 665 666 type = SM_LDAP_ATTR_NONE; 667 for (i = 0; lmap->ldap_attr[i] != NULL; i++) 668 { 669 if (sm_strcasecmp(lmap->ldap_attr[i], 670 attr) == 0) 671 { 672 type = lmap->ldap_attr_type[i]; 673 needobjclass = lmap->ldap_attr_needobjclass[i]; 674 break; 675 } 676 } 677 678 if (bitset(SM_LDAP_USE_ALLATTR, flags) && 679 type == SM_LDAP_ATTR_NONE) 680 { 681 /* URL lookups specify attrs to use */ 682 type = SM_LDAP_ATTR_NORMAL; 683 needobjclass = NULL; 684 } 685 686 if (type == SM_LDAP_ATTR_NONE) 687 { 688 /* attribute not requested */ 689 ldap_memfree(attr); 690 SM_LDAP_ERROR_CLEANUP(); 691 errno = EFAULT; 692 return EX_SOFTWARE; 693 } 694 695 /* 696 ** For recursion on a particular attribute, 697 ** we may need to see if this entry is 698 ** part of a particular objectclass. 699 ** Also, ignore objectClass attribute. 700 ** Otherwise we just ignore this attribute. 701 */ 702 703 if (type == SM_LDAP_ATTR_OBJCLASS || 704 (needobjclass != NULL && 705 !sm_ldap_has_objectclass(lmap, entry, 706 needobjclass))) 707 { 708 ldap_memfree(attr); 709 continue; 710 } 711 712 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 713 { 714 vals = ldap_get_values(lmap->ldap_ld, 715 entry, 716 attr); 717 if (vals == NULL) 718 { 719 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 720 if (save_errno == LDAP_SUCCESS) 721 { 722 ldap_memfree(attr); 723 continue; 724 } 725 726 /* Must be an error */ 727 save_errno += E_LDAPBASE; 728 ldap_memfree(attr); 729 SM_LDAP_ERROR_CLEANUP(); 730 errno = save_errno; 731 return EX_TEMPFAIL; 732 } 733 } 734 735 statp = EX_OK; 736 737 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 738 /* 739 ** Reset value to prevent lingering 740 ** LDAP_DECODING_ERROR due to 741 ** OpenLDAP 1.X's hack (see below) 742 */ 743 744 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 745 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 746 747 /* 748 ** If matching only, 749 ** no need to spin through entries 750 */ 751 752 if (bitset(SM_LDAP_MATCHONLY, flags)) 753 { 754 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 755 ldap_value_free(vals); 756 ldap_memfree(attr); 757 continue; 758 } 759 760 /* 761 ** If we don't want multiple values, 762 ** return first found. 763 */ 764 765 if ((char) delim == '\0') 766 { 767 if (*result != NULL) 768 { 769 /* already have a value */ 770 break; 771 } 772 773 if (bitset(SM_LDAP_SINGLEMATCH, 774 flags) && 775 *result != NULL) 776 { 777 /* only wanted one match */ 778 SM_LDAP_ERROR_CLEANUP(); 779 errno = ENOENT; 780 return EX_NOTFOUND; 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' && *result != NULL) 994 break; 995 } 996 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 997 if (save_errno != LDAP_SUCCESS && 998 save_errno != LDAP_DECODING_ERROR) 999 { 1000 /* Must be an error */ 1001 save_errno += E_LDAPBASE; 1002 SM_LDAP_ERROR_CLEANUP(); 1003 errno = save_errno; 1004 return EX_TEMPFAIL; 1005 } 1006 ldap_msgfree(lmap->ldap_res); 1007 lmap->ldap_res = NULL; 1008 } 1009 1010 if (ret == 0) 1011 save_errno = ETIMEDOUT; 1012 else 1013 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1014 if (save_errno != LDAP_SUCCESS) 1015 { 1016 statp = EX_TEMPFAIL; 1017 if (ret != 0) 1018 { 1019 switch (save_errno) 1020 { 1021 #ifdef LDAP_SERVER_DOWN 1022 case LDAP_SERVER_DOWN: 1023 #endif /* LDAP_SERVER_DOWN */ 1024 case LDAP_TIMEOUT: 1025 case LDAP_UNAVAILABLE: 1026 1027 /* 1028 ** server disappeared, 1029 ** try reopen on next search 1030 */ 1031 1032 statp = EX_RESTART; 1033 break; 1034 } 1035 save_errno += E_LDAPBASE; 1036 } 1037 SM_LDAP_ERROR_CLEANUP(); 1038 errno = save_errno; 1039 return statp; 1040 } 1041 1042 if (lmap->ldap_res != NULL) 1043 { 1044 ldap_msgfree(lmap->ldap_res); 1045 lmap->ldap_res = NULL; 1046 } 1047 1048 if (toplevel) 1049 { 1050 int rlidx; 1051 1052 /* 1053 ** Spin through the built-up recurse list at the top 1054 ** of the recursion. Since new items are added at the 1055 ** end of the shared list, we actually only ever get 1056 ** one level of recursion before things pop back to the 1057 ** top. Any items added to the list during that recursion 1058 ** will be expanded by the top level. 1059 */ 1060 1061 for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++) 1062 { 1063 int newflags; 1064 int sid; 1065 int status; 1066 1067 rl = recurse->lr_data[rlidx]; 1068 1069 newflags = flags; 1070 if (rl->lr_done) 1071 { 1072 /* already expanded */ 1073 continue; 1074 } 1075 1076 if (rl->lr_type == SM_LDAP_ATTR_DN) 1077 { 1078 /* do DN search */ 1079 sid = ldap_search(lmap->ldap_ld, 1080 rl->lr_search, 1081 lmap->ldap_scope, 1082 "(objectClass=*)", 1083 (lmap->ldap_attr[0] == NULL ? 1084 NULL : lmap->ldap_attr), 1085 lmap->ldap_attrsonly); 1086 } 1087 else if (rl->lr_type == SM_LDAP_ATTR_FILTER) 1088 { 1089 /* do new search */ 1090 sid = ldap_search(lmap->ldap_ld, 1091 lmap->ldap_base, 1092 lmap->ldap_scope, 1093 rl->lr_search, 1094 (lmap->ldap_attr[0] == NULL ? 1095 NULL : lmap->ldap_attr), 1096 lmap->ldap_attrsonly); 1097 } 1098 else if (rl->lr_type == SM_LDAP_ATTR_URL) 1099 { 1100 /* Parse URL */ 1101 sid = ldap_url_parse(rl->lr_search, 1102 &rl->lr_ludp); 1103 1104 if (sid != 0) 1105 { 1106 errno = sid + E_LDAPURLBASE; 1107 return EX_TEMPFAIL; 1108 } 1109 1110 /* We need to add objectClass */ 1111 if (rl->lr_ludp->lud_attrs != NULL) 1112 { 1113 int attrnum = 0; 1114 1115 while (rl->lr_ludp->lud_attrs[attrnum] != NULL) 1116 { 1117 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum], 1118 "objectClass") == 0) 1119 { 1120 /* already requested */ 1121 attrnum = -1; 1122 break; 1123 } 1124 attrnum++; 1125 } 1126 1127 if (attrnum >= 0) 1128 { 1129 int i; 1130 1131 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2)); 1132 if (rl->lr_attrs == NULL) 1133 { 1134 save_errno = errno; 1135 ldap_free_urldesc(rl->lr_ludp); 1136 errno = save_errno; 1137 return EX_TEMPFAIL; 1138 } 1139 for (i = 0 ; i < attrnum; i++) 1140 { 1141 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i]; 1142 } 1143 rl->lr_attrs[i++] = "objectClass"; 1144 rl->lr_attrs[i++] = NULL; 1145 } 1146 } 1147 1148 /* 1149 ** Use the existing connection 1150 ** for this search. It really 1151 ** should use lud_scheme://lud_host:lud_port/ 1152 ** instead but that would require 1153 ** opening a new connection. 1154 ** This should be fixed ASAP. 1155 */ 1156 1157 sid = ldap_search(lmap->ldap_ld, 1158 rl->lr_ludp->lud_dn, 1159 rl->lr_ludp->lud_scope, 1160 rl->lr_ludp->lud_filter, 1161 rl->lr_attrs, 1162 lmap->ldap_attrsonly); 1163 1164 /* Use the attributes specified by URL */ 1165 newflags |= SM_LDAP_USE_ALLATTR; 1166 } 1167 else 1168 { 1169 /* unknown or illegal attribute type */ 1170 errno = EFAULT; 1171 return EX_SOFTWARE; 1172 } 1173 1174 /* Collect results */ 1175 if (sid == -1) 1176 { 1177 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1178 statp = EX_TEMPFAIL; 1179 switch (save_errno) 1180 { 1181 #ifdef LDAP_SERVER_DOWN 1182 case LDAP_SERVER_DOWN: 1183 #endif /* LDAP_SERVER_DOWN */ 1184 case LDAP_TIMEOUT: 1185 case LDAP_UNAVAILABLE: 1186 1187 /* 1188 ** server disappeared, 1189 ** try reopen on next search 1190 */ 1191 1192 statp = EX_RESTART; 1193 break; 1194 } 1195 errno = save_errno + E_LDAPBASE; 1196 return statp; 1197 } 1198 1199 status = sm_ldap_results(lmap, sid, newflags, delim, 1200 rpool, result, resultln, 1201 resultsz, recurse); 1202 save_errno = errno; 1203 if (status != EX_OK && status != EX_NOTFOUND) 1204 { 1205 errno = save_errno; 1206 return status; 1207 } 1208 1209 /* Mark as done */ 1210 rl->lr_done = true; 1211 if (rl->lr_ludp != NULL) 1212 { 1213 ldap_free_urldesc(rl->lr_ludp); 1214 rl->lr_ludp = NULL; 1215 } 1216 if (rl->lr_attrs != NULL) 1217 { 1218 free(rl->lr_attrs); 1219 rl->lr_attrs = NULL; 1220 } 1221 1222 /* Reset rlidx as new items may have been added */ 1223 rlidx = -1; 1224 } 1225 } 1226 return statp; 1227 } 1228 1229 /* 1230 ** SM_LDAP_CLOSE -- close LDAP connection 1231 ** 1232 ** Parameters: 1233 ** lmap -- LDAP map information 1234 ** 1235 ** Returns: 1236 ** None. 1237 ** 1238 */ 1239 1240 void 1241 sm_ldap_close(lmap) 1242 SM_LDAP_STRUCT *lmap; 1243 { 1244 if (lmap->ldap_ld == NULL) 1245 return; 1246 1247 if (lmap->ldap_pid == getpid()) 1248 ldap_unbind(lmap->ldap_ld); 1249 lmap->ldap_ld = NULL; 1250 lmap->ldap_pid = 0; 1251 } 1252 1253 /* 1254 ** SM_LDAP_SETOPTS -- set LDAP options 1255 ** 1256 ** Parameters: 1257 ** ld -- LDAP session handle 1258 ** lmap -- LDAP map information 1259 ** 1260 ** Returns: 1261 ** None. 1262 ** 1263 */ 1264 1265 void 1266 sm_ldap_setopts(ld, lmap) 1267 LDAP *ld; 1268 SM_LDAP_STRUCT *lmap; 1269 { 1270 # if USE_LDAP_SET_OPTION 1271 if (lmap->ldap_version != 0) 1272 { 1273 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 1274 &lmap->ldap_version); 1275 } 1276 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 1277 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 1278 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 1279 else 1280 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1281 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 1282 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 1283 # ifdef LDAP_OPT_RESTART 1284 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1285 # endif /* LDAP_OPT_RESTART */ 1286 # else /* USE_LDAP_SET_OPTION */ 1287 /* From here on in we can use ldap internal timelimits */ 1288 ld->ld_deref = lmap->ldap_deref; 1289 ld->ld_options = lmap->ldap_options; 1290 ld->ld_sizelimit = lmap->ldap_sizelimit; 1291 ld->ld_timelimit = lmap->ldap_timelimit; 1292 # endif /* USE_LDAP_SET_OPTION */ 1293 } 1294 1295 /* 1296 ** SM_LDAP_GETERRNO -- get ldap errno value 1297 ** 1298 ** Parameters: 1299 ** ld -- LDAP session handle 1300 ** 1301 ** Returns: 1302 ** LDAP errno. 1303 ** 1304 */ 1305 1306 int 1307 sm_ldap_geterrno(ld) 1308 LDAP *ld; 1309 { 1310 int err = LDAP_SUCCESS; 1311 1312 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 1313 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 1314 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1315 # ifdef LDAP_OPT_SIZELIMIT 1316 err = ldap_get_lderrno(ld, NULL, NULL); 1317 # else /* LDAP_OPT_SIZELIMIT */ 1318 err = ld->ld_errno; 1319 1320 /* 1321 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 1322 ** OpenLDAP 1.X's hack (see above) 1323 */ 1324 1325 ld->ld_errno = LDAP_SUCCESS; 1326 # endif /* LDAP_OPT_SIZELIMIT */ 1327 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1328 return err; 1329 } 1330 # endif /* LDAPMAP */ 1331