From 6f3f2f0a8a6aa5ab0623e32e1d3c7e557b81c861 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Thu, 5 May 2022 14:27:46 +0200 Subject: [PATCH] Schedule user settings cleanup next time user logs on We are using Active Setup because eduVPN client is installed per-machine and eduVPN client settings are stored per-user. Active Setup allows us to cleanup the settings for all users, not just the one that performed the uninstall. On an eduVPN client uninstall, Active Setup is instructed to cleanup user's client settings on next user logon. It does not cleanup the settings immediately on uninstall for the user performing the uninstall, as it would require a reliable detection to distinguish the ultimate uninstall from an uninstall-before-next-version-install. This means, if user wants to start the eduVPN experience anew, one needs to uninstall the client, sign out&in or reboot, and reinstall the client. Fixes: #187 Fixes: #192 Signed-off-by: Simon Rozman --- WinStd | 2 +- doc/FAQ.md | 12 +++- eduMSICA/exports.def | 2 + eduMSICA/main.cpp | 130 ++++++++++++++++++++++++++++++++++++++++++- eduVPNClient.wxs | 26 ++++++++- 5 files changed, 165 insertions(+), 7 deletions(-) diff --git a/WinStd b/WinStd index 83c749e3..49b55331 160000 --- a/WinStd +++ b/WinStd @@ -1 +1 @@ -Subproject commit 83c749e394ef1e95b594634634a90aee89e8d8af +Subproject commit 49b55331e47900e0c0188f4541279aa8acf8ed3d diff --git a/doc/FAQ.md b/doc/FAQ.md index a8cf61db..ec99422d 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -4,10 +4,16 @@ Right click on the server name. A menu should popup. Select _Forget_. ## How to reset all my client settings? -Close the eduVPN Client first. Open a command prompt and paste this: + +### Resetting manually +Close the eduVPN and Let's Encrypt! clients first. Open a command prompt and paste one or both lines: ```cmd -rd /s "%LOCALAPPDATA%\SURF" +for /d %i in ("%LOCALAPPDATA%\SURF\eduVPN.Client.exe_Url_*") do rd /s /q "%i" +for /d %i in ("%LOCALAPPDATA%\SURF\LetsConnect.Client.exe_Url_*") do rd /s /q "%i" ``` -Hit _Enter_ and confirm the deletition. This will reset eduVPN Client to factory defaults. +Don't forget to press _Enter_ key after the line is pasted. This will reset eduVPN and Let's Encrypt! clients to factory defaults. + +### Resetting after uninstall +Uninstall the client. On next sign-in, the client settings of the user signing-in will be cleaned. diff --git a/eduMSICA/exports.def b/eduMSICA/exports.def index ad28c099..c8e8eea5 100644 --- a/eduMSICA/exports.def +++ b/eduMSICA/exports.def @@ -10,3 +10,5 @@ EXPORTS EvaluateComponents RemoveWintunDriver KillExecutableProcesses + UnpublishActiveSetup + PublishActiveSetup diff --git a/eduMSICA/main.cpp b/eduMSICA/main.cpp index b826f8c1..e6b2eabe 100644 --- a/eduMSICA/main.cpp +++ b/eduMSICA/main.cpp @@ -18,6 +18,7 @@ #include #include +#include #include using namespace std; @@ -170,7 +171,7 @@ EvaluateComponents(_In_ MSIHANDLE hInstall) else LOG_ERROR_NUM(uiResult, TEXT("MsiGetComponentState(\"%s\") failed"), WINTUN_COMPONENT); - static LPCTSTR szClientFilenames[] = { + static const LPCTSTR szClientFilenames[] = { TEXT("eduVPN.Client.exe"), TEXT("LetsConnect.Client.exe"), }; @@ -188,6 +189,47 @@ EvaluateComponents(_In_ MSIHANDLE hInstall) LOG_ERROR_NUM(uiResult, TEXT("MsiGetComponentState(\"%s\") failed"), szClientFilenames[i]); } + static const LPCTSTR szActiveSetupActionCodes[] = { + TEXT("{B26CF707-1200-4760-A900-C4621CEEADC0}"), + TEXT("{B26CF707-1200-4760-A901-C4621CEEADC0}"), + }; + for (size_t i = 0; i < _countof(szActiveSetupActionCodes) && i < _countof(szClientFilenames); ++i) { + // Get the client component state. + uiResult = MsiGetComponentState(hInstall, szClientFilenames[i], &iInstalled, &iAction); + if (uiResult == ERROR_SUCCESS) { + tstring sRegPath; + sprintf(sRegPath, TEXT("Software\\Microsoft\\Active Setup\\Installed Components\\%s"), szActiveSetupActionCodes[i]); + unsigned int uiVersion = 0; + reg_key key; + uiResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sRegPath.c_str(), 0, KEY_QUERY_VALUE, key); + if (uiResult == ERROR_SUCCESS) { + DWORD dwType; + vector aData; + if (RegQueryValueEx(key, TEXT("Version"), 0, &dwType, aData) == ERROR_SUCCESS && dwType == REG_SZ) { + aData.push_back(TEXT('\0')); + uiVersion = _tcstoul(aData.data(), NULL, 10); + } + } + + if (iInstalled >= INSTALLSTATE_LOCAL && iAction >= INSTALLSTATE_REMOVED) { + // Client component is installed and shall be removed/upgraded/reinstalled. + // Schedule user configuration cleanup. + SetFormattedProperty(hInstall, TEXT("PublishActiveSetup"), tstring_printf( + TEXT("\"%s\" \"[ProductName]\" %u \"cmd.exe /c \\\"for /d %%i in (\\\"\\\"%%LOCALAPPDATA%%\\SURF\\%s_Url_*\\\"\\\") do rd /s /q \\\"\\\"%%i\\\"\\\"\\\"\""), + sRegPath.c_str(), + uiVersion + 1, + szClientFilenames[i]).c_str()); + } + if (iAction >= INSTALLSTATE_LOCAL) { + // Client component shall be installed. + // Cancel user configuration cleanup. + MsiSetProperty(hInstall, TEXT("UnpublishActiveSetup"), tstring_printf(TEXT("\"%s\""), sRegPath.c_str()).c_str()); + } + } + else if (uiResult != ERROR_UNKNOWN_COMPONENT) + LOG_ERROR_NUM(uiResult, TEXT("MsiGetComponentState(\"%s\") failed"), szClientFilenames[i]); + } + return ERROR_SUCCESS; } @@ -297,3 +339,89 @@ KillExecutableProcesses(_In_ MSIHANDLE hInstall) } return ERROR_SUCCESS; } + +static LSTATUS +RegSetValueEx(_In_ HKEY hKey, _In_opt_ LPCTSTR lpValueName, _Reserved_ DWORD Reserved, _In_ DWORD dwData) +{ + return RegSetValueEx(hKey, lpValueName, Reserved, REG_DWORD, reinterpret_cast(&dwData), sizeof(dwData)); +} + +static LSTATUS +RegSetValueExW(_In_ HKEY hKey, _In_opt_ LPCWSTR lpValueName, _Reserved_ DWORD Reserved, _In_z_ LPCWSTR szData) +{ + size_t nSize = (wcslen(szData) + 1) * sizeof(WCHAR); + if (nSize > MAXDWORD) + return ERROR_INVALID_PARAMETER; + return RegSetValueExW(hKey, lpValueName, Reserved, REG_SZ, reinterpret_cast(szData), (DWORD)nSize); +} + +_Return_type_success_(return == ERROR_SUCCESS) UINT __stdcall +UnpublishActiveSetup(_In_ MSIHANDLE hInstall) +{ + com_initializer com_init(NULL); + s_hInstall = hInstall; + + wstring sData; + UINT uiResult = MsiGetPropertyW(hInstall, L"CustomActionData", sData); + if (uiResult != ERROR_SUCCESS) { + LOG_ERROR_NUM(uiResult, TEXT("MsiGetProperty(\"CustomActionData\") failed")); + return ERROR_SUCCESS; + } + if (sData.empty()) + return ERROR_SUCCESS; + int wargc; + unique_ptr> wargv(CommandLineToArgvW(sData.c_str(), &wargc)); + if (wargv == NULL) { + LOG_ERROR_NUM(GetLastError(), TEXT("CommandLineToArgvW failed")); + return ERROR_SUCCESS; + } + if (wargc < 1) + return ERROR_SUCCESS; + + reg_key key; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wargv[0], 0, KEY_SET_VALUE, key) != ERROR_SUCCESS) + return ERROR_SUCCESS; + RegSetValueEx(key, TEXT("IsInstalled"), 0, 0ul); + if (wargc > 1) + RegSetValueExW(key, L"StubPath", 0, wargv[1]); + else + RegDeleteValue(key, TEXT("StubPath")); + return ERROR_SUCCESS; +} + +_Return_type_success_(return == ERROR_SUCCESS) UINT __stdcall +PublishActiveSetup(_In_ MSIHANDLE hInstall) +{ + com_initializer com_init(NULL); + s_hInstall = hInstall; + + wstring sData; + UINT uiResult = MsiGetPropertyW(hInstall, L"CustomActionData", sData); + if (uiResult != ERROR_SUCCESS) { + LOG_ERROR_NUM(uiResult, TEXT("MsiGetProperty(\"CustomActionData\") failed")); + return ERROR_SUCCESS; + } + if (sData.empty()) + return ERROR_SUCCESS; + int wargc; + unique_ptr> wargv(CommandLineToArgvW(sData.c_str(), &wargc)); + if (wargv == NULL) { + LOG_ERROR_NUM(GetLastError(), TEXT("CommandLineToArgvW failed")); + return ERROR_SUCCESS; + } + if (wargc < 3) + return ERROR_SUCCESS; + + reg_key key; + if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, wargv[0], 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, key, NULL) != ERROR_SUCCESS) + return ERROR_SUCCESS; + RegSetValueExW(key, NULL, 0, wargv[1]); + RegSetValueExW(key, L"Version", 0, wargv[2]); + RegSetValueEx(key, TEXT("IsInstalled"), 0, 1ul); + RegSetValueEx(key, TEXT("DontAsk"), 0, 2ul); + if (wargc > 3) + RegSetValueExW(key, L"StubPath", 0, wargv[3]); + else + RegDeleteValue(key, TEXT("StubPath")); + return ERROR_SUCCESS; +} diff --git a/eduVPNClient.wxs b/eduVPNClient.wxs index b0e86b26..be52a9ca 100644 --- a/eduVPNClient.wxs +++ b/eduVPNClient.wxs @@ -553,7 +553,21 @@ BinaryKey="eduMSICA.dll" DllEntry="KillExecutableProcesses" Execute="deferred" - Impersonate="no" /> + Impersonate="no"/> + + + + + Before="StopServices"/> + + + +