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