From 12f74166fa4de7b7a9bbdfcc978a261cb3cda51e Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@iml.unibe.ch>
Date: Mon, 19 Sep 2022 15:14:06 +0200
Subject: [PATCH] update comments in ldap class

---
 src/ldap.class.php | 128 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 104 insertions(+), 24 deletions(-)

diff --git a/src/ldap.class.php b/src/ldap.class.php
index 93bf9f8..82e4a57 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;
     }
+
 }
-- 
GitLab