2 * Copyright (c) 2001-2006 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.78 2006/08/30 22:56:59 ca 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
;
1102 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
1103 if (save_errno
!= LDAP_SUCCESS
)
1105 statp
= EX_TEMPFAIL
;
1110 #ifdef LDAP_SERVER_DOWN
1111 case LDAP_SERVER_DOWN
:
1112 #endif /* LDAP_SERVER_DOWN */
1114 case LDAP_UNAVAILABLE
:
1117 ** server disappeared,
1118 ** try reopen on next search
1124 save_errno
+= E_LDAPBASE
;
1126 SM_LDAP_ERROR_CLEANUP();
1131 if (lmap
->ldap_res
!= NULL
)
1133 ldap_msgfree(lmap
->ldap_res
);
1134 lmap
->ldap_res
= NULL
;
1142 ** Spin through the built-up recurse list at the top
1143 ** of the recursion. Since new items are added at the
1144 ** end of the shared list, we actually only ever get
1145 ** one level of recursion before things pop back to the
1146 ** top. Any items added to the list during that recursion
1147 ** will be expanded by the top level.
1150 for (rlidx
= 0; recurse
!= NULL
&& rlidx
< recurse
->lrl_cnt
;
1157 rl
= recurse
->lrl_data
[rlidx
];
1162 /* already expanded */
1166 if (rl
->lr_type
== SM_LDAP_ATTR_DN
)
1169 sid
= ldap_search(lmap
->ldap_ld
,
1173 (lmap
->ldap_attr
[0] == NULL
?
1174 NULL
: lmap
->ldap_attr
),
1175 lmap
->ldap_attrsonly
);
1177 else if (rl
->lr_type
== SM_LDAP_ATTR_FILTER
)
1180 sid
= ldap_search(lmap
->ldap_ld
,
1184 (lmap
->ldap_attr
[0] == NULL
?
1185 NULL
: lmap
->ldap_attr
),
1186 lmap
->ldap_attrsonly
);
1188 else if (rl
->lr_type
== SM_LDAP_ATTR_URL
)
1191 sid
= ldap_url_parse(rl
->lr_search
,
1196 errno
= sid
+ E_LDAPURLBASE
;
1200 /* We need to add objectClass */
1201 if (rl
->lr_ludp
->lud_attrs
!= NULL
)
1205 while (rl
->lr_ludp
->lud_attrs
[attrnum
] != NULL
)
1207 if (strcasecmp(rl
->lr_ludp
->lud_attrs
[attrnum
],
1208 "objectClass") == 0)
1210 /* already requested */
1221 rl
->lr_attrs
= (char **)malloc(sizeof(char *) * (attrnum
+ 2));
1222 if (rl
->lr_attrs
== NULL
)
1225 ldap_free_urldesc(rl
->lr_ludp
);
1229 for (i
= 0 ; i
< attrnum
; i
++)
1231 rl
->lr_attrs
[i
] = rl
->lr_ludp
->lud_attrs
[i
];
1233 rl
->lr_attrs
[i
++] = "objectClass";
1234 rl
->lr_attrs
[i
++] = NULL
;
1239 ** Use the existing connection
1240 ** for this search. It really
1241 ** should use lud_scheme://lud_host:lud_port/
1242 ** instead but that would require
1243 ** opening a new connection.
1244 ** This should be fixed ASAP.
1247 sid
= ldap_search(lmap
->ldap_ld
,
1248 rl
->lr_ludp
->lud_dn
,
1249 rl
->lr_ludp
->lud_scope
,
1250 rl
->lr_ludp
->lud_filter
,
1252 lmap
->ldap_attrsonly
);
1254 /* Use the attributes specified by URL */
1255 newflags
|= SM_LDAP_USE_ALLATTR
;
1259 /* unknown or illegal attribute type */
1264 /* Collect results */
1267 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
1268 statp
= EX_TEMPFAIL
;
1271 #ifdef LDAP_SERVER_DOWN
1272 case LDAP_SERVER_DOWN
:
1273 #endif /* LDAP_SERVER_DOWN */
1275 case LDAP_UNAVAILABLE
:
1278 ** server disappeared,
1279 ** try reopen on next search
1285 errno
= save_errno
+ E_LDAPBASE
;
1289 status
= sm_ldap_results(lmap
, sid
, newflags
, delim
,
1290 rpool
, result
, resultln
,
1293 if (status
!= EX_OK
&& status
!= EX_NOTFOUND
)
1301 if (rl
->lr_ludp
!= NULL
)
1303 ldap_free_urldesc(rl
->lr_ludp
);
1306 if (rl
->lr_attrs
!= NULL
)
1309 rl
->lr_attrs
= NULL
;
1312 /* Reset rlidx as new items may have been added */
1320 ** SM_LDAP_CLOSE -- close LDAP connection
1323 ** lmap -- LDAP map information
1332 SM_LDAP_STRUCT
*lmap
;
1334 if (lmap
->ldap_ld
== NULL
)
1337 if (lmap
->ldap_pid
== getpid())
1338 ldap_unbind(lmap
->ldap_ld
);
1339 lmap
->ldap_ld
= NULL
;
1344 ** SM_LDAP_SETOPTS -- set LDAP options
1347 ** ld -- LDAP session handle
1348 ** lmap -- LDAP map information
1356 sm_ldap_setopts(ld
, lmap
)
1358 SM_LDAP_STRUCT
*lmap
;
1360 # if USE_LDAP_SET_OPTION
1361 if (lmap
->ldap_version
!= 0)
1363 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
,
1364 &lmap
->ldap_version
);
1366 ldap_set_option(ld
, LDAP_OPT_DEREF
, &lmap
->ldap_deref
);
1367 if (bitset(LDAP_OPT_REFERRALS
, lmap
->ldap_options
))
1368 ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_ON
);
1370 ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1371 ldap_set_option(ld
, LDAP_OPT_SIZELIMIT
, &lmap
->ldap_sizelimit
);
1372 ldap_set_option(ld
, LDAP_OPT_TIMELIMIT
, &lmap
->ldap_timelimit
);
1373 # ifdef LDAP_OPT_RESTART
1374 ldap_set_option(ld
, LDAP_OPT_RESTART
, LDAP_OPT_ON
);
1375 # endif /* LDAP_OPT_RESTART */
1376 # else /* USE_LDAP_SET_OPTION */
1377 /* From here on in we can use ldap internal timelimits */
1378 ld
->ld_deref
= lmap
->ldap_deref
;
1379 ld
->ld_options
= lmap
->ldap_options
;
1380 ld
->ld_sizelimit
= lmap
->ldap_sizelimit
;
1381 ld
->ld_timelimit
= lmap
->ldap_timelimit
;
1382 # endif /* USE_LDAP_SET_OPTION */
1386 ** SM_LDAP_GETERRNO -- get ldap errno value
1389 ** ld -- LDAP session handle
1397 sm_ldap_geterrno(ld
)
1400 int err
= LDAP_SUCCESS
;
1402 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1403 (void) ldap_get_option(ld
, LDAP_OPT_ERROR_NUMBER
, &err
);
1404 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1405 # ifdef LDAP_OPT_SIZELIMIT
1406 err
= ldap_get_lderrno(ld
, NULL
, NULL
);
1407 # else /* LDAP_OPT_SIZELIMIT */
1411 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1412 ** OpenLDAP 1.X's hack (see above)
1415 ld
->ld_errno
= LDAP_SUCCESS
;
1416 # endif /* LDAP_OPT_SIZELIMIT */
1417 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1420 # endif /* LDAPMAP */