mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-08 22:18:28 +03:00
When try to login IMAP/SMTP/SIEVE but STARTTLS is required, force STARTTLS
This commit is contained in:
parent
cef870328c
commit
a58276ff45
6 changed files with 145 additions and 140 deletions
|
|
@ -56,11 +56,6 @@ class Config
|
|||
*/
|
||||
public static $CheckNewMessages = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public static $PreferStartTlsIfAutoDetect = true;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace MailSo\Imap;
|
||||
|
||||
use MailSo\Net\Enumerations\ConnectionSecurityType;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Imap
|
||||
|
|
@ -93,15 +95,19 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
|
||||
$this->setCapabilities($this->getResponse('*'));
|
||||
|
||||
if ($this->IsSupported('STARTTLS') && \MailSo\Net\Enumerations\ConnectionSecurityType::UseStartTLS($this->iSecurityType))
|
||||
{
|
||||
if (ConnectionSecurityType::STARTTLS === $this->iSecurityType
|
||||
|| (ConnectionSecurityType::AUTO_DETECT === $this->iSecurityType && $this->IsSupported('STARTTLS'))) {
|
||||
$this->StartTLS();
|
||||
}
|
||||
}
|
||||
|
||||
private function StartTLS() : void
|
||||
{
|
||||
if ($this->IsSupported('STARTTLS')) {
|
||||
$this->SendRequestGetResponse('STARTTLS');
|
||||
$this->EnableCrypto();
|
||||
|
||||
$this->aCapabilityItems = null;
|
||||
}
|
||||
else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType)
|
||||
{
|
||||
} else {
|
||||
$this->writeLogException(
|
||||
new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'),
|
||||
\LOG_ERR, true);
|
||||
|
|
@ -143,6 +149,10 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
}
|
||||
}
|
||||
if (!$type) {
|
||||
if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) {
|
||||
$this->StartTLS();
|
||||
return $this->Login($aCredentials);
|
||||
}
|
||||
throw new \MailSo\RuntimeException('No supported SASL mechanism found, remote server wants: '
|
||||
. \implode(', ', \array_filter($this->Capability() ?: [], function($var){
|
||||
return \str_starts_with($var, 'AUTH=');
|
||||
|
|
|
|||
|
|
@ -46,12 +46,4 @@ abstract class ConnectionSecurityType
|
|||
|
||||
return self::SSL === $iResult;
|
||||
}
|
||||
|
||||
public static function UseStartTLS(int $iSecurityType, bool $bHasSupportedAuth = true) : bool
|
||||
{
|
||||
return
|
||||
(self::STARTTLS === $iSecurityType
|
||||
|| (self::AUTO_DETECT === $iSecurityType && (!$bHasSupportedAuth || \MailSo\Config::$PreferStartTlsIfAutoDetect)))
|
||||
&& \defined('STREAM_CRYPTO_METHOD_TLS_CLIENT') && \MailSo\Base\Utils::FunctionCallable('stream_socket_enable_crypto');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,6 +209,11 @@ abstract class NetClient
|
|||
}
|
||||
}
|
||||
|
||||
public function Encrypted() : bool
|
||||
{
|
||||
return $this->rConnect && !empty(\stream_get_meta_data($this->rConnect)['crypto']);
|
||||
}
|
||||
|
||||
public function EnableCrypto(bool $insecure = false)
|
||||
{
|
||||
$bError = true;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace MailSo\Sieve;
|
||||
|
||||
use MailSo\Net\Enumerations\ConnectionSecurityType;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Sieve
|
||||
|
|
@ -71,17 +73,21 @@ class ManageSieveClient extends \MailSo\Net\NetClient
|
|||
$this->validateResponse($aResponse);
|
||||
$this->parseStartupResponse($aResponse);
|
||||
|
||||
if ($this->IsSupported('STARTTLS') && \MailSo\Net\Enumerations\ConnectionSecurityType::UseStartTLS($this->iSecurityType))
|
||||
{
|
||||
if (ConnectionSecurityType::STARTTLS === $this->iSecurityType
|
||||
|| (ConnectionSecurityType::AUTO_DETECT === $this->iSecurityType && $this->IsSupported('STARTTLS'))) {
|
||||
$this->StartTLS();
|
||||
}
|
||||
}
|
||||
|
||||
private function StartTLS() : void
|
||||
{
|
||||
if ($this->IsSupported('STARTTLS')) {
|
||||
$this->sendRequestWithCheck('STARTTLS');
|
||||
$this->EnableCrypto();
|
||||
|
||||
$aResponse = $this->parseResponse();
|
||||
$this->validateResponse($aResponse);
|
||||
$this->parseStartupResponse($aResponse);
|
||||
}
|
||||
else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType)
|
||||
{
|
||||
} else {
|
||||
$this->writeLogException(
|
||||
new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'),
|
||||
\LOG_ERR, true);
|
||||
|
|
@ -106,95 +112,96 @@ class ManageSieveClient extends \MailSo\Net\NetClient
|
|||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
if ($this->IsSupported('SASL'))
|
||||
{
|
||||
$type = '';
|
||||
\array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN');
|
||||
foreach ($aCredentials['SASLMechanisms'] as $sasl_type) {
|
||||
if ($this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) {
|
||||
$type = $sasl_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$SASL = \SnappyMail\SASL::factory($type);
|
||||
$SASL->base64 = true;
|
||||
|
||||
$bAuth = false;
|
||||
try
|
||||
{
|
||||
if (0 === \strpos($type, 'SCRAM-'))
|
||||
{
|
||||
/*
|
||||
$sAuthzid = $this->getResponseValue($this->SendRequestGetResponse('AUTHENTICATE', array($type)), \MailSo\Imap\Enumerations\ResponseType::CONTINUATION);
|
||||
$this->sendRaw($SASL->authenticate($sLogin, $sPassword/*, $sAuthzid* /), true);
|
||||
$sChallenge = $SASL->challenge($this->getResponseValue($this->getResponse(), \MailSo\Imap\Enumerations\ResponseType::CONTINUATION));
|
||||
if ($this->oLogger) {
|
||||
$this->oLogger->AddSecret($sChallenge);
|
||||
}
|
||||
$this->sendRaw($sChallenge, true, '*******');
|
||||
$oResponse = $this->getResponse();
|
||||
$SASL->verify($this->getResponseValue($oResponse));
|
||||
*/
|
||||
}
|
||||
else if ('PLAIN' === $type || 'OAUTHBEARER' === $type || 'XOAUTH2' === $type)
|
||||
{
|
||||
$sAuth = $SASL->authenticate($sLogin, $sPassword, $sLoginAuthKey);
|
||||
|
||||
if ($aCredentials['InitialAuthPlain'])
|
||||
{
|
||||
$this->sendRaw("AUTHENTICATE \"{$type}\" \"{$sAuth}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sendRaw("AUTHENTICATE \"{$type}\" {".\strlen($sAuth).'+}');
|
||||
$this->sendRaw($sAuth);
|
||||
}
|
||||
|
||||
$aResponse = $this->parseResponse();
|
||||
$this->validateResponse($aResponse);
|
||||
$this->parseStartupResponse($aResponse);
|
||||
$bAuth = true;
|
||||
}
|
||||
else if ('LOGIN' === $type)
|
||||
{
|
||||
$sLogin = $SASL->authenticate($sLogin, $sPassword);
|
||||
$sPassword = $SASL->challenge('');
|
||||
|
||||
$this->sendRaw('AUTHENTICATE "LOGIN"');
|
||||
$this->sendRaw('{'.\strlen($sLogin).'+}');
|
||||
$this->sendRaw($sLogin);
|
||||
$this->sendRaw('{'.\strlen($sPassword).'+}');
|
||||
$this->sendRaw($sPassword);
|
||||
|
||||
$aResponse = $this->parseResponse();
|
||||
$this->validateResponse($aResponse);
|
||||
$this->parseStartupResponse($aResponse);
|
||||
$bAuth = true;
|
||||
}
|
||||
}
|
||||
catch (\MailSo\Sieve\Exceptions\NegativeResponseException $oException)
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Sieve\Exceptions\LoginBadCredentialsException(
|
||||
$oException->GetResponses(), '', 0, $oException),
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
if (!$bAuth)
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Sieve\Exceptions\LoginBadMethodException,
|
||||
\LOG_ERR, true);
|
||||
$type = '';
|
||||
\array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN');
|
||||
foreach ($aCredentials['SASLMechanisms'] as $sasl_type) {
|
||||
if ($this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) {
|
||||
$type = $sasl_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (!$type) {
|
||||
if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) {
|
||||
$this->StartTLS();
|
||||
return $this->Login($aCredentials);
|
||||
}
|
||||
$this->writeLogException(
|
||||
new \MailSo\Sieve\Exceptions\LoginException,
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
$SASL = \SnappyMail\SASL::factory($type);
|
||||
$SASL->base64 = true;
|
||||
|
||||
$bAuth = false;
|
||||
try
|
||||
{
|
||||
if (0 === \strpos($type, 'SCRAM-'))
|
||||
{
|
||||
/*
|
||||
$sAuthzid = $this->getResponseValue($this->SendRequestGetResponse('AUTHENTICATE', array($type)), \MailSo\Imap\Enumerations\ResponseType::CONTINUATION);
|
||||
$this->sendRaw($SASL->authenticate($sLogin, $sPassword/*, $sAuthzid* /), true);
|
||||
$sChallenge = $SASL->challenge($this->getResponseValue($this->getResponse(), \MailSo\Imap\Enumerations\ResponseType::CONTINUATION));
|
||||
if ($this->oLogger) {
|
||||
$this->oLogger->AddSecret($sChallenge);
|
||||
}
|
||||
$this->sendRaw($sChallenge, true, '*******');
|
||||
$oResponse = $this->getResponse();
|
||||
$SASL->verify($this->getResponseValue($oResponse));
|
||||
*/
|
||||
}
|
||||
else if ('PLAIN' === $type || 'OAUTHBEARER' === $type || 'XOAUTH2' === $type)
|
||||
{
|
||||
$sAuth = $SASL->authenticate($sLogin, $sPassword, $sLoginAuthKey);
|
||||
|
||||
if ($aCredentials['InitialAuthPlain'])
|
||||
{
|
||||
$this->sendRaw("AUTHENTICATE \"{$type}\" \"{$sAuth}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sendRaw("AUTHENTICATE \"{$type}\" {".\strlen($sAuth).'+}');
|
||||
$this->sendRaw($sAuth);
|
||||
}
|
||||
|
||||
$aResponse = $this->parseResponse();
|
||||
$this->validateResponse($aResponse);
|
||||
$this->parseStartupResponse($aResponse);
|
||||
$bAuth = true;
|
||||
}
|
||||
else if ('LOGIN' === $type)
|
||||
{
|
||||
$sLogin = $SASL->authenticate($sLogin, $sPassword);
|
||||
$sPassword = $SASL->challenge('');
|
||||
|
||||
$this->sendRaw('AUTHENTICATE "LOGIN"');
|
||||
$this->sendRaw('{'.\strlen($sLogin).'+}');
|
||||
$this->sendRaw($sLogin);
|
||||
$this->sendRaw('{'.\strlen($sPassword).'+}');
|
||||
$this->sendRaw($sPassword);
|
||||
|
||||
$aResponse = $this->parseResponse();
|
||||
$this->validateResponse($aResponse);
|
||||
$this->parseStartupResponse($aResponse);
|
||||
$bAuth = true;
|
||||
}
|
||||
}
|
||||
catch (\MailSo\Sieve\Exceptions\NegativeResponseException $oException)
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Sieve\Exceptions\LoginBadCredentialsException(
|
||||
$oException->GetResponses(), '', 0, $oException),
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
if (!$bAuth)
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Sieve\Exceptions\LoginBadMethodException,
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
$this->bIsLoggined = true;
|
||||
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace MailSo\Smtp;
|
||||
|
||||
use MailSo\Net\Enumerations\ConnectionSecurityType;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Smtp
|
||||
|
|
@ -18,9 +20,9 @@ namespace MailSo\Smtp;
|
|||
class SmtpClient extends \MailSo\Net\NetClient
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
* @var string
|
||||
*/
|
||||
private $bHelo = false;
|
||||
private $sEhlo = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
|
@ -107,7 +109,26 @@ class SmtpClient extends \MailSo\Net\NetClient
|
|||
|
||||
$this->validateResponse(220);
|
||||
|
||||
$this->preLoginStartTLSAndEhloProcess($sEhloHost);
|
||||
$this->ehloOrHelo($sEhloHost);
|
||||
$this->sEhlo = $sEhloHost;
|
||||
|
||||
if (ConnectionSecurityType::STARTTLS === $this->iSecurityType
|
||||
|| (ConnectionSecurityType::AUTO_DETECT === $this->iSecurityType && $this->IsSupported('STARTTLS'))) {
|
||||
$this->StartTLS();
|
||||
}
|
||||
}
|
||||
|
||||
private function StartTLS() : void
|
||||
{
|
||||
if ($this->IsSupported('STARTTLS')) {
|
||||
$this->sendRequestWithCheck('STARTTLS', 220);
|
||||
$this->EnableCrypto();
|
||||
$this->ehloOrHelo($this->sEhlo);
|
||||
} else {
|
||||
$this->writeLogException(
|
||||
new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'),
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -133,7 +154,11 @@ class SmtpClient extends \MailSo\Net\NetClient
|
|||
}
|
||||
|
||||
if (!$type) {
|
||||
\trigger_error("SMTP {$this->GetConnectedHost()} no supported AUTH options. Disable login" . ($this->IsSupported('STARTTLS') ? ' or try with STARTTLS' : ''));
|
||||
if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) {
|
||||
$this->StartTLS();
|
||||
return $this->Login($aCredentials);
|
||||
}
|
||||
\trigger_error("SMTP {$this->GetConnectedHost()} no supported AUTH options. Disable login");
|
||||
$this->writeLogException(
|
||||
new \MailSo\Smtp\Exceptions\LoginBadMethodException,
|
||||
\LOG_NOTICE, true);
|
||||
|
|
@ -410,40 +435,11 @@ class SmtpClient extends \MailSo\Net\NetClient
|
|||
$this->sendRequestWithCheck('QUIT', 221);
|
||||
}
|
||||
|
||||
$this->bHelo = false;
|
||||
$this->bMail = false;
|
||||
$this->bRcpt = false;
|
||||
$this->bData = false;
|
||||
}
|
||||
|
||||
private function preLoginStartTLSAndEhloProcess(string $sEhloHost) : void
|
||||
{
|
||||
if ($this->bHelo)
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\RuntimeException('Cannot issue EHLO/HELO to existing session'),
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
$this->ehloOrHelo($sEhloHost);
|
||||
|
||||
if ($this->IsSupported('STARTTLS') && \MailSo\Net\Enumerations\ConnectionSecurityType::UseStartTLS($this->iSecurityType, $this->HasSupportedAuth()))
|
||||
{
|
||||
$this->sendRequestWithCheck('STARTTLS', 220);
|
||||
$this->EnableCrypto();
|
||||
|
||||
$this->ehloOrHelo($sEhloHost);
|
||||
}
|
||||
else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType)
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'),
|
||||
\LOG_ERR, true);
|
||||
}
|
||||
|
||||
$this->bHelo = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \MailSo\RuntimeException
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue