Post-festum CRL checking introduced
This commit is contained in:
@@ -62,6 +62,15 @@ void eap::peer_ttls::initialize()
|
||||
|
||||
void eap::peer_ttls::shutdown()
|
||||
{
|
||||
// Signal all certificate revocation verify threads to abort and wait for them (10sec max).
|
||||
vector<HANDLE> chks;
|
||||
chks.reserve(m_crl_checkers.size());
|
||||
for (auto chk = m_crl_checkers.begin(), chk_end = m_crl_checkers.end(); chk != chk_end; ++chk) {
|
||||
SetEvent(chk->m_abort);
|
||||
chks.push_back(chk->m_thread);
|
||||
}
|
||||
WaitForMultipleObjects((DWORD)chks.size(), chks.data(), TRUE, 10000);
|
||||
|
||||
// Uninitialize EapHost. It was initialized for EapHost based inner authentication methods.
|
||||
EapHostPeerUninitialize();
|
||||
}
|
||||
@@ -379,6 +388,17 @@ void eap::peer_ttls::set_response_attributes(
|
||||
}
|
||||
|
||||
|
||||
void eap::peer_ttls::spawn_crl_check(_Inout_ winstd::cert_context &&cert)
|
||||
{
|
||||
// Create the thread and add it to the list.
|
||||
m_crl_checkers.push_back(std::move(crl_checker(*this, std::move(cert))));
|
||||
|
||||
// Now the thread is in-place, start it.
|
||||
crl_checker &chk = m_crl_checkers.back();
|
||||
chk.m_thread = CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(crl_checker::verify), &chk, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
const eap::config_method_ttls* eap::peer_ttls::combine_credentials(
|
||||
_In_ DWORD dwFlags,
|
||||
_In_ const config_connection &cfg,
|
||||
@@ -498,3 +518,143 @@ eap::peer_ttls::session::~session()
|
||||
m_module.free_memory(m_blob_cred);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// eap::peer_ttls::crl_checker
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
eap::peer_ttls::crl_checker::crl_checker(_In_ module &mod, _Inout_ winstd::cert_context &&cert) :
|
||||
m_module(mod),
|
||||
m_cert (std::move(cert)),
|
||||
m_abort (CreateEvent(NULL, TRUE, FALSE, NULL))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
eap::peer_ttls::crl_checker::crl_checker(_Inout_ crl_checker &&other) :
|
||||
m_module( other.m_module ),
|
||||
m_thread(std::move(other.m_thread)),
|
||||
m_abort (std::move(other.m_abort )),
|
||||
m_cert (std::move(other.m_cert ))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
eap::peer_ttls::crl_checker& eap::peer_ttls::crl_checker::operator=(_Inout_ crl_checker &&other)
|
||||
{
|
||||
if (this != std::addressof(other)) {
|
||||
assert(std::addressof(m_module) == std::addressof(other.m_module)); // Move threads within same module only!
|
||||
m_thread = std::move(other.m_thread);
|
||||
m_abort = std::move(other.m_abort );
|
||||
m_cert = std::move(other.m_cert );
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
||||
{
|
||||
// Initialize COM.
|
||||
com_initializer com_init(NULL);
|
||||
|
||||
// Wait for 5sec for the link to become online. (Hopefuly!)
|
||||
if (WaitForSingleObject(obj->m_abort, 5000) == WAIT_OBJECT_0) {
|
||||
// Aborted.
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prepare a list of certificates forming certificate chain.
|
||||
list<cert_context> context_data;
|
||||
for (cert_context c(obj->m_cert); c;) {
|
||||
context_data.push_back(std::move(c));
|
||||
DWORD flags = 0;
|
||||
c = CertGetIssuerCertificateFromStore(obj->m_cert->hCertStore, context_data.back(), NULL, &flags);
|
||||
if (!c) break;
|
||||
}
|
||||
|
||||
// Create an array of pointers to CERT_CONTEXT required by CertVerifyRevocation().
|
||||
vector<PCERT_CONTEXT> context;
|
||||
context.reserve(context_data.size());
|
||||
for (auto c = context_data.cbegin(), c_end = context_data.cend(); c != c_end; ++c)
|
||||
context.push_back(const_cast<PCERT_CONTEXT>(c->operator PCCERT_CONTEXT()));
|
||||
|
||||
CERT_REVOCATION_STATUS status_rev = { sizeof(CERT_REVOCATION_STATUS) };
|
||||
for (auto c = context.begin(), c_end = context.end(); c != c_end;) {
|
||||
// Check for thread abort signal.
|
||||
if (WaitForSingleObject(obj->m_abort, 0) == WAIT_OBJECT_0)
|
||||
return 1;
|
||||
|
||||
// Perform revocation check.
|
||||
if (!CertVerifyRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE,
|
||||
(DWORD)(c_end - c), reinterpret_cast<PVOID*>(&*c),
|
||||
CERT_VERIFY_REV_CHAIN_FLAG, NULL, &status_rev))
|
||||
{
|
||||
PCCERT_CONTEXT cert = *(c + status_rev.dwIndex);
|
||||
wstring subj;
|
||||
if (!CertGetNameStringW(cert, CERT_NAME_DNS_TYPE, CERT_NAME_STR_ENABLE_PUNYCODE_FLAG, NULL, subj))
|
||||
sprintf(subj, L"<error %u>", GetLastError());
|
||||
|
||||
switch (status_rev.dwError) {
|
||||
case CRYPT_E_NO_REVOCATION_CHECK:
|
||||
// Revocation check could not be performed.
|
||||
c += status_rev.dwIndex + 1;
|
||||
if (c == c_end) {
|
||||
// This "error" is expected for the root CA certificate.
|
||||
} else {
|
||||
// This really was an error, as it appeared before the root CA cerficate in the chain.
|
||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_SKIPPED, event_data((unsigned int)eap_type_ttls), event_data(subj), event_data::blank);
|
||||
}
|
||||
break;
|
||||
|
||||
case CRYPT_E_REVOKED:
|
||||
// One of the certificates in the chain was revoked.
|
||||
switch (status_rev.dwReason) {
|
||||
case CRL_REASON_AFFILIATION_CHANGED:
|
||||
case CRL_REASON_SUPERSEDED:
|
||||
case CRL_REASON_CESSATION_OF_OPERATION:
|
||||
case CRL_REASON_CERTIFICATE_HOLD:
|
||||
// The revocation was of administrative nature. No need to black-list.
|
||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKED1, event_data((unsigned int)eap_type_ttls), event_data(subj), event_data(status_rev.dwReason), event_data::blank);
|
||||
break;
|
||||
|
||||
default: {
|
||||
// One of the certificates in the chain was revoked as compromised. Black-list it.
|
||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKED, event_data((unsigned int)eap_type_ttls), event_data(subj), event_data(status_rev.dwReason), event_data::blank);
|
||||
reg_key key;
|
||||
if (key.create(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\") _T(VENDOR_NAME_STR) _T("\\") _T(PRODUCT_NAME_STR) _T("\\TLSCRL"), NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE)) {
|
||||
vector<unsigned char> hash;
|
||||
if (CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID, hash)) {
|
||||
wstring hash_unicode;
|
||||
hex_enc enc;
|
||||
enc.encode(hash_unicode, hash.data(), hash.size());
|
||||
RegSetValueExW(key, hash_unicode.c_str(), NULL, REG_SZ, reinterpret_cast<LPCBYTE>(subj.c_str()), (DWORD)((subj.length() + 1) * sizeof(wstring::value_type)));
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
// Resume checking the rest of the chain.
|
||||
c += status_rev.dwIndex + 1;
|
||||
break;
|
||||
|
||||
case ERROR_SUCCESS:
|
||||
// Odd. CertVerifyRevocation() should return TRUE then. Nevertheless, we take this as a "yes".
|
||||
c = c_end;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Checking one of the certificates in the chain for revocation failed. Resume checking the rest.
|
||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_FAILED, event_data((unsigned int)eap_type_ttls), event_data(subj), event_data(status_rev.dwError), event_data::blank);
|
||||
c += status_rev.dwIndex + 1;
|
||||
}
|
||||
} else {
|
||||
// Revocation check finished.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Revocation check succeeded.
|
||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_FINISHED, event_data((unsigned int)eap_type_ttls), event_data::blank);
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user