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