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