Bug 973933 - New updater-xpcshell binary for updater tests. r=rstrong

This is a rollup of recent work. Changes include:
- Creating an xpcshell only updater binary. This binary has an embedded xpcshell only cert for verifying test only mars.  It is only used by tests and is not signed w/ authenticode certs.
- Modifying tests to use that new binary
- Adding a check-cert option to the maintenance service
- Using that new cert-check option in new tests to test the authenticode path
- No longer doing an authenticode check during service updater tests on the xpcshell binary.
- Enables more tests for other platforms
This commit is contained in:
Brian R. Bondy 2015-04-06 12:03:18 -04:00
parent 42b12cf6ef
commit dfcd0a5105
17 changed files with 325 additions and 144 deletions

View File

@ -14,6 +14,7 @@
#include "workmonitor.h"
#include "uachelper.h"
#include "updatehelper.h"
#include "registrycertificates.h"
// Link w/ subsystem window so we don't get a console when executing
// this binary through the installer.
@ -102,6 +103,10 @@ wmain(int argc, WCHAR **argv)
return 0;
}
if (!lstrcmpi(argv[1], L"check-cert") && argc > 2) {
return DoesBinaryMatchAllowedCertificates(argv[2], argv[3], FALSE) ? 0 : 1;
}
SERVICE_TABLE_ENTRYW DispatchTable[] = {
{ SVC_NAME, (LPSERVICE_MAIN_FUNCTIONW) SvcMain },
{ nullptr, nullptr }

View File

@ -17,10 +17,17 @@
* 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)
DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath,
BOOL allowFallbackKeySkip)
{
WCHAR maintenanceServiceKey[MAX_PATH + 1];
if (!CalculateRegistryPathFromFilePath(basePathForUpdate,
@ -49,6 +56,11 @@ DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath)
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."));
return TRUE;
}
}
nsAutoRegKey baseKey(baseKeyRaw);

View File

@ -8,6 +8,7 @@
#include "certificatecheck.h"
BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate,
LPCWSTR filePath);
LPCWSTR filePath,
BOOL allowFallbackKeySkip = TRUE);
#endif

View File

