3479bd912378313ae0ae6df04d11b74f74d5d615
[platform/upstream/curl.git] / lib / ldap.c
1 /***************************************************************************
2  *                      _   _ ____  _
3  *  Project         ___| | | |  _ \| |
4  *                 / __| | | | |_) | |
5  *                | (__| |_| |  _ <| |___
6  *                \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2005, 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 http://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  * $Id$
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #ifndef CURL_DISABLE_LDAP
27 /* -- WIN32 approved -- */
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36 #ifdef HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39 #ifdef NEED_MALLOC_H
40 #include <malloc.h>
41 #endif
42 #include <errno.h>
43
44 #if defined(WIN32)
45 # include <winldap.h>
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif
51
52 #ifdef HAVE_DLFCN_H
53 # include <dlfcn.h>
54 #endif
55
56 #include "urldata.h"
57 #include <curl/curl.h>
58 #include "sendf.h"
59 #include "escape.h"
60 #include "transfer.h"
61 #include "strequal.h"
62 #include "strtok.h"
63 #include "ldap.h"
64 #include "memory.h"
65 #include "base64.h"
66
67 #define _MPRINTF_REPLACE /* use our functions only */
68 #include <curl/mprintf.h>
69
70 #include "memdebug.h"
71
72 /* WLdap32.dll functions are *not* stdcall. Must call these via __cdecl
73  * pointers in case libcurl was compiled as fastcall (cl -Gr). Watcom
74  * uses fastcall by default.
75  */
76 #if !defined(WIN32) && !defined(__cdecl)
77 #define __cdecl
78 #endif
79
80 #ifndef LDAP_SIZELIMIT_EXCEEDED
81 #define LDAP_SIZELIMIT_EXCEEDED 4
82 #endif
83 #ifndef LDAP_VERSION2
84 #define LDAP_VERSION2 2
85 #endif
86 #ifndef LDAP_VERSION3
87 #define LDAP_VERSION3 3
88 #endif
89 #ifndef LDAP_OPT_PROTOCOL_VERSION
90 #define LDAP_OPT_PROTOCOL_VERSION 0x0011
91 #endif
92
93 #define DLOPEN_MODE   RTLD_LAZY  /*! assume all dlopen() implementations have
94                                    this */
95
96 #if defined(RTLD_LAZY_GLOBAL)    /* It turns out some systems use this: */
97 # undef  DLOPEN_MODE
98 # define DLOPEN_MODE  RTLD_LAZY_GLOBAL
99 #elif defined(RTLD_GLOBAL)
100 # undef  DLOPEN_MODE
101 # define DLOPEN_MODE  (RTLD_LAZY | RTLD_GLOBAL)
102 #endif
103
104 #define DYNA_GET_FUNCTION(type, fnc) do { \
105           (fnc) = (type)DynaGetFunction(#fnc); \
106           if ((fnc) == NULL) \
107              return CURLE_FUNCTION_NOT_FOUND; \
108         } while (0)
109
110 /*! CygWin etc. configure could set these, but we don't want it.
111  * Must use WLdap32.dll code.
112  */
113 #if defined(WIN32)
114 #undef HAVE_DLOPEN
115 #undef HAVE_LIBDL
116 #endif
117
118 typedef void * (*dynafunc)(void *input);
119
120 /***********************************************************************
121  */
122 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL) || defined(WIN32)
123 static void *libldap = NULL;
124 #if defined(DL_LBER_FILE)
125 static void *liblber = NULL;
126 #endif
127 #endif
128
129 struct bv {
130   unsigned long bv_len;
131   char  *bv_val;
132 };
133
134 static int DynaOpen(const char **mod_name)
135 {
136 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
137   if (libldap == NULL) {
138     /*
139      * libldap.so can normally resolve its dependency on liblber.so
140      * automatically, but in broken installation it does not so
141      * handle it here by opening liblber.so as global.
142      */
143 #ifdef DL_LBER_FILE
144     *mod_name = DL_LBER_FILE;
145     liblber = dlopen(*mod_name, DLOPEN_MODE);
146     if (!liblber)
147       return 0;
148 #endif
149
150     /* Assume loading libldap.so will fail if loading of liblber.so failed
151      */
152     *mod_name = DL_LDAP_FILE;
153     libldap = dlopen(*mod_name, RTLD_LAZY);
154   }
155   return (libldap != NULL);
156
157 #elif defined(WIN32)
158   *mod_name = DL_LDAP_FILE;
159   if (!libldap)
160     libldap = (void*)LoadLibrary(*mod_name);
161   return (libldap != NULL);
162
163 #else
164   *mod_name = "";
165   return (0);
166 #endif
167 }
168
169 static void DynaClose(void)
170 {
171 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
172   if (libldap) {
173     dlclose(libldap);
174     libldap=NULL;
175   }
176 #ifdef DL_LBER_FILE
177   if (liblber) {
178     dlclose(liblber);
179     liblber=NULL;
180   }
181 #endif
182 #elif defined(WIN32)
183   if (libldap) {
184     FreeLibrary ((HMODULE)libldap);
185     libldap = NULL;
186   }
187 #endif
188 }
189
190 static dynafunc DynaGetFunction(const char *name)
191 {
192   dynafunc func = (void *(*)(void *))NULL;
193
194 #if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
195   if (libldap) {
196     /* This typecast magic below was brought by Joe Halpin. In ISO C, you
197      * cannot typecast a data pointer to a function pointer, but that's
198      * exactly what we need to do here to avoid compiler warnings on picky
199      * compilers! */
200     *(void**) (&func) = dlsym(libldap, name);
201   }
202 #elif defined(WIN32)
203   if (libldap) {
204     func = (dynafunc)GetProcAddress((HINSTANCE)libldap, name);
205   }
206 #else
207   (void) name;
208 #endif
209   return func;
210 }
211
212 /***********************************************************************
213  */
214 typedef struct ldap_url_desc {
215     struct ldap_url_desc *lud_next;
216     char   *lud_scheme;
217     char   *lud_host;
218     int     lud_port;
219     char   *lud_dn;
220     char  **lud_attrs;
221     int     lud_scope;
222     char   *lud_filter;
223     char  **lud_exts;
224     int     lud_crit_exts;
225 } LDAPURLDesc;
226
227 #ifdef WIN32
228 static int  _ldap_url_parse (const struct connectdata *conn,
229                              LDAPURLDesc **ludp);
230 static void _ldap_free_urldesc (LDAPURLDesc *ludp);
231
232 static void (*ldap_free_urldesc)(LDAPURLDesc *) = _ldap_free_urldesc;
233 #endif
234
235 #ifdef DEBUG_LDAP
236   #define LDAP_TRACE(x)   do { \
237                             _ldap_trace ("%u: ", __LINE__); \
238                             _ldap_trace x; \
239                           } while (0)
240
241   static void _ldap_trace (const char *fmt, ...);
242 #else
243   #define LDAP_TRACE(x)   ((void)0)
244 #endif
245
246
247 CURLcode Curl_ldap(struct connectdata *conn, bool *done)
248 {
249   CURLcode status = CURLE_OK;
250   int rc = 0;
251 #ifndef WIN32
252   int    (*ldap_url_parse)(char *, LDAPURLDesc **);
253   void   (*ldap_free_urldesc)(void *);
254 #endif
255   void  *(__cdecl *ldap_init)(char *, int);
256   int    (__cdecl *ldap_simple_bind_s)(void *, char *, char *);
257   int    (__cdecl *ldap_unbind_s)(void *);
258   int    (__cdecl *ldap_search_s)(void *, char *, int, char *, char **,
259                                   int, void **);
260   void  *(__cdecl *ldap_first_entry)(void *, void *);
261   void  *(__cdecl *ldap_next_entry)(void *, void *);
262   char  *(__cdecl *ldap_err2string)(int);
263   char  *(__cdecl *ldap_get_dn)(void *, void *);
264   char  *(__cdecl *ldap_first_attribute)(void *, void *, void **);
265   char  *(__cdecl *ldap_next_attribute)(void *, void *, void *);
266   void **(__cdecl *ldap_get_values_len)(void *, void *, const char *);
267   void   (__cdecl *ldap_value_free_len)(void **);
268   void   (__cdecl *ldap_memfree)(void *);
269   void   (__cdecl *ber_free)(void *, int);
270   int    (__cdecl *ldap_set_option)(void *, int, void *);
271
272   void *server;
273   LDAPURLDesc *ludp = NULL;
274   const char *mod_name;
275   void *result;
276   void *entryIterator;     /*! type should be 'LDAPMessage *' */
277   int num = 0;
278   struct SessionHandle *data=conn->data;
279   int ldap_proto;
280   char *val_b64;
281   size_t val_b64_sz;
282
283   *done = TRUE; /* unconditionally */
284   infof(data, "LDAP local: %s\n", data->change.url);
285
286   if (!DynaOpen(&mod_name)) {
287     failf(data, "The %s LDAP library/libraries couldn't be opened", mod_name);
288     return CURLE_LIBRARY_NOT_FOUND;
289   }
290
291   /* The types are needed because ANSI C distinguishes between
292    * pointer-to-object (data) and pointer-to-function.
293    */
294   DYNA_GET_FUNCTION(void *(__cdecl *)(char *, int), ldap_init);
295   DYNA_GET_FUNCTION(int (__cdecl *)(void *, char *, char *),
296                     ldap_simple_bind_s);
297   DYNA_GET_FUNCTION(int (__cdecl *)(void *), ldap_unbind_s);
298 #ifndef WIN32
299   DYNA_GET_FUNCTION(int (*)(char *, LDAPURLDesc **), ldap_url_parse);
300   DYNA_GET_FUNCTION(void (*)(void *), ldap_free_urldesc);
301 #endif
302   DYNA_GET_FUNCTION(int (__cdecl *)(void *, char *, int, char *, char **, int,
303                                     void **), ldap_search_s);
304   DYNA_GET_FUNCTION(void *(__cdecl *)(void *, void *), ldap_first_entry);
305   DYNA_GET_FUNCTION(void *(__cdecl *)(void *, void *), ldap_next_entry);
306   DYNA_GET_FUNCTION(char *(__cdecl *)(int), ldap_err2string);
307   DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *), ldap_get_dn);
308   DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *, void **),
309                     ldap_first_attribute);
310   DYNA_GET_FUNCTION(char *(__cdecl *)(void *, void *, void *),
311                     ldap_next_attribute);
312   DYNA_GET_FUNCTION(void **(__cdecl *)(void *, void *, const char *),
313                     ldap_get_values_len);
314   DYNA_GET_FUNCTION(void (__cdecl *)(void **), ldap_value_free_len);
315   DYNA_GET_FUNCTION(void (__cdecl *)(void *), ldap_memfree);
316   DYNA_GET_FUNCTION(void (__cdecl *)(void *, int), ber_free);
317   DYNA_GET_FUNCTION(int (__cdecl *)(void *, int, void *), ldap_set_option);
318
319   server = (*ldap_init)(conn->host.name, (int)conn->port);
320   if (server == NULL) {
321     failf(data, "LDAP local: Cannot connect to %s:%d",
322           conn->host.name, conn->port);
323     status = CURLE_COULDNT_CONNECT;
324     goto quit;
325   }
326
327   ldap_proto = LDAP_VERSION3;
328   (*ldap_set_option)(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
329   rc = (*ldap_simple_bind_s)(server,
330                              conn->bits.user_passwd ? conn->user : NULL,
331                              conn->bits.user_passwd ? conn->passwd : NULL);
332   if (rc != 0) {
333     ldap_proto = LDAP_VERSION2;
334     (*ldap_set_option)(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
335     rc = (*ldap_simple_bind_s)(server,
336                                conn->bits.user_passwd ? conn->user : NULL,
337                                conn->bits.user_passwd ? conn->passwd : NULL);
338   }
339   if (rc != 0) {
340      failf(data, "LDAP local: %s", (*ldap_err2string)(rc));
341      status = CURLE_LDAP_CANNOT_BIND;
342      goto quit;
343   }
344
345 #ifdef WIN32
346   rc = _ldap_url_parse(conn, &ludp);
347 #else
348   rc = (*ldap_url_parse)(data->change.url, &ludp);
349 #endif
350
351   if (rc != 0) {
352      failf(data, "LDAP local: %s", (*ldap_err2string)(rc));
353      status = CURLE_LDAP_INVALID_URL;
354      goto quit;
355   }
356
357   rc = (*ldap_search_s)(server, ludp->lud_dn, ludp->lud_scope,
358                         ludp->lud_filter, ludp->lud_attrs, 0, &result);
359
360   if (rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
361     failf(data, "LDAP remote: %s", (*ldap_err2string)(rc));
362     status = CURLE_LDAP_SEARCH_FAILED;
363     goto quit;
364   }
365
366   for(num = 0, entryIterator = (*ldap_first_entry)(server, result);
367       entryIterator;
368       entryIterator = (*ldap_next_entry)(server, entryIterator), num++)
369   {
370     void  *ber = NULL;      /*! is really 'BerElement **' */
371     void  *attribute;       /*! suspicious that this isn't 'const' */
372     char  *dn = (*ldap_get_dn)(server, entryIterator);
373     int i;
374
375     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
376     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)dn, 0);
377     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
378
379     for (attribute = (*ldap_first_attribute)(server, entryIterator, &ber);
380          attribute;
381          attribute = (*ldap_next_attribute)(server, entryIterator, ber))
382     {
383       struct bv **vals = (struct bv **)
384         (*ldap_get_values_len)(server, entryIterator, attribute);
385
386       if (vals != NULL)
387       {
388         for (i = 0; (vals[i] != NULL); i++)
389         {
390           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
391           Curl_client_write(conn, CLIENTWRITE_BODY, (char *) attribute, 0);
392           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
393           if ((strlen(attribute) > 7) &&
394               (strcmp(";binary",
395                       (char *)attribute +
396                       (strlen((char *)attribute) - 7)) == 0)) {
397             /* Binary attribute, encode to base64. */
398             val_b64_sz = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
399                                             &val_b64);
400             if (val_b64_sz > 0) {
401               Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
402               free(val_b64);
403             }
404           } else
405             Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
406                               vals[i]->bv_len);
407           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
408         }
409
410         /* Free memory used to store values */
411         (*ldap_value_free_len)((void **)vals);
412       }
413       Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
414
415       (*ldap_memfree)(attribute);
416     }
417     (*ldap_memfree)(dn);
418     if (ber)
419        (*ber_free)(ber, 0);
420   }
421
422 quit:
423   LDAP_TRACE (("Received %d entries\n", num));
424   if (rc == LDAP_SIZELIMIT_EXCEEDED)
425      infof(data, "There are more than %d entries\n", num);
426   if (ludp)
427      (*ldap_free_urldesc)(ludp);
428   if (server)
429      (*ldap_unbind_s)(server);
430
431   DynaClose();
432
433   /* no data to transfer */
434   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
435   conn->bits.close = TRUE;
436
437   return status;
438 }
439
440 #ifdef DEBUG_LDAP
441 static void _ldap_trace (const char *fmt, ...)
442 {
443   static int do_trace = -1;
444   va_list args;
445
446   if (do_trace == -1) {
447     const char *env = getenv("CURL_TRACE");
448     do_trace = (env && atoi(env) > 0);
449   }
450   if (!do_trace)
451     return;
452
453   va_start (args, fmt);
454   vfprintf (stderr, fmt, args);
455   va_end (args);
456 }
457 #endif
458
459 #ifdef WIN32
460 /*
461  * Return scope-value for a scope-string.
462  */
463 static int str2scope (const char *p)
464 {
465   if (!stricmp(p, "one"))
466      return LDAP_SCOPE_ONELEVEL;
467   if (!stricmp(p, "onetree"))
468      return LDAP_SCOPE_ONELEVEL;
469   if (!stricmp(p, "base"))
470      return LDAP_SCOPE_BASE;
471   if (!stricmp(p, "sub"))
472      return LDAP_SCOPE_SUBTREE;
473   if (!stricmp( p, "subtree"))
474      return LDAP_SCOPE_SUBTREE;
475   return (-1);
476 }
477
478 /*
479  * Split 'str' into strings separated by commas.
480  * Note: res[] points into 'str'.
481  */
482 static char **split_str (char *str)
483 {
484   char **res, *lasts, *s;
485   int  i;
486
487   for (i = 2, s = strchr(str,','); s; i++)
488      s = strchr(++s,',');
489
490   res = calloc(i, sizeof(char*));
491   if (!res)
492     return NULL;
493
494   for (i = 0, s = strtok_r(str, ",", &lasts); s;
495        s = strtok_r(NULL, ",", &lasts), i++)
496     res[i] = s;
497   return res;
498 }
499
500 /*
501  * Unescape the LDAP-URL components
502  */
503 static bool unescape_elements (void *data, LDAPURLDesc *ludp)
504 {
505   int i;
506
507   if (ludp->lud_filter) {
508     ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL);
509     if (!ludp->lud_filter)
510        return (FALSE);
511   }
512
513   for (i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) {
514     ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], 0, NULL);
515     if (!ludp->lud_attrs[i])
516        return (FALSE);
517   }
518
519   for (i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) {
520     ludp->lud_exts[i] = curl_easy_unescape(data, ludp->lud_exts[i], 0, NULL);
521     if (!ludp->lud_exts[i])
522        return (FALSE);
523   }
524
525   if (ludp->lud_dn) {
526     char *dn = ludp->lud_dn;
527     char *new_dn = curl_easy_unescape(data, dn, 0, NULL);
528
529     free(dn);
530     ludp->lud_dn = new_dn;
531     if (!new_dn)
532        return (FALSE);
533   }
534   return (TRUE);
535 }
536
537 /*
538  * Break apart the pieces of an LDAP URL.
539  * Syntax:
540  *   ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
541  *
542  * <hostname> already known from 'conn->host.name'.
543  * <port>     already known from 'conn->remote_port'.
544  * extract the rest from 'conn->data->reqdata.path+1'. All fields are optional.
545  * e.g.
546  *   ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
547  * yields ludp->lud_dn = "".
548  *
549  * Ref. http://developer.netscape.com/docs/manuals/dirsdk/csdk30/url.htm#2831915
550  */
551 static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp)
552 {
553   char *p, *q;
554   int i;
555
556   if (!conn->data ||
557       !conn->data->reqdata.path ||
558        conn->data->reqdata.path[0] != '/' ||
559       !checkprefix(conn->protostr, conn->data->change.url))
560      return LDAP_INVALID_SYNTAX;
561
562   ludp->lud_scope = LDAP_SCOPE_BASE;
563   ludp->lud_port  = conn->remote_port;
564   ludp->lud_host  = conn->host.name;
565
566   /* parse DN (Distinguished Name).
567    */
568   ludp->lud_dn = strdup(conn->data->reqdata.path+1);
569   if (!ludp->lud_dn)
570      return LDAP_NO_MEMORY;
571
572   p = strchr(ludp->lud_dn, '?');
573   LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) :
574                strlen(ludp->lud_dn), ludp->lud_dn));
575
576   if (!p)
577      goto success;
578
579   *p++ = '\0';
580
581   /* parse attributes. skip "??".
582    */
583   q = strchr(p, '?');
584   if (q)
585      *q++ = '\0';
586
587   if (*p && *p != '?') {
588     ludp->lud_attrs = split_str(p);
589     if (!ludp->lud_attrs)
590        return LDAP_NO_MEMORY;
591
592     for (i = 0; ludp->lud_attrs[i]; i++)
593         LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i]));
594   }
595
596   p = q;
597   if (!p)
598      goto success;
599
600   /* parse scope. skip "??"
601    */
602   q = strchr(p, '?');
603   if (q)
604      *q++ = '\0';
605
606   if (*p && *p != '?') {
607     ludp->lud_scope = str2scope(p);
608     if (ludp->lud_scope == -1)
609        return LDAP_INVALID_SYNTAX;
610     LDAP_TRACE (("scope %d\n", ludp->lud_scope));
611   }
612
613   p = q;
614   if (!p)
615      goto success;
616
617   /* parse filter
618    */
619   q = strchr(p, '?');
620   if (q)
621      *q++ = '\0';
622   if (!*p)
623      return LDAP_INVALID_SYNTAX;
624
625   ludp->lud_filter = p;
626   LDAP_TRACE (("filter '%s'\n", ludp->lud_filter));
627
628   p = q;
629   if (!p)
630      goto success;
631
632   /* parse extensions
633    */
634   ludp->lud_exts = split_str(p);
635   if (!ludp->lud_exts)
636      return LDAP_NO_MEMORY;
637
638   for (i = 0; ludp->lud_exts[i]; i++)
639       LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i]));
640
641 success:
642   if (!unescape_elements(conn->data, ludp))
643      return LDAP_NO_MEMORY;
644   return LDAP_SUCCESS;
645 }
646
647 static int _ldap_url_parse (const struct connectdata *conn,
648                             LDAPURLDesc **ludpp)
649 {
650   LDAPURLDesc *ludp = calloc(sizeof(*ludp), 1);
651   int rc;
652
653   *ludpp = NULL;
654   if (!ludp)
655      return LDAP_NO_MEMORY;
656
657   rc = _ldap_url_parse2 (conn, ludp);
658   if (rc != LDAP_SUCCESS) {
659     _ldap_free_urldesc(ludp);
660     ludp = NULL;
661   }
662   *ludpp = ludp;
663   return (rc);
664 }
665
666 static void _ldap_free_urldesc (LDAPURLDesc *ludp)
667 {
668   int i;
669
670   if (!ludp)
671      return;
672
673   if (ludp->lud_dn)
674      free(ludp->lud_dn);
675
676   if (ludp->lud_filter)
677      free(ludp->lud_filter);
678
679   if (ludp->lud_attrs) {
680     for (i = 0; ludp->lud_attrs[i]; i++)
681        free(ludp->lud_attrs[i]);
682     free(ludp->lud_attrs);
683   }
684
685   if (ludp->lud_exts) {
686     for (i = 0; ludp->lud_exts[i]; i++)
687        free(ludp->lud_exts[i]);
688     free(ludp->lud_exts);
689   }
690   free (ludp);
691 }
692 #endif  /* WIN32 */
693 #endif  /* CURL_DISABLE_LDAP */