mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 15:27:21 +00:00
[idd] network: validate server certificate identity
This commit is contained in:
@@ -42,10 +42,12 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <ncrypt.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -199,7 +201,7 @@ namespace
|
|||||||
return QuicAddrFromString(text.c_str(), port, &address) != FALSE;
|
return QuicAddrFromString(text.c_str(), port, &address) != FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadCertificateHash(QUIC_CERTIFICATE_HASH_STORE& certificate)
|
bool ReadCertificateHash(QUIC_CERTIFICATE_HASH& certificate)
|
||||||
{
|
{
|
||||||
HKEY key = nullptr;
|
HKEY key = nullptr;
|
||||||
const LSTATUS openStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
const LSTATUS openStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||||
@@ -225,10 +227,83 @@ namespace
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate.Flags = QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE;
|
return true;
|
||||||
if (strcpy_s(certificate.StoreName,
|
}
|
||||||
sizeof(certificate.StoreName), "MY") != 0)
|
|
||||||
|
bool LoadCertificateContext(HCERTSTORE& store,
|
||||||
|
PCCERT_CONTEXT& certificate)
|
||||||
|
{
|
||||||
|
QUIC_CERTIFICATE_HASH certificateHash = {};
|
||||||
|
if (!ReadCertificateHash(certificateHash))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
store = CertOpenStore(
|
||||||
|
CERT_STORE_PROV_SYSTEM_W,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
||||||
|
CERT_STORE_OPEN_EXISTING_FLAG |
|
||||||
|
CERT_STORE_READONLY_FLAG,
|
||||||
|
L"MY");
|
||||||
|
if (!store)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open the network identity certificate store: "
|
||||||
|
"0x%lx", static_cast<unsigned long>(GetLastError()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRYPT_HASH_BLOB hash = {};
|
||||||
|
hash.cbData = static_cast<DWORD>(
|
||||||
|
sizeof(certificateHash.ShaHash));
|
||||||
|
hash.pbData = certificateHash.ShaHash;
|
||||||
|
certificate = CertFindCertificateInStore(store,
|
||||||
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HASH,
|
||||||
|
&hash, nullptr);
|
||||||
|
if (!certificate)
|
||||||
|
{
|
||||||
|
const DWORD status = GetLastError();
|
||||||
|
DEBUG_ERROR("Failed to find the network identity certificate: 0x%lx",
|
||||||
|
static_cast<unsigned long>(status));
|
||||||
|
CertCloseStore(store, 0);
|
||||||
|
store = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE privateKey = 0;
|
||||||
|
DWORD keySpec = 0;
|
||||||
|
BOOL callerFree = FALSE;
|
||||||
|
const DWORD flags =
|
||||||
|
CRYPT_ACQUIRE_CACHE_FLAG |
|
||||||
|
CRYPT_ACQUIRE_COMPARE_KEY_FLAG |
|
||||||
|
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG |
|
||||||
|
CRYPT_ACQUIRE_SILENT_FLAG;
|
||||||
|
if (!CryptAcquireCertificatePrivateKey(certificate,
|
||||||
|
flags,
|
||||||
|
nullptr, &privateKey, &keySpec, &callerFree))
|
||||||
|
{
|
||||||
|
const DWORD status = GetLastError();
|
||||||
|
DEBUG_ERROR("Failed to acquire the network identity private key: "
|
||||||
|
"0x%lx", static_cast<unsigned long>(status));
|
||||||
|
CertFreeCertificateContext(certificate);
|
||||||
|
CertCloseStore(store, 0);
|
||||||
|
certificate = nullptr;
|
||||||
|
store = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!privateKey || keySpec != CERT_NCRYPT_KEY_SPEC || callerFree)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("The network identity did not provide a cached CNG "
|
||||||
|
"private key");
|
||||||
|
if (callerFree && privateKey)
|
||||||
|
NCryptFreeObject(static_cast<NCRYPT_HANDLE>(privateKey));
|
||||||
|
CertFreeCertificateContext(certificate);
|
||||||
|
CertCloseStore(store, 0);
|
||||||
|
certificate = nullptr;
|
||||||
|
store = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,6 +546,8 @@ struct CNetworkServer::Impl
|
|||||||
HQUIC registration = nullptr;
|
HQUIC registration = nullptr;
|
||||||
HQUIC configuration = nullptr;
|
HQUIC configuration = nullptr;
|
||||||
HQUIC listener = nullptr;
|
HQUIC listener = nullptr;
|
||||||
|
HCERTSTORE certificateStore = nullptr;
|
||||||
|
PCCERT_CONTEXT certificate = nullptr;
|
||||||
HANDLE drainedEvent = nullptr;
|
HANDLE drainedEvent = nullptr;
|
||||||
CSRWLock lock;
|
CSRWLock lock;
|
||||||
std::vector<Connection *> connections;
|
std::vector<Connection *> connections;
|
||||||
@@ -4718,8 +4795,7 @@ retry:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QUIC_CERTIFICATE_HASH_STORE certificate = {};
|
if (!LoadCertificateContext(certificateStore, certificate))
|
||||||
if (!ReadCertificateHash(certificate))
|
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("The persistent network identity is unavailable; run the "
|
DEBUG_ERROR("The persistent network identity is unavailable; run the "
|
||||||
"Looking Glass IDD installer to provision it");
|
"Looking Glass IDD installer to provision it");
|
||||||
@@ -4728,10 +4804,10 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QUIC_CREDENTIAL_CONFIG credential = {};
|
QUIC_CREDENTIAL_CONFIG credential = {};
|
||||||
credential.Type =
|
credential.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT;
|
||||||
QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE;
|
|
||||||
credential.Flags = QUIC_CREDENTIAL_FLAG_NONE;
|
credential.Flags = QUIC_CREDENTIAL_FLAG_NONE;
|
||||||
credential.CertificateHashStore = &certificate;
|
credential.CertificateContext = reinterpret_cast<QUIC_CERTIFICATE *>(
|
||||||
|
const_cast<CERT_CONTEXT *>(certificate));
|
||||||
status = api->ConfigurationLoadCredential(configuration, &credential);
|
status = api->ConfigurationLoadCredential(configuration, &credential);
|
||||||
if (QUIC_FAILED(status))
|
if (QUIC_FAILED(status))
|
||||||
{
|
{
|
||||||
@@ -4806,6 +4882,16 @@ retry:
|
|||||||
api->ConfigurationClose(configuration);
|
api->ConfigurationClose(configuration);
|
||||||
configuration = nullptr;
|
configuration = nullptr;
|
||||||
}
|
}
|
||||||
|
if (certificate)
|
||||||
|
{
|
||||||
|
CertFreeCertificateContext(certificate);
|
||||||
|
certificate = nullptr;
|
||||||
|
}
|
||||||
|
if (certificateStore)
|
||||||
|
{
|
||||||
|
CertCloseStore(certificateStore, 0);
|
||||||
|
certificateStore = nullptr;
|
||||||
|
}
|
||||||
if (registration && api)
|
if (registration && api)
|
||||||
{
|
{
|
||||||
api->RegistrationClose(registration);
|
api->RegistrationClose(registration);
|
||||||
|
|||||||
@@ -140,6 +140,37 @@ openKey:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool networkIdentityCanUsePrivateKey(PCCERT_CONTEXT certificate)
|
||||||
|
{
|
||||||
|
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE key = 0;
|
||||||
|
DWORD keySpec = 0;
|
||||||
|
BOOL callerFree = FALSE;
|
||||||
|
const DWORD flags =
|
||||||
|
CRYPT_ACQUIRE_COMPARE_KEY_FLAG |
|
||||||
|
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG |
|
||||||
|
CRYPT_ACQUIRE_SILENT_FLAG;
|
||||||
|
const bool acquired = CryptAcquireCertificatePrivateKey(
|
||||||
|
certificate,
|
||||||
|
flags,
|
||||||
|
NULL,
|
||||||
|
&key,
|
||||||
|
&keySpec,
|
||||||
|
&callerFree);
|
||||||
|
const bool result = acquired &&
|
||||||
|
key &&
|
||||||
|
keySpec == CERT_NCRYPT_KEY_SPEC;
|
||||||
|
|
||||||
|
if (callerFree && key)
|
||||||
|
{
|
||||||
|
if (keySpec == CERT_NCRYPT_KEY_SPEC)
|
||||||
|
NCryptFreeObject((NCRYPT_HANDLE)key);
|
||||||
|
else
|
||||||
|
CryptReleaseContext((HCRYPTPROV)key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static bool networkIdentityMatchesKey(PCCERT_CONTEXT certificate)
|
static bool networkIdentityMatchesKey(PCCERT_CONTEXT certificate)
|
||||||
{
|
{
|
||||||
DWORD size = 0;
|
DWORD size = 0;
|
||||||
@@ -158,7 +189,8 @@ static bool networkIdentityMatchesKey(PCCERT_CONTEXT certificate)
|
|||||||
LG_NETWORK_CERTIFICATE_KEY_NAME) == 0 &&
|
LG_NETWORK_CERTIFICATE_KEY_NAME) == 0 &&
|
||||||
info->pwszProvName &&
|
info->pwszProvName &&
|
||||||
wcscmp(info->pwszProvName, MS_KEY_STORAGE_PROVIDER) == 0 &&
|
wcscmp(info->pwszProvName, MS_KEY_STORAGE_PROVIDER) == 0 &&
|
||||||
CertVerifyTimeValidity(NULL, certificate->pCertInfo) == 0;
|
CertVerifyTimeValidity(NULL, certificate->pCertInfo) == 0 &&
|
||||||
|
networkIdentityCanUsePrivateKey(certificate);
|
||||||
|
|
||||||
free(info);
|
free(info);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user