import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View 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;
}

View 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

View 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__

View 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')

View 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;
}

View 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

View 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;
}

View 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__

View 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;
}

View 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

View 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])

View 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;
}

View 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

View 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;
}

View 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

View 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

View 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),
&regDWORDValueSize);
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;
}

View 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"

View 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__