diff --git a/src/ldap.class.php b/src/ldap.class.php index 93bf9f8275cb2d1bf647c158487fa203720328fd..82e4a57c387cfced3423599f0cb00f6763289b5d 100755 --- a/src/ldap.class.php +++ b/src/ldap.class.php @@ -1,16 +1,25 @@ <?php /** - * IML LDAP CLASS * - * - ldap auth - * - CRUD actions on ldap leafs - * - * last change: 2022-02-17 + * IML LDAP CONNECTOR + *<pre> + * 2022-02-22 ah added objGet(), sanitizeFilter() <br> + * 2022-08-18 ah mask password (showing 4 chars only) <br> + * 2022-08-22 ah mhash is deprecated <br> + * 2022-08-26 ah fix verifyPassword <br> + * </pre> * @author axel.hahn@iml.unibe.ch */ class imlldap { + // ---------------------------------------------------------------------- + // vars + // ---------------------------------------------------------------------- + + /** + * @var array options array for an ldap connection including some base settings and DNs + */ private $_aLdap = array( 'server' => false, 'port' => false, @@ -21,10 +30,21 @@ class imlldap { 'protoVersion' => 3, 'debugLevel' => 0, ); + /** + * @var object current ldap connection + */ private $_ldapConn = false; + + /** + * @var bool bind was done? + */ private $_ldapBind = false; var $bDebug = false; + // ---------------------------------------------------------------------- + // functions + // ---------------------------------------------------------------------- + /** * constructor * @param array $aConfig optional set ldap connection @@ -198,7 +218,7 @@ class imlldap { $this->_w(__FUNCTION__ . ' ERROR: no user was set as first param.'); die("ERROR: no user was given to connect to ldap."); } - $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . $sPw); + $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw,0,4).'**********'); $this->_ldapBind = @ldap_bind($this->_ldapConn, $sUser, $sPw); if (!$this->_ldapBind) { @@ -282,6 +302,36 @@ class imlldap { return $aReturn; } + /** + * sanitize value to put into a search filter + * WARNING: the implementation is incomplete! I replaces the first N ascii chars only + * + * source: https://www.rfc-editor.org/rfc/rfc4515.txt + * + * @example: + * $sCn = 'John Smith (john)'; + * $sSearchFilter = '(cn='.$oLdap->sanitizeFilter($sCn).')'; + * + * @param string $s value to sanitize + * @return string + */ + static public function sanitizeFilter($s){ + + // helper array to replace special chars + $aReplace=array(); + for($i=0; $i<65; $i++){ + $val=dechex($i); + if ($val<10){ + $val="0$val"; + } + $aReplace[chr($i)]='\\'.$val; + } + + $sReturn=$s; + $sReturn=str_replace(array_keys($aReplace), array_values($aReplace), $sReturn); + + return $sReturn; + } /** * search in ldap directory and get result as array * @@ -291,13 +341,13 @@ class imlldap { * @param boolean $bRecursive recusrive (uses ldap_search) or not (ldap_list) * @return array */ - public function searchDn($sDn, $sSearchFilter, $aAttributesToGet = array("*"), $bRecursive=true) { + public function searchDn($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*"), $bRecursive=true) { if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ return false; } } - $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1)); + $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1).' recursive = '.($bRecursive ? 'yes' : 'no' )); $oLdapSearch = $bRecursive ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) @@ -309,6 +359,8 @@ class imlldap { return false; } $aItems = ldap_get_entries($this->_ldapConn, $oLdapSearch); + $this->_w(__FUNCTION__ . " count of returned items: ".count($aItems)); + // $this->_w(__FUNCTION__ . " <pre>".print_r($aItems,1).'</pre>'); return $aItems; } @@ -321,7 +373,7 @@ class imlldap { * * @return array */ - public function searchUser($sSearchFilter, $aAttributesToGet = array("*"), $bRecursive=true) { + public function searchUser($sSearchFilter='', $aAttributesToGet = array("*"), $bRecursive=true) { return $this->searchDn($this->_aLdap['DnUserNode'], $sSearchFilter, $aAttributesToGet, $bRecursive); /* if (!$this->_ldapBind) { @@ -344,9 +396,9 @@ class imlldap { * It returns false if the user does not exist or is * not member of the group 'DnAppNode' (if it was set). * - * @param type $sUser user id (uid) or email (mail) to search - * @param type $aAttributesToGet i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf") - * @return boolean + * @param string $sUser user id (uid) or email (mail) to search + * @param array $aAttributesToGet i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf") + * @return boolean|array */ public function getUserInfo($sUser, $aAttributesToGet = array("*")) { if (!$this->_ldapBind) { @@ -364,7 +416,7 @@ class imlldap { $sSearchFilter = '(&' . $sSearchFilter . ')'; $aItems = $this->searchUser($sSearchFilter, $aAttributesToGet); - if (count($aItems) == 2) { + if (is_array($aItems) && count($aItems) == 2) { $this->_w(__FUNCTION__ . ' OK: I got a single result: ' . print_r($aItems[0], 1)); return $aItems[0]; } else { @@ -431,11 +483,7 @@ class imlldap { $Input = iconv('UTF-8', 'UTF-16LE', $Input); // Encrypt it with the MD4 hash - $MD4Hash = bin2hex(mhash(MHASH_MD4, $Input)); - - // You could use this instead, but mhash works on PHP 4 and 5 or above - // The hash function only works on 5 or above - //$MD4Hash=hash('md4',$Input); + $MD4Hash=hash('md4',$Input); // Make it uppercase, not necessary, but it's common to do so with NTLM hashes $NTLMHash = strtoupper($MD4Hash); @@ -473,7 +521,7 @@ class imlldap { * this requires a ldap bind with master/ admin account * * @param string $sDn dn to update - * @param string $aItem array of new ldap properties + * @param array $aItem array of new ldap properties * @return boolean */ public function objAdd($sDn, $aItem) { @@ -506,7 +554,7 @@ class imlldap { } } if ($sDn && is_array($aItem)) { - $this->_w(__FUNCTION__ . ' ' . $this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect'); + $this->_w(__FUNCTION__ . ' ' . ($this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect')); $this->_w(__FUNCTION__ . ' ldap_mod_add($this->_ldapConn, "' . $sDn . '", ' . print_r($aItem, 1) . ')'); if (!ldap_mod_add($this->_ldapConn, $sDn, $aItem)) { $this->_w(__FUNCTION__ . ' ldap_mod_add FAILED'); @@ -519,6 +567,32 @@ class imlldap { return false; } + /** + * read attributes from ldap node with given DN (using ldap_read) + * + * @param string $sDn DN to search for + * @param string $sSearchFilter filter in ldap filter syntax + * @param array $aAttributesToGet flat array of attributes to fetch + * @return array + */ + public function objGet($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*")) { + + $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = '.$sSearchFilter.', atttr= '.print_r($aAttributesToGet, 1).' )'); + if (!$this->_ldapBind) { + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + return false; + } + } + + $oLdapResult = ldap_read($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet); + + if (!$oLdapResult) { + $this->_w(__FUNCTION__ . " !!!ERROR!!! DN or filter did not match."); + return false; + } + return ldap_get_entries($this->_ldapConn, $oLdapResult); + } + /** * update an ldap object with given key-value array * if the attribute (key) does not exist it will be created. @@ -590,7 +664,7 @@ class imlldap { } } if ($sDn && is_array($aItem)) { - $this->_w(__FUNCTION__ . ' ' . $this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect'); + $this->_w(__FUNCTION__ . ' ' . ($this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect')); $this->_w(__FUNCTION__ . ' ldap_mod_del($this->_ldapConn, "' . $sDn . '", ' . print_r($aItem, 1) . ')'); if (!ldap_mod_del($this->_ldapConn, $sDn, $aItem)) { $this->_wLdaperror(__FUNCTION__); @@ -727,12 +801,18 @@ class imlldap { * @param string $sPW password * @return boolean */ - public function verifyPassword($sUser, $sPW){ - $sDn=$this->getUserDn($sUser); - if ($sDn){ + public function verifyPassword($sUser, $sPW) { + $sDn = $this->getUserDn($sUser); + if ($sDn) { return $this->bind($sDn, $sPW); + /* + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + return false; + } + */ } $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser); return false; } + }