- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)

release-0.6
alecpl 14 years ago
parent ee72e4d4ee
commit 4dd4172421

@ -46,6 +46,7 @@ CHANGELOG Roundcube Webmail
- 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
-------------

@ -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), "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,

@ -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);

Loading…
Cancel
Save