diff --git a/idd/LGIdd/transport/network/CNetworkServer.cpp b/idd/LGIdd/transport/network/CNetworkServer.cpp index 922fac1d..8494e70e 100644 --- a/idd/LGIdd/transport/network/CNetworkServer.cpp +++ b/idd/LGIdd/transport/network/CNetworkServer.cpp @@ -42,10 +42,12 @@ #include #include #include +#include #include #include #include #include +#include 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(GetLastError())); + return false; + } + + CRYPT_HASH_BLOB hash = {}; + hash.cbData = static_cast( + 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(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(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(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 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( + const_cast(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); diff --git a/idd/LGIddInstall/NetworkIdentity.c b/idd/LGIddInstall/NetworkIdentity.c index 9d7144a6..187535c9 100644 --- a/idd/LGIddInstall/NetworkIdentity.c +++ b/idd/LGIddInstall/NetworkIdentity.c @@ -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;