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