6define(
'ERR_INTERNAL', 1);
7define(
'ERR_NO_USER', 2);
8define(
'ERR_WRONG_PW', 3);
9define(
'ERR_AS_DATA_INCONSIST', 4);
10define(
'ERR_AS_INTERNAL', 5);
11define(
'ERR_AS_ATTRIBUTE', 6);
14if (!
defined(
'LDAP_OPT_DIAGNOSTIC_MESSAGE')) {
15 define(
'LDAP_OPT_DIAGNOSTIC_MESSAGE', 0x0032);
62 'host=\'' . $hostname .
63 '\', tls=
' . var_export($enable_tls, true) .
64 ', debug=
' . var_export($debug, true) .
65 ', timeout=
' . var_export($timeout, true) .
66 ', referrals=
' . var_export($referrals, true));
69 * Set debug level before calling connect. Note that this passes
70 * NULL to ldap_set_option, which is an undocumented feature.
72 * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
74 if ($debug && !ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7)) {
75 SimpleSAML\Logger::warning('Library - LDAP
__construct(): Unable to
set debug level (LDAP_OPT_DEBUG_LEVEL) to 7
');
79 * Prepare a connection for to this LDAP server. Note that this function
80 * doesn't actually connect to the server.
82 $this->ldap = @ldap_connect($hostname, $port);
83 if ($this->ldap ===
false) {
88 if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3)) {
89 throw $this->
makeException(
'Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3',
ERR_INTERNAL);
93 if (!@ldap_set_option($this->ldap, LDAP_OPT_REFERRALS, $referrals)) {
94 throw $this->
makeException(
'Library - LDAP __construct(): Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to '.$referrals,
ERR_INTERNAL);
101 if (!@ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT,
$timeout)) {
104 if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT,
$timeout)) {
110 if (stripos($hostname,
"ldaps:") ===
false and $enable_tls) {
111 if (!@ldap_start_tls($this->ldap)) {
131 if (empty($this->ldap)) {
134 $errNo = @ldap_errno($this->ldap);
143 SimpleSAML\Logger::error($description);
147 case ERR_INTERNAL:// 1 - ExInternal
148 return new SimpleSAML_Error_Exception($description, $errNo);
149 case ERR_NO_USER:// 2 - ExUserNotFound
150 return new SimpleSAML_Error_UserNotFound($description, $errNo);
151 case ERR_WRONG_PW:// 3 - ExInvalidCredential
152 return new SimpleSAML_Error_InvalidCredential($description, $errNo);
153 case ERR_AS_DATA_INCONSIST:// 4 - ExAsDataInconsist
154 return new SimpleSAML_Error_AuthSource('ldap
', $description);
155 case ERR_AS_INTERNAL:// 5 - ExAsInternal
156 return new SimpleSAML_Error_AuthSource('ldap
', $description);
160 $description .= '; cause: \
'' . ldap_error($this->ldap) .
'\' (0x
' . dechex($errNo) . ')
';
161 if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) && !empty($extendedError)) {
162 $description .= '; additional: \
'' . $extendedError .
'\'';
206 private function search(
$base, $attribute, $value, $searchFilter =
null)
209 $attribute = self::escape_filter_value($attribute,
false);
210 $value = self::escape_filter_value($value);
212 foreach ($attribute as $attr) {
213 $filter .=
'(' . $attr .
'=' . $value.
')';
215 $filter =
'(|' . $filter .
')';
218 if ($searchFilter !=
null) {
219 $filter =
"(&".$filter.
"".$searchFilter.
")";
225 $result = @ldap_search($this->ldap,
$base, $filter, array(), 0, 0, $this->timeout);
227 throw $this->makeException(
'Library - LDAP search(): Failed search on base \'' .
$base .
'\' for \
'' . $filter .
'\'');
231 $count = @ldap_count_entries($this->ldap,
$result);
232 if ($count ===
false) {
233 throw $this->makeException(
'Library - LDAP search(): Failed to get number of entries returned');
234 } elseif ($count > 1) {
236 throw $this->makeException(
'Library - LDAP search(): Found ' . $count .
' entries searching base \'' .
$base .
'\' for \
'' . $filter .
'\'',
ERR_AS_DATA_INCONSIST);
237 } elseif ($count === 0) {
239 throw $this->makeException(
'Library - LDAP search(): Found no entries searching base \'' .
$base .
'\' for \
'' . $filter .
'\'',
ERR_NO_USER);
244 $entry = @ldap_first_entry($this->ldap,
$result);
245 if ($entry ===
false) {
246 throw $this->makeException(
'Library - LDAP search(): Unable to retrieve result after searching base \'' .
$base .
'\' for \
'' . $filter .
'\'');
248 $dn = @ldap_get_dn($this->ldap, $entry);
250 throw $this->makeException(
'Library - LDAP search(): Unable to get DN after searching base \'' .
$base .
'\' for \
'' . $filter .
'\'');
280 public function searchfordn(
$base, $attribute, $value, $allowZeroHits =
false, $searchFilter =
null)
301 if ($allowZeroHits) {
306 throw $this->makeException(
'Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'(' .
307 $attribute .
' = ' . $value .
')\' on base(s) \'(' . join(
' & ', $bases) .
')\'', 2);
328 $filters = $this->escape_filter_value($filters,
false);
333 if (is_array($filters)) {
334 foreach ($filters as $attribute => $value) {
335 $filter .=
"($attribute=$value)";
337 if (count($filters) > 1) {
338 $filter = ($and ?
'(&' :
'(|') . $filter .
')';
340 } elseif (is_string($filters)) {
345 if ($filter ==
'' || $filter ==
'(=)') {
346 throw $this->makeException(
'ldap:LdapConnection->search_manual : No search filters defined',
ERR_INTERNAL);
350 $bases = (array) $bases;
352 throw $this->makeException(
'ldap:LdapConnection->search_manual : No base DNs were passed',
ERR_INTERNAL);
357 foreach ($bases as
$base) {
359 if (
$result !==
false && @ldap_count_entries($this->ldap,
$result) > 0) {
366 throw $this->makeException(
367 'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) [' .
368 implode(
'; ', $bases) .
'] with filter [' . $filter .
']. LDAP error [' .
369 ldap_error($this->ldap) .
']'
371 } elseif (@ldap_count_entries($this->ldap,
$result) < 1) {
372 throw $this->makeException(
373 'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) [' .
374 implode(
'; ', $bases) .
'] with filter [' . $filter .
']',
382 throw $this->makeException(
383 'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results'
392 for ($j = 0; $j < $entry[
'count']; $j++) {
394 $attribute = $entry[
$name];
397 for ($k = 0; $k < $attribute[
'count']; $k++) {
399 if (strtolower(
$name) ===
'jpegphoto' || strtolower(
$name) ===
'objectguid') {
432 if ($sasl_args !=
null) {
433 if (!function_exists(
'ldap_sasl_bind')) {
434 $ex_msg =
'Library - missing SASL support';
435 throw $this->makeException($ex_msg);
439 $authz_id = $sasl_args[
'authz_id'];
443 $sasl_args[
'authc_id'],
444 $sasl_args[
'authz_id'],
445 $sasl_args[
'props']);
454 $this->authz_id = $authz_id;
462 switch (ldap_errno($this->ldap)) {
478 throw $this->makeException(
'Library - LDAP bind(): Bind failed with DN \'' . $dn .
'\'');
493 if (!@ldap_set_option($this->ldap, $option, $value)) {
494 throw $this->makeException(
495 'ldap:LdapConnection->setOption : Failed to set LDAP option [' .
496 $option .
'] with the value [' . $value .
'] error: ' . ldap_error($this->ldap),
503 'ldap:LdapConnection->setOption : Set the LDAP option [' .
504 $option .
'] with the value [' . $value .
']'
540 $result = @ldap_read($this->ldap, $dn,
'objectClass=*',
$attributes, 0, 0, $this->timeout);
542 throw $this->makeException(
'Library - LDAP getAttributes(): Failed to get attributes from DN \'' . $dn .
'\'');
544 $entry = @ldap_first_entry($this->ldap,
$result);
545 if ($entry ===
false) {
546 throw $this->makeException(
'Library - LDAP getAttributes(): Could not get first entry from DN \'' . $dn .
'\'');
548 $attributes = @ldap_get_attributes($this->ldap, $entry);
550 throw $this->makeException(
'Library - LDAP getAttributes(): Could not get attributes of first entry from DN \'' . $dn .
'\'');
563 for ($j = 0; $j < $attribute[
'count']; $j++) {
564 $value = $attribute[$j];
566 if (!empty($maxsize) && strlen($value) >= $maxsize) {
569 $name .
'\' exceeded maximum allowed size by
' + ($maxsize - strlen($value)));
573 // Base64 encode binary attributes
574 if (strtolower($name) === 'jpegphoto
' || strtolower($name) === 'objectguid
') {
575 $values[] = base64_encode($value);
583 $result[$name] = $values;
588 SimpleSAML\Logger::debug(
'Library - LDAP getAttributes(): Found attributes \'(' . join(
',', array_keys(
$result)) .
')\'');
609 $username = addcslashes($username,
',+"\\<>;*');
611 if (isset(
$config[
'priv_user_dn'])) {
614 if (isset(
$config[
'dnpattern'])) {
615 $dn = str_replace(
'%username%', $username,
$config[
'dnpattern']);
617 $dn = $this->searchfordn(
$config[
'searchbase'],
$config[
'searchattributes'], $username);
624 SimpleSAML\Logger::info(
'Library - LDAP validate(): Failed to authenticate \''. $username .
'\' using DN \
'' . $dn .
'\'');
654 if (!is_array($values)) {
655 $values = array($values);
658 foreach ($values as
$key => $val) {
660 $val = str_replace(
'\\',
'\5c', $val);
661 $val = str_replace(
'*',
'\2a', $val);
662 $val = str_replace(
'(',
'\28', $val);
663 $val = str_replace(
')',
'\29', $val);
666 $val = self::asc2hex32($val);
672 $values[
$key] = $val;
693 for (
$i = 0;
$i < strlen($string);
$i++) {
694 $char = substr($string,
$i, 1);
695 if (ord($char) < 32) {
696 $hex = dechex(ord($char));
697 if (strlen($hex) == 1) {
700 $string = str_replace($char,
'\\'.$hex, $string);
711 if (preg_match(
"/^dn:/", $authz_id)) {
712 return preg_replace(
"/^dn:/",
"", $authz_id);
715 if (preg_match(
"/^u:/", $authz_id)) {
716 return $this->searchfordn($searchBase, $searchAttributes,
717 preg_replace(
"/^u:/",
"", $authz_id));
734 public function whoami($searchBase, $searchAttributes)
738 if (function_exists(
'ldap_exop_whoami')) {
739 if (version_compare(phpversion(),
'7',
'<')) {
740 if (ldap_exop_whoami($this->ldap, $authz_id) !==
true) {
741 throw $this->makeException(
'LDAP whoami exop failure');
744 if (($authz_id = ldap_exop_whoami($this->ldap)) ===
false) {
745 throw $this->makeException(
'LDAP whoami exop failure');
749 $authz_id = $this->authz_id;
752 $dn = $this->authzid_to_dn($searchBase, $searchAttributes, $authz_id);
754 if (!isset($dn) || ($dn ==
'')) {
755 throw $this->makeException(
'Cannot figure userID');
An exception for terminatinating execution or to throw for unit testing.
static arrayize($data, $index=0)
Put a non-array variable into an array.
bind($dn, $password, array $sasl_args=null)
Bind to LDAP with a specific DN and password.
searchformultiple($bases, $filters, $attributes=array(), $and=true, $escape=true)
This method was created specifically for the ldap:AttributeAddUsersGroups->searchActiveDirectory() me...
__construct($hostname, $enable_tls=true, $debug=false, $timeout=0, $port=389, $referrals=true)
Private constructor restricts instantiation to getInstance().
makeException($description, $type=null)
Convenience method to create an LDAPException as well as log the description.
whoami($searchBase, $searchAttributes)
ldap_exop_whoami accessor, if available.
authzid_to_dn($searchBase, $searchAttributes, $authz_id)
Convert SASL authz_id into a DN.
static escape_filter_value($values=array(), $singleValue=true)
Borrowed function from PEAR:LDAP.
validate($config, $username, $password=null)
Enter description here...
$authz_id
LDAP user: authz_id if SASL is in use, binding dn otherwise.
static asc2hex32($string)
Borrowed function from PEAR:LDAP.
getAttributes($dn, $attributes=null, $maxsize=null)
Search a given DN for attributes, and return the resulting associative array.
searchfordn($base, $attribute, $value, $allowZeroHits=false, $searchFilter=null)
Search for a DN.
search($base, $attribute, $value, $searchFilter=null)
Search for DN from a single base.
setOption($option, $value)
Applies an LDAP option to the current connection.
const ERR_AS_DATA_INCONSIST
const ERR_INTERNAL
Constants defining possible errors.
Attribute-related utility methods.
defined( 'APPLICATION_ENV')||define( 'APPLICATION_ENV'