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