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