summaryrefslogtreecommitdiff
path: root/program/include
diff options
context:
space:
mode:
Diffstat (limited to 'program/include')
-rw-r--r--program/include/rcube_imap_generic.php122
1 files changed, 84 insertions, 38 deletions
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index 4211cca44..62a3d8280 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -371,56 +371,99 @@ class rcube_imap_generic
}
/**
- * 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') {
- $ipad = '';
- $opad = '';
-
- // 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);
+ 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 CRAM-MD5");
+ $this->putLine($this->next_tag() . " AUTHENTICATE $type");
$line = trim($this->readLine(1024));
if ($line[0] == '+') {
- $challenge = substr($line,2);
+ $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);
+ if ($type == 'CRAM-MD5') {
+ // RFC2195: CRAM-MD5
+ $ipad = '';
+ $opad = '';
+
+ // 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);
+ }
+
+ // 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 $this->parseResult($line);
+ }
+
+ // 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;
+ }
+
+ $this->putLine('');
+ }
- // 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
- // proxy authentication
+ // proxy authorization
if (!empty($this->prefs['auth_cid'])) {
$authc = $this->prefs['auth_cid'];
$pass = $this->prefs['auth_pw'];
@@ -440,22 +483,22 @@ class rcube_imap_generic
$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 @@ class rcube_imap_generic
// 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 @@ class rcube_imap_generic
}
}
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);