diff options
author | George Hazan <ghazan@miranda.im> | 2020-10-29 19:21:32 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2020-10-29 19:21:32 +0300 |
commit | 74e691804c25c6174cb619d21b19fb5dc94afa22 (patch) | |
tree | 98625fbff7a1c7da877f208e13d0e49962ddffa1 /src | |
parent | 21aaad51c5e0bcf7157bd8e37e6e8ee0d1e55525 (diff) |
Plugin Updater: pu_stub utilities moved to mir_app
Diffstat (limited to 'src')
-rw-r--r-- | src/mir_app/mir_app.vcxproj | 1 | ||||
-rw-r--r-- | src/mir_app/mir_app.vcxproj.filters | 3 | ||||
-rw-r--r-- | src/mir_app/src/mir_app.def | 9 | ||||
-rw-r--r-- | src/mir_app/src/mir_app64.def | 9 | ||||
-rw-r--r-- | src/mir_app/src/pu_utils.cpp | 296 |
5 files changed, 318 insertions, 0 deletions
diff --git a/src/mir_app/mir_app.vcxproj b/src/mir_app/mir_app.vcxproj index 8658ff5b87..0f55a19c5f 100644 --- a/src/mir_app/mir_app.vcxproj +++ b/src/mir_app/mir_app.vcxproj @@ -139,6 +139,7 @@ <ClCompile Include="src\proto_order.cpp" />
<ClCompile Include="src\proto_ui.cpp" />
<ClCompile Include="src\proto_utils.cpp" />
+ <ClCompile Include="src\pu_utils.cpp" />
<ClCompile Include="src\searchresults.cpp" />
<ClCompile Include="src\skin2opts.cpp" />
<ClCompile Include="src\skinicons.cpp" />
diff --git a/src/mir_app/mir_app.vcxproj.filters b/src/mir_app/mir_app.vcxproj.filters index 1db664ca70..a41d536dd7 100644 --- a/src/mir_app/mir_app.vcxproj.filters +++ b/src/mir_app/mir_app.vcxproj.filters @@ -386,6 +386,9 @@ <ClCompile Include="src\chat_ui.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\pu_utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\chat.h">
diff --git a/src/mir_app/src/mir_app.def b/src/mir_app/src/mir_app.def index 0bf07ba1cc..8dabfd30b6 100644 --- a/src/mir_app/src/mir_app.def +++ b/src/mir_app/src/mir_app.def @@ -741,3 +741,12 @@ Chat_CreateMenu @824 NONAME ?GetChecker@MDatabaseCommon@@UAGPAUMIDatabaseChecker@@XZ @829 NONAME
?GetMenuItem@PROTO_INTERFACE@@QAEPAUTMO_IntMenuItem@@W4ProtoMenuItemType@@@Z @830 NONAME
_Netlib_GetTlsUnique@8 @831 NONAME
+?IsDirect@PU@@YG_NXZ @832 NONAME
+?IsProcessElevated@PU@@YG_NXZ @833 NONAME
+?PrepareEscalation@PU@@YG_NXZ @834 NONAME
+?SafeCopyFile@PU@@YGHPB_W0@Z @835 NONAME
+?SafeCreateDirectory@PU@@YGHPB_W@Z @836 NONAME
+?SafeCreateFilePath@PU@@YGHPB_W@Z @837 NONAME
+?SafeDeleteDirectory@PU@@YGHPB_W@Z @838 NONAME
+?SafeDeleteFile@PU@@YGHPB_W@Z @839 NONAME
+?SafeMoveFile@PU@@YGHPB_W0@Z @840 NONAME
diff --git a/src/mir_app/src/mir_app64.def b/src/mir_app/src/mir_app64.def index 945587d58a..70a00197d4 100644 --- a/src/mir_app/src/mir_app64.def +++ b/src/mir_app/src/mir_app64.def @@ -741,3 +741,12 @@ Chat_CreateMenu @824 NONAME ?GetChecker@MDatabaseCommon@@UEAAPEAUMIDatabaseChecker@@XZ @829 NONAME
?GetMenuItem@PROTO_INTERFACE@@QEAAPEAUTMO_IntMenuItem@@W4ProtoMenuItemType@@@Z @830 NONAME
Netlib_GetTlsUnique @831 NONAME
+?IsDirect@PU@@YA_NXZ @832 NONAME
+?IsProcessElevated@PU@@YA_NXZ @833 NONAME
+?PrepareEscalation@PU@@YA_NXZ @834 NONAME
+?SafeCopyFile@PU@@YAHPEB_W0@Z @835 NONAME
+?SafeCreateDirectory@PU@@YAHPEB_W@Z @836 NONAME
+?SafeCreateFilePath@PU@@YAHPEB_W@Z @837 NONAME
+?SafeDeleteDirectory@PU@@YAHPEB_W@Z @838 NONAME
+?SafeDeleteFile@PU@@YAHPEB_W@Z @839 NONAME
+?SafeMoveFile@PU@@YAHPEB_W0@Z @840 NONAME
diff --git a/src/mir_app/src/pu_utils.cpp b/src/mir_app/src/pu_utils.cpp new file mode 100644 index 0000000000..f65a0f969c --- /dev/null +++ b/src/mir_app/src/pu_utils.cpp @@ -0,0 +1,296 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-20 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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. +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// pu_stub.exe interface + +#include "stdafx.h" + +static HANDLE g_hPipe = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// +// are we running with admin priviledges? + +static bool IsRunAsAdmin() +{ + BOOL bIsRunAsAdmin = false; + DWORD dwError = ERROR_SUCCESS; + PSID pAdministratorsGroup = nullptr; + + // Allocate and initialize a SID of the administrators group. + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) { + dwError = GetLastError(); + goto Cleanup; + } + + // Determine whether the SID of administrators group is bEnabled in + // the primary access token of the process. + if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) { + dwError = GetLastError(); + goto Cleanup; + } + +Cleanup: + // Centralized cleanup for all allocated resources. + if (pAdministratorsGroup) { + FreeSid(pAdministratorsGroup); + pAdministratorsGroup = nullptr; + } + + // Throw the error if something failed in the function. + if (ERROR_SUCCESS != dwError) { + throw dwError; + } + + return bIsRunAsAdmin != 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Checks if we're working via pu_stub or not + +MIR_APP_DLL(bool) PU::IsDirect() +{ + return g_hPipe == nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Checks if a process has enough rights to write into Miranda's folder + +MIR_APP_DLL(bool) PU::IsProcessElevated() +{ + bool bIsElevated = false; + DWORD dwError = ERROR_SUCCESS; + HANDLE hToken = nullptr; + + // Open the primary access token of the process with TOKEN_QUERY. + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + dwError = GetLastError(); + goto Cleanup; + } + + // Retrieve token elevation information. + TOKEN_ELEVATION elevation; + DWORD dwSize; + if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { + // When the process is run on operating systems prior to Windows + // Vista, GetTokenInformation returns FALSE with the + // ERROR_INVALID_PARAMETER error code because TokenElevation is + // not supported on those operating systems. + dwError = GetLastError(); + goto Cleanup; + } + + bIsElevated = elevation.TokenIsElevated != 0; + +Cleanup: + // Centralized cleanup for all allocated resources. + if (hToken) { + CloseHandle(hToken); + hToken = nullptr; + } + + // Throw the error if something failed in the function. + if (ERROR_SUCCESS != dwError) { + throw dwError; + } + + return bIsElevated; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Launches pu_stub.exe with elevated priviledges if needed + +MIR_APP_DLL(bool) PU::PrepareEscalation() +{ + // First try to create a file near Miranda32.exe + wchar_t szPath[MAX_PATH]; + GetModuleFileName(nullptr, szPath, _countof(szPath)); + wchar_t *ext = wcsrchr(szPath, '.'); + if (ext != nullptr) + *ext = '\0'; + wcscat(szPath, L".test"); + + HANDLE hFile = CreateFile(szPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + // we are admins or UAC is disable, cool + CloseHandle(hFile); + DeleteFile(szPath); + return true; + } + + // Check the current process's "run as administrator" status. + if (IsRunAsAdmin()) + return true; + + // if pipe already opened? + if (g_hPipe != nullptr) + return true; + + // Elevate the process. Create a pipe for a stub first + wchar_t wzPipeName[MAX_PATH]; + mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId()); + g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr); + if (g_hPipe == INVALID_HANDLE_VALUE) { + g_hPipe = nullptr; + } + else { + wchar_t cmdLine[100], *p; + GetModuleFileName(nullptr, szPath, ARRAYSIZE(szPath)); + if ((p = wcsrchr(szPath, '\\')) != nullptr) + wcscpy(p + 1, L"pu_stub.exe"); + mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId()); + + // Launch a stub + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.lpVerb = L"runas"; + sei.lpFile = szPath; + sei.lpParameters = cmdLine; + sei.hwnd = nullptr; + sei.nShow = SW_NORMAL; + if (ShellExecuteEx(&sei)) { + if (g_hPipe != nullptr) + ConnectNamedPipe(g_hPipe, nullptr); + return true; + } + + DWORD dwError = GetLastError(); + if (dwError == ERROR_CANCELLED) { + // The user refused to allow privileges elevation. + // Do nothing ... + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2) +{ + BYTE buf[1024]; + DWORD l1 = lstrlen(p1), l2 = lstrlen(p2); + if (l1 > MAX_PATH || l2 > MAX_PATH) + return ERROR_BAD_ARGUMENTS; + + *(DWORD *)buf = opcode; + wchar_t *dst = (wchar_t *)&buf[sizeof(DWORD)]; + lstrcpy(dst, p1); + dst += l1 + 1; + if (p2) { + lstrcpy(dst, p2); + dst += l2 + 1; + } + else *dst++ = 0; + + DWORD dwBytes = 0, dwError; + if (!WriteFile(g_hPipe, buf, (DWORD)((BYTE *)dst - buf), &dwBytes, nullptr)) + return GetLastError(); + + dwError = 0; + if (!ReadFile(g_hPipe, &dwError, sizeof(DWORD), &dwBytes, nullptr)) + return GetLastError(); + if (dwBytes != sizeof(DWORD)) + return ERROR_BAD_ARGUMENTS; + + return dwError; +} + +MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst) +{ + if (g_hPipe == nullptr) + return CopyFileW(pSrc, pDst, FALSE); + + return TransactPipe(1, pSrc, pDst); +} + +MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst) +{ + if (g_hPipe == nullptr) { + if (!DeleteFileW(pDst)) { + DWORD dwError = GetLastError(); + if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND) + return dwError; + } + + if (!MoveFileW(pSrc, pDst)) { // use copy on error + switch (DWORD dwError = GetLastError()) { + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_NOT_FOUND: + return 0; // this file was included into many archives, so Miranda tries to move it again & again + + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + // use copy routine if a move operation isn't available + // for example, when files are on different disks + if (!CopyFileW(pSrc, pDst, FALSE)) + return GetLastError(); + + if (!DeleteFileW(pSrc)) + return GetLastError(); + break; + + default: + return dwError; + } + } + + return ERROR_SUCCESS; + } + + return TransactPipe(2, pSrc, pDst); +} + +MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile) +{ + if (g_hPipe == nullptr) + return DeleteFile(pwszFile); + + return TransactPipe(3, pwszFile, nullptr); +} + +MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder) +{ + if (g_hPipe == nullptr) + return CreateDirectoryTreeW(pwszFolder); + + return TransactPipe(4, pwszFolder, nullptr); +} + +MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName) +{ + if (g_hPipe == nullptr) + return DeleteDirectoryTreeW(pwszDirName); + + return TransactPipe(6, pwszDirName, nullptr); +} + +MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder) +{ + if (g_hPipe == nullptr) { + CreatePathToFileW(pwszFolder); + return 0; + } + + return TransactPipe(5, pwszFolder, nullptr); +} |