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'