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