1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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 https://curl.se/docs/copyright.html.
13 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP)
28
29/*
30 * Notice that USE_OPENLDAP is only a source code selection switch. When
31 * libcurl is built with USE_OPENLDAP defined the libcurl source code that
32 * gets compiled is the code from openldap.c, otherwise the code that gets
33 * compiled is the code from ldap.c.
34 *
35 * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
36 * might be required for compilation and runtime. In order to use ancient
37 * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
38 */
39
40/* Wincrypt must be included before anything that could include OpenSSL. */
41#if defined(USE_WIN32_CRYPTO)
42#include <wincrypt.h>
43/* Undefine wincrypt conflicting symbols for BoringSSL. */
44#undef X509_NAME
45#undef X509_EXTENSIONS
46#undef PKCS7_ISSUER_AND_SERIAL
47#undef PKCS7_SIGNER_INFO
48#undef OCSP_REQUEST
49#undef OCSP_RESPONSE
50#endif
51
52#ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */
53# ifdef _MSC_VER
54# pragma warning(push)
55# pragma warning(disable: 4201)
56# endif
57# include <subauth.h> /* for [P]UNICODE_STRING */
58# ifdef _MSC_VER
59# pragma warning(pop)
60# endif
61# include <winldap.h>
62# ifndef LDAP_VENDOR_NAME
63# error Your Platform SDK is NOT sufficient for LDAP support! \
64 Update your Platform SDK, or disable LDAP support!
65# else
66# include <winber.h>
67# endif
68#else
69# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */
70# ifdef HAVE_LBER_H
71# include <lber.h>
72# endif
73# include <ldap.h>
74# if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H))
75# include <ldap_ssl.h>
76# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */
77#endif
78
79#include "urldata.h"
80#include <curl/curl.h>
81#include "sendf.h"
82#include "escape.h"
83#include "progress.h"
84#include "transfer.h"
85#include "strcase.h"
86#include "strtok.h"
87#include "curl_ldap.h"
88#include "curl_multibyte.h"
89#include "curl_base64.h"
90#include "connect.h"
91/* The last 3 #include files should be in this order */
92#include "curl_printf.h"
93#include "curl_memory.h"
94#include "memdebug.h"
95
96#ifndef HAVE_LDAP_URL_PARSE
97
98/* Use our own implementation. */
99
100struct ldap_urldesc {
101 char *lud_host;
102 int lud_port;
103#if defined(USE_WIN32_LDAP)
104 TCHAR *lud_dn;
105 TCHAR **lud_attrs;
106#else
107 char *lud_dn;
108 char **lud_attrs;
109#endif
110 int lud_scope;
111#if defined(USE_WIN32_LDAP)
112 TCHAR *lud_filter;
113#else
114 char *lud_filter;
115#endif
116 char **lud_exts;
117 size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the
118 "real" struct so can only be used in code
119 without HAVE_LDAP_URL_PARSE defined */
120};
121
122#undef LDAPURLDesc
123#define LDAPURLDesc struct ldap_urldesc
124
125static int _ldap_url_parse(struct Curl_easy *data,
126 const struct connectdata *conn,
127 LDAPURLDesc **ludp);
128static void _ldap_free_urldesc(LDAPURLDesc *ludp);
129
130#undef ldap_free_urldesc
131#define ldap_free_urldesc _ldap_free_urldesc
132#endif
133
134#ifdef DEBUG_LDAP
135 #define LDAP_TRACE(x) do { \
136 _ldap_trace("%u: ", __LINE__); \
137 _ldap_trace x; \
138 } while(0)
139
140 static void _ldap_trace(const char *fmt, ...);
141#else
142 #define LDAP_TRACE(x) Curl_nop_stmt
143#endif
144
145#if defined(USE_WIN32_LDAP) && defined(ldap_err2string)
146/* Use ansi error strings in UNICODE builds */
147#undef ldap_err2string
148#define ldap_err2string ldap_err2stringA
149#endif
150
151#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600)
152/* Workaround for warning:
153 'type cast' : conversion from 'int' to 'void *' of greater size */
154#undef LDAP_OPT_ON
155#undef LDAP_OPT_OFF
156#define LDAP_OPT_ON ((void *)(size_t)1)
157#define LDAP_OPT_OFF ((void *)(size_t)0)
158#endif
159
160static CURLcode ldap_do(struct Curl_easy *data, bool *done);
161
162/*
163 * LDAP protocol handler.
164 */
165
166const struct Curl_handler Curl_handler_ldap = {
167 "LDAP", /* scheme */
168 ZERO_NULL, /* setup_connection */
169 ldap_do, /* do_it */
170 ZERO_NULL, /* done */
171 ZERO_NULL, /* do_more */
172 ZERO_NULL, /* connect_it */
173 ZERO_NULL, /* connecting */
174 ZERO_NULL, /* doing */
175 ZERO_NULL, /* proto_getsock */
176 ZERO_NULL, /* doing_getsock */
177 ZERO_NULL, /* domore_getsock */
178 ZERO_NULL, /* perform_getsock */
179 ZERO_NULL, /* disconnect */
180 ZERO_NULL, /* readwrite */
181 ZERO_NULL, /* connection_check */
182 ZERO_NULL, /* attach connection */
183 PORT_LDAP, /* defport */
184 CURLPROTO_LDAP, /* protocol */
185 CURLPROTO_LDAP, /* family */
186 PROTOPT_NONE /* flags */
187};
188
189#ifdef HAVE_LDAP_SSL
190/*
191 * LDAPS protocol handler.
192 */
193
194const struct Curl_handler Curl_handler_ldaps = {
195 "LDAPS", /* scheme */
196 ZERO_NULL, /* setup_connection */
197 ldap_do, /* do_it */
198 ZERO_NULL, /* done */
199 ZERO_NULL, /* do_more */
200 ZERO_NULL, /* connect_it */
201 ZERO_NULL, /* connecting */
202 ZERO_NULL, /* doing */
203 ZERO_NULL, /* proto_getsock */
204 ZERO_NULL, /* doing_getsock */
205 ZERO_NULL, /* domore_getsock */
206 ZERO_NULL, /* perform_getsock */
207 ZERO_NULL, /* disconnect */
208 ZERO_NULL, /* readwrite */
209 ZERO_NULL, /* connection_check */
210 ZERO_NULL, /* attach connection */
211 PORT_LDAPS, /* defport */
212 CURLPROTO_LDAPS, /* protocol */
213 CURLPROTO_LDAP, /* family */
214 PROTOPT_SSL /* flags */
215};
216#endif
217
218#if defined(USE_WIN32_LDAP)
219
220#if defined(USE_WINDOWS_SSPI)
221static int ldap_win_bind_auth(LDAP *server, const char *user,
222 const char *passwd, unsigned long authflags)
223{
224 ULONG method = 0;
225 SEC_WINNT_AUTH_IDENTITY cred;
226 int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
227
228 memset(&cred, 0, sizeof(cred));
229
230#if defined(USE_SPNEGO)
231 if(authflags & CURLAUTH_NEGOTIATE) {
232 method = LDAP_AUTH_NEGOTIATE;
233 }
234 else
235#endif
236#if defined(USE_NTLM)
237 if(authflags & CURLAUTH_NTLM) {
238 method = LDAP_AUTH_NTLM;
239 }
240 else
241#endif
242#if !defined(CURL_DISABLE_DIGEST_AUTH)
243 if(authflags & CURLAUTH_DIGEST) {
244 method = LDAP_AUTH_DIGEST;
245 }
246 else
247#endif
248 {
249 /* required anyway if one of upper preprocessor definitions enabled */
250 }
251
252 if(method && user && passwd) {
253 rc = Curl_create_sspi_identity(user, passwd, &cred);
254 if(!rc) {
255 rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method);
256 Curl_sspi_free_identity(&cred);
257 }
258 }
259 else {
260 /* proceed with current user credentials */
261 method = LDAP_AUTH_NEGOTIATE;
262 rc = ldap_bind_s(server, NULL, NULL, method);
263 }
264 return rc;
265}
266#endif /* #if defined(USE_WINDOWS_SSPI) */
267
268static int ldap_win_bind(struct Curl_easy *data, LDAP *server,
269 const char *user, const char *passwd)
270{
271 int rc = LDAP_INVALID_CREDENTIALS;
272
273 PTCHAR inuser = NULL;
274 PTCHAR inpass = NULL;
275
276 if(user && passwd && (data->set.httpauth & CURLAUTH_BASIC)) {
277 inuser = curlx_convert_UTF8_to_tchar((char *) user);
278 inpass = curlx_convert_UTF8_to_tchar((char *) passwd);
279
280 rc = ldap_simple_bind_s(server, inuser, inpass);
281
282 curlx_unicodefree(inuser);
283 curlx_unicodefree(inpass);
284 }
285#if defined(USE_WINDOWS_SSPI)
286 else {
287 rc = ldap_win_bind_auth(server, user, passwd, data->set.httpauth);
288 }
289#endif
290
291 return rc;
292}
293#endif /* #if defined(USE_WIN32_LDAP) */
294
295#if defined(USE_WIN32_LDAP)
296#define FREE_ON_WINLDAP(x) curlx_unicodefree(x)
297#else
298#define FREE_ON_WINLDAP(x)
299#endif
300
301
302static CURLcode ldap_do(struct Curl_easy *data, bool *done)
303{
304 CURLcode result = CURLE_OK;
305 int rc = 0;
306 LDAP *server = NULL;
307 LDAPURLDesc *ludp = NULL;
308 LDAPMessage *ldapmsg = NULL;
309 LDAPMessage *entryIterator;
310 int num = 0;
311 struct connectdata *conn = data->conn;
312 int ldap_proto = LDAP_VERSION3;
313 int ldap_ssl = 0;
314 char *val_b64 = NULL;
315 size_t val_b64_sz = 0;
316 curl_off_t dlsize = 0;
317#ifdef LDAP_OPT_NETWORK_TIMEOUT
318 struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
319#endif
320#if defined(USE_WIN32_LDAP)
321 TCHAR *host = NULL;
322#else
323 char *host = NULL;
324#endif
325 char *user = NULL;
326 char *passwd = NULL;
327
328 *done = TRUE; /* unconditionally */
329 infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
330 LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
331 infof(data, "LDAP local: %s", data->state.url);
332
333#ifdef HAVE_LDAP_URL_PARSE
334 rc = ldap_url_parse(data->state.url, &ludp);
335#else
336 rc = _ldap_url_parse(data, conn, &ludp);
337#endif
338 if(rc) {
339 failf(data, "Bad LDAP URL: %s", ldap_err2string(rc));
340 result = CURLE_URL_MALFORMAT;
341 goto quit;
342 }
343
344 /* Get the URL scheme (either ldap or ldaps) */
345 if(conn->given->flags & PROTOPT_SSL)
346 ldap_ssl = 1;
347 infof(data, "LDAP local: trying to establish %s connection",
348 ldap_ssl ? "encrypted" : "cleartext");
349
350#if defined(USE_WIN32_LDAP)
351 host = curlx_convert_UTF8_to_tchar(conn->host.name);
352 if(!host) {
353 result = CURLE_OUT_OF_MEMORY;
354
355 goto quit;
356 }
357#else
358 host = conn->host.name;
359#endif
360
361 if(data->state.aptr.user) {
362 user = conn->user;
363 passwd = conn->passwd;
364 }
365
366#ifdef LDAP_OPT_NETWORK_TIMEOUT
367 ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
368#endif
369 ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
370
371 if(ldap_ssl) {
372#ifdef HAVE_LDAP_SSL
373#ifdef USE_WIN32_LDAP
374 /* Win32 LDAP SDK doesn't support insecure mode without CA! */
375 server = ldap_sslinit(host, conn->port, 1);
376 ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
377#else
378 int ldap_option;
379 char *ldap_ca = conn->ssl_config.CAfile;
380#if defined(CURL_HAS_NOVELL_LDAPSDK)
381 rc = ldapssl_client_init(NULL, NULL);
382 if(rc != LDAP_SUCCESS) {
383 failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
384 result = CURLE_SSL_CERTPROBLEM;
385 goto quit;
386 }
387 if(conn->ssl_config.verifypeer) {
388 /* Novell SDK supports DER or BASE64 files. */
389 int cert_type = LDAPSSL_CERT_FILETYPE_B64;
390 if((data->set.ssl.cert_type) &&
391 (strcasecompare(data->set.ssl.cert_type, "DER")))
392 cert_type = LDAPSSL_CERT_FILETYPE_DER;
393 if(!ldap_ca) {
394 failf(data, "LDAP local: ERROR %s CA cert not set",
395 (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
396 result = CURLE_SSL_CERTPROBLEM;
397 goto quit;
398 }
399 infof(data, "LDAP local: using %s CA cert '%s'",
400 (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
401 ldap_ca);
402 rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
403 if(rc != LDAP_SUCCESS) {
404 failf(data, "LDAP local: ERROR setting %s CA cert: %s",
405 (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
406 ldap_err2string(rc));
407 result = CURLE_SSL_CERTPROBLEM;
408 goto quit;
409 }
410 ldap_option = LDAPSSL_VERIFY_SERVER;
411 }
412 else
413 ldap_option = LDAPSSL_VERIFY_NONE;
414 rc = ldapssl_set_verify_mode(ldap_option);
415 if(rc != LDAP_SUCCESS) {
416 failf(data, "LDAP local: ERROR setting cert verify mode: %s",
417 ldap_err2string(rc));
418 result = CURLE_SSL_CERTPROBLEM;
419 goto quit;
420 }
421 server = ldapssl_init(host, conn->port, 1);
422 if(!server) {
423 failf(data, "LDAP local: Cannot connect to %s:%u",
424 conn->host.dispname, conn->port);
425 result = CURLE_COULDNT_CONNECT;
426 goto quit;
427 }
428#elif defined(LDAP_OPT_X_TLS)
429 if(conn->ssl_config.verifypeer) {
430 /* OpenLDAP SDK supports BASE64 files. */
431 if((data->set.ssl.cert_type) &&
432 (!strcasecompare(data->set.ssl.cert_type, "PEM"))) {
433 failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type");
434 result = CURLE_SSL_CERTPROBLEM;
435 goto quit;
436 }
437 if(!ldap_ca) {
438 failf(data, "LDAP local: ERROR PEM CA cert not set");
439 result = CURLE_SSL_CERTPROBLEM;
440 goto quit;
441 }
442 infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca);
443 rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
444 if(rc != LDAP_SUCCESS) {
445 failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
446 ldap_err2string(rc));
447 result = CURLE_SSL_CERTPROBLEM;
448 goto quit;
449 }
450 ldap_option = LDAP_OPT_X_TLS_DEMAND;
451 }
452 else
453 ldap_option = LDAP_OPT_X_TLS_NEVER;
454
455 rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
456 if(rc != LDAP_SUCCESS) {
457 failf(data, "LDAP local: ERROR setting cert verify mode: %s",
458 ldap_err2string(rc));
459 result = CURLE_SSL_CERTPROBLEM;
460 goto quit;
461 }
462 server = ldap_init(host, conn->port);
463 if(!server) {
464 failf(data, "LDAP local: Cannot connect to %s:%u",
465 conn->host.dispname, conn->port);
466 result = CURLE_COULDNT_CONNECT;
467 goto quit;
468 }
469 ldap_option = LDAP_OPT_X_TLS_HARD;
470 rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
471 if(rc != LDAP_SUCCESS) {
472 failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
473 ldap_err2string(rc));
474 result = CURLE_SSL_CERTPROBLEM;
475 goto quit;
476 }
477/*
478 rc = ldap_start_tls_s(server, NULL, NULL);
479 if(rc != LDAP_SUCCESS) {
480 failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
481 ldap_err2string(rc));
482 result = CURLE_SSL_CERTPROBLEM;
483 goto quit;
484 }
485*/
486#else
487 /* we should probably never come up to here since configure
488 should check in first place if we can support LDAP SSL/TLS */
489 failf(data, "LDAP local: SSL/TLS not supported with this version "
490 "of the OpenLDAP toolkit\n");
491 result = CURLE_SSL_CERTPROBLEM;
492 goto quit;
493#endif
494#endif
495#endif /* CURL_LDAP_USE_SSL */
496 }
497 else if(data->set.use_ssl > CURLUSESSL_TRY) {
498 failf(data, "LDAP local: explicit TLS not supported");
499 result = CURLE_NOT_BUILT_IN;
500 goto quit;
501 }
502 else {
503 server = ldap_init(host, conn->port);
504 if(!server) {
505 failf(data, "LDAP local: Cannot connect to %s:%u",
506 conn->host.dispname, conn->port);
507 result = CURLE_COULDNT_CONNECT;
508 goto quit;
509 }
510 }
511#ifdef USE_WIN32_LDAP
512 ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
513 rc = ldap_win_bind(data, server, user, passwd);
514#else
515 rc = ldap_simple_bind_s(server, user, passwd);
516#endif
517 if(!ldap_ssl && rc) {
518 ldap_proto = LDAP_VERSION2;
519 ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
520#ifdef USE_WIN32_LDAP
521 rc = ldap_win_bind(data, server, user, passwd);
522#else
523 rc = ldap_simple_bind_s(server, user, passwd);
524#endif
525 }
526 if(rc) {
527#ifdef USE_WIN32_LDAP
528 failf(data, "LDAP local: bind via ldap_win_bind %s",
529 ldap_err2string(rc));
530#else
531 failf(data, "LDAP local: bind via ldap_simple_bind_s %s",
532 ldap_err2string(rc));
533#endif
534 result = CURLE_LDAP_CANNOT_BIND;
535 goto quit;
536 }
537
538 rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
539 ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
540
541 if(rc && rc != LDAP_SIZELIMIT_EXCEEDED) {
542 failf(data, "LDAP remote: %s", ldap_err2string(rc));
543 result = CURLE_LDAP_SEARCH_FAILED;
544 goto quit;
545 }
546
547 for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg);
548 entryIterator;
549 entryIterator = ldap_next_entry(server, entryIterator), num++) {
550 BerElement *ber = NULL;
551#if defined(USE_WIN32_LDAP)
552 TCHAR *attribute;
553#else
554 char *attribute;
555#endif
556 int i;
557
558 /* Get the DN and write it to the client */
559 {
560 char *name;
561 size_t name_len;
562#if defined(USE_WIN32_LDAP)
563 TCHAR *dn = ldap_get_dn(server, entryIterator);
564 name = curlx_convert_tchar_to_UTF8(dn);
565 if(!name) {
566 ldap_memfree(dn);
567
568 result = CURLE_OUT_OF_MEMORY;
569
570 goto quit;
571 }
572#else
573 char *dn = name = ldap_get_dn(server, entryIterator);
574#endif
575 name_len = strlen(name);
576
577 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4);
578 if(result) {
579 FREE_ON_WINLDAP(name);
580 ldap_memfree(dn);
581 goto quit;
582 }
583
584 result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len);
585 if(result) {
586 FREE_ON_WINLDAP(name);
587 ldap_memfree(dn);
588 goto quit;
589 }
590
591 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
592 if(result) {
593 FREE_ON_WINLDAP(name);
594 ldap_memfree(dn);
595
596 goto quit;
597 }
598
599 dlsize += name_len + 5;
600
601 FREE_ON_WINLDAP(name);
602 ldap_memfree(dn);
603 }
604
605 /* Get the attributes and write them to the client */
606 for(attribute = ldap_first_attribute(server, entryIterator, &ber);
607 attribute;
608 attribute = ldap_next_attribute(server, entryIterator, ber)) {
609 BerValue **vals;
610 size_t attr_len;
611#if defined(USE_WIN32_LDAP)
612 char *attr = curlx_convert_tchar_to_UTF8(attribute);
613 if(!attr) {
614 if(ber)
615 ber_free(ber, 0);
616
617 result = CURLE_OUT_OF_MEMORY;
618
619 goto quit;
620 }
621#else
622 char *attr = attribute;
623#endif
624 attr_len = strlen(attr);
625
626 vals = ldap_get_values_len(server, entryIterator, attribute);
627 if(vals) {
628 for(i = 0; (vals[i] != NULL); i++) {
629 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
630 if(result) {
631 ldap_value_free_len(vals);
632 FREE_ON_WINLDAP(attr);
633 ldap_memfree(attribute);
634 if(ber)
635 ber_free(ber, 0);
636
637 goto quit;
638 }
639
640 result = Curl_client_write(data, CLIENTWRITE_BODY, attr, attr_len);
641 if(result) {
642 ldap_value_free_len(vals);
643 FREE_ON_WINLDAP(attr);
644 ldap_memfree(attribute);
645 if(ber)
646 ber_free(ber, 0);
647
648 goto quit;
649 }
650
651 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2);
652 if(result) {
653 ldap_value_free_len(vals);
654 FREE_ON_WINLDAP(attr);
655 ldap_memfree(attribute);
656 if(ber)
657 ber_free(ber, 0);
658
659 goto quit;
660 }
661
662 dlsize += attr_len + 3;
663
664 if((attr_len > 7) &&
665 (strcmp(";binary", attr + (attr_len - 7)) == 0)) {
666 /* Binary attribute, encode to base64. */
667 result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
668 &val_b64, &val_b64_sz);
669 if(result) {
670 ldap_value_free_len(vals);
671 FREE_ON_WINLDAP(attr);
672 ldap_memfree(attribute);
673 if(ber)
674 ber_free(ber, 0);
675
676 goto quit;
677 }
678
679 if(val_b64_sz > 0) {
680 result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64,
681 val_b64_sz);
682 free(val_b64);
683 if(result) {
684 ldap_value_free_len(vals);
685 FREE_ON_WINLDAP(attr);
686 ldap_memfree(attribute);
687 if(ber)
688 ber_free(ber, 0);
689
690 goto quit;
691 }
692
693 dlsize += val_b64_sz;
694 }
695 }
696 else {
697 result = Curl_client_write(data, CLIENTWRITE_BODY, vals[i]->bv_val,
698 vals[i]->bv_len);
699 if(result) {
700 ldap_value_free_len(vals);
701 FREE_ON_WINLDAP(attr);
702 ldap_memfree(attribute);
703 if(ber)
704 ber_free(ber, 0);
705
706 goto quit;
707 }
708
709 dlsize += vals[i]->bv_len;
710 }
711
712 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
713 if(result) {
714 ldap_value_free_len(vals);
715 FREE_ON_WINLDAP(attr);
716 ldap_memfree(attribute);
717 if(ber)
718 ber_free(ber, 0);
719
720 goto quit;
721 }
722
723 dlsize++;
724 }
725
726 /* Free memory used to store values */
727 ldap_value_free_len(vals);
728 }
729
730 /* Free the attribute as we are done with it */
731 FREE_ON_WINLDAP(attr);
732 ldap_memfree(attribute);
733
734 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
735 if(result)
736 goto quit;
737 dlsize++;
738 result = Curl_pgrsSetDownloadCounter(data, dlsize);
739 if(result)
740 goto quit;
741 }
742
743 if(ber)
744 ber_free(ber, 0);
745 }
746
747quit:
748 if(ldapmsg) {
749 ldap_msgfree(ldapmsg);
750 LDAP_TRACE(("Received %d entries\n", num));
751 }
752 if(rc == LDAP_SIZELIMIT_EXCEEDED)
753 infof(data, "There are more than %d entries", num);
754 if(ludp)
755 ldap_free_urldesc(ludp);
756 if(server)
757 ldap_unbind_s(server);
758#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
759 if(ldap_ssl)
760 ldapssl_client_deinit();
761#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
762
763 FREE_ON_WINLDAP(host);
764
765 /* no data to transfer */
766 Curl_setup_transfer(data, -1, -1, FALSE, -1);
767 connclose(conn, "LDAP connection always disable reuse");
768
769 return result;
770}
771
772#ifdef DEBUG_LDAP
773static void _ldap_trace(const char *fmt, ...)
774{
775 static int do_trace = -1;
776 va_list args;
777
778 if(do_trace == -1) {
779 const char *env = getenv("CURL_TRACE");
780 do_trace = (env && strtol(env, NULL, 10) > 0);
781 }
782 if(!do_trace)
783 return;
784
785 va_start(args, fmt);
786 vfprintf(stderr, fmt, args);
787 va_end(args);
788}
789#endif
790
791#ifndef HAVE_LDAP_URL_PARSE
792
793/*
794 * Return scope-value for a scope-string.
795 */
796static int str2scope(const char *p)
797{
798 if(strcasecompare(p, "one"))
799 return LDAP_SCOPE_ONELEVEL;
800 if(strcasecompare(p, "onetree"))
801 return LDAP_SCOPE_ONELEVEL;
802 if(strcasecompare(p, "base"))
803 return LDAP_SCOPE_BASE;
804 if(strcasecompare(p, "sub"))
805 return LDAP_SCOPE_SUBTREE;
806 if(strcasecompare(p, "subtree"))
807 return LDAP_SCOPE_SUBTREE;
808 return (-1);
809}
810
811/*
812 * Split 'str' into strings separated by commas.
813 * Note: out[] points into 'str'.
814 */
815static bool split_str(char *str, char ***out, size_t *count)
816{
817 char **res;
818 char *lasts;
819 char *s;
820 size_t i;
821 size_t items = 1;
822
823 s = strchr(str, ',');
824 while(s) {
825 items++;
826 s = strchr(++s, ',');
827 }
828
829 res = calloc(items, sizeof(char *));
830 if(!res)
831 return FALSE;
832
833 for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items;
834 s = strtok_r(NULL, ",", &lasts), i++)
835 res[i] = s;
836
837 *out = res;
838 *count = items;
839
840 return TRUE;
841}
842
843/*
844 * Break apart the pieces of an LDAP URL.
845 * Syntax:
846 * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
847 *
848 * <hostname> already known from 'conn->host.name'.
849 * <port> already known from 'conn->remote_port'.
850 * extract the rest from 'data->state.path+1'. All fields are optional.
851 * e.g.
852 * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
853 * yields ludp->lud_dn = "".
854 *
855 * Defined in RFC4516 section 2.
856 */
857static int _ldap_url_parse2(struct Curl_easy *data,
858 const struct connectdata *conn, LDAPURLDesc *ludp)
859{
860 int rc = LDAP_SUCCESS;
861 char *p;
862 char *path;
863 char *q = NULL;
864 char *query = NULL;
865 size_t i;
866
867 if(!data ||
868 !data->state.up.path ||
869 data->state.up.path[0] != '/' ||
870 !strncasecompare("LDAP", data->state.up.scheme, 4))
871 return LDAP_INVALID_SYNTAX;
872
873 ludp->lud_scope = LDAP_SCOPE_BASE;
874 ludp->lud_port = conn->remote_port;
875 ludp->lud_host = conn->host.name;
876
877 /* Duplicate the path */
878 p = path = strdup(data->state.up.path + 1);
879 if(!path)
880 return LDAP_NO_MEMORY;
881
882 /* Duplicate the query if present */
883 if(data->state.up.query) {
884 q = query = strdup(data->state.up.query);
885 if(!query) {
886 free(path);
887 return LDAP_NO_MEMORY;
888 }
889 }
890
891 /* Parse the DN (Distinguished Name) */
892 if(*p) {
893 char *dn = p;
894 char *unescaped;
895 CURLcode result;
896
897 LDAP_TRACE(("DN '%s'\n", dn));
898
899 /* Unescape the DN */
900 result = Curl_urldecode(dn, 0, &unescaped, NULL, REJECT_ZERO);
901 if(result) {
902 rc = LDAP_NO_MEMORY;
903
904 goto quit;
905 }
906
907#if defined(USE_WIN32_LDAP)
908 /* Convert the unescaped string to a tchar */
909 ludp->lud_dn = curlx_convert_UTF8_to_tchar(unescaped);
910
911 /* Free the unescaped string as we are done with it */
912 free(unescaped);
913
914 if(!ludp->lud_dn) {
915 rc = LDAP_NO_MEMORY;
916
917 goto quit;
918 }
919#else
920 ludp->lud_dn = unescaped;
921#endif
922 }
923
924 p = q;
925 if(!p)
926 goto quit;
927
928 /* Parse the attributes. skip "??" */
929 q = strchr(p, '?');
930 if(q)
931 *q++ = '\0';
932
933 if(*p) {
934 char **attributes;
935 size_t count = 0;
936
937 /* Split the string into an array of attributes */
938 if(!split_str(p, &attributes, &count)) {
939 rc = LDAP_NO_MEMORY;
940
941 goto quit;
942 }
943
944 /* Allocate our array (+1 for the NULL entry) */
945#if defined(USE_WIN32_LDAP)
946 ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *));
947#else
948 ludp->lud_attrs = calloc(count + 1, sizeof(char *));
949#endif
950 if(!ludp->lud_attrs) {
951 free(attributes);
952
953 rc = LDAP_NO_MEMORY;
954
955 goto quit;
956 }
957
958 for(i = 0; i < count; i++) {
959 char *unescaped;
960 CURLcode result;
961
962 LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i]));
963
964 /* Unescape the attribute */
965 result = Curl_urldecode(attributes[i], 0, &unescaped, NULL,
966 REJECT_ZERO);
967 if(result) {
968 free(attributes);
969
970 rc = LDAP_NO_MEMORY;
971
972 goto quit;
973 }
974
975#if defined(USE_WIN32_LDAP)
976 /* Convert the unescaped string to a tchar */
977 ludp->lud_attrs[i] = curlx_convert_UTF8_to_tchar(unescaped);
978
979 /* Free the unescaped string as we are done with it */
980 free(unescaped);
981
982 if(!ludp->lud_attrs[i]) {
983 free(attributes);
984
985 rc = LDAP_NO_MEMORY;
986
987 goto quit;
988 }
989#else
990 ludp->lud_attrs[i] = unescaped;
991#endif
992
993 ludp->lud_attrs_dups++;
994 }
995
996 free(attributes);
997 }
998
999 p = q;
1000 if(!p)
1001 goto quit;
1002
1003 /* Parse the scope. skip "??" */
1004 q = strchr(p, '?');
1005 if(q)
1006 *q++ = '\0';
1007
1008 if(*p) {
1009 ludp->lud_scope = str2scope(p);
1010 if(ludp->lud_scope == -1) {
1011 rc = LDAP_INVALID_SYNTAX;
1012
1013 goto quit;
1014 }
1015 LDAP_TRACE(("scope %d\n", ludp->lud_scope));
1016 }
1017
1018 p = q;
1019 if(!p)
1020 goto quit;
1021
1022 /* Parse the filter */
1023 q = strchr(p, '?');
1024 if(q)
1025 *q++ = '\0';
1026
1027 if(*p) {
1028 char *filter = p;
1029 char *unescaped;
1030 CURLcode result;
1031
1032 LDAP_TRACE(("filter '%s'\n", filter));
1033
1034 /* Unescape the filter */
1035 result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_ZERO);
1036 if(result) {
1037 rc = LDAP_NO_MEMORY;
1038
1039 goto quit;
1040 }
1041
1042#if defined(USE_WIN32_LDAP)
1043 /* Convert the unescaped string to a tchar */
1044 ludp->lud_filter = curlx_convert_UTF8_to_tchar(unescaped);
1045
1046 /* Free the unescaped string as we are done with it */
1047 free(unescaped);
1048
1049 if(!ludp->lud_filter) {
1050 rc = LDAP_NO_MEMORY;
1051
1052 goto quit;
1053 }
1054#else
1055 ludp->lud_filter = unescaped;
1056#endif
1057 }
1058
1059 p = q;
1060 if(p && !*p) {
1061 rc = LDAP_INVALID_SYNTAX;
1062
1063 goto quit;
1064 }
1065
1066quit:
1067 free(path);
1068 free(query);
1069
1070 return rc;
1071}
1072
1073static int _ldap_url_parse(struct Curl_easy *data,
1074 const struct connectdata *conn,
1075 LDAPURLDesc **ludpp)
1076{
1077 LDAPURLDesc *ludp = calloc(1, sizeof(*ludp));
1078 int rc;
1079
1080 *ludpp = NULL;
1081 if(!ludp)
1082 return LDAP_NO_MEMORY;
1083
1084 rc = _ldap_url_parse2(data, conn, ludp);
1085 if(rc != LDAP_SUCCESS) {
1086 _ldap_free_urldesc(ludp);
1087 ludp = NULL;
1088 }
1089 *ludpp = ludp;
1090 return (rc);
1091}
1092
1093static void _ldap_free_urldesc(LDAPURLDesc *ludp)
1094{
1095 if(!ludp)
1096 return;
1097
1098#if defined(USE_WIN32_LDAP)
1099 curlx_unicodefree(ludp->lud_dn);
1100 curlx_unicodefree(ludp->lud_filter);
1101#else
1102 free(ludp->lud_dn);
1103 free(ludp->lud_filter);
1104#endif
1105
1106 if(ludp->lud_attrs) {
1107 size_t i;
1108 for(i = 0; i < ludp->lud_attrs_dups; i++) {
1109#if defined(USE_WIN32_LDAP)
1110 curlx_unicodefree(ludp->lud_attrs[i]);
1111#else
1112 free(ludp->lud_attrs[i]);
1113#endif
1114 }
1115 free(ludp->lud_attrs);
1116 }
1117
1118 free(ludp);
1119}
1120#endif /* !HAVE_LDAP_URL_PARSE */
1121#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */
1122