From 77e6348f5fce69070852df0e516f649012930d30 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 5 Mar 2023 11:41:32 +0900 Subject: [PATCH] [NTUSER][USER32] Refactor NtUserLoadKeyboardLayoutEx (#5107) - Split some code of NtUserLoadKeyboardLayoutEx to newly-added co_IntLoadKeyboardLayoutEx helper function. - Modify NtUserLoadKeyboardLayoutEx prototype. - Move co_UserImmLoadLayout code. - Implement KLF_REORDER. - Rename UserLoadKbdLayout as co_UserLoadKbdLayout. - Improve LoadKeyboardLayoutEx. CORE-11700 --- win32ss/include/ntuser.h | 8 +- win32ss/user/ntuser/kbdlayout.c | 257 +++++++++++++++++----------- win32ss/user/user32/windows/input.c | 10 +- 3 files changed, 165 insertions(+), 110 deletions(-) diff --git a/win32ss/include/ntuser.h b/win32ss/include/ntuser.h index cd7fcdf7034..656d3b443fa 100644 --- a/win32ss/include/ntuser.h +++ b/win32ss/include/ntuser.h @@ -2736,12 +2736,12 @@ NtUserKillTimer( HKL NTAPI NtUserLoadKeyboardLayoutEx( - IN HANDLE Handle, + IN HANDLE hFile, IN DWORD offTable, - IN PUNICODE_STRING puszKeyboardName, - IN HKL hKL, + IN PVOID pTables, + IN HKL hOldKL, IN PUNICODE_STRING puszKLID, - IN DWORD dwKLID, + IN DWORD dwNewKL, IN UINT Flags); BOOL diff --git a/win32ss/user/ntuser/kbdlayout.c b/win32ss/user/ntuser/kbdlayout.c index 6ce1523d208..9a5c43d60a4 100644 --- a/win32ss/user/ntuser/kbdlayout.c +++ b/win32ss/user/ntuser/kbdlayout.c @@ -369,12 +369,12 @@ cleanup: } /* - * UserLoadKbdLayout + * co_UserLoadKbdLayout * * Loads keyboard layout and creates KL object */ static PKL -UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL) +co_UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL) { LCID lCid; CHARSETINFO cs; @@ -846,6 +846,129 @@ IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL) return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0); } +PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL) +{ + PIMEINFOEX piiex; + + if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE()) + return NULL; + + piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME); + if (!piiex) + return NULL; + + if (!co_ClientImmLoadLayout(hKL, piiex)) + { + ExFreePoolWithTag(piiex, USERTAG_IME); + return NULL; + } + + return piiex; +} + +HKL APIENTRY +co_IntLoadKeyboardLayoutEx( + IN OUT PWINSTATION_OBJECT pWinSta, + IN HANDLE hSafeFile, + IN HKL hOldKL, + IN PUNICODE_STRING puszSafeKLID, + IN HKL hNewKL, + IN UINT Flags) +{ + PKL pOldKL, pNewKL; + + UNREFERENCED_PARAMETER(hSafeFile); + + if (hNewKL == NULL || (pWinSta->Flags & WSS_NOIO)) + return NULL; + + /* If hOldKL is specified, unload it and load new layput as default */ + if (hOldKL && hOldKL != hNewKL) + { + pOldKL = UserHklToKbl(hOldKL); + if (pOldKL) + UserUnloadKbl(pOldKL); + } + + /* FIXME: It seems KLF_RESET is only supported for WINLOGON */ + + /* Let's see if layout was already loaded. */ + pNewKL = UserHklToKbl(hNewKL); + if (!pNewKL) + { + /* It wasn't, so load it. */ + pNewKL = co_UserLoadKbdLayout(puszSafeKLID, hNewKL); + if (!pNewKL) + return NULL; + + if (gspklBaseLayout) + { + /* Find last not unloaded layout */ + PKL pLastKL = gspklBaseLayout->pklPrev; + while (pLastKL != gspklBaseLayout && (pLastKL->dwKL_Flags & KLF_UNLOAD)) + pLastKL = pLastKL->pklPrev; + + /* Add new layout to the list */ + pNewKL->pklNext = pLastKL->pklNext; + pNewKL->pklPrev = pLastKL; + pNewKL->pklNext->pklPrev = pNewKL; + pNewKL->pklPrev->pklNext = pNewKL; + } + else + { + /* This is the first layout */ + pNewKL->pklNext = pNewKL; + pNewKL->pklPrev = pNewKL; + gspklBaseLayout = pNewKL; + } + + pNewKL->piiex = co_UserImmLoadLayout(hNewKL); + } + + /* If this layout was prepared to unload, undo it */ + pNewKL->dwKL_Flags &= ~KLF_UNLOAD; + + /* Reorder if necessary */ + if (Flags & KLF_REORDER) + IntReorderKeyboardLayouts(pWinSta, pNewKL); + + /* Activate this layout in current thread */ + if (Flags & KLF_ACTIVATE) + co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pNewKL, Flags); + + /* Send shell message */ + if (!(Flags & KLF_NOTELLSHELL)) + co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hNewKL); + + /* FIXME: KLF_REPLACELANG */ + + return hNewKL; +} + +HANDLE FASTCALL IntVerifyKeyboardFileHandle(HANDLE hFile) +{ + PFILE_OBJECT FileObject; + NTSTATUS Status; + + if (hFile == INVALID_HANDLE_VALUE) + return NULL; + + Status = ObReferenceObjectByHandle(hFile, FILE_READ_DATA, NULL, UserMode, + (PVOID*)&FileObject, NULL); + if (!NT_SUCCESS(Status)) + { + ERR("0x%08X\n", Status); + return NULL; + } + + /* FIXME: Is the file in the system directory? */ + + if (FileObject) + ObDereferenceObject(FileObject); + + return hFile; +} + /* EXPORTS *******************************************************************/ /* @@ -1009,50 +1132,34 @@ cleanup: return bRet; } -/* Win: xxxImmLoadLayout */ -PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL) -{ - PIMEINFOEX piiex; - - if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE()) - return NULL; - - piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME); - if (!piiex) - return NULL; - - if (!co_ClientImmLoadLayout(hKL, piiex)) - { - ExFreePoolWithTag(piiex, USERTAG_IME); - return NULL; - } - - return piiex; -} - /* * NtUserLoadKeyboardLayoutEx * * Loads keyboard layout with given locale id * - * NOTE: We adopt a different design from Microsoft's one for security reason. - * We don't use the 1st and 3rd parameters of NtUserLoadKeyboardLayoutEx. + * NOTE: We adopt a different design from Microsoft's one due to security reason. + * We don't use the 3rd parameter of NtUserLoadKeyboardLayoutEx. + * See https://bugtraq.securityfocus.com/detail/50056B96.6040306 */ HKL -APIENTRY +NTAPI NtUserLoadKeyboardLayoutEx( - IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c) - IN DWORD offTable, // Offset to KbdTables - IN PUNICODE_STRING puszKeyboardName, // Not used? - IN HKL hklUnload, - IN PUNICODE_STRING pustrKLID, - IN DWORD hkl, + IN HANDLE hFile, + IN DWORD offTable, + IN PVOID pTables, + IN HKL hOldKL, + IN PUNICODE_STRING puszKLID, + IN DWORD dwNewKL, IN UINT Flags) { - HKL hklRet = NULL; - PKL pKl = NULL, pklLast; + HKL hRetKL; WCHAR Buffer[KL_NAMELENGTH]; - UNICODE_STRING ustrSafeKLID; + UNICODE_STRING uszSafeKLID; + PWINSTATION_OBJECT pWinSta; + HANDLE hSafeFile; + + UNREFERENCED_PARAMETER(offTable); + UNREFERENCED_PARAMETER(pTables); if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG| KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS| @@ -1063,14 +1170,12 @@ NtUserLoadKeyboardLayoutEx( return NULL; } - /* FIXME: It seems KLF_RESET is only supported for WINLOGON */ - - RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&uszSafeKLID, Buffer, sizeof(Buffer)); _SEH2_TRY { - ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1); - ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1); - RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID); + ProbeForRead(puszKLID, sizeof(*puszKLID), 1); + ProbeForRead(puszKLID->Buffer, sizeof(puszKLID->Length), 1); + RtlCopyUnicodeString(&uszSafeKLID, puszKLID); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { @@ -1081,67 +1186,19 @@ NtUserLoadKeyboardLayoutEx( UserEnterExclusive(); - /* If hklUnload is specified, unload it and load new layput as default */ - if (hklUnload && (hklUnload != UlongToHandle(hkl))) - { - pKl = UserHklToKbl(hklUnload); - if (pKl) - UserUnloadKbl(pKl); - } + hSafeFile = (hFile ? IntVerifyKeyboardFileHandle(hFile) : NULL); + pWinSta = IntGetProcessWindowStation(NULL); + hRetKL = co_IntLoadKeyboardLayoutEx(pWinSta, + hSafeFile, + hOldKL, + &uszSafeKLID, + (HKL)(DWORD_PTR)dwNewKL, + Flags); + if (hSafeFile) + ZwClose(hSafeFile); - /* Let's see if layout was already loaded. */ - pKl = UserHklToKbl(UlongToHandle(hkl)); - if (!pKl) - { - /* It wasn't, so load it. */ - pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl)); - if (!pKl) - goto cleanup; - - if (gspklBaseLayout) - { - /* Find last not unloaded layout */ - pklLast = gspklBaseLayout->pklPrev; - while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD) - pklLast = pklLast->pklPrev; - - /* Add new layout to the list */ - pKl->pklNext = pklLast->pklNext; - pKl->pklPrev = pklLast; - pKl->pklNext->pklPrev = pKl; - pKl->pklPrev->pklNext = pKl; - } - else - { - /* This is the first layout */ - pKl->pklNext = pKl; - pKl->pklPrev = pKl; - gspklBaseLayout = pKl; - } - - pKl->piiex = co_UserImmLoadLayout(UlongToHandle(hkl)); - } - - /* If this layout was prepared to unload, undo it */ - pKl->dwKL_Flags &= ~KLF_UNLOAD; - - /* Activate this layout in current thread */ - if (Flags & KLF_ACTIVATE) - co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags); - - /* Send shell message */ - if (!(Flags & KLF_NOTELLSHELL)) - co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl); - - /* Return hkl on success */ - hklRet = UlongToHandle(hkl); - - /* FIXME: KLF_REPLACELANG - KLF_REORDER */ - -cleanup: UserLeave(); - return hklRet; + return hRetKL; } /* diff --git a/win32ss/user/user32/windows/input.c b/win32ss/user/user32/windows/input.c index d3b7e114ba9..48b18b069da 100644 --- a/win32ss/user/user32/windows/input.c +++ b/win32ss/user/user32/windows/input.c @@ -721,9 +721,9 @@ VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName) /* * @unimplemented * - * NOTE: We adopt a different design from Microsoft's one for security reason. + * NOTE: We adopt a different design from Microsoft's one due to security reason. + * See NtUserLoadKeyboardLayoutEx. */ -/* Win: LoadKeyboardLayoutWorker */ HKL APIENTRY IntLoadKeyboardLayout( _In_ HKL hklUnload, @@ -733,7 +733,6 @@ IntLoadKeyboardLayout( _In_ BOOL unknown5) { DWORD dwKLID, dwHKL, dwType, dwSize; - UNICODE_STRING ustrKbdName; UNICODE_STRING ustrKLID; WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"; WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80]; @@ -807,7 +806,7 @@ IntLoadKeyboardLayout( szImeFileName[_countof(szImeFileName) - 1] = UNICODE_NULL; GetSystemLibraryPath(szPath, _countof(szPath), szImeFileName); - /* We don't allow the invalid "IME File" values for security reason */ + /* We don't allow the invalid "IME File" values due to security reason */ if (dwType != REG_SZ || szImeFileName[0] == 0 || wcscspn(szImeFileName, L":\\/") != wcslen(szImeFileName) || GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES) /* Does not exist? */ @@ -833,9 +832,8 @@ IntLoadKeyboardLayout( dwHKL = MAKELONG(wLow, wHigh); - ZeroMemory(&ustrKbdName, sizeof(ustrKbdName)); RtlInitUnicodeString(&ustrKLID, pwszKLID); - hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, &ustrKbdName, NULL, &ustrKLID, dwHKL, Flags); + hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, NULL, hklUnload, &ustrKLID, dwHKL, Flags); CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL); return hNewKL; }