/** * Authenticate to the POP3 server. * * @param string $method POP3 login method. * * @throws Horde_Imap_Client_Exception */ protected function _tryLogin($method) { $username = $this->getParam('username'); $password = $this->getParam('password'); switch ($method) { case 'CRAM-MD5': case 'CRAM-SHA1': case 'CRAM-SHA256': // RFC 5034: CRAM-MD5 // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library $challenge = $this->_sendLine('AUTH ' . $method); $response = base64_encode($username . ' ' . hash_hmac(Horde_String::lower(substr($method, 5)), base64_decode(substr($challenge['resp'], 2)), $password, true)); $this->_sendLine($response, array('debug' => sprintf('[AUTH Response (username: %s)]', $username))); break; case 'DIGEST-MD5': // RFC 2831; Obsoleted by RFC 6331 $challenge = $this->_sendLine('AUTH DIGEST-MD5'); $response = base64_encode(new Horde_Imap_Client_Auth_DigestMD5($username, $password, base64_decode(substr($challenge['resp'], 2)), $this->getParam('hostspec'), 'pop3')); $sresponse = $this->_sendLine($response, array('debug' => sprintf('[AUTH Response (username: %s)]', $username))); if (stripos(base64_decode(substr($sresponse['resp'], 2)), 'rspauth=') === false) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Unexpected response from server when authenticating."), Horde_Imap_Client_Exception::SERVER_CONNECT); } /* POP3 doesn't use protocol's third step. */ $this->_sendLine(''); break; case 'LOGIN': // RFC 4616 (AUTH=PLAIN) & 5034 (POP3 SASL) $this->_sendLine('AUTH LOGIN'); $this->_sendLine(base64_encode($username)); $this->_sendLine(base64_encode($password), array('debug' => sprintf('[AUTH Password (username: %s)]', $username))); break; case 'PLAIN': // RFC 5034 $this->_sendLine('AUTH PLAIN ' . base64_encode(implode("", array($username, $username, $password))), array('debug' => sprintf('AUTH PLAIN [Auth Response (username: %s)]', $username))); break; case 'APOP': /* If UTF8 (+ USER) is active, and non-ASCII exists, need to apply * SASLprep to username/password. RFC 6856[2.2]. Reject if * UTF8 (+ USER) is not supported and 8-bit characters exist. */ if (Horde_Mime::is8bit($username) || Horde_Mime::is8bit($password)) { if (empty($this->_temp['utf8']) || !$this->_capability('UTF8', 'USER') || !class_exists('Horde_Stringprep')) { $error = true; } else { Horde_Stringprep::autoload(); $saslprep = new Znerol\Component\Stringprep\Profile\SASLprep(); try { $username = $saslprep->apply($username, 'UTF-8', Znerol\Compnonent\Stringprep\Profile::MODE_QUERY); $password = $saslprep->apply($password, 'UTF-8', Znerol\Compnonent\Stringprep\Profile::MODE_STORE); $error = false; } catch (Znerol\Component\Stringprep\ProfileException $e) { $error = true; } } if ($error) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } } // RFC 1939 [7] $this->_sendLine('APOP ' . $username . ' ' . hash('md5', $this->_temp['pop3timestamp'] . $password)); break; case 'USER': /* POP3 servers without UTF8 (+ USER) does not accept non-ASCII * in USER/PASS. RFC 6856[2.2] */ if ((empty($this->_temp['utf8']) || !$this->_capability('UTF8', 'USER')) && (Horde_Mime::is8bit($username) || Horde_Mime::is8bit($password))) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } // RFC 1939 [7] $this->_sendLine('USER ' . $username); $this->_sendLine('PASS ' . $password, array('debug' => 'PASS [Password]')); break; case 'SCRAM-SHA-1': $scram = new Horde_Imap_Client_Auth_Scram($username, $password, 'SHA1'); $c1 = $this->_sendLine('AUTH ' . $method . ' ' . base64_encode($scram->getClientFirstMessage())); $sr1 = base64_decode(substr($c1['resp'], 2)); if (!$scram->parseServerFirstMessage($sr1)) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } $c2 = $this->_sendLine(base64_encode($scram->getClientFinalMessage())); $sr2 = base64_decode(substr($c2['resp'], 2)); if (!$scram->parseServerFirstMessage($sr)) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); /* This means authentication passed, according to the server, * but the server signature is incorrect. This indicates that * server verification has failed. Immediately disconnect from * the server, since this is a possible security issue. */ $this->logout(); throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Server failed verification check."), Horde_Imap_Client_Exception::LOGIN_SERVER_VERIFICATION_FAILED); } $this->_sendLine(''); break; default: $e = new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Unknown authentication method: %s"), Horde_Imap_Client_Exception::SERVER_CONNECT); $e->messagePrintf(array($method)); throw $e; } }
/** * Authenticate to the IMAP server. * * @param string $method IMAP login method. * * @return Horde_Imap_Client_Interaction_Pipeline Pipeline object. * * @throws Horde_Imap_Client_Exception */ protected function _tryLogin($method) { $username = $this->getParam('username'); $password = $this->getParam('password'); switch ($method) { case 'CRAM-MD5': case 'CRAM-SHA1': case 'CRAM-SHA256': // RFC 2195: CRAM-MD5 // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library $args = array($username, Horde_String::lower(substr($method, 5)), $password); $cmd = $this->_command('AUTHENTICATE')->add(array($method, new Horde_Imap_Client_Interaction_Command_Continuation(function ($ob) use($args) { return new Horde_Imap_Client_Data_Format_List(base64_encode($args[0] . ' ' . hash_hmac($args[1], base64_decode($ob->token->current()), $args[2], false))); }))); $cmd->debug = array(null, sprintf('[AUTHENTICATE response (username: %s)]', $username)); break; case 'DIGEST-MD5': // RFC 2831/4422; obsoleted by RFC 6331 // Need $args because PHP 5.3 doesn't allow access to $this in // anonymous functions. $args = array($username, $password, $this->getParam('hostspec')); $cmd = $this->_command('AUTHENTICATE')->add(array($method, new Horde_Imap_Client_Interaction_Command_Continuation(function ($ob) use($args) { return new Horde_Imap_Client_Data_Format_List(base64_encode(new Horde_Imap_Client_Auth_DigestMD5($args[0], $args[1], base64_decode($ob->token->current()), $args[2], 'imap'))); }), new Horde_Imap_Client_Interaction_Command_Continuation(function ($ob) { if (strpos(base64_decode($ob->token->current()), 'rspauth=') === false) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Unexpected response from server when authenticating."), Horde_Imap_Client_Exception::SERVER_CONNECT); } return new Horde_Imap_Client_Data_Format_List(); }))); $cmd->debug = array(null, sprintf('[AUTHENTICATE Response (username: %s)]', $username), null); break; case 'LOGIN': /* See, e.g., RFC 6855 [5] - LOGIN command does not support * non-ASCII characters. If we reach this point, treat as an * authentication failure. */ try { $username = new Horde_Imap_Client_Data_Format_Astring($username); $password = new Horde_Imap_Client_Data_Format_Astring($password); } catch (Horde_Imap_Client_Data_Format_Exception $e) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } $cmd = $this->_command('LOGIN')->add(array($username, $password)); $cmd->debug = array(sprintf('LOGIN %s [PASSWORD]', $username)); break; case 'PLAIN': // RFC 2595/4616 - PLAIN SASL mechanism $cmd = $this->_authInitialResponse($method, base64_encode(implode("", array($username, $username, $password))), $username); break; case 'SCRAM-SHA-1': $scram = new Horde_Imap_Client_Auth_Scram($username, $password, 'SHA1'); $cmd = $this->_authInitialResponse($method, base64_encode($scram->getClientFirstMessage())); $cmd->add(new Horde_Imap_Client_Interaction_Command_Continuation(function ($ob) use($scram) { $sr1 = base64_decode($ob->token->current()); return new Horde_Imap_Client_Data_Format_List($scram->parseServerFirstMessage($sr1) ? base64_encode($scram->getClientFinalMessage()) : '*'); })); $self = $this; $cmd->add(new Horde_Imap_Client_Interaction_Command_Continuation(function ($ob) use($scram, $self) { $sr2 = base64_decode($ob->token->current()); if (!$scram->parseServerFinalMessage($sr2)) { /* This means authentication passed, according to the * server, but the server signature is incorrect. * This indicates that server verification has failed. * Immediately disconnect from the server, since this * is a possible security issue. */ $self->logout(); throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Server failed verification check."), Horde_Imap_Client_Exception::LOGIN_SERVER_VERIFICATION_FAILED); } return new Horde_Imap_Client_Data_Format_List(); })); break; case 'XOAUTH2': // Google XOAUTH2 $cmd = $this->_authInitialResponse($method, $this->getParam('xoauth2_token')); /* This is an optional command continuation. XOAUTH2 will return * error information in continuation response. */ $error_continuation = new Horde_Imap_Client_Interaction_Command_Continuation(function ($ob) { return new Horde_Imap_Client_Data_Format_List(); }); $error_continuation->optional = true; $cmd->add($error_continuation); break; default: $e = new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Unknown authentication method: %s"), Horde_Imap_Client_Exception::SERVER_CONNECT); $e->messagePrintf(array($method)); throw $e; } return $this->_sendCmd($this->_pipeline($cmd)); }