PageRenderTime 230ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/runtime/ext/ext_ldap.cpp

http://github.com/facebook/hiphop-php
C++ | 1250 lines | 1029 code | 159 blank | 62 comment | 196 complexity | f88268ca39c84b281f5b58139a890885 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
  6. | Copyright (c) 1997-2010 The PHP Group |
  7. +----------------------------------------------------------------------+
  8. | This source file is subject to version 3.01 of the PHP license, |
  9. | that is bundled with this package in the file LICENSE, and is |
  10. | available through the world-wide-web at the following url: |
  11. | http://www.php.net/license/3_01.txt |
  12. | If you did not receive a copy of the PHP license and are unable to |
  13. | obtain it through the world-wide-web, please send a note to |
  14. | license@php.net so we can mail you a copy immediately. |
  15. +----------------------------------------------------------------------+
  16. */
  17. #include "hphp/runtime/ext/ext_ldap.h"
  18. #include "hphp/runtime/ext/ext_function.h"
  19. #include "hphp/runtime/base/builtin-functions.h"
  20. #include "hphp/util/thread-local.h"
  21. #include "folly/String.h"
  22. #include <lber.h>
  23. #define LDAP_DEPRECATED 1
  24. #include <ldap.h>
  25. #define PHP_LD_FULL_ADD 0xff
  26. namespace HPHP {
  27. IMPLEMENT_DEFAULT_EXTENSION(ldap);
  28. ///////////////////////////////////////////////////////////////////////////////
  29. class LdapRequestData {
  30. public:
  31. LdapRequestData() : m_num_links(0), m_max_links(-1) {
  32. }
  33. long m_num_links;
  34. long m_max_links;
  35. };
  36. static IMPLEMENT_THREAD_LOCAL(LdapRequestData, s_ldap_data);
  37. #define LDAPG(name) s_ldap_data->m_ ## name
  38. class LdapLink : public SweepableResourceData {
  39. public:
  40. DECLARE_RESOURCE_ALLOCATION(LdapLink)
  41. LdapLink() : link(NULL) {}
  42. ~LdapLink() { close();}
  43. void close() {
  44. if (link) {
  45. ldap_unbind_s(link);
  46. link = NULL;
  47. LDAPG(num_links)--;
  48. }
  49. rebindproc.reset();
  50. }
  51. CLASSNAME_IS("ldap link");
  52. // overriding ResourceData
  53. virtual const String& o_getClassNameHook() const { return classnameof(); }
  54. LDAP *link;
  55. Variant rebindproc;
  56. };
  57. IMPLEMENT_OBJECT_ALLOCATION(LdapLink)
  58. class LdapResult : public SweepableResourceData {
  59. public:
  60. DECLARE_RESOURCE_ALLOCATION(LdapResult)
  61. LdapResult(LDAPMessage *res) : data(res) {}
  62. ~LdapResult() { close();}
  63. void close() {
  64. if (data) {
  65. ldap_msgfree(data);
  66. data = NULL;
  67. }
  68. }
  69. CLASSNAME_IS("ldap result");
  70. // overriding ResourceData
  71. virtual const String& o_getClassNameHook() const { return classnameof();}
  72. LDAPMessage *data;
  73. };
  74. IMPLEMENT_OBJECT_ALLOCATION(LdapResult)
  75. class LdapResultEntry : public SweepableResourceData {
  76. public:
  77. DECLARE_RESOURCE_ALLOCATION(LdapResultEntry)
  78. LdapResultEntry(LDAPMessage *entry, ResourceData *res)
  79. : data(entry), ber(NULL), result(res) {}
  80. ~LdapResultEntry() { close();}
  81. void close() {
  82. if (ber != NULL) {
  83. ber_free(ber, 0);
  84. ber = NULL;
  85. }
  86. data = NULL;
  87. }
  88. CLASSNAME_IS("ldap result entry");
  89. // overriding ResourceData
  90. virtual const String& o_getClassNameHook() const { return classnameof(); }
  91. LDAPMessage *data;
  92. BerElement *ber;
  93. Resource result; // Reference to LdapResult to avoid premature deallocation
  94. };
  95. void LdapResultEntry::sweep() {
  96. close();
  97. }
  98. ///////////////////////////////////////////////////////////////////////////////
  99. static int _get_lderrno(LDAP *ldap) {
  100. int lderr;
  101. ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
  102. return lderr;
  103. }
  104. static bool php_ldap_do_modify(CResRef link, const String& dn, CArrRef entry,
  105. int oper) {
  106. bool is_full_add = false; /* flag for full add operation so ldap_mod_add
  107. can be put back into oper, gerrit THomson */
  108. LdapLink *ld = link.getTyped<LdapLink>();
  109. int num_attribs = entry.size();
  110. LDAPMod **ldap_mods =
  111. (LDAPMod **)malloc((num_attribs+1) * sizeof(LDAPMod *));
  112. int *num_berval = (int*)malloc(num_attribs * sizeof(int));
  113. ArrayIter iter(entry);
  114. int num_values;
  115. /* added by gerrit thomson to fix ldap_add using ldap_mod_add */
  116. if (oper == PHP_LD_FULL_ADD) {
  117. oper = LDAP_MOD_ADD;
  118. is_full_add = true;
  119. }
  120. /* end additional , gerrit thomson */
  121. bool ret = false;
  122. Array stringHolder;
  123. for (int i = 0; i < num_attribs; i++) {
  124. ldap_mods[i] = (LDAPMod*)malloc(sizeof(LDAPMod));
  125. ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
  126. ldap_mods[i]->mod_type = NULL;
  127. Variant key = iter.first();
  128. Variant value = iter.second();
  129. if (key.isString()) {
  130. ldap_mods[i]->mod_type = strdup(key.toString().data());
  131. } else {
  132. raise_warning("Unknown attribute in the data");
  133. /* Free allocated memory */
  134. while (i >= 0) {
  135. if (ldap_mods[i]->mod_type) {
  136. free(ldap_mods[i]->mod_type);
  137. }
  138. free(ldap_mods[i]);
  139. i--;
  140. }
  141. free(num_berval);
  142. free(ldap_mods);
  143. return false;
  144. }
  145. if (!value.isArray()) {
  146. num_values = 1;
  147. } else {
  148. num_values = value.toArray().size();
  149. }
  150. num_berval[i] = num_values;
  151. ldap_mods[i]->mod_bvalues =
  152. (struct berval**)malloc((num_values + 1) * sizeof(struct berval *));
  153. /* allow for arrays with one element, no allowance for arrays with
  154. none but probably not required, gerrit thomson. */
  155. if (num_values == 1 && !value.isArray()) {
  156. String svalue = value.toString();
  157. stringHolder.append(svalue);
  158. ldap_mods[i]->mod_bvalues[0] = (berval *)malloc(sizeof(struct berval));
  159. ldap_mods[i]->mod_bvalues[0]->bv_len = svalue.size();
  160. ldap_mods[i]->mod_bvalues[0]->bv_val = (char*)svalue.data();
  161. } else {
  162. Array arr = value.toArray();
  163. for (int j = 0; j < num_values; j++) {
  164. if (!arr.exists(j)) {
  165. raise_warning("Value array must have consecutive indices 0, 1, ...");
  166. num_berval[i] = j;
  167. num_attribs = i + 1;
  168. goto errexit;
  169. }
  170. String ivalue = arr[j].toString();
  171. ldap_mods[i]->mod_bvalues[j] = (berval *)malloc(sizeof(struct berval));
  172. ldap_mods[i]->mod_bvalues[j]->bv_len = ivalue.size();
  173. ldap_mods[i]->mod_bvalues[j]->bv_val = (char*)ivalue.data();
  174. }
  175. }
  176. ldap_mods[i]->mod_bvalues[num_values] = NULL;
  177. ++iter;
  178. }
  179. ldap_mods[num_attribs] = NULL;
  180. /* check flag to see if do_mod was called to perform full add,
  181. gerrit thomson */
  182. int rc;
  183. if (is_full_add) {
  184. if ((rc = ldap_add_s(ld->link, (char*)dn.data(), ldap_mods))
  185. != LDAP_SUCCESS) {
  186. raise_warning("Add: %s", ldap_err2string(rc));
  187. } else {
  188. ret = true;
  189. }
  190. } else {
  191. if ((rc = ldap_modify_s(ld->link, (char*)dn.data(), ldap_mods))
  192. != LDAP_SUCCESS) {
  193. raise_warning("Modify: %s", ldap_err2string(rc));
  194. } else {
  195. ret = true;
  196. }
  197. }
  198. errexit:
  199. for (int i = 0; i < num_attribs; i++) {
  200. free(ldap_mods[i]->mod_type);
  201. for (int j = 0; j < num_berval[i]; j++) {
  202. free(ldap_mods[i]->mod_bvalues[j]);
  203. }
  204. free(ldap_mods[i]->mod_bvalues);
  205. free(ldap_mods[i]);
  206. }
  207. free(num_berval);
  208. free(ldap_mods);
  209. return ret;
  210. }
  211. static void php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref,
  212. int *old_sizelimit, int *old_timelimit,
  213. int *old_deref) {
  214. /* sizelimit */
  215. if (sizelimit > -1) {
  216. ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_sizelimit);
  217. ldap_set_option(ldap, LDAP_OPT_SIZELIMIT, &sizelimit);
  218. }
  219. /* timelimit */
  220. if (timelimit > -1) {
  221. ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_timelimit);
  222. ldap_set_option(ldap, LDAP_OPT_TIMELIMIT, &timelimit);
  223. }
  224. /* deref */
  225. if (deref > -1) {
  226. ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_deref);
  227. ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
  228. }
  229. }
  230. static Variant php_ldap_do_search(CVarRef link, CVarRef base_dn,
  231. CVarRef filter, CArrRef attributes,
  232. int attrsonly, int sizelimit, int timelimit,
  233. int deref, int scope) {
  234. int num_attribs = attributes.size();
  235. int old_sizelimit = -1, old_timelimit = -1, old_deref = -1;
  236. int ldap_err = 1, parallel_search = 1;
  237. char **ldap_attrs = (char**)malloc((num_attribs+1) * sizeof(char *));
  238. Array stringHolder;
  239. Array ret = Array::Create();
  240. char *ldap_base_dn = NULL;
  241. char *ldap_filter = NULL;
  242. LdapLink *ld = NULL;
  243. for (int i = 0; i < num_attribs; i++) {
  244. if (!attributes.exists(i)) {
  245. raise_warning("Array initialization wrong");
  246. ldap_err = 0;
  247. goto cleanup;
  248. }
  249. String attr = attributes[i].toString();
  250. stringHolder.append(attr);
  251. ldap_attrs[i] = (char*)attr.data();
  252. }
  253. ldap_attrs[num_attribs] = NULL;
  254. /* parallel search? */
  255. if (link.isArray()) {
  256. int nlinks = link.toArray().size();
  257. if (nlinks == 0) {
  258. raise_warning("No links in link array");
  259. ldap_err = 0;
  260. goto cleanup;
  261. }
  262. int nbases;
  263. if (base_dn.isArray()) {
  264. nbases = base_dn.toArray().size();
  265. if (nbases != nlinks) {
  266. raise_warning("Base must either be a string, or an array with the "
  267. "same number of elements as the links array");
  268. ldap_err = 0;
  269. goto cleanup;
  270. }
  271. } else {
  272. nbases = 0; /* this means string, not array */
  273. /* If anything else than string is passed, ldap_base_dn = NULL */
  274. if (base_dn.isString()) {
  275. ldap_base_dn = (char*)base_dn.toString().data();
  276. } else {
  277. ldap_base_dn = NULL;
  278. }
  279. }
  280. int nfilters;
  281. if (filter.isArray()) {
  282. nfilters = filter.toArray().size();
  283. if (nfilters != nlinks) {
  284. raise_warning("Filter must either be a string, or an array with the "
  285. "same number of elements as the links array");
  286. ldap_err = 0;
  287. goto cleanup;
  288. }
  289. } else {
  290. nfilters = 0; /* this means string, not array */
  291. String sfilter = filter.toString();
  292. stringHolder.append(sfilter);
  293. ldap_filter = (char*)sfilter.data();
  294. }
  295. LdapLink **lds = (LdapLink**)malloc(nlinks * sizeof(LdapLink*));
  296. int *rcs = (int*)malloc(nlinks * sizeof(int));
  297. ArrayIter iter(link.toArray());
  298. ArrayIter iterdn(base_dn.toArray());
  299. ArrayIter iterfilter(filter.toArray());
  300. for (int i = 0; i < nlinks; i++) {
  301. ld = iter.second().toResource().getTyped<LdapLink>(true, true);
  302. if (ld == NULL) {
  303. ldap_err = 0;
  304. goto cleanup_parallel;
  305. }
  306. if (nbases != 0) { /* base_dn an array? */
  307. Variant entry = iterdn.second();
  308. ++iterdn;
  309. /* If anything else than string is passed, ldap_base_dn = NULL */
  310. if (entry.isString()) {
  311. ldap_base_dn = (char*)entry.toString().data();
  312. } else {
  313. ldap_base_dn = NULL;
  314. }
  315. }
  316. if (nfilters != 0) { /* filter an array? */
  317. Variant entry = iterfilter.second();
  318. ++iterfilter;
  319. String sentry = entry.toString();
  320. stringHolder.append(sentry);
  321. ldap_filter = (char*)sentry.data();
  322. }
  323. php_set_opts(ld->link, sizelimit, timelimit, deref, &old_sizelimit,
  324. &old_timelimit, &old_deref);
  325. /* Run the actual search */
  326. rcs[i] = ldap_search(ld->link, ldap_base_dn, scope, ldap_filter,
  327. ldap_attrs, attrsonly);
  328. lds[i] = ld;
  329. ++iter;
  330. }
  331. /* Collect results from the searches */
  332. for (int i = 0; i < nlinks; i++) {
  333. LDAPMessage *ldap_res;
  334. if (rcs[i] != -1) {
  335. rcs[i] = ldap_result(lds[i]->link, LDAP_RES_ANY, 1 /* LDAP_MSG_ALL */,
  336. NULL, &ldap_res);
  337. }
  338. if (rcs[i] != -1) {
  339. ret.append(Resource(NEWOBJ(LdapResult)(ldap_res)));
  340. } else {
  341. ret.append(false);
  342. }
  343. }
  344. cleanup_parallel:
  345. free(lds);
  346. free(rcs);
  347. } else {
  348. /* parallel search? */
  349. String sfilter = filter.toString();
  350. ldap_filter = (char*)sfilter.data();
  351. /* If anything else than string is passed, ldap_base_dn = NULL */
  352. if (base_dn.isString()) {
  353. ldap_base_dn = (char*)base_dn.toString().data();
  354. }
  355. ld = link.toResource().getTyped<LdapLink>(true, true);
  356. if (ld == NULL) {
  357. ldap_err = 0;
  358. goto cleanup;
  359. }
  360. php_set_opts(ld->link, sizelimit, timelimit, deref, &old_sizelimit,
  361. &old_timelimit, &old_deref);
  362. /* Run the actual search */
  363. LDAPMessage *ldap_res;
  364. int rc = ldap_search_s(ld->link, ldap_base_dn, scope, ldap_filter,
  365. ldap_attrs, attrsonly, &ldap_res);
  366. if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED
  367. #ifdef LDAP_ADMINLIMIT_EXCEEDED
  368. && rc != LDAP_ADMINLIMIT_EXCEEDED
  369. #endif
  370. #ifdef LDAP_REFERRAL
  371. && rc != LDAP_REFERRAL
  372. #endif
  373. ) {
  374. raise_warning("Search: %s", ldap_err2string(rc));
  375. ldap_err = 0;
  376. } else {
  377. if (rc == LDAP_SIZELIMIT_EXCEEDED) {
  378. raise_warning("Partial search results returned: Sizelimit exceeded");
  379. }
  380. #ifdef LDAP_ADMINLIMIT_EXCEEDED
  381. else if (rc == LDAP_ADMINLIMIT_EXCEEDED) {
  382. raise_warning("Partial search results returned: Adminlimit exceeded");
  383. }
  384. #endif
  385. parallel_search = 0;
  386. ret.append(Resource(NEWOBJ(LdapResult)(ldap_res)));
  387. }
  388. }
  389. cleanup:
  390. if (ld) {
  391. /* Restoring previous options */
  392. php_set_opts(ld->link, old_sizelimit, old_timelimit, old_deref, &sizelimit,
  393. &timelimit, &deref);
  394. }
  395. if (ldap_attrs != NULL) {
  396. free(ldap_attrs);
  397. }
  398. if (!ldap_err) {
  399. return false;
  400. }
  401. if (!parallel_search) {
  402. return ret.dequeue();
  403. } else {
  404. return ret;
  405. }
  406. }
  407. static int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req,
  408. ber_int_t msgid, void *params) {
  409. LdapLink *ld = (LdapLink*)params;
  410. /* link exists and callback set? */
  411. if (ld == NULL || ld->rebindproc.isNull()) {
  412. raise_warning("Link not found or no callback set");
  413. return LDAP_OTHER;
  414. }
  415. /* callback */
  416. Variant ret = vm_call_user_func
  417. (ld->rebindproc, make_packed_array(Resource(ld), String(url, CopyString)));
  418. return ret.toInt64();
  419. }
  420. const StaticString
  421. s_count("count"),
  422. s_dn("dn");
  423. static void get_attributes(Array &ret, LDAP *ldap,
  424. LDAPMessage *ldap_result_entry, bool to_lower) {
  425. int num_attrib = 0;
  426. BerElement *ber;
  427. char *attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber);
  428. while (attribute != NULL) {
  429. struct berval **ldap_value =
  430. ldap_get_values_len(ldap, ldap_result_entry, attribute);
  431. int num_values = ldap_count_values_len(ldap_value);
  432. Array tmp;
  433. tmp.set(s_count, num_values);
  434. for (int i = 0; i < num_values; i++) {
  435. tmp.append(String(ldap_value[i]->bv_val, ldap_value[i]->bv_len,
  436. CopyString));
  437. }
  438. ldap_value_free_len(ldap_value);
  439. String sAttribute(attribute, CopyString);
  440. ret.set(to_lower ? String(Util::toLower(attribute)) : sAttribute, tmp);
  441. ret.set(num_attrib, sAttribute);
  442. num_attrib++;
  443. ldap_memfree(attribute);
  444. attribute = ldap_next_attribute(ldap, ldap_result_entry, ber);
  445. }
  446. if (ber != NULL) {
  447. ber_free(ber, 0);
  448. }
  449. ret.set(s_count, num_attrib);
  450. }
  451. ///////////////////////////////////////////////////////////////////////////////
  452. Variant f_ldap_connect(const String& hostname /* = null_string */,
  453. int port /* = 389 */) {
  454. if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
  455. raise_warning("Too many open links (%ld)", LDAPG(num_links));
  456. return false;
  457. }
  458. LdapLink *ld = NEWOBJ(LdapLink)();
  459. Resource ret(ld);
  460. LDAP *ldap = NULL;
  461. if (!hostname.empty() && hostname.find('/') >= 0) {
  462. int rc = ldap_initialize(&ldap, hostname.data());
  463. if (rc != LDAP_SUCCESS) {
  464. raise_warning("Could not create session handle: %s",
  465. ldap_err2string(rc));
  466. return false;
  467. }
  468. } else {
  469. ldap = ldap_init((char*)hostname.data(), port);
  470. }
  471. if (ldap) {
  472. LDAPG(num_links)++;
  473. ld->link = ldap;
  474. return ret;
  475. }
  476. raise_warning("Unable to initialize LDAP: %s",
  477. folly::errnoStr(errno).c_str());
  478. return false;
  479. }
  480. Variant f_ldap_explode_dn(const String& dn, int with_attrib) {
  481. char **ldap_value;
  482. if (!(ldap_value = ldap_explode_dn((char*)dn.data(), with_attrib))) {
  483. /* Invalid parameters were passed to ldap_explode_dn */
  484. return false;
  485. }
  486. int i = 0;
  487. while (ldap_value[i] != NULL) i++;
  488. int count = i;
  489. Array ret;
  490. ret.set(s_count, count);
  491. for (i = 0; i < count; i++) {
  492. ret.append(String(ldap_value[i], CopyString));
  493. }
  494. ldap_value_free(ldap_value);
  495. return ret;
  496. }
  497. Variant f_ldap_dn2ufn(const String& db) {
  498. char *ufn = ldap_dn2ufn((char*)db.data());
  499. if (ufn) {
  500. String ret(ufn, CopyString);
  501. ldap_memfree(ufn);
  502. return ret;
  503. }
  504. return false;
  505. }
  506. String f_ldap_err2str(int errnum) {
  507. return String(ldap_err2string(errnum), CopyString);
  508. }
  509. bool f_ldap_add(CResRef link, const String& dn, CArrRef entry) {
  510. return php_ldap_do_modify(link, dn, entry, PHP_LD_FULL_ADD);
  511. }
  512. bool f_ldap_mod_add(CResRef link, const String& dn, CArrRef entry) {
  513. return php_ldap_do_modify(link, dn, entry, LDAP_MOD_ADD);
  514. }
  515. bool f_ldap_mod_del(CResRef link, const String& dn, CArrRef entry) {
  516. return php_ldap_do_modify(link, dn, entry, LDAP_MOD_DELETE);
  517. }
  518. bool f_ldap_mod_replace(CResRef link, const String& dn, CArrRef entry) {
  519. return php_ldap_do_modify(link, dn, entry, LDAP_MOD_REPLACE);
  520. }
  521. bool f_ldap_modify(CResRef link, const String& dn, CArrRef entry) {
  522. return php_ldap_do_modify(link, dn, entry, LDAP_MOD_REPLACE);
  523. }
  524. bool f_ldap_bind(CResRef link, const String& bind_rdn /* = null_string */,
  525. const String& bind_password /* = null_string */) {
  526. int rc;
  527. LdapLink *ld = link.getTyped<LdapLink>();
  528. if ((rc = ldap_bind_s(ld->link, (char*)bind_rdn.data(),
  529. (char*)bind_password.data(),
  530. LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) {
  531. raise_warning("Unable to bind to server: %s", ldap_err2string(rc));
  532. return false;
  533. }
  534. return true;
  535. }
  536. bool f_ldap_set_rebind_proc(CResRef link, CVarRef callback) {
  537. LdapLink *ld = link.getTyped<LdapLink>();
  538. if (callback.isString() && callback.toString().empty()) {
  539. /* unregister rebind procedure */
  540. if (!ld->rebindproc.isNull()) {
  541. ld->rebindproc.reset();
  542. ldap_set_rebind_proc(ld->link, NULL, NULL);
  543. }
  544. return true;
  545. }
  546. /* callable? */
  547. if (!f_is_callable(callback)) {
  548. raise_warning("Callback argument is not a valid callback");
  549. return false;
  550. }
  551. /* register rebind procedure */
  552. if (ld->rebindproc.isNull()) {
  553. ldap_set_rebind_proc(ld->link, _ldap_rebind_proc, (void *)link.get());
  554. } else {
  555. ld->rebindproc.reset();
  556. }
  557. ld->rebindproc = callback;
  558. return true;
  559. }
  560. bool f_ldap_sort(CResRef link, CResRef result, const String& sortfilter) {
  561. LdapLink *ld = link.getTyped<LdapLink>();
  562. LdapResult *res = result.getTyped<LdapResult>();
  563. if (ldap_sort_entries(ld->link, &res->data,
  564. !sortfilter.empty() ? (char*)sortfilter.data() : NULL,
  565. strcmp) != LDAP_SUCCESS) {
  566. raise_warning("%s", ldap_err2string(_get_lderrno(ld->link)));
  567. return false;
  568. }
  569. return true;
  570. }
  571. bool f_ldap_start_tls(CResRef link) {
  572. LdapLink *ld = link.getTyped<LdapLink>();
  573. int rc, protocol = LDAP_VERSION3;
  574. if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol))
  575. != LDAP_SUCCESS) ||
  576. ((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS)) {
  577. raise_warning("Unable to start TLS: %s", ldap_err2string(rc));
  578. return false;
  579. }
  580. return true;
  581. }
  582. bool f_ldap_unbind(CResRef link) {
  583. LdapLink *ld = link.getTyped<LdapLink>();
  584. ld->close();
  585. return true;
  586. }
  587. bool f_ldap_get_option(CResRef link, int option, VRefParam retval) {
  588. LdapLink *ld = link.getTyped<LdapLink>();
  589. switch (option) {
  590. /* options with int value */
  591. case LDAP_OPT_DEREF:
  592. case LDAP_OPT_SIZELIMIT:
  593. case LDAP_OPT_TIMELIMIT:
  594. case LDAP_OPT_PROTOCOL_VERSION:
  595. case LDAP_OPT_ERROR_NUMBER:
  596. case LDAP_OPT_REFERRALS:
  597. #ifdef LDAP_OPT_RESTART
  598. case LDAP_OPT_RESTART:
  599. #endif
  600. {
  601. int val;
  602. if (ldap_get_option(ld->link, option, &val)) {
  603. return false;
  604. }
  605. retval = (int64_t)val;
  606. } break;
  607. #ifdef LDAP_OPT_NETWORK_TIMEOUT
  608. case LDAP_OPT_NETWORK_TIMEOUT:
  609. {
  610. struct timeval *timeout;
  611. if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT,
  612. (void *) &timeout)) {
  613. if (timeout) {
  614. ldap_memfree(timeout);
  615. }
  616. return false;
  617. }
  618. retval = (int64_t)timeout->tv_sec;
  619. ldap_memfree(timeout);
  620. } break;
  621. #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
  622. case LDAP_X_OPT_CONNECT_TIMEOUT:
  623. {
  624. int timeout;
  625. if (ldap_get_option(ld->link, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
  626. return false;
  627. }
  628. retval = (int64_t)(timeout / 1000);
  629. } break;
  630. #endif
  631. /* options with string value */
  632. case LDAP_OPT_ERROR_STRING:
  633. #ifdef LDAP_OPT_HOST_NAME
  634. case LDAP_OPT_HOST_NAME:
  635. #endif
  636. #ifdef HAVE_LDAP_SASL
  637. case LDAP_OPT_X_SASL_MECH:
  638. case LDAP_OPT_X_SASL_REALM:
  639. case LDAP_OPT_X_SASL_AUTHCID:
  640. case LDAP_OPT_X_SASL_AUTHZID:
  641. #endif
  642. #ifdef LDAP_OPT_MATCHED_DN
  643. case LDAP_OPT_MATCHED_DN:
  644. #endif
  645. {
  646. char *val = NULL;
  647. if (ldap_get_option(ld->link, option, &val) || val == NULL ||
  648. *val == '\0') {
  649. if (val) {
  650. ldap_memfree(val);
  651. }
  652. return false;
  653. }
  654. retval = String(val, CopyString);
  655. ldap_memfree(val);
  656. } break;
  657. /* options not implemented
  658. case LDAP_OPT_SERVER_CONTROLS:
  659. case LDAP_OPT_CLIENT_CONTROLS:
  660. case LDAP_OPT_API_INFO:
  661. case LDAP_OPT_API_FEATURE_INFO:
  662. */
  663. default:
  664. return false;
  665. }
  666. return true;
  667. }
  668. const StaticString
  669. s_oid("oid"),
  670. s_value("value"),
  671. s_iscritical("iscritical");
  672. bool f_ldap_set_option(CVarRef link, int option, CVarRef newval) {
  673. LDAP *ldap = NULL;
  674. if (!link.isNull()) {
  675. LdapLink *ld = link.toResource().getTyped<LdapLink>();
  676. ldap = ld->link;
  677. }
  678. switch (option) {
  679. /* options with int value */
  680. case LDAP_OPT_DEREF:
  681. case LDAP_OPT_SIZELIMIT:
  682. case LDAP_OPT_TIMELIMIT:
  683. case LDAP_OPT_PROTOCOL_VERSION:
  684. case LDAP_OPT_ERROR_NUMBER:
  685. #ifdef LDAP_OPT_DEBUG_LEVEL
  686. case LDAP_OPT_DEBUG_LEVEL:
  687. #endif
  688. {
  689. int val = newval.toInt64();
  690. if (ldap_set_option(ldap, option, &val)) {
  691. return false;
  692. }
  693. } break;
  694. #ifdef LDAP_OPT_NETWORK_TIMEOUT
  695. case LDAP_OPT_NETWORK_TIMEOUT:
  696. {
  697. struct timeval timeout;
  698. timeout.tv_sec = newval.toInt64();
  699. timeout.tv_usec = 0;
  700. if (ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
  701. return false;
  702. }
  703. } break;
  704. #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
  705. case LDAP_X_OPT_CONNECT_TIMEOUT:
  706. {
  707. int timeout = 1000 * newval.toInt64(); /* Convert to milliseconds */
  708. if (ldap_set_option(ldap, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
  709. return false;
  710. }
  711. } break;
  712. #endif
  713. /* options with string value */
  714. case LDAP_OPT_ERROR_STRING:
  715. #ifdef LDAP_OPT_HOST_NAME
  716. case LDAP_OPT_HOST_NAME:
  717. #endif
  718. #ifdef HAVE_LDAP_SASL
  719. case LDAP_OPT_X_SASL_MECH:
  720. case LDAP_OPT_X_SASL_REALM:
  721. case LDAP_OPT_X_SASL_AUTHCID:
  722. case LDAP_OPT_X_SASL_AUTHZID:
  723. #endif
  724. #ifdef LDAP_OPT_MATCHED_DN
  725. case LDAP_OPT_MATCHED_DN:
  726. #endif
  727. {
  728. String snewval = newval.toString();
  729. char *val = (char*)snewval.data();
  730. if (ldap_set_option(ldap, option, val)) {
  731. return false;
  732. }
  733. } break;
  734. /* options with boolean value */
  735. case LDAP_OPT_REFERRALS:
  736. #ifdef LDAP_OPT_RESTART
  737. case LDAP_OPT_RESTART:
  738. #endif
  739. {
  740. void *val = newval.toBoolean() ? LDAP_OPT_ON : LDAP_OPT_OFF;
  741. if (ldap_set_option(ldap, option, val)) {
  742. return false;
  743. }
  744. } break;
  745. /* options with control list value */
  746. case LDAP_OPT_SERVER_CONTROLS:
  747. case LDAP_OPT_CLIENT_CONTROLS:
  748. {
  749. LDAPControl *ctrl, **ctrls, **ctrlp;
  750. int ncontrols;
  751. char error=0;
  752. if (!newval.isArray() || !(ncontrols = newval.toArray().size())) {
  753. raise_warning("Expected non-empty array value for this option");
  754. return false;
  755. }
  756. ctrls = (LDAPControl**)malloc((1 + ncontrols) * sizeof(*ctrls));
  757. *ctrls = NULL;
  758. ctrlp = ctrls;
  759. Array stringHolder;
  760. for (ArrayIter iter(newval.toArray()); iter; ++iter) {
  761. Variant vctrlval = iter.second();
  762. if (!vctrlval.isArray()) {
  763. raise_warning("The array value must contain only arrays, "
  764. "where each array is a control");
  765. error = 1;
  766. break;
  767. }
  768. Array ctrlval = vctrlval.toArray();
  769. if (!ctrlval.exists(s_oid)) {
  770. raise_warning("Control must have an oid key");
  771. error = 1;
  772. break;
  773. }
  774. String val = ctrlval[s_oid].toString();
  775. stringHolder.append(val);
  776. ctrl = *ctrlp = (LDAPControl*)malloc(sizeof(**ctrlp));
  777. ctrl->ldctl_oid = (char*)val.data();
  778. if (ctrlval.exists(s_value)) {
  779. val = ctrlval[s_value].toString();
  780. stringHolder.append(val);
  781. ctrl->ldctl_value.bv_val = (char*)val.data();
  782. ctrl->ldctl_value.bv_len = val.size();
  783. } else {
  784. ctrl->ldctl_value.bv_val = NULL;
  785. ctrl->ldctl_value.bv_len = 0;
  786. }
  787. if (ctrlval.exists(s_iscritical)) {
  788. ctrl->ldctl_iscritical = val.toBoolean() ? 1 : 0;
  789. } else {
  790. ctrl->ldctl_iscritical = 0;
  791. }
  792. ++ctrlp;
  793. *ctrlp = NULL;
  794. }
  795. if (!error) {
  796. error = ldap_set_option(ldap, option, ctrls);
  797. }
  798. ctrlp = ctrls;
  799. while (*ctrlp) {
  800. free(*ctrlp);
  801. ctrlp++;
  802. }
  803. free(ctrls);
  804. if (error) {
  805. return false;
  806. }
  807. } break;
  808. default:
  809. return false;
  810. }
  811. return true;
  812. }
  813. bool f_ldap_close(CResRef link) {
  814. return f_ldap_unbind(link);
  815. }
  816. Variant f_ldap_list(CVarRef link, CVarRef base_dn, CVarRef filter,
  817. CArrRef attributes /* = null_array */,
  818. int attrsonly /* = 0 */, int sizelimit /* = -1 */,
  819. int timelimit /* = -1 */, int deref /* = -1 */) {
  820. return php_ldap_do_search(link, base_dn, filter, attributes, attrsonly,
  821. sizelimit, timelimit, deref, LDAP_SCOPE_ONELEVEL);
  822. }
  823. Variant f_ldap_read(CVarRef link, CVarRef base_dn, CVarRef filter,
  824. CArrRef attributes /* = null_array */,
  825. int attrsonly /* = 0 */, int sizelimit /* = -1 */,
  826. int timelimit /* = -1 */, int deref /* = -1 */) {
  827. return php_ldap_do_search(link, base_dn, filter, attributes, attrsonly,
  828. sizelimit, timelimit, deref, LDAP_SCOPE_BASE);
  829. }
  830. Variant f_ldap_search(CVarRef link, CVarRef base_dn, CVarRef filter,
  831. CArrRef attributes /* = null_array */,
  832. int attrsonly /* = 0 */, int sizelimit /* = -1 */,
  833. int timelimit /* = -1 */, int deref /* = -1 */) {
  834. return php_ldap_do_search(link, base_dn, filter, attributes, attrsonly,
  835. sizelimit, timelimit, deref, LDAP_SCOPE_SUBTREE);
  836. }
  837. bool f_ldap_rename(CResRef link, const String& dn, const String& newrdn,
  838. const String& newparent,
  839. bool deleteoldrdn) {
  840. LdapLink *ld = link.getTyped<LdapLink>();
  841. int rc = ldap_rename_s(ld->link, (char*)dn.data(), (char*)newrdn.data(),
  842. !newparent.empty() ? (char*)newparent.data() : NULL,
  843. deleteoldrdn, NULL, NULL);
  844. return rc == LDAP_SUCCESS;
  845. }
  846. bool f_ldap_delete(CResRef link, const String& dn) {
  847. LdapLink *ld = link.getTyped<LdapLink>();
  848. int rc;
  849. if ((rc = ldap_delete_s(ld->link, (char*)dn.data())) != LDAP_SUCCESS) {
  850. raise_warning("Delete: %s", ldap_err2string(rc));
  851. return false;
  852. }
  853. return true;
  854. }
  855. Variant f_ldap_compare(CResRef link, const String& dn, const String& attribute,
  856. const String& value) {
  857. LdapLink *ld = link.getTyped<LdapLink>();
  858. int rc = ldap_compare_s(ld->link, (char*)dn.data(), (char*)attribute.data(),
  859. (char*)value.data());
  860. switch (rc) {
  861. case LDAP_COMPARE_TRUE: return true;
  862. case LDAP_COMPARE_FALSE: return false;
  863. }
  864. raise_warning("Compare: %s", ldap_err2string(rc));
  865. return -1LL;
  866. }
  867. int64_t f_ldap_errno(CResRef link) {
  868. LdapLink *ld = link.getTyped<LdapLink>();
  869. return _get_lderrno(ld->link);
  870. }
  871. String f_ldap_error(CResRef link) {
  872. LdapLink *ld = link.getTyped<LdapLink>();
  873. int ld_errno = _get_lderrno(ld->link);
  874. return String(ldap_err2string(ld_errno), CopyString);
  875. }
  876. Variant f_ldap_get_dn(CResRef link, CResRef result_entry) {
  877. LdapLink *ld = link.getTyped<LdapLink>();
  878. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  879. char *text = ldap_get_dn(ld->link, entry->data);
  880. if (text) {
  881. String ret(text, CopyString);
  882. ldap_memfree(text);
  883. return ret;
  884. }
  885. return false;
  886. }
  887. int64_t f_ldap_count_entries(CResRef link, CResRef result) {
  888. LdapLink *ld = link.getTyped<LdapLink>();
  889. LdapResult *res = result.getTyped<LdapResult>();
  890. return ldap_count_entries(ld->link, res->data);
  891. }
  892. Variant f_ldap_get_entries(CResRef link, CResRef result) {
  893. LdapLink *ld = link.getTyped<LdapLink>();
  894. LdapResult *res = result.getTyped<LdapResult>();
  895. LDAP *ldap = ld->link;
  896. int num_entries = ldap_count_entries(ldap, res->data);
  897. Array ret;
  898. ret.set(s_count, num_entries);
  899. if (num_entries == 0) {
  900. return uninit_null();
  901. }
  902. LDAPMessage *ldap_result_entry = ldap_first_entry(ldap, res->data);
  903. if (ldap_result_entry == NULL) {
  904. return false;
  905. }
  906. num_entries = 0;
  907. while (ldap_result_entry != NULL) {
  908. Array tmp1 = Array::Create();
  909. get_attributes(tmp1, ldap, ldap_result_entry, true);
  910. char *dn = ldap_get_dn(ldap, ldap_result_entry);
  911. tmp1.set(s_dn, String(dn, CopyString));
  912. ldap_memfree(dn);
  913. ret.set(num_entries, tmp1);
  914. num_entries++;
  915. ldap_result_entry = ldap_next_entry(ldap, ldap_result_entry);
  916. }
  917. ret.set(s_count, num_entries);
  918. return ret;
  919. }
  920. Variant f_ldap_first_entry(CResRef link, CResRef result) {
  921. LdapLink *ld = link.getTyped<LdapLink>();
  922. LdapResult *res = result.getTyped<LdapResult>();
  923. LDAPMessage *entry;
  924. if ((entry = ldap_first_entry(ld->link, res->data)) == NULL) {
  925. return false;
  926. }
  927. return NEWOBJ(LdapResultEntry)(entry, res);
  928. }
  929. Variant f_ldap_next_entry(CResRef link, CResRef result_entry) {
  930. LdapLink *ld = link.getTyped<LdapLink>();
  931. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  932. LDAPMessage *msg;
  933. if ((msg = ldap_next_entry(ld->link, entry->data)) == NULL) {
  934. return false;
  935. }
  936. return NEWOBJ(LdapResultEntry)(msg, entry->result.get());
  937. }
  938. Array f_ldap_get_attributes(CResRef link, CResRef result_entry) {
  939. LdapLink *ld = link.getTyped<LdapLink>();
  940. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  941. Array ret = Array::Create();
  942. get_attributes(ret, ld->link, entry->data, false);
  943. return ret;
  944. }
  945. Variant f_ldap_first_attribute(CResRef link, CResRef result_entry) {
  946. LdapLink *ld = link.getTyped<LdapLink>();
  947. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  948. char *attribute;
  949. if ((attribute =
  950. ldap_first_attribute(ld->link, entry->data, &entry->ber)) == NULL) {
  951. return false;
  952. }
  953. String ret(attribute, CopyString);
  954. ldap_memfree(attribute);
  955. return ret;
  956. }
  957. Variant f_ldap_next_attribute(CResRef link, CResRef result_entry) {
  958. LdapLink *ld = link.getTyped<LdapLink>();
  959. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  960. if (entry->ber == NULL) {
  961. raise_warning("called before calling ldap_first_attribute() or "
  962. "no attributes found in result entry");
  963. return false;
  964. }
  965. char *attribute;
  966. if ((attribute =
  967. ldap_next_attribute(ld->link, entry->data, entry->ber)) == NULL) {
  968. if (entry->ber != NULL) {
  969. ber_free(entry->ber, 0);
  970. entry->ber = NULL;
  971. }
  972. return false;
  973. }
  974. String ret(attribute, CopyString);
  975. ldap_memfree(attribute);
  976. return ret;
  977. }
  978. Variant f_ldap_first_reference(CResRef link, CResRef result) {
  979. LdapLink *ld = link.getTyped<LdapLink>();
  980. LdapResult *res = result.getTyped<LdapResult>();
  981. LDAPMessage *entry;
  982. if ((entry = ldap_first_reference(ld->link, res->data)) == NULL) {
  983. return false;
  984. }
  985. return NEWOBJ(LdapResultEntry)(entry, res);
  986. }
  987. Variant f_ldap_next_reference(CResRef link, CResRef result_entry) {
  988. LdapLink *ld = link.getTyped<LdapLink>();
  989. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  990. LDAPMessage *entry_next;
  991. if ((entry_next = ldap_next_reference(ld->link, entry->data)) == NULL) {
  992. return false;
  993. }
  994. return NEWOBJ(LdapResultEntry)(entry_next, entry->result.get());
  995. }
  996. bool f_ldap_parse_reference(CResRef link, CResRef result_entry,
  997. VRefParam referrals) {
  998. LdapLink *ld = link.getTyped<LdapLink>();
  999. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  1000. char **lreferrals, **refp;
  1001. if (ldap_parse_reference(ld->link, entry->data, &lreferrals,
  1002. NULL /* &serverctrls */, 0) != LDAP_SUCCESS) {
  1003. return false;
  1004. }
  1005. Array arr = Array::Create();
  1006. if (lreferrals != NULL) {
  1007. refp = lreferrals;
  1008. while (*refp) {
  1009. arr.append(String(*refp, CopyString));
  1010. refp++;
  1011. }
  1012. ldap_value_free(lreferrals);
  1013. }
  1014. referrals = arr;
  1015. return true;
  1016. }
  1017. bool f_ldap_parse_result(CResRef link, CResRef result, VRefParam errcode,
  1018. VRefParam matcheddn /* = null */,
  1019. VRefParam errmsg /* = null */,
  1020. VRefParam referrals /* = null */) {
  1021. LdapLink *ld = link.getTyped<LdapLink>();
  1022. LdapResult *res = result.getTyped<LdapResult>();
  1023. int lerrcode;
  1024. char **lreferrals, **refp;
  1025. char *lmatcheddn, *lerrmsg;
  1026. int rc = ldap_parse_result(ld->link, res->data, &lerrcode,
  1027. &lmatcheddn, &lerrmsg, &lreferrals,
  1028. NULL /* &serverctrls */, 0);
  1029. if (rc != LDAP_SUCCESS) {
  1030. raise_warning("Unable to parse result: %s", ldap_err2string(rc));
  1031. return false;
  1032. }
  1033. errcode = lerrcode;
  1034. /* Reverse -> fall through */
  1035. Array arr = Array::Create();
  1036. if (lreferrals != NULL) {
  1037. refp = lreferrals;
  1038. while (*refp) {
  1039. arr.append(String(*refp, CopyString));
  1040. refp++;
  1041. }
  1042. ldap_value_free(lreferrals);
  1043. }
  1044. referrals = arr;
  1045. if (lerrmsg == NULL) {
  1046. errmsg = String("");
  1047. } else {
  1048. errmsg = String(lerrmsg, CopyString);
  1049. ldap_memfree(lerrmsg);
  1050. }
  1051. if (lmatcheddn == NULL) {
  1052. matcheddn = String("");
  1053. } else {
  1054. matcheddn = String(lmatcheddn, CopyString);
  1055. ldap_memfree(lmatcheddn);
  1056. }
  1057. return true;
  1058. }
  1059. bool f_ldap_free_result(CResRef result) {
  1060. LdapResult *res = result.getTyped<LdapResult>();
  1061. res->close();
  1062. return true;
  1063. }
  1064. Variant f_ldap_get_values_len(CResRef link, CResRef result_entry,
  1065. const String& attribute) {
  1066. LdapLink *ld = link.getTyped<LdapLink>();
  1067. LdapResultEntry *entry = result_entry.getTyped<LdapResultEntry>();
  1068. struct berval **ldap_value_len;
  1069. if ((ldap_value_len =
  1070. ldap_get_values_len(ld->link, entry->data,
  1071. (char*)attribute.data())) == NULL) {
  1072. raise_warning("Cannot get the value(s) of attribute %s",
  1073. ldap_err2string(_get_lderrno(ld->link)));
  1074. return false;
  1075. }
  1076. int num_values = ldap_count_values_len(ldap_value_len);
  1077. Array ret;
  1078. for (int i = 0; i < num_values; i++) {
  1079. ret.append(String(ldap_value_len[i]->bv_val, ldap_value_len[i]->bv_len,
  1080. CopyString));
  1081. }
  1082. ret.set(s_count, num_values);
  1083. ldap_value_free_len(ldap_value_len);
  1084. return ret;
  1085. }
  1086. Variant f_ldap_get_values(CResRef link, CResRef result_entry,
  1087. const String& attribute) {
  1088. return f_ldap_get_values_len(link, result_entry, attribute);
  1089. }
  1090. ///////////////////////////////////////////////////////////////////////////////
  1091. }