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