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.62 2005/02/24 00:30:01 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 /* record completed DN's to prevent loops */ 621 dn = ldap_get_dn(lmap->ldap_ld, entry); 622 if (dn == NULL) 623 { 624 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 625 save_errno += E_LDAPBASE; 626 SM_LDAP_ERROR_CLEANUP(); 627 errno = save_errno; 628 return EX_TEMPFAIL; 629 } 630 631 rl = sm_ldap_add_recurse(&recurse, dn, 632 SM_LDAP_ATTR_DN, 633 rpool); 634 635 if (rl == NULL) 636 { 637 ldap_memfree(dn); 638 SM_LDAP_ERROR_CLEANUP(); 639 errno = ENOMEM; 640 return EX_OSERR; 641 } 642 else if (rl->lr_done) 643 { 644 /* already on list, skip it */ 645 ldap_memfree(dn); 646 continue; 647 } 648 ldap_memfree(dn); 649 650 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 651 /* 652 ** Reset value to prevent lingering 653 ** LDAP_DECODING_ERROR due to 654 ** OpenLDAP 1.X's hack (see below) 655 */ 656 657 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 658 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 659 660 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 661 &ber); 662 attr != NULL; 663 attr = ldap_next_attribute(lmap->ldap_ld, entry, 664 ber)) 665 { 666 char *tmp, *vp_tmp; 667 int type; 668 char *needobjclass = NULL; 669 670 type = SM_LDAP_ATTR_NONE; 671 for (i = 0; lmap->ldap_attr[i] != NULL; i++) 672 { 673 if (sm_strcasecmp(lmap->ldap_attr[i], 674 attr) == 0) 675 { 676 type = lmap->ldap_attr_type[i]; 677 needobjclass = lmap->ldap_attr_needobjclass[i]; 678 break; 679 } 680 } 681 682 if (bitset(SM_LDAP_USE_ALLATTR, flags) && 683 type == SM_LDAP_ATTR_NONE) 684 { 685 /* URL lookups specify attrs to use */ 686 type = SM_LDAP_ATTR_NORMAL; 687 needobjclass = NULL; 688 } 689 690 if (type == SM_LDAP_ATTR_NONE) 691 { 692 /* attribute not requested */ 693 ldap_memfree(attr); 694 SM_LDAP_ERROR_CLEANUP(); 695 errno = EFAULT; 696 return EX_SOFTWARE; 697 } 698 699 /* 700 ** For recursion on a particular attribute, 701 ** we may need to see if this entry is 702 ** part of a particular objectclass. 703 ** Also, ignore objectClass attribute. 704 ** Otherwise we just ignore this attribute. 705 */ 706 707 if (type == SM_LDAP_ATTR_OBJCLASS || 708 (needobjclass != NULL && 709 !sm_ldap_has_objectclass(lmap, entry, 710 needobjclass))) 711 { 712 ldap_memfree(attr); 713 continue; 714 } 715 716 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 717 { 718 vals = ldap_get_values(lmap->ldap_ld, 719 entry, 720 attr); 721 if (vals == NULL) 722 { 723 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 724 if (save_errno == LDAP_SUCCESS) 725 { 726 ldap_memfree(attr); 727 continue; 728 } 729 730 /* Must be an error */ 731 save_errno += E_LDAPBASE; 732 ldap_memfree(attr); 733 SM_LDAP_ERROR_CLEANUP(); 734 errno = save_errno; 735 return EX_TEMPFAIL; 736 } 737 } 738 739 statp = EX_OK; 740 741 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 742 /* 743 ** Reset value to prevent lingering 744 ** LDAP_DECODING_ERROR due to 745 ** OpenLDAP 1.X's hack (see below) 746 */ 747 748 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 749 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 750 751 /* 752 ** If matching only, 753 ** no need to spin through entries 754 */ 755 756 if (bitset(SM_LDAP_MATCHONLY, flags)) 757 { 758 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 759 ldap_value_free(vals); 760 ldap_memfree(attr); 761 continue; 762 } 763 764 /* 765 ** If we don't want multiple values, 766 ** return first found. 767 */ 768 769 if ((char) delim == '\0') 770 { 771 if (*result != NULL) 772 { 773 /* already have a value */ 774 if (bitset(SM_LDAP_SINGLEMATCH, 775 flags)) 776 { 777 /* only wanted one match */ 778 SM_LDAP_ERROR_CLEANUP(); 779 errno = ENOENT; 780 return EX_NOTFOUND; 781 } 782 break; 783 } 784 785 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 786 { 787 *result = sm_rpool_strdup_x(rpool, 788 attr); 789 ldap_memfree(attr); 790 break; 791 } 792 793 if (vals[0] == NULL) 794 { 795 ldap_value_free(vals); 796 ldap_memfree(attr); 797 continue; 798 } 799 800 vsize = strlen(vals[0]) + 1; 801 if (lmap->ldap_attrsep != '\0') 802 vsize += strlen(attr) + 1; 803 *result = sm_rpool_malloc_x(rpool, 804 vsize); 805 if (lmap->ldap_attrsep != '\0') 806 sm_snprintf(*result, vsize, 807 "%s%c%s", 808 attr, 809 lmap->ldap_attrsep, 810 vals[0]); 811 else 812 sm_strlcpy(*result, vals[0], 813 vsize); 814 ldap_value_free(vals); 815 ldap_memfree(attr); 816 break; 817 } 818 819 /* attributes only */ 820 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 821 { 822 if (*result == NULL) 823 *result = sm_rpool_strdup_x(rpool, 824 attr); 825 else 826 { 827 if (bitset(SM_LDAP_SINGLEMATCH, 828 flags) && 829 *result != NULL) 830 { 831 /* only wanted one match */ 832 SM_LDAP_ERROR_CLEANUP(); 833 errno = ENOENT; 834 return EX_NOTFOUND; 835 } 836 837 vsize = strlen(*result) + 838 strlen(attr) + 2; 839 tmp = sm_rpool_malloc_x(rpool, 840 vsize); 841 (void) sm_snprintf(tmp, 842 vsize, "%s%c%s", 843 *result, (char) delim, 844 attr); 845 *result = tmp; 846 } 847 ldap_memfree(attr); 848 continue; 849 } 850 851 /* 852 ** If there is more than one, munge then 853 ** into a map_coldelim separated string. 854 ** If we are recursing we may have an entry 855 ** with no 'normal' values to put in the 856 ** string. 857 ** This is not an error. 858 */ 859 860 if (type == SM_LDAP_ATTR_NORMAL && 861 bitset(SM_LDAP_SINGLEMATCH, flags) && 862 *result != NULL) 863 { 864 /* only wanted one match */ 865 SM_LDAP_ERROR_CLEANUP(); 866 errno = ENOENT; 867 return EX_NOTFOUND; 868 } 869 870 vsize = 0; 871 for (i = 0; vals[i] != NULL; i++) 872 { 873 if (type == SM_LDAP_ATTR_DN || 874 type == SM_LDAP_ATTR_FILTER || 875 type == SM_LDAP_ATTR_URL) 876 { 877 /* add to recursion */ 878 if (sm_ldap_add_recurse(&recurse, 879 vals[i], 880 type, 881 rpool) == NULL) 882 { 883 SM_LDAP_ERROR_CLEANUP(); 884 errno = ENOMEM; 885 return EX_OSERR; 886 } 887 continue; 888 } 889 890 vsize += strlen(vals[i]) + 1; 891 if (lmap->ldap_attrsep != '\0') 892 vsize += strlen(attr) + 1; 893 } 894 895 /* 896 ** Create/Append to string any normal 897 ** attribute values. Otherwise, just free 898 ** memory and move on to the next 899 ** attribute in this entry. 900 */ 901 902 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0) 903 { 904 char *pe; 905 906 /* Grow result string if needed */ 907 if ((*resultln + vsize) >= *resultsz) 908 { 909 while ((*resultln + vsize) >= *resultsz) 910 { 911 if (*resultsz == 0) 912 *resultsz = 1024; 913 else 914 *resultsz *= 2; 915 } 916 917 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz); 918 *vp_tmp = '\0'; 919 920 if (*result != NULL) 921 sm_strlcpy(vp_tmp, 922 *result, 923 *resultsz); 924 *result = vp_tmp; 925 } 926 927 p = *result + *resultln; 928 pe = *result + *resultsz; 929 930 for (i = 0; vals[i] != NULL; i++) 931 { 932 if (*resultln > 0 && 933 p < pe) 934 *p++ = (char) delim; 935 936 if (lmap->ldap_attrsep != '\0') 937 { 938 p += sm_strlcpy(p, attr, 939 pe - p); 940 if (p < pe) 941 *p++ = lmap->ldap_attrsep; 942 } 943 944 p += sm_strlcpy(p, vals[i], 945 pe - p); 946 *resultln = p - (*result); 947 if (p >= pe) 948 { 949 /* Internal error: buffer too small for LDAP values */ 950 SM_LDAP_ERROR_CLEANUP(); 951 errno = ENOMEM; 952 return EX_OSERR; 953 } 954 } 955 } 956 957 ldap_value_free(vals); 958 ldap_memfree(attr); 959 } 960 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 961 962 /* 963 ** We check save_errno != LDAP_DECODING_ERROR since 964 ** OpenLDAP 1.X has a very ugly *undocumented* 965 ** hack of returning this error code from 966 ** ldap_next_attribute() if the library freed the 967 ** ber attribute. See: 968 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 969 */ 970 971 if (save_errno != LDAP_SUCCESS && 972 save_errno != LDAP_DECODING_ERROR) 973 { 974 /* Must be an error */ 975 save_errno += E_LDAPBASE; 976 SM_LDAP_ERROR_CLEANUP(); 977 errno = save_errno; 978 return EX_TEMPFAIL; 979 } 980 981 /* mark this DN as done */ 982 rl->lr_done = true; 983 if (rl->lr_ludp != NULL) 984 { 985 ldap_free_urldesc(rl->lr_ludp); 986 rl->lr_ludp = NULL; 987 } 988 if (rl->lr_attrs != NULL) 989 { 990 free(rl->lr_attrs); 991 rl->lr_attrs = NULL; 992 } 993 994 /* We don't want multiple values and we have one */ 995 if ((char) delim == '\0' && 996 !bitset(SM_LDAP_SINGLEMATCH, flags) && 997 *result != NULL) 998 break; 999 } 1000 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1001 if (save_errno != LDAP_SUCCESS && 1002 save_errno != LDAP_DECODING_ERROR) 1003 { 1004 /* Must be an error */ 1005 save_errno += E_LDAPBASE; 1006 SM_LDAP_ERROR_CLEANUP(); 1007 errno = save_errno; 1008 return EX_TEMPFAIL; 1009 } 1010 ldap_msgfree(lmap->ldap_res); 1011 lmap->ldap_res = NULL; 1012 } 1013 1014 if (ret == 0) 1015 save_errno = ETIMEDOUT; 1016 else 1017 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1018 if (save_errno != LDAP_SUCCESS) 1019 { 1020 statp = EX_TEMPFAIL; 1021 if (ret != 0) 1022 { 1023 switch (save_errno) 1024 { 1025 #ifdef LDAP_SERVER_DOWN 1026 case LDAP_SERVER_DOWN: 1027 #endif /* LDAP_SERVER_DOWN */ 1028 case LDAP_TIMEOUT: 1029 case LDAP_UNAVAILABLE: 1030 1031 /* 1032 ** server disappeared, 1033 ** try reopen on next search 1034 */ 1035 1036 statp = EX_RESTART; 1037 break; 1038 } 1039 save_errno += E_LDAPBASE; 1040 } 1041 SM_LDAP_ERROR_CLEANUP(); 1042 errno = save_errno; 1043 return statp; 1044 } 1045 1046 if (lmap->ldap_res != NULL) 1047 { 1048 ldap_msgfree(lmap->ldap_res); 1049 lmap->ldap_res = NULL; 1050 } 1051 1052 if (toplevel) 1053 { 1054 int rlidx; 1055 1056 /* 1057 ** Spin through the built-up recurse list at the top 1058 ** of the recursion. Since new items are added at the 1059 ** end of the shared list, we actually only ever get 1060 ** one level of recursion before things pop back to the 1061 ** top. Any items added to the list during that recursion 1062 ** will be expanded by the top level. 1063 */ 1064 1065 for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++) 1066 { 1067 int newflags; 1068 int sid; 1069 int status; 1070 1071 rl = recurse->lr_data[rlidx]; 1072 1073 newflags = flags; 1074 if (rl->lr_done) 1075 { 1076 /* already expanded */ 1077 continue; 1078 } 1079 1080 if (rl->lr_type == SM_LDAP_ATTR_DN) 1081 { 1082 /* do DN search */ 1083 sid = ldap_search(lmap->ldap_ld, 1084 rl->lr_search, 1085 lmap->ldap_scope, 1086 "(objectClass=*)", 1087 (lmap->ldap_attr[0] == NULL ? 1088 NULL : lmap->ldap_attr), 1089 lmap->ldap_attrsonly); 1090 } 1091 else if (rl->lr_type == SM_LDAP_ATTR_FILTER) 1092 { 1093 /* do new search */ 1094 sid = ldap_search(lmap->ldap_ld, 1095 lmap->ldap_base, 1096 lmap->ldap_scope, 1097 rl->lr_search, 1098 (lmap->ldap_attr[0] == NULL ? 1099 NULL : lmap->ldap_attr), 1100 lmap->ldap_attrsonly); 1101 } 1102 else if (rl->lr_type == SM_LDAP_ATTR_URL) 1103 { 1104 /* Parse URL */ 1105 sid = ldap_url_parse(rl->lr_search, 1106 &rl->lr_ludp); 1107 1108 if (sid != 0) 1109 { 1110 errno = sid + E_LDAPURLBASE; 1111 return EX_TEMPFAIL; 1112 } 1113 1114 /* We need to add objectClass */ 1115 if (rl->lr_ludp->lud_attrs != NULL) 1116 { 1117 int attrnum = 0; 1118 1119 while (rl->lr_ludp->lud_attrs[attrnum] != NULL) 1120 { 1121 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum], 1122 "objectClass") == 0) 1123 { 1124 /* already requested */ 1125 attrnum = -1; 1126 break; 1127 } 1128 attrnum++; 1129 } 1130 1131 if (attrnum >= 0) 1132 { 1133 int i; 1134 1135 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2)); 1136 if (rl->lr_attrs == NULL) 1137 { 1138 save_errno = errno; 1139 ldap_free_urldesc(rl->lr_ludp); 1140 errno = save_errno; 1141 return EX_TEMPFAIL; 1142 } 1143 for (i = 0 ; i < attrnum; i++) 1144 { 1145 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i]; 1146 } 1147 rl->lr_attrs[i++] = "objectClass"; 1148 rl->lr_attrs[i++] = NULL; 1149 } 1150 } 1151 1152 /* 1153 ** Use the existing connection 1154 ** for this search. It really 1155 ** should use lud_scheme://lud_host:lud_port/ 1156 ** instead but that would require 1157 ** opening a new connection. 1158 ** This should be fixed ASAP. 1159 */ 1160 1161 sid = ldap_search(lmap->ldap_ld, 1162 rl->lr_ludp->lud_dn, 1163 rl->lr_ludp->lud_scope, 1164 rl->lr_ludp->lud_filter, 1165 rl->lr_attrs, 1166 lmap->ldap_attrsonly); 1167 1168 /* Use the attributes specified by URL */ 1169 newflags |= SM_LDAP_USE_ALLATTR; 1170 } 1171 else 1172 { 1173 /* unknown or illegal attribute type */ 1174 errno = EFAULT; 1175 return EX_SOFTWARE; 1176 } 1177 1178 /* Collect results */ 1179 if (sid == -1) 1180 { 1181 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1182 statp = EX_TEMPFAIL; 1183 switch (save_errno) 1184 { 1185 #ifdef LDAP_SERVER_DOWN 1186 case LDAP_SERVER_DOWN: 1187 #endif /* LDAP_SERVER_DOWN */ 1188 case LDAP_TIMEOUT: 1189 case LDAP_UNAVAILABLE: 1190 1191 /* 1192 ** server disappeared, 1193 ** try reopen on next search 1194 */ 1195 1196 statp = EX_RESTART; 1197 break; 1198 } 1199 errno = save_errno + E_LDAPBASE; 1200 return statp; 1201 } 1202 1203 status = sm_ldap_results(lmap, sid, newflags, delim, 1204 rpool, result, resultln, 1205 resultsz, recurse); 1206 save_errno = errno; 1207 if (status != EX_OK && status != EX_NOTFOUND) 1208 { 1209 errno = save_errno; 1210 return status; 1211 } 1212 1213 /* Mark as done */ 1214 rl->lr_done = true; 1215 if (rl->lr_ludp != NULL) 1216 { 1217 ldap_free_urldesc(rl->lr_ludp); 1218 rl->lr_ludp = NULL; 1219 } 1220 if (rl->lr_attrs != NULL) 1221 { 1222 free(rl->lr_attrs); 1223 rl->lr_attrs = NULL; 1224 } 1225 1226 /* Reset rlidx as new items may have been added */ 1227 rlidx = -1; 1228 } 1229 } 1230 return statp; 1231 } 1232 1233 /* 1234 ** SM_LDAP_CLOSE -- close LDAP connection 1235 ** 1236 ** Parameters: 1237 ** lmap -- LDAP map information 1238 ** 1239 ** Returns: 1240 ** None. 1241 ** 1242 */ 1243 1244 void 1245 sm_ldap_close(lmap) 1246 SM_LDAP_STRUCT *lmap; 1247 { 1248 if (lmap->ldap_ld == NULL) 1249 return; 1250 1251 if (lmap->ldap_pid == getpid()) 1252 ldap_unbind(lmap->ldap_ld); 1253 lmap->ldap_ld = NULL; 1254 lmap->ldap_pid = 0; 1255 } 1256 1257 /* 1258 ** SM_LDAP_SETOPTS -- set LDAP options 1259 ** 1260 ** Parameters: 1261 ** ld -- LDAP session handle 1262 ** lmap -- LDAP map information 1263 ** 1264 ** Returns: 1265 ** None. 1266 ** 1267 */ 1268 1269 void 1270 sm_ldap_setopts(ld, lmap) 1271 LDAP *ld; 1272 SM_LDAP_STRUCT *lmap; 1273 { 1274 # if USE_LDAP_SET_OPTION 1275 if (lmap->ldap_version != 0) 1276 { 1277 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 1278 &lmap->ldap_version); 1279 } 1280 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 1281 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 1282 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 1283 else 1284 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1285 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 1286 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 1287 # ifdef LDAP_OPT_RESTART 1288 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1289 # endif /* LDAP_OPT_RESTART */ 1290 # else /* USE_LDAP_SET_OPTION */ 1291 /* From here on in we can use ldap internal timelimits */ 1292 ld->ld_deref = lmap->ldap_deref; 1293 ld->ld_options = lmap->ldap_options; 1294 ld->ld_sizelimit = lmap->ldap_sizelimit; 1295 ld->ld_timelimit = lmap->ldap_timelimit; 1296 # endif /* USE_LDAP_SET_OPTION */ 1297 } 1298 1299 /* 1300 ** SM_LDAP_GETERRNO -- get ldap errno value 1301 ** 1302 ** Parameters: 1303 ** ld -- LDAP session handle 1304 ** 1305 ** Returns: 1306 ** LDAP errno. 1307 ** 1308 */ 1309 1310 int 1311 sm_ldap_geterrno(ld) 1312 LDAP *ld; 1313 { 1314 int err = LDAP_SUCCESS; 1315 1316 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 1317 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 1318 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1319 # ifdef LDAP_OPT_SIZELIMIT 1320 err = ldap_get_lderrno(ld, NULL, NULL); 1321 # else /* LDAP_OPT_SIZELIMIT */ 1322 err = ld->ld_errno; 1323 1324 /* 1325 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 1326 ** OpenLDAP 1.X's hack (see above) 1327 */ 1328 1329 ld->ld_errno = LDAP_SUCCESS; 1330 # endif /* LDAP_OPT_SIZELIMIT */ 1331 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1332 return err; 1333 } 1334 # endif /* LDAPMAP */ 1335