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