1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Amitay Isaacs  <amitay@w-o-i.com>                           |
16    |          Eric Warnke    <ericw@albany.edu>                           |
17    |          Rasmus Lerdorf <rasmus@php.net>                             |
18    |          Gerrit Thomson <334647@swin.edu.au>                         |
19    |          Jani Taskinen  <sniper@iki.fi>                              |
20    |          Stig Venaas    <venaas@uninett.no>                          |
21    |          Doug Goldstein <cardoe@cardoe.com>                          |
22    |          Côme Chilliet  <mcmic@php.net>                              |
23    | PHP 4.0 updates:  Zeev Suraski <zeev@php.net>                        |
24    +----------------------------------------------------------------------+
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include "php.h"
32 #include "php_ini.h"
33 
34 #include <stddef.h>
35 
36 #include "ext/standard/dl.h"
37 #include "php_ldap.h"
38 
39 #ifdef PHP_WIN32
40 #include <string.h>
41 #include "config.w32.h"
42 #define strdup _strdup
43 #undef WINDOWS
44 #undef strcasecmp
45 #undef strncasecmp
46 #define WINSOCK 1
47 #define __STDC__ 1
48 #endif
49 
50 #include "ext/standard/php_string.h"
51 #include "ext/standard/info.h"
52 
53 #ifdef HAVE_LDAP_SASL
54 #include <sasl/sasl.h>
55 #endif
56 
57 #define PHP_LDAP_ESCAPE_FILTER 0x01
58 #define PHP_LDAP_ESCAPE_DN     0x02
59 
60 #if defined(LDAP_CONTROL_PAGEDRESULTS) && !defined(HAVE_LDAP_CONTROL_FIND)
ldap_control_find( const char *oid, LDAPControl **ctrls, LDAPControl ***nextctrlp)61 LDAPControl *ldap_control_find( const char *oid, LDAPControl **ctrls, LDAPControl ***nextctrlp)
62 {
63 	assert(nextctrlp == NULL);
64 	return ldap_find_control(oid, ctrls);
65 }
66 #endif
67 
68 #if !defined(LDAP_API_FEATURE_X_OPENLDAP)
ldap_memvfree(void **v)69 void ldap_memvfree(void **v)
70 {
71 	ldap_value_free((char **)v);
72 }
73 #endif
74 
75 typedef struct {
76 	LDAP *link;
77 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
78 	zval rebindproc;
79 #endif
80 } ldap_linkdata;
81 
82 typedef struct {
83 	LDAPMessage *data;
84 	BerElement  *ber;
85 	zval         res;
86 } ldap_resultentry;
87 
88 ZEND_DECLARE_MODULE_GLOBALS(ldap)
89 static PHP_GINIT_FUNCTION(ldap);
90 
91 static int le_link, le_result, le_result_entry;
92 
93 #ifdef COMPILE_DL_LDAP
94 #ifdef ZTS
95 ZEND_TSRMLS_CACHE_DEFINE()
96 #endif
97 ZEND_GET_MODULE(ldap)
98 #endif
99 
_close_ldap_link(zend_resource *rsrc)100 static void _close_ldap_link(zend_resource *rsrc) /* {{{ */
101 {
102 	ldap_linkdata *ld = (ldap_linkdata *)rsrc->ptr;
103 
104 	/* We use ldap_destroy rather than ldap_unbind here, because ldap_unbind
105 	 * will skip the destructor entirely if a critical client control is set. */
106 	ldap_destroy(ld->link);
107 
108 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
109 	zval_ptr_dtor(&ld->rebindproc);
110 #endif
111 
112 	efree(ld);
113 	LDAPG(num_links)--;
114 }
115 /* }}} */
116 
_free_ldap_result(zend_resource *rsrc)117 static void _free_ldap_result(zend_resource *rsrc) /* {{{ */
118 {
119 	LDAPMessage *result = (LDAPMessage *)rsrc->ptr;
120 	ldap_msgfree(result);
121 }
122 /* }}} */
123 
_free_ldap_result_entry(zend_resource *rsrc)124 static void _free_ldap_result_entry(zend_resource *rsrc) /* {{{ */
125 {
126 	ldap_resultentry *entry = (ldap_resultentry *)rsrc->ptr;
127 
128 	if (entry->ber != NULL) {
129 		ber_free(entry->ber, 0);
130 		entry->ber = NULL;
131 	}
132 	zval_ptr_dtor(&entry->res);
133 	efree(entry);
134 }
135 /* }}} */
136 
137 /* {{{ Parse controls from and to arrays */
_php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array, int request)138 static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array, int request)
139 {
140 	array_init(array);
141 
142 	add_assoc_string(array, "oid", ctrl->ldctl_oid);
143 	if (request) {
144 		/* iscritical field only makes sense in request controls (which may be obtained by ldap_get_option) */
145 		add_assoc_bool(array, "iscritical", (ctrl->ldctl_iscritical != 0));
146 	}
147 
148 	/* If it is a known oid, parse to values */
149 	if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) {
150 		int expire = 0, grace = 0, rc;
151 		LDAPPasswordPolicyError pperr;
152 		zval value;
153 
154 		rc = ldap_parse_passwordpolicy_control(ld, ctrl, &expire, &grace, &pperr);
155 		if ( rc == LDAP_SUCCESS ) {
156 			array_init(&value);
157 			add_assoc_long(&value, "expire", expire);
158 			add_assoc_long(&value, "grace", grace);
159 
160 			if ( pperr != PP_noError ) {
161 				add_assoc_long(&value, "error", pperr);
162 			}
163 			add_assoc_zval(array, "value", &value);
164 		} else {
165 			add_assoc_null(array, "value");
166 		}
167 	} else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0) {
168 		int lestimated, rc;
169 		struct berval lcookie = { 0L, NULL };
170 		zval value;
171 
172 		if (ctrl->ldctl_value.bv_len) {
173 			/* ldap_parse_pageresponse_control() allocates lcookie.bv_val */
174 			rc = ldap_parse_pageresponse_control(ld, ctrl, &lestimated, &lcookie);
175 		} else {
176 			/* ldap_parse_pageresponse_control will crash if value is empty */
177 			rc = -1;
178 		}
179 
180 		if ( rc == LDAP_SUCCESS ) {
181 			array_init(&value);
182 			add_assoc_long(&value, "size", lestimated);
183 			add_assoc_stringl(&value, "cookie", lcookie.bv_val, lcookie.bv_len);
184 			add_assoc_zval(array, "value", &value);
185 		} else {
186 			add_assoc_null(array, "value");
187 		}
188 
189 		if (lcookie.bv_val) {
190 			ldap_memfree(lcookie.bv_val);
191 		}
192 	} else if ((strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PRE_READ) == 0) || (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_POST_READ) == 0)) {
193 		BerElement *ber;
194 		struct berval bv;
195 
196 		ber = ber_init(&ctrl->ldctl_value);
197 		if (ber == NULL) {
198 			add_assoc_null(array, "value");
199 		} else if (ber_scanf(ber, "{m{" /*}}*/, &bv) == LBER_ERROR) {
200 			add_assoc_null(array, "value");
201 		} else {
202 			zval value;
203 
204 			array_init(&value);
205 			add_assoc_stringl(&value, "dn", bv.bv_val, bv.bv_len);
206 
207 			while (ber_scanf(ber, "{m" /*}*/, &bv) != LBER_ERROR) {
208 				int	 i;
209 				BerVarray vals = NULL;
210 				zval tmp;
211 
212 				if (ber_scanf(ber, "[W]", &vals) == LBER_ERROR || vals == NULL)
213 				{
214 					break;
215 				}
216 
217 				array_init(&tmp);
218 				for (i = 0; vals[i].bv_val != NULL; i++) {
219 					add_next_index_stringl(&tmp, vals[i].bv_val, vals[i].bv_len);
220 				}
221 				add_assoc_zval(&value, bv.bv_val, &tmp);
222 
223 				ber_bvarray_free(vals);
224 			}
225 			add_assoc_zval(array, "value", &value);
226 		}
227 
228 		if (ber != NULL) {
229 			ber_free(ber, 1);
230 		}
231 	} else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_SORTRESPONSE) == 0) {
232 		zval value;
233 		int errcode, rc;
234 		char* attribute;
235 
236 		if (ctrl->ldctl_value.bv_len) {
237 			rc = ldap_parse_sortresponse_control(ld, ctrl, &errcode, &attribute);
238 		} else {
239 			rc = -1;
240 		}
241 		if ( rc == LDAP_SUCCESS ) {
242 			array_init(&value);
243 			add_assoc_long(&value, "errcode", errcode);
244 			if (attribute) {
245 				add_assoc_string(&value, "attribute", attribute);
246 				ldap_memfree(attribute);
247 			}
248 			add_assoc_zval(array, "value", &value);
249 		} else {
250 			add_assoc_null(array, "value");
251 		}
252 	} else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_VLVRESPONSE) == 0) {
253 		int target, count, errcode, rc;
254 		struct berval *context;
255 		zval value;
256 
257 		if (ctrl->ldctl_value.bv_len) {
258 			rc = ldap_parse_vlvresponse_control(ld, ctrl, &target, &count, &context, &errcode);
259 		} else {
260 			rc = -1;
261 		}
262 		if ( rc == LDAP_SUCCESS ) {
263 			array_init(&value);
264 			add_assoc_long(&value, "target", target);
265 			add_assoc_long(&value, "count", count);
266 			add_assoc_long(&value, "errcode", errcode);
267 			if ( context && (context->bv_len >= 0) ) {
268 				add_assoc_stringl(&value, "context", context->bv_val, context->bv_len);
269 			}
270 			add_assoc_zval(array, "value", &value);
271 			ber_bvfree(context);
272 		} else {
273 			add_assoc_null(array, "value");
274 		}
275 	} else {
276 		if (ctrl->ldctl_value.bv_len) {
277 			add_assoc_stringl(array, "value", ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len);
278 		} else {
279 			add_assoc_null(array, "value");
280 		}
281 	}
282 }
283 
_php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* array)284 static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* array)
285 {
286 	zval* val;
287 	zend_string *control_oid;
288 	int control_iscritical = 0, rc = LDAP_SUCCESS;
289 	char** ldap_attrs = NULL;
290 	LDAPSortKey** sort_keys = NULL;
291 	zend_string *tmpstring = NULL, **tmpstrings1 = NULL, **tmpstrings2 = NULL;
292 	size_t num_tmpstrings1 = 0, num_tmpstrings2 = 0;
293 
294 	if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "oid", sizeof("oid") - 1)) == NULL) {
295 		php_error_docref(NULL, E_WARNING, "Control must have an oid key");
296 		return -1;
297 	}
298 
299 	control_oid = zval_get_string(val);
300 	if (EG(exception)) {
301 		return -1;
302 	}
303 
304 	if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "iscritical", sizeof("iscritical") - 1)) != NULL) {
305 		control_iscritical = zend_is_true(val);
306 	} else {
307 		control_iscritical = 0;
308 	}
309 
310 	BerElement *ber = NULL;
311 	struct berval control_value = { 0L, NULL };
312 	int control_value_alloc = 0;
313 
314 	if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "value", sizeof("value") - 1)) != NULL) {
315 		if (Z_TYPE_P(val) != IS_ARRAY) {
316 			tmpstring = zval_get_string(val);
317 			if (EG(exception)) {
318 				rc = -1;
319 				goto failure;
320 			}
321 			control_value.bv_val = ZSTR_VAL(tmpstring);
322 			control_value.bv_len = ZSTR_LEN(tmpstring);
323 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_PAGEDRESULTS) == 0) {
324 			zval* tmp;
325 			int pagesize = 1;
326 			struct berval cookie = { 0L, NULL };
327 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "size", sizeof("size") - 1)) != NULL) {
328 				pagesize = zval_get_long(tmp);
329 			}
330 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "cookie", sizeof("cookie") - 1)) != NULL) {
331 				tmpstring = zval_get_string(tmp);
332 				if (EG(exception)) {
333 					rc = -1;
334 					goto failure;
335 				}
336 				cookie.bv_val = ZSTR_VAL(tmpstring);
337 				cookie.bv_len = ZSTR_LEN(tmpstring);
338 			}
339 			/* ldap_create_page_control_value() allocates memory for control_value.bv_val */
340 			control_value_alloc = 1;
341 			rc = ldap_create_page_control_value(ld, pagesize, &cookie, &control_value);
342 			if (rc != LDAP_SUCCESS) {
343 				php_error_docref(NULL, E_WARNING, "Failed to create paged result control value: %s (%d)", ldap_err2string(rc), rc);
344 			}
345 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_ASSERT) == 0) {
346 			zval* tmp;
347 			zend_string* assert;
348 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "filter", sizeof("filter") - 1)) == NULL) {
349 				rc = -1;
350 				php_error_docref(NULL, E_WARNING, "Filter missing from assert control value array");
351 			} else {
352 				assert = zval_get_string(tmp);
353 				if (EG(exception)) {
354 					rc = -1;
355 					goto failure;
356 				}
357 				/* ldap_create_assertion_control_value does not reset ld_errno, we need to do it ourselves
358 					 See http://www.openldap.org/its/index.cgi/Incoming?id=8674 */
359 				int success = LDAP_SUCCESS;
360 				ldap_set_option(ld, LDAP_OPT_RESULT_CODE, &success);
361 				/* ldap_create_assertion_control_value() allocates memory for control_value.bv_val */
362 				control_value_alloc = 1;
363 				rc = ldap_create_assertion_control_value(ld, ZSTR_VAL(assert), &control_value);
364 				if (rc != LDAP_SUCCESS) {
365 					php_error_docref(NULL, E_WARNING, "Failed to create assert control value: %s (%d)", ldap_err2string(rc), rc);
366 				}
367 				zend_string_release(assert);
368 			}
369 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_VALUESRETURNFILTER) == 0) {
370 			zval* tmp;
371 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "filter", sizeof("filter") - 1)) == NULL) {
372 				rc = -1;
373 				php_error_docref(NULL, E_WARNING, "Filter missing from control value array");
374 			} else {
375 				ber = ber_alloc_t(LBER_USE_DER);
376 				if (ber == NULL) {
377 					rc = -1;
378 					php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
379 				} else {
380 					tmpstring = zval_get_string(tmp);
381 					if (EG(exception)) {
382 						rc = -1;
383 						goto failure;
384 					}
385 					if (ldap_put_vrFilter(ber, ZSTR_VAL(tmpstring)) == -1) {
386 						rc = -1;
387 						php_error_docref(NULL, E_WARNING, "Failed to create control value: Bad ValuesReturnFilter: %s", ZSTR_VAL(tmpstring));
388 					} else if (ber_flatten2(ber, &control_value, control_value_alloc) == -1) {
389 						rc = -1;
390 					}
391 				}
392 			}
393 		} else if ((strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_PRE_READ) == 0) || (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_POST_READ) == 0)) {
394 			zval* tmp;
395 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "attrs", sizeof("attrs") - 1)) == NULL) {
396 				rc = -1;
397 				php_error_docref(NULL, E_WARNING, "Attributes list missing from control value array");
398 			} else {
399 				ber = ber_alloc_t(LBER_USE_DER);
400 
401 				if (ber == NULL) {
402 					rc = -1;
403 					php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
404 				} else {
405 					int num_attribs, i;
406 					zval* attr;
407 
408 					num_attribs = zend_hash_num_elements(Z_ARRVAL_P(tmp));
409 					ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
410 					tmpstrings1 = safe_emalloc(num_attribs, sizeof(zend_string*), 0);
411 					num_tmpstrings1 = 0;
412 
413 					for (i = 0; i<num_attribs; i++) {
414 						if ((attr = zend_hash_index_find(Z_ARRVAL_P(tmp), i)) == NULL) {
415 							rc = -1;
416 							php_error_docref(NULL, E_WARNING, "Failed to encode attribute list");
417 							goto failure;
418 						}
419 
420 						tmpstrings1[num_tmpstrings1] = zval_get_string(attr);
421 						if (EG(exception)) {
422 							rc = -1;
423 							goto failure;
424 						}
425 						ldap_attrs[i] = ZSTR_VAL(tmpstrings1[num_tmpstrings1]);
426 						++num_tmpstrings1;
427 					}
428 					ldap_attrs[num_attribs] = NULL;
429 
430 					ber_init2( ber, NULL, LBER_USE_DER );
431 
432 					if (ber_printf(ber, "{v}", ldap_attrs) == -1) {
433 						rc = -1;
434 						php_error_docref(NULL, E_WARNING, "Failed to encode attribute list");
435 					} else {
436 						int err;
437 						err = ber_flatten2(ber, &control_value, control_value_alloc);
438 						if (err < 0) {
439 							rc = -1;
440 							php_error_docref(NULL, E_WARNING, "Failed to encode control value (%d)", err);
441 						}
442 					}
443 				}
444 			}
445 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_SORTREQUEST) == 0) {
446 			int num_keys, i;
447 			zval *sortkey, *tmp;
448 
449 			num_keys = zend_hash_num_elements(Z_ARRVAL_P(val));
450 			sort_keys = safe_emalloc((num_keys+1), sizeof(LDAPSortKey*), 0);
451 			tmpstrings1 = safe_emalloc(num_keys, sizeof(zend_string*), 0);
452 			tmpstrings2 = safe_emalloc(num_keys, sizeof(zend_string*), 0);
453 			num_tmpstrings1 = 0;
454 			num_tmpstrings2 = 0;
455 
456 			for (i = 0; i<num_keys; i++) {
457 				if ((sortkey = zend_hash_index_find(Z_ARRVAL_P(val), i)) == NULL) {
458 					rc = -1;
459 					php_error_docref(NULL, E_WARNING, "Failed to encode sort keys list");
460 					goto failure;
461 				}
462 
463 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "attr", sizeof("attr") - 1)) == NULL) {
464 					rc = -1;
465 					php_error_docref(NULL, E_WARNING, "Sort key list missing field");
466 					goto failure;
467 				}
468 				sort_keys[i] = emalloc(sizeof(LDAPSortKey));
469 				tmpstrings1[num_tmpstrings1] = zval_get_string(tmp);
470 				if (EG(exception)) {
471 					rc = -1;
472 					goto failure;
473 				}
474 				sort_keys[i]->attributeType = ZSTR_VAL(tmpstrings1[num_tmpstrings1]);
475 				++num_tmpstrings1;
476 
477 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "oid", sizeof("oid") - 1)) != NULL) {
478 					tmpstrings2[num_tmpstrings2] = zval_get_string(tmp);
479 					if (EG(exception)) {
480 						rc = -1;
481 						goto failure;
482 					}
483 					sort_keys[i]->orderingRule = ZSTR_VAL(tmpstrings2[num_tmpstrings2]);
484 					++num_tmpstrings2;
485 				} else {
486 					sort_keys[i]->orderingRule = NULL;
487 				}
488 
489 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "reverse", sizeof("reverse") - 1)) != NULL) {
490 					sort_keys[i]->reverseOrder = zend_is_true(tmp);
491 				} else {
492 					sort_keys[i]->reverseOrder = 0;
493 				}
494 			}
495 			sort_keys[num_keys] = NULL;
496 			/* ldap_create_sort_control_value() allocates memory for control_value.bv_val */
497 			control_value_alloc = 1;
498 			rc = ldap_create_sort_control_value(ld, sort_keys, &control_value);
499 			if (rc != LDAP_SUCCESS) {
500 				php_error_docref(NULL, E_WARNING, "Failed to create sort control value: %s (%d)", ldap_err2string(rc), rc);
501 			}
502 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_VLVREQUEST) == 0) {
503 			zval* tmp;
504 			LDAPVLVInfo vlvInfo;
505 			struct berval attrValue;
506 			struct berval context;
507 
508 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "before", sizeof("before") - 1)) != NULL) {
509 				vlvInfo.ldvlv_before_count = zval_get_long(tmp);
510 			} else {
511 				rc = -1;
512 				php_error_docref(NULL, E_WARNING, "Before key missing from array value for VLV control");
513 				goto failure;
514 			}
515 
516 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "after", sizeof("after") - 1)) != NULL) {
517 				vlvInfo.ldvlv_after_count = zval_get_long(tmp);
518 			} else {
519 				rc = -1;
520 				php_error_docref(NULL, E_WARNING, "After key missing from array value for VLV control");
521 				goto failure;
522 			}
523 
524 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "attrvalue", sizeof("attrvalue") - 1)) != NULL) {
525 				tmpstring = zval_get_string(tmp);
526 				if (EG(exception)) {
527 					rc = -1;
528 					goto failure;
529 				}
530 				attrValue.bv_val = ZSTR_VAL(tmpstring);
531 				attrValue.bv_len = ZSTR_LEN(tmpstring);
532 				vlvInfo.ldvlv_attrvalue = &attrValue;
533 			} else if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "offset", sizeof("offset") - 1)) != NULL) {
534 				vlvInfo.ldvlv_attrvalue = NULL;
535 				vlvInfo.ldvlv_offset = zval_get_long(tmp);
536 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "count", sizeof("count") - 1)) != NULL) {
537 					vlvInfo.ldvlv_count = zval_get_long(tmp);
538 				} else {
539 					rc = -1;
540 					php_error_docref(NULL, E_WARNING, "Count key missing from array value for VLV control");
541 					goto failure;
542 				}
543 			} else {
544 				rc = -1;
545 				php_error_docref(NULL, E_WARNING, "Missing either attrvalue or offset key from array value for VLV control");
546 				goto failure;
547 			}
548 
549 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "context", sizeof("context") - 1)) != NULL) {
550 				tmpstring = zval_get_string(tmp);
551 				if (EG(exception)) {
552 					rc = -1;
553 					goto failure;
554 				}
555 				context.bv_val = ZSTR_VAL(tmpstring);
556 				context.bv_len = ZSTR_LEN(tmpstring);
557 				vlvInfo.ldvlv_context = &context;
558 			} else {
559 				vlvInfo.ldvlv_context = NULL;
560 			}
561 
562 			/* ldap_create_vlv_control_value() allocates memory for control_value.bv_val */
563 			control_value_alloc = 1;
564 			rc = ldap_create_vlv_control_value(ld, &vlvInfo, &control_value);
565 			if (rc != LDAP_SUCCESS) {
566 				php_error_docref(NULL, E_WARNING, "Failed to create VLV control value: %s (%d)", ldap_err2string(rc), rc);
567 			}
568 		} else {
569 			php_error_docref(NULL, E_WARNING, "Control OID %s does not expect an array as value", ZSTR_VAL(control_oid));
570 			rc = -1;
571 		}
572 	}
573 
574 	if (rc == LDAP_SUCCESS) {
575 		rc = ldap_control_create(ZSTR_VAL(control_oid), control_iscritical, &control_value, 1, ctrl);
576 	}
577 
578 failure:
579 	zend_string_release(control_oid);
580 	if (tmpstring != NULL) {
581 		zend_string_release(tmpstring);
582 	}
583 	if (tmpstrings1 != NULL) {
584 		int i;
585 		for (i = 0; i < num_tmpstrings1; ++i) {
586 			zend_string_release(tmpstrings1[i]);
587 		}
588 		efree(tmpstrings1);
589 	}
590 	if (tmpstrings2 != NULL) {
591 		int i;
592 		for (i = 0; i < num_tmpstrings2; ++i) {
593 			zend_string_release(tmpstrings2[i]);
594 		}
595 		efree(tmpstrings2);
596 	}
597 	if (control_value.bv_val != NULL && control_value_alloc != 0) {
598 		ber_memfree(control_value.bv_val);
599 	}
600 	if (ber != NULL) {
601 		ber_free(ber, 1);
602 	}
603 	if (ldap_attrs != NULL) {
604 		efree(ldap_attrs);
605 	}
606 	if (sort_keys != NULL) {
607 		LDAPSortKey** sortp = sort_keys;
608 		while (*sortp) {
609 			efree(*sortp);
610 			sortp++;
611 		}
612 		efree(sort_keys);
613 		sort_keys = NULL;
614 	}
615 
616 	if (rc == LDAP_SUCCESS) {
617 		return LDAP_SUCCESS;
618 	}
619 
620 	/* Failed */
621 	*ctrl = NULL;
622 	return -1;
623 }
624 
_php_ldap_controls_to_array(LDAP *ld, LDAPControl** ctrls, zval* array, int request)625 static void _php_ldap_controls_to_array(LDAP *ld, LDAPControl** ctrls, zval* array, int request)
626 {
627 	zval tmp1;
628 	LDAPControl **ctrlp;
629 
630 	array = zend_try_array_init(array);
631 	if (!array) {
632 		return;
633 	}
634 
635 	if (ctrls == NULL) {
636 		return;
637 	}
638 	ctrlp = ctrls;
639 	while (*ctrlp != NULL) {
640 		_php_ldap_control_to_array(ld, *ctrlp, &tmp1, request);
641 		add_assoc_zval(array, (*ctrlp)->ldctl_oid, &tmp1);
642 		ctrlp++;
643 	}
644 	ldap_controls_free(ctrls);
645 }
646 
_php_ldap_controls_from_array(LDAP *ld, zval* array)647 static LDAPControl** _php_ldap_controls_from_array(LDAP *ld, zval* array)
648 {
649 	int ncontrols;
650 	LDAPControl** ctrlp, **ctrls = NULL;
651 	zval* ctrlarray;
652 	int error = 0;
653 
654 	ncontrols = zend_hash_num_elements(Z_ARRVAL_P(array));
655 	ctrls = safe_emalloc((1 + ncontrols), sizeof(*ctrls), 0);
656 	*ctrls = NULL;
657 	ctrlp = ctrls;
658 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), ctrlarray) {
659 		if (Z_TYPE_P(ctrlarray) != IS_ARRAY) {
660 			php_error_docref(NULL, E_WARNING, "The array value must contain only arrays, where each array is a control");
661 			error = 1;
662 			break;
663 		}
664 
665 		if (_php_ldap_control_from_array(ld, ctrlp, ctrlarray) == LDAP_SUCCESS) {
666 			++ctrlp;
667 		} else {
668 			error = 1;
669 			break;
670 		}
671 
672 		*ctrlp = NULL;
673 	} ZEND_HASH_FOREACH_END();
674 
675 	if (error) {
676 		ctrlp = ctrls;
677 		while (*ctrlp) {
678 			ldap_control_free(*ctrlp);
679 			ctrlp++;
680 		}
681 		efree(ctrls);
682 		ctrls = NULL;
683 	}
684 
685 	return ctrls;
686 }
687 
_php_ldap_controls_free(LDAPControl*** ctrls)688 static void _php_ldap_controls_free (LDAPControl*** ctrls)
689 {
690 	LDAPControl **ctrlp;
691 
692 	if (*ctrls) {
693 		ctrlp = *ctrls;
694 		while (*ctrlp) {
695 			ldap_control_free(*ctrlp);
696 			ctrlp++;
697 		}
698 		efree(*ctrls);
699 		*ctrls = NULL;
700 	}
701 }
702 /* }}} */
703 
704 /* {{{ PHP_INI_BEGIN
705  */
706 PHP_INI_BEGIN()
707 	STD_PHP_INI_ENTRY_EX("ldap.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_ldap_globals, ldap_globals, display_link_numbers)
708 PHP_INI_END()
709 /* }}} */
710 
711 /* {{{ PHP_GINIT_FUNCTION
712  */
PHP_GINIT_FUNCTIONnull713 static PHP_GINIT_FUNCTION(ldap)
714 {
715 #if defined(COMPILE_DL_LDAP) && defined(ZTS)
716 	ZEND_TSRMLS_CACHE_UPDATE();
717 #endif
718 	ldap_globals->num_links = 0;
719 }
720 /* }}} */
721 
722 /* {{{ PHP_MINIT_FUNCTION
723  */
PHP_MINIT_FUNCTIONnull724 PHP_MINIT_FUNCTION(ldap)
725 {
726 	REGISTER_INI_ENTRIES();
727 
728 	/* Constants to be used with deref-parameter in php_ldap_do_search() */
729 	REGISTER_LONG_CONSTANT("LDAP_DEREF_NEVER", LDAP_DEREF_NEVER, CONST_PERSISTENT | CONST_CS);
730 	REGISTER_LONG_CONSTANT("LDAP_DEREF_SEARCHING", LDAP_DEREF_SEARCHING, CONST_PERSISTENT | CONST_CS);
731 	REGISTER_LONG_CONSTANT("LDAP_DEREF_FINDING", LDAP_DEREF_FINDING, CONST_PERSISTENT | CONST_CS);
732 	REGISTER_LONG_CONSTANT("LDAP_DEREF_ALWAYS", LDAP_DEREF_ALWAYS, CONST_PERSISTENT | CONST_CS);
733 
734 	/* Constants to be used with ldap_modify_batch() */
735 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_ADD", LDAP_MODIFY_BATCH_ADD, CONST_PERSISTENT | CONST_CS);
736 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE", LDAP_MODIFY_BATCH_REMOVE, CONST_PERSISTENT | CONST_CS);
737 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE_ALL", LDAP_MODIFY_BATCH_REMOVE_ALL, CONST_PERSISTENT | CONST_CS);
738 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REPLACE", LDAP_MODIFY_BATCH_REPLACE, CONST_PERSISTENT | CONST_CS);
739 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_ATTRIB", LDAP_MODIFY_BATCH_ATTRIB, CONST_PERSISTENT | CONST_CS);
740 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_MODTYPE", LDAP_MODIFY_BATCH_MODTYPE, CONST_PERSISTENT | CONST_CS);
741 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_VALUES", LDAP_MODIFY_BATCH_VALUES, CONST_PERSISTENT | CONST_CS);
742 
743 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
744 	/* LDAP options */
745 	REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT | CONST_CS);
746 	REGISTER_LONG_CONSTANT("LDAP_OPT_SIZELIMIT", LDAP_OPT_SIZELIMIT, CONST_PERSISTENT | CONST_CS);
747 	REGISTER_LONG_CONSTANT("LDAP_OPT_TIMELIMIT", LDAP_OPT_TIMELIMIT, CONST_PERSISTENT | CONST_CS);
748 #ifdef LDAP_OPT_NETWORK_TIMEOUT
749 	REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_OPT_NETWORK_TIMEOUT, CONST_PERSISTENT | CONST_CS);
750 #elif defined (LDAP_X_OPT_CONNECT_TIMEOUT)
751 	REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_X_OPT_CONNECT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
752 #endif
753 #ifdef LDAP_OPT_TIMEOUT
754 	REGISTER_LONG_CONSTANT("LDAP_OPT_TIMEOUT", LDAP_OPT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
755 #endif
756 	REGISTER_LONG_CONSTANT("LDAP_OPT_PROTOCOL_VERSION", LDAP_OPT_PROTOCOL_VERSION, CONST_PERSISTENT | CONST_CS);
757 	REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_NUMBER", LDAP_OPT_ERROR_NUMBER, CONST_PERSISTENT | CONST_CS);
758 	REGISTER_LONG_CONSTANT("LDAP_OPT_REFERRALS", LDAP_OPT_REFERRALS, CONST_PERSISTENT | CONST_CS);
759 #ifdef LDAP_OPT_RESTART
760 	REGISTER_LONG_CONSTANT("LDAP_OPT_RESTART", LDAP_OPT_RESTART, CONST_PERSISTENT | CONST_CS);
761 #endif
762 #ifdef LDAP_OPT_HOST_NAME
763 	REGISTER_LONG_CONSTANT("LDAP_OPT_HOST_NAME", LDAP_OPT_HOST_NAME, CONST_PERSISTENT | CONST_CS);
764 #endif
765 	REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_STRING", LDAP_OPT_ERROR_STRING, CONST_PERSISTENT | CONST_CS);
766 #ifdef LDAP_OPT_MATCHED_DN
767 	REGISTER_LONG_CONSTANT("LDAP_OPT_MATCHED_DN", LDAP_OPT_MATCHED_DN, CONST_PERSISTENT | CONST_CS);
768 #endif
769 	REGISTER_LONG_CONSTANT("LDAP_OPT_SERVER_CONTROLS", LDAP_OPT_SERVER_CONTROLS, CONST_PERSISTENT | CONST_CS);
770 	REGISTER_LONG_CONSTANT("LDAP_OPT_CLIENT_CONTROLS", LDAP_OPT_CLIENT_CONTROLS, CONST_PERSISTENT | CONST_CS);
771 #endif
772 #ifdef LDAP_OPT_DEBUG_LEVEL
773 	REGISTER_LONG_CONSTANT("LDAP_OPT_DEBUG_LEVEL", LDAP_OPT_DEBUG_LEVEL, CONST_PERSISTENT | CONST_CS);
774 #endif
775 
776 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
777 	REGISTER_LONG_CONSTANT("LDAP_OPT_DIAGNOSTIC_MESSAGE", LDAP_OPT_DIAGNOSTIC_MESSAGE, CONST_PERSISTENT | CONST_CS);
778 #endif
779 
780 #ifdef HAVE_LDAP_SASL
781 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_MECH", LDAP_OPT_X_SASL_MECH, CONST_PERSISTENT | CONST_CS);
782 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_REALM", LDAP_OPT_X_SASL_REALM, CONST_PERSISTENT | CONST_CS);
783 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHCID", LDAP_OPT_X_SASL_AUTHCID, CONST_PERSISTENT | CONST_CS);
784 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHZID", LDAP_OPT_X_SASL_AUTHZID, CONST_PERSISTENT | CONST_CS);
785 #endif
786 #ifdef LDAP_OPT_X_SASL_NOCANON
787 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_NOCANON", LDAP_OPT_X_SASL_NOCANON, CONST_PERSISTENT | CONST_CS);
788 #endif
789 #ifdef LDAP_OPT_X_SASL_USERNAME
790 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_USERNAME", LDAP_OPT_X_SASL_USERNAME, CONST_PERSISTENT | CONST_CS);
791 #endif
792 
793 #ifdef ORALDAP
794 	REGISTER_LONG_CONSTANT("GSLC_SSL_NO_AUTH", GSLC_SSL_NO_AUTH, CONST_PERSISTENT | CONST_CS);
795 	REGISTER_LONG_CONSTANT("GSLC_SSL_ONEWAY_AUTH", GSLC_SSL_ONEWAY_AUTH, CONST_PERSISTENT | CONST_CS);
796 	REGISTER_LONG_CONSTANT("GSLC_SSL_TWOWAY_AUTH", GSLC_SSL_TWOWAY_AUTH, CONST_PERSISTENT | CONST_CS);
797 #endif
798 
799 #if (LDAP_API_VERSION > 2000)
800 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_REQUIRE_CERT", LDAP_OPT_X_TLS_REQUIRE_CERT, CONST_PERSISTENT | CONST_CS);
801 
802 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_NEVER", LDAP_OPT_X_TLS_NEVER, CONST_PERSISTENT | CONST_CS);
803 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_HARD", LDAP_OPT_X_TLS_HARD, CONST_PERSISTENT | CONST_CS);
804 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DEMAND", LDAP_OPT_X_TLS_DEMAND, CONST_PERSISTENT | CONST_CS);
805 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_ALLOW", LDAP_OPT_X_TLS_ALLOW, CONST_PERSISTENT | CONST_CS);
806 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_TRY", LDAP_OPT_X_TLS_TRY, CONST_PERSISTENT | CONST_CS);
807 
808 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTDIR", LDAP_OPT_X_TLS_CACERTDIR, CONST_PERSISTENT | CONST_CS);
809 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTFILE", LDAP_OPT_X_TLS_CACERTFILE, CONST_PERSISTENT | CONST_CS);
810 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CERTFILE", LDAP_OPT_X_TLS_CERTFILE, CONST_PERSISTENT | CONST_CS);
811 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CIPHER_SUITE", LDAP_OPT_X_TLS_CIPHER_SUITE, CONST_PERSISTENT | CONST_CS);
812 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_KEYFILE", LDAP_OPT_X_TLS_KEYFILE, CONST_PERSISTENT | CONST_CS);
813 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_RANDOM_FILE", LDAP_OPT_X_TLS_RANDOM_FILE, CONST_PERSISTENT | CONST_CS);
814 #endif
815 
816 #ifdef LDAP_OPT_X_TLS_CRLCHECK
817 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLCHECK", LDAP_OPT_X_TLS_CRLCHECK, CONST_PERSISTENT | CONST_CS);
818 
819 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_NONE", LDAP_OPT_X_TLS_CRL_NONE, CONST_PERSISTENT | CONST_CS);
820 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_PEER", LDAP_OPT_X_TLS_CRL_PEER, CONST_PERSISTENT | CONST_CS);
821 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_ALL", LDAP_OPT_X_TLS_CRL_ALL, CONST_PERSISTENT | CONST_CS);
822 #endif
823 
824 #ifdef LDAP_OPT_X_TLS_DHFILE
825 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DHFILE", LDAP_OPT_X_TLS_DHFILE, CONST_PERSISTENT | CONST_CS);
826 #endif
827 
828 #ifdef LDAP_OPT_X_TLS_CRLFILE
829 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLFILE", LDAP_OPT_X_TLS_CRLFILE, CONST_PERSISTENT | CONST_CS);
830 #endif
831 
832 #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
833 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_MIN", LDAP_OPT_X_TLS_PROTOCOL_MIN, CONST_PERSISTENT | CONST_CS);
834 
835 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL2", LDAP_OPT_X_TLS_PROTOCOL_SSL2, CONST_PERSISTENT | CONST_CS);
836 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL3", LDAP_OPT_X_TLS_PROTOCOL_SSL3, CONST_PERSISTENT | CONST_CS);
837 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_0", LDAP_OPT_X_TLS_PROTOCOL_TLS1_0, CONST_PERSISTENT | CONST_CS);
838 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_1", LDAP_OPT_X_TLS_PROTOCOL_TLS1_1, CONST_PERSISTENT | CONST_CS);
839 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_2", LDAP_OPT_X_TLS_PROTOCOL_TLS1_2, CONST_PERSISTENT | CONST_CS);
840 #endif
841 
842 #ifdef LDAP_OPT_X_TLS_PACKAGE
843 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PACKAGE", LDAP_OPT_X_TLS_PACKAGE, CONST_PERSISTENT | CONST_CS);
844 #endif
845 
846 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
847 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_IDLE", LDAP_OPT_X_KEEPALIVE_IDLE, CONST_PERSISTENT | CONST_CS);
848 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_PROBES", LDAP_OPT_X_KEEPALIVE_PROBES, CONST_PERSISTENT | CONST_CS);
849 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_INTERVAL", LDAP_OPT_X_KEEPALIVE_INTERVAL, CONST_PERSISTENT | CONST_CS);
850 #endif
851 
852 	REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT | CONST_CS);
853 	REGISTER_LONG_CONSTANT("LDAP_ESCAPE_DN", PHP_LDAP_ESCAPE_DN, CONST_PERSISTENT | CONST_CS);
854 
855 #ifdef HAVE_LDAP_EXTENDED_OPERATION_S
856 	REGISTER_STRING_CONSTANT("LDAP_EXOP_START_TLS", LDAP_EXOP_START_TLS, CONST_PERSISTENT | CONST_CS);
857 	REGISTER_STRING_CONSTANT("LDAP_EXOP_MODIFY_PASSWD", LDAP_EXOP_MODIFY_PASSWD, CONST_PERSISTENT | CONST_CS);
858 	REGISTER_STRING_CONSTANT("LDAP_EXOP_REFRESH", LDAP_EXOP_REFRESH, CONST_PERSISTENT | CONST_CS);
859 	REGISTER_STRING_CONSTANT("LDAP_EXOP_WHO_AM_I", LDAP_EXOP_WHO_AM_I, CONST_PERSISTENT | CONST_CS);
860 	REGISTER_STRING_CONSTANT("LDAP_EXOP_TURN", LDAP_EXOP_TURN, CONST_PERSISTENT | CONST_CS);
861 #endif
862 
863 /* LDAP Controls */
864 /*	standard track controls */
865 #ifdef LDAP_CONTROL_MANAGEDSAIT
866 	/* RFC 3296 */
867 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_MANAGEDSAIT", LDAP_CONTROL_MANAGEDSAIT, CONST_PERSISTENT | CONST_CS);
868 #endif
869 #ifdef LDAP_CONTROL_PROXY_AUTHZ
870 	/* RFC 4370 */
871 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PROXY_AUTHZ", LDAP_CONTROL_PROXY_AUTHZ, CONST_PERSISTENT | CONST_CS);
872 #endif
873 #ifdef LDAP_CONTROL_SUBENTRIES
874 	/* RFC 3672 */
875 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SUBENTRIES", LDAP_CONTROL_SUBENTRIES, CONST_PERSISTENT | CONST_CS);
876 #endif
877 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
878 	/* RFC 3876 */
879 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_VALUESRETURNFILTER", LDAP_CONTROL_VALUESRETURNFILTER, CONST_PERSISTENT | CONST_CS);
880 #endif
881 #ifdef LDAP_CONTROL_ASSERT
882 	/* RFC 4528 */
883 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_ASSERT", LDAP_CONTROL_ASSERT, CONST_PERSISTENT | CONST_CS);
884 	/* RFC 4527 */
885 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PRE_READ", LDAP_CONTROL_PRE_READ, CONST_PERSISTENT | CONST_CS);
886 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_POST_READ", LDAP_CONTROL_POST_READ, CONST_PERSISTENT | CONST_CS);
887 #endif
888 #ifdef LDAP_CONTROL_SORTREQUEST
889 	/* RFC 2891 */
890 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTREQUEST", LDAP_CONTROL_SORTREQUEST, CONST_PERSISTENT | CONST_CS);
891 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTRESPONSE", LDAP_CONTROL_SORTRESPONSE, CONST_PERSISTENT | CONST_CS);
892 #endif
893 /*	non-standard track controls */
894 #ifdef LDAP_CONTROL_PAGEDRESULTS
895 	/* RFC 2696 */
896 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PAGEDRESULTS", LDAP_CONTROL_PAGEDRESULTS, CONST_PERSISTENT | CONST_CS);
897 #endif
898 #ifdef LDAP_CONTROL_AUTHZID_REQUEST
899 	/* RFC 3829 */
900 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_REQUEST", LDAP_CONTROL_AUTHZID_REQUEST, CONST_PERSISTENT | CONST_CS);
901 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_RESPONSE", LDAP_CONTROL_AUTHZID_RESPONSE, CONST_PERSISTENT | CONST_CS);
902 #endif
903 #ifdef LDAP_CONTROL_SYNC
904 	/* LDAP Content Synchronization Operation -- RFC 4533 */
905 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC", LDAP_CONTROL_SYNC, CONST_PERSISTENT | CONST_CS);
906 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_STATE", LDAP_CONTROL_SYNC_STATE, CONST_PERSISTENT | CONST_CS);
907 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_DONE", LDAP_CONTROL_SYNC_DONE, CONST_PERSISTENT | CONST_CS);
908 #endif
909 #ifdef LDAP_CONTROL_DONTUSECOPY
910 	/* LDAP Don't Use Copy Control (RFC 6171) */
911 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_DONTUSECOPY", LDAP_CONTROL_DONTUSECOPY, CONST_PERSISTENT | CONST_CS);
912 #endif
913 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
914 	/* Password policy Controls */
915 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYREQUEST", LDAP_CONTROL_PASSWORDPOLICYREQUEST, CONST_PERSISTENT | CONST_CS);
916 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYRESPONSE", LDAP_CONTROL_PASSWORDPOLICYRESPONSE, CONST_PERSISTENT | CONST_CS);
917 #endif
918 #ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES
919 	/* MS Active Directory controls */
920 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_INCREMENTAL_VALUES", LDAP_CONTROL_X_INCREMENTAL_VALUES, CONST_PERSISTENT | CONST_CS);
921 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_DOMAIN_SCOPE", LDAP_CONTROL_X_DOMAIN_SCOPE, CONST_PERSISTENT | CONST_CS);
922 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_PERMISSIVE_MODIFY", LDAP_CONTROL_X_PERMISSIVE_MODIFY, CONST_PERSISTENT | CONST_CS);
923 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_SEARCH_OPTIONS", LDAP_CONTROL_X_SEARCH_OPTIONS, CONST_PERSISTENT | CONST_CS);
924 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_TREE_DELETE", LDAP_CONTROL_X_TREE_DELETE, CONST_PERSISTENT | CONST_CS);
925 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_EXTENDED_DN", LDAP_CONTROL_X_EXTENDED_DN, CONST_PERSISTENT | CONST_CS);
926 #endif
927 #ifdef LDAP_CONTROL_VLVREQUEST
928 	/* LDAP VLV */
929 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVREQUEST", LDAP_CONTROL_VLVREQUEST, CONST_PERSISTENT | CONST_CS);
930 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVRESPONSE", LDAP_CONTROL_VLVRESPONSE, CONST_PERSISTENT | CONST_CS);
931 #endif
932 
933 	le_link = zend_register_list_destructors_ex(_close_ldap_link, NULL, "ldap link", module_number);
934 	le_result = zend_register_list_destructors_ex(_free_ldap_result, NULL, "ldap result", module_number);
935 	le_result_entry = zend_register_list_destructors_ex(_free_ldap_result_entry, NULL, "ldap result entry", module_number);
936 
937 	ldap_module_entry.type = type;
938 
939 	return SUCCESS;
940 }
941 /* }}} */
942 
943 /* {{{ PHP_MSHUTDOWN_FUNCTION
944  */
PHP_MSHUTDOWN_FUNCTIONnull945 PHP_MSHUTDOWN_FUNCTION(ldap)
946 {
947 	UNREGISTER_INI_ENTRIES();
948 	return SUCCESS;
949 }
950 /* }}} */
951 
952 /* {{{ PHP_MINFO_FUNCTION
953  */
PHP_MINFO_FUNCTIONnull954 PHP_MINFO_FUNCTION(ldap)
955 {
956 	char tmp[32];
957 
958 	php_info_print_table_start();
959 	php_info_print_table_row(2, "LDAP Support", "enabled");
960 
961 	if (LDAPG(max_links) == -1) {
962 		snprintf(tmp, 31, ZEND_LONG_FMT "/unlimited", LDAPG(num_links));
963 	} else {
964 		snprintf(tmp, 31, ZEND_LONG_FMT "/" ZEND_LONG_FMT, LDAPG(num_links), LDAPG(max_links));
965 	}
966 	php_info_print_table_row(2, "Total Links", tmp);
967 
968 #ifdef LDAP_API_VERSION
969 	snprintf(tmp, 31, "%d", LDAP_API_VERSION);
970 	php_info_print_table_row(2, "API Version", tmp);
971 #endif
972 
973 #ifdef LDAP_VENDOR_NAME
974 	php_info_print_table_row(2, "Vendor Name", LDAP_VENDOR_NAME);
975 #endif
976 
977 #ifdef LDAP_VENDOR_VERSION
978 	snprintf(tmp, 31, "%d", LDAP_VENDOR_VERSION);
979 	php_info_print_table_row(2, "Vendor Version", tmp);
980 #endif
981 
982 #ifdef HAVE_LDAP_SASL
983 	php_info_print_table_row(2, "SASL Support", "Enabled");
984 #endif
985 
986 	php_info_print_table_end();
987 	DISPLAY_INI_ENTRIES();
988 }
989 /* }}} */
990 
991 /* {{{ proto resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]])
992    Connect to an LDAP server */
PHP_FUNCTIONnull993 PHP_FUNCTION(ldap_connect)
994 {
995 	char *host = NULL;
996 	size_t hostlen = 0;
997 	zend_long port = LDAP_PORT;
998 #ifdef HAVE_ORALDAP
999 	char *wallet = NULL, *walletpasswd = NULL;
1000 	size_t walletlen = 0, walletpasswdlen = 0;
1001 	zend_long authmode = GSLC_SSL_NO_AUTH;
1002 	int ssl=0;
1003 #endif
1004 	ldap_linkdata *ld;
1005 	LDAP *ldap = NULL;
1006 
1007 #ifdef HAVE_ORALDAP
1008 	if (ZEND_NUM_ARGS() == 3 || ZEND_NUM_ARGS() == 4) {
1009 		WRONG_PARAM_COUNT;
1010 	}
1011 
1012 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|slssl", &host, &hostlen, &port, &wallet, &walletlen, &walletpasswd, &walletpasswdlen, &authmode) != SUCCESS) {
1013 		RETURN_FALSE;
1014 	}
1015 
1016 	if (ZEND_NUM_ARGS() == 5) {
1017 		ssl = 1;
1018 	}
1019 #else
1020 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sl", &host, &hostlen, &port) != SUCCESS) {
1021 		RETURN_FALSE;
1022 	}
1023 #endif
1024 
1025 	if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
1026 		php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", LDAPG(num_links));
1027 		RETURN_FALSE;
1028 	}
1029 
1030 	ld = ecalloc(1, sizeof(ldap_linkdata));
1031 
1032 	{
1033 		int rc = LDAP_SUCCESS;
1034 		char	*url = host;
1035 		if (url && !ldap_is_ldap_url(url)) {
1036 			size_t urllen = hostlen + sizeof( "ldap://:65535" );
1037 
1038 			if (port <= 0 || port > 65535) {
1039 				efree(ld);
1040 				php_error_docref(NULL, E_WARNING, "invalid port number: " ZEND_LONG_FMT, port);
1041 				RETURN_FALSE;
1042 			}
1043 
1044 			url = emalloc(urllen);
1045 			snprintf( url, urllen, "ldap://%s:" ZEND_LONG_FMT, host, port );
1046 		}
1047 
1048 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1049 		/* ldap_init() is deprecated, use ldap_initialize() instead.
1050 		 */
1051 		rc = ldap_initialize(&ldap, url);
1052 #else /* ! LDAP_API_FEATURE_X_OPENLDAP */
1053 		/* ldap_init does not support URLs.
1054 		 * We must try the original host and port information.
1055 		 */
1056 		ldap = ldap_init(host, port);
1057 		if (ldap == NULL) {
1058 			efree(ld);
1059 			php_error_docref(NULL, E_WARNING, "Could not create session handle");
1060 			RETURN_FALSE;
1061 		}
1062 #endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
1063 		if (url != host) {
1064 			efree(url);
1065 		}
1066 		if (rc != LDAP_SUCCESS) {
1067 			efree(ld);
1068 			php_error_docref(NULL, E_WARNING, "Could not create session handle: %s", ldap_err2string(rc));
1069 			RETURN_FALSE;
1070 		}
1071 	}
1072 
1073 	if (ldap == NULL) {
1074 		efree(ld);
1075 		RETURN_FALSE;
1076 	} else {
1077 #ifdef HAVE_ORALDAP
1078 		if (ssl) {
1079 			if (ldap_init_SSL(&ldap->ld_sb, wallet, walletpasswd, authmode)) {
1080 				efree(ld);
1081 				php_error_docref(NULL, E_WARNING, "SSL init failed");
1082 				RETURN_FALSE;
1083 			}
1084 		}
1085 #endif
1086 		LDAPG(num_links)++;
1087 		ld->link = ldap;
1088 		RETURN_RES(zend_register_resource(ld, le_link));
1089 	}
1090 
1091 }
1092 /* }}} */
1093 
1094 /* {{{ _get_lderrno
1095  */
_get_lderrno(LDAP *ldap)1096 static int _get_lderrno(LDAP *ldap)
1097 {
1098 #if LDAP_API_VERSION > 2000 || HAVE_ORALDAP
1099 	int lderr;
1100 
1101 	/* New versions of OpenLDAP do it this way */
1102 	ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
1103 	return lderr;
1104 #else
1105 	return ldap->ld_errno;
1106 #endif
1107 }
1108 /* }}} */
1109 
1110 /* {{{ _set_lderrno
1111  */
_set_lderrno(LDAP *ldap, int lderr)1112 static void _set_lderrno(LDAP *ldap, int lderr)
1113 {
1114 #if LDAP_API_VERSION > 2000 || HAVE_ORALDAP
1115 	/* New versions of OpenLDAP do it this way */
1116 	ldap_set_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
1117 #else
1118 	ldap->ld_errno = lderr;
1119 #endif
1120 }
1121 /* }}} */
1122 
1123 /* {{{ proto bool ldap_bind(resource link [, string dn [, string password]])
1124    Bind to LDAP directory */
PHP_FUNCTIONnull1125 PHP_FUNCTION(ldap_bind)
1126 {
1127 	zval *link;
1128 	char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
1129 	size_t ldap_bind_dnlen, ldap_bind_pwlen;
1130 	ldap_linkdata *ld;
1131 	int rc;
1132 
1133 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|ss", &link, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen) != SUCCESS) {
1134 		RETURN_FALSE;
1135 	}
1136 
1137 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1138 		RETURN_FALSE;
1139 	}
1140 
1141 	if (ldap_bind_dn != NULL && memchr(ldap_bind_dn, '\0', ldap_bind_dnlen) != NULL) {
1142 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1143 		php_error_docref(NULL, E_WARNING, "DN contains a null byte");
1144 		RETURN_FALSE;
1145 	}
1146 
1147 	if (ldap_bind_pw != NULL && memchr(ldap_bind_pw, '\0', ldap_bind_pwlen) != NULL) {
1148 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1149 		php_error_docref(NULL, E_WARNING, "Password contains a null byte");
1150 		RETURN_FALSE;
1151 	}
1152 
1153 	{
1154 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1155 		/* ldap_simple_bind_s() is deprecated, use ldap_sasl_bind_s() instead.
1156 		 */
1157 		struct berval   cred;
1158 
1159 		cred.bv_val = ldap_bind_pw;
1160 		cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0;
1161 		rc = ldap_sasl_bind_s(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred,
1162 				NULL, NULL,     /* no controls right now */
1163 				NULL);	  /* we don't care about the server's credentials */
1164 #else /* ! LDAP_API_FEATURE_X_OPENLDAP */
1165 		rc = ldap_simple_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw);
1166 #endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
1167 	}
1168 	if ( rc != LDAP_SUCCESS) {
1169 		php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
1170 		RETURN_FALSE;
1171 	} else {
1172 		RETURN_TRUE;
1173 	}
1174 }
1175 /* }}} */
1176 
1177 /* {{{ proto resource ldap_bind_ext(resource link [, string dn [, string password [, serverctrls]]])
1178    Bind to LDAP directory */
PHP_FUNCTIONnull1179 PHP_FUNCTION(ldap_bind_ext)
1180 {
1181 	zval *serverctrls = NULL;
1182 	zval *link;
1183 	char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
1184 	size_t ldap_bind_dnlen, ldap_bind_pwlen;
1185 	ldap_linkdata *ld;
1186 	LDAPControl **lserverctrls = NULL;
1187 	LDAPMessage *ldap_res;
1188 	int rc;
1189 
1190 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|ssa", &link, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &serverctrls) != SUCCESS) {
1191 		RETURN_FALSE;
1192 	}
1193 
1194 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1195 		RETURN_FALSE;
1196 	}
1197 
1198 	if (ldap_bind_dn != NULL && memchr(ldap_bind_dn, '\0', ldap_bind_dnlen) != NULL) {
1199 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1200 		php_error_docref(NULL, E_WARNING, "DN contains a null byte");
1201 		RETURN_FALSE;
1202 	}
1203 
1204 	if (ldap_bind_pw != NULL && memchr(ldap_bind_pw, '\0', ldap_bind_pwlen) != NULL) {
1205 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1206 		php_error_docref(NULL, E_WARNING, "Password contains a null byte");
1207 		RETURN_FALSE;
1208 	}
1209 
1210 	if (serverctrls) {
1211 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
1212 		if (lserverctrls == NULL) {
1213 			RETVAL_FALSE;
1214 			goto cleanup;
1215 		}
1216 	}
1217 
1218 	{
1219 		/* ldap_simple_bind() is deprecated, use ldap_sasl_bind() instead */
1220 		struct berval   cred;
1221 		int msgid;
1222 
1223 		cred.bv_val = ldap_bind_pw;
1224 		cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0;
1225 		/* asynchronous call */
1226 		rc = ldap_sasl_bind(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred,
1227 				lserverctrls, NULL, &msgid);
1228 		if (rc != LDAP_SUCCESS ) {
1229 			php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s (%d)", ldap_err2string(rc), rc);
1230 			RETVAL_FALSE;
1231 			goto cleanup;
1232 		}
1233 
1234 		rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
1235 		if (rc == -1) {
1236 			php_error_docref(NULL, E_WARNING, "Bind operation failed");
1237 			RETVAL_FALSE;
1238 			goto cleanup;
1239 		}
1240 
1241 		/* return a PHP control object */
1242 		RETVAL_RES(zend_register_resource(ldap_res, le_result));
1243 	}
1244 
1245 cleanup:
1246 	if (lserverctrls) {
1247 		_php_ldap_controls_free(&lserverctrls);
1248 	}
1249 
1250 	return;
1251 }
1252 /* }}} */
1253 
1254 #ifdef HAVE_LDAP_SASL
1255 typedef struct {
1256 	char *mech;
1257 	char *realm;
1258 	char *authcid;
1259 	char *passwd;
1260 	char *authzid;
1261 } php_ldap_bictx;
1262 
1263 /* {{{ _php_sasl_setdefs
1264  */
_php_sasl_setdefs(LDAP *ld, char *sasl_mech, char *sasl_realm, char *sasl_authc_id, char *passwd, char *sasl_authz_id)1265 static php_ldap_bictx *_php_sasl_setdefs(LDAP *ld, char *sasl_mech, char *sasl_realm, char *sasl_authc_id, char *passwd, char *sasl_authz_id)
1266 {
1267 	php_ldap_bictx *ctx;
1268 
1269 	ctx = ber_memalloc(sizeof(php_ldap_bictx));
1270 	ctx->mech    = (sasl_mech) ? ber_strdup(sasl_mech) : NULL;
1271 	ctx->realm   = (sasl_realm) ? ber_strdup(sasl_realm) : NULL;
1272 	ctx->authcid = (sasl_authc_id) ? ber_strdup(sasl_authc_id) : NULL;
1273 	ctx->passwd  = (passwd) ? ber_strdup(passwd) : NULL;
1274 	ctx->authzid = (sasl_authz_id) ? ber_strdup(sasl_authz_id) : NULL;
1275 
1276 	if (ctx->mech == NULL) {
1277 		ldap_get_option(ld, LDAP_OPT_X_SASL_MECH, &ctx->mech);
1278 	}
1279 	if (ctx->realm == NULL) {
1280 		ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &ctx->realm);
1281 	}
1282 	if (ctx->authcid == NULL) {
1283 		ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHCID, &ctx->authcid);
1284 	}
1285 	if (ctx->authzid == NULL) {
1286 		ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHZID, &ctx->authzid);
1287 	}
1288 
1289 	return ctx;
1290 }
1291 /* }}} */
1292 
1293 /* {{{ _php_sasl_freedefs
1294  */
_php_sasl_freedefs(php_ldap_bictx *ctx)1295 static void _php_sasl_freedefs(php_ldap_bictx *ctx)
1296 {
1297 	if (ctx->mech) ber_memfree(ctx->mech);
1298 	if (ctx->realm) ber_memfree(ctx->realm);
1299 	if (ctx->authcid) ber_memfree(ctx->authcid);
1300 	if (ctx->passwd) ber_memfree(ctx->passwd);
1301 	if (ctx->authzid) ber_memfree(ctx->authzid);
1302 	ber_memfree(ctx);
1303 }
1304 /* }}} */
1305 
1306 /* {{{ _php_sasl_interact
1307    Internal interact function for SASL */
_php_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)1308 static int _php_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)
1309 {
1310 	sasl_interact_t *interact = in;
1311 	const char *p;
1312 	php_ldap_bictx *ctx = defaults;
1313 
1314 	for (;interact->id != SASL_CB_LIST_END;interact++) {
1315 		p = NULL;
1316 		switch(interact->id) {
1317 			case SASL_CB_GETREALM:
1318 				p = ctx->realm;
1319 				break;
1320 			case SASL_CB_AUTHNAME:
1321 				p = ctx->authcid;
1322 				break;
1323 			case SASL_CB_USER:
1324 				p = ctx->authzid;
1325 				break;
1326 			case SASL_CB_PASS:
1327 				p = ctx->passwd;
1328 				break;
1329 		}
1330 		if (p) {
1331 			interact->result = p;
1332 			interact->len = strlen(interact->result);
1333 		}
1334 	}
1335 	return LDAP_SUCCESS;
1336 }
1337 /* }}} */
1338 
1339 /* {{{ proto bool ldap_sasl_bind(resource link [, string binddn [, string password [, string sasl_mech [, string sasl_realm [, string sasl_authc_id [, string sasl_authz_id [, string props]]]]]]])
1340    Bind to LDAP directory using SASL */
PHP_FUNCTIONnull1341 PHP_FUNCTION(ldap_sasl_bind)
1342 {
1343 	zval *link;
1344 	ldap_linkdata *ld;
1345 	char *binddn = NULL;
1346 	char *passwd = NULL;
1347 	char *sasl_mech = NULL;
1348 	char *sasl_realm = NULL;
1349 	char *sasl_authz_id = NULL;
1350 	char *sasl_authc_id = NULL;
1351 	char *props = NULL;
1352 	size_t rc, dn_len, passwd_len, mech_len, realm_len, authc_id_len, authz_id_len, props_len;
1353 	php_ldap_bictx *ctx;
1354 
1355 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|sssssss", &link, &binddn, &dn_len, &passwd, &passwd_len, &sasl_mech, &mech_len, &sasl_realm, &realm_len, &sasl_authc_id, &authc_id_len, &sasl_authz_id, &authz_id_len, &props, &props_len) != SUCCESS) {
1356 		RETURN_FALSE;
1357 	}
1358 
1359 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1360 		RETURN_FALSE;
1361 	}
1362 
1363 	ctx = _php_sasl_setdefs(ld->link, sasl_mech, sasl_realm, sasl_authc_id, passwd, sasl_authz_id);
1364 
1365 	if (props) {
1366 		ldap_set_option(ld->link, LDAP_OPT_X_SASL_SECPROPS, props);
1367 	}
1368 
1369 	rc = ldap_sasl_interactive_bind_s(ld->link, binddn, ctx->mech, NULL, NULL, LDAP_SASL_QUIET, _php_sasl_interact, ctx);
1370 	if (rc != LDAP_SUCCESS) {
1371 		php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
1372 		RETVAL_FALSE;
1373 	} else {
1374 		RETVAL_TRUE;
1375 	}
1376 	_php_sasl_freedefs(ctx);
1377 }
1378 /* }}} */
1379 #endif /* HAVE_LDAP_SASL */
1380 
1381 /* {{{ proto bool ldap_unbind(resource link)
1382    Unbind from LDAP directory */
PHP_FUNCTIONnull1383 PHP_FUNCTION(ldap_unbind)
1384 {
1385 	zval *link;
1386 	ldap_linkdata *ld;
1387 
1388 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
1389 		RETURN_FALSE;
1390 	}
1391 
1392 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1393 		RETURN_FALSE;
1394 	}
1395 
1396 	zend_list_close(Z_RES_P(link));
1397 	RETURN_TRUE;
1398 }
1399 /* }}} */
1400 
1401 /* {{{ php_set_opts
1402  */
php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref, int *old_sizelimit, int *old_timelimit, int *old_deref)1403 static void php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref, int *old_sizelimit, int *old_timelimit, int *old_deref)
1404 {
1405 	/* sizelimit */
1406 	if (sizelimit > -1) {
1407 #if (LDAP_API_VERSION >= 2004) || HAVE_ORALDAP
1408 		ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_sizelimit);
1409 		ldap_set_option(ldap, LDAP_OPT_SIZELIMIT, &sizelimit);
1410 #else
1411 		*old_sizelimit = ldap->ld_sizelimit;
1412 		ldap->ld_sizelimit = sizelimit;
1413 #endif
1414 	}
1415 
1416 	/* timelimit */
1417 	if (timelimit > -1) {
1418 #if (LDAP_API_VERSION >= 2004) || HAVE_ORALDAP
1419 		ldap_get_option(ldap, LDAP_OPT_TIMELIMIT, old_timelimit);
1420 		ldap_set_option(ldap, LDAP_OPT_TIMELIMIT, &timelimit);
1421 #else
1422 		*old_timelimit = ldap->ld_timelimit;
1423 		ldap->ld_timelimit = timelimit;
1424 #endif
1425 	}
1426 
1427 	/* deref */
1428 	if (deref > -1) {
1429 #if (LDAP_API_VERSION >= 2004) || HAVE_ORALDAP
1430 		ldap_get_option(ldap, LDAP_OPT_DEREF, old_deref);
1431 		ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
1432 #else
1433 		*old_deref = ldap->ld_deref;
1434 		ldap->ld_deref = deref;
1435 #endif
1436 	}
1437 }
1438 /* }}} */
1439 
1440 /* {{{ php_ldap_do_search
1441  */
php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)1442 static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)
1443 {
1444 	zval *link, *base_dn, *filter, *attrs = NULL, *attr, *serverctrls = NULL;
1445 	zend_long attrsonly, sizelimit, timelimit, deref;
1446 	zend_string *ldap_filter = NULL, *ldap_base_dn = NULL;
1447 	char **ldap_attrs = NULL;
1448 	ldap_linkdata *ld = NULL;
1449 	LDAPMessage *ldap_res = NULL;
1450 	LDAPControl **lserverctrls = NULL;
1451 	int ldap_attrsonly = 0, ldap_sizelimit = -1, ldap_timelimit = -1, ldap_deref = -1;
1452 	int old_ldap_sizelimit = -1, old_ldap_timelimit = -1, old_ldap_deref = -1;
1453 	int num_attribs = 0, ret = 1, i, errno, argcount = ZEND_NUM_ARGS();
1454 
1455 	if (zend_parse_parameters(argcount, "zzz|a/lllla/", &link, &base_dn, &filter, &attrs, &attrsonly,
1456 		&sizelimit, &timelimit, &deref, &serverctrls) == FAILURE) {
1457 		return;
1458 	}
1459 
1460 	/* Reverse -> fall through */
1461 	switch (argcount) {
1462 		case 9:
1463 		case 8:
1464 			ldap_deref = deref;
1465 		case 7:
1466 			ldap_timelimit = timelimit;
1467 		case 6:
1468 			ldap_sizelimit = sizelimit;
1469 		case 5:
1470 			ldap_attrsonly = attrsonly;
1471 		case 4:
1472 			num_attribs = zend_hash_num_elements(Z_ARRVAL_P(attrs));
1473 			ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
1474 
1475 			for (i = 0; i<num_attribs; i++) {
1476 				if ((attr = zend_hash_index_find(Z_ARRVAL_P(attrs), i)) == NULL) {
1477 					php_error_docref(NULL, E_WARNING, "Array initialization wrong");
1478 					ret = 0;
1479 					goto cleanup;
1480 				}
1481 
1482 				convert_to_string(attr);
1483 				if (EG(exception)) {
1484 					ret = 0;
1485 					goto cleanup;
1486 				}
1487 				ldap_attrs[i] = Z_STRVAL_P(attr);
1488 			}
1489 			ldap_attrs[num_attribs] = NULL;
1490 		default:
1491 			break;
1492 	}
1493 
1494 	/* parallel search? */
1495 	if (Z_TYPE_P(link) == IS_ARRAY) {
1496 		int i, nlinks, nbases, nfilters, *rcs;
1497 		ldap_linkdata **lds;
1498 		zval *entry, resource;
1499 
1500 		nlinks = zend_hash_num_elements(Z_ARRVAL_P(link));
1501 		if (nlinks == 0) {
1502 			php_error_docref(NULL, E_WARNING, "No links in link array");
1503 			ret = 0;
1504 			goto cleanup;
1505 		}
1506 
1507 		if (Z_TYPE_P(base_dn) == IS_ARRAY) {
1508 			nbases = zend_hash_num_elements(Z_ARRVAL_P(base_dn));
1509 			if (nbases != nlinks) {
1510 				php_error_docref(NULL, E_WARNING, "Base must either be a string, or an array with the same number of elements as the links array");
1511 				ret = 0;
1512 				goto cleanup;
1513 			}
1514 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(base_dn));
1515 		} else {
1516 			nbases = 0; /* this means string, not array */
1517 			ldap_base_dn = zval_get_string(base_dn);
1518 			if (EG(exception)) {
1519 				ret = 0;
1520 				goto cleanup;
1521 			}
1522 		}
1523 
1524 		if (Z_TYPE_P(filter) == IS_ARRAY) {
1525 			nfilters = zend_hash_num_elements(Z_ARRVAL_P(filter));
1526 			if (nfilters != nlinks) {
1527 				php_error_docref(NULL, E_WARNING, "Filter must either be a string, or an array with the same number of elements as the links array");
1528 				ret = 0;
1529 				goto cleanup;
1530 			}
1531 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(filter));
1532 		} else {
1533 			nfilters = 0; /* this means string, not array */
1534 			ldap_filter = zval_get_string(filter);
1535 			if (EG(exception)) {
1536 				ret = 0;
1537 				goto cleanup;
1538 			}
1539 		}
1540 
1541 		lds = safe_emalloc(nlinks, sizeof(ldap_linkdata), 0);
1542 		rcs = safe_emalloc(nlinks, sizeof(*rcs), 0);
1543 
1544 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(link));
1545 		for (i=0; i<nlinks; i++) {
1546 			entry = zend_hash_get_current_data(Z_ARRVAL_P(link));
1547 
1548 			ld = (ldap_linkdata *) zend_fetch_resource_ex(entry, "ldap link", le_link);
1549 			if (ld == NULL) {
1550 				ret = 0;
1551 				goto cleanup_parallel;
1552 			}
1553 			if (nbases != 0) { /* base_dn an array? */
1554 				entry = zend_hash_get_current_data(Z_ARRVAL_P(base_dn));
1555 				zend_hash_move_forward(Z_ARRVAL_P(base_dn));
1556 				ldap_base_dn = zval_get_string(entry);
1557 				if (EG(exception)) {
1558 					ret = 0;
1559 					goto cleanup_parallel;
1560 				}
1561 			}
1562 			if (nfilters != 0) { /* filter an array? */
1563 				entry = zend_hash_get_current_data(Z_ARRVAL_P(filter));
1564 				zend_hash_move_forward(Z_ARRVAL_P(filter));
1565 				ldap_filter = zval_get_string(entry);
1566 				if (EG(exception)) {
1567 					ret = 0;
1568 					goto cleanup_parallel;
1569 				}
1570 			}
1571 
1572 			if (argcount > 8) {
1573 				/* We have to parse controls again for each link as they use it */
1574 				_php_ldap_controls_free(&lserverctrls);
1575 				lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
1576 				if (lserverctrls == NULL) {
1577 					rcs[i] = -1;
1578 					continue;
1579 				}
1580 			}
1581 
1582 			php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
1583 
1584 			/* Run the actual search */
1585 			ldap_search_ext(ld->link, ZSTR_VAL(ldap_base_dn), scope, ZSTR_VAL(ldap_filter), ldap_attrs, ldap_attrsonly, lserverctrls, NULL, NULL, ldap_sizelimit, &rcs[i]);
1586 			lds[i] = ld;
1587 			zend_hash_move_forward(Z_ARRVAL_P(link));
1588 		}
1589 
1590 		array_init(return_value);
1591 
1592 		/* Collect results from the searches */
1593 		for (i=0; i<nlinks; i++) {
1594 			if (rcs[i] != -1) {
1595 				rcs[i] = ldap_result(lds[i]->link, LDAP_RES_ANY, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
1596 			}
1597 			if (rcs[i] != -1) {
1598 				ZVAL_RES(&resource, zend_register_resource(ldap_res, le_result));
1599 				add_next_index_zval(return_value, &resource);
1600 			} else {
1601 				add_next_index_bool(return_value, 0);
1602 			}
1603 		}
1604 
1605 cleanup_parallel:
1606 		efree(lds);
1607 		efree(rcs);
1608 	} else {
1609 		ldap_filter = zval_get_string(filter);
1610 		if (EG(exception)) {
1611 			ret = 0;
1612 			goto cleanup;
1613 		}
1614 
1615 		ldap_base_dn = zval_get_string(base_dn);
1616 		if (EG(exception)) {
1617 			ret = 0;
1618 			goto cleanup;
1619 		}
1620 
1621 		ld = (ldap_linkdata *) zend_fetch_resource_ex(link, "ldap link", le_link);
1622 		if (ld == NULL) {
1623 			ret = 0;
1624 			goto cleanup;
1625 		}
1626 
1627 		if (argcount > 8) {
1628 			lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
1629 			if (lserverctrls == NULL) {
1630 				ret = 0;
1631 				goto cleanup;
1632 			}
1633 		}
1634 
1635 		php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
1636 
1637 		/* Run the actual search */
1638 		errno = ldap_search_ext_s(ld->link, ZSTR_VAL(ldap_base_dn), scope, ZSTR_VAL(ldap_filter), ldap_attrs, ldap_attrsonly, lserverctrls, NULL, NULL, ldap_sizelimit, &ldap_res);
1639 
1640 		if (errno != LDAP_SUCCESS
1641 			&& errno != LDAP_SIZELIMIT_EXCEEDED
1642 #ifdef LDAP_ADMINLIMIT_EXCEEDED
1643 			&& errno != LDAP_ADMINLIMIT_EXCEEDED
1644 #endif
1645 #ifdef LDAP_REFERRAL
1646 			&& errno != LDAP_REFERRAL
1647 #endif
1648 		) {
1649 			/* ldap_res should be freed regardless of return value of ldap_search_ext_s()
1650 			 * see: https://linux.die.net/man/3/ldap_search_ext_s */
1651 			if (ldap_res != NULL) {
1652 				ldap_msgfree(ldap_res);
1653 			}
1654 			php_error_docref(NULL, E_WARNING, "Search: %s", ldap_err2string(errno));
1655 			ret = 0;
1656 		} else {
1657 			if (errno == LDAP_SIZELIMIT_EXCEEDED) {
1658 				php_error_docref(NULL, E_WARNING, "Partial search results returned: Sizelimit exceeded");
1659 			}
1660 #ifdef LDAP_ADMINLIMIT_EXCEEDED
1661 			else if (errno == LDAP_ADMINLIMIT_EXCEEDED) {
1662 				php_error_docref(NULL, E_WARNING, "Partial search results returned: Adminlimit exceeded");
1663 			}
1664 #endif
1665 
1666 			RETVAL_RES(zend_register_resource(ldap_res, le_result));
1667 		}
1668 	}
1669 
1670 cleanup:
1671 	if (ld) {
1672 		/* Restoring previous options */
1673 		php_set_opts(ld->link, old_ldap_sizelimit, old_ldap_timelimit, old_ldap_deref, &ldap_sizelimit, &ldap_timelimit, &ldap_deref);
1674 	}
1675 	if (ldap_filter) {
1676 		zend_string_release(ldap_filter);
1677 	}
1678 	if (ldap_base_dn) {
1679 		zend_string_release(ldap_base_dn);
1680 	}
1681 	if (ldap_attrs != NULL) {
1682 		efree(ldap_attrs);
1683 	}
1684 	if (!ret) {
1685 		RETVAL_BOOL(ret);
1686 	}
1687 	if (lserverctrls) {
1688 		_php_ldap_controls_free(&lserverctrls);
1689 	}
1690 }
1691 /* }}} */
1692 
1693 /* {{{ proto resource ldap_read(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref [, array servercontrols]]]]]])
1694    Read an entry */
PHP_FUNCTIONnull1695 PHP_FUNCTION(ldap_read)
1696 {
1697 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_BASE);
1698 }
1699 /* }}} */
1700 
1701 /* {{{ proto resource ldap_list(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref [, array servercontrols]]]]]])
1702    Single-level search */
PHP_FUNCTIONnull1703 PHP_FUNCTION(ldap_list)
1704 {
1705 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_ONELEVEL);
1706 }
1707 /* }}} */
1708 
1709 /* {{{ proto resource ldap_search(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref [, array servercontrols]]]]]])
1710    Search LDAP tree under base_dn */
PHP_FUNCTIONnull1711 PHP_FUNCTION(ldap_search)
1712 {
1713 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_SUBTREE);
1714 }
1715 /* }}} */
1716 
1717 /* {{{ proto bool ldap_free_result(resource result)
1718    Free result memory */
PHP_FUNCTIONnull1719 PHP_FUNCTION(ldap_free_result)
1720 {
1721 	zval *result;
1722 	LDAPMessage *ldap_result;
1723 
1724 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) != SUCCESS) {
1725 		return;
1726 	}
1727 
1728 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1729 		RETURN_FALSE;
1730 	}
1731 
1732 	zend_list_close(Z_RES_P(result));  /* Delete list entry */
1733 	RETVAL_TRUE;
1734 }
1735 /* }}} */
1736 
1737 /* {{{ proto int ldap_count_entries(resource link, resource result)
1738    Count the number of entries in a search result */
PHP_FUNCTIONnull1739 PHP_FUNCTION(ldap_count_entries)
1740 {
1741 	zval *link, *result;
1742 	ldap_linkdata *ld;
1743 	LDAPMessage *ldap_result;
1744 
1745 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
1746 		return;
1747 	}
1748 
1749 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1750 		RETURN_FALSE;
1751 	}
1752 
1753 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1754 		RETURN_FALSE;
1755 	}
1756 
1757 	RETURN_LONG(ldap_count_entries(ld->link, ldap_result));
1758 }
1759 /* }}} */
1760 
1761 /* {{{ proto resource ldap_first_entry(resource link, resource result)
1762    Return first result id */
PHP_FUNCTIONnull1763 PHP_FUNCTION(ldap_first_entry)
1764 {
1765 	zval *link, *result;
1766 	ldap_linkdata *ld;
1767 	ldap_resultentry *resultentry;
1768 	LDAPMessage *ldap_result, *entry;
1769 
1770 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
1771 		return;
1772 	}
1773 
1774 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1775 		RETURN_FALSE;
1776 	}
1777 
1778 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1779 		RETURN_FALSE;
1780 	}
1781 
1782 	if ((entry = ldap_first_entry(ld->link, ldap_result)) == NULL) {
1783 		RETVAL_FALSE;
1784 	} else {
1785 		resultentry = emalloc(sizeof(ldap_resultentry));
1786 		RETVAL_RES(zend_register_resource(resultentry, le_result_entry));
1787 		ZVAL_COPY(&resultentry->res, result);
1788 		resultentry->data = entry;
1789 		resultentry->ber = NULL;
1790 	}
1791 }
1792 /* }}} */
1793 
1794 /* {{{ proto resource ldap_next_entry(resource link, resource result_entry)
1795    Get next result entry */
PHP_FUNCTIONnull1796 PHP_FUNCTION(ldap_next_entry)
1797 {
1798 	zval *link, *result_entry;
1799 	ldap_linkdata *ld;
1800 	ldap_resultentry *resultentry, *resultentry_next;
1801 	LDAPMessage *entry_next;
1802 
1803 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
1804 		return;
1805 	}
1806 
1807 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1808 		RETURN_FALSE;
1809 	}
1810 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
1811 		RETURN_FALSE;
1812 	}
1813 
1814 	if ((entry_next = ldap_next_entry(ld->link, resultentry->data)) == NULL) {
1815 		RETVAL_FALSE;
1816 	} else {
1817 		resultentry_next = emalloc(sizeof(ldap_resultentry));
1818 		RETVAL_RES(zend_register_resource(resultentry_next, le_result_entry));
1819 		ZVAL_COPY(&resultentry_next->res, &resultentry->res);
1820 		resultentry_next->data = entry_next;
1821 		resultentry_next->ber = NULL;
1822 	}
1823 }
1824 /* }}} */
1825 
1826 /* {{{ proto array ldap_get_entries(resource link, resource result)
1827    Get all result entries */
PHP_FUNCTIONnull1828 PHP_FUNCTION(ldap_get_entries)
1829 {
1830 	zval *link, *result;
1831 	LDAPMessage *ldap_result, *ldap_result_entry;
1832 	zval tmp1, tmp2;
1833 	ldap_linkdata *ld;
1834 	LDAP *ldap;
1835 	int num_entries, num_attrib, num_values, i;
1836 	BerElement *ber;
1837 	char *attribute;
1838 	size_t attr_len;
1839 	struct berval **ldap_value;
1840 	char *dn;
1841 
1842 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
1843 		return;
1844 	}
1845 
1846 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1847 		RETURN_FALSE;
1848 	}
1849 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1850 		RETURN_FALSE;
1851 	}
1852 
1853 	ldap = ld->link;
1854 	num_entries = ldap_count_entries(ldap, ldap_result);
1855 
1856 	array_init(return_value);
1857 	add_assoc_long(return_value, "count", num_entries);
1858 
1859 	if (num_entries == 0) {
1860 		return;
1861 	}
1862 
1863 	ldap_result_entry = ldap_first_entry(ldap, ldap_result);
1864 	if (ldap_result_entry == NULL) {
1865 		zend_array_destroy(Z_ARR_P(return_value));
1866 		RETURN_FALSE;
1867 	}
1868 
1869 	num_entries = 0;
1870 	while (ldap_result_entry != NULL) {
1871 		array_init(&tmp1);
1872 
1873 		num_attrib = 0;
1874 		attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber);
1875 
1876 		while (attribute != NULL) {
1877 			ldap_value = ldap_get_values_len(ldap, ldap_result_entry, attribute);
1878 			num_values = ldap_count_values_len(ldap_value);
1879 
1880 			array_init(&tmp2);
1881 			add_assoc_long(&tmp2, "count", num_values);
1882 			for (i = 0; i < num_values; i++) {
1883 				add_index_stringl(&tmp2, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len);
1884 			}
1885 			ldap_value_free_len(ldap_value);
1886 
1887 			attr_len = strlen(attribute);
1888 			zend_hash_str_update(Z_ARRVAL(tmp1), php_strtolower(attribute, attr_len), attr_len, &tmp2);
1889 			add_index_string(&tmp1, num_attrib, attribute);
1890 
1891 			num_attrib++;
1892 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1893 			ldap_memfree(attribute);
1894 #endif
1895 			attribute = ldap_next_attribute(ldap, ldap_result_entry, ber);
1896 		}
1897 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1898 		if (ber != NULL) {
1899 			ber_free(ber, 0);
1900 		}
1901 #endif
1902 
1903 		add_assoc_long(&tmp1, "count", num_attrib);
1904 		dn = ldap_get_dn(ldap, ldap_result_entry);
1905 		if (dn) {
1906 			add_assoc_string(&tmp1, "dn", dn);
1907 		} else {
1908 			add_assoc_null(&tmp1, "dn");
1909 		}
1910 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1911 		ldap_memfree(dn);
1912 #else
1913 		free(dn);
1914 #endif
1915 
1916 		zend_hash_index_update(Z_ARRVAL_P(return_value), num_entries, &tmp1);
1917 
1918 		num_entries++;
1919 		ldap_result_entry = ldap_next_entry(ldap, ldap_result_entry);
1920 	}
1921 
1922 	add_assoc_long(return_value, "count", num_entries);
1923 
1924 }
1925 /* }}} */
1926 
1927 /* {{{ proto string ldap_first_attribute(resource link, resource result_entry)
1928    Return first attribute */
PHP_FUNCTIONnull1929 PHP_FUNCTION(ldap_first_attribute)
1930 {
1931 	zval *link, *result_entry;
1932 	ldap_linkdata *ld;
1933 	ldap_resultentry *resultentry;
1934 	char *attribute;
1935 	zend_long dummy_ber;
1936 
1937 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1938 		return;
1939 	}
1940 
1941 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1942 		RETURN_FALSE;
1943 	}
1944 
1945 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
1946 		RETURN_FALSE;
1947 	}
1948 
1949 	if ((attribute = ldap_first_attribute(ld->link, resultentry->data, &resultentry->ber)) == NULL) {
1950 		RETURN_FALSE;
1951 	} else {
1952 		RETVAL_STRING(attribute);
1953 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1954 		ldap_memfree(attribute);
1955 #endif
1956 	}
1957 }
1958 /* }}} */
1959 
1960 /* {{{ proto string ldap_next_attribute(resource link, resource result_entry)
1961    Get the next attribute in result */
PHP_FUNCTIONnull1962 PHP_FUNCTION(ldap_next_attribute)
1963 {
1964 	zval *link, *result_entry;
1965 	ldap_linkdata *ld;
1966 	ldap_resultentry *resultentry;
1967 	char *attribute;
1968 	zend_long dummy_ber;
1969 
1970 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1971 		return;
1972 	}
1973 
1974 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1975 		RETURN_FALSE;
1976 	}
1977 
1978 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
1979 		RETURN_FALSE;
1980 	}
1981 
1982 	if (resultentry->ber == NULL) {
1983 		php_error_docref(NULL, E_WARNING, "called before calling ldap_first_attribute() or no attributes found in result entry");
1984 		RETURN_FALSE;
1985 	}
1986 
1987 	if ((attribute = ldap_next_attribute(ld->link, resultentry->data, resultentry->ber)) == NULL) {
1988 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1989 		if (resultentry->ber != NULL) {
1990 			ber_free(resultentry->ber, 0);
1991 			resultentry->ber = NULL;
1992 		}
1993 #endif
1994 		RETURN_FALSE;
1995 	} else {
1996 		RETVAL_STRING(attribute);
1997 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1998 		ldap_memfree(attribute);
1999 #endif
2000 	}
2001 }
2002 /* }}} */
2003 
2004 /* {{{ proto array ldap_get_attributes(resource link, resource result_entry)
2005    Get attributes from a search result entry */
PHP_FUNCTIONnull2006 PHP_FUNCTION(ldap_get_attributes)
2007 {
2008 	zval *link, *result_entry;
2009 	zval tmp;
2010 	ldap_linkdata *ld;
2011 	ldap_resultentry *resultentry;
2012 	char *attribute;
2013 	struct berval **ldap_value;
2014 	int i, num_values, num_attrib;
2015 	BerElement *ber;
2016 
2017 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
2018 		return;
2019 	}
2020 
2021 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2022 		RETURN_FALSE;
2023 	}
2024 
2025 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
2026 		RETURN_FALSE;
2027 	}
2028 
2029 	array_init(return_value);
2030 	num_attrib = 0;
2031 
2032 	attribute = ldap_first_attribute(ld->link, resultentry->data, &ber);
2033 	while (attribute != NULL) {
2034 		ldap_value = ldap_get_values_len(ld->link, resultentry->data, attribute);
2035 		num_values = ldap_count_values_len(ldap_value);
2036 
2037 		array_init(&tmp);
2038 		add_assoc_long(&tmp, "count", num_values);
2039 		for (i = 0; i < num_values; i++) {
2040 			add_index_stringl(&tmp, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len);
2041 		}
2042 		ldap_value_free_len(ldap_value);
2043 
2044 		zend_hash_str_update(Z_ARRVAL_P(return_value), attribute, strlen(attribute), &tmp);
2045 		add_index_string(return_value, num_attrib, attribute);
2046 
2047 		num_attrib++;
2048 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2049 		ldap_memfree(attribute);
2050 #endif
2051 		attribute = ldap_next_attribute(ld->link, resultentry->data, ber);
2052 	}
2053 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2054 	if (ber != NULL) {
2055 		ber_free(ber, 0);
2056 	}
2057 #endif
2058 
2059 	add_assoc_long(return_value, "count", num_attrib);
2060 }
2061 /* }}} */
2062 
2063 /* {{{ proto array ldap_get_values_len(resource link, resource result_entry, string attribute)
2064    Get all values with lengths from a result entry */
PHP_FUNCTIONnull2065 PHP_FUNCTION(ldap_get_values_len)
2066 {
2067 	zval *link, *result_entry;
2068 	ldap_linkdata *ld;
2069 	ldap_resultentry *resultentry;
2070 	char *attr;
2071 	struct berval **ldap_value_len;
2072 	int i, num_values;
2073 	size_t attr_len;
2074 
2075 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrs", &link, &result_entry, &attr, &attr_len) != SUCCESS) {
2076 		return;
2077 	}
2078 
2079 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2080 		RETURN_FALSE;
2081 	}
2082 
2083 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
2084 		RETURN_FALSE;
2085 	}
2086 
2087 	if ((ldap_value_len = ldap_get_values_len(ld->link, resultentry->data, attr)) == NULL) {
2088 		php_error_docref(NULL, E_WARNING, "Cannot get the value(s) of attribute %s", ldap_err2string(_get_lderrno(ld->link)));
2089 		RETURN_FALSE;
2090 	}
2091 
2092 	num_values = ldap_count_values_len(ldap_value_len);
2093 	array_init(return_value);
2094 
2095 	for (i=0; i<num_values; i++) {
2096 		add_next_index_stringl(return_value, ldap_value_len[i]->bv_val, ldap_value_len[i]->bv_len);
2097 	}
2098 
2099 	add_assoc_long(return_value, "count", num_values);
2100 	ldap_value_free_len(ldap_value_len);
2101 
2102 }
2103 /* }}} */
2104 
2105 /* {{{ proto string ldap_get_dn(resource link, resource result_entry)
2106    Get the DN of a result entry */
PHP_FUNCTIONnull2107 PHP_FUNCTION(ldap_get_dn)
2108 {
2109 	zval *link, *result_entry;
2110 	ldap_linkdata *ld;
2111 	ldap_resultentry *resultentry;
2112 	char *text;
2113 
2114 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
2115 		return;
2116 	}
2117 
2118 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2119 		RETURN_FALSE;
2120 	}
2121 
2122 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
2123 		RETURN_FALSE;
2124 	}
2125 
2126 	text = ldap_get_dn(ld->link, resultentry->data);
2127 	if (text != NULL) {
2128 		RETVAL_STRING(text);
2129 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2130 		ldap_memfree(text);
2131 #else
2132 		free(text);
2133 #endif
2134 	} else {
2135 		RETURN_FALSE;
2136 	}
2137 }
2138 /* }}} */
2139 
2140 /* {{{ proto array ldap_explode_dn(string dn, int with_attrib)
2141    Splits DN into its component parts */
PHP_FUNCTIONnull2142 PHP_FUNCTION(ldap_explode_dn)
2143 {
2144 	zend_long with_attrib;
2145 	char *dn, **ldap_value;
2146 	int i, count;
2147 	size_t dn_len;
2148 
2149 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &dn, &dn_len, &with_attrib) != SUCCESS) {
2150 		return;
2151 	}
2152 
2153 	if (!(ldap_value = ldap_explode_dn(dn, with_attrib))) {
2154 		/* Invalid parameters were passed to ldap_explode_dn */
2155 		RETURN_FALSE;
2156 	}
2157 
2158 	i=0;
2159 	while (ldap_value[i] != NULL) i++;
2160 	count = i;
2161 
2162 	array_init(return_value);
2163 
2164 	add_assoc_long(return_value, "count", count);
2165 	for (i = 0; i<count; i++) {
2166 		add_index_string(return_value, i, ldap_value[i]);
2167 	}
2168 
2169 	ldap_memvfree((void **)ldap_value);
2170 }
2171 /* }}} */
2172 
2173 /* {{{ proto string ldap_dn2ufn(string dn)
2174    Convert DN to User Friendly Naming format */
PHP_FUNCTIONnull2175 PHP_FUNCTION(ldap_dn2ufn)
2176 {
2177 	char *dn, *ufn;
2178 	size_t dn_len;
2179 
2180 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &dn, &dn_len) != SUCCESS) {
2181 		return;
2182 	}
2183 
2184 	ufn = ldap_dn2ufn(dn);
2185 
2186 	if (ufn != NULL) {
2187 		RETVAL_STRING(ufn);
2188 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2189 		ldap_memfree(ufn);
2190 #endif
2191 	} else {
2192 		RETURN_FALSE;
2193 	}
2194 }
2195 /* }}} */
2196 
2197 
2198 /* added to fix use of ldap_modify_add for doing an ldap_add, gerrit thomson. */
2199 #define PHP_LD_FULL_ADD 0xff
2200 /* {{{ php_ldap_do_modify
2201  */
php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)2202 static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
2203 {
2204 	zval *serverctrls = NULL;
2205 	zval *link, *entry, *value, *ivalue;
2206 	ldap_linkdata *ld;
2207 	char *dn;
2208 	LDAPMod **ldap_mods;
2209 	LDAPControl **lserverctrls = NULL;
2210 	LDAPMessage *ldap_res;
2211 	int i, j, num_attribs, num_values, msgid;
2212 	size_t dn_len;
2213 	int *num_berval;
2214 	zend_string *attribute;
2215 	zend_ulong index;
2216 	int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */
2217 
2218 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/|a", &link, &dn, &dn_len, &entry, &serverctrls) != SUCCESS) {
2219 		return;
2220 	}
2221 
2222 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2223 		RETURN_FALSE;
2224 	}
2225 
2226 	num_attribs = zend_hash_num_elements(Z_ARRVAL_P(entry));
2227 	ldap_mods = safe_emalloc((num_attribs+1), sizeof(LDAPMod *), 0);
2228 	num_berval = safe_emalloc(num_attribs, sizeof(int), 0);
2229 	zend_hash_internal_pointer_reset(Z_ARRVAL_P(entry));
2230 
2231 	/* added by gerrit thomson to fix ldap_add using ldap_mod_add */
2232 	if (oper == PHP_LD_FULL_ADD) {
2233 		oper = LDAP_MOD_ADD;
2234 		is_full_add = 1;
2235 	}
2236 	/* end additional , gerrit thomson */
2237 
2238 	for (i = 0; i < num_attribs; i++) {
2239 		ldap_mods[i] = emalloc(sizeof(LDAPMod));
2240 		ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
2241 		ldap_mods[i]->mod_type = NULL;
2242 
2243 		if (zend_hash_get_current_key(Z_ARRVAL_P(entry), &attribute, &index) == HASH_KEY_IS_STRING) {
2244 			ldap_mods[i]->mod_type = estrndup(ZSTR_VAL(attribute), ZSTR_LEN(attribute));
2245 		} else {
2246 			php_error_docref(NULL, E_WARNING, "Unknown attribute in the data");
2247 			/* Free allocated memory */
2248 			while (i >= 0) {
2249 				if (ldap_mods[i]->mod_type) {
2250 					efree(ldap_mods[i]->mod_type);
2251 				}
2252 				efree(ldap_mods[i]);
2253 				i--;
2254 			}
2255 			efree(num_berval);
2256 			efree(ldap_mods);
2257 			RETURN_FALSE;
2258 		}
2259 
2260 		value = zend_hash_get_current_data(Z_ARRVAL_P(entry));
2261 
2262 		ZVAL_DEREF(value);
2263 		if (Z_TYPE_P(value) != IS_ARRAY) {
2264 			num_values = 1;
2265 		} else {
2266 			SEPARATE_ARRAY(value);
2267 			num_values = zend_hash_num_elements(Z_ARRVAL_P(value));
2268 		}
2269 
2270 		num_berval[i] = num_values;
2271 		ldap_mods[i]->mod_bvalues = safe_emalloc((num_values + 1), sizeof(struct berval *), 0);
2272 
2273 /* allow for arrays with one element, no allowance for arrays with none but probably not required, gerrit thomson. */
2274 		if ((num_values == 1) && (Z_TYPE_P(value) != IS_ARRAY)) {
2275 			convert_to_string(value);
2276 			if (EG(exception)) {
2277 				RETVAL_FALSE;
2278 				goto cleanup;
2279 			}
2280 			ldap_mods[i]->mod_bvalues[0] = (struct berval *) emalloc (sizeof(struct berval));
2281 			ldap_mods[i]->mod_bvalues[0]->bv_val = Z_STRVAL_P(value);
2282 			ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_P(value);
2283 		} else {
2284 			for (j = 0; j < num_values; j++) {
2285 				if ((ivalue = zend_hash_index_find(Z_ARRVAL_P(value), j)) == NULL) {
2286 					php_error_docref(NULL, E_WARNING, "Value array must have consecutive indices 0, 1, ...");
2287 					num_berval[i] = j;
2288 					num_attribs = i + 1;
2289 					RETVAL_FALSE;
2290 					goto cleanup;
2291 				}
2292 				convert_to_string(ivalue);
2293 				if (EG(exception)) {
2294 					RETVAL_FALSE;
2295 					goto cleanup;
2296 				}
2297 				ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval));
2298 				ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_P(ivalue);
2299 				ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(ivalue);
2300 			}
2301 		}
2302 		ldap_mods[i]->mod_bvalues[num_values] = NULL;
2303 		zend_hash_move_forward(Z_ARRVAL_P(entry));
2304 	}
2305 	ldap_mods[num_attribs] = NULL;
2306 
2307 	if (serverctrls) {
2308 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2309 		if (lserverctrls == NULL) {
2310 			RETVAL_FALSE;
2311 			goto cleanup;
2312 		}
2313 	}
2314 
2315 /* check flag to see if do_mod was called to perform full add , gerrit thomson */
2316 	if (is_full_add == 1) {
2317 		if (ext) {
2318 			i = ldap_add_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
2319 		} else {
2320 			i = ldap_add_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
2321 		}
2322 		if (i != LDAP_SUCCESS) {
2323 			php_error_docref(NULL, E_WARNING, "Add: %s", ldap_err2string(i));
2324 			RETVAL_FALSE;
2325 		} else if (ext) {
2326 			i = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
2327 			if (i == -1) {
2328 				php_error_docref(NULL, E_WARNING, "Add operation failed");
2329 				RETVAL_FALSE;
2330 				goto cleanup;
2331 			}
2332 
2333 			/* return a PHP control object */
2334 			RETVAL_RES(zend_register_resource(ldap_res, le_result));
2335 		} else RETVAL_TRUE;
2336 	} else {
2337 		if (ext) {
2338 			i = ldap_modify_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
2339 		} else {
2340 			i = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
2341 		}
2342 		if (i != LDAP_SUCCESS) {
2343 			php_error_docref(NULL, E_WARNING, "Modify: %s", ldap_err2string(i));
2344 			RETVAL_FALSE;
2345 		} else if (ext) {
2346 			i = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
2347 			if (i == -1) {
2348 				php_error_docref(NULL, E_WARNING, "Modify operation failed");
2349 				RETVAL_FALSE;
2350 				goto cleanup;
2351 			}
2352 
2353 			/* return a PHP control object */
2354 			RETVAL_RES(zend_register_resource(ldap_res, le_result));
2355 		} else RETVAL_TRUE;
2356 	}
2357 
2358 cleanup:
2359 	for (i = 0; i < num_attribs; i++) {
2360 		efree(ldap_mods[i]->mod_type);
2361 		for (j = 0; j < num_berval[i]; j++) {
2362 			efree(ldap_mods[i]->mod_bvalues[j]);
2363 		}
2364 		efree(ldap_mods[i]->mod_bvalues);
2365 		efree(ldap_mods[i]);
2366 	}
2367 	efree(num_berval);
2368 	efree(ldap_mods);
2369 
2370 	if (lserverctrls) {
2371 		_php_ldap_controls_free(&lserverctrls);
2372 	}
2373 
2374 	return;
2375 }
2376 /* }}} */
2377 
2378 /* {{{ proto bool ldap_add(resource link, string dn, array entry [, array servercontrols])
2379    Add entries to LDAP directory */
PHP_FUNCTIONnull2380 PHP_FUNCTION(ldap_add)
2381 {
2382 	/* use a newly define parameter into the do_modify so ldap_mod_add can be used the way it is supposed to be used , Gerrit THomson */
2383 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, 0);
2384 }
2385 /* }}} */
2386 
2387 /* {{{ proto resource ldap_add_ext(resource link, string dn, array entry [, array servercontrols])
2388    Add entries to LDAP directory */
PHP_FUNCTIONnull2389 PHP_FUNCTION(ldap_add_ext)
2390 {
2391 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, 1);
2392 }
2393 /* }}} */
2394 
2395 /* three functions for attribute base modifications, gerrit Thomson */
2396 
2397 /* {{{ proto bool ldap_mod_replace(resource link, string dn, array entry [, array servercontrols])
2398    Replace attribute values with new ones */
PHP_FUNCTIONnull2399 PHP_FUNCTION(ldap_mod_replace)
2400 {
2401 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, 0);
2402 }
2403 /* }}} */
2404 
2405 /* {{{ proto resource ldap_mod_replace_ext(resource link, string dn, array entry [, array servercontrols])
2406    Replace attribute values with new ones */
PHP_FUNCTIONnull2407 PHP_FUNCTION(ldap_mod_replace_ext)
2408 {
2409 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, 1);
2410 }
2411 /* }}} */
2412 
2413 /* {{{ proto bool ldap_mod_add(resource link, string dn, array entry [, array servercontrols])
2414    Add attribute values to current */
PHP_FUNCTIONnull2415 PHP_FUNCTION(ldap_mod_add)
2416 {
2417 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, 0);
2418 }
2419 /* }}} */
2420 
2421 /* {{{ proto resource ldap_mod_add(resource link, string dn, array entry [, array servercontrols])
2422    Add attribute values to current */
PHP_FUNCTIONnull2423 PHP_FUNCTION(ldap_mod_add_ext)
2424 {
2425 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, 1);
2426 }
2427 /* }}} */
2428 
2429 /* {{{ proto bool ldap_mod_del(resource link, string dn, array entry [, array servercontrols])
2430    Delete attribute values */
PHP_FUNCTIONnull2431 PHP_FUNCTION(ldap_mod_del)
2432 {
2433 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, 0);
2434 }
2435 /* }}} */
2436 
2437 /* {{{ proto resource ldap_mod_del_ext(resource link, string dn, array entry [, array servercontrols])
2438    Delete attribute values */
PHP_FUNCTIONnull2439 PHP_FUNCTION(ldap_mod_del_ext)
2440 {
2441 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, 1);
2442 }
2443 /* }}} */
2444 
2445 /* {{{ php_ldap_do_delete
2446  */
php_ldap_do_delete(INTERNAL_FUNCTION_PARAMETERS, int ext)2447 static void php_ldap_do_delete(INTERNAL_FUNCTION_PARAMETERS, int ext)
2448 {
2449 	zval *serverctrls = NULL;
2450 	zval *link;
2451 	ldap_linkdata *ld;
2452 	LDAPControl **lserverctrls = NULL;
2453 	LDAPMessage *ldap_res;
2454 	char *dn;
2455 	int rc, msgid;
2456 	size_t dn_len;
2457 
2458 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|a", &link, &dn, &dn_len, &serverctrls) != SUCCESS) {
2459 		return;
2460 	}
2461 
2462 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2463 		RETURN_FALSE;
2464 	}
2465 
2466 	if (serverctrls) {
2467 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2468 		if (lserverctrls == NULL) {
2469 			RETVAL_FALSE;
2470 			goto cleanup;
2471 		}
2472 	}
2473 
2474 	if (ext) {
2475 		rc = ldap_delete_ext(ld->link, dn, lserverctrls, NULL, &msgid);
2476 	} else {
2477 		rc = ldap_delete_ext_s(ld->link, dn, lserverctrls, NULL);
2478 	}
2479 	if (rc != LDAP_SUCCESS) {
2480 		php_error_docref(NULL, E_WARNING, "Delete: %s", ldap_err2string(rc));
2481 		RETVAL_FALSE;
2482 		goto cleanup;
2483 	} else if (ext) {
2484 		rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
2485 		if (rc == -1) {
2486 			php_error_docref(NULL, E_WARNING, "Delete operation failed");
2487 			RETVAL_FALSE;
2488 			goto cleanup;
2489 		}
2490 
2491 		/* return a PHP control object */
2492 		RETVAL_RES(zend_register_resource(ldap_res, le_result));
2493 	} else {
2494 		RETVAL_TRUE;
2495 	}
2496 
2497 cleanup:
2498 	if (lserverctrls) {
2499 		_php_ldap_controls_free(&lserverctrls);
2500 	}
2501 
2502 	return;
2503 }
2504 /* }}} */
2505 
2506 /* {{{ proto bool ldap_delete(resource link, string dn [, array servercontrols])
2507    Delete an entry from a directory */
PHP_FUNCTIONnull2508 PHP_FUNCTION(ldap_delete)
2509 {
2510 	php_ldap_do_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2511 }
2512 /* }}} */
2513 
2514 /* {{{ proto resource ldap_delete_ext(resource link, string dn [, array servercontrols])
2515    Delete an entry from a directory */
PHP_FUNCTIONnull2516 PHP_FUNCTION(ldap_delete_ext)
2517 {
2518 	php_ldap_do_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2519 }
2520 /* }}} */
2521 
2522 /* {{{ _ldap_str_equal_to_const
2523  */
_ldap_str_equal_to_const(const char *str, size_t str_len, const char *cstr)2524 static size_t _ldap_str_equal_to_const(const char *str, size_t str_len, const char *cstr)
2525 {
2526 	size_t i;
2527 
2528 	if (strlen(cstr) != str_len)
2529 		return 0;
2530 
2531 	for (i = 0; i < str_len; ++i) {
2532 		if (str[i] != cstr[i]) {
2533 			return 0;
2534 		}
2535 	}
2536 
2537 	return 1;
2538 }
2539 /* }}} */
2540 
2541 /* {{{ _ldap_strlen_max
2542  */
_ldap_strlen_max(const char *str, size_t max_len)2543 static size_t _ldap_strlen_max(const char *str, size_t max_len)
2544 {
2545 	size_t i;
2546 
2547 	for (i = 0; i < max_len; ++i) {
2548 		if (str[i] == '\0') {
2549 			return i;
2550 		}
2551 	}
2552 
2553 	return max_len;
2554 }
2555 /* }}} */
2556 
2557 /* {{{ _ldap_hash_fetch
2558  */
_ldap_hash_fetch(zval *hashTbl, const char *key, zval **out)2559 static void _ldap_hash_fetch(zval *hashTbl, const char *key, zval **out)
2560 {
2561 	*out = zend_hash_str_find(Z_ARRVAL_P(hashTbl), key, strlen(key));
2562 }
2563 /* }}} */
2564 
2565 /* {{{ proto bool ldap_modify_batch(resource link, string dn, array modifs [, array servercontrols])
2566    Perform multiple modifications as part of one operation */
PHP_FUNCTIONnull2567 PHP_FUNCTION(ldap_modify_batch)
2568 {
2569 	zval *serverctrls = NULL;
2570 	ldap_linkdata *ld;
2571 	zval *link, *mods, *mod, *modinfo;
2572 	zend_string *modval;
2573 	zval *attrib, *modtype, *vals;
2574 	zval *fetched;
2575 	char *dn;
2576 	size_t dn_len;
2577 	int i, j, k;
2578 	int num_mods, num_modprops, num_modvals;
2579 	LDAPMod **ldap_mods;
2580 	LDAPControl **lserverctrls = NULL;
2581 	uint32_t oper;
2582 
2583 	/*
2584 	$mods = array(
2585 		array(
2586 			"attrib" => "unicodePwd",
2587 			"modtype" => LDAP_MODIFY_BATCH_REMOVE,
2588 			"values" => array($oldpw)
2589 		),
2590 		array(
2591 			"attrib" => "unicodePwd",
2592 			"modtype" => LDAP_MODIFY_BATCH_ADD,
2593 			"values" => array($newpw)
2594 		),
2595 		array(
2596 			"attrib" => "userPrincipalName",
2597 			"modtype" => LDAP_MODIFY_BATCH_REPLACE,
2598 			"values" => array("janitor@corp.contoso.com")
2599 		),
2600 		array(
2601 			"attrib" => "userCert",
2602 			"modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
2603 		)
2604 	);
2605 	*/
2606 
2607 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/|a", &link, &dn, &dn_len, &mods, &serverctrls) != SUCCESS) {
2608 		return;
2609 	}
2610 
2611 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2612 		RETURN_FALSE;
2613 	}
2614 
2615 	/* perform validation */
2616 	{
2617 		zend_string *modkey;
2618 		zend_long modtype;
2619 
2620 		/* to store the wrongly-typed keys */
2621 		zend_ulong tmpUlong;
2622 
2623 		/* make sure the DN contains no NUL bytes */
2624 		if (_ldap_strlen_max(dn, dn_len) != dn_len) {
2625 			php_error_docref(NULL, E_WARNING, "DN must not contain NUL bytes");
2626 			RETURN_FALSE;
2627 		}
2628 
2629 		/* make sure the top level is a normal array */
2630 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(mods));
2631 		if (zend_hash_get_current_key_type(Z_ARRVAL_P(mods)) != HASH_KEY_IS_LONG) {
2632 			php_error_docref(NULL, E_WARNING, "Modifications array must not be string-indexed");
2633 			RETURN_FALSE;
2634 		}
2635 
2636 		num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods));
2637 
2638 		for (i = 0; i < num_mods; i++) {
2639 			/* is the numbering consecutive? */
2640 			if ((fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i)) == NULL) {
2641 				php_error_docref(NULL, E_WARNING, "Modifications array must have consecutive indices 0, 1, ...");
2642 				RETURN_FALSE;
2643 			}
2644 			mod = fetched;
2645 
2646 			/* is it an array? */
2647 			if (Z_TYPE_P(mod) != IS_ARRAY) {
2648 				php_error_docref(NULL, E_WARNING, "Each entry of modifications array must be an array itself");
2649 				RETURN_FALSE;
2650 			}
2651 
2652 			SEPARATE_ARRAY(mod);
2653 			/* for the modification hashtable... */
2654 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(mod));
2655 			num_modprops = zend_hash_num_elements(Z_ARRVAL_P(mod));
2656 
2657 			for (j = 0; j < num_modprops; j++) {
2658 				/* are the keys strings? */
2659 				if (zend_hash_get_current_key(Z_ARRVAL_P(mod), &modkey, &tmpUlong) != HASH_KEY_IS_STRING) {
2660 					php_error_docref(NULL, E_WARNING, "Each entry of modifications array must be string-indexed");
2661 					RETURN_FALSE;
2662 				}
2663 
2664 				/* is this a valid entry? */
2665 				if (
2666 					!_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_ATTRIB) &&
2667 					!_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_MODTYPE) &&
2668 					!_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_VALUES)
2669 				) {
2670 					php_error_docref(NULL, E_WARNING, "The only allowed keys in entries of the modifications array are '" LDAP_MODIFY_BATCH_ATTRIB "', '" LDAP_MODIFY_BATCH_MODTYPE "' and '" LDAP_MODIFY_BATCH_VALUES "'");
2671 					RETURN_FALSE;
2672 				}
2673 
2674 				fetched = zend_hash_get_current_data(Z_ARRVAL_P(mod));
2675 				modinfo = fetched;
2676 
2677 				/* does the value type match the key? */
2678 				if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_ATTRIB)) {
2679 					if (Z_TYPE_P(modinfo) != IS_STRING) {
2680 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must be a string");
2681 						RETURN_FALSE;
2682 					}
2683 
2684 					if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
2685 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not contain NUL bytes");
2686 						RETURN_FALSE;
2687 					}
2688 				}
2689 				else if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_MODTYPE)) {
2690 					if (Z_TYPE_P(modinfo) != IS_LONG) {
2691 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_MODTYPE "' value must be a long");
2692 						RETURN_FALSE;
2693 					}
2694 
2695 					/* is the value in range? */
2696 					modtype = Z_LVAL_P(modinfo);
2697 					if (
2698 						modtype != LDAP_MODIFY_BATCH_ADD &&
2699 						modtype != LDAP_MODIFY_BATCH_REMOVE &&
2700 						modtype != LDAP_MODIFY_BATCH_REPLACE &&
2701 						modtype != LDAP_MODIFY_BATCH_REMOVE_ALL
2702 					) {
2703 						php_error_docref(NULL, E_WARNING, "The '" LDAP_MODIFY_BATCH_MODTYPE "' value must match one of the LDAP_MODIFY_BATCH_* constants");
2704 						RETURN_FALSE;
2705 					}
2706 
2707 					/* if it's REMOVE_ALL, there must not be a values array; otherwise, there must */
2708 					if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
2709 						if (zend_hash_str_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES))) {
2710 							php_error_docref(NULL, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must not be provided");
2711 							RETURN_FALSE;
2712 						}
2713 					}
2714 					else {
2715 						if (!zend_hash_str_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES))) {
2716 							php_error_docref(NULL, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is not LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must be provided");
2717 							RETURN_FALSE;
2718 						}
2719 					}
2720 				}
2721 				else if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_VALUES)) {
2722 					if (Z_TYPE_P(modinfo) != IS_ARRAY) {
2723 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' value must be an array");
2724 						RETURN_FALSE;
2725 					}
2726 
2727 					SEPARATE_ARRAY(modinfo);
2728 					/* is the array not empty? */
2729 					zend_hash_internal_pointer_reset(Z_ARRVAL_P(modinfo));
2730 					num_modvals = zend_hash_num_elements(Z_ARRVAL_P(modinfo));
2731 					if (num_modvals == 0) {
2732 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have at least one element");
2733 						RETURN_FALSE;
2734 					}
2735 
2736 					/* are its keys integers? */
2737 					if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) {
2738 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must not be string-indexed");
2739 						RETURN_FALSE;
2740 					}
2741 
2742 					/* are the keys consecutive? */
2743 					for (k = 0; k < num_modvals; k++) {
2744 						if ((fetched = zend_hash_index_find(Z_ARRVAL_P(modinfo), k)) == NULL) {
2745 							php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have consecutive indices 0, 1, ...");
2746 							RETURN_FALSE;
2747 						}
2748 					}
2749 				}
2750 
2751 				zend_hash_move_forward(Z_ARRVAL_P(mod));
2752 			}
2753 		}
2754 	}
2755 	/* validation was successful */
2756 
2757 	/* allocate array of modifications */
2758 	ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0);
2759 
2760 	/* for each modification */
2761 	for (i = 0; i < num_mods; i++) {
2762 		/* allocate the modification struct */
2763 		ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0);
2764 
2765 		/* fetch the relevant data */
2766 		fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i);
2767 		mod = fetched;
2768 
2769 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib);
2770 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_MODTYPE, &modtype);
2771 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_VALUES, &vals);
2772 
2773 		/* map the modification type */
2774 		switch (Z_LVAL_P(modtype)) {
2775 			case LDAP_MODIFY_BATCH_ADD:
2776 				oper = LDAP_MOD_ADD;
2777 				break;
2778 			case LDAP_MODIFY_BATCH_REMOVE:
2779 			case LDAP_MODIFY_BATCH_REMOVE_ALL:
2780 				oper = LDAP_MOD_DELETE;
2781 				break;
2782 			case LDAP_MODIFY_BATCH_REPLACE:
2783 				oper = LDAP_MOD_REPLACE;
2784 				break;
2785 			default:
2786 				zend_throw_error(NULL, "Unknown and uncaught modification type.");
2787 				RETVAL_FALSE;
2788 				efree(ldap_mods[i]);
2789 				num_mods = i;
2790 				goto cleanup;
2791 		}
2792 
2793 		/* fill in the basic info */
2794 		ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
2795 		ldap_mods[i]->mod_type = estrndup(Z_STRVAL_P(attrib), Z_STRLEN_P(attrib));
2796 
2797 		if (Z_LVAL_P(modtype) == LDAP_MODIFY_BATCH_REMOVE_ALL) {
2798 			/* no values */
2799 			ldap_mods[i]->mod_bvalues = NULL;
2800 		}
2801 		else {
2802 			/* allocate space for the values as part of this modification */
2803 			num_modvals = zend_hash_num_elements(Z_ARRVAL_P(vals));
2804 			ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0);
2805 
2806 			/* for each value */
2807 			for (j = 0; j < num_modvals; j++) {
2808 				/* fetch it */
2809 				fetched = zend_hash_index_find(Z_ARRVAL_P(vals), j);
2810 				modval = zval_get_string(fetched);
2811 				if (EG(exception)) {
2812 					RETVAL_FALSE;
2813 					ldap_mods[i]->mod_bvalues[j] = NULL;
2814 					num_mods = i + 1;
2815 					goto cleanup;
2816 				}
2817 
2818 				/* allocate the data struct */
2819 				ldap_mods[i]->mod_bvalues[j] = safe_emalloc(1, sizeof(struct berval), 0);
2820 
2821 				/* fill it */
2822 				ldap_mods[i]->mod_bvalues[j]->bv_len = ZSTR_LEN(modval);
2823 				ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(ZSTR_VAL(modval), ZSTR_LEN(modval));
2824 				zend_string_release(modval);
2825 			}
2826 
2827 			/* NULL-terminate values */
2828 			ldap_mods[i]->mod_bvalues[num_modvals] = NULL;
2829 		}
2830 	}
2831 
2832 	/* NULL-terminate modifications */
2833 	ldap_mods[num_mods] = NULL;
2834 
2835 	if (serverctrls) {
2836 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2837 		if (lserverctrls == NULL) {
2838 			RETVAL_FALSE;
2839 			goto cleanup;
2840 		}
2841 	}
2842 
2843 	/* perform (finally) */
2844 	if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL)) != LDAP_SUCCESS) {
2845 		php_error_docref(NULL, E_WARNING, "Batch Modify: %s", ldap_err2string(i));
2846 		RETVAL_FALSE;
2847 	} else RETVAL_TRUE;
2848 
2849 	/* clean up */
2850 	cleanup: {
2851 		for (i = 0; i < num_mods; i++) {
2852 			/* attribute */
2853 			efree(ldap_mods[i]->mod_type);
2854 
2855 			if (ldap_mods[i]->mod_bvalues != NULL) {
2856 				/* each BER value */
2857 				for (j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) {
2858 					/* free the data bytes */
2859 					efree(ldap_mods[i]->mod_bvalues[j]->bv_val);
2860 
2861 					/* free the bvalue struct */
2862 					efree(ldap_mods[i]->mod_bvalues[j]);
2863 				}
2864 
2865 				/* the BER value array */
2866 				efree(ldap_mods[i]->mod_bvalues);
2867 			}
2868 
2869 			/* the modification */
2870 			efree(ldap_mods[i]);
2871 		}
2872 
2873 		/* the modifications array */
2874 		efree(ldap_mods);
2875 
2876 		if (lserverctrls) {
2877 			_php_ldap_controls_free(&lserverctrls);
2878 		}
2879 	}
2880 }
2881 /* }}} */
2882 
2883 /* {{{ proto int ldap_errno(resource link)
2884    Get the current ldap error number */
PHP_FUNCTIONnull2885 PHP_FUNCTION(ldap_errno)
2886 {
2887 	zval *link;
2888 	ldap_linkdata *ld;
2889 
2890 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
2891 		return;
2892 	}
2893 
2894 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2895 		RETURN_FALSE;
2896 	}
2897 
2898 	RETURN_LONG(_get_lderrno(ld->link));
2899 }
2900 /* }}} */
2901 
2902 /* {{{ proto string ldap_err2str(int errno)
2903    Convert error number to error string */
PHP_FUNCTIONnull2904 PHP_FUNCTION(ldap_err2str)
2905 {
2906 	zend_long perrno;
2907 
2908 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &perrno) != SUCCESS) {
2909 		return;
2910 	}
2911 
2912 	RETURN_STRING(ldap_err2string(perrno));
2913 }
2914 /* }}} */
2915 
2916 /* {{{ proto string ldap_error(resource link)
2917    Get the current ldap error string */
PHP_FUNCTIONnull2918 PHP_FUNCTION(ldap_error)
2919 {
2920 	zval *link;
2921 	ldap_linkdata *ld;
2922 	int ld_errno;
2923 
2924 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
2925 		return;
2926 	}
2927 
2928 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2929 		RETURN_FALSE;
2930 	}
2931 
2932 	ld_errno = _get_lderrno(ld->link);
2933 
2934 	RETURN_STRING(ldap_err2string(ld_errno));
2935 }
2936 /* }}} */
2937 
2938 /* {{{ proto bool ldap_compare(resource link, string dn, string attr, string value)
2939    Determine if an entry has a specific value for one of its attributes */
PHP_FUNCTIONnull2940 PHP_FUNCTION(ldap_compare)
2941 {
2942 	zval *serverctrls = NULL;
2943 	zval *link;
2944 	char *dn, *attr, *value;
2945 	size_t dn_len, attr_len, value_len;
2946 	ldap_linkdata *ld;
2947 	LDAPControl **lserverctrls = NULL;
2948 	int errno;
2949 	struct berval lvalue;
2950 
2951 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsss|a", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len, &serverctrls) != SUCCESS) {
2952 		return;
2953 	}
2954 
2955 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2956 		RETURN_FALSE;
2957 	}
2958 
2959 	if (serverctrls) {
2960 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2961 		if (lserverctrls == NULL) {
2962 			RETVAL_FALSE;
2963 			goto cleanup;
2964 		}
2965 	}
2966 
2967 	lvalue.bv_val = value;
2968 	lvalue.bv_len = value_len;
2969 
2970 	errno = ldap_compare_ext_s(ld->link, dn, attr, &lvalue, lserverctrls, NULL);
2971 
2972 	switch (errno) {
2973 		case LDAP_COMPARE_TRUE:
2974 			RETVAL_TRUE;
2975 			break;
2976 
2977 		case LDAP_COMPARE_FALSE:
2978 			RETVAL_FALSE;
2979 			break;
2980 
2981 		default:
2982 			php_error_docref(NULL, E_WARNING, "Compare: %s", ldap_err2string(errno));
2983 			RETVAL_LONG(-1);
2984 	}
2985 
2986 cleanup:
2987 	if (lserverctrls) {
2988 		_php_ldap_controls_free(&lserverctrls);
2989 	}
2990 
2991 	return;
2992 }
2993 /* }}} */
2994 
2995 /* {{{ proto bool ldap_sort(resource link, resource result, string sortfilter)
2996    Sort LDAP result entries */
PHP_FUNCTIONnull2997 PHP_FUNCTION(ldap_sort)
2998 {
2999 	zval *link, *result;
3000 	ldap_linkdata *ld;
3001 	char *sortfilter;
3002 	size_t sflen;
3003 	zend_resource *le;
3004 
3005 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrs", &link, &result, &sortfilter, &sflen) != SUCCESS) {
3006 		RETURN_FALSE;
3007 	}
3008 
3009 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3010 		RETURN_FALSE;
3011 	}
3012 
3013 	le = Z_RES_P(result);
3014 	if (le->type != le_result) {
3015 		php_error_docref(NULL, E_WARNING, "Supplied resource is not a valid ldap result resource");
3016 		RETURN_FALSE;
3017 	}
3018 
3019 	if (ldap_sort_entries(ld->link, (LDAPMessage **) &le->ptr, sflen ? sortfilter : NULL, strcmp) != LDAP_SUCCESS) {
3020 		php_error_docref(NULL, E_WARNING, "%s", ldap_err2string(errno));
3021 		RETURN_FALSE;
3022 	}
3023 
3024 	RETURN_TRUE;
3025 }
3026 /* }}} */
3027 
3028 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
3029 /* {{{ proto bool ldap_get_option(resource link, int option, mixed retval)
3030    Get the current value of various session-wide parameters */
PHP_FUNCTIONnull3031 PHP_FUNCTION(ldap_get_option)
3032 {
3033 	zval *link, *retval;
3034 	ldap_linkdata *ld;
3035 	zend_long option;
3036 
3037 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &link, &option, &retval) != SUCCESS) {
3038 		return;
3039 	}
3040 
3041 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3042 		RETURN_FALSE;
3043 	}
3044 
3045 	switch (option) {
3046 	/* options with int value */
3047 	case LDAP_OPT_DEREF:
3048 	case LDAP_OPT_SIZELIMIT:
3049 	case LDAP_OPT_TIMELIMIT:
3050 	case LDAP_OPT_PROTOCOL_VERSION:
3051 	case LDAP_OPT_ERROR_NUMBER:
3052 	case LDAP_OPT_REFERRALS:
3053 #ifdef LDAP_OPT_RESTART
3054 	case LDAP_OPT_RESTART:
3055 #endif
3056 #ifdef LDAP_OPT_X_SASL_NOCANON
3057 	case LDAP_OPT_X_SASL_NOCANON:
3058 #endif
3059 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
3060 	case LDAP_OPT_X_TLS_REQUIRE_CERT:
3061 #endif
3062 #ifdef LDAP_OPT_X_TLS_CRLCHECK
3063 	case LDAP_OPT_X_TLS_CRLCHECK:
3064 #endif
3065 #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
3066 	case LDAP_OPT_X_TLS_PROTOCOL_MIN:
3067 #endif
3068 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
3069 	case LDAP_OPT_X_KEEPALIVE_IDLE:
3070 	case LDAP_OPT_X_KEEPALIVE_PROBES:
3071 	case LDAP_OPT_X_KEEPALIVE_INTERVAL:
3072 #endif
3073 		{
3074 			int val;
3075 
3076 			if (ldap_get_option(ld->link, option, &val)) {
3077 				RETURN_FALSE;
3078 			}
3079 			ZEND_TRY_ASSIGN_REF_LONG(retval, val);
3080 		} break;
3081 #ifdef LDAP_OPT_NETWORK_TIMEOUT
3082 	case LDAP_OPT_NETWORK_TIMEOUT:
3083 		{
3084 			struct timeval *timeout = NULL;
3085 
3086 			if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
3087 				if (timeout) {
3088 					ldap_memfree(timeout);
3089 				}
3090 				RETURN_FALSE;
3091 			}
3092 			if (!timeout) {
3093 				RETURN_FALSE;
3094 			}
3095 			ZEND_TRY_ASSIGN_REF_LONG(retval, timeout->tv_sec);
3096 			ldap_memfree(timeout);
3097 		} break;
3098 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
3099 	case LDAP_X_OPT_CONNECT_TIMEOUT:
3100 		{
3101 			int timeout;
3102 
3103 			if (ldap_get_option(ld->link, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
3104 				RETURN_FALSE;
3105 			}
3106 			ZEND_TRY_ASSIGN_REF_LONG(retval, (timeout / 1000));
3107 		} break;
3108 #endif
3109 #ifdef LDAP_OPT_TIMEOUT
3110 	case LDAP_OPT_TIMEOUT:
3111 		{
3112 			struct timeval *timeout = NULL;
3113 
3114 			if (ldap_get_option(ld->link, LDAP_OPT_TIMEOUT, (void *) &timeout)) {
3115 				if (timeout) {
3116 					ldap_memfree(timeout);
3117 				}
3118 				RETURN_FALSE;
3119 			}
3120 			if (!timeout) {
3121 				RETURN_FALSE;
3122 			}
3123 			ZEND_TRY_ASSIGN_REF_LONG(retval, timeout->tv_sec);
3124 			ldap_memfree(timeout);
3125 		} break;
3126 #endif
3127 	/* options with string value */
3128 	case LDAP_OPT_ERROR_STRING:
3129 #ifdef LDAP_OPT_HOST_NAME
3130 	case LDAP_OPT_HOST_NAME:
3131 #endif
3132 #ifdef HAVE_LDAP_SASL
3133 	case LDAP_OPT_X_SASL_MECH:
3134 	case LDAP_OPT_X_SASL_REALM:
3135 	case LDAP_OPT_X_SASL_AUTHCID:
3136 	case LDAP_OPT_X_SASL_AUTHZID:
3137 #endif
3138 #ifdef LDAP_OPT_X_SASL_USERNAME
3139 	case LDAP_OPT_X_SASL_USERNAME:
3140 #endif
3141 #if (LDAP_API_VERSION > 2000)
3142 	case LDAP_OPT_X_TLS_CACERTDIR:
3143 	case LDAP_OPT_X_TLS_CACERTFILE:
3144 	case LDAP_OPT_X_TLS_CERTFILE:
3145 	case LDAP_OPT_X_TLS_CIPHER_SUITE:
3146 	case LDAP_OPT_X_TLS_KEYFILE:
3147 	case LDAP_OPT_X_TLS_RANDOM_FILE:
3148 #endif
3149 #ifdef LDAP_OPT_X_TLS_PACKAGE
3150 	case LDAP_OPT_X_TLS_PACKAGE:
3151 #endif
3152 #ifdef LDAP_OPT_X_TLS_CRLFILE
3153 	case LDAP_OPT_X_TLS_CRLFILE:
3154 #endif
3155 #ifdef LDAP_OPT_X_TLS_DHFILE
3156 	case LDAP_OPT_X_TLS_DHFILE:
3157 #endif
3158 #ifdef LDAP_OPT_MATCHED_DN
3159 	case LDAP_OPT_MATCHED_DN:
3160 #endif
3161 		{
3162 			char *val = NULL;
3163 
3164 			if (ldap_get_option(ld->link, option, &val) || val == NULL || *val == '\0') {
3165 				if (val) {
3166 					ldap_memfree(val);
3167 				}
3168 				RETURN_FALSE;
3169 			}
3170 			ZEND_TRY_ASSIGN_REF_STRING(retval, val);
3171 			ldap_memfree(val);
3172 		} break;
3173 	case LDAP_OPT_SERVER_CONTROLS:
3174 	case LDAP_OPT_CLIENT_CONTROLS:
3175 		{
3176 			LDAPControl **ctrls = NULL;
3177 
3178 			if (ldap_get_option(ld->link, option, &ctrls) || ctrls == NULL) {
3179 				if (ctrls) {
3180 					ldap_memfree(ctrls);
3181 				}
3182 				RETURN_FALSE;
3183 			}
3184 			_php_ldap_controls_to_array(ld->link, ctrls, retval, 1);
3185 		} break;
3186 /* options not implemented
3187 	case LDAP_OPT_API_INFO:
3188 	case LDAP_OPT_API_FEATURE_INFO:
3189 */
3190 	default:
3191 		RETURN_FALSE;
3192 	}
3193 	RETURN_TRUE;
3194 }
3195 /* }}} */
3196 
3197 /* {{{ proto bool ldap_set_option(resource link, int option, mixed newval)
3198    Set the value of various session-wide parameters */
PHP_FUNCTIONnull3199 PHP_FUNCTION(ldap_set_option)
3200 {
3201 	zval *link, *newval;
<