Kaydet (Commit) 06dcc803 authored tarafından Markus Mohrhard's avatar Markus Mohrhard

add files for the new update service on windows

üst 9870d31a
/* 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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <softpub.h>
#include <wintrust.h>
#include "certificatecheck.hxx"
#include "servicebase.hxx"
#pragma comment(lib, "wintrust.lib")
#pragma comment(lib, "crypt32.lib")
static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
/**
* Checks to see if a file stored at filePath matches the specified info.
*
* @param filePath The PE file path to check
* @param infoToMatch The acceptable information to match
* @return ERROR_SUCCESS if successful, ERROR_NOT_FOUND if the info
* does not match, or the last error otherwise.
*/
DWORD
CheckCertificateForPEFile(LPCWSTR filePath,
CertificateCheckInfo &infoToMatch)
{
HCERTSTORE certStore = nullptr;
HCRYPTMSG cryptMsg = nullptr;
PCCERT_CONTEXT certContext = nullptr;
PCMSG_SIGNER_INFO signerInfo = nullptr;
DWORD lastError = ERROR_SUCCESS;
// Get the HCERTSTORE and HCRYPTMSG from the signed file.
DWORD encoding, contentType, formatType;
BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
filePath,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_CONTENT_FLAG_ALL,
0, &encoding, &contentType,
&formatType, &certStore, &cryptMsg, nullptr);
if (!result) {
lastError = GetLastError();
LOG_WARN(("CryptQueryObject failed. (%d)", lastError));
goto cleanup;
}
// Pass in nullptr to get the needed signer information size.
DWORD signerInfoSize;
result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0,
nullptr, &signerInfoSize);
if (!result) {
lastError = GetLastError();
LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError));
goto cleanup;
}
// Allocate the needed size for the signer information.
signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize);
if (!signerInfo) {
lastError = GetLastError();
LOG_WARN(("Unable to allocate memory for Signer Info. (%d)", lastError));
goto cleanup;
}
// Get the signer information (PCMSG_SIGNER_INFO).
// In particular we want the issuer and serial number.
result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0,
(PVOID)signerInfo, &signerInfoSize);
if (!result) {
lastError = GetLastError();
LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError));
goto cleanup;
}
// Search for the signer certificate in the certificate store.
CERT_INFO certInfo;
certInfo.Issuer = signerInfo->Issuer;
certInfo.SerialNumber = signerInfo->SerialNumber;
certContext = CertFindCertificateInStore(certStore, ENCODING, 0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&certInfo, nullptr);
if (!certContext) {
lastError = GetLastError();
LOG_WARN(("CertFindCertificateInStore failed. (%d)", lastError));
goto cleanup;
}
if (!DoCertificateAttributesMatch(certContext, infoToMatch)) {
lastError = ERROR_NOT_FOUND;
LOG_WARN(("Certificate did not match issuer or name. (%d)", lastError));
goto cleanup;
}
cleanup:
if (signerInfo) {
LocalFree(signerInfo);
}
if (certContext) {
CertFreeCertificateContext(certContext);
}
if (certStore) {
CertCloseStore(certStore, 0);
}
if (cryptMsg) {
CryptMsgClose(cryptMsg);
}
return lastError;
}
/**
* Checks to see if a file stored at filePath matches the specified info.
*
* @param certContext The certificate context of the file
* @param infoToMatch The acceptable information to match
* @return FALSE if the info does not match or if any error occurs in the check
*/
BOOL
DoCertificateAttributesMatch(PCCERT_CONTEXT certContext,
CertificateCheckInfo &infoToMatch)
{
DWORD dwData;
LPTSTR szName = nullptr;
if (infoToMatch.issuer) {
// Pass in nullptr to get the needed size of the issuer buffer.
dwData = CertGetNameString(certContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, nullptr,
nullptr, 0);
if (!dwData) {
LOG_WARN(("CertGetNameString failed. (%d)", GetLastError()));
return FALSE;
}
// Allocate memory for Issuer name buffer.
LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
if (!szName) {
LOG_WARN(("Unable to allocate memory for issuer name. (%d)",
GetLastError()));
return FALSE;
}
// Get Issuer name.
if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, nullptr, szName, dwData)) {
LOG_WARN(("CertGetNameString failed. (%d)", GetLastError()));
LocalFree(szName);
return FALSE;
}
// If the issuer does not match, return a failure.
if (!infoToMatch.issuer ||
wcscmp(szName, infoToMatch.issuer)) {
LocalFree(szName);
return FALSE;
}
LocalFree(szName);
szName = nullptr;
}
if (infoToMatch.name) {
// Pass in nullptr to get the needed size of the name buffer.
dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
0, nullptr, nullptr, 0);
if (!dwData) {
LOG_WARN(("CertGetNameString failed. (%d)", GetLastError()));
return FALSE;
}
// Allocate memory for the name buffer.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
if (!szName) {
LOG_WARN(("Unable to allocate memory for subject name. (%d)",
GetLastError()));
return FALSE;
}
// Obtain the name.
if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
nullptr, szName, dwData))) {
LOG_WARN(("CertGetNameString failed. (%d)", GetLastError()));
LocalFree(szName);
return FALSE;
}
// If the issuer does not match, return a failure.
if (!infoToMatch.name ||
wcscmp(szName, infoToMatch.name)) {
LocalFree(szName);
return FALSE;
}
// We have a match!
LocalFree(szName);
}
// If there were any errors we would have aborted by now.
return TRUE;
}
/**
* Duplicates the specified string
*
* @param inputString The string to duplicate
* @return The duplicated string which should be freed by the caller.
*/
LPWSTR
AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString =
(LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR));
if (outputString) {
lstrcpyW(outputString, inputString);
}
return outputString;
}
/**
* Verifies the trust of the specified file path.
*
* @param filePath The file path to check.
* @return ERROR_SUCCESS if successful, or the last error code otherwise.
*/
DWORD
VerifyCertificateTrustForFile(LPCWSTR filePath)
{
// Setup the file to check.
WINTRUST_FILE_INFO fileToCheck;
ZeroMemory(&fileToCheck, sizeof(fileToCheck));
fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO);
fileToCheck.pcwszFilePath = filePath;
// Setup what to check, we want to check it is signed and trusted.
WINTRUST_DATA trustData;
ZeroMemory(&trustData, sizeof(trustData));
trustData.cbStruct = sizeof(trustData);
trustData.pPolicyCallbackData = nullptr;
trustData.pSIPClientData = nullptr;
trustData.dwUIChoice = WTD_UI_NONE;
trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
trustData.dwUnionChoice = WTD_CHOICE_FILE;
trustData.dwStateAction = 0;
trustData.hWVTStateData = nullptr;
trustData.pwszURLReference = nullptr;
// no UI
trustData.dwUIContext = 0;
trustData.pFile = &fileToCheck;
GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
// Check if the file is signed by something that is trusted.
LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData);
if (ERROR_SUCCESS == ret) {
// The hash that represents the subject is trusted and there were no
// verification errors. No publisher nor time stamp chain errors.
LOG(("The file \"%ls\" is signed and the signature was verified.",
filePath));
return ERROR_SUCCESS;
}
DWORD lastError = GetLastError();
LOG_WARN(("There was an error validating trust of the certificate for file"
" \"%ls\". Returned: %d. (%d)", filePath, ret, lastError));
return ret;
}
/* 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/. */
#ifndef _CERTIFICATECHECK_H_
#define _CERTIFICATECHECK_H_
#include <wincrypt.h>
struct CertificateCheckInfo
{
LPCWSTR name;
LPCWSTR issuer;
};
BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT pCertContext,
CertificateCheckInfo &infoToMatch);
DWORD VerifyCertificateTrustForFile(LPCWSTR filePath);
DWORD CheckCertificateForPEFile(LPCWSTR filePath,
CertificateCheckInfo &infoToMatch);
#endif
This diff is collapsed.
/* 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/. */
void WINAPI SvcMain(DWORD dwArgc, LPWSTR *lpszArgv);
void SvcInit(DWORD dwArgc, LPWSTR *lpszArgv);
void WINAPI SvcCtrlHandler(DWORD dwCtrl);
void ReportSvcStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint);
/* 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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "registrycertificates.hxx"
#include "pathhash.hxx"
#include "nsWindowsHelpers.hxx"
#include "servicebase.hxx"
#include "updatehelper.hxx"
#define MAX_KEY_LENGTH 255
/**
* Verifies if the file path matches any certificate stored in the registry.
*
* @param filePath The file path of the application to check if allowed.
* @return TRUE if the binary matches any of the allowed certificates.
*/
BOOL
DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath)
{
WCHAR maintenanceServiceKey[MAX_PATH + 1];
if (!CalculateRegistryPathFromFilePath(basePathForUpdate,
maintenanceServiceKey)) {
return FALSE;
}
// We use KEY_WOW64_64KEY to always force 64-bit view.
// The user may have both x86 and x64 applications installed
// which each register information. We need a consistent place
// to put those certificate attributes in and hence why we always
// force the non redirected registry under Wow6432Node.
// This flag is ignored on 32bit systems.
HKEY baseKeyRaw;
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
maintenanceServiceKey, 0,
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not open key. (%d)", retCode));
// Our tests run with a different apply directory for each test.
// We use this registry key on our test slaves to store the
// allowed name/issuers.
retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
TEST_ONLY_FALLBACK_KEY_PATH, 0,
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not open fallback key. (%d)", retCode));
return FALSE;
}
}
nsAutoRegKey baseKey(baseKeyRaw);
// Get the number of subkeys.
DWORD subkeyCount = 0;
retCode = RegQueryInfoKeyW(baseKey, nullptr, nullptr, nullptr, &subkeyCount,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not query info key. (%d)", retCode));
return FALSE;
}
// Enumerate the subkeys, each subkey represents an allowed certificate.
for (DWORD i = 0; i < subkeyCount; i++) {
WCHAR subkeyBuffer[MAX_KEY_LENGTH];
DWORD subkeyBufferCount = MAX_KEY_LENGTH;
retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer,
&subkeyBufferCount, nullptr,
nullptr, nullptr, nullptr);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not enum certs. (%d)", retCode));
return FALSE;
}
// Open the subkey for the current certificate
HKEY subKeyRaw;
retCode = RegOpenKeyExW(baseKey,
subkeyBuffer,
0,
KEY_READ | KEY_WOW64_64KEY,
&subKeyRaw);
nsAutoRegKey subKey(subKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not open subkey. (%d)", retCode));
continue; // Try the next subkey
}
const int MAX_CHAR_COUNT = 256;
DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
WCHAR name[MAX_CHAR_COUNT] = { L'\0' };
WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' };
// Get the name from the registry
retCode = RegQueryValueExW(subKey, L"name", 0, nullptr,
(LPBYTE)name, &valueBufSize);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not obtain name from registry. (%d)", retCode));
continue; // Try the next subkey
}
// Get the issuer from the registry
valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
retCode = RegQueryValueExW(subKey, L"issuer", 0, nullptr,
(LPBYTE)issuer, &valueBufSize);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Could not obtain issuer from registry. (%d)", retCode));
continue; // Try the next subkey
}
CertificateCheckInfo allowedCertificate = {
name,
issuer,
};
retCode = CheckCertificateForPEFile(filePath, allowedCertificate);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Error on certificate check. (%d)", retCode));
continue; // Try the next subkey
}
retCode = VerifyCertificateTrustForFile(filePath);
if (retCode != ERROR_SUCCESS) {
LOG_WARN(("Error on certificate trust check. (%d)", retCode));
continue; // Try the next subkey
}
// Raise the roof, we found a match!
return TRUE;
}
// No certificates match, :'(
return FALSE;
}
/* 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/. */
#ifndef _REGISTRYCERTIFICATES_H_
#define _REGISTRYCERTIFICATES_H_
#include "certificatecheck.hxx"
BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate,
LPCWSTR filePath);
#endif
/* 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/. */
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by updater.rc
//
#define IDI_DIALOG 1003
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
/* 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 "servicebase.hxx"
#include "nsWindowsHelpers.hxx"
// Shared code between applications and updater.exe
#include "nsWindowsRestart.cpp"
/**
* Verifies if 2 files are byte for byte equivalent.
*
* @param file1Path The first file to verify.
* @param file2Path The second file to verify.
* @param sameContent Out parameter, TRUE if the files are equal
* @return TRUE If there was no error checking the files.
*/
BOOL
VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent)
{
sameContent = FALSE;
nsAutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, 0, nullptr));
if (INVALID_HANDLE_VALUE == file1) {
return FALSE;
}
nsAutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, 0, nullptr));
if (INVALID_HANDLE_VALUE == file2) {
return FALSE;
}
DWORD fileSize1 = GetFileSize(file1, nullptr);
DWORD fileSize2 = GetFileSize(file2, nullptr);
if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2) {
return FALSE;
}
if (fileSize1 != fileSize2) {
// sameContent is already set to FALSE
return TRUE;
}
char buf1[COMPARE_BLOCKSIZE];
char buf2[COMPARE_BLOCKSIZE];
DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE;
DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE;
DWORD readAmount;
for (DWORD i = 0; i < numBlocks; i++) {
if (!ReadFile(file1, buf1, COMPARE_BLOCKSIZE, &readAmount, nullptr) ||
readAmount != COMPARE_BLOCKSIZE) {
return FALSE;
}
if (!ReadFile(file2, buf2, COMPARE_BLOCKSIZE, &readAmount, nullptr) ||
readAmount != COMPARE_BLOCKSIZE) {
return FALSE;
}
if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE)) {
// sameContent is already set to FALSE
return TRUE;
}
}
if (leftOver) {
if (!ReadFile(file1, buf1, leftOver, &readAmount, nullptr) ||
readAmount != leftOver) {
return FALSE;
}
if (!ReadFile(file2, buf2, leftOver, &readAmount, nullptr) ||
readAmount != leftOver) {
return FALSE;
}
if (memcmp(buf1, buf2, leftOver)) {
// sameContent is already set to FALSE
return TRUE;
}
}
sameContent = TRUE;
return TRUE;
}
/* 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 <windows.h>
#include "updatelogging.hxx"
BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent);
// 32KiB for comparing files at a time seems reasonable.
// The bigger the better for speed, but this will be used
// on the stack so I don't want it to be too big.
#define COMPARE_BLOCKSIZE 32768
// The following string resource value is used to uniquely identify the signed
// Mozilla application as an updater. Before the maintenance service will
// execute the updater it must have this updater identity string in its string
// table. No other signed Mozilla product will have this string table value.
#define UPDATER_IDENTITY_STRING \
"libo-updater.exe-4cdccec4-5ee0-4a06-9817-4cd899a9db49"
#define IDS_UPDATER_IDENTITY 1006
This diff is collapsed.
/* 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 "readstrings.h"
#define SVC_DISPLAY_NAME L"Mozilla Maintenance Service"
enum SvcInstallAction { UpgradeSvc, InstallSvc, ForceInstallSvc };
BOOL SvcInstall(SvcInstallAction action);
BOOL SvcUninstall();
BOOL StopService();
BOOL SetUserAccessServiceDACL(SC_HANDLE hService);
DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl,
PSECURITY_DESCRIPTOR psd);
struct MaintenanceServiceStringTable
{
char serviceDescription[MAX_TEXT_LEN];
};
This diff is collapsed.
/* 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/. */
BOOL ExecuteServiceCommand(int argc, LPWSTR *argv);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment