alecpl
2010-10-22 7bf255bfe1e2fb573da7d1b107bc7cb7fef35198
- Add SASL-IR support (RFC 4959)
- Add LOGINDISABLED support (RFC 2595)
- Add support for AUTH=PLAIN authentication to IMAP


3 files modified
145 ■■■■ changed files
CHANGELOG 3 ●●●●● patch | view | raw | blame | history
config/main.inc.php.dist 4 ●●●● patch | view | raw | blame | history
program/include/rcube_imap_generic.php 138 ●●●● patch | view | raw | blame | history
CHANGELOG
@@ -41,6 +41,9 @@
- Improve performance of unseen messages counting (#1487058)
- Improve performance of messages counting using ESEARCH extension (RFC4731)
- Add LIST-STATUS support in rcube_imap_generic class (RFC5819)
- Add SASL-IR support in IMAP (RFC 4959)
- Add LOGINDISABLED support (RFC 2595)
- Add support for AUTH=PLAIN in IMAP authentication
RELEASE 0.4.2
-------------
config/main.inc.php.dist
@@ -70,8 +70,8 @@
// TCP port used for IMAP connections
$rcmail_config['default_port'] = 143;
// IMAP auth type. Can be "auth" (CRAM-MD5), "plain" (PLAIN) or "check" to auto detect.
// Optional, defaults to "check"
// IMAP auth type. Can be "auth" (CRAM-MD5), "plain" (PLAIN), "login" (LOGIN)
// or "check" (or empty) to auto detect. Optional, defaults to "check"
$rcmail_config['imap_auth_type'] = null;
// If you know your imap's root directory and its folder delimiter,
program/include/rcube_imap_generic.php
@@ -370,8 +370,18 @@
        $this->capability_readed = false;
    }
    function authenticate($user, $pass, $encChallenge)
    /**
     * CRAM-MD5/PLAIN Authentication
     *
     * @param string $user
     * @param string $pass
     * @param string $type Authentication type (PLAIN or CRAM-MD5)
     *
     * @return resource Connection resourse on success, error code on error
     */
    function authenticate($user, $pass, $type='PLAIN')
    {
        if ($type == 'CRAM-MD5') {
        $ipad = '';
        $opad = '';
@@ -387,27 +397,68 @@
            $pass .= chr(0);
        }
            $this->putLine($this->next_tag() . " AUTHENTICATE CRAM-MD5");
            $line = trim($this->readLine(1024));
            if ($line[0] == '+') {
                $challenge = substr($line,2);
            }
            else {
                return self::ERROR_BYE;
            }
        // generate hash
        $hash  = md5($this->_xor($pass,$opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($encChallenge))));
        // generate reply
        $reply = base64_encode($user . ' ' . $hash);
        // send result, get reply
            // send result, get reply and process it
        $this->putLine($reply);
        $line = $this->readLine(1024);
        // process result
        $result = $this->parseResult($line);
            if ($result != self::ERROR_OK) {
                $this->set_error($result, "Unble to authenticate user (CRAM-MD5): $line");
            }
        }
        else { // PLAIN
            $reply = base64_encode($user . chr(0) . $user . chr(0) . $pass);
            // RFC 4959 (SASL-IR): save one round trip
            if ($this->getCapability('SASL-IR')) {
                $result = $this->execute("AUTHENTICATE PLAIN", array($reply), self::COMMAND_NORESPONSE);
            }
            else {
                $this->putLine($this->next_tag() . " AUTHENTICATE PLAIN");
                $line = trim($this->readLine(1024));
                if ($line[0] != '+') {
                    return self::ERROR_BYE;
                }
                // send result, get reply and process it
                $this->putLine($reply);
                $line = $this->readLine(1024);
                $result = $this->parseResult($line);
                if ($result != self::ERROR_OK) {
                    $this->set_error($result, "Unble to authenticate user (AUTH): $line");
                }
            }
        }
        if ($result == self::ERROR_OK) {
            return $this->fp;
        }
        $this->error = "Authentication for $user failed (AUTH): $line";
        return $result;
    }
    /**
     * LOGIN Authentication
     *
     * @param string $user
     * @param string $pass
     *
     * @return resource Connection resourse on success, error code on error
     */
    function login($user, $password)
    {
        list($code, $response) = $this->execute('LOGIN', array(
@@ -421,9 +472,6 @@
        if ($code == self::ERROR_OK) {
            return $this->fp;
        }
        @fclose($this->fp);
        $this->fp    = false;
        return $code;
    }
@@ -634,44 +682,48 @@
            }
        }
        $orig_method = $auth_method;
        $auth_methods = array();
        $result       = null;
        if ($auth_method == 'CHECK') {
            // check for supported auth methods
        if ($auth_method == 'CHECK') {
            if ($this->getCapability('AUTH=CRAM-MD5') || $this->getCapability('AUTH=CRAM_MD5')) {
                $auth_method = 'AUTH';
                $auth_methods[] = 'AUTH';
            }
            if ($this->getCapability('AUTH=PLAIN')) {
                $auth_methods[] = 'PLAIN';
            }
            // RFC 2595 (LOGINDISABLED) LOGIN disabled when connection is not secure
            if (!$this->getCapability('LOGINDISABLED')) {
                $auth_methods[] = 'LOGIN';
            }
            }
            else {
                // default to plain text auth
                $auth_method = 'PLAIN';
            }
            $auth_methods[] = $auth_method;
        }
        if ($auth_method == 'AUTH') {
            // do CRAM-MD5 authentication
            $this->putLine($this->next_tag() . " AUTHENTICATE CRAM-MD5");
            $line = trim($this->readLine(1024));
            if ($line[0] == '+') {
                // got a challenge string, try CRAM-MD5
                $result = $this->authenticate($user, $password, substr($line,2));
                // stop if server sent BYE response
                if ($result == self::ERROR_BYE) {
                    return false;
                }
            }
            if (!is_resource($result) && $orig_method == 'CHECK') {
                $auth_method = 'PLAIN';
            }
        }
        if ($auth_method == 'PLAIN') {
            // do plain text auth
        // Authenticate
        foreach ($auth_methods as $method) {
            switch ($method) {
            case 'AUTH':
                $result = $this->authenticate($user, $password, 'CRAM-MD5');
                break;
            case 'PLAIN':
                $result = $this->authenticate($user, $password, 'PLAIN');
                break;
            case 'LOGIN':
            $result = $this->login($user, $password);
                break;
            default:
                $this->set_error(self::ERROR_BAD, "Configuration error. Unknown auth method: $method");
        }
            if (is_resource($result)) {
                break;
            }
        }
        // Connected and authenticated
        if (is_resource($result)) {
            if ($this->prefs['force_caps']) {
                $this->clearCapability();
@@ -680,9 +732,13 @@
            $this->logged = true;
            return true;
        } else {
            return false;
        }
        // Close connection
        @fclose($this->fp);
        $this->fp = false;
        return false;
    }
    function connected()