ldap.c 16 KB

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