alecpl
2010-10-23 4dd4172421fd16c5a84f4a80f626672a91d343f0
- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)


3 files modified
99 ■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
config/main.inc.php.dist 4 ●●●● patch | view | raw | blame | history
program/include/rcube_imap_generic.php 94 ●●●● patch | view | raw | blame | history
CHANGELOG
@@ -46,6 +46,7 @@
- Add support for AUTH=PLAIN in IMAP authentication
- Re-implemented SMTP proxy authentication support
- Add support for IMAP proxy authentication (#1486690)
- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)
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), "login" (LOGIN)
// or "check" (or empty) to auto detect. Optional, defaults to "check"
// IMAP AUTH type (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or empty to use
// best server supported one)
$rcmail_config['imap_auth_type'] = null;
// If you know your imap's root directory and its folder delimiter,
program/include/rcube_imap_generic.php
@@ -371,17 +371,35 @@
    }
    /**
     * CRAM-MD5/PLAIN Authentication
     * DIGEST-MD5/CRAM-MD5/PLAIN Authentication
     *
     * @param string $user
     * @param string $pass
     * @param string $type Authentication type (PLAIN or CRAM-MD5)
     * @param string $type Authentication type (PLAIN/CRAM-MD5/DIGEST-MD5)
     *
     * @return resource Connection resourse on success, error code on error
     */
    function authenticate($user, $pass, $type='PLAIN')
    {
        if ($type == 'CRAM-MD5' || $type == 'DIGEST-MD5') {
            if ($type == 'DIGEST-MD5' && !class_exists('Auth_SASL')) {
                $this->set_error(self::ERROR_BYE,
                    "The Auth_SASL package is required for DIGEST-MD5 authentication");
                return self::ERROR_BAD;
            }
            $this->putLine($this->next_tag() . " AUTHENTICATE $type");
            $line = trim($this->readLine(1024));
            if ($line[0] == '+') {
                $challenge = substr($line, 2);
            }
            else {
                return $this->parseResult($line);
            }
        if ($type == 'CRAM-MD5') {
                // RFC2195: CRAM-MD5
            $ipad = '';
            $opad = '';
@@ -397,30 +415,55 @@
                $pass .= chr(0);
            }
            $this->putLine($this->next_tag() . " AUTHENTICATE CRAM-MD5");
            $line = trim($this->readLine(1024));
                // generate hash
                $hash  = md5($this->_xor($pass, $opad) . pack("H*",
                    md5($this->_xor($pass, $ipad) . base64_decode($challenge))));
                $reply = base64_encode($user . ' ' . $hash);
                // send result
                $this->putLine($reply);
            }
            else {
                // RFC2831: DIGEST-MD5
                // proxy authorization
                if (!empty($this->prefs['auth_cid'])) {
                    $authc = $this->prefs['auth_cid'];
                    $pass  = $this->prefs['auth_pw'];
                }
                else {
                    $authc = $user;
                }
                $auth_sasl = Auth_SASL::factory('digestmd5');
                $reply = base64_encode($auth_sasl->getResponse($authc, $pass,
                    base64_decode($challenge), $this->host, 'imap', $user));
                // send result
                $this->putLine($reply);
                $line = $this->readLine(1024);
            if ($line[0] == '+') {
                $challenge = substr($line,2);
            }
            else {
                return self::ERROR_BYE;
                    return $this->parseResult($line);
            }
            // generate hash
            $hash  = md5($this->_xor($pass, $opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($encChallenge))));
            $reply = base64_encode($user . ' ' . $hash);
                // check response
                $challenge = base64_decode($challenge);
                if (strpos($challenge, 'rspauth=') === false) {
                    $this->set_error(self::ERROR_BAD,
                        "Unexpected response from server to DIGEST-MD5 response");
                    return self::ERROR_BAD;
                }
            // send result, get reply and process it
            $this->putLine($reply);
                $this->putLine('');
            }
            $line = $this->readLine(1024);
            $result = $this->parseResult($line);
            if ($result != self::ERROR_OK) {
                $this->set_error($result, "Unble to authenticate user (CRAM-MD5): $line");
            }
        }
        else { // PLAIN
            // proxy authentication
            // proxy authorization
            if (!empty($this->prefs['auth_cid'])) {
                $authc = $this->prefs['auth_cid'];
                $pass  = $this->prefs['auth_pw'];
@@ -440,21 +483,21 @@
                $line = trim($this->readLine(1024));
                if ($line[0] != '+') {
                    return self::ERROR_BYE;
                    return $this->parseResult($line);
                }
                // 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;
        }
        else {
            $this->set_error($result, "Unable to authenticate user ($type): $line");
        }
        return $result;
@@ -696,8 +739,11 @@
        // check for supported auth methods
        if ($auth_method == 'CHECK') {
            if ($this->getCapability('AUTH=DIGEST-MD5')) {
                $auth_methods[] = 'DIGEST-MD5';
            }
            if ($this->getCapability('AUTH=CRAM-MD5') || $this->getCapability('AUTH=CRAM_MD5')) {
                $auth_methods[] = 'AUTH';
                $auth_methods[] = 'CRAM-MD5';
            }
            if ($this->getCapability('AUTH=PLAIN')) {
                $auth_methods[] = 'PLAIN';
@@ -708,17 +754,17 @@
            }
        }
        else {
            $auth_methods[] = $auth_method;
            // replace AUTH with CRAM-MD5 for backward compat.
            $auth_methods[] = $auth_method == 'AUTH' ? 'CRAM-MD5' : $auth_method;
        }
        // Authenticate
        foreach ($auth_methods as $method) {
            switch ($method) {
            case 'AUTH':
                $result = $this->authenticate($user, $password, 'CRAM-MD5');
                break;
            case 'DIGEST-MD5':
            case 'CRAM-MD5':
            case 'PLAIN':
                $result = $this->authenticate($user, $password, 'PLAIN');
                $result = $this->authenticate($user, $password, $method);
                break;
            case 'LOGIN':
                   $result = $this->login($user, $password);