diff options
Diffstat (limited to 'program/include/rcube_imap_generic.php')
-rw-r--r-- | program/include/rcube_imap_generic.php | 170 |
1 files changed, 113 insertions, 57 deletions
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() |