alecpl
2011-02-17 4d982d38a85567491a163d2f1561ad938640dea2
- Add LDAP SASL bind and proxy authentication (#1486692)


3 files modified
90 ■■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
config/main.inc.php.dist 5 ●●●●● patch | view | raw | blame | history
program/include/rcube_ldap.php 84 ●●●●● patch | view | raw | blame | history
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)
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
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