ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
LDAP.php
Go to the documentation of this file.
1 <?php
2 
6 define('ERR_INTERNAL', 1);
7 define('ERR_NO_USER', 2);
8 define('ERR_WRONG_PW', 3);
9 define('ERR_AS_DATA_INCONSIST', 4);
10 define('ERR_AS_INTERNAL', 5);
11 define('ERR_AS_ATTRIBUTE', 6);
12 
13 // not defined in earlier PHP versions
14 if (!defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) {
15  define('LDAP_OPT_DIAGNOSTIC_MESSAGE', 0x0032);
16 }
17 
26 {
32  protected $ldap = null;
33 
37  protected $authz_id = null;
38 
44  protected $timeout = 0;
45 
56  public function __construct($hostname, $enable_tls = true, $debug = false, $timeout = 0, $port = 389, $referrals = true)
57  {
58  // Debug
59  SimpleSAML\Logger::debug('Library - LDAP __construct(): Setup LDAP with '.
60  'host=\''.$hostname.
61  '\', tls='.var_export($enable_tls, true).
62  ', debug='.var_export($debug, true).
63  ', timeout='.var_export($timeout, true).
64  ', referrals='.var_export($referrals, true));
65 
66  /*
67  * Set debug level before calling connect. Note that this passes
68  * NULL to ldap_set_option, which is an undocumented feature.
69  *
70  * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
71  */
72  if ($debug && !ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7)) {
73  SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7');
74  }
75 
76  /*
77  * Prepare a connection for to this LDAP server. Note that this function
78  * doesn't actually connect to the server.
79  */
80  $this->ldap = @ldap_connect($hostname, $port);
81  if ($this->ldap === false) {
82  throw $this->makeException('Library - LDAP __construct(): Unable to connect to \''.$hostname.'\'', ERR_INTERNAL);
83  }
84 
85  // Enable LDAP protocol version 3
86  if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3)) {
87  throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', ERR_INTERNAL);
88  }
89 
90  // Set referral option
91  if (!@ldap_set_option($this->ldap, LDAP_OPT_REFERRALS, $referrals)) {
92  throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to '.$referrals, ERR_INTERNAL);
93  }
94 
95  // Set timeouts, if supported
96  // (OpenLDAP 2.x.x or Netscape Directory SDK x.x needed)
97  $this->timeout = $timeout;
98  if ($timeout > 0) {
99  if (!@ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT, $timeout)) {
100  SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to '.$timeout);
101  }
102  if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout)) {
103  SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to '.$timeout);
104  }
105  }
106 
107  // Enable TLS, if needed
108  if (stripos($hostname, "ldaps:") === false && $enable_tls) {
109  if (!@ldap_start_tls($this->ldap)) {
110  throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL);
111  }
112  }
113  }
114 
115 
124  private function makeException($description, $type = null)
125  {
126  $errNo = 0x00;
127 
128  // Log LDAP code and description, if possible
129  if (empty($this->ldap)) {
131  } else {
132  $errNo = @ldap_errno($this->ldap);
133  }
134 
135  // Decide exception type and return
136  if ($type) {
137  if ($errNo !== 0) {
138  // Only log real LDAP errors; not success
139  SimpleSAML\Logger::error($description.'; cause: \''.ldap_error($this->ldap).'\' (0x'.dechex($errNo).')');
140  } else {
141  SimpleSAML\Logger::error($description);
142  }
143 
144  switch ($type) {
145  case ERR_INTERNAL:// 1 - ExInternal
146  return new SimpleSAML_Error_Exception($description, $errNo);
147  case ERR_NO_USER:// 2 - ExUserNotFound
148  return new SimpleSAML_Error_UserNotFound($description, $errNo);
149  case ERR_WRONG_PW:// 3 - ExInvalidCredential
150  return new SimpleSAML_Error_InvalidCredential($description, $errNo);
151  case ERR_AS_DATA_INCONSIST:// 4 - ExAsDataInconsist
152  return new SimpleSAML_Error_AuthSource('ldap', $description);
153  case ERR_AS_INTERNAL:// 5 - ExAsInternal
154  return new SimpleSAML_Error_AuthSource('ldap', $description);
155  }
156  } else {
157  if ($errNo !== 0) {
158  $description .= '; cause: \''.ldap_error($this->ldap).'\' (0x'.dechex($errNo).')';
159  if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) && !empty($extendedError)) {
160  $description .= '; additional: \''.$extendedError.'\'';
161  }
162  }
163  switch ($errNo) {
164  case 0x20://LDAP_NO_SUCH_OBJECT
166  return new SimpleSAML_Error_UserNotFound($description, $errNo);
167  case 0x31://LDAP_INVALID_CREDENTIALS
170  case -1://NO_SERVER_CONNECTION
172  return new SimpleSAML_Error_AuthSource('ldap', $description);
173  default:
175  return new SimpleSAML_Error_AuthSource('ldap', $description);
176  }
177  }
178  }
179 
180 
208  private function search($base, $attribute, $value, $searchFilter = null, $scope = "subtree")
209  {
210  // Create the search filter
211  $attribute = self::escape_filter_value($attribute, false);
212  $value = self::escape_filter_value($value, true);
213  $filter = '';
214  foreach ($attribute as $attr) {
215  $filter .= '('.$attr.'='.$value.')';
216  }
217  $filter = '(|'.$filter.')';
218 
219  // Append LDAP filters if defined
220  if ($searchFilter != null) {
221  $filter = "(&".$filter."".$searchFilter.")";
222  }
223 
224  // Search using generated filter
225  SimpleSAML\Logger::debug('Library - LDAP search(): Searching base ('.$scope.') \''.$base.'\' for \''.$filter.'\'');
226  if ($scope === 'base') {
227  $result = @ldap_read($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER);
228  } else if ($scope === 'onelevel') {
229  $result = @ldap_list($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER);
230  } else {
231  $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER);
232  }
233 
234  if ($result === false) {
235  throw $this->makeException('Library - LDAP search(): Failed search on base \''.$base.'\' for \''.$filter.'\'');
236  }
237 
238  // Sanity checks on search results
239  $count = @ldap_count_entries($this->ldap, $result);
240  if ($count === false) {
241  throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned');
242  } elseif ($count > 1) {
243  // More than one entry is found. External error
244  throw $this->makeException('Library - LDAP search(): Found '.$count.' entries searching base \''.$base.'\' for \''.$filter.'\'', ERR_AS_DATA_INCONSIST);
245  } elseif ($count === 0) {
246  // No entry is fond => wrong username is given (or not registered in the catalogue). User error
247  throw $this->makeException('Library - LDAP search(): Found no entries searching base \''.$base.'\' for \''.$filter.'\'', ERR_NO_USER);
248  }
249 
250 
251  // Resolve the DN from the search result
252  $entry = @ldap_first_entry($this->ldap, $result);
253  if ($entry === false) {
254  throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \''.$base.'\' for \''.$filter.'\'');
255  }
256  $dn = @ldap_get_dn($this->ldap, $entry);
257  if ($dn === false) {
258  throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \''.$base.'\' for \''.$filter.'\'');
259  }
260  return $dn;
261  }
262 
263 
291  public function searchfordn($base, $attribute, $value, $allowZeroHits = false, $searchFilter = null, $scope = 'subtree')
292  {
293  // Traverse all search bases, returning DN if found
295  foreach ($bases as $current) {
296  try {
297  // Single base search
298  $result = $this->search($current, $attribute, $value, $searchFilter, $scope);
299 
300  // We don't hawe to look any futher if user is found
301  if (!empty($result)) {
302  return $result;
303  }
304  // If search failed, attempt the other base DNs
305  } catch (SimpleSAML_Error_UserNotFound $e) {
306  // Just continue searching
307  }
308  }
309  // Decide what to do for zero entries
310  SimpleSAML\Logger::debug('Library - LDAP searchfordn(): No entries found');
311  if ($allowZeroHits) {
312  // Zero hits allowed
313  return null;
314  } else {
315  // Zero hits not allowed
316  throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'('.
317  join(' | ', $attribute).' = '.$value.')\' on base(s) \'('.join(' & ', $bases).')\'', 2);
318  }
319  }
320 
321 
335  public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true, $scope = 'subtree')
336  {
337  // Escape the filter values, if requested
338  if ($escape) {
339  $filters = $this->escape_filter_value($filters, false);
340  }
341 
342  // Build search filter
343  $filter = '';
344  if (is_array($filters)) {
345  foreach ($filters as $attribute => $value) {
346  $filter .= "($attribute=$value)";
347  }
348  if (count($filters) > 1) {
349  $filter = ($and ? '(&' : '(|').$filter.')';
350  }
351  } elseif (is_string($filters)) {
352  $filter = $filters;
353  }
354 
355  // Verify filter was created
356  if ($filter == '' || $filter == '(=)') {
357  throw $this->makeException('ldap:LdapConnection->search_manual : No search filters defined', ERR_INTERNAL);
358  }
359 
360  // Verify at least one base was passed
361  $bases = (array) $bases;
362  if (empty($bases)) {
363  throw $this->makeException('ldap:LdapConnection->search_manual : No base DNs were passed', ERR_INTERNAL);
364  }
365 
366  // Search each base until result is found
367  $result = false;
368  foreach ($bases as $base) {
369  if ($scope === 'base') {
370  $result = @ldap_read($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout);
371  } else if ($scope === 'onelevel') {
372  $result = @ldap_list($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout);
373  } else {
374  $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout);
375  }
376 
377  if ($result !== false && @ldap_count_entries($this->ldap, $result) > 0) {
378  break;
379  }
380  }
381 
382  // Verify that a result was found in one of the bases
383  if ($result === false) {
384  throw $this->makeException(
385  'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) ['.
386  implode('; ', $bases).'] with filter ['.$filter.']. LDAP error ['.
387  ldap_error($this->ldap).']'
388  );
389  } elseif (@ldap_count_entries($this->ldap, $result) < 1) {
390  throw $this->makeException(
391  'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) ['.
392  implode('; ', $bases).'] with filter ['.$filter.']',
394  );
395  }
396 
397  // Get all results
398  $results = ldap_get_entries($this->ldap, $result);
399  if ($results === false) {
400  throw $this->makeException(
401  'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results'
402  );
403  }
404 
405  // parse each entry and process its attributes
406  for ($i = 0; $i < $results['count']; $i++) {
407  $entry = $results[$i];
408 
409  // iterate over the attributes of the entry
410  for ($j = 0; $j < $entry['count']; $j++) {
411  $name = $entry[$j];
412  $attribute = $entry[$name];
413 
414  // decide whether to base64 encode or not
415  for ($k = 0; $k < $attribute['count']; $k++) {
416  // base64 encode binary attributes
417  if (strtolower($name) === 'jpegphoto' || strtolower($name) === 'objectguid') {
418  $results[$i][$name][$k] = base64_encode($attribute[$k]);
419  }
420  }
421  }
422  }
423 
424  // Remove the count and return
425  unset($results['count']);
426  return $results;
427  }
428 
429 
446  public function bind($dn, $password, array $sasl_args = null)
447  {
448  if ($sasl_args != null) {
449  if (!function_exists('ldap_sasl_bind')) {
450  $ex_msg = 'Library - missing SASL support';
451  throw $this->makeException($ex_msg);
452  }
453 
454  // SASL Bind, with error handling
455  $authz_id = $sasl_args['authz_id'];
456  $error = @ldap_sasl_bind(
457  $this->ldap,
458  $dn,
459  $password,
460  $sasl_args['mech'],
461  $sasl_args['realm'],
462  $sasl_args['authc_id'],
463  $sasl_args['authz_id'],
464  $sasl_args['props']
465  );
466  } else {
467  // Simple Bind, with error handling
468  $authz_id = $dn;
469  $error = @ldap_bind($this->ldap, $dn, $password);
470  }
471 
472  if ($error === true) {
473  // Good
474  $this->authz_id = $authz_id;
475  SimpleSAML\Logger::debug('Library - LDAP bind(): Bind successful with DN \''.$dn.'\'');
476  return true;
477  }
478 
479  /* Handle errors
480  * LDAP_INVALID_CREDENTIALS
481  * LDAP_INSUFFICIENT_ACCESS */
482  switch (ldap_errno($this->ldap)) {
483  case 32: // LDAP_NO_SUCH_OBJECT
484  // no break
485  case 47: // LDAP_X_PROXY_AUTHZ_FAILURE
486  // no break
487  case 48: // LDAP_INAPPROPRIATE_AUTH
488  // no break
489  case 49: // LDAP_INVALID_CREDENTIALS
490  // no break
491  case 50: // LDAP_INSUFFICIENT_ACCESS
492  return false;
493  default:
494  break;
495  }
496 
497  // Bad
498  throw $this->makeException('Library - LDAP bind(): Bind failed with DN \''.$dn.'\'');
499  }
500 
501 
510  public function setOption($option, $value)
511  {
512  // Attempt to set the LDAP option
513  if (!@ldap_set_option($this->ldap, $option, $value)) {
514  throw $this->makeException(
515  'ldap:LdapConnection->setOption : Failed to set LDAP option ['.
516  $option.'] with the value ['.$value.'] error: '.ldap_error($this->ldap),
518  );
519  }
520 
521  // Log debug message
523  'ldap:LdapConnection->setOption : Set the LDAP option ['.
524  $option.'] with the value ['.$value.']'
525  );
526  }
527 
528 
545  public function getAttributes($dn, $attributes = null, $maxsize = null)
546  {
547  // Preparations, including a pretty debug message...
548  $description = 'all attributes';
549  if (is_array($attributes)) {
550  $description = '\''.join(',', $attributes).'\'';
551  } else {
552  // Get all attributes...
553  // TODO: Verify that this originally was the intended behaviour. Could $attributes be a string?
554  $attributes = array();
555  }
556  SimpleSAML\Logger::debug('Library - LDAP getAttributes(): Getting '.$description.' from DN \''.$dn.'\'');
557 
558  // Attempt to get attributes
559  // TODO: Should aliases be dereferenced?
560  $result = @ldap_read($this->ldap, $dn, 'objectClass=*', $attributes, 0, 0, $this->timeout);
561  if ($result === false) {
562  throw $this->makeException('Library - LDAP getAttributes(): Failed to get attributes from DN \''.$dn.'\'');
563  }
564  $entry = @ldap_first_entry($this->ldap, $result);
565  if ($entry === false) {
566  throw $this->makeException('Library - LDAP getAttributes(): Could not get first entry from DN \''.$dn.'\'');
567  }
568  $attributes = @ldap_get_attributes($this->ldap, $entry); // Recycling $attributes... Possibly bad practice.
569  if ($attributes === false) {
570  throw $this->makeException('Library - LDAP getAttributes(): Could not get attributes of first entry from DN \''.$dn.'\'');
571  }
572 
573  // Parsing each found attribute into our result set
574  $result = array(); // Recycling $result... Possibly bad practice.
575  for ($i = 0; $i < $attributes['count']; $i++) {
576  // Ignore attributes that exceed the maximum allowed size
577  $name = $attributes[$i];
578  $attribute = $attributes[$name];
579 
580  // Deciding whether to base64 encode
581  $values = array();
582  for ($j = 0; $j < $attribute['count']; $j++) {
583  $value = $attribute[$j];
584 
585  if (!empty($maxsize) && strlen($value) > $maxsize) {
586  // Ignoring and warning
587  SimpleSAML\Logger::warning('Library - LDAP getAttributes(): Attribute \''.
588  $name.'\' exceeded maximum allowed size by '.(strlen($value) - $maxsize));
589  continue;
590  }
591 
592  // Base64 encode binary attributes
593  if (strtolower($name) === 'jpegphoto' || strtolower($name) === 'objectguid' || strtolower($name) === 'ms-ds-consistencyguid') {
594  $values[] = base64_encode($value);
595  } else {
596  $values[] = $value;
597  }
598  }
599 
600  // Adding
601  $result[$name] = $values;
602  }
603 
604  // We're done
605  SimpleSAML\Logger::debug('Library - LDAP getAttributes(): Found attributes \'('.join(',', array_keys($result)).')\'');
606  return $result;
607  }
608 
609 
618  public function validate($config, $username, $password = null)
619  {
620  /* Escape any characters with a special meaning in LDAP. The following
621  * characters have a special meaning (according to RFC 2253):
622  * ',', '+', '"', '\', '<', '>', ';', '*'
623  * These characters are escaped by prefixing them with '\'.
624  */
625  $username = addcslashes($username, ',+"\\<>;*');
626 
627  if (isset($config['priv_user_dn'])) {
628  $this->bind($config['priv_user_dn'], $config['priv_user_pw']);
629  }
630  if (isset($config['dnpattern'])) {
631  $dn = str_replace('%username%', $username, $config['dnpattern']);
632  } else {
633  $dn = $this->searchfordn($config['searchbase'], $config['searchattributes'], $username);
634  }
635 
636  if ($password !== null) { // checking users credentials ... assuming below that she may read her own attributes ...
637  // escape characters with a special meaning, also in the password
638  $password = addcslashes($password, ',+"\\<>;*');
639  if (!$this->bind($dn, $password)) {
640  SimpleSAML\Logger::info('Library - LDAP validate(): Failed to authenticate \''.$username.'\' using DN \''.$dn.'\'');
641  return false;
642  }
643  }
644 
645  /*
646  * Retrieve attributes from LDAP
647  */
648  $attributes = $this->getAttributes($dn, $config['attributes']);
649  return $attributes;
650  }
651 
652 
666  public static function escape_filter_value($values = array(), $singleValue = true)
667  {
668  // Parameter validation
670 
671  foreach ($values as $key => $val) {
672  // Escaping of filter meta characters
673  $val = str_replace('\\', '\5c', $val);
674  $val = str_replace('*', '\2a', $val);
675  $val = str_replace('(', '\28', $val);
676  $val = str_replace(')', '\29', $val);
677 
678  // ASCII < 32 escaping
679  $val = self::asc2hex32($val);
680 
681  if (null === $val) {
682  $val = '\0'; // apply escaped "null" if string is empty
683  }
684 
685  $values[$key] = $val;
686  }
687  if ($singleValue) {
688  return $values[0];
689  }
690  return $values;
691  }
692 
693 
704  public static function asc2hex32($string)
705  {
706  for ($i = 0; $i < strlen($string); $i++) {
707  $char = substr($string, $i, 1);
708  if (ord($char) < 32) {
709  $hex = dechex(ord($char));
710  if (strlen($hex) == 1) {
711  $hex = '0'.$hex;
712  }
713  $string = str_replace($char, '\\'.$hex, $string);
714  }
715  }
716  return $string;
717  }
718 
722  private function authzid_to_dn($searchBase, $searchAttributes, $authz_id)
723  {
724  if (preg_match("/^dn:/", $authz_id)) {
725  return preg_replace("/^dn:/", "", $authz_id);
726  }
727 
728  if (preg_match("/^u:/", $authz_id)) {
729  return $this->searchfordn(
730  $searchBase,
731  $searchAttributes,
732  preg_replace("/^u:/", "", $authz_id)
733  );
734  }
735  return $authz_id;
736  }
737 
750  public function whoami($searchBase, $searchAttributes)
751  {
752  $authz_id = '';
753  if (function_exists('ldap_exop_whoami')) {
754  if (version_compare(phpversion(), '7', '<')) {
755  if (ldap_exop_whoami($this->ldap, $authz_id) !== true) {
756  throw $this->makeException('LDAP whoami exop failure');
757  }
758  } else {
759  if (($authz_id = ldap_exop_whoami($this->ldap)) === false) {
760  throw $this->makeException('LDAP whoami exop failure');
761  }
762  }
763  } else {
765  }
766 
767  $dn = $this->authzid_to_dn($searchBase, $searchAttributes, $authz_id);
768 
769  if (!isset($dn) || ($dn == '')) {
770  throw $this->makeException('Cannot figure userID');
771  }
772 
773  return $dn;
774  }
775 }
static asc2hex32($string)
Borrowed function from PEAR:LDAP.
Definition: LDAP.php:704
$config
Definition: bootstrap.php:15
$result
$type
static arrayize($data, $index=0)
Put a non-array variable into an array.
Definition: Arrays.php:24
const ERR_NO_USER
Definition: LDAP.php:7
authzid_to_dn($searchBase, $searchAttributes, $authz_id)
Convert SASL authz_id into a DN.
Definition: LDAP.php:722
static debug($string)
Definition: Logger.php:211
setOption($option, $value)
Applies an LDAP option to the current connection.
Definition: LDAP.php:510
static escape_filter_value($values=array(), $singleValue=true)
Borrowed function from PEAR:LDAP.
Definition: LDAP.php:666
const ERR_AS_DATA_INCONSIST
Definition: LDAP.php:9
$base
Definition: index.php:4
Attribute-related utility methods.
static info($string)
Definition: Logger.php:199
searchfordn($base, $attribute, $value, $allowZeroHits=false, $searchFilter=null, $scope='subtree')
Search for a DN.
Definition: LDAP.php:291
searchformultiple($bases, $filters, $attributes=array(), $and=true, $escape=true, $scope='subtree')
This method was created specifically for the ldap:AttributeAddUsersGroups->searchActiveDirectory() me...
Definition: LDAP.php:335
validate($config, $username, $password=null)
Enter description here...
Definition: LDAP.php:618
$authz_id
LDAP user: authz_id if SASL is in use, binding dn otherwise.
Definition: LDAP.php:37
static warning($string)
Definition: Logger.php:177
$values
makeException($description, $type=null)
Convenience method to create an LDAPException as well as log the description.
Definition: LDAP.php:124
static error($string)
Definition: Logger.php:166
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
getAttributes($dn, $attributes=null, $maxsize=null)
Search a given DN for attributes, and return the resulting associative array.
Definition: LDAP.php:545
__construct($hostname, $enable_tls=true, $debug=false, $timeout=0, $port=389, $referrals=true)
Private constructor restricts instantiation to getInstance().
Definition: LDAP.php:56
font size
Definition: langcheck.php:162
$password
Definition: cron.php:14
$results
Definition: svg-scanner.php:47
$i
Definition: disco.tpl.php:19
whoami($searchBase, $searchAttributes)
ldap_exop_whoami accessor, if available.
Definition: LDAP.php:750
bind($dn, $password, array $sasl_args=null)
Bind to LDAP with a specific DN and password.
Definition: LDAP.php:446
const ERR_INTERNAL
Constants defining possible errors.
Definition: LDAP.php:6
$key
Definition: croninfo.php:18
search($base, $attribute, $value, $searchFilter=null, $scope="subtree")
Search for DN from a single base.
Definition: LDAP.php:208