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