mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +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;
|
public static $CheckNewMessages = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public static $PreferStartTlsIfAutoDetect = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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=');
|
||||||
|
|
|
||||||
|
|
@ -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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,95 +112,96 @@ class ManageSieveClient extends \MailSo\Net\NetClient
|
||||||
\LOG_ERR, true);
|
\LOG_ERR, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->IsSupported('SASL'))
|
$type = '';
|
||||||
{
|
\array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN');
|
||||||
$type = '';
|
foreach ($aCredentials['SASLMechanisms'] as $sasl_type) {
|
||||||
\array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN');
|
if ($this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) {
|
||||||
foreach ($aCredentials['SASLMechanisms'] as $sasl_type) {
|
$type = $sasl_type;
|
||||||
if ($this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) {
|
break;
|
||||||
$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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
if (!$type) {
|
||||||
|
if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) {
|
||||||
|
$this->StartTLS();
|
||||||
|
return $this->Login($aCredentials);
|
||||||
|
}
|
||||||
$this->writeLogException(
|
$this->writeLogException(
|
||||||
new \MailSo\Sieve\Exceptions\LoginException,
|
new \MailSo\Sieve\Exceptions\LoginException,
|
||||||
\LOG_ERR, true);
|
\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;
|
$this->bIsLoggined = true;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue