2 * Copyright (c) 2001-2009 Sendmail, Inc. and its suppliers.
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.
10 /* some "deprecated" calls are used, e.g., ldap_get_values() */
11 #define LDAP_DEPRECATED 1
14 SM_RCSID("@(#)$Id: ldap.c,v 1.83 2009/06/19 22:02:26 guenther Exp $")
17 # include <sys/types.h>
23 # include <sm/bitops.h>
24 # include <sm/clock.h>
26 # include <sm/debug.h>
27 # include <sm/errstring.h>
29 # include <sm/string.h>
31 # undef EX_OK /* for SVr4.2 SMP */
33 # include <sm/sysexits.h>
35 SM_DEBUG_T SmLDAPTrace
= SM_DEBUG_INITIALIZER("sm_trace_ldap",
36 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
38 static void ldaptimeout
__P((int));
39 static bool sm_ldap_has_objectclass
__P((SM_LDAP_STRUCT
*, LDAPMessage
*, char *));
40 static SM_LDAP_RECURSE_ENTRY
*sm_ldap_add_recurse
__P((SM_LDAP_RECURSE_LIST
**, char *, int, SM_RPOOL_T
*));
43 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
46 ** lmap -- pointer to SM_LDAP_STRUCT to clear
54 # if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
55 ERROR FFR_LDAP_VERSION
> _LDAP_VERSION_MAX
56 # endif /* defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX */
57 # if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
58 ERROR FFR_LDAP_VERSION
< _LDAP_VERSION_MIN
59 # endif /* defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN */
60 # define SM_LDAP_VERSION_DEFAULT _FFR_LDAP_VERSION
61 #else /* _FFR_LDAP_VERSION */
62 # define SM_LDAP_VERSION_DEFAULT 0
63 #endif /* _FFR_LDAP_VERSION */
72 lmap
->ldap_host
= NULL
;
73 lmap
->ldap_port
= LDAP_PORT
;
74 lmap
->ldap_uri
= NULL
;
75 lmap
->ldap_version
= SM_LDAP_VERSION_DEFAULT
;
76 lmap
->ldap_deref
= LDAP_DEREF_NEVER
;
77 lmap
->ldap_timelimit
= LDAP_NO_LIMIT
;
78 lmap
->ldap_sizelimit
= LDAP_NO_LIMIT
;
79 # ifdef LDAP_REFERRALS
80 lmap
->ldap_options
= LDAP_OPT_REFERRALS
;
81 # else /* LDAP_REFERRALS */
82 lmap
->ldap_options
= 0;
83 # endif /* LDAP_REFERRALS */
84 lmap
->ldap_attrsep
= '\0';
85 lmap
->ldap_binddn
= NULL
;
86 lmap
->ldap_secret
= NULL
;
87 lmap
->ldap_method
= LDAP_AUTH_SIMPLE
;
88 lmap
->ldap_base
= NULL
;
89 lmap
->ldap_scope
= LDAP_SCOPE_SUBTREE
;
90 lmap
->ldap_attrsonly
= LDAPMAP_FALSE
;
91 lmap
->ldap_timeout
.tv_sec
= 0;
92 lmap
->ldap_timeout
.tv_usec
= 0;
94 lmap
->ldap_filter
= NULL
;
95 lmap
->ldap_attr
[0] = NULL
;
96 lmap
->ldap_attr_type
[0] = SM_LDAP_ATTR_NONE
;
97 lmap
->ldap_attr_needobjclass
[0] = NULL
;
98 lmap
->ldap_res
= NULL
;
99 lmap
->ldap_next
= NULL
;
101 lmap
->ldap_multi_args
= false;
105 ** SM_LDAP_START -- actually connect to an LDAP server
108 ** name -- name of map for debug output.
109 ** lmap -- the LDAP map being opened.
112 ** true if connection is successful, false otherwise.
115 ** Populates lmap->ldap_ld.
118 static jmp_buf LDAPTimeout
;
120 #define SM_LDAP_SETTIMEOUT(to) \
125 if (setjmp(LDAPTimeout) != 0) \
130 ev = sm_setevent(to, ldaptimeout, 0); \
134 #define SM_LDAP_CLEARTIMEOUT() \
142 sm_ldap_start(name
, lmap
)
144 SM_LDAP_STRUCT
*lmap
;
152 if (sm_debug_active(&SmLDAPTrace
, 2))
153 sm_dprintf("ldapmap_start(%s)\n", name
== NULL
? "" : name
);
155 if (lmap
->ldap_host
!= NULL
)
156 id
= lmap
->ldap_host
;
157 else if (lmap
->ldap_uri
!= NULL
)
162 if (sm_debug_active(&SmLDAPTrace
, 9))
164 /* Don't print a port number for LDAP URIs */
165 if (lmap
->ldap_uri
!= NULL
)
166 sm_dprintf("ldapmap_start(%s)\n", id
);
168 sm_dprintf("ldapmap_start(%s, %d)\n", id
,
172 if (lmap
->ldap_uri
!= NULL
)
174 #if SM_CONF_LDAP_INITIALIZE
175 /* LDAP server supports URIs so use them directly */
176 save_errno
= ldap_initialize(&ld
, lmap
->ldap_uri
);
177 #else /* SM_CONF_LDAP_INITIALIZE */
179 LDAPURLDesc
*ludp
= NULL
;
181 /* Blast apart URL and use the ldap_init/ldap_open below */
182 err
= ldap_url_parse(lmap
->ldap_uri
, &ludp
);
185 errno
= err
+ E_LDAPURLBASE
;
188 lmap
->ldap_host
= sm_strdup_x(ludp
->lud_host
);
189 if (lmap
->ldap_host
== NULL
)
192 ldap_free_urldesc(ludp
);
196 lmap
->ldap_port
= ludp
->lud_port
;
197 ldap_free_urldesc(ludp
);
198 #endif /* SM_CONF_LDAP_INITIALIZE */
204 ld
= ldap_init(lmap
->ldap_host
, lmap
->ldap_port
);
206 # else /* USE_LDAP_INIT */
208 ** If using ldap_open(), the actual connection to the server
209 ** happens now so we need the timeout here. For ldap_init(),
210 ** the connection happens at bind time.
213 SM_LDAP_SETTIMEOUT(lmap
->ldap_timeout
.tv_sec
);
214 ld
= ldap_open(lmap
->ldap_host
, lmap
->ldap_port
);
217 /* clear the event if it has not sprung */
218 SM_LDAP_CLEARTIMEOUT();
219 # endif /* USE_LDAP_INIT */
226 sm_ldap_setopts(ld
, lmap
);
230 ** If using ldap_init(), the actual connection to the server
231 ** happens at ldap_bind_s() so we need the timeout here.
234 SM_LDAP_SETTIMEOUT(lmap
->ldap_timeout
.tv_sec
);
235 # endif /* USE_LDAP_INIT */
237 # ifdef LDAP_AUTH_KRBV4
238 if (lmap
->ldap_method
== LDAP_AUTH_KRBV4
&&
239 lmap
->ldap_secret
!= NULL
)
242 ** Need to put ticket in environment here instead of
243 ** during parseargs as there may be different tickets
244 ** for different LDAP connections.
247 (void) putenv(lmap
->ldap_secret
);
249 # endif /* LDAP_AUTH_KRBV4 */
251 bind_result
= ldap_bind_s(ld
, lmap
->ldap_binddn
,
252 lmap
->ldap_secret
, lmap
->ldap_method
);
255 /* clear the event if it has not sprung */
256 SM_LDAP_CLEARTIMEOUT();
257 # endif /* USE_LDAP_INIT */
259 if (bind_result
!= LDAP_SUCCESS
)
261 errno
= bind_result
+ E_LDAPBASE
;
265 /* Save PID to make sure only this PID closes the LDAP connection */
266 lmap
->ldap_pid
= getpid();
277 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
278 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
283 longjmp(LDAPTimeout
, 1);
287 ** SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
289 ** Initiate an LDAP search, return the msgid.
290 ** The calling function must collect the results.
293 ** lmap -- LDAP map information
294 ** argv -- key vector of substitutions in LDAP filter
295 ** NOTE: argv must have SM_LDAP_ARGS elements to prevent
296 ** out of bound array references
299 ** <0 on failure (SM_LDAP_ERR*), msgid on success
304 sm_ldap_search_m(lmap
, argv
)
305 SM_LDAP_STRUCT
*lmap
;
310 char filter
[LDAPMAP_MAX_FILTER
+ 1];
312 SM_REQUIRE(lmap
!= NULL
);
313 SM_REQUIRE(argv
!= NULL
);
314 SM_REQUIRE(argv
[0] != NULL
);
316 memset(filter
, '\0', sizeof filter
);
318 p
= lmap
->ldap_filter
;
319 while ((q
= strchr(p
, '%')) != NULL
)
323 if (lmap
->ldap_multi_args
)
325 #if SM_LDAP_ARGS < 10
326 # ERROR _SM_LDAP_ARGS must be 10
327 #endif /* SM_LDAP_ARGS < 10 */
330 else if (q
[1] >= '0' && q
[1] <= '9')
332 key
= argv
[q
[1] - '0'];
335 # if SM_LDAP_ERROR_ON_MISSING_ARGS
336 return SM_LDAP_ERR_ARG_MISS
;
337 # else /* SM_LDAP_ERROR_ON_MISSING_ARGS */
339 # endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
350 (void) sm_snprintf(fp
, SPACELEFT(filter
, fp
),
351 "%.*s%s", (int) (q
- p
), p
, key
);
355 else if (q
[1] == '0' ||
356 (lmap
->ldap_multi_args
&& q
[1] >= '0' && q
[1] <= '9'))
360 (void) sm_snprintf(fp
, SPACELEFT(filter
, fp
),
361 "%.*s", (int) (q
- p
), p
);
365 /* Properly escape LDAP special characters */
366 while (SPACELEFT(filter
, fp
) > 0 &&
369 if (*k
== '*' || *k
== '(' ||
370 *k
== ')' || *k
== '\\')
372 (void) sm_strlcat(fp
,
373 (*k
== '*' ? "\\2A" :
374 (*k
== '(' ? "\\28" :
375 (*k
== ')' ? "\\29" :
376 (*k
== '\\' ? "\\5C" :
378 SPACELEFT(filter
, fp
));
388 (void) sm_snprintf(fp
, SPACELEFT(filter
, fp
),
389 "%.*s", (int) (q
- p
+ 1), p
);
390 p
= q
+ (q
[1] == '%' ? 2 : 1);
394 (void) sm_strlcpy(fp
, p
, SPACELEFT(filter
, fp
));
395 if (sm_debug_active(&SmLDAPTrace
, 20))
396 sm_dprintf("ldap search filter=%s\n", filter
);
398 lmap
->ldap_res
= NULL
;
399 msgid
= ldap_search(lmap
->ldap_ld
, lmap
->ldap_base
,
400 lmap
->ldap_scope
, filter
,
401 (lmap
->ldap_attr
[0] == NULL
? NULL
:
403 lmap
->ldap_attrsonly
);
408 ** SM_LDAP_SEARCH -- initiate LDAP search
410 ** Initiate an LDAP search, return the msgid.
411 ** The calling function must collect the results.
412 ** Note this is just a wrapper into sm_ldap_search_m()
415 ** lmap -- LDAP map information
416 ** key -- key to substitute in LDAP filter
419 ** <0 on failure, msgid on success
424 sm_ldap_search(lmap
, key
)
425 SM_LDAP_STRUCT
*lmap
;
428 char *argv
[SM_LDAP_ARGS
];
430 memset(argv
, '\0', sizeof argv
);
432 return sm_ldap_search_m(lmap
, argv
);
436 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
437 ** particular objectClass
440 ** lmap -- pointer to SM_LDAP_STRUCT in use
441 ** entry -- current LDAP entry struct
442 ** ocvalue -- particular objectclass in question.
443 ** may be of form (fee|foo|fum) meaning
444 ** any entry can be part of either fee,
445 ** foo or fum objectclass
448 ** true if item has that objectClass
452 sm_ldap_has_objectclass(lmap
, entry
, ocvalue
)
453 SM_LDAP_STRUCT
*lmap
;
463 vals
= ldap_get_values(lmap
->ldap_ld
, entry
, "objectClass");
467 for (i
= 0; vals
[i
] != NULL
; i
++)
475 while (*p
!= '\0' && *p
!= '|')
478 if ((p
- q
) == strlen(vals
[i
]) &&
479 sm_strncasecmp(vals
[i
], q
, p
- q
) == 0)
481 ldap_value_free(vals
);
491 ldap_value_free(vals
);
496 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
499 ** lmap -- pointer to SM_LDAP_STRUCT in use
500 ** msgid -- msgid returned by sm_ldap_search()
501 ** flags -- flags for the lookup
502 ** delim -- delimiter for result concatenation
503 ** rpool -- memory pool for storage
504 ** result -- return string
505 ** recurse -- recursion list
511 # define SM_LDAP_ERROR_CLEANUP() \
513 if (lmap->ldap_res != NULL) \
515 ldap_msgfree(lmap->ldap_res); \
516 lmap->ldap_res = NULL; \
518 (void) ldap_abandon(lmap->ldap_ld, msgid); \
521 static SM_LDAP_RECURSE_ENTRY
*
522 sm_ldap_add_recurse(top
, item
, type
, rpool
)
523 SM_LDAP_RECURSE_LIST
**top
;
535 SM_LDAP_RECURSE_ENTRY
*newe
;
536 SM_LDAP_RECURSE_ENTRY
**olddata
;
539 ** This code will maintain a list of
540 ** SM_LDAP_RECURSE_ENTRY structures
541 ** in ascending order.
546 /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
547 *top
= sm_rpool_malloc_x(rpool
, sizeof **top
);
549 (*top
)->lrl_size
= 0;
550 (*top
)->lrl_data
= NULL
;
553 if ((*top
)->lrl_cnt
>= (*top
)->lrl_size
)
555 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
556 olddata
= (*top
)->lrl_data
;
557 if ((*top
)->lrl_size
== 0)
560 (*top
)->lrl_size
= 256;
564 oldsizeb
= (*top
)->lrl_size
* sizeof *((*top
)->lrl_data
);
565 (*top
)->lrl_size
*= 2;
567 (*top
)->lrl_data
= sm_rpool_malloc_x(rpool
,
568 (*top
)->lrl_size
* sizeof *((*top
)->lrl_data
));
570 memcpy((*top
)->lrl_data
, olddata
, oldsizeb
);
574 ** Binary search/insert item:type into list.
575 ** Return current entry pointer if already exists.
579 m
= (*top
)->lrl_cnt
- 1;
585 while (insertat
== -1)
589 rc
= sm_strcasecmp(item
, (*top
)->lrl_data
[p
]->lr_search
);
591 rc
= type
- (*top
)->lrl_data
[p
]->lr_type
;
598 return (*top
)->lrl_data
[p
];
602 else if (n
>= (*top
)->lrl_cnt
)
603 insertat
= (*top
)->lrl_cnt
;
609 ** Not found in list, make room
610 ** at insert point and add it.
613 newe
= sm_rpool_malloc_x(rpool
, sizeof *newe
);
616 moveb
= ((*top
)->lrl_cnt
- insertat
) * sizeof *((*top
)->lrl_data
);
618 memmove(&((*top
)->lrl_data
[insertat
+ 1]),
619 &((*top
)->lrl_data
[insertat
]),
622 newe
->lr_search
= sm_rpool_strdup_x(rpool
, item
);
623 newe
->lr_type
= type
;
624 newe
->lr_ludp
= NULL
;
625 newe
->lr_attrs
= NULL
;
626 newe
->lr_done
= false;
628 ((*top
)->lrl_data
)[insertat
] = newe
;
635 sm_ldap_results(lmap
, msgid
, flags
, delim
, rpool
, result
,
636 resultln
, resultsz
, recurse
)
637 SM_LDAP_STRUCT
*lmap
;
645 SM_LDAP_RECURSE_LIST
*recurse
;
654 SM_LDAP_RECURSE_ENTRY
*rl
;
656 /* Are we the top top level of the search? */
657 toplevel
= (recurse
== NULL
);
661 while ((ret
= ldap_result(lmap
->ldap_ld
, msgid
, 0,
662 (lmap
->ldap_timeout
.tv_sec
== 0 ? NULL
:
663 &(lmap
->ldap_timeout
)),
664 &(lmap
->ldap_res
))) == LDAP_RES_SEARCH_ENTRY
)
668 /* If we don't want multiple values and we have one, break */
669 if ((char) delim
== '\0' &&
670 !bitset(SM_LDAP_SINGLEMATCH
, flags
) &&
674 /* Cycle through all entries */
675 for (entry
= ldap_first_entry(lmap
->ldap_ld
, lmap
->ldap_res
);
677 entry
= ldap_next_entry(lmap
->ldap_ld
, lmap
->ldap_res
))
685 ** If matching only and found an entry,
686 ** no need to spin through attributes
689 if (bitset(SM_LDAP_MATCHONLY
, flags
))
695 #if _FFR_LDAP_SINGLEDN
696 if (bitset(SM_LDAP_SINGLEDN
, flags
) && *result
!= NULL
)
698 /* only wanted one match */
699 SM_LDAP_ERROR_CLEANUP();
703 #endif /* _FFR_LDAP_SINGLEDN */
705 /* record completed DN's to prevent loops */
706 dn
= ldap_get_dn(lmap
->ldap_ld
, entry
);
709 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
710 save_errno
+= E_LDAPBASE
;
711 SM_LDAP_ERROR_CLEANUP();
716 rl
= sm_ldap_add_recurse(&recurse
, dn
,
723 SM_LDAP_ERROR_CLEANUP();
727 else if (rl
->lr_done
)
729 /* already on list, skip it */
735 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
737 ** Reset value to prevent lingering
738 ** LDAP_DECODING_ERROR due to
739 ** OpenLDAP 1.X's hack (see below)
742 lmap
->ldap_ld
->ld_errno
= LDAP_SUCCESS
;
743 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
745 for (attr
= ldap_first_attribute(lmap
->ldap_ld
, entry
,
748 attr
= ldap_next_attribute(lmap
->ldap_ld
, entry
,
753 char *needobjclass
= NULL
;
755 type
= SM_LDAP_ATTR_NONE
;
756 for (i
= 0; lmap
->ldap_attr
[i
] != NULL
; i
++)
758 if (sm_strcasecmp(lmap
->ldap_attr
[i
],
761 type
= lmap
->ldap_attr_type
[i
];
762 needobjclass
= lmap
->ldap_attr_needobjclass
[i
];
767 if (bitset(SM_LDAP_USE_ALLATTR
, flags
) &&
768 type
== SM_LDAP_ATTR_NONE
)
770 /* URL lookups specify attrs to use */
771 type
= SM_LDAP_ATTR_NORMAL
;
775 if (type
== SM_LDAP_ATTR_NONE
)
777 /* attribute not requested */
779 SM_LDAP_ERROR_CLEANUP();
785 ** For recursion on a particular attribute,
786 ** we may need to see if this entry is
787 ** part of a particular objectclass.
788 ** Also, ignore objectClass attribute.
789 ** Otherwise we just ignore this attribute.
792 if (type
== SM_LDAP_ATTR_OBJCLASS
||
793 (needobjclass
!= NULL
&&
794 !sm_ldap_has_objectclass(lmap
, entry
,
801 if (lmap
->ldap_attrsonly
== LDAPMAP_FALSE
)
803 vals
= ldap_get_values(lmap
->ldap_ld
,
808 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
809 if (save_errno
== LDAP_SUCCESS
)
815 /* Must be an error */
816 save_errno
+= E_LDAPBASE
;
818 SM_LDAP_ERROR_CLEANUP();
826 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
828 ** Reset value to prevent lingering
829 ** LDAP_DECODING_ERROR due to
830 ** OpenLDAP 1.X's hack (see below)
833 lmap
->ldap_ld
->ld_errno
= LDAP_SUCCESS
;
834 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
838 ** no need to spin through entries
841 if (bitset(SM_LDAP_MATCHONLY
, flags
))
843 if (lmap
->ldap_attrsonly
== LDAPMAP_FALSE
)
844 ldap_value_free(vals
);
850 ** If we don't want multiple values,
851 ** return first found.
854 if ((char) delim
== '\0')
858 /* already have a value */
859 if (bitset(SM_LDAP_SINGLEMATCH
,
862 /* only wanted one match */
863 SM_LDAP_ERROR_CLEANUP();
870 if (lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
872 *result
= sm_rpool_strdup_x(rpool
,
880 ldap_value_free(vals
);
885 vsize
= strlen(vals
[0]) + 1;
886 if (lmap
->ldap_attrsep
!= '\0')
887 vsize
+= strlen(attr
) + 1;
888 *result
= sm_rpool_malloc_x(rpool
,
890 if (lmap
->ldap_attrsep
!= '\0')
891 sm_snprintf(*result
, vsize
,
897 sm_strlcpy(*result
, vals
[0],
899 ldap_value_free(vals
);
904 /* attributes only */
905 if (lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
908 *result
= sm_rpool_strdup_x(rpool
,
912 if (bitset(SM_LDAP_SINGLEMATCH
,
916 /* only wanted one match */
917 SM_LDAP_ERROR_CLEANUP();
922 vsize
= strlen(*result
) +
924 tmp
= sm_rpool_malloc_x(rpool
,
926 (void) sm_snprintf(tmp
,
928 *result
, (char) delim
,
937 ** If there is more than one, munge then
938 ** into a map_coldelim separated string.
939 ** If we are recursing we may have an entry
940 ** with no 'normal' values to put in the
942 ** This is not an error.
945 if (type
== SM_LDAP_ATTR_NORMAL
&&
946 bitset(SM_LDAP_SINGLEMATCH
, flags
) &&
949 /* only wanted one match */
950 SM_LDAP_ERROR_CLEANUP();
956 for (i
= 0; vals
[i
] != NULL
; i
++)
958 if (type
== SM_LDAP_ATTR_DN
||
959 type
== SM_LDAP_ATTR_FILTER
||
960 type
== SM_LDAP_ATTR_URL
)
962 /* add to recursion */
963 if (sm_ldap_add_recurse(&recurse
,
968 SM_LDAP_ERROR_CLEANUP();
975 vsize
+= strlen(vals
[i
]) + 1;
976 if (lmap
->ldap_attrsep
!= '\0')
977 vsize
+= strlen(attr
) + 1;
981 ** Create/Append to string any normal
982 ** attribute values. Otherwise, just free
983 ** memory and move on to the next
984 ** attribute in this entry.
987 if (type
== SM_LDAP_ATTR_NORMAL
&& vsize
> 0)
991 /* Grow result string if needed */
992 if ((*resultln
+ vsize
) >= *resultsz
)
994 while ((*resultln
+ vsize
) >= *resultsz
)
1002 vp_tmp
= sm_rpool_malloc_x(rpool
, *resultsz
);
1005 if (*result
!= NULL
)
1012 p
= *result
+ *resultln
;
1013 pe
= *result
+ *resultsz
;
1015 for (i
= 0; vals
[i
] != NULL
; i
++)
1017 if (*resultln
> 0 &&
1019 *p
++ = (char) delim
;
1021 if (lmap
->ldap_attrsep
!= '\0')
1023 p
+= sm_strlcpy(p
, attr
,
1026 *p
++ = lmap
->ldap_attrsep
;
1029 p
+= sm_strlcpy(p
, vals
[i
],
1031 *resultln
= p
- (*result
);
1034 /* Internal error: buffer too small for LDAP values */
1035 SM_LDAP_ERROR_CLEANUP();
1042 ldap_value_free(vals
);
1045 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
1048 ** We check save_errno != LDAP_DECODING_ERROR since
1049 ** OpenLDAP 1.X has a very ugly *undocumented*
1050 ** hack of returning this error code from
1051 ** ldap_next_attribute() if the library freed the
1052 ** ber attribute. See:
1053 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
1056 if (save_errno
!= LDAP_SUCCESS
&&
1057 save_errno
!= LDAP_DECODING_ERROR
)
1059 /* Must be an error */
1060 save_errno
+= E_LDAPBASE
;
1061 SM_LDAP_ERROR_CLEANUP();
1066 /* mark this DN as done */
1068 if (rl
->lr_ludp
!= NULL
)
1070 ldap_free_urldesc(rl
->lr_ludp
);
1073 if (rl
->lr_attrs
!= NULL
)
1076 rl
->lr_attrs
= NULL
;
1079 /* We don't want multiple values and we have one */
1080 if ((char) delim
== '\0' &&
1081 !bitset(SM_LDAP_SINGLEMATCH
, flags
) &&
1085 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
1086 if (save_errno
!= LDAP_SUCCESS
&&
1087 save_errno
!= LDAP_DECODING_ERROR
)
1089 /* Must be an error */
1090 save_errno
+= E_LDAPBASE
;
1091 SM_LDAP_ERROR_CLEANUP();
1095 ldap_msgfree(lmap
->ldap_res
);
1096 lmap
->ldap_res
= NULL
;
1100 save_errno
= ETIMEDOUT
;
1106 ** We may have gotten an LDAP_RES_SEARCH_RESULT response
1107 ** with an error inside it, so we have to extract that
1108 ** with ldap_parse_result(). This can happen when talking
1109 ** to an LDAP proxy whose backend has gone down.
1112 save_errno
= ldap_parse_result(lmap
->ldap_ld
, lmap
->ldap_res
,
1113 &rc
, NULL
, NULL
, NULL
, NULL
, 0);
1114 if (save_errno
== LDAP_SUCCESS
)
1117 if (save_errno
!= LDAP_SUCCESS
)
1119 statp
= EX_TEMPFAIL
;
1122 #ifdef LDAP_SERVER_DOWN
1123 case LDAP_SERVER_DOWN
:
1124 #endif /* LDAP_SERVER_DOWN */
1127 case LDAP_UNAVAILABLE
:
1130 ** server disappeared,
1131 ** try reopen on next search
1138 save_errno
+= E_LDAPBASE
;
1139 SM_LDAP_ERROR_CLEANUP();
1144 if (lmap
->ldap_res
!= NULL
)
1146 ldap_msgfree(lmap
->ldap_res
);
1147 lmap
->ldap_res
= NULL
;
1155 ** Spin through the built-up recurse list at the top
1156 ** of the recursion. Since new items are added at the
1157 ** end of the shared list, we actually only ever get
1158 ** one level of recursion before things pop back to the
1159 ** top. Any items added to the list during that recursion
1160 ** will be expanded by the top level.
1163 for (rlidx
= 0; recurse
!= NULL
&& rlidx
< recurse
->lrl_cnt
;
1170 rl
= recurse
->lrl_data
[rlidx
];
1175 /* already expanded */
1179 if (rl
->lr_type
== SM_LDAP_ATTR_DN
)
1182 sid
= ldap_search(lmap
->ldap_ld
,
1186 (lmap
->ldap_attr
[0] == NULL
?
1187 NULL
: lmap
->ldap_attr
),
1188 lmap
->ldap_attrsonly
);
1190 else if (rl
->lr_type
== SM_LDAP_ATTR_FILTER
)
1193 sid
= ldap_search(lmap
->ldap_ld
,
1197 (lmap
->ldap_attr
[0] == NULL
?
1198 NULL
: lmap
->ldap_attr
),
1199 lmap
->ldap_attrsonly
);
1201 else if (rl
->lr_type
== SM_LDAP_ATTR_URL
)
1204 sid
= ldap_url_parse(rl
->lr_search
,
1209 errno
= sid
+ E_LDAPURLBASE
;
1213 /* We need to add objectClass */
1214 if (rl
->lr_ludp
->lud_attrs
!= NULL
)
1218 while (rl
->lr_ludp
->lud_attrs
[attrnum
] != NULL
)
1220 if (strcasecmp(rl
->lr_ludp
->lud_attrs
[attrnum
],
1221 "objectClass") == 0)
1223 /* already requested */
1234 rl
->lr_attrs
= (char **)malloc(sizeof(char *) * (attrnum
+ 2));
1235 if (rl
->lr_attrs
== NULL
)
1238 ldap_free_urldesc(rl
->lr_ludp
);
1242 for (i
= 0 ; i
< attrnum
; i
++)
1244 rl
->lr_attrs
[i
] = rl
->lr_ludp
->lud_attrs
[i
];
1246 rl
->lr_attrs
[i
++] = "objectClass";
1247 rl
->lr_attrs
[i
++] = NULL
;
1252 ** Use the existing connection
1253 ** for this search. It really
1254 ** should use lud_scheme://lud_host:lud_port/
1255 ** instead but that would require
1256 ** opening a new connection.
1257 ** This should be fixed ASAP.
1260 sid
= ldap_search(lmap
->ldap_ld
,
1261 rl
->lr_ludp
->lud_dn
,
1262 rl
->lr_ludp
->lud_scope
,
1263 rl
->lr_ludp
->lud_filter
,
1265 lmap
->ldap_attrsonly
);
1267 /* Use the attributes specified by URL */
1268 newflags
|= SM_LDAP_USE_ALLATTR
;
1272 /* unknown or illegal attribute type */
1277 /* Collect results */
1280 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
1281 statp
= EX_TEMPFAIL
;
1284 #ifdef LDAP_SERVER_DOWN
1285 case LDAP_SERVER_DOWN
:
1286 #endif /* LDAP_SERVER_DOWN */
1289 case LDAP_UNAVAILABLE
:
1292 ** server disappeared,
1293 ** try reopen on next search
1299 errno
= save_errno
+ E_LDAPBASE
;
1303 status
= sm_ldap_results(lmap
, sid
, newflags
, delim
,
1304 rpool
, result
, resultln
,
1307 if (status
!= EX_OK
&& status
!= EX_NOTFOUND
)
1315 if (rl
->lr_ludp
!= NULL
)
1317 ldap_free_urldesc(rl
->lr_ludp
);
1320 if (rl
->lr_attrs
!= NULL
)
1323 rl
->lr_attrs
= NULL
;
1326 /* Reset rlidx as new items may have been added */
1334 ** SM_LDAP_CLOSE -- close LDAP connection
1337 ** lmap -- LDAP map information
1346 SM_LDAP_STRUCT
*lmap
;
1348 if (lmap
->ldap_ld
== NULL
)
1351 if (lmap
->ldap_pid
== getpid())
1352 ldap_unbind(lmap
->ldap_ld
);
1353 lmap
->ldap_ld
= NULL
;
1358 ** SM_LDAP_SETOPTS -- set LDAP options
1361 ** ld -- LDAP session handle
1362 ** lmap -- LDAP map information
1370 sm_ldap_setopts(ld
, lmap
)
1372 SM_LDAP_STRUCT
*lmap
;
1374 # if USE_LDAP_SET_OPTION
1375 if (lmap
->ldap_version
!= 0)
1377 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
,
1378 &lmap
->ldap_version
);
1380 ldap_set_option(ld
, LDAP_OPT_DEREF
, &lmap
->ldap_deref
);
1381 if (bitset(LDAP_OPT_REFERRALS
, lmap
->ldap_options
))
1382 ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_ON
);
1384 ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1385 ldap_set_option(ld
, LDAP_OPT_SIZELIMIT
, &lmap
->ldap_sizelimit
);
1386 ldap_set_option(ld
, LDAP_OPT_TIMELIMIT
, &lmap
->ldap_timelimit
);
1387 # if _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
1388 if (lmap
->ldap_networktmo
> 0)
1392 tmo
.tv_sec
= lmap
->ldap_networktmo
;
1394 ldap_set_option(ld
, LDAP_OPT_NETWORK_TIMEOUT
, &tmo
);
1396 # endif /* _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
1397 # ifdef LDAP_OPT_RESTART
1398 ldap_set_option(ld
, LDAP_OPT_RESTART
, LDAP_OPT_ON
);
1399 # endif /* LDAP_OPT_RESTART */
1400 # else /* USE_LDAP_SET_OPTION */
1401 /* From here on in we can use ldap internal timelimits */
1402 ld
->ld_deref
= lmap
->ldap_deref
;
1403 ld
->ld_options
= lmap
->ldap_options
;
1404 ld
->ld_sizelimit
= lmap
->ldap_sizelimit
;
1405 ld
->ld_timelimit
= lmap
->ldap_timelimit
;
1406 # endif /* USE_LDAP_SET_OPTION */
1410 ** SM_LDAP_GETERRNO -- get ldap errno value
1413 ** ld -- LDAP session handle
1421 sm_ldap_geterrno(ld
)
1424 int err
= LDAP_SUCCESS
;
1426 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1427 (void) ldap_get_option(ld
, LDAP_OPT_ERROR_NUMBER
, &err
);
1428 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1429 # ifdef LDAP_OPT_SIZELIMIT
1430 err
= ldap_get_lderrno(ld
, NULL
, NULL
);
1431 # else /* LDAP_OPT_SIZELIMIT */
1435 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1436 ** OpenLDAP 1.X's hack (see above)
1439 ld
->ld_errno
= LDAP_SUCCESS
;
1440 # endif /* LDAP_OPT_SIZELIMIT */
1441 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1444 # endif /* LDAPMAP */