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
sm_ldap_clear(lmap)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
ldap_debug_cb(msg)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
sm_ldap_setoptsg(lmap)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
sm_ldap_setopts(ld,lmap)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
ldaptimeout(unused)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
sm_ldap_start(name,lmap)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
sm_ldap_search_m(lmap,argv)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
sm_ldap_search(lmap,key)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
sm_ldap_has_objectclass(lmap,entry,ocvalue)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 *
sm_ldap_add_recurse(top,item,type,rpool)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
sm_ldap_results(lmap,msgid,flags,delim,rpool,result,resultln,resultsz,recurse)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
sm_ldap_close(lmap)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
sm_ldap_geterrno(ld)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 *
sm_ldap_geterror(ld)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 */
1704