• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 # 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 
92 struct 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 
117 static int  _ldap_url_parse(struct Curl_easy *data,
118                             const struct connectdata *conn,
119                             LDAPURLDesc **ludp);
120 static 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 
152 static CURLcode ldap_do(struct Curl_easy *data, bool *done);
153 
154 /*
155  * LDAP protocol handler.
156  */
157 
158 const 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 
186 const 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)
ldap_win_bind_auth(LDAP * server,const char * user,const char * passwd,unsigned long authflags)213 static 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 
ldap_win_bind(struct Curl_easy * data,LDAP * server,const char * user,const char * passwd)260 static 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 
ldap_do(struct Curl_easy * data,bool * done)294 static 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 
737 quit:
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
_ldap_trace(const char * fmt,...)763 static 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  */
str2scope(const char * p)786 static 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  */
split_str(char * str,char *** out,size_t * count)805 static 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  */
_ldap_url_parse2(struct Curl_easy * data,const struct connectdata * conn,LDAPURLDesc * ludp)847 static 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 
1056 quit:
1057   free(path);
1058   free(query);
1059 
1060   return rc;
1061 }
1062 
_ldap_url_parse(struct Curl_easy * data,const struct connectdata * conn,LDAPURLDesc ** ludpp)1063 static 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 
_ldap_free_urldesc(LDAPURLDesc * ludp)1083 static 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 */
1112