lib/Auth lib/ext

Aleksander Machniak machniak at kolabsys.com
Fri Nov 14 12:54:03 CET 2014


 lib/Auth/LDAP.php     |   12 ++---
 lib/ext/Net/LDAP3.php |  120 +++++++++++++++++++++++++++++++++++---------------
 2 files changed, 90 insertions(+), 42 deletions(-)

New commits:
commit b0833fcc63d5b8d5ad94d3aff32a0af99aa06516
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Nov 14 06:51:22 2014 -0500

    Fix handling of special characters in RDN attributes (#3905)
    Fix update of objects which base DN contains special characters (#3824)

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index d711c16..eab0b32 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -562,7 +562,7 @@ class LDAP extends Net_LDAP3 {
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]user_rdn_attr
-        $dn = "cn=" . $attrs['cn'] . "," . $base_dn;
+        $dn = "cn=" . Net_LDAP3::quote_string($attrs['cn'], true) . "," . $base_dn;
 
         return $this->entry_add($dn, $attrs);
     }
@@ -734,7 +734,7 @@ class LDAP extends Net_LDAP3 {
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]ou_rdn_attr
-        $dn = "ou=" . $attrs['ou'] . "," . $base_dn;
+        $dn = "ou=" . Net_LDAP3::quote_string($attrs['ou'], true) . "," . $base_dn;
 
         return $this->entry_add($dn, $attrs);
     }
@@ -792,7 +792,7 @@ class LDAP extends Net_LDAP3 {
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]resource_rdn_attr
-        $dn = "cn=" . $attrs['cn'] . "," . $base_dn;
+        $dn = "cn=" . Net_LDAP3::quote_string($attrs['cn'], true) . "," . $base_dn;
 
         return $this->entry_add($dn, $attrs);
     }
@@ -843,7 +843,7 @@ class LDAP extends Net_LDAP3 {
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]role_rdn_attr
-        $dn = "cn=" . $attrs['cn'] . "," . $base_dn;
+        $dn = "cn=" . Net_LDAP3::quote_string($attrs['cn'], true) . "," . $base_dn;
 
         return $this->entry_add($dn, $attrs);
     }
@@ -895,7 +895,7 @@ class LDAP extends Net_LDAP3 {
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]user_rdn_attr
-        $dn = "cn=" . $attrs['cn'] . "," . $base_dn;
+        $dn = "cn=" . Net_LDAP3::quote_string($attrs['cn'], true) . "," . $base_dn;
 
         return $this->entry_add($dn, $attrs);
     }
@@ -967,7 +967,7 @@ class LDAP extends Net_LDAP3 {
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]user_rdn_attr
-        $dn = "uid=" . $attrs['uid'] . "," . $base_dn;
+        $dn = "uid=" . Net_LDAP3::quote_string($attrs['uid'], true) . "," . $base_dn;
 
         return $this->entry_add($dn, $attrs);
     }
diff --git a/lib/ext/Net/LDAP3.php b/lib/ext/Net/LDAP3.php
index 64c46d8..7e463f0 100644
--- a/lib/ext/Net/LDAP3.php
+++ b/lib/ext/Net/LDAP3.php
@@ -1241,7 +1241,7 @@ class Net_LDAP3
 
                                     $this->_debug("old attrs. is array, new attrs. is not array. new attr. exists in old attrs.");
 
