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