mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[idd] network: validate server certificate identity
This commit is contained in:
@@ -42,10 +42,12 @@
|
||||
#include <limits.h>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <ncrypt.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wincrypt.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -199,7 +201,7 @@ namespace
|
||||
return QuicAddrFromString(text.c_str(), port, &address) != FALSE;
|
||||
}
|
||||
|
||||
bool ReadCertificateHash(QUIC_CERTIFICATE_HASH_STORE& certificate)
|
||||
bool ReadCertificateHash(QUIC_CERTIFICATE_HASH& certificate)
|
||||
{
|
||||
HKEY key = nullptr;
|
||||
const LSTATUS openStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
@@ -225,10 +227,83 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
certificate.Flags = QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE;
|
||||
if (strcpy_s(certificate.StoreName,
|
||||
sizeof(certificate.StoreName), "MY") != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadCertificateContext(HCERTSTORE& store,
|
||||
PCCERT_CONTEXT& certificate)
|
||||
{
|
||||
QUIC_CERTIFICATE_HASH certificateHash = {};
|
||||
if (!ReadCertificateHash(certificateHash))
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -467,17 +542,19 @@ struct CNetworkServer::Impl
|
||||
CNetworkClipboard& clipboard;
|
||||
CNetworkVideo& video;
|
||||
NetworkSettings settings;
|
||||
const QUIC_API_TABLE * api = nullptr;
|
||||
HQUIC registration = nullptr;
|
||||
HQUIC configuration = nullptr;
|
||||
HQUIC listener = nullptr;
|
||||
HANDLE drainedEvent = nullptr;
|
||||
const QUIC_API_TABLE * api = nullptr;
|
||||
HQUIC registration = nullptr;
|
||||
HQUIC configuration = nullptr;
|
||||
HQUIC listener = nullptr;
|
||||
HCERTSTORE certificateStore = nullptr;
|
||||
PCCERT_CONTEXT certificate = nullptr;
|
||||
HANDLE drainedEvent = nullptr;
|
||||
CSRWLock lock;
|
||||
std::vector<Connection *> connections;
|
||||
uint32_t nextClient = 1;
|
||||
unsigned activeCallbacks = 0;
|
||||
bool stopping = true;
|
||||
bool running = false;
|
||||
uint32_t nextClient = 1;
|
||||
unsigned activeCallbacks = 0;
|
||||
bool stopping = true;
|
||||
bool running = false;
|
||||
|
||||
void EnterCallback()
|
||||
{
|
||||
@@ -4718,8 +4795,7 @@ retry:
|
||||
return false;
|
||||
}
|
||||
|
||||
QUIC_CERTIFICATE_HASH_STORE certificate = {};
|
||||
if (!ReadCertificateHash(certificate))
|
||||
if (!LoadCertificateContext(certificateStore, certificate))
|
||||
{
|
||||
DEBUG_ERROR("The persistent network identity is unavailable; run the "
|
||||
"Looking Glass IDD installer to provision it");
|
||||
@@ -4728,10 +4804,10 @@ retry:
|
||||
}
|
||||
|
||||
QUIC_CREDENTIAL_CONFIG credential = {};
|
||||
credential.Type =
|
||||
QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE;
|
||||
credential.Flags = QUIC_CREDENTIAL_FLAG_NONE;
|
||||
credential.CertificateHashStore = &certificate;
|
||||
credential.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT;
|
||||
credential.Flags = QUIC_CREDENTIAL_FLAG_NONE;
|
||||
credential.CertificateContext = reinterpret_cast<QUIC_CERTIFICATE *>(
|
||||
const_cast<CERT_CONTEXT *>(certificate));
|
||||
status = api->ConfigurationLoadCredential(configuration, &credential);
|
||||
if (QUIC_FAILED(status))
|
||||
{
|
||||
@@ -4806,6 +4882,16 @@ retry:
|
||||
api->ConfigurationClose(configuration);
|
||||
configuration = nullptr;
|
||||
}
|
||||
if (certificate)
|
||||
{
|
||||
CertFreeCertificateContext(certificate);
|
||||
certificate = nullptr;
|
||||
}
|
||||
if (certificateStore)
|
||||
{
|
||||
CertCloseStore(certificateStore, 0);
|
||||
certificateStore = nullptr;
|
||||
}
|
||||
if (registration && api)
|
||||
{
|
||||
api->RegistrationClose(registration);
|
||||
|
||||
@@ -140,6 +140,37 @@ openKey:
|
||||
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)
|
||||
{
|
||||
DWORD size = 0;
|
||||
@@ -158,7 +189,8 @@ static bool networkIdentityMatchesKey(PCCERT_CONTEXT certificate)
|
||||
LG_NETWORK_CERTIFICATE_KEY_NAME) == 0 &&
|
||||
info->pwszProvName &&
|
||||
wcscmp(info->pwszProvName, MS_KEY_STORAGE_PROVIDER) == 0 &&
|
||||
CertVerifyTimeValidity(NULL, certificate->pCertInfo) == 0;
|
||||
CertVerifyTimeValidity(NULL, certificate->pCertInfo) == 0 &&
|
||||
networkIdentityCanUsePrivateKey(certificate);
|
||||
|
||||
free(info);
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user