When try to login IMAP/SMTP/SIEVE but STARTTLS is required, force STARTTLS

This commit is contained in:
the-djmaze 2022-10-30 11:43:17 +01:00
parent cef870328c
commit a58276ff45
6 changed files with 145 additions and 140 deletions

View file

@ -56,11 +56,6 @@ class Config
*/ */
public static $CheckNewMessages = true; public static $CheckNewMessages = true;
/**
* @var bool
*/
public static $PreferStartTlsIfAutoDetect = true;
/** /**
* @var string * @var string
*/ */

View file

@ -11,6 +11,8 @@
namespace MailSo\Imap; namespace MailSo\Imap;
use MailSo\Net\Enumerations\ConnectionSecurityType;
/** /**
* @category MailSo * @category MailSo
* @package Imap * @package Imap
@ -93,15 +95,19 @@ class ImapClient extends \MailSo\Net\NetClient
$this->setCapabilities($this->getResponse('*')); $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->SendRequestGetResponse('STARTTLS');
$this->EnableCrypto(); $this->EnableCrypto();
$this->aCapabilityItems = null; $this->aCapabilityItems = null;
} } else {
else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType)
{
$this->writeLogException( $this->writeLogException(
new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'), new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'),
\LOG_ERR, true); \LOG_ERR, true);
@ -143,6 +149,10 @@ class ImapClient extends \MailSo\Net\NetClient
} }
} }
if (!$type) { 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: ' throw new \MailSo\RuntimeException('No supported SASL mechanism found, remote server wants: '
. \implode(', ', \array_filter($this->Capability() ?: [], function($var){ . \implode(', ', \array_filter($this->Capability() ?: [], function($var){
return \str_starts_with($var, 'AUTH='); return \str_starts_with($var, 'AUTH=');

View file

@ -46,12 +46,4 @@ abstract class ConnectionSecurityType
return self::SSL === $iResult; 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');
}
} }

View file

@ -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) public function EnableCrypto(bool $insecure = false)
{ {
$bError = true; $bError = true;

View file

@ -11,6 +11,8 @@
namespace MailSo\Sieve; namespace MailSo\Sieve;
use MailSo\Net\Enumerations\ConnectionSecurityType;
/** /**
* @category MailSo * @category MailSo
* @package Sieve * @package Sieve
@ -71,17 +73,21 @@ class ManageSieveClient extends \MailSo\Net\NetClient
$this->validateResponse($aResponse); $this->validateResponse($aResponse);
$this->parseStartupResponse($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->sendRequestWithCheck('STARTTLS');
$this->EnableCrypto(); $this->EnableCrypto();
$aResponse = $this->parseResponse(); $aResponse = $this->parseResponse();
$this->validateResponse($aResponse); $this->validateResponse($aResponse);
$this->parseStartupResponse($aResponse); $this->parseStartupResponse($aResponse);
} } else {
else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType)
{
$this->writeLogException( $this->writeLogException(
new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'), new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'),
\LOG_ERR, true); \LOG_ERR, true);
@ -106,8 +112,6 @@ class ManageSieveClient extends \MailSo\Net\NetClient
\LOG_ERR, true); \LOG_ERR, true);
} }
if ($this->IsSupported('SASL'))
{
$type = ''; $type = '';
\array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN'); \array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN');
foreach ($aCredentials['SASLMechanisms'] as $sasl_type) { foreach ($aCredentials['SASLMechanisms'] as $sasl_type) {
@ -117,6 +121,16 @@ class ManageSieveClient extends \MailSo\Net\NetClient
} }
} }
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 = \SnappyMail\SASL::factory($type);
$SASL->base64 = true; $SASL->base64 = true;
@ -187,13 +201,6 @@ class ManageSieveClient extends \MailSo\Net\NetClient
new \MailSo\Sieve\Exceptions\LoginBadMethodException, new \MailSo\Sieve\Exceptions\LoginBadMethodException,
\LOG_ERR, true); \LOG_ERR, true);
} }
}
else
{
$this->writeLogException(
new \MailSo\Sieve\Exceptions\LoginException,
\LOG_ERR, true);
}
$this->bIsLoggined = true; $this->bIsLoggined = true;

View file

@ -11,6 +11,8 @@
namespace MailSo\Smtp; namespace MailSo\Smtp;
use MailSo\Net\Enumerations\ConnectionSecurityType;
/** /**
* @category MailSo * @category MailSo
* @package Smtp * @package Smtp
@ -18,9 +20,9 @@ namespace MailSo\Smtp;
class SmtpClient extends \MailSo\Net\NetClient class SmtpClient extends \MailSo\Net\NetClient
{ {
/** /**
* @var bool * @var string
*/ */
private $bHelo = false; private $sEhlo = '';
/** /**
* @var bool * @var bool
@ -107,7 +109,26 @@ class SmtpClient extends \MailSo\Net\NetClient
$this->validateResponse(220); $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) { 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( $this->writeLogException(
new \MailSo\Smtp\Exceptions\LoginBadMethodException, new \MailSo\Smtp\Exceptions\LoginBadMethodException,
\LOG_NOTICE, true); \LOG_NOTICE, true);
@ -410,40 +435,11 @@ class SmtpClient extends \MailSo\Net\NetClient
$this->sendRequestWithCheck('QUIT', 221); $this->sendRequestWithCheck('QUIT', 221);
} }
$this->bHelo = false;
$this->bMail = false; $this->bMail = false;
$this->bRcpt = false; $this->bRcpt = false;
$this->bData = 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 \InvalidArgumentException
* @throws \MailSo\RuntimeException * @throws \MailSo\RuntimeException