summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--config/main.inc.php.dist5
-rw-r--r--program/include/rcube_ldap.php84
3 files changed, 83 insertions, 7 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 85f2bc3e9..db6c4af25 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 3d9cd0663..db029f152 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -470,6 +470,11 @@ $rcmail_config['ldap_public']['Verisign'] = array(
// 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 308c4f238..c8253713b 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -162,11 +162,16 @@ class rcube_ldap extends rcube_addressbook
{
$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 @@ class rcube_ldap extends rcube_addressbook
$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 @@ class rcube_ldap extends rcube_addressbook
/**
+ * 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