[+] TLS pinning

[*] ISocket::Shutdown(*bool bNow*), allowing for flush of the send channel when false
[*] Fix StartRead and StartWrite after shutdown (NT)
[*] Amended dead-lock
This commit is contained in:
Reece Wilson 2022-11-18 04:15:05 +00:00
parent f86665fd36
commit d9dd1182b9
17 changed files with 217 additions and 48 deletions

View File

@ -14,7 +14,7 @@ namespace Aurora::IO::Net
virtual const NetError &GetError() = 0;
virtual const NetEndpoint &GetLocalEndpoint() = 0;
virtual void Shutdown() = 0;
virtual void Shutdown(bool bNow = true) = 0;
virtual void Destroy() = 0;
virtual AuUInt ToPlatformHandle() = 0;

View File

@ -12,7 +12,8 @@ namespace Aurora::IO::TLS
struct ICertificateChain
{
virtual AuUInt32 GetCertificateCount() = 0;
virtual AuMemoryViewRead GetCertificate(AuUInt32 idx) = 0;
virtual AuSPtr<AuMemoryViewRead> GetCertificate(AuUInt32 idx) = 0;
virtual Crypto::X509::DecodedCertificate GetCertificateDetails(AuUInt32 idx) = 0;
};
AUKN_SYM AuSPtr<ICertificateChain> ChainFromOne(const AuMemoryViewRead &read);

View File

@ -351,35 +351,37 @@ namespace Aurora::Crypto::X509
return AuTime::FromCivilTime(tm, true);
}
void DecodeInternal(const mbedtls_x509_crt &crt, DecodedCertificate &out)
{
auto &issuer = crt.issuer;
auto &subject = crt.subject;
FindCommonNames(issuer, out.issuer);
FindCommonNames(subject, out.subject);
out.validity.issued = ConvertTime(crt.valid_from);
out.validity.expire = ConvertTime(crt.valid_to);
x509_get_ca_id((mbedtls_x509_crt *)&crt, out.issuer.id);
x509_get_subject_id((mbedtls_x509_crt *)&crt, out.subject.id);
out.serialNumber.resize(crt.serial.len);
memcpy(out.serialNumber.data(), crt.serial.p, out.serialNumber.size());
out.algorithmOid.resize(crt.sig_oid.len);
memcpy(out.algorithmOid.data(), crt.sig_oid.p, out.algorithmOid.size());
AuList<AuString> oscp;
x509_get_aia((mbedtls_x509_crt *)&crt, oscp, out.AIAs);
}
AUKN_SYM bool Decode(const Certificate &der, DecodedCertificate &out)
{
bool failed = false;
return ParseCert(der,
[&](mbedtls_x509_crt &crt)
{
auto &issuer = crt.issuer;
auto &subject = crt.subject;
FindCommonNames(issuer, out.issuer);
FindCommonNames(subject, out.subject);
out.validity.issued = ConvertTime(crt.valid_from);
out.validity.expire = ConvertTime(crt.valid_to);
x509_get_ca_id(&crt, out.issuer.id);
x509_get_subject_id(&crt, out.subject.id);
out.serialNumber.resize(crt.serial.len);
memcpy(out.serialNumber.data(), crt.serial.p, out.serialNumber.size());
out.algorithmOid.resize(crt.sig_oid.len);
memcpy(out.algorithmOid.data(), crt.sig_oid.p, out.algorithmOid.size());
AuList<AuString> oscp;
x509_get_aia(&crt, oscp, out.AIAs);
}) && !failed;
DecodeInternal(crt, out);
});
}
static bool IsHighRiskStateIssuer(const mbedtls_x509_crt &ca)

View File

@ -7,3 +7,12 @@
***/
#pragma once
#include "../Crypto.hpp"
#include <mbedtls/x509_crt.h>
namespace Aurora::Crypto::X509
{
struct DecodedCertificate;
void DecodeInternal(const mbedtls_x509_crt &crt, DecodedCertificate &out);
}

View File

@ -210,9 +210,24 @@ namespace Aurora::IO::Net
return ::ioctlsocket(this->osHandle_, FIONBIO, &iMode) == 0;
}
void Socket::Shutdown()
void Socket::Shutdown(bool bNow)
{
this->SendEnd();
::shutdown(this->osHandle_, SD_BOTH);
if (bNow)
{
this->SendEnd();
::shutdown(this->osHandle_, SD_BOTH);
}
else
{
if (!this->socketChannel_.outputChannel.AsWritableByteBuffer()->RemainingBytes())
{
this->Shutdown(true);
}
else
{
this->socketChannel_.outputChannel.bShutdownOnComplete = true;
this->socketChannel_.ScheduleOutOfFrameWrite();
}
}
}
}

View File

@ -58,7 +58,7 @@ namespace Aurora::IO::Net
virtual bool MakeNonblocking() override;
virtual void Shutdown() override;
virtual void Shutdown(bool bNow) override;
virtual void CloseSocket() override;
};

View File