-                                    $rdn_attr_value = array_shift($old_attrs[$attr]);
+                                    $rdn_attr_value  = array_shift($old_attrs[$attr]);
                                     $_attr_to_remove = array();
 
                                     foreach ($old_attrs[$attr] as $value) {
@@ -1256,14 +1256,14 @@ class Net_LDAP3
 
                                     if (strtolower($new_attrs[$attr]) !== strtolower($rdn_attr_value)) {
                                         $this->_debug("new attrs is not the same as the old rdn value, issuing a rename");
-                                        $mod_array['rename']['dn'] = $subject_dn;
-                                        $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr][0];
+                                        $mod_array['rename']['dn']      = $subject_dn;
+                                        $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . self::quote_string($new_attrs[$attr], true);
                                     }
                                 }
                                 else {
                                     $this->_debug("new attrs is not the same as any of the old rdn value, issuing a full rename");
-                                    $mod_array['rename']['dn'] = $subject_dn;
-                                    $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr];
+                                    $mod_array['rename']['dn']      = $subject_dn;
+                                    $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . self::quote_string($new_attrs[$attr], true);
                                 }
                             }
                             else {
@@ -1274,17 +1274,17 @@ class Net_LDAP3
                                 }
                                 else {
                                     // TODO: This fails.
-                                    $mod_array['rename']['dn'] = $subject_dn;
-                                    $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr][0];
-                                    $mod_array['del'][$attr] = $old_attrs[$attr][0];
+                                    $mod_array['rename']['dn']      = $subject_dn;
+                                    $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . self::quote_string($new_attrs[$attr][0], true);
+                                    $mod_array['del'][$attr]        = $old_attrs[$attr][0];
                                 }
                             }
                         }
                         else {
                             if (!is_array($new_attrs[$attr])) {
                                 $this->_debug("Renaming " . $old_attrs[$attr] . " to " . $new_attrs[$attr]);
-                                $mod_array['rename']['dn'] = $subject_dn;
-                                $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr];
+                                $mod_array['rename']['dn']      = $subject_dn;
+                                $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . self::quote_string($new_attrs[$attr], true);
                             }
                             else {
                                 $this->_debug("Adding to replace");
@@ -1293,7 +1293,6 @@ class Net_LDAP3
                                 continue;
                             }
                         }
