mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 15:27:21 +00:00
[idd] network: provision persistent server identity
This commit is contained in:
30
idd/LGCommon/NetworkIdentity.h
Normal file
30
idd/LGCommon/NetworkIdentity.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LG_NETWORK_IDENTITY_H
|
||||||
|
#define LG_NETWORK_IDENTITY_H
|
||||||
|
|
||||||
|
#define LG_NETWORK_CERTIFICATE_KEY_NAME L"LookingGlassNetworkIdentity"
|
||||||
|
#define LG_NETWORK_CERTIFICATE_SUBJECT L"CN=Looking Glass Network"
|
||||||
|
#define LG_NETWORK_CERTIFICATE_FRIENDLY_NAME L"Looking Glass Network Identity"
|
||||||
|
#define LG_NETWORK_CERTIFICATE_HASH_VALUE L"NetworkCertificateHash"
|
||||||
|
#define LG_NETWORK_CERTIFICATE_FINGERPRINT_VALUE L"NetworkCertificateFingerprint"
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -35,6 +35,8 @@
|
|||||||
#include <sddl.h>
|
#include <sddl.h>
|
||||||
#include <aclapi.h>
|
#include <aclapi.h>
|
||||||
|
|
||||||
|
#include "NetworkIdentity.h"
|
||||||
|
|
||||||
#pragma comment(lib, "cfgmgr32.lib")
|
#pragma comment(lib, "cfgmgr32.lib")
|
||||||
|
|
||||||
#define LGIDD_CLASS_GUID GUID_DEVCLASS_DISPLAY
|
#define LGIDD_CLASS_GUID GUID_DEVCLASS_DISPLAY
|
||||||
@@ -1022,6 +1024,10 @@ void install(const DeviceDesc *const devices[DEVICE_COUNT])
|
|||||||
if (!ensureKeyWithAce())
|
if (!ensureKeyWithAce())
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
_putws(L"Preparing network identity...");
|
||||||
|
if (!networkIdentityProvision())
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
for (size_t i = 0; i < DEVICE_COUNT; ++i)
|
for (size_t i = 0; i < DEVICE_COUNT; ++i)
|
||||||
{
|
{
|
||||||
const DeviceDesc *device = devices[i];
|
const DeviceDesc *device = devices[i];
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;crypt32.lib;ncrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
||||||
@@ -101,7 +101,7 @@ copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\"</Com
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;crypt32.lib;ncrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
||||||
@@ -122,7 +122,7 @@ copy /Y "$(SolutionDir)..\LICENSE" "$(SolutionDir)$(Platform)\$(Configuration)\
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;crypt32.lib;ncrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
||||||
@@ -142,7 +142,7 @@ copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\"</Com
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>setupapi.lib;shlwapi.lib;newdev.lib;crypt32.lib;ncrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
||||||
@@ -154,6 +154,11 @@ copy /Y "$(SolutionDir)..\LICENSE" "$(SolutionDir)$(Platform)\$(Configuration)\
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="LGIddInstall.c" />
|
<ClCompile Include="LGIddInstall.c" />
|
||||||
|
<ClCompile Include="NetworkIdentity.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="NetworkIdentity.h" />
|
||||||
|
<ClInclude Include="..\LGCommon\NetworkIdentity.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -18,5 +18,16 @@
|
|||||||
<ClCompile Include="LGIddInstall.c">
|
<ClCompile Include="LGIddInstall.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="NetworkIdentity.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="NetworkIdentity.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\LGCommon\NetworkIdentity.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
516
idd/LGIddInstall/NetworkIdentity.c
Normal file
516
idd/LGIddInstall/NetworkIdentity.c
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
|
#include "NetworkIdentity.h"
|
||||||
|
#include "../LGCommon/NetworkIdentity.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ncrypt.h>
|
||||||
|
#include <sddl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
|
||||||
|
#define LGIDD_REGKEY L"Software\\LookingGlass\\IDD"
|
||||||
|
#define LG_NETWORK_CERTIFICATE_LIFETIME_YEARS 30
|
||||||
|
|
||||||
|
static void networkIdentityError(const wchar_t *operation, DWORD status)
|
||||||
|
{
|
||||||
|
fwprintf(stderr, L"%s failed with code 0x%08lx\n", operation, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool networkIdentitySetKeySecurity(NCRYPT_KEY_HANDLE key)
|
||||||
|
{
|
||||||
|
static const wchar_t securityDescriptor[] =
|
||||||
|
L"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;LS)"
|
||||||
|
L"(A;;GA;;;S-1-5-84-0-0-0-0-0)";
|
||||||
|
|
||||||
|
PSECURITY_DESCRIPTOR descriptor = NULL;
|
||||||
|
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||||
|
securityDescriptor, SDDL_REVISION_1, &descriptor, NULL))
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"ConvertStringSecurityDescriptorToSecurityDescriptorW",
|
||||||
|
GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SECURITY_STATUS status = NCryptSetProperty(
|
||||||
|
key,
|
||||||
|
NCRYPT_SECURITY_DESCR_PROPERTY,
|
||||||
|
(PBYTE)descriptor,
|
||||||
|
GetSecurityDescriptorLength(descriptor),
|
||||||
|
DACL_SECURITY_INFORMATION | NCRYPT_PERSIST_FLAG);
|
||||||
|
LocalFree(descriptor);
|
||||||
|
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"NCryptSetProperty(key security)", (DWORD)status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool networkIdentityOpenKey(
|
||||||
|
NCRYPT_PROV_HANDLE *provider,
|
||||||
|
NCRYPT_KEY_HANDLE *key)
|
||||||
|
{
|
||||||
|
bool retry = false;
|
||||||
|
SECURITY_STATUS status = NCryptOpenStorageProvider(
|
||||||
|
provider, MS_KEY_STORAGE_PROVIDER, 0);
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
networkIdentityError(L"NCryptOpenStorageProvider", (DWORD)status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
openKey:
|
||||||
|
status = NCryptOpenKey(*provider, key,
|
||||||
|
LG_NETWORK_CERTIFICATE_KEY_NAME, 0, NCRYPT_MACHINE_KEY_FLAG);
|
||||||
|
if (status == NTE_BAD_KEYSET && !retry)
|
||||||
|
{
|
||||||
|
status = NCryptCreatePersistedKey(
|
||||||
|
*provider,
|
||||||
|
key,
|
||||||
|
NCRYPT_ECDSA_P256_ALGORITHM,
|
||||||
|
LG_NETWORK_CERTIFICATE_KEY_NAME,
|
||||||
|
0,
|
||||||
|
NCRYPT_MACHINE_KEY_FLAG);
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
const DWORD usage = NCRYPT_ALLOW_SIGNING_FLAG;
|
||||||
|
status = NCryptSetProperty(
|
||||||
|
*key,
|
||||||
|
NCRYPT_KEY_USAGE_PROPERTY,
|
||||||
|
(PBYTE)&usage,
|
||||||
|
sizeof usage,
|
||||||
|
0);
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
status = NCryptFinalizeKey(*key, 0);
|
||||||
|
}
|
||||||
|
else if (status == NTE_EXISTS)
|
||||||
|
{
|
||||||
|
retry = true;
|
||||||
|
goto openKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"NCryptOpenKey/NCryptCreatePersistedKey", (DWORD)status);
|
||||||
|
NCryptFreeObject(*provider);
|
||||||
|
*provider = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!networkIdentitySetKeySecurity(*key))
|
||||||
|
{
|
||||||
|
NCryptFreeObject(*key);
|
||||||
|
NCryptFreeObject(*provider);
|
||||||
|
*key = 0;
|
||||||
|
*provider = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool networkIdentityMatchesKey(PCCERT_CONTEXT certificate)
|
||||||
|
{
|
||||||
|
DWORD size = 0;
|
||||||
|
if (!CertGetCertificateContextProperty(
|
||||||
|
certificate, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CRYPT_KEY_PROV_INFO *info = malloc(size);
|
||||||
|
if (!info)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool result = CertGetCertificateContextProperty(
|
||||||
|
certificate, CERT_KEY_PROV_INFO_PROP_ID, info, &size) &&
|
||||||
|
info->pwszContainerName &&
|
||||||
|
wcscmp(info->pwszContainerName,
|
||||||
|
LG_NETWORK_CERTIFICATE_KEY_NAME) == 0 &&
|
||||||
|
info->pwszProvName &&
|
||||||
|
wcscmp(info->pwszProvName, MS_KEY_STORAGE_PROVIDER) == 0 &&
|
||||||
|
CertVerifyTimeValidity(NULL, certificate->pCertInfo) == 0;
|
||||||
|
|
||||||
|
free(info);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PCCERT_CONTEXT networkIdentityFindCertificate(HCERTSTORE store)
|
||||||
|
{
|
||||||
|
PCCERT_CONTEXT certificate = NULL;
|
||||||
|
while ((certificate = CertEnumCertificatesInStore(store, certificate)))
|
||||||
|
if (networkIdentityMatchesKey(certificate))
|
||||||
|
return certificate;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool networkIdentityEncodeExtension(
|
||||||
|
LPCSTR objectId,
|
||||||
|
LPCSTR structureType,
|
||||||
|
const void *value,
|
||||||
|
bool critical,
|
||||||
|
CERT_EXTENSION *extension)
|
||||||
|
{
|
||||||
|
extension->pszObjId = (LPSTR)objectId;
|
||||||
|
extension->fCritical = critical;
|
||||||
|
|
||||||
|
if (!CryptEncodeObjectEx(
|
||||||
|
X509_ASN_ENCODING,
|
||||||
|
structureType,
|
||||||
|
value,
|
||||||
|
CRYPT_ENCODE_ALLOC_FLAG,
|
||||||
|
NULL,
|
||||||
|
&extension->Value.pbData,
|
||||||
|
&extension->Value.cbData))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CryptEncodeObjectEx", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void networkIdentityFreeExtensions(
|
||||||
|
CERT_EXTENSION *extensions,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
if (extensions[i].Value.pbData)
|
||||||
|
LocalFree(extensions[i].Value.pbData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PCCERT_CONTEXT networkIdentityCreateCertificate(
|
||||||
|
HCERTSTORE store,
|
||||||
|
NCRYPT_KEY_HANDLE key)
|
||||||
|
{
|
||||||
|
BYTE *encodedName = NULL;
|
||||||
|
DWORD encodedNameSize = 0;
|
||||||
|
PCCERT_CONTEXT result = NULL;
|
||||||
|
|
||||||
|
if (!CertStrToNameW(
|
||||||
|
X509_ASN_ENCODING,
|
||||||
|
LG_NETWORK_CERTIFICATE_SUBJECT,
|
||||||
|
CERT_X500_NAME_STR,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&encodedNameSize,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CertStrToNameW(size)", GetLastError());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedName = malloc(encodedNameSize);
|
||||||
|
if (!encodedName)
|
||||||
|
{
|
||||||
|
networkIdentityError(L"malloc(certificate name)", ERROR_OUTOFMEMORY);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CertStrToNameW(
|
||||||
|
X509_ASN_ENCODING,
|
||||||
|
LG_NETWORK_CERTIFICATE_SUBJECT,
|
||||||
|
CERT_X500_NAME_STR,
|
||||||
|
NULL,
|
||||||
|
encodedName,
|
||||||
|
&encodedNameSize,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CertStrToNameW", GetLastError());
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
CERT_BASIC_CONSTRAINTS2_INFO constraints =
|
||||||
|
{
|
||||||
|
.fCA = FALSE,
|
||||||
|
.fPathLenConstraint = FALSE,
|
||||||
|
.dwPathLenConstraint = 0,
|
||||||
|
};
|
||||||
|
LPSTR usages[] = { szOID_PKIX_KP_SERVER_AUTH };
|
||||||
|
CERT_ENHKEY_USAGE enhancedKeyUsage =
|
||||||
|
{
|
||||||
|
.cUsageIdentifier = ARRAYSIZE(usages),
|
||||||
|
.rgpszUsageIdentifier = usages,
|
||||||
|
};
|
||||||
|
BYTE keyUsageByte = CERT_DIGITAL_SIGNATURE_KEY_USAGE;
|
||||||
|
CRYPT_BIT_BLOB keyUsage =
|
||||||
|
{
|
||||||
|
.cbData = sizeof keyUsageByte,
|
||||||
|
.pbData = &keyUsageByte,
|
||||||
|
.cUnusedBits = 0,
|
||||||
|
};
|
||||||
|
CERT_EXTENSION extensions[3] = { 0 };
|
||||||
|
if (!networkIdentityEncodeExtension(
|
||||||
|
szOID_BASIC_CONSTRAINTS2,
|
||||||
|
X509_BASIC_CONSTRAINTS2,
|
||||||
|
&constraints,
|
||||||
|
true,
|
||||||
|
&extensions[0]) ||
|
||||||
|
!networkIdentityEncodeExtension(
|
||||||
|
szOID_ENHANCED_KEY_USAGE,
|
||||||
|
X509_ENHANCED_KEY_USAGE,
|
||||||
|
&enhancedKeyUsage,
|
||||||
|
false,
|
||||||
|
&extensions[1]) ||
|
||||||
|
!networkIdentityEncodeExtension(
|
||||||
|
szOID_KEY_USAGE,
|
||||||
|
X509_KEY_USAGE,
|
||||||
|
&keyUsage,
|
||||||
|
true,
|
||||||
|
&extensions[2]))
|
||||||
|
{
|
||||||
|
networkIdentityFreeExtensions(extensions, ARRAYSIZE(extensions));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
CERT_EXTENSIONS certificateExtensions =
|
||||||
|
{
|
||||||
|
.cExtension = (DWORD)ARRAYSIZE(extensions),
|
||||||
|
.rgExtension = extensions,
|
||||||
|
};
|
||||||
|
CRYPT_KEY_PROV_INFO keyProvider =
|
||||||
|
{
|
||||||
|
.pwszContainerName = LG_NETWORK_CERTIFICATE_KEY_NAME,
|
||||||
|
.pwszProvName = MS_KEY_STORAGE_PROVIDER,
|
||||||
|
.dwProvType = 0,
|
||||||
|
.dwFlags = CRYPT_MACHINE_KEYSET,
|
||||||
|
.cProvParam = 0,
|
||||||
|
.rgProvParam = NULL,
|
||||||
|
.dwKeySpec = CERT_NCRYPT_KEY_SPEC,
|
||||||
|
};
|
||||||
|
CRYPT_ALGORITHM_IDENTIFIER signature =
|
||||||
|
{
|
||||||
|
.pszObjId = szOID_ECDSA_SHA256,
|
||||||
|
};
|
||||||
|
CERT_NAME_BLOB subject =
|
||||||
|
{
|
||||||
|
.cbData = encodedNameSize,
|
||||||
|
.pbData = encodedName,
|
||||||
|
};
|
||||||
|
SYSTEMTIME start;
|
||||||
|
SYSTEMTIME end;
|
||||||
|
FILETIME expiration;
|
||||||
|
GetSystemTime(&start);
|
||||||
|
if (!SystemTimeToFileTime(&start, &expiration))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"SystemTimeToFileTime", GetLastError());
|
||||||
|
networkIdentityFreeExtensions(extensions, ARRAYSIZE(extensions));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
ULARGE_INTEGER expirationValue;
|
||||||
|
expirationValue.LowPart = expiration.dwLowDateTime;
|
||||||
|
expirationValue.HighPart = expiration.dwHighDateTime;
|
||||||
|
expirationValue.QuadPart +=
|
||||||
|
(ULONGLONG)LG_NETWORK_CERTIFICATE_LIFETIME_YEARS * 365U * 24U *
|
||||||
|
60U * 60U * 10000000U;
|
||||||
|
expiration.dwLowDateTime = expirationValue.LowPart;
|
||||||
|
expiration.dwHighDateTime = expirationValue.HighPart;
|
||||||
|
if (!FileTimeToSystemTime(&expiration, &end))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"FileTimeToSystemTime", GetLastError());
|
||||||
|
networkIdentityFreeExtensions(extensions, ARRAYSIZE(extensions));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCCERT_CONTEXT certificate = CertCreateSelfSignCertificate(
|
||||||
|
(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE)key,
|
||||||
|
&subject,
|
||||||
|
0,
|
||||||
|
&keyProvider,
|
||||||
|
&signature,
|
||||||
|
&start,
|
||||||
|
&end,
|
||||||
|
&certificateExtensions);
|
||||||
|
networkIdentityFreeExtensions(extensions, ARRAYSIZE(extensions));
|
||||||
|
if (!certificate)
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CertCreateSelfSignCertificate", GetLastError());
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRYPT_DATA_BLOB friendlyName =
|
||||||
|
{
|
||||||
|
.cbData = (DWORD)sizeof LG_NETWORK_CERTIFICATE_FRIENDLY_NAME,
|
||||||
|
.pbData = (BYTE *)LG_NETWORK_CERTIFICATE_FRIENDLY_NAME,
|
||||||
|
};
|
||||||
|
if (!CertSetCertificateContextProperty(
|
||||||
|
certificate,
|
||||||
|
CERT_FRIENDLY_NAME_PROP_ID,
|
||||||
|
0,
|
||||||
|
&friendlyName))
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"CertSetCertificateContextProperty(friendly name)",
|
||||||
|
GetLastError());
|
||||||
|
CertFreeCertificateContext(certificate);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CertAddCertificateContextToStore(
|
||||||
|
store,
|
||||||
|
certificate,
|
||||||
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
||||||
|
&result))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CertAddCertificateContextToStore", GetLastError());
|
||||||
|
}
|
||||||
|
CertFreeCertificateContext(certificate);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
free(encodedName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool networkIdentityStoreHashes(PCCERT_CONTEXT certificate)
|
||||||
|
{
|
||||||
|
BYTE certificateHash[20];
|
||||||
|
DWORD certificateHashSize = sizeof certificateHash;
|
||||||
|
if (!CertGetCertificateContextProperty(
|
||||||
|
certificate,
|
||||||
|
CERT_HASH_PROP_ID,
|
||||||
|
certificateHash,
|
||||||
|
&certificateHashSize))
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"CertGetCertificateContextProperty(CERT_HASH_PROP_ID)",
|
||||||
|
GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE fingerprint[32];
|
||||||
|
DWORD fingerprintSize = sizeof fingerprint;
|
||||||
|
if (!CryptHashCertificate2(
|
||||||
|
L"SHA256",
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
certificate->pbCertEncoded,
|
||||||
|
certificate->cbCertEncoded,
|
||||||
|
fingerprint,
|
||||||
|
&fingerprintSize))
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CryptHashCertificate2(SHA256)", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HKEY key = NULL;
|
||||||
|
const LSTATUS openStatus = RegOpenKeyExW(
|
||||||
|
HKEY_LOCAL_MACHINE,
|
||||||
|
LGIDD_REGKEY,
|
||||||
|
0,
|
||||||
|
KEY_SET_VALUE | KEY_WOW64_64KEY,
|
||||||
|
&key);
|
||||||
|
if (openStatus != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"RegOpenKeyExW(network identity)", (DWORD)openStatus);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LSTATUS status = RegSetValueExW(
|
||||||
|
key,
|
||||||
|
LG_NETWORK_CERTIFICATE_HASH_VALUE,
|
||||||
|
0,
|
||||||
|
REG_BINARY,
|
||||||
|
certificateHash,
|
||||||
|
certificateHashSize);
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
status = RegSetValueExW(
|
||||||
|
key,
|
||||||
|
LG_NETWORK_CERTIFICATE_FINGERPRINT_VALUE,
|
||||||
|
0,
|
||||||
|
REG_BINARY,
|
||||||
|
fingerprint,
|
||||||
|
fingerprintSize);
|
||||||
|
}
|
||||||
|
RegCloseKey(key);
|
||||||
|
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
networkIdentityError(
|
||||||
|
L"RegSetValueExW(network identity)", (DWORD)status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintf(L"Network identity SHA256 fingerprint: ");
|
||||||
|
for (DWORD i = 0; i < fingerprintSize; ++i)
|
||||||
|
{
|
||||||
|
if (i)
|
||||||
|
wprintf(L":");
|
||||||
|
wprintf(L"%02X", (unsigned)fingerprint[i]);
|
||||||
|
}
|
||||||
|
wprintf(L"\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool networkIdentityProvision(void)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
NCRYPT_PROV_HANDLE provider = 0;
|
||||||
|
NCRYPT_KEY_HANDLE key = 0;
|
||||||
|
HCERTSTORE store = NULL;
|
||||||
|
PCCERT_CONTEXT certificate = NULL;
|
||||||
|
|
||||||
|
if (!networkIdentityOpenKey(&provider, &key))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
store = CertOpenStore(
|
||||||
|
CERT_STORE_PROV_SYSTEM_W,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG,
|
||||||
|
L"MY");
|
||||||
|
if (!store)
|
||||||
|
{
|
||||||
|
networkIdentityError(L"CertOpenStore(LocalMachine\\MY)", GetLastError());
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate = networkIdentityFindCertificate(store);
|
||||||
|
if (!certificate)
|
||||||
|
certificate = networkIdentityCreateCertificate(store, key);
|
||||||
|
if (!certificate)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
result = networkIdentityStoreHashes(certificate);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (certificate)
|
||||||
|
CertFreeCertificateContext(certificate);
|
||||||
|
if (store)
|
||||||
|
CertCloseStore(store, 0);
|
||||||
|
if (key)
|
||||||
|
NCryptFreeObject(key);
|
||||||
|
if (provider)
|
||||||
|
NCryptFreeObject(provider);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
28
idd/LGIddInstall/NetworkIdentity.h
Normal file
28
idd/LGIddInstall/NetworkIdentity.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LG_IDD_INSTALL_NETWORK_IDENTITY_H
|
||||||
|
#define LG_IDD_INSTALL_NETWORK_IDENTITY_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool networkIdentityProvision(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user