diff options
| author | Odd Stranne <odd@mullvad.net> | 2018-08-16 22:06:56 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2018-08-29 12:14:24 +0200 |
| commit | e0fbb5dfda63ebb054a47aaf88a270ab1334a68a (patch) | |
| tree | f89aae227ff12068e03842b849f5afff52eb0db0 /windows/nsis-plugins/src/cleanup/cleaningops.cpp | |
| parent | ac290fef91b3fb7fe35cffada330c56436d223ed (diff) | |
| download | mullvadvpn-e0fbb5dfda63ebb054a47aaf88a270ab1334a68a.tar.xz mullvadvpn-e0fbb5dfda63ebb054a47aaf88a270ab1334a68a.zip | |
Add uninstaller plugin for extended system cleaning
Diffstat (limited to 'windows/nsis-plugins/src/cleanup/cleaningops.cpp')
| -rw-r--r-- | windows/nsis-plugins/src/cleanup/cleaningops.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/windows/nsis-plugins/src/cleanup/cleaningops.cpp b/windows/nsis-plugins/src/cleanup/cleaningops.cpp new file mode 100644 index 0000000000..dd14b9035c --- /dev/null +++ b/windows/nsis-plugins/src/cleanup/cleaningops.cpp @@ -0,0 +1,269 @@ +#include "stdafx.h" +#include "cleaningops.h" +#include <libcommon/filesystem.h> +#include <libcommon/fileenumerator.h> +#include <libcommon/string.h> +#include <libcommon/memory.h> +#include <libcommon/security.h> +#include <libcommon/process.h> +#include <experimental/filesystem> +#include <utility> +#include <functional> +#include <processthreadsapi.h> + +namespace +{ + +// +// Returns range in lhs that is also present in rhs. +// Equalence/equivalence determined by 'comp'. +// +// Returns pair<lhsBegin, lhsBegin> if there is no mirrored range. +// +template<typename ForwardIterator> +std::pair<ForwardIterator, ForwardIterator> +mirrored_range +( + ForwardIterator lhsBegin, ForwardIterator lhsEnd, + ForwardIterator rhsBegin, ForwardIterator rhsEnd, + std::function<bool(const typename ForwardIterator::value_type &, const typename ForwardIterator::value_type &)> comp +) +{ + ForwardIterator begin = lhsBegin; + + while (lhsBegin != lhsEnd + && rhsBegin != rhsEnd) + { + if (false == comp(*lhsBegin, *rhsBegin)) + { + break; + } + + ++lhsBegin; + ++rhsBegin; + } + + return std::make_pair(begin, lhsBegin); +} + +std::wstring ConstructLocalAppDataPath(const std::wstring &base, const std::wstring &user, + const std::pair<std::vector<std::wstring>::iterator, std::vector<std::wstring>::iterator> &tokens) +{ + auto result = common::fs::MakePath(base, user); + + std::for_each(tokens.first, tokens.second, [&](const std::wstring &token) + { + result = common::fs::MakePath(result, token); + }); + + return result; +} + +std::wstring GetSystemUserLocalAppData() +{ + common::security::AdjustCurrentProcessTokenPrivilege(L"SeDebugPrivilege"); + + common::memory::ScopeDestructor sd; + + sd += [] + { + common::security::AdjustCurrentProcessTokenPrivilege(L"SeDebugPrivilege", false); + }; + + auto systemDir = common::fs::GetKnownFolderPath(FOLDERID_System, KF_FLAG_DEFAULT, NULL); + auto lsassPid = common::process::GetProcessIdFromName(common::fs::MakePath(systemDir, L"lsass.exe")); + + auto processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lsassPid); + + if (nullptr == processHandle) + { + throw std::runtime_error("Failed to access the \"LSASS\" process"); + } + + HANDLE processToken; + + auto status = OpenProcessToken(processHandle, TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &processToken); + + CloseHandle(processHandle); + + if (FALSE == status) + { + throw std::runtime_error("Failed to acquire process token for the \"LSASS\" process"); + } + + sd += [&]() + { + CloseHandle(processToken); + }; + + return common::fs::GetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, processToken); +} + +} // anonymous namespace + +namespace cleaningops +{ + +void RemoveLogsCacheCurrentUser() +{ + auto localAppData = common::fs::GetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr); + + auto appdir = common::fs::MakePath(localAppData, L"Mullvad VPN"); + + std::experimental::filesystem::remove_all(appdir); +} + +void RemoveLogsCacheOtherUsers() +{ + // + // Determine relative path to "local app data" from home directory. + // + // Beware, the local app data path may be overriden from its default location + // as a node somewhere beneath the home directory. + // + + auto localAppData = common::fs::GetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr); + auto homeDir = common::fs::GetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, nullptr); + + // + // Tokenize to get rid of slashes pointing in different directions. + // + auto localAppDataTokens = common::string::Tokenize(localAppData, L"\\/"); + auto homeDirTokens = common::string::Tokenize(homeDir, L"\\/"); + + auto mirror = mirrored_range + ( + localAppDataTokens.begin(), localAppDataTokens.end(), + homeDirTokens.begin(), homeDirTokens.end(), + [](const std::wstring &lhs, const std::wstring &rhs) + { + return 0 == _wcsicmp(lhs.c_str(), rhs.c_str()); + } + ); + + auto equalTokensCount = (size_t)std::distance(mirror.first, mirror.second); + + // + // Abort if "local app data" is not beneath home dir. + // + if (equalTokensCount < homeDirTokens.size()) + { + return; + } + + auto relativeLocalAppData = std::make_pair(std::next(localAppDataTokens.begin(), equalTokensCount), localAppDataTokens.end()); + auto currentUser = *homeDirTokens.rbegin(); + + // + // Find all other users and construct the most plausible path for their + // respective "local app data" dirs. + // + + auto parentHomeDir = common::fs::GetKnownFolderPath(FOLDERID_UserProfiles, KF_FLAG_DEFAULT, nullptr); + + common::fs::FileEnumerator files(parentHomeDir); + + files.addFilter(std::make_unique<common::fs::FilterDirectories>()); + files.addFilter(std::make_unique<common::fs::FilterNotRelativeDirs>()); + + auto notNamedSet = std::make_unique<common::fs::FilterNotNamedSet>(); + + notNamedSet->addObject(std::wstring(currentUser)); + notNamedSet->addObject(L"All Users"); // Redirects to 'c:\programdata'. + notNamedSet->addObject(L"Public"); // Shared documents, not an actual user or user template. + + files.addFilter(std::move(notNamedSet)); + + WIN32_FIND_DATAW file; + + while (files.next(file)) + { + auto userLocalAppData = ConstructLocalAppDataPath(files.getDirectory(), file.cFileName, relativeLocalAppData); + + std::error_code dummy; + std::experimental::filesystem::remove_all(common::fs::MakePath(userLocalAppData, L"Mullvad VPN"), dummy); + } +} + +void RemoveLogsServiceUser() +{ + auto programData = common::fs::GetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_DEFAULT, nullptr); + + auto appdir = common::fs::MakePath(programData, L"Mullvad VPN"); + + std::experimental::filesystem::remove_all(appdir); +} + +void RemoveCacheServiceUser() +{ + auto localAppData = GetSystemUserLocalAppData(); + auto mullvadAppData = common::fs::MakePath(localAppData, L"Mullvad VPN"); + + common::fs::ScopedNativeFileSystem nativeFileSystem; + + common::security::AddAdminToObjectDacl(mullvadAppData, SE_FILE_OBJECT); + + { + common::fs::FileEnumerator files(mullvadAppData); + + auto notNamedSet = std::make_unique<common::fs::FilterNotNamedSet>(); + + notNamedSet->addObject(L"account-history.json"); + notNamedSet->addObject(L"settings.json"); + + files.addFilter(std::move(notNamedSet)); + files.addFilter(std::make_unique<common::fs::FilterFiles>()); + + WIN32_FIND_DATAW file; + + while (files.next(file)) + { + std::error_code dummy; + std::experimental::filesystem::remove(common::fs::MakePath(files.getDirectory(), file.cFileName), dummy); + } + } + + // + // This fails unless the directory is empty. + // Which is what we want, since removing cache and settings files are separate operations. + // + RemoveDirectoryW(std::wstring(L"\\\\?\\").append(mullvadAppData).c_str()); +} + +void RemoveSettingsServiceUser() +{ + auto localAppData = GetSystemUserLocalAppData(); + auto mullvadAppData = common::fs::MakePath(localAppData, L"Mullvad VPN"); + + common::fs::ScopedNativeFileSystem nativeFileSystem; + + common::security::AddAdminToObjectDacl(mullvadAppData, SE_FILE_OBJECT); + + { + common::fs::FileEnumerator files(mullvadAppData); + + auto filter = std::make_unique<common::fs::FilterNamedSet>(); + + filter->addObject(L"account-history.json"); + filter->addObject(L"settings.json"); + + files.addFilter(std::move(filter)); + files.addFilter(std::make_unique<common::fs::FilterFiles>()); + + WIN32_FIND_DATAW file; + + while (files.next(file)) + { + std::error_code dummy; + std::experimental::filesystem::remove(common::fs::MakePath(files.getDirectory(), file.cFileName), dummy); + } + } + + // + // This fails unless the directory is empty. + // Which is what we want, since removing cache and settings files are separate operations. + // + RemoveDirectoryW(std::wstring(L"\\\\?\\").append(mullvadAppData).c_str()); +} + +} |