@ -1162,12 +1162,15 @@ function getTestDirPath() {
* The relative path to the file or directory to get from the root of
* the test's data directory. If not specified the test's data
* directory will be returned.
* @param aAllowNonExists (optional)
* Whether or not to throw an error if the path exists.
* If not specified, then false is used.
* @return The nsIFile for the file in the test data directory.
* @throws If the file or directory does not exist.
*/
function getTestDirFile(aRelPath) {
function getTestDirFile(aRelPath, aAllowNonExists) {
let relpath = getTestDirPath() + (aRelPath ? aRelPath : "");
return do_get_file(relpath, false);
return do_get_file(relpath, !!aAllowNonExists);
}
function getSpecialFolderDir(aCSIDL) {
@ -1426,11 +1429,12 @@ function unlockDirectory(aDir) {
function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) {
// Copy the updater binary to the updates directory.
let binDir = gGREBinDirOrig.clone();
let updater = binDir.clone();
updater.append("updater.app");
let updater = getTestDirFile("updater.app", true);
if (!updater.exists()) {
updater = binDir.clone();
updater.append(FILE_UPDATER_BIN);
updater = getTestDirFile(FILE_UPDATER_BIN);
if (!updater.exists()) {
do_throw("Unable to find updater binary!");
}
}
Assert.ok(updater.exists(), "updater or updater.app should exist");
@ -1688,8 +1692,6 @@ function setupAppFiles() {
// dependentlibs.list file.
let appFiles = [ { relPath : FILE_APP_BIN,
inGreDir : false },
{ relPath : FILE_UPDATER_BIN,
inGreDir : false },
{ relPath : FILE_APPLICATION_INI,
inGreDir : true },
{ relPath : "dependentlibs.list",
@ -1724,6 +1726,17 @@ function setupAppFiles() {
copyFileToTestAppDir(aAppFile.relPath, aAppFile.inGreDir);
});
// Copy the xpcshell updater binary
let updater = getTestDirFile("updater.app", true);
if (!updater.exists()) {
updater = getTestDirFile(FILE_UPDATER_BIN);
if (!updater.exists()) {
do_throw("Unable to find updater binary!");
}
}
let testBinDir = getGREBinDir()
updater.copyToFollowingLinks(testBinDir, updater.leafName);
debugDump("finish - copying or creating symlinks to application files " +
"for the test");
}
@ -2015,10 +2028,13 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus, aCheckSvcLog) {
setEnvironment();
// There is a security check done by the service to make sure the updater
// we are executing is the same as the one in the apply-to dir.
// To make sure they match from tests we copy updater.exe to the apply-to dir.
copyFileToTestAppDir(FILE_UPDATER_BIN, false);
let updater = getTestDirFile(FILE_UPDATER_BIN);
if (!updater.exists()) {
do_throw("Unable to find updater binary!");
}
let testBinDir = getGREBinDir()
updater.copyToFollowingLinks(testBinDir, updater.leafName);
updater.copyToFollowingLinks(updatesDir, updater.leafName);
// The service will execute maintenanceservice_installer.exe and
// will copy maintenanceservice.exe out of the same directory from

View File

@ -19,9 +19,9 @@ skip-if = toolkit == 'gonk'
reason = bug 820380
[marStageSuccessPartial.js]
[marVersionDowngrade.js]
skip-if = os != 'win'
skip-if = os != 'win' && os != 'mac' && os != 'linux'
[marWrongChannel.js]
skip-if = os != 'win'
skip-if = os != 'win' && os != 'mac' && os != 'linux'
[marStageFailurePartial.js]
[marCallbackAppSuccessComplete_win.js]
skip-if = os != 'win'

View File

@ -0,0 +1,40 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* We skip authenticode cert checks from the service udpates
* so that we can use updater-xpcshell with the wrong certs for testing.
* This tests that code path. */
function run_test() {
if (!IS_AUTHENTICODE_CHECK_ENABLED) {
return;
}
let binDir = getGREBinDir();
let maintenanceServiceBin = binDir.clone();
maintenanceServiceBin.append(FILE_MAINTENANCE_SERVICE_BIN);
let updaterBin = binDir.clone();
updaterBin.append(FILE_UPDATER_BIN);
logTestInfo("Launching maintenance service bin: " +
maintenanceServiceBin.path + " to check updater: " +
updaterBin.path + " signature.");
// Bypass the manifest and run as invoker
let env = Cc["@mozilla.org/process/environment;1"].
getService(Ci.nsIEnvironment);
env.set("__COMPAT_LAYER", "RunAsInvoker");
let dummyInstallPath = "---";
let maintenanceServiceBinArgs = ["check-cert", dummyInstallPath,
updaterBin.path];
let maintenanceServiceBinProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
maintenanceServiceBinProcess.init(maintenanceServiceBin);
maintenanceServiceBinProcess.run(true, maintenanceServiceBinArgs,
maintenanceServiceBinArgs.length);
do_check_eq(maintenanceServiceBinProcess.exitValue, 0);
}

View File

@ -80,3 +80,4 @@ run-sequentially = Uses the Mozilla Maintenance Service.
run-sequentially = Uses the Mozilla Maintenance Service.
[marAppApplyUpdateStageSuccessSvc.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[checkUpdaterSigSvc.js]

View File

@ -3,6 +3,8 @@
# 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/.
# For changes here, also consider ./updater-xpcshell/Makefile.in
ifndef MOZ_WINCONSOLE
ifdef MOZ_DEBUG
MOZ_WINCONSOLE = 1
@ -20,17 +22,22 @@ else ifneq (,$(filter nightly aurora nightly-elm nightly-profiling nightly-oak n
PRIMARY_CERT = nightly_aurora_level3_primary.der
SECONDARY_CERT = nightly_aurora_level3_secondary.der
else
PRIMARY_CERT = xpcshellCertificate.der
SECONDARY_CERT = xpcshellCertificate.der
PRIMARY_CERT = dep1.der
SECONDARY_CERT = dep2.der
endif
CERT_HEADERS := primaryCert.h secondaryCert.h
CERT_HEADERS := primaryCert.h secondaryCert.h xpcshellCert.h
export:: $(CERT_HEADERS)
primaryCert.h: $(PRIMARY_CERT)
secondaryCert.h: $(SECONDARY_CERT)
# This is how the xpcshellCertificate.der file is generated, in case we ever
# have to regenerate it.
# ./certutil -L -d modules/libmar/tests/unit/data -n mycert -r > xpcshellCertificate.der
xpcshellCert.h: xpcshellCertificate.der
$(CERT_HEADERS): gen_cert_header.py
$(PYTHON) $< $(@:.h=Data) $(filter-out $<,$^) > $@

View File

@ -17,8 +17,14 @@
// These are generated at compile time based on the DER file for the channel
// being used
#ifdef MOZ_VERIFY_MAR_SIGNATURE
#ifdef UPDATER_XPCSHELL_CERT
#include "../xpcshellCert.h"
#else
#include "primaryCert.h"
#include "secondaryCert.h"
#endif
#endif
#define UPDATER_NO_STRING_GLUE_STL
#include "nsVersionComparator.cpp"
@ -76,10 +82,17 @@ ArchiveReader::VerifySignature()
return ARCHIVE_NOT_OPEN;
}
#ifdef MOZ_VERIFY_MAR_SIGNATURE
#ifdef UPDATER_XPCSHELL_CERT
int rv = VerifyLoadedCert(mArchive, xpcshellCertData);
#else
int rv = VerifyLoadedCert(mArchive, primaryCertData);
if (rv != OK) {
rv = VerifyLoadedCert(mArchive, secondaryCertData);
}
#endif
#endif
return rv;
}

Binary file not shown.

Binary file not shown.

View File

@ -6,125 +6,7 @@
Program('updater')
SOURCES += [
'archivereader.cpp',
'bspatch.cpp',
'updater.cpp',
]
have_progressui = 0
if CONFIG['MOZ_VERIFY_MAR_SIGNATURE']:
USE_LIBS += [
'verifymar',
]
if CONFIG['OS_ARCH'] == 'WINNT':
have_progressui = 1
SOURCES += [
'loaddlls.cpp',
'progressui_win.cpp',
'win_dirent.cpp',
]
RCINCLUDE = 'updater.rc'
DEFINES['UNICODE'] = True
DEFINES['_UNICODE'] = True
DEFINES['NOMINMAX'] = True
USE_STATIC_LIBS = True
# Pick up nsWindowsRestart.cpp
LOCAL_INCLUDES += [
'/toolkit/xre',
]
USE_LIBS += [
'updatecommon-standalone',
]
OS_LIBS += [
'comctl32',
'ws2_32',
'shell32',
'shlwapi',
'crypt32',
'advapi32',
]
elif CONFIG['OS_ARCH'] == 'Linux':
USE_LIBS += [
'updatecommon',
'/modules/libmar/sign/signmar',
'/security/nss/lib/nss/nss3',
'/security/nss/lib/util/nssutil3',
]
OS_LIBS += CONFIG['NSPR_LIBS']
else:
USE_LIBS += [
'updatecommon',
]
USE_LIBS += [
'mar',
]
if CONFIG['MOZ_NATIVE_BZ2']:
OS_LIBS += CONFIG['MOZ_BZ2_LIBS']
else:
USE_LIBS += [
'bz2',
]
if CONFIG['MOZ_ENABLE_GTK']:
have_progressui = 1
SOURCES += [
'progressui_gtk.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
have_progressui = 1
SOURCES += [
'launchchild_osx.mm',
'progressui_osx.mm',
]
OS_LIBS += ['-framework Cocoa -framework Security']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
have_progressui = 1
SOURCES += [
'automounter_gonk.cpp',
'progressui_gonk.cpp',
]
DISABLE_STL_WRAPPING = True
OS_LIBS += [
'cutils',
'sysutils',
]
if have_progressui == 0:
SOURCES += [
'progressui_null.cpp',
]
DEFINES['NS_NO_XPCOM'] = True
DISABLE_STL_WRAPPING = True
for var in ('MAR_CHANNEL_ID', 'MOZ_APP_VERSION'):
DEFINES[var] = '"%s"' % CONFIG[var]
LOCAL_INCLUDES += [
'../common',
'/xpcom/glue',
]
DELAYLOAD_DLLS += [
'crypt32.dll',
'comctl32.dll',
'userenv.dll',
'wsock32.dll',
]
if CONFIG['_MSC_VER']:
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
elif CONFIG['OS_ARCH'] == 'WINNT':
WIN32_EXE_LDFLAGS += ['-municode']
if CONFIG['MOZ_WIDGET_GTK']:
CXXFLAGS += CONFIG['TK_CFLAGS']
OS_LIBS += CONFIG['TK_LIBS']
updater_rel_path = ''
include('updater-common.build')
DIRS += ['updater-xpcshell']
FAIL_ON_WARNINGS = True

View File

@ -0,0 +1,128 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
srcs = [
'archivereader.cpp',
'bspatch.cpp',
'updater.cpp',
]
have_progressui = 0
if CONFIG['MOZ_VERIFY_MAR_SIGNATURE']:
USE_LIBS += [
'verifymar',
]
if CONFIG['OS_ARCH'] == 'WINNT':
have_progressui = 1
srcs += [
'loaddlls.cpp',
'progressui_win.cpp',
'win_dirent.cpp',
]
RCINCLUDE = '%supdater.rc' % updater_rel_path
DEFINES['UNICODE'] = True
DEFINES['_UNICODE'] = True
DEFINES['NOMINMAX'] = True
USE_STATIC_LIBS = True
# Pick up nsWindowsRestart.cpp
LOCAL_INCLUDES += [
'/toolkit/xre',
]
USE_LIBS += [
'updatecommon-standalone',
]
OS_LIBS += [
'comctl32',
'ws2_32',
'shell32',
'shlwapi',
'crypt32',
'advapi32',
]
elif CONFIG['OS_ARCH'] == 'Linux' and CONFIG['MOZ_VERIFY_MAR_SIGNATURE']:
USE_LIBS += [
'/modules/libmar/sign/signmar',
'nss',
'updatecommon',
]
OS_LIBS += CONFIG['NSPR_LIBS']
else:
USE_LIBS += [
'updatecommon',
]
USE_LIBS += [
'mar',
]
if CONFIG['MOZ_NATIVE_BZ2']:
OS_LIBS += CONFIG['MOZ_BZ2_LIBS']
else:
USE_LIBS += [
'bz2',
]
if CONFIG['MOZ_ENABLE_GTK']:
have_progressui = 1
srcs += [
'progressui_gtk.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
have_progressui = 1
srcs += [
'launchchild_osx.mm',
'progressui_osx.mm',
]
OS_LIBS += ['-framework Cocoa -framework Security']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
have_progressui = 1
srcs += [
'automounter_gonk.cpp',
'progressui_gonk.cpp',
]
DISABLE_STL_WRAPPING = True
OS_LIBS += [
'cutils',
'sysutils',
]
if have_progressui == 0:
srcs += [
'progressui_null.cpp',
]
srcs.sort()
SOURCES += ['%s%s' % (updater_rel_path, f) for f in srcs]
DEFINES['NS_NO_XPCOM'] = True
DISABLE_STL_WRAPPING = True
for var in ('MAR_CHANNEL_ID', 'MOZ_APP_VERSION'):
DEFINES[var] = '"%s"' % CONFIG[var]
LOCAL_INCLUDES += [
'/toolkit/mozapps/update/common',
'/xpcom/glue',
]
DELAYLOAD_DLLS += [
'crypt32.dll',
'comctl32.dll',
'userenv.dll',
'wsock32.dll',
]
if CONFIG['_MSC_VER']:
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
elif CONFIG['OS_ARCH'] == 'WINNT':
WIN32_EXE_LDFLAGS += ['-municode']
if CONFIG['MOZ_WIDGET_GTK']:
CXXFLAGS += CONFIG['TK_CFLAGS']
OS_LIBS += CONFIG['TK_LIBS']

View File

@ -0,0 +1,50 @@
# vim:set ts=8 sw=8 sts=8 noet:
# 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/.
# For changes here, also consider ../Makefile.in
XPCSHELLTESTROOT = $(abspath $(DEPTH))/_tests/xpcshell/toolkit/mozapps/update/tests
ifndef MOZ_PROFILE_GENERATE
ifdef COMPILE_ENVIRONMENT
INSTALL_TARGETS += xpcshell-updater
xpcshell-updater_TARGET := libs
xpcshell-updater_DEST := $(XPCSHELLTESTROOT)/data
xpcshell-updater_FILES := $(DIST)/bin/updater-xpcshell$(BIN_SUFFIX)
endif
endif # Not MOZ_PROFILE_GENERATE
include $(topsrcdir)/config/rules.mk
ifndef MOZ_WINCONSOLE
ifdef MOZ_DEBUG
MOZ_WINCONSOLE = 1
else
MOZ_WINCONSOLE = 0
endif
endif
ifdef MOZ_WIDGET_GTK
libs:: ../updater.png
$(NSINSTALL) -D $(DIST)/bin/icons
$(INSTALL) $(IFLAGS1) $^ $(DIST)/bin/icons
endif
libs::
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
$(NSINSTALL) -D $(XPCSHELLTESTROOT)/data/updater-xpcshell.app
rsync -a -C --exclude '*.in' $(srcdir)/../macbuild/Contents $(XPCSHELLTESTROOT)/data/updater-xpcshell.app
sed -e 's/%APP_NAME%/$(MOZ_APP_DISPLAYNAME)/' $(srcdir)/../macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
iconv -f UTF-8 -t UTF-16 > $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings
$(NSINSTALL) -D $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS/updater-xpcshell
$(NSINSTALL) $(XPCSHELLTESTROOT)/data/updater-xpcshell $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS
rm -f $(XPCSHELLTESTROOT)/data/updater-xpcshell
mv $(XPCSHELLTESTROOT)/data/updater-xpcshell.app $(XPCSHELLTESTROOT)/data/updater.app
mv $(XPCSHELLTESTROOT)/data/updater.app/Contents/MacOS/updater-xpcshell $(XPCSHELLTESTROOT)/data/updater.app/Contents/MacOS/updater
else
mv $(XPCSHELLTESTROOT)/data/updater-xpcshell$(BIN_SUFFIX) $(XPCSHELLTESTROOT)/data/updater$(BIN_SUFFIX)
endif
CXXFLAGS += $(MOZ_BZ2_CFLAGS)

View File

@ -0,0 +1,11 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
Program('updater-xpcshell')
updater_rel_path = '../'
DEFINES['UPDATER_XPCSHELL_CERT'] = True
include('../updater-common.build')

View File

@ -2194,7 +2194,12 @@ UpdateThreadFunc(void *param)
NS_tchar updateSettingsPath[MAX_TEXT_LEN];
NS_tsnprintf(updateSettingsPath,
sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]),
NS_T("%s/update-settings.ini"), gWorkingDirPath);
#ifdef XP_MACOSX
NS_T("%s/Contents/Resources/update-settings.ini"),
#else
NS_T("%s/update-settings.ini"),
#endif
gWorkingDirPath);
MARChannelStringTable MARStrings;
if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) {
// If we can't read from update-settings.ini then we shouldn't impose

View File

@ -4,7 +4,17 @@
// Microsoft Visual C++ generated resource script.
//
#ifdef UPDATER_XPCSHELL_CERT
#include "../resource.h"
#define MANIFEST_PATH "../updater.exe.manifest"
#define COMCTL32_MANIFEST_PATH "../updater.exe.comctl32.manifest"
#define ICON_PATH "../updater.ico"
#else
#include "resource.h"
#define MANIFEST_PATH "updater.exe.manifest"
#define COMCTL32_MANIFEST_PATH "updater.exe.comctl32.manifest"
#define ICON_PATH "updater.ico"
#endif
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
@ -30,15 +40,15 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// RT_MANIFEST
//
1 RT_MANIFEST "updater.exe.manifest"
IDR_COMCTL32_MANIFEST RT_MANIFEST "updater.exe.comctl32.manifest"
1 RT_MANIFEST MANIFEST_PATH
IDR_COMCTL32_MANIFEST RT_MANIFEST COMCTL32_MANIFEST_PATH
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
IDI_DIALOG ICON "updater.ico"
IDI_DIALOG ICON ICON_PATH
/////////////////////////////////////////////////////////////////////////////