1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: ldap.c,v 1.2 2007-03-15 19:22:13 andy Exp $
22 ***************************************************************************/
26 #ifndef CURL_DISABLE_LDAP
27 /* -- WIN32 approved -- */
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
36 #ifdef HAVE_SYS_STAT_H
57 #include <curl/curl.h>
67 #define _MPRINTF_REPLACE /* use our functions only */
68 #include <curl/mprintf.h>
72 /* WLdap32.dll functions are *not* stdcall. Must call these via __cdecl
73 * pointers in case libcurl was compiled as fastcall (cl -Gr). Watcom
74 * uses fastcall by default.
76 #if !defined(WIN32) && !defined(__cdecl)
80 #ifndef LDAP_SIZELIMIT_EXCEEDED
81 #define LDAP_SIZELIMIT_EXCEEDED 4
84 #define LDAP_VERSION2 2
87 #define LDAP_VERSION3 3
89 #ifndef LDAP_OPT_PROTOCOL_VERSION
90 #define LDAP_OPT_PROTOCOL_VERSION 0x0011
93 #define DLOPEN_MODE RTLD_LAZY /*! assume all dlopen() implementations have
96 #if defined(RTLD_LAZY_GLOBAL) /* It turns out some systems use this: */
98 # define DLOPEN_MODE RTLD_LAZY_GLOBAL
99 #elif defined(RTLD_GLOBAL)
101 # define DLOPEN_MODE (RTLD_LAZY | RTLD_GLOBAL)
104 #define DYNA_GET_FUNCTION(type, fnc) do { \
105 (fnc) = (type)DynaGetFunction(#fnc); \
107 return CURLE_FUNCTION_NOT_FOUND; \
110 /*! CygWin etc. configure could set these, but we don't want it.
111 * Must use WLdap32.dll code.
119 * We use this ZERO_NULL to avoid picky compiler warnings,
120 * when assigning a NULL pointer to a function pointer var.
125 typedef void * (*dynafunc
)(void *input
);
127 /***********************************************************************
129 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) || defined(WIN32)
130 static void *libldap
= NULL
;
131 #if defined(DL_LBER_FILE)
132 static void *liblber
= NULL
;
137 unsigned long bv_len
;
141 static int DynaOpen(const char **mod_name
)
143 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
144 if (libldap
== NULL
) {
146 * libldap.so can normally resolve its dependency on liblber.so
147 * automatically, but in broken installation it does not so
148 * handle it here by opening liblber.so as global.
151 *mod_name
= DL_LBER_FILE
;
152 liblber
= dlopen(*mod_name
, DLOPEN_MODE
);
157 /* Assume loading libldap.so will fail if loading of liblber.so failed
159 *mod_name
= DL_LDAP_FILE
;
160 libldap
= dlopen(*mod_name
, RTLD_LAZY
);
162 return (libldap
!= NULL
);
165 *mod_name
= DL_LDAP_FILE
;
167 libldap
= (void*)LoadLibrary(*mod_name
);
168 return (libldap
!= NULL
);
176 static void DynaClose(void)
178 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
191 FreeLibrary ((HMODULE
)libldap
);
197 static dynafunc
DynaGetFunction(const char *name
)
199 dynafunc func
= (dynafunc
)ZERO_NULL
;
201 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
203 /* This typecast magic below was brought by Joe Halpin. In ISO C, you
204 * cannot typecast a data pointer to a function pointer, but that's
205 * exactly what we need to do here to avoid compiler warnings on picky
207 *(void**) (&func
) = dlsym(libldap
, name
);
211 func
= (dynafunc
)GetProcAddress((HINSTANCE
)libldap
, name
);
219 /***********************************************************************
221 typedef struct ldap_url_desc
{
222 struct ldap_url_desc
*lud_next
;
235 static int _ldap_url_parse (const struct connectdata
*conn
,
237 static void _ldap_free_urldesc (LDAPURLDesc
*ludp
);
239 static void (*ldap_free_urldesc
)(LDAPURLDesc
*) = _ldap_free_urldesc
;
243 #define LDAP_TRACE(x) do { \
244 _ldap_trace ("%u: ", __LINE__); \
248 static void _ldap_trace (const char *fmt
, ...);
250 #define LDAP_TRACE(x) ((void)0)
254 CURLcode
Curl_ldap(struct connectdata
*conn
, bool *done
)
256 CURLcode status
= CURLE_OK
;
259 int (*ldap_url_parse
)(char *, LDAPURLDesc
**);
260 void (*ldap_free_urldesc
)(void *);
262 void *(__cdecl
*ldap_init
)(char *, int);
263 int (__cdecl
*ldap_simple_bind_s
)(void *, char *, char *);
264 int (__cdecl
*ldap_unbind_s
)(void *);
265 int (__cdecl
*ldap_search_s
)(void *, char *, int, char *, char **,
267 void *(__cdecl
*ldap_first_entry
)(void *, void *);
268 void *(__cdecl
*ldap_next_entry
)(void *, void *);
269 char *(__cdecl
*ldap_err2string
)(int);
270 char *(__cdecl
*ldap_get_dn
)(void *, void *);
271 char *(__cdecl
*ldap_first_attribute
)(void *, void *, void **);
272 char *(__cdecl
*ldap_next_attribute
)(void *, void *, void *);
273 void **(__cdecl
*ldap_get_values_len
)(void *, void *, const char *);
274 void (__cdecl
*ldap_value_free_len
)(void **);
275 void (__cdecl
*ldap_memfree
)(void *);
276 void (__cdecl
*ber_free
)(void *, int);
277 int (__cdecl
*ldap_set_option
)(void *, int, void *);
280 LDAPURLDesc
*ludp
= NULL
;
281 const char *mod_name
;
283 void *entryIterator
; /*! type should be 'LDAPMessage *' */
285 struct SessionHandle
*data
=conn
->data
;
290 *done
= TRUE
; /* unconditionally */
291 infof(data
, "LDAP local: %s\n", data
->change
.url
);
293 if (!DynaOpen(&mod_name
)) {
294 failf(data
, "The %s LDAP library/libraries couldn't be opened", mod_name
);
295 return CURLE_LIBRARY_NOT_FOUND
;
298 /* The types are needed because ANSI C distinguishes between
299 * pointer-to-object (data) and pointer-to-function.
301 DYNA_GET_FUNCTION(void *(__cdecl
*)(char *, int), ldap_init
);
302 DYNA_GET_FUNCTION(int (__cdecl
*)(void *, char *, char *),
304 DYNA_GET_FUNCTION(int (__cdecl
*)(void *), ldap_unbind_s
);
306 DYNA_GET_FUNCTION(int (*)(char *, LDAPURLDesc
**), ldap_url_parse
);
307 DYNA_GET_FUNCTION(void (*)(void *), ldap_free_urldesc
);
309 DYNA_GET_FUNCTION(int (__cdecl
*)(void *, char *, int, char *, char **, int,
310 void **), ldap_search_s
);
311 DYNA_GET_FUNCTION(void *(__cdecl
*)(void *, void *), ldap_first_entry
);
312 DYNA_GET_FUNCTION(void *(__cdecl
*)(void *, void *), ldap_next_entry
);
313 DYNA_GET_FUNCTION(char *(__cdecl
*)(int), ldap_err2string
);
314 DYNA_GET_FUNCTION(char *(__cdecl
*)(void *, void *), ldap_get_dn
);
315 DYNA_GET_FUNCTION(char *(__cdecl
*)(void *, void *, void **),
316 ldap_first_attribute
);
317 DYNA_GET_FUNCTION(char *(__cdecl
*)(void *, void *, void *),
318 ldap_next_attribute
);
319 DYNA_GET_FUNCTION(void **(__cdecl
*)(void *, void *, const char *),
320 ldap_get_values_len
);
321 DYNA_GET_FUNCTION(void (__cdecl
*)(void **), ldap_value_free_len
);
322 DYNA_GET_FUNCTION(void (__cdecl
*)(void *), ldap_memfree
);
323 DYNA_GET_FUNCTION(void (__cdecl
*)(void *, int), ber_free
);
324 DYNA_GET_FUNCTION(int (__cdecl
*)(void *, int, void *), ldap_set_option
);
326 server
= (*ldap_init
)(conn
->host
.name
, (int)conn
->port
);
327 if (server
== NULL
) {
328 failf(data
, "LDAP local: Cannot connect to %s:%d",
329 conn
->host
.name
, conn
->port
);
330 status
= CURLE_COULDNT_CONNECT
;
334 ldap_proto
= LDAP_VERSION3
;
335 (*ldap_set_option
)(server
, LDAP_OPT_PROTOCOL_VERSION
, &ldap_proto
);
336 rc
= (*ldap_simple_bind_s
)(server
,
337 conn
->bits
.user_passwd
? conn
->user
: NULL
,
338 conn
->bits
.user_passwd
? conn
->passwd
: NULL
);
340 ldap_proto
= LDAP_VERSION2
;
341 (*ldap_set_option
)(server
, LDAP_OPT_PROTOCOL_VERSION
, &ldap_proto
);
342 rc
= (*ldap_simple_bind_s
)(server
,
343 conn
->bits
.user_passwd
? conn
->user
: NULL
,
344 conn
->bits
.user_passwd
? conn
->passwd
: NULL
);
347 failf(data
, "LDAP local: %s", (*ldap_err2string
)(rc
));
348 status
= CURLE_LDAP_CANNOT_BIND
;
353 rc
= _ldap_url_parse(conn
, &ludp
);
355 rc
= (*ldap_url_parse
)(data
->change
.url
, &ludp
);
359 failf(data
, "LDAP local: %s", (*ldap_err2string
)(rc
));
360 status
= CURLE_LDAP_INVALID_URL
;
364 rc
= (*ldap_search_s
)(server
, ludp
->lud_dn
, ludp
->lud_scope
,
365 ludp
->lud_filter
, ludp
->lud_attrs
, 0, &result
);
367 if (rc
!= 0 && rc
!= LDAP_SIZELIMIT_EXCEEDED
) {
368 failf(data
, "LDAP remote: %s", (*ldap_err2string
)(rc
));
369 status
= CURLE_LDAP_SEARCH_FAILED
;
373 for(num
= 0, entryIterator
= (*ldap_first_entry
)(server
, result
);
375 entryIterator
= (*ldap_next_entry
)(server
, entryIterator
), num
++)
377 void *ber
= NULL
; /*! is really 'BerElement **' */
378 void *attribute
; /*! suspicious that this isn't 'const' */
379 char *dn
= (*ldap_get_dn
)(server
, entryIterator
);
382 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)"DN: ", 4);
383 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)dn
, 0);
384 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)"\n", 1);
386 for (attribute
= (*ldap_first_attribute
)(server
, entryIterator
, &ber
);
388 attribute
= (*ldap_next_attribute
)(server
, entryIterator
, ber
))
390 struct bv
**vals
= (struct bv
**)
391 (*ldap_get_values_len
)(server
, entryIterator
, attribute
);
395 for (i
= 0; (vals
[i
] != NULL
); i
++)
397 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)"\t", 1);
398 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *) attribute
, 0);
399 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)": ", 2);
400 if ((strlen(attribute
) > 7) &&
403 (strlen((char *)attribute
) - 7)) == 0)) {
404 /* Binary attribute, encode to base64. */
405 val_b64_sz
= Curl_base64_encode(conn
->data
,
409 if (val_b64_sz
> 0) {
410 Curl_client_write(conn
, CLIENTWRITE_BODY
, val_b64
, val_b64_sz
);
414 Curl_client_write(conn
, CLIENTWRITE_BODY
, vals
[i
]->bv_val
,
416 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)"\n", 0);
419 /* Free memory used to store values */
420 (*ldap_value_free_len
)((void **)vals
);
422 Curl_client_write(conn
, CLIENTWRITE_BODY
, (char *)"\n", 1);
424 (*ldap_memfree
)(attribute
);
432 LDAP_TRACE (("Received %d entries\n", num
));
433 if (rc
== LDAP_SIZELIMIT_EXCEEDED
)
434 infof(data
, "There are more than %d entries\n", num
);
436 (*ldap_free_urldesc
)(ludp
);
438 (*ldap_unbind_s
)(server
);
442 /* no data to transfer */
443 Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, -1, NULL
);
444 conn
->bits
.close
= TRUE
;
450 static void _ldap_trace (const char *fmt
, ...)
452 static int do_trace
= -1;
455 if (do_trace
== -1) {
456 const char *env
= getenv("CURL_TRACE");
457 do_trace
= (env
&& atoi(env
) > 0);
462 va_start (args
, fmt
);
463 vfprintf (stderr
, fmt
, args
);
470 * Return scope-value for a scope-string.
472 static int str2scope (const char *p
)
474 if (!stricmp(p
, "one"))
475 return LDAP_SCOPE_ONELEVEL
;
476 if (!stricmp(p
, "onetree"))
477 return LDAP_SCOPE_ONELEVEL
;
478 if (!stricmp(p
, "base"))
479 return LDAP_SCOPE_BASE
;
480 if (!stricmp(p
, "sub"))
481 return LDAP_SCOPE_SUBTREE
;
482 if (!stricmp( p
, "subtree"))
483 return LDAP_SCOPE_SUBTREE
;
488 * Split 'str' into strings separated by commas.
489 * Note: res[] points into 'str'.
491 static char **split_str (char *str
)
493 char **res
, *lasts
, *s
;
496 for (i
= 2, s
= strchr(str
,','); s
; i
++)
499 res
= calloc(i
, sizeof(char*));
503 for (i
= 0, s
= strtok_r(str
, ",", &lasts
); s
;
504 s
= strtok_r(NULL
, ",", &lasts
), i
++)
510 * Unescape the LDAP-URL components
512 static bool unescape_elements (void *data
, LDAPURLDesc
*ludp
)
516 if (ludp
->lud_filter
) {
517 ludp
->lud_filter
= curl_easy_unescape(data
, ludp
->lud_filter
, 0, NULL
);
518 if (!ludp
->lud_filter
)
522 for (i
= 0; ludp
->lud_attrs
&& ludp
->lud_attrs
[i
]; i
++) {
523 ludp
->lud_attrs
[i
] = curl_easy_unescape(data
, ludp
->lud_attrs
[i
], 0, NULL
);
524 if (!ludp
->lud_attrs
[i
])
528 for (i
= 0; ludp
->lud_exts
&& ludp
->lud_exts
[i
]; i
++) {
529 ludp
->lud_exts
[i
] = curl_easy_unescape(data
, ludp
->lud_exts
[i
], 0, NULL
);
530 if (!ludp
->lud_exts
[i
])
535 char *dn
= ludp
->lud_dn
;
536 char *new_dn
= curl_easy_unescape(data
, dn
, 0, NULL
);
539 ludp
->lud_dn
= new_dn
;
547 * Break apart the pieces of an LDAP URL.
549 * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
551 * <hostname> already known from 'conn->host.name'.
552 * <port> already known from 'conn->remote_port'.
553 * extract the rest from 'conn->data->reqdata.path+1'. All fields are optional.
555 * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
556 * yields ludp->lud_dn = "".
558 * Ref. http://developer.netscape.com/docs/manuals/dirsdk/csdk30/url.htm#2831915
560 static int _ldap_url_parse2 (const struct connectdata
*conn
, LDAPURLDesc
*ludp
)
566 !conn
->data
->reqdata
.path
||
567 conn
->data
->reqdata
.path
[0] != '/' ||
568 !checkprefix(conn
->protostr
, conn
->data
->change
.url
))
569 return LDAP_INVALID_SYNTAX
;
571 ludp
->lud_scope
= LDAP_SCOPE_BASE
;
572 ludp
->lud_port
= conn
->remote_port
;
573 ludp
->lud_host
= conn
->host
.name
;
575 /* parse DN (Distinguished Name).
577 ludp
->lud_dn
= strdup(conn
->data
->reqdata
.path
+1);
579 return LDAP_NO_MEMORY
;
581 p
= strchr(ludp
->lud_dn
, '?');
582 LDAP_TRACE (("DN '%.*s'\n", p
? (size_t)(p
-ludp
->lud_dn
) :
583 strlen(ludp
->lud_dn
), ludp
->lud_dn
));
590 /* parse attributes. skip "??".
596 if (*p
&& *p
!= '?') {
597 ludp
->lud_attrs
= split_str(p
);
598 if (!ludp
->lud_attrs
)
599 return LDAP_NO_MEMORY
;
601 for (i
= 0; ludp
->lud_attrs
[i
]; i
++)
602 LDAP_TRACE (("attr[%d] '%s'\n", i
, ludp
->lud_attrs
[i
]));
609 /* parse scope. skip "??"
615 if (*p
&& *p
!= '?') {
616 ludp
->lud_scope
= str2scope(p
);
617 if (ludp
->lud_scope
== -1)
618 return LDAP_INVALID_SYNTAX
;
619 LDAP_TRACE (("scope %d\n", ludp
->lud_scope
));
632 return LDAP_INVALID_SYNTAX
;
634 ludp
->lud_filter
= p
;
635 LDAP_TRACE (("filter '%s'\n", ludp
->lud_filter
));
643 ludp
->lud_exts
= split_str(p
);
645 return LDAP_NO_MEMORY
;
647 for (i
= 0; ludp
->lud_exts
[i
]; i
++)
648 LDAP_TRACE (("exts[%d] '%s'\n", i
, ludp
->lud_exts
[i
]));
651 if (!unescape_elements(conn
->data
, ludp
))
652 return LDAP_NO_MEMORY
;
656 static int _ldap_url_parse (const struct connectdata
*conn
,
659 LDAPURLDesc
*ludp
= calloc(sizeof(*ludp
), 1);
664 return LDAP_NO_MEMORY
;
666 rc
= _ldap_url_parse2 (conn
, ludp
);
667 if (rc
!= LDAP_SUCCESS
) {
668 _ldap_free_urldesc(ludp
);
675 static void _ldap_free_urldesc (LDAPURLDesc
*ludp
)
685 if (ludp
->lud_filter
)
686 free(ludp
->lud_filter
);
688 if (ludp
->lud_attrs
) {
689 for (i
= 0; ludp
->lud_attrs
[i
]; i
++)
690 free(ludp
->lud_attrs
[i
]);
691 free(ludp
->lud_attrs
);
694 if (ludp
->lud_exts
) {
695 for (i
= 0; ludp
->lud_exts
[i
]; i
++)
696 free(ludp
->lud_exts
[i
]);
697 free(ludp
->lud_exts
);
702 #endif /* CURL_DISABLE_LDAP */