+++ /dev/null
-/*
- * PROJECT: ReactOS Application compatibility module
- * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
- * PURPOSE: Registry layer manipulation functions
- * COPYRIGHT: Copyright 2015-2017 Mark Jansen (mark.jansen@reactos.org)
- */
-
-#define WIN32_NO_STATUS
-#include "windef.h"
-#include "winbase.h"
-#include "strsafe.h"
-#include <ntndk.h>
-#include "apphelp.h"
-
-#define GPLK_USER 1
-#define GPLK_MACHINE 2
-#define MAX_LAYER_LENGTH 256
-#define LAYER_APPLY_TO_SYSTEM_EXES 1
-#define LAYER_UNK_FLAG2 2
-
-#ifndef REG_SZ
-#define REG_SZ 1
-#endif
-
-#if defined(__GNUC__)
-#define APPCOMPAT_LAYER_KEY (const WCHAR[]){'\\','S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','A','p','p','C','o','m','p','a','t','F','l','a','g','s','\\','L','a','y','e','r','s',0}
-#define REGISTRY_MACHINE (const WCHAR[]){'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e',0}
-#define SPACE_ONLY (const WCHAR[]){' ',0}
-#define DISALLOWED_LAYER_CHARS (const WCHAR[]){' ','#','!',0}
-#define LAYER_SEPARATORS (const WCHAR[]){' ','\t',0}
-#define SIGN_MEDIA_FMT (const WCHAR[]){'S','I','G','N','.','M','E','D','I','A','=','%','X',' ','%','s',0}
-
-#else
-#define APPCOMPAT_LAYER_KEY L"\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"
-#define REGISTRY_MACHINE L"\\Registry\\Machine"
-#define SPACE_ONLY L" "
-#define DISALLOWED_LAYER_CHARS L" #!"
-#define LAYER_SEPARATORS L" \t"
-#define SIGN_MEDIA_FMT L"SIGN.MEDIA=%X %s"
-#endif
-
-/* Fixme: use RTL_UNICODE_STRING_BUFFER */
-typedef struct SDB_TMP_STR
-{
- UNICODE_STRING Str;
- WCHAR FixedBuffer[MAX_PATH];
-} SDB_TMP_STR, *PSDB_TMP_STR;
-
-void SdbpInitTempStr(PSDB_TMP_STR String)
-{
- String->Str.Buffer = String->FixedBuffer;
- String->Str.Length = 0;
- String->Str.MaximumLength = sizeof(String->FixedBuffer);
-}
-
-void SdbpFreeTempStr(PSDB_TMP_STR String)
-{
- if (String->Str.Buffer != String->FixedBuffer)
- {
- SdbFree(String->Str.Buffer);
- }
-}
-
-void SdbpResizeTempStr(PSDB_TMP_STR String, WORD newLength)
-{
- if (newLength > String->Str.MaximumLength)
- {
- SdbpFreeTempStr(String);
- String->Str.MaximumLength = newLength * sizeof(WCHAR);
- String->Str.Buffer = SdbAlloc(String->Str.MaximumLength);
- String->Str.Length = 0;
- }
-}
-
-BOOL SdbpGetLongPathName(PCWSTR wszPath, PSDB_TMP_STR Result)
-{
- DWORD max = Result->Str.MaximumLength / 2;
- DWORD ret = GetLongPathNameW(wszPath, Result->Str.Buffer, max);
- if (ret)
- {
- if (ret >= max)
- {
- SdbpResizeTempStr(Result, ret);
- max = Result->Str.MaximumLength / 2;
- ret = GetLongPathNameW(wszPath, Result->Str.Buffer, max);
- }
- if (ret && ret < max)
- {
- Result->Str.Length = ret * 2;
- return TRUE;
- }
- }
- SHIM_ERR("Failed to convert short path to long path error 0x%lx\n", GetLastError());
- return FALSE;
-}
-
-BOOL SdbpIsPathOnRemovableMedia(PCWSTR Path)
-{
- WCHAR tmp[] = { 'A',':','\\',0 };
- ULONG type;
- if (!Path || Path[0] == UNICODE_NULL)
- {
- SHIM_ERR("Invalid argument\n");
- return FALSE;
- }
- switch (Path[1])
- {
- case L':':
- break;
- case L'\\':
- SHIM_INFO("\"%S\" is a network path.\n", Path);
- return FALSE;
- default:
- SHIM_INFO("\"%S\" not a full path we can operate on.\n", Path);
- return FALSE;
- }
- tmp[0] = Path[0];
- type = GetDriveTypeW(tmp);
-
- return type == DRIVE_REMOVABLE || type == DRIVE_CDROM;
-}
-
-/* Convert a path on removable media to 'SIGN.MEDIA=%X filename' */
-BOOL SdbpBuildSignMediaId(PSDB_TMP_STR LongPath)
-{
- SDB_TMP_STR Scratch;
- PWCHAR Ptr;
-
- SdbpInitTempStr(&Scratch);
- SdbpResizeTempStr(&Scratch, LongPath->Str.Length / sizeof(WCHAR) + 30);
- StringCbCopyNW(Scratch.Str.Buffer, Scratch.Str.MaximumLength, LongPath->Str.Buffer, LongPath->Str.Length);
- Ptr = wcsrchr(LongPath->Str.Buffer, '\\');
- if (Ptr)
- {
- HANDLE FindHandle;
- WIN32_FIND_DATAW FindData;
- Ptr[1] = '*';
- Ptr[2] = '\0';
- FindHandle = FindFirstFileW(LongPath->Str.Buffer, &FindData);
- if (FindHandle != INVALID_HANDLE_VALUE)
- {
- DWORD SignMedia = 0;
- do
- {
- if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && FindData.nFileSizeLow)
- SignMedia = SignMedia << 1 ^ FindData.nFileSizeLow;
- } while (FindNextFileW(FindHandle, &FindData));
-
- FindClose(FindHandle);
- SdbpResizeTempStr(LongPath, (LongPath->Str.Length >> 1) + 20);
- StringCbPrintfW(LongPath->Str.Buffer, LongPath->Str.MaximumLength, SIGN_MEDIA_FMT, SignMedia, Scratch.Str.Buffer + 3);
- LongPath->Str.Length = (USHORT)SdbpStrlen(LongPath->Str.Buffer) * sizeof(WCHAR);
- SdbpFreeTempStr(&Scratch);
- return TRUE;
- }
- }
- SdbpFreeTempStr(&Scratch);
- SdbpFreeTempStr(LongPath);
- return FALSE;
-}
-
-/* Convert a given path to a long or media path */
-BOOL SdbpResolvePath(PSDB_TMP_STR LongPath, PCWSTR wszPath)
-{
- SdbpInitTempStr(LongPath);
- if (!SdbpGetLongPathName(wszPath, LongPath))
- {
- SdbpFreeTempStr(LongPath);
- return FALSE;
- }
- if (SdbpIsPathOnRemovableMedia(LongPath->Str.Buffer))
- {
- return SdbpBuildSignMediaId(LongPath);
- }
- return TRUE;
-}
-
-static ACCESS_MASK g_QueryFlag = 0xffffffff;
-ACCESS_MASK QueryFlag(void)
-{
- if (g_QueryFlag == 0xffffffff)
- {
- ULONG_PTR wow64_ptr = 0;
- NTSTATUS Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &wow64_ptr, sizeof(wow64_ptr), NULL);
- g_QueryFlag = (NT_SUCCESS(Status) && wow64_ptr != 0) ? KEY_WOW64_64KEY : 0;
- }
- return g_QueryFlag;
-}
-
-NTSTATUS SdbpOpenKey(PUNICODE_STRING FullPath, BOOL bMachine, ACCESS_MASK Access, PHANDLE KeyHandle)
-{
- UNICODE_STRING BasePath;
- const WCHAR* LayersKey = APPCOMPAT_LAYER_KEY;
- OBJECT_ATTRIBUTES ObjectLayer = RTL_INIT_OBJECT_ATTRIBUTES(FullPath, OBJ_CASE_INSENSITIVE);
- NTSTATUS Status;
- FullPath->Buffer = NULL;
- FullPath->Length = FullPath->MaximumLength = 0;
- if (bMachine)
- {
- RtlInitUnicodeString(&BasePath, REGISTRY_MACHINE);
- }
- else
- {
- Status = RtlFormatCurrentUserKeyPath(&BasePath);
- if (!NT_SUCCESS(Status))
- {
- SHIM_ERR("Unable to acquire user registry key, Error: 0x%lx\n", Status);
- return Status;
- }
- }
- FullPath->MaximumLength = (USHORT)(BasePath.Length + SdbpStrsize(LayersKey));
- FullPath->Buffer = SdbAlloc(FullPath->MaximumLength);
- FullPath->Length = 0;
- RtlAppendUnicodeStringToString(FullPath, &BasePath);
- if (!bMachine)
- RtlFreeUnicodeString(&BasePath);
- RtlAppendUnicodeToString(FullPath, LayersKey);
-
- Status = NtOpenKey(KeyHandle, Access | QueryFlag(), &ObjectLayer);
- if (!NT_SUCCESS(Status))
- {
- SHIM_ERR("Unable to open Key \"%wZ\" Status 0x%lx\n", FullPath, Status);
- SdbFree(FullPath->Buffer);
- FullPath->Buffer = NULL;
- }
- return Status;
-}
-
-
-BOOL SdbpGetPermLayersInternal(PUNICODE_STRING FullPath, PWSTR pwszLayers, PDWORD pdwBytes, BOOL bMachine)
-{
- UNICODE_STRING FullKey;
- ULONG ValueBuffer[(MAX_LAYER_LENGTH * sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG) - 1) / sizeof(ULONG)];
- PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
- ULONG Length = 0;
- HANDLE KeyHandle;
- NTSTATUS Status;
-
- Status = SdbpOpenKey(&FullKey, bMachine, KEY_QUERY_VALUE, &KeyHandle);
- if (NT_SUCCESS(Status))
- {
- Status = NtQueryValueKey(KeyHandle, FullPath, KeyValuePartialInformation, PartialInfo, sizeof(ValueBuffer), &Length);
- if (NT_SUCCESS(Status))
- {
- StringCbCopyNW(pwszLayers, *pdwBytes, (PCWSTR)PartialInfo->Data, PartialInfo->DataLength);
- *pdwBytes = PartialInfo->DataLength;
- }
- else
- {
- SHIM_INFO("Failed to read value info from Key \"%wZ\" Status 0x%lx\n", &FullKey, Status);
- }
- NtClose(KeyHandle);
- SdbFree(FullKey.Buffer);
- }
- return NT_SUCCESS(Status);
-}
-
-BOOL SdbDeletePermLayerKeys(PCWSTR wszPath, BOOL bMachine)
-{
- UNICODE_STRING FullKey;
- SDB_TMP_STR LongPath;
- HANDLE KeyHandle;
- NTSTATUS Status;
-
- if (!SdbpResolvePath(&LongPath, wszPath))
- return FALSE;
-
- Status = SdbpOpenKey(&FullKey, bMachine, KEY_SET_VALUE, &KeyHandle);
- if (NT_SUCCESS(Status))
- {
- Status = NtDeleteValueKey(KeyHandle, &LongPath.Str);
- if (!NT_SUCCESS(Status))
- {
- SHIM_INFO("Failed to delete value from Key \"%wZ\" Status 0x%lx\n", &FullKey, Status);
- /* This is what we want, so if the key didnt exist, we should not fail :) */
- if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
- Status = STATUS_SUCCESS;
- }
- NtClose(KeyHandle);
- SdbFree(FullKey.Buffer);
- }
- SdbpFreeTempStr(&LongPath);
- return NT_SUCCESS(Status);
-}
-
-BOOL SdbpMatchLayer(PCWSTR start, PCWSTR end, PCWSTR compare)
-{
- size_t len;
- if (!end)
- return !wcsicmp(start, compare);
- len = end - start;
- return wcslen(compare) == len && !_wcsnicmp(start, compare, len);
-}
-
-BOOL SdbpAppendLayer(PWSTR target, DWORD len, PCWSTR layer, PCWSTR end)
-{
- NTSTATUS Status = STATUS_SUCCESS;
- if (target[0])
- Status = StringCbCatW(target, len, SPACE_ONLY);
-
- if (NT_SUCCESS(Status))
- {
- if (end)
- Status = StringCbCatNW(target, len, layer, (end - layer) * sizeof(WCHAR));
- else
- Status = StringCbCatW(target, len, layer);
- }
-
- return NT_SUCCESS(Status);
-}
-
-
-/**
- * Determine if we allow permission layers to apply on this file.
- *
- * @param [in] Path Full pathname of the file, only the drive part is used.
- *
- * @return TRUE if we allow permission layer, FALSE if not.
- */
-BOOL WINAPI AllowPermLayer(PCWSTR Path)
-{
- WCHAR tmp[] = { 'A',':','\\', 0 };
- ULONG type;
- if (!Path)
- {
- SHIM_ERR("Invalid argument\n");
- return FALSE;
- }
- switch (Path[1])
- {
- case L':':
- break;
- case L'\\':
- SHIM_INFO("\"%S\" is a network path.\n", Path);
- return FALSE;
- default:
- SHIM_INFO("\"%S\" not a full path we can operate on.\n", Path);
- return FALSE;
- }
- tmp[0] = Path[0];
- type = GetDriveTypeW(tmp);
- if (type == DRIVE_REMOTE)
- {
- /* The logging here indicates that it does not like a CDROM or removable media, but it only
- seems to bail out on a media that reports it is remote...
- I have included correct logging, I doubt anyone would parse the logging, so this shouldnt break anything. */
- SHIM_INFO("\"%S\" is on a remote drive.\n", Path);
- return FALSE;
- }
- return TRUE;
-}
-
-/**
- * Read the layers specified for the application.
- *
- * @param [in] wszPath Full pathname of the file.
- * @param [out] pwszLayers On return, the layers set on the file.
- * @param pdwBytes The size of the pwszLayers buffer in bytes, and on return the size of
- * the data written (in bytes)
- * @param [in] dwFlags The flags, [GPLK_USER | GPLK_MACHINE].
- *
- * @return TRUE if it succeeds, FALSE if it fails.
- */
-BOOL WINAPI SdbGetPermLayerKeys(PCWSTR wszPath, PWSTR pwszLayers, PDWORD pdwBytes, DWORD dwFlags)
-{
- BOOL Result = FALSE;
- SDB_TMP_STR LongPath;
- DWORD dwBytes, dwTotal = 0;
- if (!wszPath || !pdwBytes)
- {
- SHIM_ERR("NULL parameter passed for wszPath or pdwBytes.\n");
- return FALSE;
- }
-
- if (!SdbpResolvePath(&LongPath, wszPath))
- return FALSE;
- dwBytes = *pdwBytes;
- if (dwFlags & GPLK_MACHINE)
- {
- if (SdbpGetPermLayersInternal(&LongPath.Str, pwszLayers, &dwBytes, TRUE))
- {
- Result = TRUE;
- dwTotal = dwBytes - sizeof(WCHAR); /* Compensate for the nullterm. */
- pwszLayers += dwTotal / sizeof(WCHAR);
- dwBytes = *pdwBytes - dwBytes;
- if (dwFlags & GPLK_USER)
- {
- *(pwszLayers++) = L' ';
- *pwszLayers = L'\0';
- dwBytes -= sizeof(WCHAR);
- dwTotal += sizeof(WCHAR);
- }
- }
- }
- if (dwFlags & GPLK_USER)
- {
- if (SdbpGetPermLayersInternal(&LongPath.Str, pwszLayers, &dwBytes, FALSE))
- {
- Result = TRUE;
- dwTotal += dwBytes - sizeof(WCHAR); /* Compensate for the nullterm. */
- }
- else if (dwTotal > 0 && pwszLayers[-1] == L' ')
- {
- pwszLayers[-1] = '\0';
- dwTotal -= sizeof(WCHAR);
- }
- }
- if (dwTotal)
- dwTotal += sizeof(WCHAR);
- *pdwBytes = dwTotal;
- SdbpFreeTempStr(&LongPath);
- return Result;
-}
-
-/**
- * Set or clear the Layer key.
- *
- * @param [in] wszPath Full pathname of the file.
- * @param [in] wszLayers The layers to add (space separated), or an empty string / NULL to
- * remove all layers.
- * @param [in] bMachine TRUE to machine.
- *
- * @return TRUE if it succeeds, FALSE if it fails.
- */
-BOOL WINAPI SdbSetPermLayerKeys(PCWSTR wszPath, PCWSTR wszLayers, BOOL bMachine)
-{
- UNICODE_STRING FullKey;
- SDB_TMP_STR LongPath;
- HANDLE KeyHandle;
- NTSTATUS Status;
-
- if (!wszLayers || *wszLayers == '\0')
- return SdbDeletePermLayerKeys(wszPath, bMachine);
-
- if (!SdbpResolvePath(&LongPath, wszPath))
- return FALSE;
-
- Status = SdbpOpenKey(&FullKey, bMachine, KEY_SET_VALUE, &KeyHandle);
- if (NT_SUCCESS(Status))
- {
- Status = NtSetValueKey(KeyHandle, &LongPath.Str, 0, REG_SZ, (PVOID)wszLayers, SdbpStrsize(wszLayers));
- if (!NT_SUCCESS(Status))
- {
- SHIM_INFO("Failed to write a value to Key \"%wZ\" Status 0x%lx\n", &FullKey, Status);
- if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
- Status = STATUS_SUCCESS;
- }
- NtClose(KeyHandle);
- SdbFree(FullKey.Buffer);
- }
- SdbpFreeTempStr(&LongPath);
- return NT_SUCCESS(Status);
-}
-
-/**
- * Adds or removes a single layer entry.
- *
- * @param [in] wszPath Full pathname of the file.
- * @param [in] wszLayer The layer to add or remove.
- * @param [in] dwFlags Additional flags to add / remove [LAYER_APPLY_TO_SYSTEM_EXES | ???].
- * @param [in] bMachine When TRUE, the setting applies to all users, when FALSE only applies
- * to the current user.
- * @param [in] bEnable TRUE to enable, FALSE to disable a layer / flag specified.
- *
- * @return TRUE if it succeeds, FALSE if it fails.
- */
-BOOL WINAPI SetPermLayerState(PCWSTR wszPath, PCWSTR wszLayer, DWORD dwFlags, BOOL bMachine, BOOL bEnable)
-{
- WCHAR fullLayer[MAX_LAYER_LENGTH] = { 0 };
- WCHAR newLayer[MAX_LAYER_LENGTH] = { 0 };
- DWORD dwBytes = sizeof(fullLayer), dwWriteFlags = 0;
- PWSTR start, p;
-
- if (!wszLayer)
- {
- SHIM_ERR("Invalid argument\n");
- return FALSE;
- }
- if (dwFlags & ~(LAYER_APPLY_TO_SYSTEM_EXES | LAYER_UNK_FLAG2))
- {
- SHIM_ERR("Invalid flags\n");
- return FALSE;
- }
- p = wcspbrk(wszLayer, DISALLOWED_LAYER_CHARS);
- if (p)
- {
- switch (*p)
- {
- case ' ':
- SHIM_ERR("Only one layer can be passed in at a time.\n");
- return FALSE;
- case '#':
- case '!':
- SHIM_ERR("Flags cannot be passed in with the layer name.\n");
- return FALSE;
- }
- }
- if (!SdbGetPermLayerKeys(wszPath, fullLayer, &dwBytes, bMachine ? GPLK_MACHINE : GPLK_USER))
- {
- fullLayer[0] = '\0';
- dwBytes = sizeof(fullLayer);
- }
-
- start = fullLayer;
- while (*start == '!' || *start == '#' || *start == ' ' || *start == '\t')
- {
- if (*start == '#')
- dwWriteFlags |= LAYER_APPLY_TO_SYSTEM_EXES;
- else if (*start == '!')
- dwWriteFlags |= LAYER_UNK_FLAG2;
- start++;
- }
- if (bEnable)
- dwWriteFlags |= dwFlags;
- else
- dwWriteFlags &= ~dwFlags;
-
- p = newLayer;
- if (dwWriteFlags & LAYER_UNK_FLAG2)
- *(p++) = '!';
- if (dwWriteFlags & LAYER_APPLY_TO_SYSTEM_EXES)
- *(p++) = '#';
-
- do
- {
- while (*start == ' ' || *start == '\t')
- ++start;
-
- if (*start == '\0')
- break;
- p = wcspbrk(start, LAYER_SEPARATORS);
- if (!SdbpMatchLayer(start, p, wszLayer))
- {
- SdbpAppendLayer(newLayer, sizeof(newLayer), start, p);
- }
- start = p + 1;
- } while (p);
-
- if (bEnable && wszLayer[0])
- {
- SdbpAppendLayer(newLayer, sizeof(newLayer), wszLayer, NULL);
- }
-
- return SdbSetPermLayerKeys(wszPath, newLayer, bMachine);
-}