codemod 2010-2016 to 2010-present
[hiphop-php.git] / hphp / runtime / ext / ldap / ext_ldap.cpp
blobfe24bf4d043848530153ff3e9394e3e926ac7ccc
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/ldap/ext_ldap.h"
19 #include "hphp/runtime/ext/std/ext_std_function.h"
20 #include "hphp/runtime/base/array-init.h"
21 #include "hphp/runtime/base/builtin-functions.h"
22 #include "hphp/util/thread-local.h"
23 #include <folly/String.h>
24 #include <lber.h>
25 #include "hphp/util/text-util.h"
27 #define LDAP_DEPRECATED 1
28 #include <ldap.h>
30 #include <folly/portability/SysTime.h>
32 #define PHP_LD_FULL_ADD 0xff
34 namespace HPHP {
36 const int64_t
37 k_LDAP_ESCAPE_FILTER = 1<<0,
38 k_LDAP_ESCAPE_DN = 1<<1;
40 #define LDAP_MODIFY_BATCH_ADD 0x01
41 #define LDAP_MODIFY_BATCH_REMOVE 0x02
42 #define LDAP_MODIFY_BATCH_REMOVE_ALL 0x12
43 #define LDAP_MODIFY_BATCH_REPLACE 0x03
44 #define LDAP_MODIFY_BATCH_ATTRIB "attrib"
45 #define LDAP_MODIFY_BATCH_MODTYPE "modtype"
46 #define LDAP_MODIFY_BATCH_VALUES "values"
48 static const StaticString
49 s_LDAP_MODIFY_BATCH_ATTRIB(LDAP_MODIFY_BATCH_ATTRIB),
50 s_LDAP_MODIFY_BATCH_MODTYPE(LDAP_MODIFY_BATCH_MODTYPE),
51 s_LDAP_MODIFY_BATCH_VALUES(LDAP_MODIFY_BATCH_VALUES);
53 static struct LdapExtension final : Extension {
54 LdapExtension() : Extension("ldap", NO_EXTENSION_VERSION_YET) {}
55 void moduleInit() override {
56 HHVM_RC_INT(LDAP_ESCAPE_FILTER, k_LDAP_ESCAPE_FILTER);
57 HHVM_RC_INT(LDAP_ESCAPE_DN, k_LDAP_ESCAPE_DN);
59 HHVM_FE(ldap_connect);
60 HHVM_FE(ldap_explode_dn);
61 HHVM_FE(ldap_dn2ufn);
62 HHVM_FE(ldap_err2str);
63 HHVM_FE(ldap_add);
64 HHVM_FE(ldap_mod_add);
65 HHVM_FE(ldap_mod_del);
66 HHVM_FE(ldap_mod_replace);
67 HHVM_FE(ldap_modify);
68 HHVM_FE(ldap_modify_batch);
69 HHVM_FE(ldap_bind);
70 HHVM_FE(ldap_set_rebind_proc);
71 HHVM_FE(ldap_sort);
72 HHVM_FE(ldap_start_tls);
73 HHVM_FE(ldap_unbind);
74 HHVM_FE(ldap_get_option);
75 HHVM_FE(ldap_set_option);
76 HHVM_FE(ldap_close);
77 HHVM_FE(ldap_list);
78 HHVM_FE(ldap_read);
79 HHVM_FE(ldap_search);
80 HHVM_FE(ldap_rename);
81 HHVM_FE(ldap_delete);
82 HHVM_FE(ldap_compare);
83 HHVM_FE(ldap_errno);
84 HHVM_FE(ldap_error);
85 HHVM_FE(ldap_get_dn);
86 HHVM_FE(ldap_count_entries);
87 HHVM_FE(ldap_get_entries);
88 HHVM_FE(ldap_first_entry);
89 HHVM_FE(ldap_next_entry);
90 HHVM_FE(ldap_get_attributes);
91 HHVM_FE(ldap_first_attribute);
92 HHVM_FE(ldap_next_attribute);
93 HHVM_FE(ldap_first_reference);
94 HHVM_FE(ldap_next_reference);
95 HHVM_FE(ldap_parse_reference);
96 HHVM_FE(ldap_parse_result);
97 HHVM_FE(ldap_free_result);
98 HHVM_FE(ldap_get_values_len);
99 HHVM_FE(ldap_get_values);
100 HHVM_FE(ldap_control_paged_result);
101 HHVM_FE(ldap_control_paged_result_response);
102 HHVM_FE(ldap_escape);
104 HHVM_RC_INT_SAME(LDAP_DEREF_ALWAYS);
105 HHVM_RC_INT_SAME(LDAP_DEREF_FINDING);
106 HHVM_RC_INT_SAME(LDAP_DEREF_NEVER);
107 HHVM_RC_INT_SAME(LDAP_DEREF_SEARCHING);
109 HHVM_RC_INT_SAME(LDAP_MODIFY_BATCH_ADD);
110 HHVM_RC_INT_SAME(LDAP_MODIFY_BATCH_REMOVE);
111 HHVM_RC_INT_SAME(LDAP_MODIFY_BATCH_REMOVE_ALL);
112 HHVM_RC_INT_SAME(LDAP_MODIFY_BATCH_REPLACE);
113 HHVM_RC_STR_SAME(LDAP_MODIFY_BATCH_ATTRIB);
114 HHVM_RC_STR_SAME(LDAP_MODIFY_BATCH_MODTYPE);
115 HHVM_RC_STR_SAME(LDAP_MODIFY_BATCH_VALUES);
117 HHVM_RC_INT_SAME(LDAP_OPT_DEREF);
118 HHVM_RC_INT_SAME(LDAP_OPT_SIZELIMIT);
119 HHVM_RC_INT_SAME(LDAP_OPT_TIMELIMIT);
120 HHVM_RC_INT_SAME(LDAP_OPT_PROTOCOL_VERSION);
121 HHVM_RC_INT_SAME(LDAP_OPT_ERROR_NUMBER);
122 HHVM_RC_INT_SAME(LDAP_OPT_REFERRALS);
123 HHVM_RC_INT_SAME(LDAP_OPT_ERROR_STRING);
124 HHVM_RC_INT_SAME(LDAP_OPT_SERVER_CONTROLS);
125 HHVM_RC_INT_SAME(LDAP_OPT_CLIENT_CONTROLS);
127 #ifdef LDAP_OPT_NETWORK_TIMEOUT
128 HHVM_RC_INT_SAME(LDAP_OPT_NETWORK_TIMEOUT);
129 #elif defined(LDAP_X_OPT_NETWORK_TIMEOUT)
130 HHVM_RC_INT(LDAP_OPT_NETWORK_TIMEOUT, LDAP_X_OPT_NETWORK_TIMEOUT);
131 #endif
132 #ifdef LDAP_OPT_TIMEOUT
133 HHVM_RC_INT_SAME(LDAP_OPT_TIMEOUT);
134 #endif
135 #ifdef LDAP_OPT_RESTART
136 HHVM_RC_INT_SAME(LDAP_OPT_RESTART);
137 #endif
138 #ifdef LDAP_OPT_HOST_NAME
139 HHVM_RC_INT_SAME(LDAP_OPT_HOST_NAME);
140 #endif
141 #ifdef LDAP_OPT_MATCHED_DN
142 HHVM_RC_INT_SAME(LDAP_OPT_MATCHED_DN);
143 #endif
144 #ifdef LDAP_OPT_DEBUG_LEVEL
145 HHVM_RC_INT_SAME(LDAP_OPT_DEBUG_LEVEL);
146 #endif
148 loadSystemlib();
150 } s_ldap_extension;
152 ///////////////////////////////////////////////////////////////////////////////
154 struct LdapRequestData {
155 LdapRequestData() : m_num_links(0), m_max_links(-1) {
158 long m_num_links;
159 long m_max_links;
161 static IMPLEMENT_THREAD_LOCAL(LdapRequestData, s_ldap_data);
162 #define LDAPG(name) s_ldap_data->m_ ## name
164 struct LdapLink : SweepableResourceData {
165 DECLARE_RESOURCE_ALLOCATION(LdapLink)
167 LdapLink() {}
168 ~LdapLink() { closeImpl(); }
170 void close() {
171 closeImpl();
172 rebindproc.unset();
175 bool isInvalid() const override {
176 return link == nullptr;
179 private:
180 void closeImpl();
182 public:
183 CLASSNAME_IS("ldap link");
184 // overriding ResourceData
185 const String& o_getClassNameHook() const override { return classnameof(); }
187 LDAP *link{nullptr};
188 Variant rebindproc;
191 namespace {
193 req::ptr<LdapLink> getLdapLinkFromToken(void* userData) {
194 auto token = reinterpret_cast<MemoryManager::RootId>(userData);
195 return MM().lookupRoot<LdapLink>(token);
198 void* getLdapLinkToken(const req::ptr<LdapLink>& link) {
199 return reinterpret_cast<void*>(MM().addRoot(link));
202 // Note: a raw pointer is ok here since clearLdapLink is being
203 // called from ~LdapLink which might be getting invoked from
204 // sweep and we can't create any new req::ptrs at the point.
205 void clearLdapLink(const LdapLink* link) {
206 MM().removeRoot(link);
211 void LdapLink::sweep() {
212 closeImpl();
213 rebindproc.releaseForSweep();
216 void LdapLink::closeImpl() {
217 if (link) {
218 ldap_unbind_s(link);
219 link = nullptr;
220 LDAPG(num_links)--;
221 clearLdapLink(this);
225 struct LdapResult : SweepableResourceData {
226 DECLARE_RESOURCE_ALLOCATION(LdapResult)
228 LdapResult(LDAPMessage *res) : data(res) {}
229 ~LdapResult() { close();}
231 void close() {
232 if (data) {
233 ldap_msgfree(data);
234 data = NULL;
238 bool isInvalid() const override {
239 return data == nullptr;
242 CLASSNAME_IS("ldap result");
243 // overriding ResourceData
244 const String& o_getClassNameHook() const override { return classnameof();}
246 LDAPMessage *data;
248 IMPLEMENT_RESOURCE_ALLOCATION(LdapResult)
250 struct LdapResultEntry : SweepableResourceData {
251 DECLARE_RESOURCE_ALLOCATION(LdapResultEntry)
253 LdapResultEntry(LDAPMessage *entry, req::ptr<LdapResult> res)
254 : data(entry), ber(nullptr), result(std::move(res)) {}
255 ~LdapResultEntry() { close();}
257 void close() {
258 if (ber != NULL) {
259 ber_free(ber, 0);
260 ber = NULL;
262 data = NULL;
265 bool isInvalid() const override {
266 return data == nullptr;
269 CLASSNAME_IS("ldap result entry");
270 // overriding ResourceData
271 const String& o_getClassNameHook() const override { return classnameof(); }
273 LDAPMessage *data;
274 BerElement *ber;
275 req::ptr<LdapResult> result;
278 void LdapResultEntry::sweep() {
279 close();
282 ///////////////////////////////////////////////////////////////////////////////
284 template<typename T>
285 static req::ptr<LdapLink> get_valid_ldap_link_resource(const T& link) {
286 auto ld = dyn_cast_or_null<LdapLink>(link);
287 if (ld == nullptr || ld->isInvalid()) {
288 raise_warning("Not a valid ldap link resource");
289 return nullptr;
291 return ld;
294 static req::ptr<LdapResult> get_valid_ldap_result_resource(const Resource& result) {
295 auto res = dyn_cast_or_null<LdapResult>(result);
296 if (res == nullptr || res->isInvalid()) {
297 raise_warning("Not a valid ldap result resource");
298 return nullptr;
300 return res;
303 static req::ptr<LdapResultEntry> get_valid_ldap_result_entry_resource(const Resource& result_entry) {
304 auto entry = dyn_cast_or_null<LdapResultEntry>(result_entry);
305 if (entry == nullptr || entry->isInvalid()) {
306 raise_warning("Not a valid ldap result entry resource");
307 return nullptr;
309 return entry;
312 ///////////////////////////////////////////////////////////////////////////////
314 static int _get_lderrno(LDAP *ldap) {
315 int lderr;
316 ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
317 return lderr;
320 static bool php_ldap_do_modify(const Resource& link, const String& dn, const Array& entry,
321 int oper) {
322 bool is_full_add = false; /* flag for full add operation so ldap_mod_add
323 can be put back into oper, gerrit THomson */
325 auto ld = get_valid_ldap_link_resource(link);
326 if (!ld) {
327 return false;
330 int num_attribs = entry.size();
331 LDAPMod **ldap_mods =
332 (LDAPMod **)malloc((num_attribs+1) * sizeof(LDAPMod *));
333 int *num_berval = (int*)malloc(num_attribs * sizeof(int));
335 ArrayIter iter(entry);
336 int num_values;
338 /* added by gerrit thomson to fix ldap_add using ldap_mod_add */
339 if (oper == PHP_LD_FULL_ADD) {
340 oper = LDAP_MOD_ADD;
341 is_full_add = true;
343 /* end additional , gerrit thomson */
345 bool ret = false;
346 Array stringHolder;
347 for (int i = 0; i < num_attribs; i++) {
348 ldap_mods[i] = (LDAPMod*)malloc(sizeof(LDAPMod));
349 ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
350 ldap_mods[i]->mod_type = NULL;
352 Variant key = iter.first();
353 Variant value = iter.second();
354 if (key.isString()) {
355 ldap_mods[i]->mod_type = strdup(key.toString().data());
356 } else {
357 raise_warning("Unknown attribute in the data");
358 /* Free allocated memory */
359 while (i >= 0) {
360 if (ldap_mods[i]->mod_type) {
361 free(ldap_mods[i]->mod_type);
363 free(ldap_mods[i]);
364 i--;
366 free(num_berval);
367 free(ldap_mods);
368 return false;
371 if (!value.isArray()) {
372 num_values = 1;
373 } else {
374 num_values = value.toArray().size();
377 num_berval[i] = num_values;
378 ldap_mods[i]->mod_bvalues =
379 (struct berval**)malloc((num_values + 1) * sizeof(struct berval *));
381 /* allow for arrays with one element, no allowance for arrays with
382 none but probably not required, gerrit thomson. */
383 if (num_values == 1 && !value.isArray()) {
384 String svalue = value.toString();
385 stringHolder.append(svalue);
386 ldap_mods[i]->mod_bvalues[0] = (berval *)malloc(sizeof(struct berval));
387 ldap_mods[i]->mod_bvalues[0]->bv_len = svalue.size();
388 ldap_mods[i]->mod_bvalues[0]->bv_val = (char*)svalue.data();
389 } else {
390 Array arr = value.toArray();
391 for (int j = 0; j < num_values; j++) {
392 if (!arr.exists(j)) {
393 raise_warning("Value array must have consecutive indices 0, 1, ...");
394 num_berval[i] = j;
395 num_attribs = i + 1;
396 goto errexit;
398 String ivalue = arr[j].toString();
399 ldap_mods[i]->mod_bvalues[j] = (berval *)malloc(sizeof(struct berval));
400 ldap_mods[i]->mod_bvalues[j]->bv_len = ivalue.size();
401 ldap_mods[i]->mod_bvalues[j]->bv_val = (char*)ivalue.data();
404 ldap_mods[i]->mod_bvalues[num_values] = NULL;
405 ++iter;
407 ldap_mods[num_attribs] = NULL;
409 /* check flag to see if do_mod was called to perform full add,
410 gerrit thomson */
411 int rc;
412 if (is_full_add) {
413 if ((rc = ldap_add_s(ld->link, (char*)dn.data(), ldap_mods))
414 != LDAP_SUCCESS) {
415 raise_warning("Add: %s", ldap_err2string(rc));
416 } else {
417 ret = true;
419 } else {
420 if ((rc = ldap_modify_s(ld->link, (char*)dn.data(), ldap_mods))
421 != LDAP_SUCCESS) {
422 raise_warning("Modify: %s", ldap_err2string(rc));
423 } else {
424 ret = true;
428 errexit:
429 for (int i = 0; i < num_attribs; i++) {
430 free(ldap_mods[i]->mod_type);
431 for (int j = 0; j < num_berval[i]; j++) {
432 free(ldap_mods[i]->mod_bvalues[j]);
434 free(ldap_mods[i]->mod_bvalues);
435 free(ldap_mods[i]);
437 free(num_berval);
438 free(ldap_mods);
440 return ret;
443 static void php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref,
444 int *old_sizelimit, int *old_timelimit,
445 int *old_deref) {
446 /* sizelimit */
447 if (sizelimit > -1) {
448 ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_sizelimit);
449 ldap_set_option(ldap, LDAP_OPT_SIZELIMIT, &sizelimit);
452 /* timelimit */
453 if (timelimit > -1) {
454 ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_timelimit);
455 ldap_set_option(ldap, LDAP_OPT_TIMELIMIT, &timelimit);
458 /* deref */
459 if (deref > -1) {
460 ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_deref);
461 ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
465 static Variant php_ldap_do_search(const Variant& link, const Variant& base_dn,
466 const Variant& filter,
467 const Variant& attributes,
468 int attrsonly, int sizelimit, int timelimit,
469 int deref, int scope) {
470 const Array& arr_attributes = attributes.isNull()
471 ? null_array
472 : attributes.toArray();
473 int num_attribs = arr_attributes.size();
474 int old_sizelimit = -1, old_timelimit = -1, old_deref = -1;
475 int ldap_err = 1, parallel_search = 1;
476 char **ldap_attrs = (char**)malloc((num_attribs+1) * sizeof(char *));
477 Array stringHolder;
478 Array ret = Array::Create();
480 char *ldap_base_dn = NULL;
481 char *ldap_filter = NULL;
482 req::ptr<LdapLink> ld;
484 for (int i = 0; i < num_attribs; i++) {
485 if (!arr_attributes.exists(i)) {
486 raise_warning("Array initialization wrong");
487 ldap_err = 0;
488 goto cleanup;
490 String attr = arr_attributes[i].toString();
491 stringHolder.append(attr);
492 ldap_attrs[i] = (char*)attr.data();
494 ldap_attrs[num_attribs] = NULL;
496 /* parallel search? */
497 if (link.isArray()) {
498 int nlinks = link.toArray().size();
499 if (nlinks == 0) {
500 raise_warning("No links in link array");
501 ldap_err = 0;
502 goto cleanup;
505 int nbases;
506 if (base_dn.isArray()) {
507 nbases = base_dn.toArray().size();
508 if (nbases != nlinks) {
509 raise_warning("Base must either be a string, or an array with the "
510 "same number of elements as the links array");
511 ldap_err = 0;
512 goto cleanup;
514 } else {
515 nbases = 0; /* this means string, not array */
516 /* If anything else than string is passed, ldap_base_dn = NULL */
517 if (base_dn.isString()) {
518 ldap_base_dn = (char*)base_dn.toString().data();
519 } else {
520 ldap_base_dn = NULL;
524 int nfilters;
525 if (filter.isArray()) {
526 nfilters = filter.toArray().size();
527 if (nfilters != nlinks) {
528 raise_warning("Filter must either be a string, or an array with the "
529 "same number of elements as the links array");
530 ldap_err = 0;
531 goto cleanup;
533 } else {
534 nfilters = 0; /* this means string, not array */
535 String sfilter = filter.toString();
536 stringHolder.append(sfilter);
537 ldap_filter = (char*)sfilter.data();
540 req::vector<req::ptr<LdapLink>> lds;
541 lds.resize(nlinks);
543 req::vector<int> rcs;
544 rcs.resize(nlinks);
546 ArrayIter iter(link.toArray());
547 ArrayIter iterdn(base_dn.toArray());
548 ArrayIter iterfilter(filter.toArray());
549 for (int i = 0; i < nlinks; i++) {
550 ld = get_valid_ldap_link_resource(iter.second());
551 if (!ld) {
552 ldap_err = 0;
553 goto cleanup;
555 if (nbases != 0) { /* base_dn an array? */
556 Variant entry = iterdn.second();
557 ++iterdn;
559 /* If anything else than string is passed, ldap_base_dn = NULL */
560 if (entry.isString()) {
561 ldap_base_dn = (char*)entry.toString().data();
562 } else {
563 ldap_base_dn = NULL;
566 if (nfilters != 0) { /* filter an array? */
567 Variant entry = iterfilter.second();
568 ++iterfilter;
569 String sentry = entry.toString();
570 stringHolder.append(sentry);
571 ldap_filter = (char*)sentry.data();
574 php_set_opts(ld->link, sizelimit, timelimit, deref, &old_sizelimit,
575 &old_timelimit, &old_deref);
577 /* Run the actual search */
578 rcs[i] = ldap_search(ld->link, ldap_base_dn, scope, ldap_filter,
579 ldap_attrs, attrsonly);
580 lds[i] = ld;
581 ++iter;
584 /* Collect results from the searches */
585 for (int i = 0; i < nlinks; i++) {
586 LDAPMessage *ldap_res;
587 if (rcs[i] != -1) {
588 rcs[i] = ldap_result(lds[i]->link, LDAP_RES_ANY, 1 /* LDAP_MSG_ALL */,
589 NULL, &ldap_res);
591 if (rcs[i] != -1) {
592 ret.append(Variant(req::make<LdapResult>(ldap_res)));
593 } else {
594 ret.append(false);
597 } else {
598 /* parallel search? */
599 String sfilter = filter.toString();
600 ldap_filter = (char*)sfilter.data();
602 /* If anything else than string is passed, ldap_base_dn = NULL */
603 if (base_dn.isString()) {
604 ldap_base_dn = (char*)base_dn.toString().data();
607 ld = get_valid_ldap_link_resource(link);
608 if (!ld) {
609 ldap_err = 0;
610 goto cleanup;
613 php_set_opts(ld->link, sizelimit, timelimit, deref, &old_sizelimit,
614 &old_timelimit, &old_deref);
616 /* Run the actual search */
617 LDAPMessage *ldap_res;
618 int rc = ldap_search_s(ld->link, ldap_base_dn, scope, ldap_filter,
619 ldap_attrs, attrsonly, &ldap_res);
621 if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED
622 #ifdef LDAP_ADMINLIMIT_EXCEEDED
623 && rc != LDAP_ADMINLIMIT_EXCEEDED
624 #endif
625 #ifdef LDAP_REFERRAL
626 && rc != LDAP_REFERRAL
627 #endif
629 raise_warning("Search: %s", ldap_err2string(rc));
630 ldap_err = 0;
631 } else {
632 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
633 raise_warning("Partial search results returned: Sizelimit exceeded");
635 #ifdef LDAP_ADMINLIMIT_EXCEEDED
636 else if (rc == LDAP_ADMINLIMIT_EXCEEDED) {
637 raise_warning("Partial search results returned: Adminlimit exceeded");
639 #endif
640 parallel_search = 0;
641 ret.append(Variant(req::make<LdapResult>(ldap_res)));
644 cleanup:
645 if (ld) {
646 /* Restoring previous options */
647 php_set_opts(ld->link, old_sizelimit, old_timelimit, old_deref, &sizelimit,
648 &timelimit, &deref);
650 if (ldap_attrs != NULL) {
651 free(ldap_attrs);
654 if (!ldap_err) {
655 return false;
658 if (!parallel_search) {
659 return ret.dequeue();
660 } else {
661 return ret;
665 static int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req,
666 ber_int_t msgid, void *params) {
667 auto ld = getLdapLinkFromToken(params);
669 /* link exists and callback set? */
670 if (!ld || ld->rebindproc.isNull()) {
671 raise_warning("Link not found or no callback set");
672 return LDAP_OTHER;
675 /* callback */
676 Variant ret = vm_call_user_func
677 (ld->rebindproc, make_packed_array(Variant(ld), String(url, CopyString)));
678 return ret.toInt64();
681 const StaticString
682 s_count("count"),
683 s_dn("dn");
685 static void get_attributes(Array &ret, LDAP *ldap,
686 LDAPMessage *ldap_result_entry, bool to_lower) {
687 int num_attrib = 0;
688 BerElement *ber;
689 char *attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber);
691 while (attribute != NULL) {
692 struct berval **ldap_value =
693 ldap_get_values_len(ldap, ldap_result_entry, attribute);
694 int num_values = ldap_count_values_len(ldap_value);
696 Array tmp;
697 tmp.set(s_count, num_values);
698 for (int i = 0; i < num_values; i++) {
699 tmp.append(String(ldap_value[i]->bv_val, ldap_value[i]->bv_len,
700 CopyString));
702 ldap_value_free_len(ldap_value);
704 String sAttribute = to_lower ? String(toLower(attribute))
705 : String(attribute, CopyString);
706 ret.set(sAttribute, tmp);
707 ret.set(num_attrib, sAttribute);
709 num_attrib++;
710 ldap_memfree(attribute);
711 attribute = ldap_next_attribute(ldap, ldap_result_entry, ber);
714 if (ber != NULL) {
715 ber_free(ber, 0);
718 ret.set(s_count, num_attrib);
721 ///////////////////////////////////////////////////////////////////////////////
723 Variant HHVM_FUNCTION(ldap_connect,
724 const Variant& hostname /* = uninit_variant */,
725 int port /* = 389 */) {
726 const String& str_hostname = hostname.isNull()
727 ? null_string
728 : hostname.toString();
729 if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
730 raise_warning("Too many open links (%ld)", LDAPG(num_links));
731 return false;
734 auto ld = req::make<LdapLink>();
736 LDAP *ldap = NULL;
737 if (!str_hostname.empty() && str_hostname.find('/') >= 0) {
738 int rc = ldap_initialize(&ldap, str_hostname.data());
739 if (rc != LDAP_SUCCESS) {
740 raise_warning("Could not create session handle: %s",
741 ldap_err2string(rc));
742 return false;
744 } else {
745 ldap = ldap_init((char*)str_hostname.data(), port);
748 if (ldap) {
749 LDAPG(num_links)++;
750 ld->link = ldap;
751 return Variant(std::move(ld));
753 raise_warning("Unable to initialize LDAP: %s",
754 folly::errnoStr(errno).c_str());
755 return false;
758 Variant HHVM_FUNCTION(ldap_explode_dn,
759 const String& dn,
760 int with_attrib) {
761 char **ldap_value;
762 if (!(ldap_value = ldap_explode_dn((char*)dn.data(), with_attrib))) {
763 /* Invalid parameters were passed to ldap_explode_dn */
764 return false;
767 int i = 0;
768 while (ldap_value[i] != NULL) i++;
769 int count = i;
771 Array ret;
772 ret.set(s_count, count);
773 for (i = 0; i < count; i++) {
774 ret.append(String(ldap_value[i], CopyString));
777 ldap_value_free(ldap_value);
778 return ret;
781 Variant HHVM_FUNCTION(ldap_dn2ufn,
782 const String& db) {
783 char *ufn = ldap_dn2ufn((char*)db.data());
784 if (ufn) {
785 String ret(ufn, CopyString);
786 ldap_memfree(ufn);
787 return ret;
789 return false;
792 String HHVM_FUNCTION(ldap_err2str,
793 int errnum) {
794 return String(ldap_err2string(errnum), CopyString);
797 bool HHVM_FUNCTION(ldap_add,
798 const Resource& link,
799 const String& dn,
800 const Array& entry) {
801 return php_ldap_do_modify(link, dn, entry, PHP_LD_FULL_ADD);
804 bool HHVM_FUNCTION(ldap_mod_add,
805 const Resource& link,
806 const String& dn,
807 const Array& entry) {
808 return php_ldap_do_modify(link, dn, entry, LDAP_MOD_ADD);
811 bool HHVM_FUNCTION(ldap_mod_del,
812 const Resource& link,
813 const String& dn,
814 const Array& entry) {
815 return php_ldap_do_modify(link, dn, entry, LDAP_MOD_DELETE);
818 bool HHVM_FUNCTION(ldap_mod_replace,
819 const Resource& link,
820 const String& dn,
821 const Array& entry) {
822 return php_ldap_do_modify(link, dn, entry, LDAP_MOD_REPLACE);
825 bool HHVM_FUNCTION(ldap_modify,
826 const Resource& link,
827 const String& dn,
828 const Array& entry) {
829 return php_ldap_do_modify(link, dn, entry, LDAP_MOD_REPLACE);
832 bool HHVM_FUNCTION(ldap_modify_batch,
833 const Resource& link,
834 const String& dn,
835 const Array& modifs) {
837 $modifs = [
839 "attrib" => "unicodePwd",
840 "modtype" => LDAP_MODIFY_BATCH_REMOVE,
841 "values" => [$oldpw]
844 "attrib" => "unicodePwd",
845 "modtype" => LDAP_MODIFY_BATCH_ADD,
846 "values" => [$newpw]
849 "attrib" => "userPrincipalName",
850 "modtype" => LDAP_MODIFY_BATCH_REPLACE,
851 "values" => ["janitor@corp.contoso.com"]
854 "attrib" => "userCert",
855 "modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
860 auto ld = get_valid_ldap_link_resource(link);
861 if (!ld) {
862 return false;
865 ssize_t num_mods = modifs.size();
866 int return_value;
868 /* perform validation */
870 /* make sure the DN contains no NUL bytes */
871 if (dn.find('\0') != String::npos) {
872 raise_warning("DN must not contain NUL bytes");
873 return false;
876 for (ssize_t i = 0; i < num_mods; ++i) {
877 /* are the keys consecutive integers? */
878 if (!modifs.exists((int64_t)i)) {
879 raise_warning("Modifications array must have integer indices "
880 "0, 1, ...");
881 return false;
884 Variant mod = modifs[(int64_t)i];
886 /* is the value an array itself? */
887 if (!mod.isArray()) {
888 raise_warning("Each entry of modifications array must be an array "
889 "itself");
890 return false;
893 /* for the modification hashtable... */
894 const Array& modprops = mod.asCArrRef();
895 ssize_t num_modprops = modprops.size();
896 ArrayIter modprops_iter(modprops);
898 for (ssize_t j = 0; j < num_modprops; ++j) {
899 /* is the key a string? */
900 if (!modprops_iter.first().isString()) {
901 raise_warning("Each entry of modifications array must be "
902 "string-indexed");
903 return false;
906 /* is this a valid entry? */
907 const String& modkey = modprops_iter.first().asCStrRef();
908 if (modkey != s_LDAP_MODIFY_BATCH_ATTRIB
909 && modkey != s_LDAP_MODIFY_BATCH_MODTYPE
910 && modkey != s_LDAP_MODIFY_BATCH_VALUES) {
911 raise_warning("The only allowed keys in entries of the modifications "
912 "array are '" LDAP_MODIFY_BATCH_ATTRIB "', '"
913 LDAP_MODIFY_BATCH_MODTYPE "' and '"
914 LDAP_MODIFY_BATCH_VALUES "'");
915 return false;
918 /* does the value type match the key? */
919 if (modkey == s_LDAP_MODIFY_BATCH_ATTRIB) {
920 if (!modprops_iter.second().isString()) {
921 raise_warning("A '" LDAP_MODIFY_BATCH_ATTRIB "' value must be "
922 "a string");
923 return false;
925 if (modprops_iter.second().asCStrRef().find('\0') != String::npos) {
926 raise_warning("A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not "
927 "contain NUL bytes");
928 return false;
930 } else if (modkey == s_LDAP_MODIFY_BATCH_MODTYPE) {
931 if (!modprops_iter.second().isInteger()) {
932 raise_warning("A '" LDAP_MODIFY_BATCH_MODTYPE "' value must be "
933 "an integer");
934 return false;
937 int64_t modtype = modprops_iter.second().asInt64Val();
938 if (modtype != LDAP_MODIFY_BATCH_ADD
939 && modtype != LDAP_MODIFY_BATCH_REMOVE
940 && modtype != LDAP_MODIFY_BATCH_REPLACE
941 && modtype != LDAP_MODIFY_BATCH_REMOVE_ALL) {
942 raise_warning("The '" LDAP_MODIFY_BATCH_MODTYPE "' value must "
943 "match one of the LDAP_MODIFY_BATCH_* constants");
944 return false;
947 /* if it's REMOVE_ALL, there must not be a values array; */
948 /* otherwise, there must */
949 if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
950 if (modprops.exists(s_LDAP_MODIFY_BATCH_VALUES)) {
951 raise_warning("If '" LDAP_MODIFY_BATCH_MODTYPE "' is "
952 "LDAP_MODIFY_BATCH_REMOVE_ALL, a '"
953 LDAP_MODIFY_BATCH_VALUES "' array must not "
954 "be provided");
955 return false;
957 } else {
958 if (!modprops.exists(s_LDAP_MODIFY_BATCH_VALUES)) {
959 raise_warning("If '" LDAP_MODIFY_BATCH_MODTYPE "' is not "
960 "LDAP_MODIFY_BATCH_REMOVE_ALL, a '"
961 LDAP_MODIFY_BATCH_VALUES "' array must "
962 "be provided");
963 return false;
966 } else if (modkey == s_LDAP_MODIFY_BATCH_VALUES) {
967 if (!modprops_iter.second().isArray()) {
968 raise_warning("A '" LDAP_MODIFY_BATCH_VALUES "' value must be "
969 "an array");
970 return false;
973 const Array& modvalues = modprops_iter.second().asCArrRef();
974 ssize_t num_modvalues = modvalues.size();
976 /* is the array not empty? */
977 if (num_modvalues == 0) {
978 raise_warning("A '" LDAP_MODIFY_BATCH_VALUES "' array must have "
979 "at least one element");
980 return false;
983 /* for the modification hashtable... */
984 for (ssize_t k = 0; k < num_modvalues; ++k) {
985 /* are the keys consecutive integers? */
986 if (!modvalues.exists((int64_t)k)) {
987 raise_warning("A '" LDAP_MODIFY_BATCH_VALUES "' array must have "
988 "integer indices 0, 1, ...");
989 return false;
992 Variant modvalue = modvalues[(int64_t)k];
994 /* is the data element a string? */
995 if (!modvalue.isString()) {
996 raise_warning("Each element of a '" LDAP_MODIFY_BATCH_VALUES "' "
997 "array must be a string");
998 return false;
1003 ++modprops_iter;
1008 /* validation was successful */
1010 /* allocate array of modifications */
1011 req::vector<LDAPMod *> ldap_mods(num_mods + 1);
1014 /* for each modification */
1015 for (int64_t i = 0; i < num_mods; ++i) {
1016 /* allocate the modification struct */
1017 ldap_mods[i] = req::make_raw<LDAPMod>();
1019 /* fetch the relevant data */
1020 const Array& mod = modifs[i].asCArrRef();
1021 const String& attrib = mod[s_LDAP_MODIFY_BATCH_ATTRIB].asCStrRef();
1022 int64_t modtype = mod[s_LDAP_MODIFY_BATCH_MODTYPE].asInt64Val();
1023 const Array& vals = mod[s_LDAP_MODIFY_BATCH_VALUES].asCArrRef();
1025 /* map the modification type */
1026 int oper;
1027 switch (modtype) {
1028 case LDAP_MODIFY_BATCH_ADD:
1029 oper = LDAP_MOD_ADD;
1030 break;
1031 case LDAP_MODIFY_BATCH_REMOVE:
1032 case LDAP_MODIFY_BATCH_REMOVE_ALL:
1033 oper = LDAP_MOD_DELETE;
1034 break;
1035 case LDAP_MODIFY_BATCH_REPLACE:
1036 oper = LDAP_MOD_REPLACE;
1037 break;
1038 default:
1039 raise_error("Unknown and uncaught modification type.");
1040 return false;
1043 /* fill in the basic info */
1044 ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
1045 ldap_mods[i]->mod_type = req::strndup(attrib.data(), attrib.size());
1047 if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
1048 /* no values */
1049 ldap_mods[i]->mod_bvalues = nullptr;
1050 } else {
1051 /* allocate space for the values as part of this modification */
1052 ssize_t num_modvals = vals.size();
1053 ldap_mods[i]->mod_bvalues =
1054 (struct berval **)req::malloc((num_modvals + 1) *
1055 sizeof(struct berval *));
1057 /* for each value */
1058 for (int64_t j = 0; j < num_modvals; ++j) {
1059 /* fetch it */
1060 const String& modval = vals[j].asCStrRef();
1062 /* allocate the data struct */
1063 ldap_mods[i]->mod_bvalues[j] = req::make_raw<struct berval>();
1065 /* fill it */
1066 ldap_mods[i]->mod_bvalues[j]->bv_len = modval.size();
1067 ldap_mods[i]->mod_bvalues[j]->bv_val =
1068 req::make_raw_array<char>(modval.size());
1069 memcpy(ldap_mods[i]->mod_bvalues[j]->bv_val, modval.data(),
1070 modval.size());
1073 /* NULL-terminate values */
1074 ldap_mods[i]->mod_bvalues[num_modvals] = nullptr;
1078 /* NULL-terminate modifications */
1079 ldap_mods[num_mods] = nullptr;
1082 /* perform (finally) */
1083 if ((return_value = ldap_modify_ext_s(ld->link, dn.data(), ldap_mods.data(),
1084 nullptr, nullptr)) != LDAP_SUCCESS) {
1085 raise_warning("Batch Modify: %s", ldap_err2string(return_value));
1086 /* only return after cleanup */
1089 /* clean up */
1091 for (ssize_t i = 0; i < num_mods; ++i) {
1092 /* attribute */
1093 req::free(ldap_mods[i]->mod_type);
1095 if (ldap_mods[i]->mod_bvalues != nullptr) {
1096 /* each BER value */
1097 for (ssize_t j = 0; ldap_mods[i]->mod_bvalues[j] != nullptr; ++j) {
1098 /* free the data bytes */
1099 req::destroy_raw_array(ldap_mods[i]->mod_bvalues[j]->bv_val,
1100 ldap_mods[i]->mod_bvalues[j]->bv_len);
1102 /* free the bvalue struct */
1103 req::destroy_raw(ldap_mods[i]->mod_bvalues[j]);
1106 /* the BER value array */
1107 req::free(ldap_mods[i]->mod_bvalues);
1110 /* the modifications */
1111 req::destroy_raw(ldap_mods[i]);
1115 return (return_value == LDAP_SUCCESS);
1118 bool HHVM_FUNCTION(ldap_bind,
1119 const Resource& link,
1120 const Variant& bind_rdn /* = uninit_variant */,
1121 const Variant& bind_password /* = uninit_variant */) {
1123 auto ld = get_valid_ldap_link_resource(link);
1124 if (!ld) {
1125 return false;
1128 const String& str_bind_rdn = bind_rdn.isNull()
1129 ? null_string
1130 : bind_rdn.toString();
1131 const String& str_bind_password = bind_password.isNull()
1132 ? null_string
1133 : bind_password.toString();
1135 if (memchr(str_bind_rdn.data(), '\0', str_bind_rdn.size()) != nullptr) {
1136 raise_warning("DN contains a null byte");
1137 return false;
1139 if (memchr(str_bind_password.data(), '\0',
1140 str_bind_password.size()) != nullptr) {
1141 raise_warning("Password contains a null byte");
1142 return false;
1145 int rc;
1146 if ((rc = ldap_bind_s(ld->link, (char*)str_bind_rdn.data(),
1147 (char*)str_bind_password.data(),
1148 LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) {
1149 raise_warning("Unable to bind to server: %s", ldap_err2string(rc));
1150 return false;
1152 return true;
1155 bool HHVM_FUNCTION(ldap_set_rebind_proc,
1156 const Resource& link,
1157 const Variant& callback) {
1158 auto ld = get_valid_ldap_link_resource(link);
1159 if (!ld) {
1160 return false;
1163 if (callback.isString() && callback.toString().empty()) {
1164 /* unregister rebind procedure */
1165 if (!ld->rebindproc.isNull()) {
1166 ld->rebindproc.unset();
1167 ldap_set_rebind_proc(ld->link, NULL, NULL);
1169 return true;
1172 /* callable? */
1173 if (!is_callable(callback)) {
1174 raise_warning("Callback argument is not a valid callback");
1175 return false;
1178 /* register rebind procedure */
1179 if (ld->rebindproc.isNull()) {
1180 ldap_set_rebind_proc(ld->link,
1181 _ldap_rebind_proc,
1182 getLdapLinkToken(ld));
1183 } else {
1184 ld->rebindproc.unset();
1187 ld->rebindproc = callback;
1188 return true;
1191 bool HHVM_FUNCTION(ldap_sort,
1192 const Resource& link,
1193 const Resource& result,
1194 const String& sortfilter) {
1195 auto ld = get_valid_ldap_link_resource(link);
1196 if (!ld) {
1197 return false;
1199 auto res = get_valid_ldap_result_resource(result);
1200 if (!res) {
1201 return false;
1204 if (ldap_sort_entries(ld->link, &res->data,
1205 !sortfilter.empty() ? (char*)sortfilter.data() : NULL,
1206 strcmp) != LDAP_SUCCESS) {
1207 raise_warning("%s", ldap_err2string(_get_lderrno(ld->link)));
1208 return false;
1210 return true;
1213 bool HHVM_FUNCTION(ldap_start_tls,
1214 const Resource& link) {
1215 auto ld = get_valid_ldap_link_resource(link);
1216 if (!ld) {
1217 return false;
1219 int rc, protocol = LDAP_VERSION3;
1220 if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol))
1221 != LDAP_SUCCESS) ||
1222 ((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS)) {
1223 raise_warning("Unable to start TLS: %s", ldap_err2string(rc));
1224 return false;
1226 return true;
1229 bool HHVM_FUNCTION(ldap_unbind, const Resource& link) {
1230 auto ld = get_valid_ldap_link_resource(link);
1231 if (!ld) {
1232 return false;
1234 ld->close();
1235 return true;
1238 bool HHVM_FUNCTION(ldap_get_option,
1239 const Resource& link,
1240 int option,
1241 VRefParam retval) {
1242 auto ld = get_valid_ldap_link_resource(link);
1243 if (!ld) {
1244 return false;
1247 switch (option) {
1248 /* options with int value */
1249 case LDAP_OPT_DEREF:
1250 case LDAP_OPT_SIZELIMIT:
1251 case LDAP_OPT_TIMELIMIT:
1252 case LDAP_OPT_PROTOCOL_VERSION:
1253 case LDAP_OPT_ERROR_NUMBER:
1254 case LDAP_OPT_REFERRALS:
1255 #ifdef LDAP_OPT_RESTART
1256 case LDAP_OPT_RESTART:
1257 #endif
1259 int val;
1260 if (ldap_get_option(ld->link, option, &val)) {
1261 return false;
1263 retval.assignIfRef((int64_t)val);
1264 } break;
1265 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1266 case LDAP_OPT_NETWORK_TIMEOUT:
1268 struct timeval *timeout;
1269 if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT,
1270 (void *) &timeout)) {
1271 if (timeout) {
1272 ldap_memfree(timeout);
1274 return false;
1276 retval.assignIfRef((int64_t)timeout->tv_sec);
1277 ldap_memfree(timeout);
1278 } break;
1279 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
1280 case LDAP_X_OPT_CONNECT_TIMEOUT:
1282 int timeout;
1283 if (ldap_get_option(ld->link, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
1284 return false;
1286 retval.assignIfRef((int64_t)(timeout / 1000));
1287 } break;
1288 #endif
1289 /* options with string value */
1290 case LDAP_OPT_ERROR_STRING:
1291 #ifdef LDAP_OPT_HOST_NAME
1292 case LDAP_OPT_HOST_NAME:
1293 #endif
1294 #ifdef HAVE_LDAP_SASL
1295 case LDAP_OPT_X_SASL_MECH:
1296 case LDAP_OPT_X_SASL_REALM:
1297 case LDAP_OPT_X_SASL_AUTHCID:
1298 case LDAP_OPT_X_SASL_AUTHZID:
1299 #endif
1300 #ifdef LDAP_OPT_MATCHED_DN
1301 case LDAP_OPT_MATCHED_DN:
1302 #endif
1304 char *val = NULL;
1305 if (ldap_get_option(ld->link, option, &val) || val == NULL ||
1306 *val == '\0') {
1307 if (val) {
1308 ldap_memfree(val);
1310 return false;
1312 retval.assignIfRef(String(val, CopyString));
1313 ldap_memfree(val);
1314 } break;
1315 /* options not implemented
1316 case LDAP_OPT_SERVER_CONTROLS:
1317 case LDAP_OPT_CLIENT_CONTROLS:
1318 case LDAP_OPT_API_INFO:
1319 case LDAP_OPT_API_FEATURE_INFO:
1321 default:
1322 return false;
1324 return true;
1327 const StaticString
1328 s_oid("oid"),
1329 s_value("value"),
1330 s_iscritical("iscritical");
1332 bool HHVM_FUNCTION(ldap_set_option,
1333 const Variant& link,
1334 int option,
1335 const Variant& newval) {
1336 LDAP *ldap = NULL;
1337 if (!link.isNull()) {
1338 auto ld = get_valid_ldap_link_resource(link);
1339 if (!ld) {
1340 return false;
1342 ldap = ld->link;
1345 switch (option) {
1346 /* options with int value */
1347 case LDAP_OPT_DEREF:
1348 case LDAP_OPT_SIZELIMIT:
1349 case LDAP_OPT_TIMELIMIT:
1350 case LDAP_OPT_PROTOCOL_VERSION:
1351 case LDAP_OPT_ERROR_NUMBER:
1352 #ifdef LDAP_OPT_DEBUG_LEVEL
1353 case LDAP_OPT_DEBUG_LEVEL:
1354 #endif
1356 int val = newval.toInt64();
1357 if (ldap_set_option(ldap, option, &val)) {
1358 return false;
1360 } break;
1361 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1362 case LDAP_OPT_NETWORK_TIMEOUT:
1364 struct timeval timeout;
1365 timeout.tv_sec = newval.toInt64();
1366 timeout.tv_usec = 0;
1367 if (ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
1368 return false;
1370 } break;
1371 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
1372 case LDAP_X_OPT_CONNECT_TIMEOUT:
1374 int timeout = 1000 * newval.toInt64(); /* Convert to milliseconds */
1375 if (ldap_set_option(ldap, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
1376 return false;
1378 } break;
1379 #endif
1380 /* options with string value */
1381 case LDAP_OPT_ERROR_STRING:
1382 #ifdef LDAP_OPT_HOST_NAME
1383 case LDAP_OPT_HOST_NAME:
1384 #endif
1385 #ifdef HAVE_LDAP_SASL
1386 case LDAP_OPT_X_SASL_MECH:
1387 case LDAP_OPT_X_SASL_REALM:
1388 case LDAP_OPT_X_SASL_AUTHCID:
1389 case LDAP_OPT_X_SASL_AUTHZID:
1390 #endif
1391 #ifdef LDAP_OPT_MATCHED_DN
1392 case LDAP_OPT_MATCHED_DN:
1393 #endif
1395 String snewval = newval.toString();
1396 char *val = (char*)snewval.data();
1397 if (ldap_set_option(ldap, option, val)) {
1398 return false;
1400 } break;
1401 /* options with boolean value */
1402 case LDAP_OPT_REFERRALS:
1403 #ifdef LDAP_OPT_RESTART
1404 case LDAP_OPT_RESTART:
1405 #endif
1407 void *val = newval.toBoolean() ? LDAP_OPT_ON : LDAP_OPT_OFF;
1408 if (ldap_set_option(ldap, option, val)) {
1409 return false;
1411 } break;
1412 /* options with control list value */
1413 case LDAP_OPT_SERVER_CONTROLS:
1414 case LDAP_OPT_CLIENT_CONTROLS:
1416 LDAPControl *ctrl, **ctrls, **ctrlp;
1417 int ncontrols;
1418 char error=0;
1420 if (!newval.isArray() || !(ncontrols = newval.toArray().size())) {
1421 raise_warning("Expected non-empty array value for this option");
1422 return false;
1424 ctrls = (LDAPControl**)malloc((1 + ncontrols) * sizeof(*ctrls));
1425 *ctrls = NULL;
1426 ctrlp = ctrls;
1427 Array stringHolder;
1428 for (ArrayIter iter(newval.toArray()); iter; ++iter) {
1429 Variant vctrlval = iter.second();
1430 if (!vctrlval.isArray()) {
1431 raise_warning("The array value must contain only arrays, "
1432 "where each array is a control");
1433 error = 1;
1434 break;
1436 Array ctrlval = vctrlval.toArray();
1437 if (!ctrlval.exists(s_oid)) {
1438 raise_warning("Control must have an oid key");
1439 error = 1;
1440 break;
1442 String val = ctrlval[s_oid].toString();
1443 stringHolder.append(val);
1444 ctrl = *ctrlp = (LDAPControl*)malloc(sizeof(**ctrlp));
1445 ctrl->ldctl_oid = (char*)val.data();
1446 if (ctrlval.exists(s_value)) {
1447 val = ctrlval[s_value].toString();
1448 stringHolder.append(val);
1449 ctrl->ldctl_value.bv_val = (char*)val.data();
1450 ctrl->ldctl_value.bv_len = val.size();
1451 } else {
1452 ctrl->ldctl_value.bv_val = NULL;
1453 ctrl->ldctl_value.bv_len = 0;
1455 if (ctrlval.exists(s_iscritical)) {
1456 ctrl->ldctl_iscritical = val.toBoolean() ? 1 : 0;
1457 } else {
1458 ctrl->ldctl_iscritical = 0;
1461 ++ctrlp;
1462 *ctrlp = NULL;
1464 if (!error) {
1465 error = ldap_set_option(ldap, option, ctrls);
1467 ctrlp = ctrls;
1468 while (*ctrlp) {
1469 free(*ctrlp);
1470 ctrlp++;
1472 free(ctrls);
1473 if (error) {
1474 return false;
1476 } break;
1477 default:
1478 return false;
1480 return true;
1483 bool HHVM_FUNCTION(ldap_close,
1484 const Resource& link) {
1485 return f_ldap_unbind(link);
1488 Variant HHVM_FUNCTION(ldap_list,
1489 const Variant& link,
1490 const Variant& base_dn,
1491 const Variant& filter,
1492 const Variant& attributes /* = uninit_variant */,
1493 int attrsonly /* = 0 */,
1494 int sizelimit /* = -1 */,
1495 int timelimit /* = -1 */,
1496 int deref /* = -1 */) {
1497 return php_ldap_do_search(link, base_dn, filter, attributes, attrsonly,
1498 sizelimit, timelimit, deref, LDAP_SCOPE_ONELEVEL);
1501 Variant HHVM_FUNCTION(ldap_read,
1502 const Variant& link,
1503 const Variant& base_dn,
1504 const Variant& filter,
1505 const Variant& attributes /* = uninit_variant */,
1506 int attrsonly /* = 0 */,
1507 int sizelimit /* = -1 */,
1508 int timelimit /* = -1 */,
1509 int deref /* = -1 */) {
1510 return php_ldap_do_search(link, base_dn, filter, attributes, attrsonly,
1511 sizelimit, timelimit, deref, LDAP_SCOPE_BASE);
1514 Variant HHVM_FUNCTION(ldap_search,
1515 const Variant& link,
1516 const Variant& base_dn,
1517 const Variant& filter,
1518 const Variant& attributes /* = uninit_variant */,
1519 int attrsonly /* = 0 */,
1520 int sizelimit /* = -1 */,
1521 int timelimit /* = -1 */,
1522 int deref /* = -1 */) {
1523 return php_ldap_do_search(link, base_dn, filter, attributes, attrsonly,
1524 sizelimit, timelimit, deref, LDAP_SCOPE_SUBTREE);
1527 bool HHVM_FUNCTION(ldap_rename,
1528 const Resource& link,
1529 const String& dn,
1530 const String& newrdn,
1531 const String& newparent,
1532 bool deleteoldrdn) {
1533 auto ld = get_valid_ldap_link_resource(link);
1534 if (!ld) {
1535 return false;
1538 int rc = ldap_rename_s(ld->link, (char*)dn.data(), (char*)newrdn.data(),
1539 !newparent.empty() ? (char*)newparent.data() : NULL,
1540 deleteoldrdn, NULL, NULL);
1541 return rc == LDAP_SUCCESS;
1544 bool HHVM_FUNCTION(ldap_delete,
1545 const Resource& link,
1546 const String& dn) {
1547 auto ld = get_valid_ldap_link_resource(link);
1548 if (!ld) {
1549 return false;
1552 int rc;
1553 if ((rc = ldap_delete_s(ld->link, (char*)dn.data())) != LDAP_SUCCESS) {
1554 raise_warning("Delete: %s", ldap_err2string(rc));
1555 return false;
1557 return true;
1560 Variant HHVM_FUNCTION(ldap_compare,
1561 const Resource& link,
1562 const String& dn,
1563 const String& attribute,
1564 const String& value) {
1565 auto ld = get_valid_ldap_link_resource(link);
1566 if (!ld) {
1567 return -1LL;
1570 int rc = ldap_compare_s(ld->link, (char*)dn.data(), (char*)attribute.data(),
1571 (char*)value.data());
1572 switch (rc) {
1573 case LDAP_COMPARE_TRUE: return true;
1574 case LDAP_COMPARE_FALSE: return false;
1576 raise_warning("Compare: %s", ldap_err2string(rc));
1577 return -1LL;
1580 Variant HHVM_FUNCTION(ldap_errno,
1581 const Resource& link) {
1582 auto ld = get_valid_ldap_link_resource(link);
1583 if (!ld) {
1584 return false;
1587 return _get_lderrno(ld->link);
1590 Variant HHVM_FUNCTION(ldap_error,
1591 const Resource& link) {
1592 auto ld = get_valid_ldap_link_resource(link);
1593 if (!ld) {
1594 return false;
1597 int ld_errno = _get_lderrno(ld->link);
1598 return String(ldap_err2string(ld_errno), CopyString);
1601 Variant HHVM_FUNCTION(ldap_get_dn,
1602 const Resource& link,
1603 const Resource& result_entry) {
1604 auto ld = get_valid_ldap_link_resource(link);
1605 if (!ld) {
1606 return false;
1608 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1609 if (!entry) {
1610 return false;
1613 char *text = ldap_get_dn(ld->link, entry->data);
1614 if (text) {
1615 String ret(text, CopyString);
1616 ldap_memfree(text);
1617 return ret;
1619 return false;
1622 Variant HHVM_FUNCTION(ldap_count_entries,
1623 const Resource& link,
1624 const Resource& result) {
1625 auto ld = get_valid_ldap_link_resource(link);
1626 if (!ld) {
1627 return false;
1629 auto res = get_valid_ldap_result_resource(result);
1630 if (!res) {
1631 return false;
1634 return ldap_count_entries(ld->link, res->data);
1637 Variant HHVM_FUNCTION(ldap_get_entries,
1638 const Resource& link,
1639 const Resource& result) {
1640 auto ld = get_valid_ldap_link_resource(link);
1641 if (!ld) {
1642 return false;
1644 auto res = get_valid_ldap_result_resource(result);
1645 if (!res) {
1646 return false;
1649 LDAP *ldap = ld->link;
1651 int num_entries = ldap_count_entries(ldap, res->data);
1652 Array ret;
1653 ret.set(s_count, num_entries);
1654 if (num_entries == 0) {
1655 return init_null();
1658 LDAPMessage *ldap_result_entry = ldap_first_entry(ldap, res->data);
1659 if (ldap_result_entry == NULL) {
1660 return false;
1663 num_entries = 0;
1664 while (ldap_result_entry != NULL) {
1665 Array tmp1 = Array::Create();
1666 get_attributes(tmp1, ldap, ldap_result_entry, true);
1668 char *dn = ldap_get_dn(ldap, ldap_result_entry);
1669 tmp1.set(s_dn, String(dn, CopyString));
1670 ldap_memfree(dn);
1672 ret.set(num_entries, tmp1);
1674 num_entries++;
1675 ldap_result_entry = ldap_next_entry(ldap, ldap_result_entry);
1678 ret.set(s_count, num_entries);
1679 return ret;
1682 Variant HHVM_FUNCTION(ldap_first_entry,
1683 const Resource& link,
1684 const Resource& result) {
1685 auto ld = get_valid_ldap_link_resource(link);
1686 if (!ld) {
1687 return false;
1689 auto res = get_valid_ldap_result_resource(result);
1690 if (!res) {
1691 return false;
1694 LDAPMessage *entry;
1695 if ((entry = ldap_first_entry(ld->link, res->data)) == NULL) {
1696 return false;
1699 return Variant(req::make<LdapResultEntry>(entry, res));
1702 Variant HHVM_FUNCTION(ldap_next_entry,
1703 const Resource& link,
1704 const Resource& result_entry) {
1705 auto ld = get_valid_ldap_link_resource(link);
1706 if (!ld) {
1707 return false;
1709 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1710 if (!entry) {
1711 return false;
1714 LDAPMessage *msg;
1715 if ((msg = ldap_next_entry(ld->link, entry->data)) == NULL) {
1716 return false;
1719 return Variant(req::make<LdapResultEntry>(msg, entry->result));
1722 Variant HHVM_FUNCTION(ldap_get_attributes,
1723 const Resource& link,
1724 const Resource& result_entry) {
1725 auto ld = get_valid_ldap_link_resource(link);
1726 if (!ld) {
1727 return false;
1729 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1730 if (!entry) {
1731 return false;
1734 Array ret = Array::Create();
1735 get_attributes(ret, ld->link, entry->data, false);
1736 return ret;
1739 Variant HHVM_FUNCTION(ldap_first_attribute,
1740 const Resource& link,
1741 const Resource& result_entry) {
1742 auto ld = get_valid_ldap_link_resource(link);
1743 if (!ld) {
1744 return false;
1746 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1747 if (!entry) {
1748 return false;
1751 char *attribute;
1752 if ((attribute =
1753 ldap_first_attribute(ld->link, entry->data, &entry->ber)) == NULL) {
1754 return false;
1756 String ret(attribute, CopyString);
1757 ldap_memfree(attribute);
1758 return ret;
1761 Variant HHVM_FUNCTION(ldap_next_attribute,
1762 const Resource& link,
1763 const Resource& result_entry) {
1764 auto ld = get_valid_ldap_link_resource(link);
1765 if (!ld) {
1766 return false;
1768 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1769 if (!entry) {
1770 return false;
1773 if (entry->ber == NULL) {
1774 raise_warning("called before calling ldap_first_attribute() or "
1775 "no attributes found in result entry");
1776 return false;
1779 char *attribute;
1780 if ((attribute =
1781 ldap_next_attribute(ld->link, entry->data, entry->ber)) == NULL) {
1782 if (entry->ber != NULL) {
1783 ber_free(entry->ber, 0);
1784 entry->ber = NULL;
1786 return false;
1788 String ret(attribute, CopyString);
1789 ldap_memfree(attribute);
1790 return ret;
1793 Variant HHVM_FUNCTION(ldap_first_reference,
1794 const Resource& link,
1795 const Resource& result) {
1796 auto ld = get_valid_ldap_link_resource(link);
1797 if (!ld) {
1798 return false;
1800 auto res = get_valid_ldap_result_resource(result);
1801 if (!res) {
1802 return false;
1805 LDAPMessage *entry;
1806 if ((entry = ldap_first_reference(ld->link, res->data)) == NULL) {
1807 return false;
1810 return Variant(req::make<LdapResultEntry>(entry, res));
1813 Variant HHVM_FUNCTION(ldap_next_reference,
1814 const Resource& link,
1815 const Resource& result_entry) {
1816 auto ld = get_valid_ldap_link_resource(link);
1817 if (!ld) {
1818 return false;
1820 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1821 if (!entry) {
1822 return false;
1825 LDAPMessage *entry_next;
1826 if ((entry_next = ldap_next_reference(ld->link, entry->data)) == NULL) {
1827 return false;
1830 return Variant(req::make<LdapResultEntry>(entry_next, entry->result));
1833 bool HHVM_FUNCTION(ldap_parse_reference,
1834 const Resource& link,
1835 const Resource& result_entry,
1836 VRefParam referrals) {
1837 auto ld = get_valid_ldap_link_resource(link);
1838 if (!ld) {
1839 return false;
1841 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1842 if (!entry) {
1843 return false;
1846 char **lreferrals, **refp;
1847 if (ldap_parse_reference(ld->link, entry->data, &lreferrals,
1848 NULL /* &serverctrls */, 0) != LDAP_SUCCESS) {
1849 return false;
1852 Array arr = Array::Create();
1853 if (lreferrals != NULL) {
1854 refp = lreferrals;
1855 while (*refp) {
1856 arr.append(String(*refp, CopyString));
1857 refp++;
1859 ldap_value_free(lreferrals);
1861 referrals.assignIfRef(arr);
1862 return true;
1865 bool HHVM_FUNCTION(ldap_parse_result,
1866 const Resource& link,
1867 const Resource& result,
1868 VRefParam errcode,
1869 VRefParam matcheddn /* = null */,
1870 VRefParam errmsg /* = null */,
1871 VRefParam referrals /* = null */) {
1872 auto ld = get_valid_ldap_link_resource(link);
1873 if (!ld) {
1874 return false;
1876 auto res = get_valid_ldap_result_resource(result);
1877 if (!res) {
1878 return false;
1881 int lerrcode;
1882 char **lreferrals, **refp;
1883 char *lmatcheddn, *lerrmsg;
1884 int rc = ldap_parse_result(ld->link, res->data, &lerrcode,
1885 &lmatcheddn, &lerrmsg, &lreferrals,
1886 NULL /* &serverctrls */, 0);
1887 if (rc != LDAP_SUCCESS) {
1888 raise_warning("Unable to parse result: %s", ldap_err2string(rc));
1889 return false;
1892 errcode.assignIfRef(lerrcode);
1894 /* Reverse -> fall through */
1895 Array arr = Array::Create();
1896 if (lreferrals != NULL) {
1897 refp = lreferrals;
1898 while (*refp) {
1899 arr.append(String(*refp, CopyString));
1900 refp++;
1902 ldap_value_free(lreferrals);
1904 referrals.assignIfRef(arr);
1906 if (lerrmsg == NULL) {
1907 errmsg.assignIfRef(empty_string_variant());
1908 } else {
1909 errmsg.assignIfRef(String(lerrmsg, CopyString));
1910 ldap_memfree(lerrmsg);
1913 if (lmatcheddn == NULL) {
1914 matcheddn.assignIfRef(empty_string_variant());
1915 } else {
1916 matcheddn.assignIfRef(String(lmatcheddn, CopyString));
1917 ldap_memfree(lmatcheddn);
1919 return true;
1922 bool HHVM_FUNCTION(ldap_free_result,
1923 const Resource& result) {
1924 auto res = get_valid_ldap_result_resource(result);
1925 if (!res) {
1926 return false;
1928 res->close();
1929 return true;
1932 Variant HHVM_FUNCTION(ldap_get_values_len,
1933 const Resource& link,
1934 const Resource& result_entry,
1935 const String& attribute) {
1936 auto ld = get_valid_ldap_link_resource(link);
1937 if (!ld) {
1938 return false;
1940 auto entry = get_valid_ldap_result_entry_resource(result_entry);
1941 if (!entry) {
1942 return false;
1945 struct berval **ldap_value_len;
1946 if ((ldap_value_len =
1947 ldap_get_values_len(ld->link, entry->data,
1948 (char*)attribute.data())) == NULL) {
1949 raise_warning("Cannot get the value(s) of attribute %s",
1950 ldap_err2string(_get_lderrno(ld->link)));
1951 return false;
1954 int num_values = ldap_count_values_len(ldap_value_len);
1955 Array ret;
1956 for (int i = 0; i < num_values; i++) {
1957 ret.append(String(ldap_value_len[i]->bv_val, ldap_value_len[i]->bv_len,
1958 CopyString));
1960 ret.set(s_count, num_values);
1961 ldap_value_free_len(ldap_value_len);
1962 return ret;
1965 Variant HHVM_FUNCTION(ldap_get_values,
1966 const Resource& link,
1967 const Resource& result_entry,
1968 const String& attribute) {
1969 return f_ldap_get_values_len(link, result_entry, attribute);
1972 bool HHVM_FUNCTION(ldap_control_paged_result,
1973 const Resource& link,
1974 int pagesize,
1975 bool iscritical,
1976 const String& cookie) {
1977 auto ld = get_valid_ldap_link_resource(link);
1978 if (!ld) {
1979 return false;
1981 LDAPControl ctrl, *ctrlsp[2];
1982 int rc;
1983 struct berval lcookie;
1984 BerElement *ber;
1986 ber = ber_alloc_t(LBER_USE_DER);
1987 if (ber == nullptr) {
1988 raise_warning("Unable to alloc BER encoding resources for paged "
1989 "results control");
1990 return false;
1993 lcookie.bv_val = (char *)cookie.c_str();
1994 lcookie.bv_len = cookie.length();
1995 if (ber_printf(ber, "{iO}", pagesize, &lcookie) == LBER_ERROR) {
1996 raise_warning("Unable to BER printf paged results control");
1997 ber_free(ber, 1);
1998 return false;
2001 rc = ber_flatten2(ber, &ctrl.ldctl_value, 0);
2002 if (rc == LBER_ERROR) {
2003 raise_warning("Unable to BER encode paged results control");
2004 ber_free(ber, 1);
2005 return false;
2008 ctrl.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2009 ctrl.ldctl_iscritical = iscritical;
2011 ctrlsp[0] = &ctrl;
2012 ctrlsp[1] = nullptr;
2013 rc = ldap_set_option(ld->link, LDAP_OPT_SERVER_CONTROLS, ctrlsp);
2015 ber_free(ber, 1);
2016 return rc == LDAP_SUCCESS;
2019 bool HHVM_FUNCTION(ldap_control_paged_result_response,
2020 const Resource& link,
2021 const Resource& result,
2022 VRefParam cookie,
2023 VRefParam estimated) {
2024 auto ld = get_valid_ldap_link_resource(link);
2025 if (!ld) {
2026 return false;
2028 auto res = get_valid_ldap_result_resource(result);
2029 if (!res) {
2030 return false;
2032 int rc, lerrcode;
2033 LDAPControl **lserverctrls, *lctrl;
2034 BerElement *ber;
2035 int lestimated;
2036 struct berval lcookie;
2037 ber_tag_t tag;
2039 rc = ldap_parse_result(ld->link,
2040 res->data,
2041 &lerrcode,
2042 nullptr, /* matcheddn */
2043 nullptr, /* errmsg */
2044 nullptr, /* referrals */
2045 &lserverctrls,
2047 if (rc != LDAP_SUCCESS) {
2048 raise_warning("Unable to parse result: %s (%d)", ldap_err2string(rc), rc);
2049 return false;
2052 if (lerrcode != LDAP_SUCCESS) {
2053 raise_warning("Result is: %s (%d)", ldap_err2string(lerrcode), lerrcode);
2054 return false;
2057 if (lserverctrls == nullptr) {
2058 raise_warning("No server controls in result");
2059 return false;
2062 lctrl = ldap_find_control(LDAP_CONTROL_PAGEDRESULTS, lserverctrls);
2063 if (lctrl == nullptr) {
2064 raise_warning("No paged results control response in result");
2065 ldap_controls_free(lserverctrls);
2066 return false;
2069 ber = ber_init(&lctrl->ldctl_value);
2070 if (ber == nullptr) {
2071 raise_warning("Unable to alloc BER decoding resources for paged "
2072 "results control response");
2073 ldap_controls_free(lserverctrls);
2074 return false;
2077 tag = ber_scanf(ber, "{io}", &lestimated, &lcookie);
2078 ber_free(ber, 1);
2079 ldap_controls_free(lserverctrls);
2081 if (tag == LBER_ERROR) {
2082 raise_warning("Unable to decode paged results control response");
2083 return false;
2086 if (lestimated < 0) {
2087 raise_warning("Invalid paged results control response value");
2088 ber_memfree(lcookie.bv_val);
2089 return false;
2092 cookie.assignIfRef(String(lcookie.bv_val, lcookie.bv_len, CopyString));
2093 estimated.assignIfRef(lestimated);
2095 ber_memfree(lcookie.bv_val);
2096 return true;
2099 String HHVM_FUNCTION(ldap_escape,
2100 const String& value,
2101 const String& ignores /* = "" */,
2102 int flags /* = 0 */) {
2103 char esc[256] = {};
2105 if (flags & k_LDAP_ESCAPE_FILTER) { // llvm.org/bugs/show_bug.cgi?id=18389
2106 esc['*'*1u] = esc['('*1u] = esc[')'*1u] = esc['\0'*1u] = esc['\\'*1u] = 1;
2109 if (flags & k_LDAP_ESCAPE_DN) {
2110 esc[','*1u] = esc['='*1u] = esc['+'*1u] = esc['<'*1u] = esc['\\'*1u] = 1;
2111 esc['>'*1u] = esc[';'*1u] = esc['"'*1u] = esc['#'*1u] = 1;
2114 if (!flags) {
2115 memset(esc, 1, sizeof(esc));
2118 for (int i = 0; i < ignores.size(); i++) {
2119 esc[(unsigned char)ignores[i]] = 0;
2122 char hex[] = "0123456789abcdef";
2124 String result(3 * value.size(), ReserveString);
2125 char *rdata = result.get()->mutableData(), *r = rdata;
2127 for (int i = 0; i < value.size(); i++) {
2128 auto c = (unsigned char)value[i];
2129 if (esc[c]) {
2130 *r++ = '\\';
2131 *r++ = hex[c >> 4];
2132 *r++ = hex[c & 0xf];
2133 } else {
2134 *r++ = c;
2138 result.setSize(r - rdata);
2139 return result;
2142 ///////////////////////////////////////////////////////////////////////////////