From bc1519dd87a96d37a1b9c64efb43901d5c386872 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 12 Feb 2020 09:18:24 +0900 Subject: [PATCH] Shell Folders: Reject invalid characters (#2328) Reject invalid input filename characters by using shell32!SHLimitInputEdit function and IItemNameLimits interface. Improve SHLimitInputEdit to sanitize paste. CORE-11701 --- dll/win32/shell32/CDefView.cpp | 4 + dll/win32/shell32/folders/CDesktopFolder.h | 26 ++++- dll/win32/shell32/folders/CFSFolder.h | 26 ++++- dll/win32/shell32/folders/CMyDocsFolder.h | 26 ++++- dll/win32/shell32/shellmenu/CMergedFolder.h | 24 +++++ dll/win32/shell32/wine/shellord.c | 110 ++++++++++++++++++-- sdk/include/reactos/undocshell.h | 13 +++ 7 files changed, 219 insertions(+), 10 deletions(-) diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index 608ada85bf4..2bbf523fbe7 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -1989,9 +1989,13 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl m_pSFParent->GetAttributesOf(1, &pidl, &dwAttr); if (SFGAO_CANRENAME & dwAttr) { + HWND hEdit = reinterpret_cast(m_ListView.SendMessage(LVM_GETEDITCONTROL)); + SHLimitInputEdit(hEdit, m_pSFParent); + m_isEditing = TRUE; return FALSE; } + return TRUE; } diff --git a/dll/win32/shell32/folders/CDesktopFolder.h b/dll/win32/shell32/folders/CDesktopFolder.h index 3ac844323aa..eddeb8043bd 100644 --- a/dll/win32/shell32/folders/CDesktopFolder.h +++ b/dll/win32/shell32/folders/CDesktopFolder.h @@ -28,7 +28,8 @@ class CDesktopFolder : public CComObjectRootEx, public IShellFolder2, public IPersistFolder2, - public IContextMenuCB + public IContextMenuCB, + public IItemNameLimits { private: /* both paths are parsible from the desktop */ @@ -79,6 +80,28 @@ class CDesktopFolder : // IContextMenuCB virtual HRESULT WINAPI CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); + /*** IItemNameLimits methods ***/ + + STDMETHODIMP + GetMaxLength(LPCWSTR pszName, int *piMaxNameLen) + { + return E_NOTIMPL; + } + + STDMETHODIMP + GetValidCharacters(LPWSTR *ppwszValidChars, LPWSTR *ppwszInvalidChars) + { + if (ppwszValidChars) + { + *ppwszValidChars = NULL; + } + if (ppwszInvalidChars) + { + SHStrDupW(INVALID_FILETITLE_CHARACTERSW, ppwszInvalidChars); + } + return S_OK; + } + DECLARE_REGISTRY_RESOURCEID(IDR_SHELLDESKTOP) DECLARE_CENTRAL_INSTANCE_NOT_AGGREGATABLE(CDesktopFolder) @@ -90,6 +113,7 @@ class CDesktopFolder : COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2) COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) + COM_INTERFACE_ENTRY_IID(IID_IItemNameLimits, IItemNameLimits) END_COM_MAP() }; diff --git a/dll/win32/shell32/folders/CFSFolder.h b/dll/win32/shell32/folders/CFSFolder.h index 6c6f055a3f8..c5f1d1bf2ae 100644 --- a/dll/win32/shell32/folders/CFSFolder.h +++ b/dll/win32/shell32/folders/CFSFolder.h @@ -29,7 +29,8 @@ class CFSFolder : public IShellFolder2, public IPersistFolder3, public IContextMenuCB, - public IShellFolderViewCB + public IShellFolderViewCB, + public IItemNameLimits { private: const CLSID *m_pclsid; @@ -88,6 +89,28 @@ class CFSFolder : // IShellFolderViewCB virtual HRESULT WINAPI MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam); + /*** IItemNameLimits methods ***/ + + STDMETHODIMP + GetMaxLength(LPCWSTR pszName, int *piMaxNameLen) + { + return E_NOTIMPL; + } + + STDMETHODIMP + GetValidCharacters(LPWSTR *ppwszValidChars, LPWSTR *ppwszInvalidChars) + { + if (ppwszValidChars) + { + *ppwszValidChars = NULL; + } + if (ppwszInvalidChars) + { + SHStrDupW(INVALID_FILETITLE_CHARACTERSW, ppwszInvalidChars); + } + return S_OK; + } + DECLARE_REGISTRY_RESOURCEID(IDR_SHELLFSFOLDER) DECLARE_NOT_AGGREGATABLE(CFSFolder) @@ -101,6 +124,7 @@ class CFSFolder : COM_INTERFACE_ENTRY_IID(IID_IPersistFolder3, IPersistFolder3) COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB) + COM_INTERFACE_ENTRY_IID(IID_IItemNameLimits, IItemNameLimits) END_COM_MAP() protected: diff --git a/dll/win32/shell32/folders/CMyDocsFolder.h b/dll/win32/shell32/folders/CMyDocsFolder.h index 9f89621fae1..6bed840fcbc 100644 --- a/dll/win32/shell32/folders/CMyDocsFolder.h +++ b/dll/win32/shell32/folders/CMyDocsFolder.h @@ -26,7 +26,8 @@ class CMyDocsFolder : public CComCoClass, public CComObjectRootEx, public IShellFolder2, - public IPersistFolder2 + public IPersistFolder2, + public IItemNameLimits { private: CComPtr m_pisfInner; @@ -65,6 +66,28 @@ class CMyDocsFolder : // IPersistFolder2 virtual HRESULT WINAPI GetCurFolder(PIDLIST_ABSOLUTE * pidl); + /*** IItemNameLimits methods ***/ + + STDMETHODIMP + GetMaxLength(LPCWSTR pszName, int *piMaxNameLen) + { + return E_NOTIMPL; + } + + STDMETHODIMP + GetValidCharacters(LPWSTR *ppwszValidChars, LPWSTR *ppwszInvalidChars) + { + if (ppwszValidChars) + { + *ppwszValidChars = NULL; + } + if (ppwszInvalidChars) + { + SHStrDupW(INVALID_FILETITLE_CHARACTERSW, ppwszInvalidChars); + } + return S_OK; + } + DECLARE_REGISTRY_RESOURCEID(IDR_MYDOCUMENTS) DECLARE_NOT_AGGREGATABLE(CMyDocsFolder) @@ -76,6 +99,7 @@ class CMyDocsFolder : COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2) COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) + COM_INTERFACE_ENTRY_IID(IID_IItemNameLimits, IItemNameLimits) END_COM_MAP() }; diff --git a/dll/win32/shell32/shellmenu/CMergedFolder.h b/dll/win32/shell32/shellmenu/CMergedFolder.h index 4bc9ee20278..e76fc98c098 100644 --- a/dll/win32/shell32/shellmenu/CMergedFolder.h +++ b/dll/win32/shell32/shellmenu/CMergedFolder.h @@ -50,6 +50,7 @@ class CMergedFolder : public CComObjectRootEx, public IShellFolder2, public IPersistFolder2, + public IItemNameLimits, public IAugmentedShellFolder3 // -- undocumented //public IShellService, // DEPRECATED IE4 interface: https://msdn.microsoft.com/en-us/library/windows/desktop/bb774870%28v=vs.85%29.aspx //public ITranslateShellChangeNotify,// -- undocumented @@ -84,6 +85,7 @@ public: COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2) + COM_INTERFACE_ENTRY_IID(IID_IItemNameLimits, IItemNameLimits) COM_INTERFACE_ENTRY_IID(IID_IAugmentedShellFolder, IAugmentedShellFolder) COM_INTERFACE_ENTRY_IID(IID_IAugmentedShellFolder2, IAugmentedShellFolder2) COM_INTERFACE_ENTRY_IID(IID_IAugmentedShellFolder3, IAugmentedShellFolder3) @@ -196,6 +198,28 @@ public: // IPersistFolder2 virtual HRESULT STDMETHODCALLTYPE GetCurFolder(PIDLIST_ABSOLUTE * pidl); + /*** IItemNameLimits methods ***/ + + STDMETHODIMP + GetMaxLength(LPCWSTR pszName, int *piMaxNameLen) + { + return E_NOTIMPL; + } + + STDMETHODIMP + GetValidCharacters(LPWSTR *ppwszValidChars, LPWSTR *ppwszInvalidChars) + { + if (ppwszValidChars) + { + *ppwszValidChars = NULL; + } + if (ppwszInvalidChars) + { + SHStrDupW(INVALID_FILETITLE_CHARACTERSW, ppwszInvalidChars); + } + return S_OK; + } + // IAugmentedShellFolder2 virtual HRESULT STDMETHODCALLTYPE AddNameSpace(LPGUID lpGuid, IShellFolder * psf, LPCITEMIDLIST pcidl, ULONG dwUnknown); virtual HRESULT STDMETHODCALLTYPE GetNameSpaceID(LPCITEMIDLIST pcidl, LPGUID lpGuid); diff --git a/dll/win32/shell32/wine/shellord.c b/dll/win32/shell32/wine/shellord.c index 404ecc2a98b..9b4c6ab9eba 100644 --- a/dll/win32/shell32/wine/shellord.c +++ b/dll/win32/shell32/wine/shellord.c @@ -2179,10 +2179,88 @@ UxSubclassInfo_Destroy(UxSubclassInfo *pInfo) HeapFree(GetProcessHeap(), 0, pInfo); } +static BOOL +DoSanitizeText(LPWSTR pszSanitized, LPCWSTR pszInvalidChars, LPCWSTR pszValidChars) +{ + LPWSTR pch1, pch2; + BOOL bFound = FALSE; + + for (pch1 = pch2 = pszSanitized; *pch1; ++pch1) + { + if (pszInvalidChars) + { + if (wcschr(pszInvalidChars, *pch1) != NULL) + { + bFound = TRUE; + continue; + } + } + else if (pszValidChars) + { + if (wcschr(pszValidChars, *pch1) == NULL) + { + bFound = TRUE; + continue; + } + } + + *pch2 = *pch1; + ++pch2; + } + *pch2 = 0; + + return bFound; +} + +static void +DoSanitizeClipboard(HWND hwnd, UxSubclassInfo *pInfo) +{ + HGLOBAL hData; + LPWSTR pszText, pszSanitized; + DWORD cbData; + + if (GetWindowLongPtrW(hwnd, GWL_STYLE) & ES_READONLY) + return; + if (!OpenClipboard(hwnd)) + return; + + hData = GetClipboardData(CF_UNICODETEXT); + pszText = GlobalLock(hData); + if (!pszText) + { + CloseClipboard(); + return; + } + SHStrDupW(pszText, &pszSanitized); + GlobalUnlock(hData); + + if (pszSanitized && + DoSanitizeText(pszSanitized, pInfo->pwszInvalidChars, pInfo->pwszValidChars)) + { + MessageBeep(0xFFFFFFFF); + + /* Update clipboard text */ + cbData = (lstrlenW(pszSanitized) + 1) * sizeof(WCHAR); + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbData); + pszText = GlobalLock(hData); + if (pszText) + { + CopyMemory(pszText, pszSanitized, cbData); + GlobalUnlock(hData); + + SetClipboardData(CF_UNICODETEXT, hData); + } + } + + CoTaskMemFree(pszSanitized); + CloseClipboard(); +} + static LRESULT CALLBACK LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC fnWndProc; + WCHAR wch; UxSubclassInfo *pInfo = GetPropW(hwnd, L"UxSubclassInfo"); if (!pInfo) return DefWindowProcW(hwnd, uMsg, wParam, lParam); @@ -2191,8 +2269,22 @@ LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) switch (uMsg) { + case WM_KEYDOWN: + if (GetKeyState(VK_SHIFT) < 0 && wParam == VK_INSERT) + DoSanitizeClipboard(hwnd, pInfo); + else if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V') + DoSanitizeClipboard(hwnd, pInfo); + + return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); + + case WM_PASTE: + DoSanitizeClipboard(hwnd, pInfo); + return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); + case WM_CHAR: - { + if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V') + break; + if (pInfo->pwszInvalidChars) { if (wcschr(pInfo->pwszInvalidChars, (WCHAR)wParam) != NULL) @@ -2210,11 +2302,18 @@ LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); - } + + case WM_UNICHAR: + if (wParam == UNICODE_NOCHAR) + return TRUE; + + /* FALL THROUGH */ case WM_IME_CHAR: - { - WCHAR wch = (WCHAR)wParam; + wch = (WCHAR)wParam; + if (GetKeyState(VK_CONTROL) < 0 && wch == L'V') + break; + if (!IsWindowUnicode(hwnd) && HIBYTE(wch) != 0) { CHAR data[] = {HIBYTE(wch), LOBYTE(wch)}; @@ -2238,13 +2337,10 @@ LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); - } case WM_NCDESTROY: - { UxSubclassInfo_Destroy(pInfo); return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); - } default: return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 1757a055734..1927bf26015 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -697,6 +697,19 @@ IStream* WINAPI SHGetViewStream(LPCITEMIDLIST, DWORD, LPCTSTR, LPCTSTR, LPCTSTR) EXTERN_C HRESULT WINAPI SHCreateSessionKey(REGSAM samDesired, PHKEY phKey); +/***************************************************************************** + * INVALID_FILETITLE_CHARACTERS + */ + +#define INVALID_FILETITLE_CHARACTERSA "\\/:*?\"<>|" +#define INVALID_FILETITLE_CHARACTERSW L"\\/:*?\"<>|" + +#ifdef UNICODE + #define INVALID_FILETITLE_CHARACTERS INVALID_FILETITLE_CHARACTERSW +#else + #define INVALID_FILETITLE_CHARACTERS INVALID_FILETITLE_CHARACTERSA +#endif + /***************************************************************************** * Shell Link */