1 /* 2 * Copyright (c) 2001-2007 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 /* 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.80 2007/10/12 00:19:44 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 1102 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1103 if (save_errno != LDAP_SUCCESS) 1104 { 1105 statp = EX_TEMPFAIL; 1106 switch (save_errno) 1107 { 1108 #ifdef LDAP_SERVER_DOWN 1109 case LDAP_SERVER_DOWN: 1110 #endif /* LDAP_SERVER_DOWN */ 1111 case LDAP_TIMEOUT: 1112 case ETIMEDOUT: 1113 case LDAP_UNAVAILABLE: 1114 1115 /* 1116 ** server disappeared, 1117 ** try reopen on next search 1118 */ 1119 1120 statp = EX_RESTART; 1121 break; 1122 } 1123 if (ret != 0) 1124 save_errno += E_LDAPBASE; 1125 SM_LDAP_ERROR_CLEANUP(); 1126 errno = save_errno; 1127 return statp; 1128 } 1129 1130 if (lmap->ldap_res != NULL) 1131 { 1132 ldap_msgfree(lmap->ldap_res); 1133 lmap->ldap_res = NULL; 1134 } 1135 1136 if (toplevel) 1137 { 1138 int rlidx; 1139 1140 /* 1141 ** Spin through the built-up recurse list at the top 1142 ** of the recursion. Since new items are added at the 1143 ** end of the shared list, we actually only ever get 1144 ** one level of recursion before things pop back to the 1145 ** top. Any items added to the list during that recursion 1146 ** will be expanded by the top level. 1147 */ 1148 1149 for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt; 1150 rlidx++) 1151 { 1152 int newflags; 1153 int sid; 1154 int status; 1155 1156 rl = recurse->lrl_data[rlidx]; 1157 1158 newflags = flags; 1159 if (rl->lr_done) 1160 { 1161 /* already expanded */ 1162 continue; 1163 } 1164 1165 if (rl->lr_type == SM_LDAP_ATTR_DN) 1166 { 1167 /* do DN search */ 1168 sid = ldap_search(lmap->ldap_ld, 1169 rl->lr_search, 1170 lmap->ldap_scope, 1171 "(objectClass=*)", 1172 (lmap->ldap_attr[0] == NULL ? 1173 NULL : lmap->ldap_attr), 1174 lmap->ldap_attrsonly); 1175 } 1176 else if (rl->lr_type == SM_LDAP_ATTR_FILTER) 1177 { 1178 /* do new search */ 1179 sid = ldap_search(lmap->ldap_ld, 1180 lmap->ldap_base, 1181 lmap->ldap_scope, 1182 rl->lr_search, 1183 (lmap->ldap_attr[0] == NULL ? 1184 NULL : lmap->ldap_attr), 1185 lmap->ldap_attrsonly); 1186 } 1187 else if (rl->lr_type == SM_LDAP_ATTR_URL) 1188 { 1189 /* Parse URL */ 1190 sid = ldap_url_parse(rl->lr_search, 1191 &rl->lr_ludp); 1192 1193 if (sid != 0) 1194 { 1195 errno = sid + E_LDAPURLBASE; 1196 return EX_TEMPFAIL; 1197 } 1198 1199 /* We need to add objectClass */ 1200 if (rl->lr_ludp->lud_attrs != NULL) 1201 { 1202 int attrnum = 0; 1203 1204 while (rl->lr_ludp->lud_attrs[attrnum] != NULL) 1205 { 1206 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum], 1207 "objectClass") == 0) 1208 { 1209 /* already requested */ 1210 attrnum = -1; 1211 break; 1212 } 1213 attrnum++; 1214 } 1215 1216 if (attrnum >= 0) 1217 { 1218 int i; 1219 1220 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2)); 1221 if (rl->lr_attrs == NULL) 1222 { 1223 save_errno = errno; 1224 ldap_free_urldesc(rl->lr_ludp); 1225 errno = save_errno; 1226 return EX_TEMPFAIL; 1227 } 1228 for (i = 0 ; i < attrnum; i++) 1229 { 1230 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i]; 1231 } 1232 rl->lr_attrs[i++] = "objectClass"; 1233 rl->lr_attrs[i++] = NULL; 1234 } 1235 } 1236 1237 /* 1238 ** Use the existing connection 1239 ** for this search. It really 1240 ** should use lud_scheme://lud_host:lud_port/ 1241 ** instead but that would require 1242 ** opening a new connection. 1243 ** This should be fixed ASAP. 1244 */ 1245 1246 sid = ldap_search(lmap->ldap_ld, 1247 rl->lr_ludp->lud_dn, 1248 rl->lr_ludp->lud_scope, 1249 rl->lr_ludp->lud_filter, 1250 rl->lr_attrs, 1251 lmap->ldap_attrsonly); 1252 1253 /* Use the attributes specified by URL */ 1254 newflags |= SM_LDAP_USE_ALLATTR; 1255 } 1256 else 1257 { 1258 /* unknown or illegal attribute type */ 1259 errno = EFAULT; 1260 return EX_SOFTWARE; 1261 } 1262 1263 /* Collect results */ 1264 if (sid == -1) 1265 { 1266 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 1267 statp = EX_TEMPFAIL; 1268 switch (save_errno) 1269 { 1270 #ifdef LDAP_SERVER_DOWN 1271 case LDAP_SERVER_DOWN: 1272 #endif /* LDAP_SERVER_DOWN */ 1273 case LDAP_TIMEOUT: 1274 case ETIMEDOUT: 1275 case LDAP_UNAVAILABLE: 1276 1277 /* 1278 ** server disappeared, 1279 ** try reopen on next search 1280 */ 1281 1282 statp = EX_RESTART; 1283 break; 1284 } 1285 errno = save_errno + E_LDAPBASE; 1286 return statp; 1287 } 1288 1289 status = sm_ldap_results(lmap, sid, newflags, delim, 1290 rpool, result, resultln, 1291 resultsz, recurse); 1292 save_errno = errno; 1293 if (status != EX_OK && status != EX_NOTFOUND) 1294 { 1295 errno = save_errno; 1296 return status; 1297 } 1298 1299 /* Mark as done */ 1300 rl->lr_done = true; 1301 if (rl->lr_ludp != NULL) 1302 { 1303 ldap_free_urldesc(rl->lr_ludp); 1304 rl->lr_ludp = NULL; 1305 } 1306 if (rl->lr_attrs != NULL) 1307 { 1308 free(rl->lr_attrs); 1309 rl->lr_attrs = NULL; 1310 } 1311 1312 /* Reset rlidx as new items may have been added */ 1313 rlidx = -1; 1314 } 1315 } 1316 return statp; 1317 } 1318 1319 /* 1320 ** SM_LDAP_CLOSE -- close LDAP connection 1321 ** 1322 ** Parameters: 1323 ** lmap -- LDAP map information 1324 ** 1325 ** Returns: 1326 ** None. 1327 ** 1328 */ 1329 1330 void 1331 sm_ldap_close(lmap) 1332 SM_LDAP_STRUCT *lmap; 1333 { 1334 if (lmap->ldap_ld == NULL) 1335 return; 1336 1337 if (lmap->ldap_pid == getpid()) 1338 ldap_unbind(lmap->ldap_ld); 1339 lmap->ldap_ld = NULL; 1340 lmap->ldap_pid = 0; 1341 } 1342 1343 /* 1344 ** SM_LDAP_SETOPTS -- set LDAP options 1345 ** 1346 ** Parameters: 1347 ** ld -- LDAP session handle 1348 ** lmap -- LDAP map information 1349 ** 1350 ** Returns: 1351 ** None. 1352 ** 1353 */ 1354 1355 void 1356 sm_ldap_setopts(ld, lmap) 1357 LDAP *ld; 1358 SM_LDAP_STRUCT *lmap; 1359 { 1360 # if USE_LDAP_SET_OPTION 1361 if (lmap->ldap_version != 0) 1362 { 1363 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 1364 &lmap->ldap_version); 1365 } 1366 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 1367 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 1368 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 1369 else 1370 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1371 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 1372 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 1373 # if _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) 1374 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &lmap->ldap_networktmo); 1375 # endif /* _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */ 1376 # ifdef LDAP_OPT_RESTART 1377 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1378 # endif /* LDAP_OPT_RESTART */ 1379 # else /* USE_LDAP_SET_OPTION */ 1380 /* From here on in we can use ldap internal timelimits */ 1381 ld->ld_deref = lmap->ldap_deref; 1382 ld->ld_options = lmap->ldap_options; 1383 ld->ld_sizelimit = lmap->ldap_sizelimit; 1384 ld->ld_timelimit = lmap->ldap_timelimit; 1385 # endif /* USE_LDAP_SET_OPTION */ 1386 } 1387 1388 /* 1389 ** SM_LDAP_GETERRNO -- get ldap errno value 1390 ** 1391 ** Parameters: 1392 ** ld -- LDAP session handle 1393 ** 1394 ** Returns: 1395 ** LDAP errno. 1396 ** 1397 */ 1398 1399 int 1400 sm_ldap_geterrno(ld) 1401 LDAP *ld; 1402 { 1403 int err = LDAP_SUCCESS; 1404 1405 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 1406 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 1407 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1408 # ifdef LDAP_OPT_SIZELIMIT 1409 err = ldap_get_lderrno(ld, NULL, NULL); 1410 # else /* LDAP_OPT_SIZELIMIT */ 1411 err = ld->ld_errno; 1412 1413 /* 1414 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 1415 ** OpenLDAP 1.X's hack (see above) 1416 */ 1417 1418 ld->ld_errno = LDAP_SUCCESS; 1419 # endif /* LDAP_OPT_SIZELIMIT */ 1420 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 1421 return err; 1422 } 1423 # endif /* LDAPMAP */ 1424