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