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 *));
39 static char *sm_ldap_geterror __P((LDAP *));
42 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
45 ** lmap -- pointer to SM_LDAP_STRUCT to clear
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"
56 # if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
57 # error "_FFR_LDAP_VERSION < LDAP_VERSION_MAX"
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 */
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;
81 lmap->ldap_options = 0;
83 lmap->ldap_attrsep = '\0';
84 # if LDAP_NETWORK_TIMEOUT
85 lmap->ldap_networktmo = 0;
87 lmap->ldap_binddn = NULL;
88 lmap->ldap_secret = NULL;
89 lmap->ldap_method = LDAP_AUTH_SIMPLE;
90 lmap->ldap_base = NULL;
91 lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
92 lmap->ldap_attrsonly = LDAPMAP_FALSE;
93 lmap->ldap_timeout.tv_sec = 0;
94 lmap->ldap_timeout.tv_usec = 0;
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;
103 lmap->ldap_multi_args = false;
106 # if _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN)
107 static void ldap_debug_cb __P((const char *msg));
113 if (sm_debug_active(&SmLDAPTrace, 4))
114 sm_dprintf("%s", msg);
116 # endif /* _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN) */
119 # if LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
120 # define SET_LDAP_TMO(ld, lmap) \
123 if (lmap->ldap_networktmo > 0) \
125 struct timeval tmo; \
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; \
132 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo); \
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) */
140 ** SM_LDAP_SETOPTSG -- set some (global) LDAP options
143 ** lmap -- LDAP map information
150 # if _FFR_SM_LDAP_DBG
151 static bool dbg_init = false;
153 # if SM_CONF_LDAP_INITIALIZE
154 static void sm_ldap_setoptsg __P((SM_LDAP_STRUCT *lmap));
156 sm_ldap_setoptsg(lmap)
157 SM_LDAP_STRUCT *lmap;
159 # if USE_LDAP_SET_OPTION
161 SET_LDAP_TMO(NULL, lmap);
163 # if _FFR_SM_LDAP_DBG
164 if (!dbg_init && sm_debug_active(&SmLDAPTrace, 1) &&
165 lmap->ldap_debug != 0)
168 # if defined(LBER_OPT_LOG_PRINT_FN)
169 r = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, ldap_debug_cb);
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);
183 # endif /* _FFR_SM_LDAP_DBG */
184 # endif /* USE_LDAP_SET_OPTION */
186 # endif /* SM_CONF_LDAP_INITIALIZE */
189 ** SM_LDAP_SETOPTS -- set LDAP options
192 ** ld -- LDAP session handle
193 ** lmap -- LDAP map information
201 sm_ldap_setopts(ld, lmap)
203 SM_LDAP_STRUCT *lmap;
205 # if USE_LDAP_SET_OPTION
206 if (lmap->ldap_version != 0)
208 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
209 &lmap->ldap_version);
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);
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)
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);
233 # endif /* _FFR_SM_LDAP_DBG */
234 # ifdef LDAP_OPT_RESTART
235 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
238 if (sm_debug_active(&SmLDAPTrace, 101))
243 cert = getcwd(buf, sizeof(buf));
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);
253 # endif /* _FFR_TESTS */
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 */
265 ** SM_LDAP_START -- actually connect to an LDAP server
268 ** name -- name of map for debug output.
269 ** lmap -- the LDAP map being opened.
272 ** true if connection is successful, false otherwise.
275 ** Populates lmap->ldap_ld.
278 # if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
279 static jmp_buf LDAPTimeout;
280 static void ldaptimeout __P((int));
288 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
289 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
294 longjmp(LDAPTimeout, 1);
298 #define SM_LDAP_SETTIMEOUT(to, where) \
303 if (setjmp(LDAPTimeout) != 0) \
305 if (sm_debug_active(&SmLDAPTrace, 9)) \
306 sm_dprintf("ldap_settimeout(%s)=triggered\n",\
311 ev = sm_setevent(to, ldaptimeout, 0); \
315 #define SM_LDAP_CLEARTIMEOUT() \
321 # endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
324 sm_ldap_start(name, lmap)
326 SM_LDAP_STRUCT *lmap;
331 # if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
339 if (sm_debug_active(&SmLDAPTrace, 2))
340 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
342 if (lmap->ldap_host != NULL)
343 id = lmap->ldap_host;
344 else if (lmap->ldap_uri != NULL)
349 if (sm_debug_active(&SmLDAPTrace, 9))
351 /* Don't print a port number for LDAP URIs */
352 if (lmap->ldap_uri != NULL)
353 sm_dprintf("ldapmap_start(%s)\n", id);
355 sm_dprintf("ldapmap_start(%s, %d)\n", id,
359 if (lmap->ldap_uri != NULL)
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);
370 # else /* SM_CONF_LDAP_INITIALIZE */
371 LDAPURLDesc *ludp = NULL;
373 /* Blast apart URL and use the ldap_init/ldap_open below */
374 err = ldap_url_parse(lmap->ldap_uri, &ludp);
377 errno = err + E_LDAPURLBASE;
380 lmap->ldap_host = sm_strdup_x(ludp->lud_host);
381 if (lmap->ldap_host == NULL)
384 ldap_free_urldesc(ludp);
388 lmap->ldap_port = ludp->lud_port;
389 ldap_free_urldesc(ludp);
390 # endif /* SM_CONF_LDAP_INITIALIZE */
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);
401 # else /* USE_LDAP_INIT */
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.
408 if (sm_debug_active(&SmLDAPTrace, 9))
409 sm_dprintf("ldap_open(%s, %d)\n", lmap->ldap_host, lmap->ldap_port);
411 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_open");
412 ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
415 /* clear the event if it has not sprung */
416 SM_LDAP_CLEARTIMEOUT();
417 # endif /* USE_LDAP_INIT */
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);
428 sm_ldap_setopts(ld, lmap);
429 # if USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT
431 ** If using ldap_init(), the actual connection to the server
432 ** happens at ldap_bind_s() so we need the timeout here.
435 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_bind");
436 # endif /* USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT */
438 # ifdef LDAP_AUTH_KRBV4
439 if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
440 lmap->ldap_secret != NULL)
443 ** Need to put ticket in environment here instead of
444 ** during parseargs as there may be different tickets
445 ** for different LDAP connections.
448 (void) putenv(lmap->ldap_secret);
450 # endif /* LDAP_AUTH_KRBV4 */
452 # if LDAP_NETWORK_TIMEOUT
453 tmo.tv_sec = lmap->ldap_networktmo;
455 tmo.tv_sec = lmap->ldap_timeout.tv_sec;
459 if (sm_debug_active(&SmLDAPTrace, 9))
460 sm_dprintf("ldap_bind(%s)\n", lmap->ldap_uri);
462 msgid = ldap_bind(ld, lmap->ldap_binddn, lmap->ldap_secret,
465 if (sm_debug_active(&SmLDAPTrace, 9))
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);
473 ldap_memfree(errmsg);
480 err = sm_ldap_geterrno(ld);
481 if (LDAP_SUCCESS != err)
482 save_errno = err + E_LDAPBASE;
487 r = ldap_result(ld, msgid, LDAP_MSG_ALL,
488 tmo.tv_sec == 0 ? NULL : &(tmo), &(lmap->ldap_res));
490 if (sm_debug_active(&SmLDAPTrace, 9))
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);
498 ldap_memfree(errmsg);
504 err = sm_ldap_geterrno(ld);
505 if (LDAP_SUCCESS != err)
506 save_errno = err + E_LDAPBASE;
511 save_errno = ETIMEDOUT;
515 r = ldap_parse_result(ld, lmap->ldap_res, &err, NULL, &errmsg, NULL,
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)
523 if (err != LDAP_SUCCESS)
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 */
536 if (r != LDAP_SUCCESS)
542 errno = r + E_LDAPBASE;
545 ldap_memfree(errmsg);
551 /* Save PID to make sure only this PID closes the LDAP connection */
552 lmap->ldap_pid = getpid();
556 ldap_memfree(errmsg);
563 ** SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
565 ** Initiate an LDAP search, return the msgid.
566 ** The calling function must collect the results.
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
575 ** <0 on failure (SM_LDAP_ERR*), msgid on success
580 sm_ldap_search_m(lmap, argv)
581 SM_LDAP_STRUCT *lmap;
586 char filter[LDAPMAP_MAX_FILTER + 1];
588 SM_REQUIRE(lmap != NULL);
589 SM_REQUIRE(argv != NULL);
590 SM_REQUIRE(argv[0] != NULL);
592 memset(filter, '\0', sizeof filter);
594 p = lmap->ldap_filter;
595 while ((q = strchr(p, '%')) != NULL)
599 if (lmap->ldap_multi_args)
601 # if SM_LDAP_ARGS < 10
602 # error _SM_LDAP_ARGS must be 10
606 else if (q[1] >= '0' && q[1] <= '9')
608 key = argv[q[1] - '0'];
611 # if SM_LDAP_ERROR_ON_MISSING_ARGS
612 return SM_LDAP_ERR_ARG_MISS;
626 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
627 "%.*s%s", (int) (q - p), p, key);
631 else if (q[1] == '0' ||
632 (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
636 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
637 "%.*s", (int) (q - p), p);
641 /* Properly escape LDAP special characters */
642 while (SPACELEFT(filter, fp) > 0 &&
645 if (*k == '*' || *k == '(' ||
646 *k == ')' || *k == '\\')
648 (void) sm_strlcat(fp,
649 (*k == '*' ? "\\2A" :
650 (*k == '(' ? "\\28" :
651 (*k == ')' ? "\\29" :
652 (*k == '\\' ? "\\5C" :
654 SPACELEFT(filter, fp));
664 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
665 "%.*s", (int) (q - p + 1), p);
666 p = q + (q[1] == '%' ? 2 : 1);
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);
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 :
679 lmap->ldap_attrsonly);
684 ** SM_LDAP_SEARCH -- initiate LDAP search
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()
691 ** lmap -- LDAP map information
692 ** key -- key to substitute in LDAP filter
695 ** <0 on failure, msgid on success
700 sm_ldap_search(lmap, key)
701 SM_LDAP_STRUCT *lmap;
704 char *argv[SM_LDAP_ARGS];
706 memset(argv, '\0', sizeof argv);
708 return sm_ldap_search_m(lmap, argv);
712 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
713 ** particular objectClass
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
724 ** true if item has that objectClass
728 sm_ldap_has_objectclass(lmap, entry, ocvalue)
729 SM_LDAP_STRUCT *lmap;
739 vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
743 for (i = 0; vals[i] != NULL; i++)
751 while (*p != '\0' && *p != '|')
754 if ((p - q) == strlen(vals[i]) &&
755 sm_strncasecmp(vals[i], q, p - q) == 0)
757 ldap_value_free(vals);
767 ldap_value_free(vals);
772 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
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
787 # define SM_LDAP_ERROR_CLEANUP() \
789 if (lmap->ldap_res != NULL) \
791 ldap_msgfree(lmap->ldap_res); \
792 lmap->ldap_res = NULL; \
794 (void) ldap_abandon(lmap->ldap_ld, msgid); \
797 static SM_LDAP_RECURSE_ENTRY *
798 sm_ldap_add_recurse(top, item, type, rpool)
799 SM_LDAP_RECURSE_LIST **top;
811 SM_LDAP_RECURSE_ENTRY *newe;
812 SM_LDAP_RECURSE_ENTRY **olddata;
815 ** This code will maintain a list of
816 ** SM_LDAP_RECURSE_ENTRY structures
817 ** in ascending order.
822 /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
823 *top = sm_rpool_malloc_x(rpool, sizeof **top);
825 (*top)->lrl_size = 0;
826 (*top)->lrl_data = NULL;
829 if ((*top)->lrl_cnt >= (*top)->lrl_size)
831 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
832 olddata = (*top)->lrl_data;
833 if ((*top)->lrl_size == 0)
836 (*top)->lrl_size = 256;
840 oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
841 (*top)->lrl_size *= 2;
843 (*top)->lrl_data = sm_rpool_malloc_x(rpool,
844 (*top)->lrl_size * sizeof *((*top)->lrl_data));
846 memcpy((*top)->lrl_data, olddata, oldsizeb);
850 ** Binary search/insert item:type into list.
851 ** Return current entry pointer if already exists.
855 m = (*top)->lrl_cnt - 1;
861 while (insertat == -1)
865 rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
867 rc = type - (*top)->lrl_data[p]->lr_type;
874 return (*top)->lrl_data[p];
878 else if (n >= (*top)->lrl_cnt)
879 insertat = (*top)->lrl_cnt;
885 ** Not found in list, make room
886 ** at insert point and add it.
889 newe = sm_rpool_malloc_x(rpool, sizeof *newe);
892 moveb = ((*top)->lrl_cnt - insertat) * sizeof *((*top)->lrl_data);
894 memmove(&((*top)->lrl_data[insertat + 1]),
895 &((*top)->lrl_data[insertat]),
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;
904 ((*top)->lrl_data)[insertat] = newe;
911 sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
912 resultln, resultsz, recurse)
913 SM_LDAP_STRUCT *lmap;
921 SM_LDAP_RECURSE_LIST *recurse;
930 SM_LDAP_RECURSE_ENTRY *rl;
932 /* Are we the top top level of the search? */
933 toplevel = (recurse == NULL);
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)
944 /* If we don't want multiple values and we have one, break */
945 if ((char) delim == '\0' &&
946 !bitset(SM_LDAP_SINGLEMATCH, flags) &&
950 /* Cycle through all entries */
951 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
953 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
961 ** If matching only and found an entry,
962 ** no need to spin through attributes
965 if (bitset(SM_LDAP_MATCHONLY, flags))
971 # if _FFR_LDAP_SINGLEDN
972 if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
974 /* only wanted one match */
975 SM_LDAP_ERROR_CLEANUP();
979 # endif /* _FFR_LDAP_SINGLEDN */
981 /* record completed DN's to prevent loops */
982 dn = ldap_get_dn(lmap->ldap_ld, entry);
985 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
986 save_errno += E_LDAPBASE;
987 SM_LDAP_ERROR_CLEANUP();
992 rl = sm_ldap_add_recurse(&recurse, dn,
999 SM_LDAP_ERROR_CLEANUP();
1003 else if (rl->lr_done)
1005 /* already on list, skip it */
1011 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
1013 ** Reset value to prevent lingering
1014 ** LDAP_DECODING_ERROR due to
1015 ** OpenLDAP 1.X's hack (see below)
1018 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
1019 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
1021 for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
1024 attr = ldap_next_attribute(lmap->ldap_ld, entry,
1029 char *needobjclass = NULL;
1031 type = SM_LDAP_ATTR_NONE;
1032 for (i = 0; lmap->ldap_attr[i] != NULL; i++)
1034 if (SM_STRCASEEQ(lmap->ldap_attr[i],
1037 type = lmap->ldap_attr_type[i];
1038 needobjclass = lmap->ldap_attr_needobjclass[i];
1043 if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
1044 type == SM_LDAP_ATTR_NONE)
1046 /* URL lookups specify attrs to use */
1047 type = SM_LDAP_ATTR_NORMAL;
1048 needobjclass = NULL;
1051 if (type == SM_LDAP_ATTR_NONE)
1053 /* attribute not requested */
1055 SM_LDAP_ERROR_CLEANUP();
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.
1068 if (type == SM_LDAP_ATTR_OBJCLASS ||
1069 (needobjclass != NULL &&
1070 !sm_ldap_has_objectclass(lmap, entry,
1077 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
1079 vals = ldap_get_values(lmap->ldap_ld,
1084 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1085 if (save_errno == LDAP_SUCCESS)
1091 /* Must be an error */
1092 save_errno += E_LDAPBASE;
1094 SM_LDAP_ERROR_CLEANUP();
1102 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
1104 ** Reset value to prevent lingering
1105 ** LDAP_DECODING_ERROR due to
1106 ** OpenLDAP 1.X's hack (see below)
1109 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
1110 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
1113 ** If matching only,
1114 ** no need to spin through entries
1117 if (bitset(SM_LDAP_MATCHONLY, flags))
1119 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
1120 ldap_value_free(vals);
1126 ** If we don't want multiple values,
1127 ** return first found.
1130 if ((char) delim == '\0')
1132 if (*result != NULL)
1134 /* already have a value */
1135 if (bitset(SM_LDAP_SINGLEMATCH,
1138 /* only wanted one match */
1139 SM_LDAP_ERROR_CLEANUP();
1146 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
1148 *result = sm_rpool_strdup_x(rpool,
1154 if (vals[0] == NULL)
1156 ldap_value_free(vals);
1161 vsize = strlen(vals[0]) + 1;
1162 if (lmap->ldap_attrsep != '\0')
1163 vsize += strlen(attr) + 1;
1164 *result = sm_rpool_malloc_x(rpool,
1166 if (lmap->ldap_attrsep != '\0')
1167 sm_snprintf(*result, vsize,
1173 sm_strlcpy(*result, vals[0],
1175 ldap_value_free(vals);
1180 /* attributes only */
1181 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
1183 if (*result == NULL)
1184 *result = sm_rpool_strdup_x(rpool,
1188 if (bitset(SM_LDAP_SINGLEMATCH,
1192 /* only wanted one match */
1193 SM_LDAP_ERROR_CLEANUP();
1198 vsize = strlen(*result) +
1200 tmp = sm_rpool_malloc_x(rpool,
1202 (void) sm_snprintf(tmp,
1204 *result, (char) delim,
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
1218 ** This is not an error.
1221 if (type == SM_LDAP_ATTR_NORMAL &&
1222 bitset(SM_LDAP_SINGLEMATCH, flags) &&
1225 /* only wanted one match */
1226 SM_LDAP_ERROR_CLEANUP();
1232 for (i = 0; vals[i] != NULL; i++)
1234 if (type == SM_LDAP_ATTR_DN ||
1235 type == SM_LDAP_ATTR_FILTER ||
1236 type == SM_LDAP_ATTR_URL)
1238 /* add to recursion */
1239 if (sm_ldap_add_recurse(&recurse,
1244 SM_LDAP_ERROR_CLEANUP();
1251 vsize += strlen(vals[i]) + 1;
1252 if (lmap->ldap_attrsep != '\0')
1253 vsize += strlen(attr) + 1;
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.
1263 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
1267 /* Grow result string if needed */
1268 if ((*resultln + vsize) >= *resultsz)
1270 while ((*resultln + vsize) >= *resultsz)
1278 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
1281 if (*result != NULL)
1288 p = *result + *resultln;
1289 pe = *result + *resultsz;
1291 for (i = 0; vals[i] != NULL; i++)
1293 if (*resultln > 0 &&
1295 *p++ = (char) delim;
1297 if (lmap->ldap_attrsep != '\0')
1299 p += sm_strlcpy(p, attr,
1302 *p++ = lmap->ldap_attrsep;
1305 p += sm_strlcpy(p, vals[i],
1307 *resultln = p - (*result);
1310 /* Internal error: buffer too small for LDAP values */
1311 SM_LDAP_ERROR_CLEANUP();
1318 ldap_value_free(vals);
1321 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
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
1332 if (save_errno != LDAP_SUCCESS &&
1333 save_errno != LDAP_DECODING_ERROR)
1335 /* Must be an error */
1336 save_errno += E_LDAPBASE;
1337 SM_LDAP_ERROR_CLEANUP();
1342 /* mark this DN as done */
1344 if (rl->lr_ludp != NULL)
1346 ldap_free_urldesc(rl->lr_ludp);
1349 if (rl->lr_attrs != NULL)
1352 rl->lr_attrs = NULL;
1355 /* We don't want multiple values and we have one */
1356 if ((char) delim == '\0' &&
1357 !bitset(SM_LDAP_SINGLEMATCH, flags) &&
1361 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1362 if (save_errno != LDAP_SUCCESS &&
1363 save_errno != LDAP_DECODING_ERROR)
1365 /* Must be an error */
1366 save_errno += E_LDAPBASE;
1367 SM_LDAP_ERROR_CLEANUP();
1371 ldap_msgfree(lmap->ldap_res);
1372 lmap->ldap_res = NULL;
1376 save_errno = ETIMEDOUT;
1377 else if (ret == LDAP_RES_SEARCH_RESULT)
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.
1386 if (lmap->ldap_res == NULL)
1387 save_errno = LDAP_UNAVAILABLE;
1392 save_errno = ldap_parse_result(lmap->ldap_ld,
1393 lmap->ldap_res, &rc, NULL, NULL,
1395 if (save_errno == LDAP_SUCCESS)
1400 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1401 if (save_errno != LDAP_SUCCESS)
1403 statp = EX_TEMPFAIL;
1406 # ifdef LDAP_SERVER_DOWN
1407 case LDAP_SERVER_DOWN:
1411 case LDAP_UNAVAILABLE:
1414 ** server disappeared,
1415 ** try reopen on next search
1422 save_errno += E_LDAPBASE;
1423 SM_LDAP_ERROR_CLEANUP();
1428 if (lmap->ldap_res != NULL)
1430 ldap_msgfree(lmap->ldap_res);
1431 lmap->ldap_res = NULL;
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.
1447 for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1454 rl = recurse->lrl_data[rlidx];
1459 /* already expanded */
1463 if (rl->lr_type == SM_LDAP_ATTR_DN)
1466 sid = ldap_search(lmap->ldap_ld,
1470 (lmap->ldap_attr[0] == NULL ?
1471 NULL : lmap->ldap_attr),
1472 lmap->ldap_attrsonly);
1474 else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1477 sid = ldap_search(lmap->ldap_ld,
1481 (lmap->ldap_attr[0] == NULL ?
1482 NULL : lmap->ldap_attr),
1483 lmap->ldap_attrsonly);
1485 else if (rl->lr_type == SM_LDAP_ATTR_URL)
1488 sid = ldap_url_parse(rl->lr_search,
1493 errno = sid + E_LDAPURLBASE;
1497 /* We need to add objectClass */
1498 if (rl->lr_ludp->lud_attrs != NULL)
1502 while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1504 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1505 "objectClass") == 0)
1507 /* already requested */
1518 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1519 if (rl->lr_attrs == NULL)
1522 ldap_free_urldesc(rl->lr_ludp);
1526 for (i = 0 ; i < attrnum; i++)
1528 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1530 rl->lr_attrs[i++] = "objectClass";
1531 rl->lr_attrs[i++] = NULL;
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.
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,
1549 lmap->ldap_attrsonly);
1551 /* Use the attributes specified by URL */
1552 newflags |= SM_LDAP_USE_ALLATTR;
1556 /* unknown or illegal attribute type */
1561 /* Collect results */
1564 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1565 statp = EX_TEMPFAIL;
1568 # ifdef LDAP_SERVER_DOWN
1569 case LDAP_SERVER_DOWN:
1573 case LDAP_UNAVAILABLE:
1576 ** server disappeared,
1577 ** try reopen on next search
1583 errno = save_errno + E_LDAPBASE;
1587 status = sm_ldap_results(lmap, sid, newflags, delim,
1588 rpool, result, resultln,
1591 if (status != EX_OK && status != EX_NOTFOUND)
1599 if (rl->lr_ludp != NULL)
1601 ldap_free_urldesc(rl->lr_ludp);
1604 if (rl->lr_attrs != NULL)
1607 rl->lr_attrs = NULL;
1610 /* Reset rlidx as new items may have been added */
1618 ** SM_LDAP_CLOSE -- close LDAP connection
1621 ** lmap -- LDAP map information
1629 SM_LDAP_STRUCT *lmap;
1631 if (lmap->ldap_ld == NULL)
1634 if (lmap->ldap_pid == getpid())
1635 ldap_unbind(lmap->ldap_ld);
1636 lmap->ldap_ld = NULL;
1641 ** SM_LDAP_GETERRNO -- get ldap errno value
1644 ** ld -- LDAP session handle
1651 sm_ldap_geterrno(ld)
1654 int err = LDAP_SUCCESS;
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
1660 # define LDAP_GET_RESULT_CODE LDAP_OPT_ERROR_NUMBER
1662 (void) ldap_get_option(ld, LDAP_GET_RESULT_CODE, &err);
1664 # ifdef LDAP_OPT_SIZELIMIT
1665 err = ldap_get_lderrno(ld, NULL, NULL);
1670 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1671 ** OpenLDAP 1.X's hack (see above)
1674 ld->ld_errno = LDAP_SUCCESS;
1675 # endif /* LDAP_OPT_SIZELIMIT */
1676 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1681 ** SM_LDAP_GETERROR -- get ldap error value
1684 ** ld -- LDAP session handle
1691 sm_ldap_geterror(ld)
1696 # if defined(LDAP_OPT_DIAGNOSTIC_MESSAGE)
1697 (void) ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error);
1703 #endif /* LDAPMAP */