Improved SASL

This commit is contained in:
djmaze 2021-04-01 09:48:42 +02:00
parent e96ced3bde
commit fcc219cc07
5 changed files with 71 additions and 51 deletions

View file

@ -180,56 +180,40 @@ class ImapClient extends \MailSo\Net\NetClient
try try
{ {
if ('CRAM-MD5' === $type) if (0 === \strpos($type, 'SCRAM-SHA-'))
{ {
$oContinuationResponse = $this->SendRequestGetResponse('AUTHENTICATE', array($type))->getLast(); $sAuthzid = $this->getResponseValue($this->SendRequestGetResponse('AUTHENTICATE', array($type)), Enumerations\ResponseType::CONTINUATION);
if ($oContinuationResponse && Enumerations\ResponseType::CONTINUATION === $oContinuationResponse->ResponseType) $this->sendRaw($SASL->authenticate($sLogin, $sPassword/*, $sAuthzid*/));
{ $sChallenge = $SASL->challenge($this->getResponseValue($this->getResponse(), Enumerations\ResponseType::CONTINUATION));
$sTicket = $oContinuationResponse->ResponseList[1] ?? null; if ($this->oLogger) {
if ($sTicket) $this->oLogger->AddSecret($sChallenge);
{
$sToken = $SASL->authenticate($sLogin, $sPassword, $sTicket);
$this->oLogger->Write('ticket: '.\base64_decode($sTicket));
if ($this->oLogger)
{
$this->oLogger->AddSecret($sToken);
}
$this->sendRaw($sToken, true, '*******');
$this->getResponse();
}
else
{
$this->writeLogException(
new Exceptions\LoginException,
\MailSo\Log\Enumerations\Type::NOTICE, true);
}
} }
else $this->sendRaw($sChallenge, true, '*******');
{ $sSignature = $this->getResponseValue($this->getResponse());
$this->writeLogException( $SASL->verify($sSignature);
new Exceptions\LoginException, }
\MailSo\Log\Enumerations\Type::NOTICE, true); else if ('CRAM-MD5' === $type)
{
$sChallenge = $this->getResponseValue($this->SendRequestGetResponse('AUTHENTICATE', array($type)), Enumerations\ResponseType::CONTINUATION);
$this->oLogger->Write('challenge: '.\base64_decode($sChallenge));
$sAuth = $SASL->authenticate($sLogin, $sPassword, $sChallenge);
if ($this->oLogger) {
$this->oLogger->AddSecret($sAuth);
} }
$this->sendRaw($sAuth, true, '*******');
$this->getResponse();
} }
else if ('PLAIN' === $type) else if ('PLAIN' === $type)
{ {
$sToken = $SASL->authenticate($sLogin, $sPassword); $sAuth = $SASL->authenticate($sLogin, $sPassword);
if ($this->oLogger) if ($this->oLogger) {
{ $this->oLogger->AddSecret($sAuth);
$this->oLogger->AddSecret($sToken);
} }
if ($this->IsSupported('SASL-IR')) {
if ($this->IsSupported('SASL-IR')) $this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN', $sAuth));
{ } else {
$this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN', $sToken));
}
else
{
$this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN')); $this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN'));
$this->sendRaw($sToken, true, '*******'); $this->sendRaw($sAuth, true, '*******');
$this->getResponse(); $this->getResponse();
} }
} }
@ -1025,6 +1009,23 @@ class ImapClient extends \MailSo\Net\NetClient
return $this->getResponse(); return $this->getResponse();
} }
private function getResponseValue(ResponseCollection $oResponseCollection, int $type = 0) : string
{
$oResponse = $oResponseCollection->getLast();
if ($oResponse && (!$type || $type === $oResponse->ResponseType)) {
$sResult = $oResponse->ResponseList[1] ?? null;
if ($sResult) {
return $sResult;
}
$this->writeLogException(
new Exceptions\LoginException,
\MailSo\Log\Enumerations\Type::NOTICE, true);
}
$this->writeLogException(
new Exceptions\LoginException,
\MailSo\Log\Enumerations\Type::NOTICE, true);
}
public function GetLastResponse() : ResponseCollection public function GetLastResponse() : ResponseCollection
{ {
return $this->oLastResponse; return $this->oLastResponse;

View file

@ -120,12 +120,31 @@ class ManageSieveClient extends \MailSo\Net\NetClient
if ($this->IsSupported('SASL')) if ($this->IsSupported('SASL'))
{ {
// $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']);
$type = '';
$types = [
// 'SCRAM-SHA-256' => 1, // !$encrypted
// 'SCRAM-SHA-1' => 1, // !$encrypted
// 'CRAM-MD5' => 1, // $encrypted
'PLAIN' => 1, // $encrypted
'LOGIN' => 1 // $encrypted
];
foreach ($types as $sasl_type => $active) {
if ($active && $this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) {
$type = $sasl_type;
break;
}
}
$SASL = \SnappyMail\SASL::factory($type);
$SASL->base64 = true;
$bAuth = false; $bAuth = false;
try try
{ {
if ($this->IsAuthSupported('PLAIN')) if ('PLAIN' === $type)
{ {
$sAuth = \base64_encode($sLoginAuthKey."\0".$sLogin."\0".$sPassword); $sAuth = $SASL->authenticate($sLogin, $sPassword, $sLoginAuthKey);
if ($this->__USE_INITIAL_AUTH_PLAIN_COMMAND) if ($this->__USE_INITIAL_AUTH_PLAIN_COMMAND)
{ {
@ -142,10 +161,10 @@ class ManageSieveClient extends \MailSo\Net\NetClient
$this->parseStartupResponse($aResponse); $this->parseStartupResponse($aResponse);
$bAuth = true; $bAuth = true;
} }
else if ($this->IsAuthSupported('LOGIN')) else if ('LOGIN' === $type)
{ {
$sLogin = \base64_encode($sLogin); $sLogin = $SASL->authenticate($sLogin, $sPassword);
$sPassword = \base64_encode($sPassword); $sPassword = $SASL->challenge('');
$this->sendRequest('AUTHENTICATE "LOGIN"'); $this->sendRequest('AUTHENTICATE "LOGIN"');
$this->sendRequest('{'.\strlen($sLogin).'+}'); $this->sendRequest('{'.\strlen($sLogin).'+}');

View file

@ -7,9 +7,9 @@ abstract class SASL
public public
$base64 = false; $base64 = false;
abstract public function authenticate(string $authcid, string $passphrase, ?string $authzid = null); abstract public function authenticate(string $authcid, string $passphrase, ?string $authzid = null) : string;
public function challenge(string $challenge) : string public function challenge(string $challenge) : ?string
{ {
return null; return null;
} }

View file

@ -16,7 +16,7 @@ class Login extends \SnappyMail\SASL
return $this->encode($username); return $this->encode($username);
} }
public function challenge(string $challenge) : string public function challenge(string $challenge) : ?string
{ {
if ($challenge && 'Password:' !== $this->decode($challenge)) { if ($challenge && 'Password:' !== $this->decode($challenge)) {
throw new \Exception("invalid response: {$challenge}"); throw new \Exception("invalid response: {$challenge}");

View file

@ -42,7 +42,7 @@ class Scram extends \SnappyMail\SASL
return $this->encode($this->gs2_header . $this->auth_message); return $this->encode($this->gs2_header . $this->auth_message);
} }
public function challenge(string $challenge) : string public function challenge(string $challenge) : ?string
{ {
$challenge = $this->decode($challenge); $challenge = $this->decode($challenge);
$values = static::parseMessage($challenge); $values = static::parseMessage($challenge);