diff options
author | Stephan Bergmann <stephan.bergmann@allotropia.de> | 2023-12-21 14:11:59 +0100 |
---|---|---|
committer | Stephan Bergmann <stephan.bergmann@allotropia.de> | 2023-12-21 16:50:25 +0100 |
commit | 04b18e28d3b44e2175331e78183e3b0fe6d81187 (patch) | |
tree | f70539ddadd8619794c4658f8cbabc3de9568fd8 /external | |
parent | 522f2174561a58cd9b29aba2a94f7268281bf880 (diff) |
Windows MSI custom action for --enable-online-update-mar
...which needs to call update_service.exe with "install" (resp. "uninstall") and
needs to create (resp. delete) a registry entry containing the issuer and name
of the certificate with which the updater.exe is signed (which is required by
one of the many sanity and security checks performed by update_service.exe
before it will actually do an automatic update). (The issuer and name of the
certificate are for now hardcoded to the values used by TDF when signing its
Windows builds.)
(gid_Customaction_uninstall_updateservice needs to run rather early, when
update_service.exe has not yet been removed, so I rather randomly picked
"MigrateFeatureStates" as the point where to run it.)
Change-Id: I6e0f62ec3e51d74d4a526a490badc7c14ebe99ae
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161102
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
Diffstat (limited to 'external')
-rw-r--r-- | external/onlineupdate/Library_install_updateservice.mk | 54 | ||||
-rw-r--r-- | external/onlineupdate/Module_onlineupdate.mk | 1 | ||||
-rw-r--r-- | external/onlineupdate/install_updateservice.cxx | 179 | ||||
-rw-r--r-- | external/onlineupdate/install_updateservice.def | 4 |
4 files changed, 238 insertions, 0 deletions
diff --git a/external/onlineupdate/Library_install_updateservice.mk b/external/onlineupdate/Library_install_updateservice.mk new file mode 100644 index 000000000000..216b81a95a07 --- /dev/null +++ b/external/onlineupdate/Library_install_updateservice.mk @@ -0,0 +1,54 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,install_updateservice)) + +$(eval $(call gb_Library_use_unpacked,install_updateservice,onlineupdate)) + +$(eval $(call gb_Library_set_include,install_updateservice, \ + -I$(call gb_UnpackedTarball_get_dir,onlineupdate)/onlineupdate/source/update/common \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_add_defs,install_updateservice, \ + -U_DLL \ +)) + +$(eval $(call gb_Library_add_cxxflags,install_updateservice, \ + $(if $(MSVC_USE_DEBUG_RUNTIME),/MTd,/MT) \ + $(if $(filter -fsanitize=%,$(CC)),,/fno-sanitize-address-vcasan-lib) \ +)) + +$(eval $(call gb_Library_add_ldflags,install_updateservice, \ + /DEF:$(SRCDIR)/external/onlineupdate/install_updateservice.def \ + /NODEFAULTLIB \ +)) + +$(eval $(call gb_Library_add_exception_objects,install_updateservice, \ + external/onlineupdate/install_updateservice \ +)) + +$(eval $(call gb_Library_use_static_libraries,install_updateservice, \ + updatehelper \ +)) + +$(eval $(call gb_Library_use_system_win32_libs,install_updateservice, \ + libcmt \ + libcpmt \ + libucrt \ + libvcruntime \ + msi \ + kernel32 \ + user32 \ + advapi32 \ + shell32 \ + shlwapi \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/external/onlineupdate/Module_onlineupdate.mk b/external/onlineupdate/Module_onlineupdate.mk index a6bc698aa3ca..97f7d5c3f7bd 100644 --- a/external/onlineupdate/Module_onlineupdate.mk +++ b/external/onlineupdate/Module_onlineupdate.mk @@ -18,6 +18,7 @@ $(eval $(call gb_Module_add_targets,onlineupdate,\ StaticLibrary_updatehelper \ $(if $(filter WNT,$(OS)),\ Executable_update_service \ + Library_install_updateservice \ WinResTarget_updater )\ Executable_test_updater_dialog \ Executable_mar \ diff --git a/external/onlineupdate/install_updateservice.cxx b/external/onlineupdate/install_updateservice.cxx new file mode 100644 index 000000000000..4604e55462d6 --- /dev/null +++ b/external/onlineupdate/install_updateservice.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <cstddef> +#include <limits> +#include <memory> +#include <string> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> + +#include <pathhash.h> + +// Replacements for functions used in +// workdir/UnpackedTarball/onlineupdate/onlineupdate/source/update/common/pathhash.cpp but not +// available here: + +extern "C" wchar_t* _wcslwr(wchar_t* str) +{ + for (auto p = str; *p != L'\0'; ++p) + { + if (*p >= L'A' && *p <= L'Z') + { + *p += L'a' - L'A'; + } + } + return str; +}; + +extern "C" wchar_t* wcsncpy(wchar_t* strDest, wchar_t const* strSource, std::size_t count) +{ + for (std::size_t i = 0; i != count; ++i) + { + strDest[i] = *strSource; + if (*strSource != L'\0') + { + ++strSource; + } + } + return strDest; +} + +namespace +{ +bool getInstallLocation(MSIHANDLE handle, std::wstring* installLocation) +{ + DWORD n = 0; + if (MsiGetPropertyW(handle, L"INSTALLLOCATION", const_cast<wchar_t*>(L""), &n) + != ERROR_MORE_DATA + || n == std::numeric_limits<DWORD>::max()) + { + return false; + } + ++n; + auto buf = std::make_unique<wchar_t[]>(n); + if (MsiGetPropertyW(handle, L"INSTALLLOCATION", buf.get(), &n) != ERROR_SUCCESS) + { + return false; + } + if (n != 0 && buf[n - 1] == L'\\') + { + --n; + } + installLocation->assign(buf.get(), n); + return true; +} + +typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard; + +CloseHandleGuard guard(HANDLE handle) { return CloseHandleGuard(handle, CloseHandle); } + +bool runExecutable(std::wstring const& installLocation, wchar_t const* commandLine) +{ + STARTUPINFOW si{}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi{}; + if (!CreateProcessW((installLocation + L"\\program\\update_service.exe").c_str(), + const_cast<LPWSTR>(commandLine), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, + nullptr, nullptr, &si, &pi)) + { + return false; + } + auto const g(guard(pi.hProcess)); + DWORD res = WaitForSingleObject(pi.hProcess, INFINITE); + if (res != WAIT_OBJECT_0) + { + return false; + } + DWORD ec = 0; + if (!GetExitCodeProcess(pi.hProcess, &ec)) + { + return false; + } + if (ec != 0) + { + return false; + } + return true; +} +} + +extern "C" __declspec(dllexport) UINT __stdcall InstallUpdateservice(MSIHANDLE handle) +{ + std::wstring loc; + if (!getInstallLocation(handle, &loc)) + { + return false; + } + if (!runExecutable(loc, L"install")) + { + return ERROR_INVALID_FUNCTION; + } + WCHAR maintenanceServiceKey[MAX_PATH + 1]; + if (!CalculateRegistryPathFromFilePath(loc.c_str(), maintenanceServiceKey)) + { + return ERROR_INVALID_FUNCTION; + } + HKEY key; + if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, (std::wstring(maintenanceServiceKey) + L"\\0").c_str(), + 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_WOW64_64KEY, nullptr, + &key, nullptr) + != ERROR_SUCCESS) + { + return ERROR_INVALID_FUNCTION; + } + if (RegSetValueExW(key, L"issuer", 0, REG_SZ, + reinterpret_cast<BYTE const*>(L"Certum Code Signing 2021 CA"), + sizeof L"Certum Code Signing 2021 CA") + != ERROR_SUCCESS) + { + return ERROR_INVALID_FUNCTION; + } + if (RegSetValueExW(key, L"name", 0, REG_SZ, + reinterpret_cast<BYTE const*>(L"The Document Foundation"), + sizeof L"The Document Foundation") + != ERROR_SUCCESS) + { + return ERROR_INVALID_FUNCTION; + } + if (RegCloseKey(key) != ERROR_SUCCESS) + { + return ERROR_INVALID_FUNCTION; + } + return ERROR_SUCCESS; +} + +extern "C" __declspec(dllexport) UINT __stdcall UninstallUpdateservice(MSIHANDLE handle) +{ + std::wstring loc; + if (!getInstallLocation(handle, &loc)) + { + return false; + } + if (!runExecutable(loc, L"uninstall")) + { + return ERROR_INVALID_FUNCTION; + } + WCHAR maintenanceServiceKey[MAX_PATH + 1]; + if (!CalculateRegistryPathFromFilePath(loc.c_str(), maintenanceServiceKey)) + { + return ERROR_INVALID_FUNCTION; + } + if (RegDeleteTreeW(HKEY_LOCAL_MACHINE, maintenanceServiceKey) != ERROR_SUCCESS) + { + return ERROR_INVALID_FUNCTION; + } + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/external/onlineupdate/install_updateservice.def b/external/onlineupdate/install_updateservice.def new file mode 100644 index 000000000000..a210387fce33 --- /dev/null +++ b/external/onlineupdate/install_updateservice.def @@ -0,0 +1,4 @@ +LIBRARY "install_updateservice.dll" +EXPORTS + InstallUpdateservice + UninstallUpdateservice |