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