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