2 * Copyright (c) 2001-2009 Proofpoint, Inc. and its suppliers.
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.
10 /* some "deprecated" calls are used, e.g., ldap_get_values() */
11 #define LDAP_DEPRECATED 1
14 SM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
17 # include <sys/types.h>
23 # include <sm/bitops.h>
24 # include <sm/clock.h>
26 # include <sm/debug.h>
27 # include <sm/errstring.h>
29 # include <sm/string.h>
30 # include <sm/sysexits.h>
31 # include <sm/sendmail.h>
33 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
34 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
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 *));
40 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
43 ** lmap -- pointer to SM_LDAP_STRUCT to clear
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"
54 # if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
55 # ERROR "_FFR_LDAP_VERSION < LDAP_VERSION_MAX"
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 */
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;
79 lmap->ldap_options = 0;
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;
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;
98 lmap->ldap_multi_args = false;
101 # if _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN)
102 static void ldap_debug_cb __P((const char *msg));
108 if (sm_debug_active(&SmLDAPTrace, 4))
109 sm_dprintf("%s", msg);
111 # endif /* _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN) */
114 # if LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
115 # define SET_LDAP_TMO(ld, lmap) \
118 if (lmap->ldap_networktmo > 0) \
120 struct timeval tmo; \
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; \
127 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo); \
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) */
135 ** SM_LDAP_SETOPTSG -- set some (global) LDAP options
138 ** lmap -- LDAP map information
145 # if _FFR_SM_LDAP_DBG
146 static bool dbg_init = false;
148 # if SM_CONF_LDAP_INITIALIZE
149 static void sm_ldap_setoptsg __P((SM_LDAP_STRUCT *lmap));
151 sm_ldap_setoptsg(lmap)
152 SM_LDAP_STRUCT *lmap;
154 # if USE_LDAP_SET_OPTION
156 SET_LDAP_TMO(NULL, lmap);
158 # if _FFR_SM_LDAP_DBG
159 if (!dbg_init && sm_debug_active(&SmLDAPTrace, 1) &&
160 lmap->ldap_debug != 0)
163 # if defined(LBER_OPT_LOG_PRINT_FN)
164 r = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, ldap_debug_cb);
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);
178 # endif /* _FFR_SM_LDAP_DBG */
179 # endif /* USE_LDAP_SET_OPTION */
181 # endif /* SM_CONF_LDAP_INITIALIZE */
184 ** SM_LDAP_SETOPTS -- set LDAP options
187 ** ld -- LDAP session handle
188 ** lmap -- LDAP map information
196 sm_ldap_setopts(ld, lmap)
198 SM_LDAP_STRUCT *lmap;
200 # if USE_LDAP_SET_OPTION
201 if (lmap->ldap_version != 0)
203 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
204 &lmap->ldap_version);
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);
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)
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);
228 # endif /* _FFR_SM_LDAP_DBG */
229 # ifdef LDAP_OPT_RESTART
230 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
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 */
243 ** SM_LDAP_START -- actually connect to an LDAP server
246 ** name -- name of map for debug output.
247 ** lmap -- the LDAP map being opened.
250 ** true if connection is successful, false otherwise.
253 ** Populates lmap->ldap_ld.
256 # if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
257 static jmp_buf LDAPTimeout;
258 static void ldaptimeout __P((int));
266 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
267 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
272 longjmp(LDAPTimeout, 1);
276 #define SM_LDAP_SETTIMEOUT(to, where) \
281 if (setjmp(LDAPTimeout) != 0) \
283 if (sm_debug_active(&SmLDAPTrace, 9)) \
284 sm_dprintf("ldap_settimeout(%s)=triggered\n",\
289 ev = sm_setevent(to, ldaptimeout, 0); \
293 #define SM_LDAP_CLEARTIMEOUT() \
299 # endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
302 sm_ldap_start(name, lmap)
304 SM_LDAP_STRUCT *lmap;
308 # if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
315 if (sm_debug_active(&SmLDAPTrace, 2))
316 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
318 if (lmap->ldap_host != NULL)
319 id = lmap->ldap_host;
320 else if (lmap->ldap_uri != NULL)
325 if (sm_debug_active(&SmLDAPTrace, 9))
327 /* Don't print a port number for LDAP URIs */
328 if (lmap->ldap_uri != NULL)
329 sm_dprintf("ldapmap_start(%s)\n", id);
331 sm_dprintf("ldapmap_start(%s, %d)\n", id,
335 if (lmap->ldap_uri != NULL)
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);
346 # else /* SM_CONF_LDAP_INITIALIZE */
347 LDAPURLDesc *ludp = NULL;
349 /* Blast apart URL and use the ldap_init/ldap_open below */
350 err = ldap_url_parse(lmap->ldap_uri, &ludp);
353 errno = err + E_LDAPURLBASE;
356 lmap->ldap_host = sm_strdup_x(ludp->lud_host);
357 if (lmap->ldap_host == NULL)
360 ldap_free_urldesc(ludp);
364 lmap->ldap_port = ludp->lud_port;
365 ldap_free_urldesc(ludp);
366 # endif /* SM_CONF_LDAP_INITIALIZE */
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);
377 # else /* USE_LDAP_INIT */
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.
384 if (sm_debug_active(&SmLDAPTrace, 9))
385 sm_dprintf("ldap_open(%s, %d)\n", lmap->ldap_host, lmap->ldap_port);
387 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_open");
388 ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
391 /* clear the event if it has not sprung */
392 SM_LDAP_CLEARTIMEOUT();
393 # endif /* USE_LDAP_INIT */
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);
404 sm_ldap_setopts(ld, lmap);
405 # if USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT
407 ** If using ldap_init(), the actual connection to the server
408 ** happens at ldap_bind_s() so we need the timeout here.
411 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_bind");
412 # endif /* USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT */
414 # ifdef LDAP_AUTH_KRBV4
415 if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
416 lmap->ldap_secret != NULL)
419 ** Need to put ticket in environment here instead of
420 ** during parseargs as there may be different tickets
421 ** for different LDAP connections.
424 (void) putenv(lmap->ldap_secret);
426 # endif /* LDAP_AUTH_KRBV4 */
428 # if LDAP_NETWORK_TIMEOUT
429 tmo.tv_sec = lmap->ldap_networktmo;
431 tmo.tv_sec = lmap->ldap_timeout.tv_sec;
435 if (sm_debug_active(&SmLDAPTrace, 9))
436 sm_dprintf("ldap_bind(%s)\n", lmap->ldap_uri);
438 msgid = ldap_bind(ld, lmap->ldap_binddn, lmap->ldap_secret,
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,
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);
460 save_errno = ETIMEDOUT;
464 r = ldap_parse_result(ld, lmap->ldap_res, &err, NULL, NULL, NULL, NULL,
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)
470 if (err != LDAP_SUCCESS)
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 */
483 if (r != LDAP_SUCCESS)
489 errno = r + E_LDAPBASE;
493 /* Save PID to make sure only this PID closes the LDAP connection */
494 lmap->ldap_pid = getpid();
500 ** SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
502 ** Initiate an LDAP search, return the msgid.
503 ** The calling function must collect the results.
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
512 ** <0 on failure (SM_LDAP_ERR*), msgid on success
517 sm_ldap_search_m(lmap, argv)
518 SM_LDAP_STRUCT *lmap;
523 char filter[LDAPMAP_MAX_FILTER + 1];
525 SM_REQUIRE(lmap != NULL);
526 SM_REQUIRE(argv != NULL);
527 SM_REQUIRE(argv[0] != NULL);
529 memset(filter, '\0', sizeof filter);
531 p = lmap->ldap_filter;
532 while ((q = strchr(p, '%')) != NULL)
536 if (lmap->ldap_multi_args)
538 # if SM_LDAP_ARGS < 10
539 # ERROR _SM_LDAP_ARGS must be 10
543 else if (q[1] >= '0' && q[1] <= '9')
545 key = argv[q[1] - '0'];
548 # if SM_LDAP_ERROR_ON_MISSING_ARGS
549 return SM_LDAP_ERR_ARG_MISS;
563 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
564 "%.*s%s", (int) (q - p), p, key);
568 else if (q[1] == '0' ||
569 (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
573 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
574 "%.*s", (int) (q - p), p);
578 /* Properly escape LDAP special characters */
579 while (SPACELEFT(filter, fp) > 0 &&
582 if (*k == '*' || *k == '(' ||
583 *k == ')' || *k == '\\')
585 (void) sm_strlcat(fp,
586 (*k == '*' ? "\\2A" :
587 (*k == '(' ? "\\28" :
588 (*k == ')' ? "\\29" :
589 (*k == '\\' ? "\\5C" :
591 SPACELEFT(filter, fp));
601 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
602 "%.*s", (int) (q - p + 1), p);
603 p = q + (q[1] == '%' ? 2 : 1);
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);
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 :
616 lmap->ldap_attrsonly);
621 ** SM_LDAP_SEARCH -- initiate LDAP search
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()
628 ** lmap -- LDAP map information
629 ** key -- key to substitute in LDAP filter
632 ** <0 on failure, msgid on success
637 sm_ldap_search(lmap, key)
638 SM_LDAP_STRUCT *lmap;
641 char *argv[SM_LDAP_ARGS];
643 memset(argv, '\0', sizeof argv);
645 return sm_ldap_search_m(lmap, argv);
649 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
650 ** particular objectClass
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
661 ** true if item has that objectClass
665 sm_ldap_has_objectclass(lmap, entry, ocvalue)
666 SM_LDAP_STRUCT *lmap;
676 vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
680 for (i = 0; vals[i] != NULL; i++)
688 while (*p != '\0' && *p != '|')
691 if ((p - q) == strlen(vals[i]) &&
692 sm_strncasecmp(vals[i], q, p - q) == 0)
694 ldap_value_free(vals);
704 ldap_value_free(vals);
709 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
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
724 # define SM_LDAP_ERROR_CLEANUP() \
726 if (lmap->ldap_res != NULL) \
728 ldap_msgfree(lmap->ldap_res); \
729 lmap->ldap_res = NULL; \
731 (void) ldap_abandon(lmap->ldap_ld, msgid); \
734 static SM_LDAP_RECURSE_ENTRY *
735 sm_ldap_add_recurse(top, item, type, rpool)
736 SM_LDAP_RECURSE_LIST **top;
748 SM_LDAP_RECURSE_ENTRY *newe;
749 SM_LDAP_RECURSE_ENTRY **olddata;
752 ** This code will maintain a list of
753 ** SM_LDAP_RECURSE_ENTRY structures
754 ** in ascending order.
759 /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
760 *top = sm_rpool_malloc_x(rpool, sizeof **top);
762 (*top)->lrl_size = 0;
763 (*top)->lrl_data = NULL;
766 if ((*top)->lrl_cnt >= (*top)->lrl_size)
768 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
769 olddata = (*top)->lrl_data;
770 if ((*top)->lrl_size == 0)
773 (*top)->lrl_size = 256;
777 oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
778 (*top)->lrl_size *= 2;
780 (*top)->lrl_data = sm_rpool_malloc_x(rpool,
781 (*top)->lrl_size * sizeof *((*top)->lrl_data));
783 memcpy((*top)->lrl_data, olddata, oldsizeb);
787 ** Binary search/insert item:type into list.
788 ** Return current entry pointer if already exists.
792 m = (*top)->lrl_cnt - 1;
798 while (insertat == -1)
802 rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
804 rc = type - (*top)->lrl_data[p]->lr_type;
811 return (*top)->lrl_data[p];
815 else if (n >= (*top)->lrl_cnt)
816 insertat = (*top)->lrl_cnt;
822 ** Not found in list, make room
823 ** at insert point and add it.
826 newe = sm_rpool_malloc_x(rpool, sizeof *newe);
829 moveb = ((*top)->lrl_cnt - insertat) * sizeof *((*top)->lrl_data);
831 memmove(&((*top)->lrl_data[insertat + 1]),
832 &((*top)->lrl_data[insertat]),
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;
841 ((*top)->lrl_data)[insertat] = newe;
848 sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
849 resultln, resultsz, recurse)
850 SM_LDAP_STRUCT *lmap;
858 SM_LDAP_RECURSE_LIST *recurse;
867 SM_LDAP_RECURSE_ENTRY *rl;
869 /* Are we the top top level of the search? */
870 toplevel = (recurse == NULL);
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)
881 /* If we don't want multiple values and we have one, break */
882 if ((char) delim == '\0' &&
883 !bitset(SM_LDAP_SINGLEMATCH, flags) &&
887 /* Cycle through all entries */
888 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
890 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
898 ** If matching only and found an entry,
899 ** no need to spin through attributes
902 if (bitset(SM_LDAP_MATCHONLY, flags))
908 # if _FFR_LDAP_SINGLEDN
909 if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
911 /* only wanted one match */
912 SM_LDAP_ERROR_CLEANUP();
916 # endif /* _FFR_LDAP_SINGLEDN */
918 /* record completed DN's to prevent loops */
919 dn = ldap_get_dn(lmap->ldap_ld, entry);
922 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
923 save_errno += E_LDAPBASE;
924 SM_LDAP_ERROR_CLEANUP();
929 rl = sm_ldap_add_recurse(&recurse, dn,
936 SM_LDAP_ERROR_CLEANUP();
940 else if (rl->lr_done)
942 /* already on list, skip it */
948 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
950 ** Reset value to prevent lingering
951 ** LDAP_DECODING_ERROR due to
952 ** OpenLDAP 1.X's hack (see below)
955 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
956 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
958 for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
961 attr = ldap_next_attribute(lmap->ldap_ld, entry,
966 char *needobjclass = NULL;
968 type = SM_LDAP_ATTR_NONE;
969 for (i = 0; lmap->ldap_attr[i] != NULL; i++)
971 if (SM_STRCASEEQ(lmap->ldap_attr[i],
974 type = lmap->ldap_attr_type[i];
975 needobjclass = lmap->ldap_attr_needobjclass[i];
980 if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
981 type == SM_LDAP_ATTR_NONE)
983 /* URL lookups specify attrs to use */
984 type = SM_LDAP_ATTR_NORMAL;
988 if (type == SM_LDAP_ATTR_NONE)
990 /* attribute not requested */
992 SM_LDAP_ERROR_CLEANUP();
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.
1005 if (type == SM_LDAP_ATTR_OBJCLASS ||
1006 (needobjclass != NULL &&
1007 !sm_ldap_has_objectclass(lmap, entry,
1014 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
1016 vals = ldap_get_values(lmap->ldap_ld,
1021 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1022 if (save_errno == LDAP_SUCCESS)
1028 /* Must be an error */
1029 save_errno += E_LDAPBASE;
1031 SM_LDAP_ERROR_CLEANUP();
1039 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
1041 ** Reset value to prevent lingering
1042 ** LDAP_DECODING_ERROR due to
1043 ** OpenLDAP 1.X's hack (see below)
1046 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
1047 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
1050 ** If matching only,
1051 ** no need to spin through entries
1054 if (bitset(SM_LDAP_MATCHONLY, flags))
1056 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
1057 ldap_value_free(vals);
1063 ** If we don't want multiple values,
1064 ** return first found.
1067 if ((char) delim == '\0')
1069 if (*result != NULL)
1071 /* already have a value */
1072 if (bitset(SM_LDAP_SINGLEMATCH,
1075 /* only wanted one match */
1076 SM_LDAP_ERROR_CLEANUP();
1083 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
1085 *result = sm_rpool_strdup_x(rpool,
1091 if (vals[0] == NULL)
1093 ldap_value_free(vals);
1098 vsize = strlen(vals[0]) + 1;
1099 if (lmap->ldap_attrsep != '\0')
1100 vsize += strlen(attr) + 1;
1101 *result = sm_rpool_malloc_x(rpool,
1103 if (lmap->ldap_attrsep != '\0')
1104 sm_snprintf(*result, vsize,
1110 sm_strlcpy(*result, vals[0],
1112 ldap_value_free(vals);
1117 /* attributes only */
1118 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
1120 if (*result == NULL)
1121 *result = sm_rpool_strdup_x(rpool,
1125 if (bitset(SM_LDAP_SINGLEMATCH,
1129 /* only wanted one match */
1130 SM_LDAP_ERROR_CLEANUP();
1135 vsize = strlen(*result) +
1137 tmp = sm_rpool_malloc_x(rpool,
1139 (void) sm_snprintf(tmp,
1141 *result, (char) delim,
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
1155 ** This is not an error.
1158 if (type == SM_LDAP_ATTR_NORMAL &&
1159 bitset(SM_LDAP_SINGLEMATCH, flags) &&
1162 /* only wanted one match */
1163 SM_LDAP_ERROR_CLEANUP();
1169 for (i = 0; vals[i] != NULL; i++)
1171 if (type == SM_LDAP_ATTR_DN ||
1172 type == SM_LDAP_ATTR_FILTER ||
1173 type == SM_LDAP_ATTR_URL)
1175 /* add to recursion */
1176 if (sm_ldap_add_recurse(&recurse,
1181 SM_LDAP_ERROR_CLEANUP();
1188 vsize += strlen(vals[i]) + 1;
1189 if (lmap->ldap_attrsep != '\0')
1190 vsize += strlen(attr) + 1;
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.
1200 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
1204 /* Grow result string if needed */
1205 if ((*resultln + vsize) >= *resultsz)
1207 while ((*resultln + vsize) >= *resultsz)
1215 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
1218 if (*result != NULL)
1225 p = *result + *resultln;
1226 pe = *result + *resultsz;
1228 for (i = 0; vals[i] != NULL; i++)
1230 if (*resultln > 0 &&
1232 *p++ = (char) delim;
1234 if (lmap->ldap_attrsep != '\0')
1236 p += sm_strlcpy(p, attr,
1239 *p++ = lmap->ldap_attrsep;
1242 p += sm_strlcpy(p, vals[i],
1244 *resultln = p - (*result);
1247 /* Internal error: buffer too small for LDAP values */
1248 SM_LDAP_ERROR_CLEANUP();
1255 ldap_value_free(vals);
1258 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
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
1269 if (save_errno != LDAP_SUCCESS &&
1270 save_errno != LDAP_DECODING_ERROR)
1272 /* Must be an error */
1273 save_errno += E_LDAPBASE;
1274 SM_LDAP_ERROR_CLEANUP();
1279 /* mark this DN as done */
1281 if (rl->lr_ludp != NULL)
1283 ldap_free_urldesc(rl->lr_ludp);
1286 if (rl->lr_attrs != NULL)
1289 rl->lr_attrs = NULL;
1292 /* We don't want multiple values and we have one */
1293 if ((char) delim == '\0' &&
1294 !bitset(SM_LDAP_SINGLEMATCH, flags) &&
1298 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1299 if (save_errno != LDAP_SUCCESS &&
1300 save_errno != LDAP_DECODING_ERROR)
1302 /* Must be an error */
1303 save_errno += E_LDAPBASE;
1304 SM_LDAP_ERROR_CLEANUP();
1308 ldap_msgfree(lmap->ldap_res);
1309 lmap->ldap_res = NULL;
1313 save_errno = ETIMEDOUT;
1314 else if (ret == LDAP_RES_SEARCH_RESULT)
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.
1323 if (lmap->ldap_res == NULL)
1324 save_errno = LDAP_UNAVAILABLE;
1329 save_errno = ldap_parse_result(lmap->ldap_ld,
1330 lmap->ldap_res, &rc, NULL, NULL,
1332 if (save_errno == LDAP_SUCCESS)
1337 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1338 if (save_errno != LDAP_SUCCESS)
1340 statp = EX_TEMPFAIL;
1343 # ifdef LDAP_SERVER_DOWN
1344 case LDAP_SERVER_DOWN:
1348 case LDAP_UNAVAILABLE:
1351 ** server disappeared,
1352 ** try reopen on next search
1359 save_errno += E_LDAPBASE;
1360 SM_LDAP_ERROR_CLEANUP();
1365 if (lmap->ldap_res != NULL)
1367 ldap_msgfree(lmap->ldap_res);
1368 lmap->ldap_res = NULL;
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.
1384 for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1391 rl = recurse->lrl_data[rlidx];
1396 /* already expanded */
1400 if (rl->lr_type == SM_LDAP_ATTR_DN)
1403 sid = ldap_search(lmap->ldap_ld,
1407 (lmap->ldap_attr[0] == NULL ?
1408 NULL : lmap->ldap_attr),
1409 lmap->ldap_attrsonly);
1411 else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1414 sid = ldap_search(lmap->ldap_ld,
1418 (lmap->ldap_attr[0] == NULL ?
1419 NULL : lmap->ldap_attr),
1420 lmap->ldap_attrsonly);
1422 else if (rl->lr_type == SM_LDAP_ATTR_URL)
1425 sid = ldap_url_parse(rl->lr_search,
1430 errno = sid + E_LDAPURLBASE;
1434 /* We need to add objectClass */
1435 if (rl->lr_ludp->lud_attrs != NULL)
1439 while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1441 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1442 "objectClass") == 0)
1444 /* already requested */
1455 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1456 if (rl->lr_attrs == NULL)
1459 ldap_free_urldesc(rl->lr_ludp);
1463 for (i = 0 ; i < attrnum; i++)
1465 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1467 rl->lr_attrs[i++] = "objectClass";
1468 rl->lr_attrs[i++] = NULL;
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.
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,
1486 lmap->ldap_attrsonly);
1488 /* Use the attributes specified by URL */
1489 newflags |= SM_LDAP_USE_ALLATTR;
1493 /* unknown or illegal attribute type */
1498 /* Collect results */
1501 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1502 statp = EX_TEMPFAIL;
1505 # ifdef LDAP_SERVER_DOWN
1506 case LDAP_SERVER_DOWN:
1510 case LDAP_UNAVAILABLE:
1513 ** server disappeared,
1514 ** try reopen on next search
1520 errno = save_errno + E_LDAPBASE;
1524 status = sm_ldap_results(lmap, sid, newflags, delim,
1525 rpool, result, resultln,
1528 if (status != EX_OK && status != EX_NOTFOUND)
1536 if (rl->lr_ludp != NULL)
1538 ldap_free_urldesc(rl->lr_ludp);
1541 if (rl->lr_attrs != NULL)
1544 rl->lr_attrs = NULL;
1547 /* Reset rlidx as new items may have been added */
1555 ** SM_LDAP_CLOSE -- close LDAP connection
1558 ** lmap -- LDAP map information
1567 SM_LDAP_STRUCT *lmap;
1569 if (lmap->ldap_ld == NULL)
1572 if (lmap->ldap_pid == getpid())
1573 ldap_unbind(lmap->ldap_ld);
1574 lmap->ldap_ld = NULL;
1578 ** SM_LDAP_GETERRNO -- get ldap errno value
1581 ** ld -- LDAP session handle
1589 sm_ldap_geterrno(ld)
1592 int err = LDAP_SUCCESS;
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
1598 # define LDAP_GET_RESULT_CODE LDAP_OPT_ERROR_NUMBER
1600 (void) ldap_get_option(ld, LDAP_GET_RESULT_CODE, &err);
1602 # ifdef LDAP_OPT_SIZELIMIT
1603 err = ldap_get_lderrno(ld, NULL, NULL);
1608 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1609 ** OpenLDAP 1.X's hack (see above)
1612 ld->ld_errno = LDAP_SUCCESS;
1613 # endif /* LDAP_OPT_SIZELIMIT */
1614 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1617 #endif /* LDAPMAP */