VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/ldap.c@ 99371

Last change on this file since 99371 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette