Update zoneinfo database.
[dragonfly/netmp.git] / contrib / sendmail / libsm / ldap.c
blobb022b39e4f1270b94a7897ca87a01a48cfb9b7e8
1 /*
2 * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
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 */
10 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: ldap.c,v 1.44.2.3 2003/07/07 20:16:16 gshapiro Exp $")
13 #if LDAPMAP
14 # include <sys/types.h>
15 # include <errno.h>
16 # include <setjmp.h>
17 # include <stdlib.h>
18 # include <unistd.h>
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>
32 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
33 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
35 static void ldaptimeout __P((int));
38 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
40 ** Parameters:
41 ** lmap -- pointer to SM_LDAP_STRUCT to clear
43 ** Returns:
44 ** None.
48 void
49 sm_ldap_clear(lmap)
50 SM_LDAP_STRUCT *lmap;
52 if (lmap == NULL)
53 return;
55 lmap->ldap_target = NULL;
56 lmap->ldap_port = LDAP_PORT;
57 #if _FFR_LDAP_URI
58 lmap->ldap_uri = false;
59 #endif /* _FFR_LDAP_URI */
60 # if _FFR_LDAP_SETVERSION
61 lmap->ldap_version = 0;
62 # endif /* _FFR_LDAP_SETVERSION */
63 lmap->ldap_deref = LDAP_DEREF_NEVER;
64 lmap->ldap_timelimit = LDAP_NO_LIMIT;
65 lmap->ldap_sizelimit = LDAP_NO_LIMIT;
66 # ifdef LDAP_REFERRALS
67 lmap->ldap_options = LDAP_OPT_REFERRALS;
68 # else /* LDAP_REFERRALS */
69 lmap->ldap_options = 0;
70 # endif /* LDAP_REFERRALS */
71 lmap->ldap_attrsep = '\0';
72 lmap->ldap_binddn = NULL;
73 lmap->ldap_secret = NULL;
74 lmap->ldap_method = LDAP_AUTH_SIMPLE;
75 lmap->ldap_base = NULL;
76 lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
77 lmap->ldap_attrsonly = LDAPMAP_FALSE;
78 lmap->ldap_timeout.tv_sec = 0;
79 lmap->ldap_timeout.tv_usec = 0;
80 lmap->ldap_ld = NULL;
81 lmap->ldap_filter = NULL;
82 lmap->ldap_attr[0] = NULL;
83 #if _FFR_LDAP_RECURSION
84 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
85 lmap->ldap_attr_needobjclass[0] = NULL;
86 #endif /* _FFR_LDAP_RECURSION */
87 lmap->ldap_res = NULL;
88 lmap->ldap_next = NULL;
89 lmap->ldap_pid = 0;
93 ** SM_LDAP_START -- actually connect to an LDAP server
95 ** Parameters:
96 ** name -- name of map for debug output.
97 ** lmap -- the LDAP map being opened.
99 ** Returns:
100 ** true if connection is successful, false otherwise.
102 ** Side Effects:
103 ** Populates lmap->ldap_ld.
106 static jmp_buf LDAPTimeout;
108 #define SM_LDAP_SETTIMEOUT(to) \
109 do \
111 if (to != 0) \
113 if (setjmp(LDAPTimeout) != 0) \
115 errno = ETIMEDOUT; \
116 return false; \
118 ev = sm_setevent(to, ldaptimeout, 0); \
120 } while (0)
122 #define SM_LDAP_CLEARTIMEOUT() \
123 do \
125 if (ev != NULL) \
126 sm_clrevent(ev); \
127 } while (0)
129 bool
130 sm_ldap_start(name, lmap)
131 char *name;
132 SM_LDAP_STRUCT *lmap;
134 int bind_result;
135 int save_errno;
136 SM_EVENT *ev = NULL;
137 LDAP *ld;
139 if (sm_debug_active(&SmLDAPTrace, 2))
140 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
142 if (sm_debug_active(&SmLDAPTrace, 9))
143 sm_dprintf("ldapmap_start(%s, %d)\n",
144 lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target,
145 lmap->ldap_port);
147 # if USE_LDAP_INIT
148 # if _FFR_LDAP_URI
149 if (lmap->ldap_uri)
150 errno = ldap_initialize(&ld, lmap->ldap_target);
151 else
152 # endif /* _FFR_LDAP_URI */
153 ld = ldap_init(lmap->ldap_target, lmap->ldap_port);
154 save_errno = errno;
155 # else /* USE_LDAP_INIT */
157 ** If using ldap_open(), the actual connection to the server
158 ** happens now so we need the timeout here. For ldap_init(),
159 ** the connection happens at bind time.
162 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
163 ld = ldap_open(lmap->ldap_target, lmap->ldap_port);
164 save_errno = errno;
166 /* clear the event if it has not sprung */
167 SM_LDAP_CLEARTIMEOUT();
168 # endif /* USE_LDAP_INIT */
170 errno = save_errno;
171 if (ld == NULL)
172 return false;
174 sm_ldap_setopts(ld, lmap);
176 # if USE_LDAP_INIT
178 ** If using ldap_init(), the actual connection to the server
179 ** happens at ldap_bind_s() so we need the timeout here.
182 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
183 # endif /* USE_LDAP_INIT */
185 # ifdef LDAP_AUTH_KRBV4
186 if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
187 lmap->ldap_secret != NULL)
190 ** Need to put ticket in environment here instead of
191 ** during parseargs as there may be different tickets
192 ** for different LDAP connections.
195 (void) putenv(lmap->ldap_secret);
197 # endif /* LDAP_AUTH_KRBV4 */
199 bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
200 lmap->ldap_secret, lmap->ldap_method);
202 # if USE_LDAP_INIT
203 /* clear the event if it has not sprung */
204 SM_LDAP_CLEARTIMEOUT();
205 # endif /* USE_LDAP_INIT */
207 if (bind_result != LDAP_SUCCESS)
209 errno = bind_result + E_LDAPBASE;
210 return false;
213 /* Save PID to make sure only this PID closes the LDAP connection */
214 lmap->ldap_pid = getpid();
215 lmap->ldap_ld = ld;
216 return true;
219 /* ARGSUSED */
220 static void
221 ldaptimeout(unused)
222 int unused;
225 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
226 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
227 ** DOING.
230 errno = ETIMEDOUT;
231 longjmp(LDAPTimeout, 1);
235 ** SM_LDAP_SEARCH -- iniate LDAP search
237 ** Initiate an LDAP search, return the msgid.
238 ** The calling function must collect the results.
240 ** Parameters:
241 ** lmap -- LDAP map information
242 ** key -- key to substitute in LDAP filter
244 ** Returns:
245 ** -1 on failure, msgid on success
250 sm_ldap_search(lmap, key)
251 SM_LDAP_STRUCT *lmap;
252 char *key;
254 int msgid;
255 char *fp, *p, *q;
256 char filter[LDAPMAP_MAX_FILTER + 1];
258 /* substitute key into filter, perhaps multiple times */
259 memset(filter, '\0', sizeof filter);
260 fp = filter;
261 p = lmap->ldap_filter;
262 while ((q = strchr(p, '%')) != NULL)
264 if (q[1] == 's')
266 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
267 "%.*s%s", (int) (q - p), p, key);
268 fp += strlen(fp);
269 p = q + 2;
271 else if (q[1] == '0')
273 char *k = key;
275 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
276 "%.*s", (int) (q - p), p);
277 fp += strlen(fp);
278 p = q + 2;
280 /* Properly escape LDAP special characters */
281 while (SPACELEFT(filter, fp) > 0 &&
282 *k != '\0')
284 if (*k == '*' || *k == '(' ||
285 *k == ')' || *k == '\\')
287 (void) sm_strlcat(fp,
288 (*k == '*' ? "\\2A" :
289 (*k == '(' ? "\\28" :
290 (*k == ')' ? "\\29" :
291 (*k == '\\' ? "\\5C" :
292 "\00")))),
293 SPACELEFT(filter, fp));
294 fp += strlen(fp);
295 k++;
297 else
298 *fp++ = *k++;
301 else
303 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
304 "%.*s", (int) (q - p + 1), p);
305 p = q + (q[1] == '%' ? 2 : 1);
306 fp += strlen(fp);
309 (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
310 if (sm_debug_active(&SmLDAPTrace, 20))
311 sm_dprintf("ldap search filter=%s\n", filter);
313 lmap->ldap_res = NULL;
314 msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
315 lmap->ldap_scope, filter,
316 (lmap->ldap_attr[0] == NULL ? NULL :
317 lmap->ldap_attr),
318 lmap->ldap_attrsonly);
319 return msgid;
322 # if _FFR_LDAP_RECURSION
324 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
325 ** particular objectClass
327 ** Parameters:
328 ** lmap -- pointer to SM_LDAP_STRUCT in use
329 ** entry -- current LDAP entry struct
330 ** ocvalue -- particular objectclass in question.
331 ** may be of form (fee|foo|fum) meaning
332 ** any entry can be part of either fee,
333 ** foo or fum objectclass
335 ** Returns:
336 ** true if item has that objectClass
339 static bool
340 sm_ldap_has_objectclass(lmap, entry, ocvalue)
341 SM_LDAP_STRUCT *lmap;
342 LDAPMessage *entry;
343 char *ocvalue;
345 char **vals = NULL;
346 int i;
348 if (ocvalue == NULL)
349 return false;
351 vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
352 if (vals == NULL)
353 return false;
355 for (i = 0; vals[i] != NULL; i++)
357 char *p;
358 char *q;
360 p = q = ocvalue;
361 while (*p != '\0')
363 while (*p != '\0' && *p != '|')
364 p++;
366 if ((p - q) == strlen(vals[i]) &&
367 sm_strncasecmp(vals[i], q, p - q) == 0)
369 ldap_value_free(vals);
370 return true;
373 while (*p == '|')
374 p++;
375 q = p;
379 ldap_value_free(vals);
380 return false;
384 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
386 ** Parameters:
387 ** lmap -- pointer to SM_LDAP_STRUCT in use
388 ** msgid -- msgid returned by sm_ldap_search()
389 ** flags -- flags for the lookup
390 ** delim -- delimiter for result concatenation
391 ** rpool -- memory pool for storage
392 ** result -- return string
393 ** recurse -- recursion list
395 ** Returns:
396 ** status (sysexit)
399 # define SM_LDAP_ERROR_CLEANUP() \
401 if (lmap->ldap_res != NULL) \
403 ldap_msgfree(lmap->ldap_res); \
404 lmap->ldap_res = NULL; \
406 (void) ldap_abandon(lmap->ldap_ld, msgid); \
409 static SM_LDAP_RECURSE_ENTRY *
410 sm_ldap_add_recurse(top, item, type, rpool)
411 SM_LDAP_RECURSE_LIST **top;
412 char *item;
413 int type;
414 SM_RPOOL_T *rpool;
416 int n;
417 int m;
418 int p;
419 int insertat;
420 int moveb;
421 int oldsizeb;
422 int rc;
423 SM_LDAP_RECURSE_ENTRY *newe;
424 SM_LDAP_RECURSE_ENTRY **olddata;
427 ** This code will maintain a list of
428 ** SM_LDAP_RECURSE_ENTRY structures
429 ** in ascending order.
432 if (*top == NULL)
434 /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
435 *top = sm_rpool_malloc_x(rpool, sizeof **top);
436 (*top)->lr_cnt = 0;
437 (*top)->lr_size = 0;
438 (*top)->lr_data = NULL;
441 if ((*top)->lr_cnt >= (*top)->lr_size)
443 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
444 olddata = (*top)->lr_data;
445 if ((*top)->lr_size == 0)
447 oldsizeb = 0;
448 (*top)->lr_size = 256;
450 else
452 oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data);
453 (*top)->lr_size *= 2;
455 (*top)->lr_data = sm_rpool_malloc_x(rpool,
456 (*top)->lr_size * sizeof *((*top)->lr_data));
457 if (oldsizeb > 0)
458 memcpy((*top)->lr_data, olddata, oldsizeb);
462 ** Binary search/insert item:type into list.
463 ** Return current entry pointer if already exists.
466 n = 0;
467 m = (*top)->lr_cnt - 1;
468 if (m < 0)
469 insertat = 0;
470 else
471 insertat = -1;
473 while (insertat == -1)
475 p = (m + n) / 2;
477 rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search);
478 if (rc == 0)
479 rc = type - (*top)->lr_data[p]->lr_type;
481 if (rc < 0)
482 m = p - 1;
483 else if (rc > 0)
484 n = p + 1;
485 else
486 return (*top)->lr_data[p];
488 if (m == -1)
489 insertat = 0;
490 else if (n >= (*top)->lr_cnt)
491 insertat = (*top)->lr_cnt;
492 else if (m < n)
493 insertat = m + 1;
497 ** Not found in list, make room
498 ** at insert point and add it.
501 newe = sm_rpool_malloc_x(rpool, sizeof *newe);
502 if (newe != NULL)
504 moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data);
505 if (moveb > 0)
506 memmove(&((*top)->lr_data[insertat + 1]),
507 &((*top)->lr_data[insertat]),
508 moveb);
510 newe->lr_search = sm_rpool_strdup_x(rpool, item);
511 newe->lr_type = type;
512 newe->lr_done = false;
514 ((*top)->lr_data)[insertat] = newe;
515 (*top)->lr_cnt++;
517 return newe;
521 sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
522 resultln, resultsz, recurse)
523 SM_LDAP_STRUCT *lmap;
524 int msgid;
525 int flags;
526 int delim;
527 SM_RPOOL_T *rpool;
528 char **result;
529 int *resultln;
530 int *resultsz;
531 SM_LDAP_RECURSE_LIST *recurse;
533 bool toplevel;
534 int i;
535 int statp;
536 int vsize;
537 int ret;
538 int save_errno;
539 char *p;
540 SM_LDAP_RECURSE_ENTRY *rl;
542 /* Are we the top top level of the search? */
543 toplevel = (recurse == NULL);
545 /* Get results */
546 statp = EX_NOTFOUND;
547 while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
548 (lmap->ldap_timeout.tv_sec == 0 ? NULL :
549 &(lmap->ldap_timeout)),
550 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
552 LDAPMessage *entry;
554 /* If we don't want multiple values and we have one, break */
555 if ((char) delim == '\0' && *result != NULL)
556 break;
558 /* Cycle through all entries */
559 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
560 entry != NULL;
561 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
563 BerElement *ber;
564 char *attr;
565 char **vals = NULL;
566 char *dn;
569 ** If matching only and found an entry,
570 ** no need to spin through attributes
573 if (statp == EX_OK &&
574 bitset(SM_LDAP_MATCHONLY, flags))
575 continue;
577 /* record completed DN's to prevent loops */
578 dn = ldap_get_dn(lmap->ldap_ld, entry);
579 if (dn == NULL)
581 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
582 save_errno += E_LDAPBASE;
583 SM_LDAP_ERROR_CLEANUP();
584 errno = save_errno;
585 return EX_TEMPFAIL;
588 rl = sm_ldap_add_recurse(&recurse, dn,
589 SM_LDAP_ATTR_DN,
590 rpool);
592 if (rl == NULL)
594 ldap_memfree(dn);
595 SM_LDAP_ERROR_CLEANUP();
596 errno = ENOMEM;
597 return EX_OSERR;
599 else if (rl->lr_done)
601 /* already on list, skip it */
602 ldap_memfree(dn);
603 continue;
605 ldap_memfree(dn);
607 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
609 ** Reset value to prevent lingering
610 ** LDAP_DECODING_ERROR due to
611 ** OpenLDAP 1.X's hack (see below)
614 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
615 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
617 for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
618 &ber);
619 attr != NULL;
620 attr = ldap_next_attribute(lmap->ldap_ld, entry,
621 ber))
623 char *tmp, *vp_tmp;
624 int type;
625 char *needobjclass = NULL;
627 type = SM_LDAP_ATTR_NONE;
628 for (i = 0; lmap->ldap_attr[i] != NULL; i++)
630 if (sm_strcasecmp(lmap->ldap_attr[i],
631 attr) == 0)
633 type = lmap->ldap_attr_type[i];
634 needobjclass = lmap->ldap_attr_needobjclass[i];
635 break;
639 if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
640 type == SM_LDAP_ATTR_NONE)
642 /* URL lookups specify attrs to use */
643 type = SM_LDAP_ATTR_NORMAL;
644 needobjclass = NULL;
647 if (type == SM_LDAP_ATTR_NONE)
649 /* attribute not requested */
650 ldap_memfree(attr);
651 SM_LDAP_ERROR_CLEANUP();
652 errno = EFAULT;
653 return EX_SOFTWARE;
657 ** For recursion on a particular attribute,
658 ** we may need to see if this entry is
659 ** part of a particular objectclass.
660 ** Also, ignore objectClass attribute.
661 ** Otherwise we just ignore this attribute.
664 if (type == SM_LDAP_ATTR_OBJCLASS ||
665 (needobjclass != NULL &&
666 !sm_ldap_has_objectclass(lmap, entry,
667 needobjclass)))
669 ldap_memfree(attr);
670 continue;
673 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
675 vals = ldap_get_values(lmap->ldap_ld,
676 entry,
677 attr);
678 if (vals == NULL)
680 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
681 if (save_errno == LDAP_SUCCESS)
683 ldap_memfree(attr);
684 continue;
687 /* Must be an error */
688 save_errno += E_LDAPBASE;
689 ldap_memfree(attr);
690 SM_LDAP_ERROR_CLEANUP();
691 errno = save_errno;
692 return EX_TEMPFAIL;
696 statp = EX_OK;
698 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
700 ** Reset value to prevent lingering
701 ** LDAP_DECODING_ERROR due to
702 ** OpenLDAP 1.X's hack (see below)
705 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
706 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
709 ** If matching only,
710 ** no need to spin through entries
713 if (bitset(SM_LDAP_MATCHONLY, flags))
715 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
716 ldap_value_free(vals);
717 ldap_memfree(attr);
718 continue;
722 ** If we don't want multiple values,
723 ** return first found.
726 if ((char) delim == '\0')
728 if (*result != NULL)
730 /* already have a value */
731 break;
734 if (bitset(SM_LDAP_SINGLEMATCH,
735 flags) &&
736 *result != NULL)
738 /* only wanted one match */
739 SM_LDAP_ERROR_CLEANUP();
740 errno = ENOENT;
741 return EX_NOTFOUND;
744 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
746 *result = sm_rpool_strdup_x(rpool,
747 attr);
748 ldap_memfree(attr);
749 break;
752 if (vals[0] == NULL)
754 ldap_value_free(vals);
755 ldap_memfree(attr);
756 continue;
759 vsize = strlen(vals[0]) + 1;
760 if (lmap->ldap_attrsep != '\0')
761 vsize += strlen(attr) + 1;
762 *result = sm_rpool_malloc_x(rpool,
763 vsize);
764 if (lmap->ldap_attrsep != '\0')
765 sm_snprintf(*result, vsize,
766 "%s%c%s",
767 attr,
768 lmap->ldap_attrsep,
769 vals[0]);
770 else
771 sm_strlcpy(*result, vals[0],
772 vsize);
773 ldap_value_free(vals);
774 ldap_memfree(attr);
775 break;
778 /* attributes only */
779 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
781 if (*result == NULL)
782 *result = sm_rpool_strdup_x(rpool,
783 attr);
784 else
786 if (bitset(SM_LDAP_SINGLEMATCH,
787 flags) &&
788 *result != NULL)
790 /* only wanted one match */
791 SM_LDAP_ERROR_CLEANUP();
792 errno = ENOENT;
793 return EX_NOTFOUND;
796 vsize = strlen(*result) +
797 strlen(attr) + 2;
798 tmp = sm_rpool_malloc_x(rpool,
799 vsize);
800 (void) sm_snprintf(tmp,
801 vsize, "%s%c%s",
802 *result, (char) delim,
803 attr);
804 *result = tmp;
806 ldap_memfree(attr);
807 continue;
811 ** If there is more than one, munge then
812 ** into a map_coldelim separated string.
813 ** If we are recursing we may have an entry
814 ** with no 'normal' values to put in the
815 ** string.
816 ** This is not an error.
819 if (type == SM_LDAP_ATTR_NORMAL &&
820 bitset(SM_LDAP_SINGLEMATCH, flags) &&
821 *result != NULL)
823 /* only wanted one match */
824 SM_LDAP_ERROR_CLEANUP();
825 errno = ENOENT;
826 return EX_NOTFOUND;
829 vsize = 0;
830 for (i = 0; vals[i] != NULL; i++)
832 if (type == SM_LDAP_ATTR_DN ||
833 type == SM_LDAP_ATTR_FILTER ||
834 type == SM_LDAP_ATTR_URL)
836 /* add to recursion */
837 if (sm_ldap_add_recurse(&recurse,
838 vals[i],
839 type,
840 rpool) == NULL)
842 SM_LDAP_ERROR_CLEANUP();
843 errno = ENOMEM;
844 return EX_OSERR;
846 continue;
849 vsize += strlen(vals[i]) + 1;
850 if (lmap->ldap_attrsep != '\0')
851 vsize += strlen(attr) + 1;
855 ** Create/Append to string any normal
856 ** attribute values. Otherwise, just free
857 ** memory and move on to the next
858 ** attribute in this entry.
861 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
863 char *pe;
865 /* Grow result string if needed */
866 if ((*resultln + vsize) >= *resultsz)
868 while ((*resultln + vsize) >= *resultsz)
870 if (*resultsz == 0)
871 *resultsz = 1024;
872 else
873 *resultsz *= 2;
876 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
877 *vp_tmp = '\0';
879 if (*result != NULL)
880 sm_strlcpy(vp_tmp,
881 *result,
882 *resultsz);
883 *result = vp_tmp;
886 p = *result + *resultln;
887 pe = *result + *resultsz;
889 for (i = 0; vals[i] != NULL; i++)
891 if (*resultln > 0 &&
892 p < pe)
893 *p++ = (char) delim;
895 if (lmap->ldap_attrsep != '\0')
897 p += sm_strlcpy(p, attr,
898 pe - p);
899 if (p < pe)
900 *p++ = lmap->ldap_attrsep;
903 p += sm_strlcpy(p, vals[i],
904 pe - p);
905 *resultln = p - (*result);
906 if (p >= pe)
908 /* Internal error: buffer too small for LDAP values */
909 SM_LDAP_ERROR_CLEANUP();
910 errno = ENOMEM;
911 return EX_OSERR;
916 ldap_value_free(vals);
917 ldap_memfree(attr);
919 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
922 ** We check save_errno != LDAP_DECODING_ERROR since
923 ** OpenLDAP 1.X has a very ugly *undocumented*
924 ** hack of returning this error code from
925 ** ldap_next_attribute() if the library freed the
926 ** ber attribute. See:
927 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
930 if (save_errno != LDAP_SUCCESS &&
931 save_errno != LDAP_DECODING_ERROR)
933 /* Must be an error */
934 save_errno += E_LDAPBASE;
935 SM_LDAP_ERROR_CLEANUP();
936 errno = save_errno;
937 return EX_TEMPFAIL;
940 /* mark this DN as done */
941 rl->lr_done = true;
943 /* We don't want multiple values and we have one */
944 if ((char) delim == '\0' && *result != NULL)
945 break;
947 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
948 if (save_errno != LDAP_SUCCESS &&
949 save_errno != LDAP_DECODING_ERROR)
951 /* Must be an error */
952 save_errno += E_LDAPBASE;
953 SM_LDAP_ERROR_CLEANUP();
954 errno = save_errno;
955 return EX_TEMPFAIL;
957 ldap_msgfree(lmap->ldap_res);
958 lmap->ldap_res = NULL;
961 if (ret == 0)
962 save_errno = ETIMEDOUT;
963 else
964 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
965 if (save_errno != LDAP_SUCCESS)
967 statp = EX_TEMPFAIL;
968 if (ret != 0)
970 switch (save_errno)
972 #ifdef LDAP_SERVER_DOWN
973 case LDAP_SERVER_DOWN:
974 #endif /* LDAP_SERVER_DOWN */
975 case LDAP_TIMEOUT:
976 case LDAP_UNAVAILABLE:
979 ** server disappeared,
980 ** try reopen on next search
983 statp = EX_RESTART;
984 break;
986 save_errno += E_LDAPBASE;
988 SM_LDAP_ERROR_CLEANUP();
989 errno = save_errno;
990 return statp;
993 if (lmap->ldap_res != NULL)
995 ldap_msgfree(lmap->ldap_res);
996 lmap->ldap_res = NULL;
999 if (toplevel)
1001 int rlidx;
1004 ** Spin through the built-up recurse list at the top
1005 ** of the recursion. Since new items are added at the
1006 ** end of the shared list, we actually only ever get
1007 ** one level of recursion before things pop back to the
1008 ** top. Any items added to the list during that recursion
1009 ** will be expanded by the top level.
1012 for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++)
1014 int newflags;
1015 int sid;
1016 int status;
1018 rl = recurse->lr_data[rlidx];
1020 newflags = flags;
1021 if (rl->lr_done)
1023 /* already expanded */
1024 continue;
1027 if (rl->lr_type == SM_LDAP_ATTR_DN)
1029 /* do DN search */
1030 sid = ldap_search(lmap->ldap_ld,
1031 rl->lr_search,
1032 lmap->ldap_scope,
1033 "(objectClass=*)",
1034 (lmap->ldap_attr[0] == NULL ?
1035 NULL : lmap->ldap_attr),
1036 lmap->ldap_attrsonly);
1038 else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1040 /* do new search */
1041 sid = ldap_search(lmap->ldap_ld,
1042 lmap->ldap_base,
1043 lmap->ldap_scope,
1044 rl->lr_search,
1045 (lmap->ldap_attr[0] == NULL ?
1046 NULL : lmap->ldap_attr),
1047 lmap->ldap_attrsonly);
1049 else if (rl->lr_type == SM_LDAP_ATTR_URL)
1051 /* do new URL search */
1052 sid = ldap_url_search(lmap->ldap_ld,
1053 rl->lr_search,
1054 lmap->ldap_attrsonly);
1055 newflags |= SM_LDAP_USE_ALLATTR;
1057 else
1059 /* unknown or illegal attribute type */
1060 errno = EFAULT;
1061 return EX_SOFTWARE;
1064 /* Collect results */
1065 if (sid == -1)
1067 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1068 statp = EX_TEMPFAIL;
1069 switch (save_errno)
1071 #ifdef LDAP_SERVER_DOWN
1072 case LDAP_SERVER_DOWN:
1073 #endif /* LDAP_SERVER_DOWN */
1074 case LDAP_TIMEOUT:
1075 case LDAP_UNAVAILABLE:
1078 ** server disappeared,
1079 ** try reopen on next search
1082 statp = EX_RESTART;
1083 break;
1085 errno = save_errno + E_LDAPBASE;
1086 return statp;
1089 status = sm_ldap_results(lmap, sid, newflags, delim,
1090 rpool, result, resultln,
1091 resultsz, recurse);
1092 save_errno = errno;
1093 if (status != EX_OK && status != EX_NOTFOUND)
1095 errno = save_errno;
1096 return status;
1099 /* Mark as done */
1100 rl->lr_done = true;
1102 /* Reset rlidx as new items may have been added */
1103 rlidx = -1;
1106 return statp;
1108 #endif /* _FFR_LDAP_RECURSION */
1111 ** SM_LDAP_CLOSE -- close LDAP connection
1113 ** Parameters:
1114 ** lmap -- LDAP map information
1116 ** Returns:
1117 ** None.
1121 void
1122 sm_ldap_close(lmap)
1123 SM_LDAP_STRUCT *lmap;
1125 if (lmap->ldap_ld == NULL)
1126 return;
1128 if (lmap->ldap_pid == getpid())
1129 ldap_unbind(lmap->ldap_ld);
1130 lmap->ldap_ld = NULL;
1131 lmap->ldap_pid = 0;
1135 ** SM_LDAP_SETOPTS -- set LDAP options
1137 ** Parameters:
1138 ** ld -- LDAP session handle
1139 ** lmap -- LDAP map information
1141 ** Returns:
1142 ** None.
1146 void
1147 sm_ldap_setopts(ld, lmap)
1148 LDAP *ld;
1149 SM_LDAP_STRUCT *lmap;
1151 # if USE_LDAP_SET_OPTION
1152 # if _FFR_LDAP_SETVERSION
1153 if (lmap->ldap_version != 0)
1155 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
1156 &lmap->ldap_version);
1158 # endif /* _FFR_LDAP_SETVERSION */
1159 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
1160 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
1161 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
1162 else
1163 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1164 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
1165 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1166 # ifdef LDAP_OPT_RESTART
1167 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1168 # endif /* LDAP_OPT_RESTART */
1169 # else /* USE_LDAP_SET_OPTION */
1170 /* From here on in we can use ldap internal timelimits */
1171 ld->ld_deref = lmap->ldap_deref;
1172 ld->ld_options = lmap->ldap_options;
1173 ld->ld_sizelimit = lmap->ldap_sizelimit;
1174 ld->ld_timelimit = lmap->ldap_timelimit;
1175 # endif /* USE_LDAP_SET_OPTION */
1179 ** SM_LDAP_GETERRNO -- get ldap errno value
1181 ** Parameters:
1182 ** ld -- LDAP session handle
1184 ** Returns:
1185 ** LDAP errno.
1190 sm_ldap_geterrno(ld)
1191 LDAP *ld;
1193 int err = LDAP_SUCCESS;
1195 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1196 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
1197 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1198 # ifdef LDAP_OPT_SIZELIMIT
1199 err = ldap_get_lderrno(ld, NULL, NULL);
1200 # else /* LDAP_OPT_SIZELIMIT */
1201 err = ld->ld_errno;
1204 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1205 ** OpenLDAP 1.X's hack (see above)
1208 ld->ld_errno = LDAP_SUCCESS;
1209 # endif /* LDAP_OPT_SIZELIMIT */
1210 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1211 return err;
1213 # endif /* LDAPMAP */