@ -106,6 +106,8 @@ namespace Aurora::IO::Net
AuUInt endpointSize_ {};
bool bHasFinalized_ {};
bool bHasEnded {};
protected:
AuUInt osHandle_;
@ -124,7 +126,6 @@ namespace Aurora::IO::Net
NetError error_;
bool bHasEnded {};
bool bHasErrored_ {};
bool bHasConnected_ {};
};

View File

@ -141,7 +141,20 @@ namespace Aurora::IO::Net
void SocketChannelOutput::WriteTick()
{
AU_LOCK_GUARD(this->lock_);
bool bShouldShutdown {};
{
AU_LOCK_GUARD(this->lock_);
bShutdownOnComplete = WriteTickLocked();
}
if (bShouldShutdown)
{
this->pParent_->Shutdown(true);
}
}
bool SocketChannelOutput::WriteTickLocked()
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
auto pHackTransaction =
@ -156,7 +169,7 @@ namespace Aurora::IO::Net
// IsComplete?
if (!pHackTransaction->bLatch)
{
return;
return false;
}
}
@ -168,7 +181,7 @@ namespace Aurora::IO::Net
{
SysPushErrorIO("Couldn't begin wait");
this->pParent_->SendErrorBeginShutdown({});
return;
return false;
}
if (!this->pNetWriteTransaction_->StartWrite(0, pFrameToSend))
@ -176,13 +189,19 @@ namespace Aurora::IO::Net
this->pParent_->ToWorkerEx()->DecrementIOEventTaskCounter();
SysPushErrorIO("Couldn't dispatch the to-send frame");
this->pParent_->SendErrorBeginShutdown({});
return;
return false;
}
}
else
{
this->pNetWriteTransaction_->SetCallback({});
if (this->bShutdownOnComplete)
{
return true;
}
}
return false;
}
void SocketChannelOutput::OnEndOfReadTick()

View File

@ -27,11 +27,13 @@ namespace Aurora::IO::Net
// Write outbound shit
void SchedWriteTick();
void SendIfData();
bool WriteTickLocked();
void WriteTick();
void OnEndOfReadTick();
void OnAsyncFileOpFinished(AuUInt64 offset, AuUInt32 length) override;
bool bShutdownOnComplete {};
private:
SocketBase * pParent_;
AuSPtr<IAsyncTransaction> pNetWriteTransaction_;

View File

@ -174,13 +174,14 @@ namespace Aurora::IO::Net
{
}
void SocketServer::Shutdown()
void SocketServer::Shutdown(bool bNow)
{
Socket::Shutdown(bNow);
}
void SocketServer::Destroy()
{
Socket::Destroy();
}
void SocketServer::ScheduleAcceptTick()

View File

@ -35,7 +35,7 @@ namespace Aurora::IO::Net
virtual void FinishConstructAsync() override;
virtual void Shutdown() override;
virtual void Shutdown(bool bNow) override;
virtual void Destroy() override;
void ScheduleAcceptTick();

View File

@ -59,6 +59,16 @@ namespace Aurora::IO::Net
return false;
}
if (!this->pSocket)
{
return false;
}
if (this->pSocket->bHasEnded)
{
return false;
}
if (this->bIsIrredeemable)
{
SysPushErrorIO("Transaction was signaled to be destroyed to reset mid synchronizable operation. You can no longer use this stream object");
@ -154,6 +164,16 @@ namespace Aurora::IO::Net
return false;
}
if (!this->pSocket)
{
return false;
}
if (this->pSocket->bHasEnded)
{
return false;
}
if (!memoryView)
{
SysPushErrorArg();

View File

@ -7,6 +7,7 @@
***/
#include "TLS.hpp"
#include "TLSCertificateChain.hpp"
#include <Source/Crypto/X509/x509.hpp>
namespace Aurora::IO::TLS
{
@ -22,11 +23,77 @@ namespace Aurora::IO::TLS
AuUInt32 CertificateChain::GetCertificateCount()
{
return 0;
AuUInt32 ret {};
auto pCert = this->pCertificate;
if (!pCert)
{
return {};
}
do
{
auto index = ret++;
}
while (pCert = pCert->next);
return ret;
}
AuMemoryViewRead CertificateChain::GetCertificate(AuUInt32 idx)
AuSPtr<AuMemoryViewRead> CertificateChain::GetCertificate(AuUInt32 idx)
{
AuUInt32 ret {};
auto pCert = this->pCertificate;
if (!pCert)
{
return {};
}
do
{
auto index = ret++;
if (index == idx)
{
struct View : AuMemoryViewRead
{
View(const AuMemoryViewRead &in, AuSPtr<void> pin) :
AuMemoryViewRead(in),
pin(pin)
{ }
AuSPtr<void> pin;
};
return AuMakeSharedThrow<View>(AuMemoryViewRead { pCert->raw.p, pCert->raw.len}, AuSharedFromThis());
}
}
while (pCert = pCert->next);
return {};
}
Crypto::X509::DecodedCertificate CertificateChain::GetCertificateDetails(AuUInt32 idx)
{
AuUInt32 ret {};
auto pCert = this->pCertificate;
if (!pCert)
{
return {};
}
do
{
auto index = ret++;
if (index == idx)
{
Crypto::X509::DecodedCertificate cert;
AuCrypto::X509::DecodeInternal(*pCert, cert);
return cert;
}
}
while (pCert = pCert->next);
return {};
}
@ -44,6 +111,7 @@ namespace Aurora::IO::TLS
cert.length);
if (iRet != 0)
{
this->pCertificate = nullptr;
SysPushErrorCrypto("Failed to parse certificate chain: {}", iRet);
return false;
}
@ -66,6 +134,7 @@ namespace Aurora::IO::TLS
cert.length);
if (iRet != 0)
{
this->pCertificate = nullptr;
SysPushErrorCrypto("Failed to parse certificate chain: {}", iRet);
return false;
}
@ -95,7 +164,7 @@ namespace Aurora::IO::TLS
bool CertificateChain::Init(const mbedtls_x509_crt *pCert)
{
this->pCertificate = &this->ownCertificate;
this->pCertificate = (mbedtls_x509_crt *)pCert;
return this->Precache();
}

