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