Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546/* * PROJECT: ReactOS Application compatibility module * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Registry layer manipulation functions * COPYRIGHT: Copyright 2015-2019 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 Wow64QueryFlag(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 | Wow64QueryFlag(), &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);}