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