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