-
                     }
                     else {
                         if (!isset($new_attrs[$attr]) || $new_attrs[$attr] === '' || (is_array($new_attrs[$attr]) && empty($new_attrs[$attr]))) {
@@ -1389,9 +1388,12 @@ class Net_LDAP3
             $old_ou      = implode(',', $subject_dn_components);
         }
 
+        $subject_dn = self::unified_dn($subject_dn);
+        $prefix     = self::unified_dn('ou=' . $old_ou) . ',';
+
         // object is an organizational unit
-        if (strpos($subject_dn, 'ou=' . $old_ou) === 0) {
-            $root = substr($subject_dn, strlen($old_ou) + 4); // remove ou=*,
+        if (strpos($subject_dn, $prefix) === 0) {
+            $root = substr($subject_dn, strlen($prefix)); // remove ou=*,
 
             if ((!empty($new_attrs['base_dn']) && strtolower($new_attrs['base_dn']) !== strtolower($root))
                 || (strtolower($old_ou) !== strtolower($new_ou))
@@ -1402,15 +1404,22 @@ class Net_LDAP3
 
                 $mod_array['rename']['new_parent'] = $root;
                 $mod_array['rename']['dn']         = $subject_dn;
-                $mod_array['rename']['new_rdn']    = 'ou=' . $new_ou;
+                $mod_array['rename']['new_rdn']    = 'ou=' . self::quote_string($new_ou, true);
             }
         }
         // not OU object, but changed ou attribute
-        else if ((!empty($old_ou) && !empty($new_ou)) && strtolower($old_ou) !== strtolower($new_ou)) {
-            $mod_array['rename']['new_parent'] = $new_ou;
-            if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) {
-                $mod_array['rename']['dn']      = $subject_dn;
-                $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$rdn_attr];
+        else if (!empty($old_ou) && !empty($new_ou)) {
+            // unify DN strings for comparison
+            $old_ou = self::unified_dn($old_ou);
+            $new_ou = self::unified_dn($new_ou);
+
+            if (strtolower($old_ou) !== strtolower($new_ou)) {
+                $mod_array['rename']['new_parent'] = $new_ou;
+                if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) {
+                    $rdn_attr_value = self::quote_string($new_attrs[$rdn_attr], true);
+                    $mod_array['rename']['dn']      = $subject_dn;
+                    $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $rdn_attr_value;
+                }
             }
         }
 
@@ -2145,23 +2154,23 @@ class Net_LDAP3
 
     private function modify_entry_attributes($subject_dn, $attributes)
     {
-        // Opportunities to set false include failed ldap commands.
-        $result = true;
-
         if (is_array($attributes['rename']) && !empty($attributes['rename'])) {
-            $olddn  = $attributes['rename']['dn'];
-            $newrdn = $attributes['rename']['new_rdn'];
-
-            if (!empty($attributes['rename']['new_parent'])) {
-                $new_parent = $attributes['rename']['new_parent'];
-            }
-            else {
-                $new_parent = null;
-            }
+            $olddn      = $attributes['rename']['dn'];
+            $newrdn     = $attributes['rename']['new_rdn'];
+            $new_parent = $attributes['rename']['new_parent'];
 
             $this->_debug("LDAP: C: Rename $olddn to $newrdn,$new_parent");
 
-            $result = ldap_rename($this->conn, $olddn, $newrdn, $new_parent, true);
+            // Note: for some reason the operation fails if RDN contains special characters
+            // and last argument of ldap_rename() is set to TRUE. That's why we use FALSE.
+            // However, we need to modify RDN attribute value later, otherwise it
+            // will contain an array of previous and current values
+            for ($i = 1; $i >= 0; $i--) {
+                $result = ldap_rename($this->conn, $olddn, $newrdn, $new_parent, $i == 1);
+                if ($result) {
+                    break;
+                }
+            }
 
             if ($result) {
                 $this->_debug("LDAP: S: OK");
@@ -2176,6 +2185,12 @@ class Net_LDAP3
                     $old_parent_dn = implode(",", $old_parent_dn_components);
                     $subject_dn    = $newrdn . ',' . $old_parent_dn;
                 }
+
+                // modify RDN attribute value, see note above
+                if (!$i && empty($attributes['replace'][$attr])) {
+                    list($attr, $val) = explode('=', $newrdn, 2);
+                    $attributes['replace'][$attr] = self::quote_string($val, true, true);
+                }
             }
             else {
                 $this->_debug("LDAP: S: " . ldap_error($this->conn));
@@ -2391,14 +2406,15 @@ class Net_LDAP3
     /**
      * Quotes attribute value string
      *
-     * @param string $str Attribute value
-     * @param bool   $dn  True if the attribute is a DN
+     * @param string $str     Attribute value
+     * @param bool   $dn      True if the attribute is a DN
+     * @param bool   $reverse Do reverse replacement
      *
      * @return string Quoted string
      */
-    public static function quote_string($str, $is_dn = false)
+    public static function quote_string($str, $is_dn = false, $reverse = false)
     {
-        // take firt entry if array given
+        // take first entry if array given
         if (is_array($str)) {
             $str = reset($str);
         }
@@ -2426,10 +2442,42 @@ class Net_LDAP3
             );
         }
 
+        if ($reverse) {
+            return str_replace(array_values($replace), array_keys($replace), $str);
+        }
+
         return strtr($str, $replace);
     }
 
     /**
+     * Unify DN string for comparison
+     *
+     * @para string $str DN string
+     *
+     * @return string Unified DN string
+     */
+    public static function unified_dn($str)
+    {
+        $result = array();
+
+        foreach (explode(',', $str) as $token) {
+            list($attr, $value) = explode('=', $token, 2);
+
+            $pos = 0;
+            while (preg_match('/\\\\[0-9a-fA-F]{2}/', $value, $matches, PREG_OFFSET_CAPTURE, $pos)) {
+                $char  = chr(hexdec(substr($matches[0][0], 1)));
+                $pos   = $matches[0][1];
+                $value = substr_replace($value, $char, $pos, 3);
+                $pos += 1;
+            }
+
+            $result[] = $attr . '=' . self::quote_string($value, true);
+        }
+
+        return implode(',', $result);
+    }
+
+    /**
      * create ber encoding for sort control
      *
      * @param array List of cols to sort by




More information about the commits mailing list