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