View File

@ -8,13 +8,14 @@
#pragma once
namespace Aurora::IO::TLS
{
struct CertificateChain : ICertificateChain
struct CertificateChain : ICertificateChain, AuEnableSharedFromThis<CertificateChain>
{
CertificateChain();
~CertificateChain();
virtual AuUInt32 GetCertificateCount() override;
virtual AuMemoryViewRead GetCertificate(AuUInt32 idx) override;
virtual AuSPtr<AuMemoryViewRead> GetCertificate(AuUInt32 idx) override;
virtual Crypto::X509::DecodedCertificate GetCertificateDetails(AuUInt32 idx) override;
bool Init(const AuList<AuByteBuffer> &certs);
bool Init(const AuList<AuMemoryViewRead> &certs);

View File

@ -10,6 +10,8 @@
#include <Source/IO/Protocol/ProtocolStack.hpp>
#include <Aurora/IO/Net/NetExperimental.hpp>
#include <Source/IO/Net/AuNetSocket.hpp>
#include <Source/Crypto/X509/x509.hpp>
#include "TLSCertificateChain.hpp"
namespace Aurora::IO::TLS
{
@ -127,14 +129,31 @@ namespace Aurora::IO::TLS
return uBytesRead;
}
bool TLSContext::CheckCertificate(const AuMemoryViewRead &read)
bool TLSContext::CheckCertificate(mbedtls_x509_crt const *child, const AuMemoryViewRead &read)
{
if (!this->meta_.pCertPin)
{
return true;
}
return this->meta_.pCertPin->CheckCertificate({}, read);
if (this->bPinLock_)
{
return true;
}
auto pCertChain = AuMakeShared<CertificateChain>();
if (!pCertChain)
{
SysPushErrorMemory();
return false;
}
pCertChain->Init(child);
auto bRet = this->meta_.pCertPin->CheckCertificate(pCertChain, read);
this->bPinLock_ = true;
pCertChain->pCertificate = nullptr;
return bRet;
}
//
@ -204,7 +223,7 @@ namespace Aurora::IO::TLS
mbedtls_x509_crt **candidate_cas) -> int
{
return ((TLSContext *)p_ctx)->CheckCertificate({ child->raw.p, child->raw.len }) ? 0 : -1;
return ((TLSContext *)p_ctx)->CheckCertificate(child, { child->raw.p, child->raw.len }) ? 0 : -1;
}, this);
::mbedtls_ssl_conf_rng(&this->conf, mbedtls_ctr_drbg_random, &gCtrDrbg);
@ -407,7 +426,7 @@ namespace Aurora::IO::TLS
if (auto pSocket = this->wpSocket_.lock())
{
pSocket->Shutdown();
pSocket->Shutdown(false);
}
AuResetMember(this->meta_);
@ -478,6 +497,8 @@ namespace Aurora::IO::TLS
this->bIsFatal = false;
this->iFatalError = 0;
this->bPinLock_ = false;
this->channelRecv_.HasCompletedHandshake() = false;
if (::mbedtls_ssl_session_reset(&this->ssl) != 0)

View File

@ -60,6 +60,7 @@ namespace Aurora::IO::TLS
bool bIsFatal {};
bool bIsAlive {};
bool bPinLock_ {};
int iFatalError {};
mbedtls_ssl_context ssl {};
@ -67,7 +68,7 @@ namespace Aurora::IO::TLS
int Read(void *pOut, AuUInt length);
int Write(const void *pIn, AuUInt length);
bool CheckCertificate(const AuMemoryViewRead &read);
bool CheckCertificate(mbedtls_x509_crt const *child, const AuMemoryViewRead &read);
private:

View File

@ -64,6 +64,7 @@ namespace Aurora::IO::TLS
this->bHasCompletedHandshake_ = true;
this->pParent_->bIsAlive = true;
this->pParent_->bPinLock_ = false;
return true;
}
@ -102,6 +103,12 @@ namespace Aurora::IO::TLS
this->pParent_->OnClose();
return true;
}
case MBEDTLS_ERR_X509_FATAL_ERROR:
{
this->bHasFailedOnce = true;
this->pParent_->OnClose();
return false;
}
case 0:
{
bComplete = true;