From 4d982d38a85567491a163d2f1561ad938640dea2 Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Thu, 17 Feb 2011 09:35:06 -0500 Subject: [PATCH] - Add LDAP SASL bind and proxy authentication (#1486692) --- CHANGELOG | 1 program/include/rcube_ldap.php | 84 ++++++++++++++++++++++++++++++++++++++--- config/main.inc.php.dist | 5 ++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 85f2bc3..db6c4af 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add LDAP SASL bind and proxy authentication (#1486692) - Add variable for 'Today' label in date_today option (#1486120) - Fix dont_override setting does not override existing user preferences (#1487664) - Use only one from IMAP authentication methods to prevent login delays (1487784) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 3d9cd06..db029f1 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -470,6 +470,11 @@ // The login name is used to search for the DN to bind with 'search_base_dn' => '', 'search_filter' => '', // e.g. '(&(objectClass=posixAccount)(uid=%u))' + // Optional authentication identifier to be used as SASL authorization proxy + // bind_dn need to be empty + 'auth_cid' => '', + // SASL authentication method (for proxy auth), e.g. DIGEST-MD5 + 'auth_method' => '', // Indicates if we can write to the LDAP directory or not. // If writable is true then these fields need to be populated: // LDAP_Object_Classes, required_fields, LDAP_rdn diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index 308c4f2..c825371 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -162,11 +162,16 @@ { $this->ready = true; + $bind_pass = $this->prop['bind_pass']; + $bind_user = $this->prop['bind_user']; + $bind_dn = $this->prop['bind_dn']; + $base_dn = $this->prop['base_dn']; + // User specific access, generate the proper values to use. if ($this->prop['user_specific']) { // No password set, use the session password - if (empty($this->prop['bind_pass'])) { - $this->prop['bind_pass'] = $RCMAIL->decrypt($_SESSION['password']); + if (empty($bind_pass)) { + $bind_pass = $RCMAIL->decrypt($_SESSION['password']); } // Get the pieces needed for variable replacement. @@ -190,19 +195,31 @@ $this->_debug("S: search returned dn: $bind_dn"); if ($bind_dn) { - $this->prop['bind_dn'] = $bind_dn; $dn = ldap_explode_dn($bind_dn, 1); $replaces['%dn'] = $dn[0]; } } } // Replace the bind_dn and base_dn variables. - $this->prop['bind_dn'] = strtr($this->prop['bind_dn'], $replaces); - $this->prop['base_dn'] = strtr($this->prop['base_dn'], $replaces); + $bind_dn = strtr($bind_dn, $replaces); + $base_dn = strtr($base_dn, $replaces); + + if (empty($bind_user)) { + $bind_user = $u; + } } - if (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass'])) - $this->ready = $this->_bind($this->prop['bind_dn'], $this->prop['bind_pass']); + if (!empty($bind_pass)) { + if (!empty($bind_dn)) { + $this->ready = $this->_bind($bind_dn, $bind_pass); + } + else if (!empty($this->prop['auth_cid'])) { + $this->ready = $this->_sasl_bind($this->prop['auth_cid'], $bind_pass, $bind_user); + } + else { + $this->ready = $this->_sasl_bind($bind_user, $bind_pass); + } + } } else raise_error(array('code' => 100, 'type' => 'ldap', @@ -217,6 +234,59 @@ /** + * Bind connection with (SASL-) user and password + * + * @param string $authc Authentication user + * @param string $pass Bind password + * @param string $authz Autorization user + * + * @return boolean True on success, False on error + */ + private function _sasl_bind($authc, $pass, $authz=null) + { + if (!$this->conn) { + return false; + } + + if (!function_exists('ldap_sasl_bind')) { + raise_error(array( + 'code' => 100, 'type' => 'ldap', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Unable to bind: ldap_sasl_bind() not exists"), + true, true); + } + + if (!empty($authz)) { + $authz = 'u:' . $authz; + } + + if (!empty($this->prop['auth_method'])) { + $method = $this->prop['auth_method']; + } + else { + $method = 'DIGEST-MD5'; + } + + $this->_debug("C: Bind [mech: $method, authc: $authc, authz: $authz] [pass: $pass]"); + + if (ldap_sasl_bind($this->conn, NULL, $pass, $method, NULL, $authc, $authz)) { + $this->_debug("S: OK"); + return true; + } + + $this->_debug("S: ".ldap_error($this->conn)); + + raise_error(array( + 'code' => ldap_errno($this->conn), 'type' => 'ldap', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Bind failed for authcid=$authc ".ldap_error($this->conn)), + true); + + return false; + } + + + /** * Bind connection with DN and password * * @param string Bind DN -- Gitblit v1.9.1