From 7bf255bfe1e2fb573da7d1b107bc7cb7fef35198 Mon Sep 17 00:00:00 2001 From: alecpl Date: Fri, 22 Oct 2010 18:52:20 +0000 Subject: - Add SASL-IR support (RFC 4959) - Add LOGINDISABLED support (RFC 2595) - Add support for AUTH=PLAIN authentication to IMAP --- CHANGELOG | 15 +-- config/main.inc.php.dist | 4 +- program/include/rcube_imap_generic.php | 170 ++++++++++++++++++++++----------- 3 files changed, 124 insertions(+), 65 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6cb80558e..ca435bd09 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,8 +21,8 @@ CHANGELOG Roundcube Webmail - Improve tabs to fixed width and add tabs in identities info (#1486974) - Add unique index on users.username+users.mail_host - Make htmleditor option more consistent and add option to use HTML on reply to HTML message (#1485840) -- Use empty envelope sender address for message disposition notifications (RFC2298.3) -- Support SMTP Delivery Status Notifications - RFC3461 (#1486142) +- Use empty envelope sender address for message disposition notifications (RFC 2298.3) +- Support SMTP Delivery Status Notifications - RFC 3461 (#1486142) - Use css sprite image for messages list - Add (different) attachment icon for messages of type multipart/report (#1486165) - Prevent from inserting empty link when composing HTML message (#1486944) @@ -31,16 +31,19 @@ CHANGELOG Roundcube Webmail - Improve displaying of UI messages (#1486977) - Fix double e-mail filed in identity form (#1487054) - Display IMAP errors for LIST/THREAD/SEARCH commands (#1486905) -- Add LITERAL+ (IMAP4 non-synchronizing literals) support (RFC2088) +- Add LITERAL+ (IMAP4 non-synchronizing literals) support (RFC 2088) - Add separate column for message status icon (#1486665) -- Add ACL extension support into IMAP classes (RFC4314) +- Add ACL extension support into IMAP classes (RFC 4314) - Add ANNOTATEMORE extension support into IMAP classes (draft-daboo-imap-annotatemore) -- Add METADATA extension support into IMAP classes (RFC5464) +- Add METADATA extension support into IMAP classes (RFC 5464) - Fix decoding of e-mail address strings in message headers (#1487068) - Fix handling of attachments when Content-Disposition is not inline nor attachment (#1487051) - 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 LIST-STATUS support in rcube_imap_generic class (RFC 5819) +- 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 ------------- diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 4540adc34..664251217 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -70,8 +70,8 @@ $rcmail_config['default_host'] = ''; // 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, diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index bea9d07c0..67d71f619 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -370,44 +370,95 @@ class rcube_imap_generic $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') { - $ipad = ''; - $opad = ''; + if ($type == 'CRAM-MD5') { + $ipad = ''; + $opad = ''; - // initialize ipad, opad - for ($i=0; $i<64; $i++) { - $ipad .= chr(0x36); - $opad .= chr(0x5C); - } + // initialize ipad, opad + for ($i=0; $i<64; $i++) { + $ipad .= chr(0x36); + $opad .= chr(0x5C); + } - // pad $pass so it's 64 bytes - $padLen = 64 - strlen($pass); - for ($i=0; $i<$padLen; $i++) { - $pass .= chr(0); - } + // pad $pass so it's 64 bytes + $padLen = 64 - strlen($pass); + for ($i=0; $i<$padLen; $i++) { + $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 hash + $hash = md5($this->_xor($pass, $opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($encChallenge)))); + $reply = base64_encode($user . ' ' . $hash); - // generate reply - $reply = base64_encode($user . ' ' . $hash); + // 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 (CRAM-MD5): $line"); + } + } + else { // PLAIN + $reply = base64_encode($user . chr(0) . $user . chr(0) . $pass); - // send result, get reply - $this->putLine($reply); - $line = $this->readLine(1024); + // 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"); + } + } + } - // process result - $result = $this->parseResult($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( @@ -422,9 +473,6 @@ class rcube_imap_generic return $this->fp; } - @fclose($this->fp); - $this->fp = false; - return $code; } @@ -634,44 +682,48 @@ class rcube_imap_generic } } - $orig_method = $auth_method; + $auth_methods = array(); + $result = null; + // check for supported auth methods if ($auth_method == 'CHECK') { - // check for supported auth methods if ($this->getCapability('AUTH=CRAM-MD5') || $this->getCapability('AUTH=CRAM_MD5')) { - $auth_method = 'AUTH'; + $auth_methods[] = 'AUTH'; } - else { - // default to plain text auth - $auth_method = 'PLAIN'; + 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 { + $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; - } - } + // 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) && $orig_method == 'CHECK') { - $auth_method = 'PLAIN'; + if (is_resource($result)) { + break; } } - if ($auth_method == 'PLAIN') { - // do plain text auth - $result = $this->login($user, $password); - } - + // Connected and authenticated if (is_resource($result)) { if ($this->prefs['force_caps']) { $this->clearCapability(); @@ -680,9 +732,13 @@ class rcube_imap_generic $this->logged = true; return true; - } else { - return false; - } + } + + // Close connection + @fclose($this->fp); + $this->fp = false; + + return false; } function connected() -- cgit v1.2.3