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