mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-16 20:27:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
250
toolkit/mozapps/update/common/certificatecheck.cpp
Normal file
250
toolkit/mozapps/update/common/certificatecheck.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/* 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.h"
|
||||
#include "updatecommon.h"
|
||||
|
||||
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;
|
||||
LPWSTR 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.
|
||||
szName = (LPWSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
|
||||
if (!szName) {
|
||||
LOG_WARN(("Unable to allocate memory for issuer name. (%d)",
|
||||
GetLastError()));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get Issuer name.
|
||||
if (!CertGetNameStringW(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 = (LPWSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
|
||||
if (!szName) {
|
||||
LOG_WARN(("Unable to allocate memory for subject name. (%d)",
|
||||
GetLastError()));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Obtain the name.
|
||||
if (!(CertGetNameStringW(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
22
toolkit/mozapps/update/common/certificatecheck.h
Normal file
22
toolkit/mozapps/update/common/certificatecheck.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* 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
|
||||
110
toolkit/mozapps/update/common/errors.h
Normal file
110
toolkit/mozapps/update/common/errors.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 Errors_h__
|
||||
#define Errors_h__
|
||||
|
||||
#define OK 0
|
||||
|
||||
// Error codes that are no longer used should not be used again unless they
|
||||
// aren't used in client code (e.g. nsUpdateService.js, updates.js,
|
||||
// UpdatePrompt.js, etc.).
|
||||
|
||||
#define MAR_ERROR_EMPTY_ACTION_LIST 1
|
||||
#define LOADSOURCE_ERROR_WRONG_SIZE 2
|
||||
|
||||
// Error codes 3-16 are for general update problems.
|
||||
#define USAGE_ERROR 3
|
||||
#define CRC_ERROR 4
|
||||
#define PARSE_ERROR 5
|
||||
#define READ_ERROR 6
|
||||
#define WRITE_ERROR 7
|
||||
// #define UNEXPECTED_ERROR 8 // Replaced with errors 38-42
|
||||
#define ELEVATION_CANCELED 9
|
||||
#define READ_STRINGS_MEM_ERROR 10
|
||||
#define ARCHIVE_READER_MEM_ERROR 11
|
||||
#define BSPATCH_MEM_ERROR 12
|
||||
#define UPDATER_MEM_ERROR 13
|
||||
#define UPDATER_QUOTED_PATH_MEM_ERROR 14
|
||||
#define BAD_ACTION_ERROR 15
|
||||
#define STRING_CONVERSION_ERROR 16
|
||||
|
||||
// Error codes 17-23 are related to security tasks for MAR
|
||||
// signing and MAR protection.
|
||||
#define CERT_LOAD_ERROR 17
|
||||
#define CERT_HANDLING_ERROR 18
|
||||
#define CERT_VERIFY_ERROR 19
|
||||
#define ARCHIVE_NOT_OPEN 20
|
||||
#define COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR 21
|
||||
#define MAR_CHANNEL_MISMATCH_ERROR 22
|
||||
#define VERSION_DOWNGRADE_ERROR 23
|
||||
|
||||
// Error codes 24-33 and 49-57 are for the Windows maintenance service.
|
||||
#define SERVICE_UPDATER_COULD_NOT_BE_STARTED 24
|
||||
#define SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS 25
|
||||
#define SERVICE_UPDATER_SIGN_ERROR 26
|
||||
#define SERVICE_UPDATER_COMPARE_ERROR 27
|
||||
#define SERVICE_UPDATER_IDENTITY_ERROR 28
|
||||
#define SERVICE_STILL_APPLYING_ON_SUCCESS 29
|
||||
#define SERVICE_STILL_APPLYING_ON_FAILURE 30
|
||||
#define SERVICE_UPDATER_NOT_FIXED_DRIVE 31
|
||||
#define SERVICE_COULD_NOT_LOCK_UPDATER 32
|
||||
#define SERVICE_INSTALLDIR_ERROR 33
|
||||
|
||||
#define NO_INSTALLDIR_ERROR 34
|
||||
#define WRITE_ERROR_ACCESS_DENIED 35
|
||||
// #define WRITE_ERROR_SHARING_VIOLATION 36 // Replaced with errors 46-48
|
||||
#define WRITE_ERROR_CALLBACK_APP 37
|
||||
#define UNEXPECTED_BZIP_ERROR 39
|
||||
#define UNEXPECTED_MAR_ERROR 40
|
||||
#define UNEXPECTED_BSPATCH_ERROR 41
|
||||
#define UNEXPECTED_FILE_OPERATION_ERROR 42
|
||||
#define FILESYSTEM_MOUNT_READWRITE_ERROR 43
|
||||
#define DELETE_ERROR_EXPECTED_DIR 46
|
||||
#define DELETE_ERROR_EXPECTED_FILE 47
|
||||
#define RENAME_ERROR_EXPECTED_FILE 48
|
||||
|
||||
// Error codes 24-33 and 49-57 are for the Windows maintenance service.
|
||||
#define SERVICE_COULD_NOT_COPY_UPDATER 49
|
||||
#define SERVICE_STILL_APPLYING_TERMINATED 50
|
||||
#define SERVICE_STILL_APPLYING_NO_EXIT_CODE 51
|
||||
#define SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR 52
|
||||
#define SERVICE_CALC_REG_PATH_ERROR 53
|
||||
#define SERVICE_INVALID_APPLYTO_DIR_ERROR 54
|
||||
#define SERVICE_INVALID_INSTALL_DIR_PATH_ERROR 55
|
||||
#define SERVICE_INVALID_WORKING_DIR_PATH_ERROR 56
|
||||
#define SERVICE_INSTALL_DIR_REG_ERROR 57
|
||||
|
||||
#define WRITE_ERROR_FILE_COPY 61
|
||||
#define WRITE_ERROR_DELETE_FILE 62
|
||||
#define WRITE_ERROR_OPEN_PATCH_FILE 63
|
||||
#define WRITE_ERROR_PATCH_FILE 64
|
||||
#define WRITE_ERROR_APPLY_DIR_PATH 65
|
||||
#define WRITE_ERROR_CALLBACK_PATH 66
|
||||
#define WRITE_ERROR_FILE_ACCESS_DENIED 67
|
||||
#define WRITE_ERROR_DIR_ACCESS_DENIED 68
|
||||
#define WRITE_ERROR_DELETE_BACKUP 69
|
||||
#define WRITE_ERROR_EXTRACT 70
|
||||
#define REMOVE_FILE_SPEC_ERROR 71
|
||||
#define INVALID_APPLYTO_DIR_STAGED_ERROR 72
|
||||
#define LOCK_ERROR_PATCH_FILE 73
|
||||
#define INVALID_APPLYTO_DIR_ERROR 74
|
||||
#define INVALID_INSTALL_DIR_PATH_ERROR 75
|
||||
#define INVALID_WORKING_DIR_PATH_ERROR 76
|
||||
#define INVALID_CALLBACK_PATH_ERROR 77
|
||||
#define INVALID_CALLBACK_DIR_ERROR 78
|
||||
|
||||
// Error codes 80 through 99 are reserved for nsUpdateService.js
|
||||
|
||||
// The following error codes are only used by updater.exe
|
||||
// when a fallback key exists for tests.
|
||||
#define FALLBACKKEY_UNKNOWN_ERROR 100
|
||||
#define FALLBACKKEY_REGPATH_ERROR 101
|
||||
#define FALLBACKKEY_NOKEY_ERROR 102
|
||||
#define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
|
||||
#define FALLBACKKEY_LAUNCH_ERROR 104
|
||||
|
||||
#endif // Errors_h__
|
||||
32
toolkit/mozapps/update/common/moz.build
Normal file
32
toolkit/mozapps/update/common/moz.build
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'readstrings.h',
|
||||
'updatecommon.h',
|
||||
'updatedefines.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
EXPORTS += [
|
||||
'pathhash.h',
|
||||
'uachelper.h',
|
||||
'updatehelper.cpp',
|
||||
'updatehelper.h',
|
||||
]
|
||||
if CONFIG['MOZ_MAINTENANCE_SERVICE']:
|
||||
EXPORTS += [
|
||||
'certificatecheck.h',
|
||||
'registrycertificates.h',
|
||||
]
|
||||
|
||||
Library('updatecommon')
|
||||
|
||||
DEFINES['NS_NO_XPCOM'] = True
|
||||
|
||||
srcdir = '.'
|
||||
|
||||
include('sources.mozbuild')
|
||||
139
toolkit/mozapps/update/common/pathhash.cpp
Normal file
139
toolkit/mozapps/update/common/pathhash.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/* 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 <wincrypt.h>
|
||||
#include "pathhash.h"
|
||||
|
||||
|
||||
/**
|
||||
* Converts a binary sequence into a hex string
|
||||
*
|
||||
* @param hash The binary data sequence
|
||||
* @param hashSize The size of the binary data sequence
|
||||
* @param hexString A buffer to store the hex string, must be of
|
||||
* size 2 * @hashSize
|
||||
*/
|
||||
static void
|
||||
BinaryDataToHexString(const BYTE *hash, DWORD &hashSize,
|
||||
LPWSTR hexString)
|
||||
{
|
||||
WCHAR *p = hexString;
|
||||
for (DWORD i = 0; i < hashSize; ++i) {
|
||||
wsprintfW(p, L"%.2x", hash[i]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates an MD5 hash for the given input binary data
|
||||
*
|
||||
* @param data Any sequence of bytes
|
||||
* @param dataSize The number of bytes inside @data
|
||||
* @param hash Output buffer to store hash, must be freed by the caller
|
||||
* @param hashSize The number of bytes in the output buffer
|
||||
* @return TRUE on success
|
||||
*/
|
||||
static BOOL
|
||||
CalculateMD5(const char *data, DWORD dataSize,
|
||||
BYTE **hash, DWORD &hashSize)
|
||||
{
|
||||
HCRYPTPROV hProv = 0;
|
||||
HCRYPTHASH hHash = 0;
|
||||
|
||||
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT)) {
|
||||
if (NTE_BAD_KEYSET != GetLastError()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Maybe it doesn't exist, try to create it.
|
||||
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!CryptHashData(hHash, reinterpret_cast<const BYTE*>(data),
|
||||
dataSize, 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwCount = sizeof(DWORD);
|
||||
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&hashSize,
|
||||
&dwCount, 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*hash = new BYTE[hashSize];
|
||||
ZeroMemory(*hash, hashSize);
|
||||
if (!CryptGetHashParam(hHash, HP_HASHVAL, *hash, &hashSize, 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (hHash) {
|
||||
CryptDestroyHash(hHash);
|
||||
}
|
||||
|
||||
if (hProv) {
|
||||
CryptReleaseContext(hProv,0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a file path into a unique registry location for cert storage
|
||||
*
|
||||
* @param filePath The input file path to get a registry path from
|
||||
* @param registryPath A buffer to write the registry path to, must
|
||||
* be of size in WCHARs MAX_PATH + 1
|
||||
* @return TRUE if successful
|
||||
*/
|
||||
BOOL
|
||||
CalculateRegistryPathFromFilePath(const LPCWSTR filePath,
|
||||
LPWSTR registryPath)
|
||||
{
|
||||
size_t filePathLen = wcslen(filePath);
|
||||
if (!filePathLen) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If the file path ends in a slash, ignore that character
|
||||
if (filePath[filePathLen -1] == L'\\' ||
|
||||
filePath[filePathLen - 1] == L'/') {
|
||||
filePathLen--;
|
||||
}
|
||||
|
||||
// Copy in the full path into our own buffer.
|
||||
// Copying in the extra slash is OK because we calculate the hash
|
||||
// based on the filePathLen which excludes the slash.
|
||||
// +2 to account for the possibly trailing slash and the null terminator.
|
||||
WCHAR *lowercasePath = new WCHAR[filePathLen + 2];
|
||||
memset(lowercasePath, 0, (filePathLen + 2) * sizeof(WCHAR));
|
||||
wcsncpy(lowercasePath, filePath, filePathLen + 1);
|
||||
_wcslwr(lowercasePath);
|
||||
|
||||
BYTE *hash;
|
||||
DWORD hashSize = 0;
|
||||
if (!CalculateMD5(reinterpret_cast<const char*>(lowercasePath),
|
||||
filePathLen * 2,
|
||||
&hash, hashSize)) {
|
||||
delete[] lowercasePath;
|
||||
return FALSE;
|
||||
}
|
||||
delete[] lowercasePath;
|
||||
|
||||
LPCWSTR baseRegPath = L"SOFTWARE\\Mozilla\\"
|
||||
L"MaintenanceService\\";
|
||||
wcsncpy(registryPath, baseRegPath, MAX_PATH);
|
||||
BinaryDataToHexString(hash, hashSize,
|
||||
registryPath + wcslen(baseRegPath));
|
||||
delete[] hash;
|
||||
return TRUE;
|
||||
}
|
||||
19
toolkit/mozapps/update/common/pathhash.h
Normal file
19
toolkit/mozapps/update/common/pathhash.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* 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 _PATHHASH_H_
|
||||
#define _PATHHASH_H_
|
||||
|
||||
/**
|
||||
* Converts a file path into a unique registry location for cert storage
|
||||
*
|
||||
* @param filePath The input file path to get a registry path from
|
||||
* @param registryPath A buffer to write the registry path to, must
|
||||
* be of size in WCHARs MAX_PATH + 1
|
||||
* @return TRUE if successful
|
||||
*/
|
||||
BOOL CalculateRegistryPathFromFilePath(const LPCWSTR filePath,
|
||||
LPWSTR registryPath);
|
||||
|
||||
#endif
|
||||
236
toolkit/mozapps/update/common/readstrings.cpp
Normal file
236
toolkit/mozapps/update/common/readstrings.cpp
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "readstrings.h"
|
||||
#include "errors.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
# define NS_tfopen _wfopen
|
||||
# define OPEN_MODE L"rb"
|
||||
#else
|
||||
# define NS_tfopen fopen
|
||||
# define OPEN_MODE "r"
|
||||
#endif
|
||||
|
||||
// stack based FILE wrapper to ensure that fclose is called.
|
||||
class AutoFILE {
|
||||
public:
|
||||
explicit AutoFILE(FILE *fp) : fp_(fp) {}
|
||||
~AutoFILE() { if (fp_) fclose(fp_); }
|
||||
operator FILE *() { return fp_; }
|
||||
private:
|
||||
FILE *fp_;
|
||||
};
|
||||
|
||||
class AutoCharArray {
|
||||
public:
|
||||
explicit AutoCharArray(size_t len) { ptr_ = new char[len]; }
|
||||
~AutoCharArray() { delete[] ptr_; }
|
||||
operator char *() { return ptr_; }
|
||||
private:
|
||||
char *ptr_;
|
||||
};
|
||||
|
||||
static const char kNL[] = "\r\n";
|
||||
static const char kEquals[] = "=";
|
||||
static const char kWhitespace[] = " \t";
|
||||
static const char kRBracket[] = "]";
|
||||
|
||||
static const char*
|
||||
NS_strspnp(const char *delims, const char *str)
|
||||
{
|
||||
const char *d;
|
||||
do {
|
||||
for (d = delims; *d != '\0'; ++d) {
|
||||
if (*str == *d) {
|
||||
++str;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (*d);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char*
|
||||
NS_strtok(const char *delims, char **str)
|
||||
{
|
||||
if (!*str)
|
||||
return nullptr;
|
||||
|
||||
char *ret = (char*) NS_strspnp(delims, *str);
|
||||
|
||||
if (!*ret) {
|
||||
*str = ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char *i = ret;
|
||||
do {
|
||||
for (const char *d = delims; *d != '\0'; ++d) {
|
||||
if (*i == *d) {
|
||||
*i = '\0';
|
||||
*str = ++i;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
++i;
|
||||
} while (*i);
|
||||
|
||||
*str = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a key in a keyList containing zero-delimited keys ending with "\0\0".
|
||||
* Returns a zero-based index of the key in the list, or -1 if the key is not found.
|
||||
*/
|
||||
static int
|
||||
find_key(const char *keyList, char* key)
|
||||
{
|
||||
if (!keyList)
|
||||
return -1;
|
||||
|
||||
int index = 0;
|
||||
const char *p = keyList;
|
||||
while (*p)
|
||||
{
|
||||
if (strcmp(key, p) == 0)
|
||||
return index;
|
||||
|
||||
p += strlen(p) + 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
// The key was not found if we came here
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* A very basic parser for updater.ini taken mostly from nsINIParser.cpp
|
||||
* that can be used by standalone apps.
|
||||
*
|
||||
* @param path Path to the .ini file to read
|
||||
* @param keyList List of zero-delimited keys ending with two zero characters
|
||||
* @param numStrings Number of strings to read into results buffer - must be equal to the number of keys
|
||||
* @param results Two-dimensional array of strings to be filled in the same order as the keys provided
|
||||
* @param section Optional name of the section to read; defaults to "Strings"
|
||||
*/
|
||||
int
|
||||
ReadStrings(const NS_tchar *path,
|
||||
const char *keyList,
|
||||
unsigned int numStrings,
|
||||
char results[][MAX_TEXT_LEN],
|
||||
const char *section)
|
||||
{
|
||||
AutoFILE fp(NS_tfopen(path, OPEN_MODE));
|
||||
|
||||
if (!fp)
|
||||
return READ_ERROR;
|
||||
|
||||
/* get file size */
|
||||
if (fseek(fp, 0, SEEK_END) != 0)
|
||||
return READ_ERROR;
|
||||
|
||||
long len = ftell(fp);
|
||||
if (len <= 0)
|
||||
return READ_ERROR;
|
||||
|
||||
size_t flen = size_t(len);
|
||||
AutoCharArray fileContents(flen + 1);
|
||||
if (!fileContents)
|
||||
return READ_STRINGS_MEM_ERROR;
|
||||
|
||||
/* read the file in one swoop */
|
||||
if (fseek(fp, 0, SEEK_SET) != 0)
|
||||
return READ_ERROR;
|
||||
|
||||
size_t rd = fread(fileContents, sizeof(char), flen, fp);
|
||||
if (rd != flen)
|
||||
return READ_ERROR;
|
||||
|
||||
fileContents[flen] = '\0';
|
||||
|
||||
char *buffer = fileContents;
|
||||
bool inStringsSection = false;
|
||||
|
||||
unsigned int read = 0;
|
||||
|
||||
while (char *token = NS_strtok(kNL, &buffer)) {
|
||||
if (token[0] == '#' || token[0] == ';') // it's a comment
|
||||
continue;
|
||||
|
||||
token = (char*) NS_strspnp(kWhitespace, token);
|
||||
if (!*token) // empty line
|
||||
continue;
|
||||
|
||||
if (token[0] == '[') { // section header!
|
||||
++token;
|
||||
char const * currSection = token;
|
||||
|
||||
char *rb = NS_strtok(kRBracket, &token);
|
||||
if (!rb || NS_strtok(kWhitespace, &token)) {
|
||||
// there's either an unclosed [Section or a [Section]Moretext!
|
||||
// we could frankly decide that this INI file is malformed right
|
||||
// here and stop, but we won't... keep going, looking for
|
||||
// a well-formed [section] to continue working with
|
||||
inStringsSection = false;
|
||||
}
|
||||
else {
|
||||
if (section)
|
||||
inStringsSection = strcmp(currSection, section) == 0;
|
||||
else
|
||||
inStringsSection = strcmp(currSection, "Strings") == 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inStringsSection) {
|
||||
// If we haven't found a section header (or we found a malformed
|
||||
// section header), or this isn't the [Strings] section don't bother
|
||||
// parsing this line.
|
||||
continue;
|
||||
}
|
||||
|
||||
char *key = token;
|
||||
char *e = NS_strtok(kEquals, &token);
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
int keyIndex = find_key(keyList, key);
|
||||
if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings)
|
||||
{
|
||||
strncpy(results[keyIndex], token, MAX_TEXT_LEN - 1);
|
||||
results[keyIndex][MAX_TEXT_LEN - 1] = '\0';
|
||||
read++;
|
||||
}
|
||||
}
|
||||
|
||||
return (read == numStrings) ? OK : PARSE_ERROR;
|
||||
}
|
||||
|
||||
// A wrapper function to read strings for the updater.
|
||||
// Added for compatibility with the original code.
|
||||
int
|
||||
ReadStrings(const NS_tchar *path, StringTable *results)
|
||||
{
|
||||
const unsigned int kNumStrings = 2;
|
||||
const char *kUpdaterKeys = "Title\0Info\0";
|
||||
char updater_strings[kNumStrings][MAX_TEXT_LEN];
|
||||
|
||||
int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings);
|
||||
|
||||
strncpy(results->title, updater_strings[0], MAX_TEXT_LEN - 1);
|
||||
results->title[MAX_TEXT_LEN - 1] = '\0';
|
||||
strncpy(results->info, updater_strings[1], MAX_TEXT_LEN - 1);
|
||||
results->info[MAX_TEXT_LEN - 1] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
43
toolkit/mozapps/update/common/readstrings.h
Normal file
43
toolkit/mozapps/update/common/readstrings.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 READSTRINGS_H__
|
||||
#define READSTRINGS_H__
|
||||
|
||||
#define MAX_TEXT_LEN 600
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include <windows.h>
|
||||
typedef WCHAR NS_tchar;
|
||||
#else
|
||||
typedef char NS_tchar;
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
struct StringTable
|
||||
{
|
||||
char title[MAX_TEXT_LEN];
|
||||
char info[MAX_TEXT_LEN];
|
||||
};
|
||||
|
||||
/**
|
||||
* This function reads in localized strings from updater.ini
|
||||
*/
|
||||
int ReadStrings(const NS_tchar *path, StringTable *results);
|
||||
|
||||
/**
|
||||
* This function reads in localized strings corresponding to the keys from a given .ini
|
||||
*/
|
||||
int ReadStrings(const NS_tchar *path,
|
||||
const char *keyList,
|
||||
unsigned int numStrings,
|
||||
char results[][MAX_TEXT_LEN],
|
||||
const char *section = nullptr);
|
||||
|
||||
#endif // READSTRINGS_H__
|
||||
154
toolkit/mozapps/update/common/registrycertificates.cpp
Normal file
154
toolkit/mozapps/update/common/registrycertificates.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* 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.h"
|
||||
#include "pathhash.h"
|
||||
#include "updatecommon.h"
|
||||
#include "updatehelper.h"
|
||||
#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.
|
||||
* @param allowFallbackKeySkip when this is TRUE the fallback registry key will
|
||||
* be used to skip the certificate check. This is the default since the
|
||||
* fallback registry key is located under HKEY_LOCAL_MACHINE which can't be
|
||||
* written to by a low integrity process.
|
||||
* Note: the maintenance service binary can be used to perform this check for
|
||||
* testing or troubleshooting.
|
||||
* @return TRUE if the binary matches any of the allowed certificates.
|
||||
*/
|
||||
BOOL
|
||||
DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath,
|
||||
BOOL allowFallbackKeySkip)
|
||||
{
|
||||
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 baseKey;
|
||||
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
maintenanceServiceKey, 0,
|
||||
KEY_READ | KEY_WOW64_64KEY, &baseKey);
|
||||
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, &baseKey);
|
||||
if (retCode != ERROR_SUCCESS) {
|
||||
LOG_WARN(("Could not open fallback key. (%d)", retCode));
|
||||
return FALSE;
|
||||
} else if (allowFallbackKeySkip) {
|
||||
LOG_WARN(("Fallback key present, skipping VerifyCertificateTrustForFile "
|
||||
"check and the certificate attribute registry matching "
|
||||
"check."));
|
||||
RegCloseKey(baseKey);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
RegCloseKey(baseKey);
|
||||
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));
|
||||
RegCloseKey(baseKey);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Open the subkey for the current certificate
|
||||
HKEY subKey;
|
||||
retCode = RegOpenKeyExW(baseKey,
|
||||
subkeyBuffer,
|
||||
0,
|
||||
KEY_READ | KEY_WOW64_64KEY,
|
||||
&subKey);
|
||||
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));
|
||||
RegCloseKey(subKey);
|
||||
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));
|
||||
RegCloseKey(subKey);
|
||||
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));
|
||||
RegCloseKey(subKey);
|
||||
continue; // Try the next subkey
|
||||
}
|
||||
|
||||
retCode = VerifyCertificateTrustForFile(filePath);
|
||||
if (retCode != ERROR_SUCCESS) {
|
||||
LOG_WARN(("Error on certificate trust check. (%d)", retCode));
|
||||
RegCloseKey(subKey);
|
||||
continue; // Try the next subkey
|
||||
}
|
||||
|
||||
RegCloseKey(baseKey);
|
||||
// Raise the roof, we found a match!
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
RegCloseKey(baseKey);
|
||||
// No certificates match, :'(
|
||||
return FALSE;
|
||||
}
|
||||
14
toolkit/mozapps/update/common/registrycertificates.h
Normal file
14
toolkit/mozapps/update/common/registrycertificates.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* 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.h"
|
||||
|
||||
BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate,
|
||||
LPCWSTR filePath,
|
||||
BOOL allowFallbackKeySkip = TRUE);
|
||||
|
||||
#endif
|
||||
28
toolkit/mozapps/update/common/sources.mozbuild
Normal file
28
toolkit/mozapps/update/common/sources.mozbuild
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# 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/.
|
||||
|
||||
sources = []
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
sources += [
|
||||
'pathhash.cpp',
|
||||
'uachelper.cpp',
|
||||
'updatehelper.cpp',
|
||||
]
|
||||
if CONFIG['MOZ_MAINTENANCE_SERVICE']:
|
||||
sources += [
|
||||
'certificatecheck.cpp',
|
||||
'registrycertificates.cpp',
|
||||
]
|
||||
OS_LIBS += [
|
||||
'crypt32',
|
||||
'wintrust',
|
||||
]
|
||||
|
||||
sources += [
|
||||
'readstrings.cpp',
|
||||
'updatecommon.cpp',
|
||||
]
|
||||
|
||||
SOURCES += sorted(['%s/%s' % (srcdir, s) for s in sources])
|
||||
222
toolkit/mozapps/update/common/uachelper.cpp
Normal file
222
toolkit/mozapps/update/common/uachelper.cpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/* 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 <wtsapi32.h>
|
||||
#include "uachelper.h"
|
||||
#include "updatecommon.h"
|
||||
|
||||
// See the MSDN documentation with title: Privilege Constants
|
||||
// At the time of this writing, this documentation is located at:
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx
|
||||
LPCTSTR UACHelper::PrivsToDisable[] = {
|
||||
SE_ASSIGNPRIMARYTOKEN_NAME,
|
||||
SE_AUDIT_NAME,
|
||||
SE_BACKUP_NAME,
|
||||
// CreateProcess will succeed but the app will fail to launch on some WinXP
|
||||
// machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens
|
||||
// for limited user accounts on those machines. The define is kept here as a
|
||||
// reminder that it should never be re-added.
|
||||
// This permission is for directory watching but also from MSDN: "This
|
||||
// privilege also causes the system to skip all traversal access checks."
|
||||
// SE_CHANGE_NOTIFY_NAME,
|
||||
SE_CREATE_GLOBAL_NAME,
|
||||
SE_CREATE_PAGEFILE_NAME,
|
||||
SE_CREATE_PERMANENT_NAME,
|
||||
SE_CREATE_SYMBOLIC_LINK_NAME,
|
||||
SE_CREATE_TOKEN_NAME,
|
||||
SE_DEBUG_NAME,
|
||||
SE_ENABLE_DELEGATION_NAME,
|
||||
SE_IMPERSONATE_NAME,
|
||||
SE_INC_BASE_PRIORITY_NAME,
|
||||
SE_INCREASE_QUOTA_NAME,
|
||||
SE_INC_WORKING_SET_NAME,
|
||||
SE_LOAD_DRIVER_NAME,
|
||||
SE_LOCK_MEMORY_NAME,
|
||||
SE_MACHINE_ACCOUNT_NAME,
|
||||
SE_MANAGE_VOLUME_NAME,
|
||||
SE_PROF_SINGLE_PROCESS_NAME,
|
||||
SE_RELABEL_NAME,
|
||||
SE_REMOTE_SHUTDOWN_NAME,
|
||||
SE_RESTORE_NAME,
|
||||
SE_SECURITY_NAME,
|
||||
SE_SHUTDOWN_NAME,
|
||||
SE_SYNC_AGENT_NAME,
|
||||
SE_SYSTEM_ENVIRONMENT_NAME,
|
||||
SE_SYSTEM_PROFILE_NAME,
|
||||
SE_SYSTEMTIME_NAME,
|
||||
SE_TAKE_OWNERSHIP_NAME,
|
||||
SE_TCB_NAME,
|
||||
SE_TIME_ZONE_NAME,
|
||||
SE_TRUSTED_CREDMAN_ACCESS_NAME,
|
||||
SE_UNDOCK_NAME,
|
||||
SE_UNSOLICITED_INPUT_NAME
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens a user token for the given session ID
|
||||
*
|
||||
* @param sessionID The session ID for the token to obtain
|
||||
* @return A handle to the token to obtain which will be primary if enough
|
||||
* permissions exist. Caller should close the handle.
|
||||
*/
|
||||
HANDLE
|
||||
UACHelper::OpenUserToken(DWORD sessionID)
|
||||
{
|
||||
HMODULE module = LoadLibraryW(L"wtsapi32.dll");
|
||||
HANDLE token = nullptr;
|
||||
decltype(WTSQueryUserToken)* wtsQueryUserToken =
|
||||
(decltype(WTSQueryUserToken)*) GetProcAddress(module, "WTSQueryUserToken");
|
||||
if (wtsQueryUserToken) {
|
||||
wtsQueryUserToken(sessionID, &token);
|
||||
}
|
||||
FreeLibrary(module);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a linked token for the specified token.
|
||||
*
|
||||
* @param token The token to get the linked token from
|
||||
* @return A linked token or nullptr if one does not exist.
|
||||
* Caller should close the handle.
|
||||
*/
|
||||
HANDLE
|
||||
UACHelper::OpenLinkedToken(HANDLE token)
|
||||
{
|
||||
// Magic below...
|
||||
// UAC creates 2 tokens. One is the restricted token which we have.
|
||||
// the other is the UAC elevated one. Since we are running as a service
|
||||
// as the system account we have access to both.
|
||||
TOKEN_LINKED_TOKEN tlt;
|
||||
HANDLE hNewLinkedToken = nullptr;
|
||||
DWORD len;
|
||||
if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken,
|
||||
&tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) {
|
||||
token = tlt.LinkedToken;
|
||||
hNewLinkedToken = token;
|
||||
}
|
||||
return hNewLinkedToken;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables or disables a privilege for the specified token.
|
||||
*
|
||||
* @param token The token to adjust the privilege on.
|
||||
* @param priv The privilege to adjust.
|
||||
* @param enable Whether to enable or disable it
|
||||
* @return TRUE if the token was adjusted to the specified value.
|
||||
*/
|
||||
BOOL
|
||||
UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable)
|
||||
{
|
||||
LUID luidOfPriv;
|
||||
if (!LookupPrivilegeValue(nullptr, priv, &luidOfPriv)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TOKEN_PRIVILEGES tokenPriv;
|
||||
tokenPriv.PrivilegeCount = 1;
|
||||
tokenPriv.Privileges[0].Luid = luidOfPriv;
|
||||
tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
|
||||
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
if (!AdjustTokenPrivileges(token, false, &tokenPriv,
|
||||
sizeof(tokenPriv), nullptr, nullptr)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return GetLastError() == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each privilege that is specified, an attempt will be made to
|
||||
* drop the privilege.
|
||||
*
|
||||
* @param token The token to adjust the privilege on.
|
||||
* Pass nullptr for current token.
|
||||
* @param unneededPrivs An array of unneeded privileges.
|
||||
* @param count The size of the array
|
||||
* @return TRUE if there were no errors
|
||||
*/
|
||||
BOOL
|
||||
UACHelper::DisableUnneededPrivileges(HANDLE token,
|
||||
LPCTSTR *unneededPrivs,
|
||||
size_t count)
|
||||
{
|
||||
HANDLE obtainedToken = nullptr;
|
||||
if (!token) {
|
||||
// Note: This handle is a pseudo-handle and need not be closed
|
||||
HANDLE process = GetCurrentProcess();
|
||||
if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken)) {
|
||||
LOG_WARN(("Could not obtain token for current process, no "
|
||||
"privileges changed. (%d)", GetLastError()));
|
||||
return FALSE;
|
||||
}
|
||||
token = obtainedToken;
|
||||
}
|
||||
|
||||
BOOL result = TRUE;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (SetPrivilege(token, unneededPrivs[i], FALSE)) {
|
||||
LOG(("Disabled unneeded token privilege: %s.",
|
||||
unneededPrivs[i]));
|
||||
} else {
|
||||
LOG(("Could not disable token privilege value: %s. (%d)",
|
||||
unneededPrivs[i], GetLastError()));
|
||||
result = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (obtainedToken) {
|
||||
CloseHandle(obtainedToken);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables privileges for the specified token.
|
||||
* The privileges to disable are in PrivsToDisable.
|
||||
* In the future there could be new privs and we are not sure if we should
|
||||
* explicitly disable these or not.
|
||||
*
|
||||
* @param token The token to drop the privilege on.
|
||||
* Pass nullptr for current token.
|
||||
* @return TRUE if there were no errors
|
||||
*/
|
||||
BOOL
|
||||
UACHelper::DisablePrivileges(HANDLE token)
|
||||
{
|
||||
static const size_t PrivsToDisableSize =
|
||||
sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]);
|
||||
|
||||
return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable,
|
||||
PrivsToDisableSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user can elevate.
|
||||
*
|
||||
* @return true if the user can elevate.
|
||||
* false otherwise.
|
||||
*/
|
||||
bool
|
||||
UACHelper::CanUserElevate()
|
||||
{
|
||||
HANDLE token;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TOKEN_ELEVATION_TYPE elevationType;
|
||||
DWORD len;
|
||||
bool canElevate = GetTokenInformation(token, TokenElevationType,
|
||||
&elevationType,
|
||||
sizeof(elevationType), &len) &&
|
||||
(elevationType == TokenElevationTypeLimited);
|
||||
CloseHandle(token);
|
||||
|
||||
return canElevate;
|
||||
}
|
||||
23
toolkit/mozapps/update/common/uachelper.h
Normal file
23
toolkit/mozapps/update/common/uachelper.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* 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 _UACHELPER_H_
|
||||
#define _UACHELPER_H_
|
||||
|
||||
class UACHelper
|
||||
{
|
||||
public:
|
||||
static HANDLE OpenUserToken(DWORD sessionID);
|
||||
static HANDLE OpenLinkedToken(HANDLE token);
|
||||
static BOOL DisablePrivileges(HANDLE token);
|
||||
static bool CanUserElevate();
|
||||
|
||||
private:
|
||||
static BOOL SetPrivilege(HANDLE token, LPCTSTR privs, BOOL enable);
|
||||
static BOOL DisableUnneededPrivileges(HANDLE token,
|
||||
LPCTSTR *unneededPrivs, size_t count);
|
||||
static LPCTSTR PrivsToDisable[];
|
||||
};
|
||||
|
||||
#endif
|
||||
213
toolkit/mozapps/update/common/updatecommon.cpp
Normal file
213
toolkit/mozapps/update/common/updatecommon.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/* 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/. */
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "updatecommon.h"
|
||||
|
||||
UpdateLog::UpdateLog() : logFP(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateLog::Init(NS_tchar* sourcePath,
|
||||
const NS_tchar* fileName)
|
||||
{
|
||||
if (logFP) {
|
||||
return;
|
||||
}
|
||||
|
||||
int dstFilePathLen = NS_tsnprintf(mDstFilePath,
|
||||
sizeof(mDstFilePath)/sizeof(mDstFilePath[0]),
|
||||
NS_T("%s/%s"), sourcePath, fileName);
|
||||
// If the destination path was over the length limit,
|
||||
// disable logging by skipping opening the file and setting logFP.
|
||||
if ((dstFilePathLen > 0) &&
|
||||
(dstFilePathLen <
|
||||
static_cast<int>(sizeof(mDstFilePath)/sizeof(mDstFilePath[0])))) {
|
||||
#ifdef XP_WIN
|
||||
if (GetTempFileNameW(sourcePath, L"log", 0, mTmpFilePath) != 0) {
|
||||
logFP = NS_tfopen(mTmpFilePath, NS_T("w"));
|
||||
|
||||
// Delete this file now so it is possible to tell from the unelevated
|
||||
// updater process if the elevated updater process has written the log.
|
||||
DeleteFileW(mDstFilePath);
|
||||
}
|
||||
#elif XP_MACOSX
|
||||
logFP = NS_tfopen(mDstFilePath, NS_T("w"));
|
||||
#else
|
||||
// On platforms that have an updates directory in the installation directory
|
||||
// (e.g. platforms other than Windows and Mac) the update log is written to
|
||||
// a temporary file and then to the update log file. This is needed since
|
||||
// the installation directory is moved during a replace request. This can be
|
||||
// removed when the platform's updates directory is located outside of the
|
||||
// installation directory.
|
||||
logFP = tmpfile();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateLog::Finish()
|
||||
{
|
||||
if (!logFP) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(XP_WIN) && !defined(XP_MACOSX)
|
||||
const int blockSize = 1024;
|
||||
char buffer[blockSize];
|
||||
fflush(logFP);
|
||||
rewind(logFP);
|
||||
|
||||
FILE *updateLogFP = NS_tfopen(mDstFilePath, NS_T("wb+"));
|
||||
while (!feof(logFP)) {
|
||||
size_t read = fread(buffer, 1, blockSize, logFP);
|
||||
if (ferror(logFP)) {
|
||||
fclose(logFP);
|
||||
logFP = nullptr;
|
||||
fclose(updateLogFP);
|
||||
updateLogFP = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
|
||||
while (written < read) {
|
||||
size_t chunkWritten = fwrite(buffer, 1, read - written, updateLogFP);
|
||||
if (chunkWritten <= 0) {
|
||||
fclose(logFP);
|
||||
logFP = nullptr;
|
||||
fclose(updateLogFP);
|
||||
updateLogFP = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
written += chunkWritten;
|
||||
}
|
||||
}
|
||||
fclose(updateLogFP);
|
||||
updateLogFP = nullptr;
|
||||
#endif
|
||||
|
||||
fclose(logFP);
|
||||
logFP = nullptr;
|
||||
|
||||
#ifdef XP_WIN
|
||||
// When the log file already exists then the elevated updater has already
|
||||
// written the log file and the temp file for the log should be discarded.
|
||||
if (!NS_taccess(mDstFilePath, F_OK)) {
|
||||
DeleteFileW(mTmpFilePath);
|
||||
} else {
|
||||
MoveFileW(mTmpFilePath, mDstFilePath);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateLog::Flush()
|
||||
{
|
||||
if (!logFP) {
|
||||
return;
|
||||
}
|
||||
|
||||
fflush(logFP);
|
||||
}
|
||||
|
||||
void UpdateLog::Printf(const char *fmt, ... )
|
||||
{
|
||||
if (!logFP) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(logFP, fmt, ap);
|
||||
fprintf(logFP, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void UpdateLog::WarnPrintf(const char *fmt, ... )
|
||||
{
|
||||
if (!logFP) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(logFP, "*** Warning: ");
|
||||
vfprintf(logFP, fmt, ap);
|
||||
fprintf(logFP, "***\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs checks of a full path for validity for this application.
|
||||
*
|
||||
* @param origFullPath
|
||||
* The full path to check.
|
||||
* @return true if the path is valid for this application and false otherwise.
|
||||
*/
|
||||
bool
|
||||
IsValidFullPath(NS_tchar* origFullPath)
|
||||
{
|
||||
// Subtract 1 from MAXPATHLEN for null termination.
|
||||
if (NS_tstrlen(origFullPath) > MAXPATHLEN - 1) {
|
||||
// The path is longer than acceptable for this application.
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
NS_tchar testPath[MAXPATHLEN] = {NS_T('\0')};
|
||||
// GetFullPathNameW will replace / with \ which PathCanonicalizeW requires.
|
||||
if (GetFullPathNameW(origFullPath, MAXPATHLEN, testPath, nullptr) == 0) {
|
||||
// Unable to get the full name for the path (e.g. invalid path).
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_tchar canonicalPath[MAXPATHLEN] = {NS_T('\0')};
|
||||
if (!PathCanonicalizeW(canonicalPath, testPath)) {
|
||||
// Path could not be canonicalized (e.g. invalid path).
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the path passed in resolves to a differerent path.
|
||||
if (NS_tstricmp(origFullPath, canonicalPath) != 0) {
|
||||
// Case insensitive string comparison between the supplied path and the
|
||||
// canonical path are not equal. This will prevent directory traversal and
|
||||
// the use of / in paths since they are converted to \.
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_tstrncpy(testPath, origFullPath, MAXPATHLEN);
|
||||
if (!PathStripToRootW(testPath)) {
|
||||
// It should always be possible to strip a valid path to its root.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (origFullPath[0] == NS_T('\\')) {
|
||||
// Only allow UNC server share paths.
|
||||
if (!PathIsUNCServerShareW(testPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Only allow full paths.
|
||||
if (origFullPath[0] != NS_T('/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The path must not traverse directories
|
||||
if (NS_tstrstr(origFullPath, NS_T("..")) != nullptr ||
|
||||
NS_tstrstr(origFullPath, NS_T("./")) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
47
toolkit/mozapps/update/common/updatecommon.h
Normal file
47
toolkit/mozapps/update/common/updatecommon.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* 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 UPDATECOMMON_H
|
||||
#define UPDATECOMMON_H
|
||||
|
||||
#include "updatedefines.h"
|
||||
#include <stdio.h>
|
||||
|
||||
class UpdateLog
|
||||
{
|
||||
public:
|
||||
static UpdateLog & GetPrimaryLog()
|
||||
{
|
||||
static UpdateLog primaryLog;
|
||||
return primaryLog;
|
||||
}
|
||||
|
||||
void Init(NS_tchar* sourcePath, const NS_tchar* fileName);
|
||||
void Finish();
|
||||
void Flush();
|
||||
void Printf(const char *fmt, ... );
|
||||
void WarnPrintf(const char *fmt, ... );
|
||||
|
||||
~UpdateLog()
|
||||
{
|
||||
Finish();
|
||||
}
|
||||
|
||||
protected:
|
||||
UpdateLog();
|
||||
FILE *logFP;
|
||||
NS_tchar mTmpFilePath[MAXPATHLEN];
|
||||
NS_tchar mDstFilePath[MAXPATHLEN];
|
||||
};
|
||||
|
||||
bool IsValidFullPath(NS_tchar* fullPath);
|
||||
|
||||
#define LOG_WARN(args) UpdateLog::GetPrimaryLog().WarnPrintf args
|
||||
#define LOG(args) UpdateLog::GetPrimaryLog().Printf args
|
||||
#define LogInit(PATHNAME_, FILENAME_) \
|
||||
UpdateLog::GetPrimaryLog().Init(PATHNAME_, FILENAME_)
|
||||
#define LogFinish() UpdateLog::GetPrimaryLog().Finish()
|
||||
#define LogFlush() UpdateLog::GetPrimaryLog().Flush()
|
||||
|
||||
#endif
|
||||
142
toolkit/mozapps/update/common/updatedefines.h
Normal file
142
toolkit/mozapps/update/common/updatedefines.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/* 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 UPDATEDEFINES_H
|
||||
#define UPDATEDEFINES_H
|
||||
|
||||
#include "readstrings.h"
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
# ifdef PATH_MAX
|
||||
# define MAXPATHLEN PATH_MAX
|
||||
# elif defined(MAX_PATH)
|
||||
# define MAXPATHLEN MAX_PATH
|
||||
# elif defined(_MAX_PATH)
|
||||
# define MAXPATHLEN _MAX_PATH
|
||||
# elif defined(CCHMAXPATH)
|
||||
# define MAXPATHLEN CCHMAXPATH
|
||||
# else
|
||||
# define MAXPATHLEN 1024
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN)
|
||||
# include <windows.h>
|
||||
# include <shlwapi.h>
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
# include <stdio.h>
|
||||
# include <stdarg.h>
|
||||
|
||||
# ifndef F_OK
|
||||
# define F_OK 00
|
||||
# endif
|
||||
# ifndef W_OK
|
||||
# define W_OK 02
|
||||
# endif
|
||||
# ifndef R_OK
|
||||
# define R_OK 04
|
||||
# endif
|
||||
# define S_ISDIR(s) (((s) & _S_IFMT) == _S_IFDIR)
|
||||
# define S_ISREG(s) (((s) & _S_IFMT) == _S_IFREG)
|
||||
|
||||
# define access _access
|
||||
|
||||
# define putenv _putenv
|
||||
# if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
# define stat _stat
|
||||
# endif
|
||||
# define DELETE_DIR L"tobedeleted"
|
||||
# define CALLBACK_BACKUP_EXT L".moz-callback"
|
||||
|
||||
# define LOG_S "%S"
|
||||
# define NS_T(str) L ## str
|
||||
# define NS_SLASH NS_T('\\')
|
||||
|
||||
static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt, ...)
|
||||
{
|
||||
size_t _count = count - 1;
|
||||
va_list varargs;
|
||||
va_start(varargs, fmt);
|
||||
int result = _vsnwprintf(dest, count - 1, fmt, varargs);
|
||||
va_end(varargs);
|
||||
dest[_count] = L'\0';
|
||||
return result;
|
||||
}
|
||||
#define NS_tsnprintf mywcsprintf
|
||||
# define NS_taccess _waccess
|
||||
# define NS_tchdir _wchdir
|
||||
# define NS_tchmod _wchmod
|
||||
# define NS_tfopen _wfopen
|
||||
# define NS_tmkdir(path, perms) _wmkdir(path)
|
||||
# define NS_tremove _wremove
|
||||
// _wrename is used to avoid the link tracking service.
|
||||
# define NS_trename _wrename
|
||||
# define NS_trmdir _wrmdir
|
||||
# define NS_tstat _wstat
|
||||
# define NS_tlstat _wstat // No symlinks on Windows
|
||||
# define NS_tstat_t _stat
|
||||
# define NS_tstrcat wcscat
|
||||
# define NS_tstrcmp wcscmp
|
||||
# define NS_tstricmp wcsicmp
|
||||
# define NS_tstrcpy wcscpy
|
||||
# define NS_tstrncpy wcsncpy
|
||||
# define NS_tstrlen wcslen
|
||||
# define NS_tstrchr wcschr
|
||||
# define NS_tstrrchr wcsrchr
|
||||
# define NS_tstrstr wcsstr
|
||||
# include "win_dirent.h"
|
||||
# define NS_tDIR DIR
|
||||
# define NS_tdirent dirent
|
||||
# define NS_topendir opendir
|
||||
# define NS_tclosedir closedir
|
||||
# define NS_treaddir readdir
|
||||
#else
|
||||
# include <sys/wait.h>
|
||||
# include <unistd.h>
|
||||
|
||||
#ifdef SOLARIS
|
||||
# include <sys/stat.h>
|
||||
#else
|
||||
# include <fts.h>
|
||||
#endif
|
||||
# include <dirent.h>
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
# define LOG_S "%s"
|
||||
# define NS_T(str) str
|
||||
# define NS_SLASH NS_T('/')
|
||||
# define NS_tsnprintf snprintf
|
||||
# define NS_taccess access
|
||||
# define NS_tchdir chdir
|
||||
# define NS_tchmod chmod
|
||||
# define NS_tfopen fopen
|
||||
# define NS_tmkdir mkdir
|
||||
# define NS_tremove remove
|
||||
# define NS_trename rename
|
||||
# define NS_trmdir rmdir
|
||||
# define NS_tstat stat
|
||||
# define NS_tstat_t stat
|
||||
# define NS_tlstat lstat
|
||||
# define NS_tstrcat strcat
|
||||
# define NS_tstrcmp strcmp
|
||||
# define NS_tstricmp strcasecmp
|
||||
# define NS_tstrcpy strcpy
|
||||
# define NS_tstrncpy strncpy
|
||||
# define NS_tstrlen strlen
|
||||
# define NS_tstrrchr strrchr
|
||||
# define NS_tstrstr strstr
|
||||
# define NS_tDIR DIR
|
||||
# define NS_tdirent dirent
|
||||
# define NS_topendir opendir
|
||||
# define NS_tclosedir closedir
|
||||
# define NS_treaddir readdir
|
||||
#endif
|
||||
|
||||
#define BACKUP_EXT NS_T(".moz-backup")
|
||||
|
||||
#endif
|
||||
609
toolkit/mozapps/update/common/updatehelper.cpp
Normal file
609
toolkit/mozapps/update/common/updatehelper.cpp
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
/* 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>
|
||||
|
||||
// Needed for CreateToolhelp32Snapshot
|
||||
#include <tlhelp32.h>
|
||||
#ifndef ONLY_SERVICE_LAUNCHING
|
||||
|
||||
#include <stdio.h>
|
||||
#include "shlobj.h"
|
||||
#include "updatehelper.h"
|
||||
#include "uachelper.h"
|
||||
#include "pathhash.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
// Needed for PathAppendW
|
||||
#include <shlwapi.h>
|
||||
|
||||
using mozilla::MakeUnique;
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
|
||||
BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,
|
||||
LPCWSTR siblingFilePath,
|
||||
LPCWSTR newFileName);
|
||||
|
||||
/**
|
||||
* Obtains the path of a file in the same directory as the specified file.
|
||||
*
|
||||
* @param destinationBuffer A buffer of size MAX_PATH + 1 to store the result.
|
||||
* @param siblingFIlePath The path of another file in the same directory
|
||||
* @param newFileName The filename of another file in the same directory
|
||||
* @return TRUE if successful
|
||||
*/
|
||||
BOOL
|
||||
PathGetSiblingFilePath(LPWSTR destinationBuffer,
|
||||
LPCWSTR siblingFilePath,
|
||||
LPCWSTR newFileName)
|
||||
{
|
||||
if (wcslen(siblingFilePath) >= MAX_PATH) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wcsncpy(destinationBuffer, siblingFilePath, MAX_PATH);
|
||||
if (!PathRemoveFileSpecW(destinationBuffer)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return PathAppendSafe(destinationBuffer, newFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the upgrade process for update of the service if it is
|
||||
* already installed.
|
||||
*
|
||||
* @param installDir the installation directory where
|
||||
* maintenanceservice_installer.exe is located.
|
||||
* @return TRUE if successful
|
||||
*/
|
||||
BOOL
|
||||
StartServiceUpdate(LPCWSTR installDir)
|
||||
{
|
||||
// Get a handle to the local computer SCM database
|
||||
SC_HANDLE manager = OpenSCManager(nullptr, nullptr,
|
||||
SC_MANAGER_ALL_ACCESS);
|
||||
if (!manager) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Open the service
|
||||
SC_HANDLE svc = OpenServiceW(manager, SVC_NAME,
|
||||
SERVICE_ALL_ACCESS);
|
||||
if (!svc) {
|
||||
CloseServiceHandle(manager);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If we reach here, then the service is installed, so
|
||||
// proceed with upgrading it.
|
||||
|
||||
CloseServiceHandle(manager);
|
||||
|
||||
// The service exists and we opened it, get the config bytes needed
|
||||
DWORD bytesNeeded;
|
||||
if (!QueryServiceConfigW(svc, nullptr, 0, &bytesNeeded) &&
|
||||
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
CloseServiceHandle(svc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get the service config information, in particular we want the binary
|
||||
// path of the service.
|
||||
UniquePtr<char[]> serviceConfigBuffer = MakeUnique<char[]>(bytesNeeded);
|
||||
if (!QueryServiceConfigW(svc,
|
||||
reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()),
|
||||
bytesNeeded, &bytesNeeded)) {
|
||||
CloseServiceHandle(svc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CloseServiceHandle(svc);
|
||||
|
||||
QUERY_SERVICE_CONFIGW &serviceConfig =
|
||||
*reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());
|
||||
|
||||
PathUnquoteSpacesW(serviceConfig.lpBinaryPathName);
|
||||
|
||||
// Obtain the temp path of the maintenance service binary
|
||||
WCHAR tmpService[MAX_PATH + 1] = { L'\0' };
|
||||
if (!PathGetSiblingFilePath(tmpService, serviceConfig.lpBinaryPathName,
|
||||
L"maintenanceservice_tmp.exe")) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get the new maintenance service path from the install dir
|
||||
WCHAR newMaintServicePath[MAX_PATH + 1] = { L'\0' };
|
||||
wcsncpy(newMaintServicePath, installDir, MAX_PATH);
|
||||
PathAppendSafe(newMaintServicePath,
|
||||
L"maintenanceservice.exe");
|
||||
|
||||
// Copy the temp file in alongside the maintenace service.
|
||||
// This is a requirement for maintenance service upgrades.
|
||||
if (!CopyFileW(newMaintServicePath, tmpService, FALSE)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Start the upgrade comparison process
|
||||
STARTUPINFOW si = {0};
|
||||
si.cb = sizeof(STARTUPINFOW);
|
||||
// No particular desktop because no UI
|
||||
si.lpDesktop = L"";
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
WCHAR cmdLine[64] = { '\0' };
|
||||
wcsncpy(cmdLine, L"dummyparam.exe upgrade",
|
||||
sizeof(cmdLine) / sizeof(cmdLine[0]) - 1);
|
||||
BOOL svcUpdateProcessStarted = CreateProcessW(tmpService,
|
||||
cmdLine,
|
||||
nullptr, nullptr, FALSE,
|
||||
0,
|
||||
nullptr, installDir, &si, &pi);
|
||||
if (svcUpdateProcessStarted) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
return svcUpdateProcessStarted;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Executes a maintenance service command
|
||||
*
|
||||
* @param argc The total number of arguments in argv
|
||||
* @param argv An array of null terminated strings to pass to the service,
|
||||
* @return ERROR_SUCCESS if the service command was started.
|
||||
* Less than 16000, a windows system error code from StartServiceW
|
||||
* More than 20000, 20000 + the last state of the service constant if
|
||||
* the last state is something other than stopped.
|
||||
* 17001 if the SCM could not be opened
|
||||
* 17002 if the service could not be opened
|
||||
*/
|
||||
DWORD
|
||||
StartServiceCommand(int argc, LPCWSTR* argv)
|
||||
{
|
||||
DWORD lastState = WaitForServiceStop(SVC_NAME, 5);
|
||||
if (lastState != SERVICE_STOPPED) {
|
||||
return 20000 + lastState;
|
||||
}
|
||||
|
||||
// Get a handle to the SCM database.
|
||||
SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr,
|
||||
SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_ENUMERATE_SERVICE);
|
||||
if (!serviceManager) {
|
||||
return 17001;
|
||||
}
|
||||
|
||||
// Get a handle to the service.
|
||||
SC_HANDLE service = OpenServiceW(serviceManager,
|
||||
SVC_NAME,
|
||||
SERVICE_START);
|
||||
if (!service) {
|
||||
CloseServiceHandle(serviceManager);
|
||||
return 17002;
|
||||
}
|
||||
|
||||
// Wait at most 5 seconds trying to start the service in case of errors
|
||||
// like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT.
|
||||
const DWORD maxWaitMS = 5000;
|
||||
DWORD currentWaitMS = 0;
|
||||
DWORD lastError = ERROR_SUCCESS;
|
||||
while (currentWaitMS < maxWaitMS) {
|
||||
BOOL result = StartServiceW(service, argc, argv);
|
||||
if (result) {
|
||||
lastError = ERROR_SUCCESS;
|
||||
break;
|
||||
} else {
|
||||
lastError = GetLastError();
|
||||
}
|
||||
Sleep(100);
|
||||
currentWaitMS += 100;
|
||||
}
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return lastError;
|
||||
}
|
||||
|
||||
#ifndef ONLY_SERVICE_LAUNCHING
|
||||
|
||||
/**
|
||||
* Launch a service initiated action for a software update with the
|
||||
* specified arguments.
|
||||
*
|
||||
* @param exePath The path of the executable to run
|
||||
* @param argc The total number of arguments in argv
|
||||
* @param argv An array of null terminated strings to pass to the exePath,
|
||||
* argv[0] must be the path to the updater.exe
|
||||
* @return ERROR_SUCCESS if successful
|
||||
*/
|
||||
DWORD
|
||||
LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv)
|
||||
{
|
||||
// The service command is the same as the updater.exe command line except
|
||||
// it has 2 extra args: 1) The Path to udpater.exe, and 2) the command
|
||||
// being executed which is "software-update"
|
||||
LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2];
|
||||
updaterServiceArgv[0] = L"MozillaMaintenance";
|
||||
updaterServiceArgv[1] = L"software-update";
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
updaterServiceArgv[i + 2] = argv[i];
|
||||
}
|
||||
|
||||
// Execute the service command by starting the service with
|
||||
// the passed in arguments.
|
||||
DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv);
|
||||
delete[] updaterServiceArgv;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins a base directory path with a filename.
|
||||
*
|
||||
* @param base The base directory path of size MAX_PATH + 1
|
||||
* @param extra The filename to append
|
||||
* @return TRUE if the file name was successful appended to base
|
||||
*/
|
||||
BOOL
|
||||
PathAppendSafe(LPWSTR base, LPCWSTR extra)
|
||||
{
|
||||
if (wcslen(base) + wcslen(extra) >= MAX_PATH) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return PathAppendW(base, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets update.status to a specific failure code
|
||||
*
|
||||
* @param updateDirPath The path of the update directory
|
||||
* @return TRUE if successful
|
||||
*/
|
||||
BOOL
|
||||
WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
|
||||
{
|
||||
// The temp file is not removed on failure since there is client code that
|
||||
// will remove it.
|
||||
WCHAR tmpUpdateStatusFilePath[MAX_PATH + 1] = { L'\0' };
|
||||
if (GetTempFileNameW(updateDirPath, L"svc", 0, tmpUpdateStatusFilePath) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HANDLE tmpStatusFile = CreateFileW(tmpUpdateStatusFilePath, GENERIC_WRITE, 0,
|
||||
nullptr, CREATE_ALWAYS, 0, nullptr);
|
||||
if (tmpStatusFile == INVALID_HANDLE_VALUE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char failure[32];
|
||||
sprintf(failure, "failed: %d", errorCode);
|
||||
DWORD toWrite = strlen(failure);
|
||||
DWORD wrote;
|
||||
BOOL ok = WriteFile(tmpStatusFile, failure,
|
||||
toWrite, &wrote, nullptr);
|
||||
CloseHandle(tmpStatusFile);
|
||||
|
||||
if (!ok || wrote != toWrite) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' };
|
||||
wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
|
||||
if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (MoveFileExW(tmpUpdateStatusFilePath, updateStatusFilePath,
|
||||
MOVEFILE_REPLACE_EXISTING) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Waits for a service to enter a stopped state.
|
||||
* This function does not stop the service, it just blocks until the service
|
||||
* is stopped.
|
||||
*
|
||||
* @param serviceName The service to wait for.
|
||||
* @param maxWaitSeconds The maximum number of seconds to wait
|
||||
* @return state of the service after a timeout or when stopped.
|
||||
* A value of 255 is returned for an error. Typical values are:
|
||||
* SERVICE_STOPPED 0x00000001
|
||||
* SERVICE_START_PENDING 0x00000002
|
||||
* SERVICE_STOP_PENDING 0x00000003
|
||||
* SERVICE_RUNNING 0x00000004
|
||||
* SERVICE_CONTINUE_PENDING 0x00000005
|
||||
* SERVICE_PAUSE_PENDING 0x00000006
|
||||
* SERVICE_PAUSED 0x00000007
|
||||
* last status not set 0x000000CF
|
||||
* Could no query status 0x000000DF
|
||||
* Could not open service, access denied 0x000000EB
|
||||
* Could not open service, invalid handle 0x000000EC
|
||||
* Could not open service, invalid name 0x000000ED
|
||||
* Could not open service, does not exist 0x000000EE
|
||||
* Could not open service, other error 0x000000EF
|
||||
* Could not open SCM, access denied 0x000000FD
|
||||
* Could not open SCM, database does not exist 0x000000FE;
|
||||
* Could not open SCM, other error 0x000000FF;
|
||||
* Note: The strange choice of error codes above SERVICE_PAUSED are chosen
|
||||
* in case Windows comes out with other service stats higher than 7, they
|
||||
* would likely call it 8 and above. JS code that uses this in TestAUSHelper
|
||||
* only handles values up to 255 so that's why we don't use GetLastError
|
||||
* directly.
|
||||
*/
|
||||
DWORD
|
||||
WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
||||
{
|
||||
// 0x000000CF is defined above to be not set
|
||||
DWORD lastServiceState = 0x000000CF;
|
||||
|
||||
// Get a handle to the SCM database.
|
||||
SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr,
|
||||
SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_ENUMERATE_SERVICE);
|
||||
if (!serviceManager) {
|
||||
DWORD lastError = GetLastError();
|
||||
switch(lastError) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return 0x000000FD;
|
||||
case ERROR_DATABASE_DOES_NOT_EXIST:
|
||||
return 0x000000FE;
|
||||
default:
|
||||
return 0x000000FF;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a handle to the service.
|
||||
SC_HANDLE service = OpenServiceW(serviceManager,
|
||||
serviceName,
|
||||
SERVICE_QUERY_STATUS);
|
||||
if (!service) {
|
||||
DWORD lastError = GetLastError();
|
||||
CloseServiceHandle(serviceManager);
|
||||
switch(lastError) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return 0x000000EB;
|
||||
case ERROR_INVALID_HANDLE:
|
||||
return 0x000000EC;
|
||||
case ERROR_INVALID_NAME:
|
||||
return 0x000000ED;
|
||||
case ERROR_SERVICE_DOES_NOT_EXIST:
|
||||
return 0x000000EE;
|
||||
default:
|
||||
return 0x000000EF;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD currentWaitMS = 0;
|
||||
SERVICE_STATUS_PROCESS ssp;
|
||||
ssp.dwCurrentState = lastServiceState;
|
||||
while (currentWaitMS < maxWaitSeconds * 1000) {
|
||||
DWORD bytesNeeded;
|
||||
if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
|
||||
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
|
||||
DWORD lastError = GetLastError();
|
||||
switch (lastError) {
|
||||
case ERROR_INVALID_HANDLE:
|
||||
ssp.dwCurrentState = 0x000000D9;
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
ssp.dwCurrentState = 0x000000DA;
|
||||
break;
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
ssp.dwCurrentState = 0x000000DB;
|
||||
break;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
ssp.dwCurrentState = 0x000000DC;
|
||||
break;
|
||||
case ERROR_INVALID_LEVEL:
|
||||
ssp.dwCurrentState = 0x000000DD;
|
||||
break;
|
||||
case ERROR_SHUTDOWN_IN_PROGRESS:
|
||||
ssp.dwCurrentState = 0x000000DE;
|
||||
break;
|
||||
// These 3 errors can occur when the service is not yet stopped but
|
||||
// it is stopping.
|
||||
case ERROR_INVALID_SERVICE_CONTROL:
|
||||
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
|
||||
case ERROR_SERVICE_NOT_ACTIVE:
|
||||
currentWaitMS += 50;
|
||||
Sleep(50);
|
||||
continue;
|
||||
default:
|
||||
ssp.dwCurrentState = 0x000000DF;
|
||||
}
|
||||
|
||||
// We couldn't query the status so just break out
|
||||
break;
|
||||
}
|
||||
|
||||
// The service is already in use.
|
||||
if (ssp.dwCurrentState == SERVICE_STOPPED) {
|
||||
break;
|
||||
}
|
||||
currentWaitMS += 50;
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
lastServiceState = ssp.dwCurrentState;
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return lastServiceState;
|
||||
}
|
||||
|
||||
#ifndef ONLY_SERVICE_LAUNCHING
|
||||
|
||||
/**
|
||||
* Determines if there is at least one process running for the specified
|
||||
* application. A match will be found across any session for any user.
|
||||
*
|
||||
* @param process The process to check for existance
|
||||
* @return ERROR_NOT_FOUND if the process was not found
|
||||
* ERROR_SUCCESS if the process was found and there were no errors
|
||||
* Other Win32 system error code for other errors
|
||||
**/
|
||||
DWORD
|
||||
IsProcessRunning(LPCWSTR filename)
|
||||
{
|
||||
// Take a snapshot of all processes in the system.
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (INVALID_HANDLE_VALUE == snapshot) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
PROCESSENTRY32W processEntry;
|
||||
processEntry.dwSize = sizeof(PROCESSENTRY32W);
|
||||
if (!Process32FirstW(snapshot, &processEntry)) {
|
||||
DWORD lastError = GetLastError();
|
||||
CloseHandle(snapshot);
|
||||
return lastError;
|
||||
}
|
||||
|
||||
do {
|
||||
if (wcsicmp(filename, processEntry.szExeFile) == 0) {
|
||||
CloseHandle(snapshot);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
} while (Process32NextW(snapshot, &processEntry));
|
||||
CloseHandle(snapshot);
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the specified applicaiton to exit.
|
||||
*
|
||||
* @param filename The application to wait for.
|
||||
* @param maxSeconds The maximum amount of seconds to wait for all
|
||||
* instances of the application to exit.
|
||||
* @return ERROR_SUCCESS if no instances of the application exist
|
||||
* WAIT_TIMEOUT if the process is still running after maxSeconds.
|
||||
* Any other Win32 system error code.
|
||||
*/
|
||||
DWORD
|
||||
WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
|
||||
{
|
||||
DWORD applicationRunningError = WAIT_TIMEOUT;
|
||||
for(DWORD i = 0; i < maxSeconds; i++) {
|
||||
DWORD applicationRunningError = IsProcessRunning(filename);
|
||||
if (ERROR_NOT_FOUND == applicationRunningError) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
if (ERROR_SUCCESS == applicationRunningError) {
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
return applicationRunningError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the fallback key exists or not
|
||||
*
|
||||
* @return TRUE if the fallback key exists and there was no error checking
|
||||
*/
|
||||
BOOL
|
||||
DoesFallbackKeyExist()
|
||||
{
|
||||
HKEY testOnlyFallbackKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
TEST_ONLY_FALLBACK_KEY_PATH, 0,
|
||||
KEY_READ | KEY_WOW64_64KEY,
|
||||
&testOnlyFallbackKey) != ERROR_SUCCESS) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
RegCloseKey(testOnlyFallbackKey);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Determines if the file system for the specified file handle is local
|
||||
* @param file path to check the filesystem type for, must be at most MAX_PATH
|
||||
* @param isLocal out parameter which will hold TRUE if the drive is local
|
||||
* @return TRUE if the call succeeded
|
||||
*/
|
||||
BOOL
|
||||
IsLocalFile(LPCWSTR file, BOOL &isLocal)
|
||||
{
|
||||
WCHAR rootPath[MAX_PATH + 1] = { L'\0' };
|
||||
if (wcslen(file) > MAX_PATH) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wcsncpy(rootPath, file, MAX_PATH);
|
||||
PathStripToRootW(rootPath);
|
||||
isLocal = GetDriveTypeW(rootPath) == DRIVE_FIXED;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the DWORD value of a registry key value
|
||||
*
|
||||
* @param key The base key to where the value name exists
|
||||
* @param valueName The name of the value
|
||||
* @param retValue Out parameter which will hold the value
|
||||
* @return TRUE on success
|
||||
*/
|
||||
static BOOL
|
||||
GetDWORDValue(HKEY key, LPCWSTR valueName, DWORD &retValue)
|
||||
{
|
||||
DWORD regDWORDValueSize = sizeof(DWORD);
|
||||
LONG retCode = RegQueryValueExW(key, valueName, 0, nullptr,
|
||||
reinterpret_cast<LPBYTE>(&retValue),
|
||||
®DWORDValueSize);
|
||||
return ERROR_SUCCESS == retCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the the system's elevation type allows
|
||||
* unprmopted elevation.
|
||||
*
|
||||
* @param isUnpromptedElevation Out parameter which specifies if unprompted
|
||||
* elevation is allowed.
|
||||
* @return TRUE if the user can actually elevate and the value was obtained
|
||||
* successfully.
|
||||
*/
|
||||
BOOL
|
||||
IsUnpromptedElevation(BOOL &isUnpromptedElevation)
|
||||
{
|
||||
if (!UACHelper::CanUserElevate()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LPCWSTR UACBaseRegKey =
|
||||
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
|
||||
HKEY baseKey;
|
||||
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
UACBaseRegKey, 0,
|
||||
KEY_READ, &baseKey);
|
||||
if (retCode != ERROR_SUCCESS) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD consent, secureDesktop;
|
||||
BOOL success = GetDWORDValue(baseKey, L"ConsentPromptBehaviorAdmin",
|
||||
consent);
|
||||
success = success &&
|
||||
GetDWORDValue(baseKey, L"PromptOnSecureDesktop", secureDesktop);
|
||||
isUnpromptedElevation = !consent && !secureDesktop;
|
||||
|
||||
RegCloseKey(baseKey);
|
||||
return success;
|
||||
}
|
||||
29
toolkit/mozapps/update/common/updatehelper.h
Normal file
29
toolkit/mozapps/update/common/updatehelper.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* 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 StartServiceUpdate(LPCWSTR installDir);
|
||||
DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv);
|
||||
BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
|
||||
DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
|
||||
DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
|
||||
DWORD IsProcessRunning(LPCWSTR filename);
|
||||
BOOL DoesFallbackKeyExist();
|
||||
BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal);
|
||||
DWORD StartServiceCommand(int argc, LPCWSTR* argv);
|
||||
BOOL IsUnpromptedElevation(BOOL &isUnpromptedElevation);
|
||||
|
||||
#define SVC_NAME L"MozillaMaintenance"
|
||||
|
||||
#define BASE_SERVICE_REG_KEY \
|
||||
L"SOFTWARE\\Mozilla\\MaintenanceService"
|
||||
|
||||
// The test only fallback key, as its name implies, is only present on machines
|
||||
// that will use automated tests. Since automated tests always run from a
|
||||
// different directory for each test, the presence of this key bypasses the
|
||||
// "This is a valid installation directory" check. This key also stores
|
||||
// the allowed name and issuer for cert checks so that the cert check
|
||||
// code can still be run unchanged.
|
||||
#define TEST_ONLY_FALLBACK_KEY_PATH \
|
||||
BASE_SERVICE_REG_KEY L"\\3932ecacee736d366d6436db0f55bce4"
|
||||
|
||||
32
toolkit/mozapps/update/common/win_dirent.h
Normal file
32
toolkit/mozapps/update/common/win_dirent.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 WINDIRENT_H__
|
||||
#define WINDIRENT_H__
|
||||
|
||||
#ifndef XP_WIN
|
||||
#error This library should only be used on Windows
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct DIR {
|
||||
explicit DIR(const WCHAR* path);
|
||||
~DIR();
|
||||
HANDLE findHandle;
|
||||
WCHAR name[MAX_PATH];
|
||||
};
|
||||
|
||||
struct dirent {
|
||||
dirent();
|
||||
WCHAR d_name[MAX_PATH];
|
||||
};
|
||||
|
||||
DIR* opendir(const WCHAR* path);
|
||||
int closedir(DIR* dir);
|
||||
dirent* readdir(DIR* dir);
|
||||
|
||||
#endif // WINDIRENT_H__
|
||||
Loading…
Add table
Add a link
Reference in a new issue