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