Bug 1216469 - Bypass verification for signed packages from trust origins. r=valentin

This commit is contained in:
Jonathan Hao 2015-10-22 17:09:44 +08:00
parent c006ac8bbd
commit 3d02a2da65
10 changed files with 82 additions and 58 deletions

View File

@ -1452,12 +1452,7 @@ pref("network.http.enable-packaged-apps", false);
// Enable this to bring in the signature verification if the signature exists.
// Set to false if you don't need the signed packaged web app support (i.e. NSec).
pref("network.http.packaged-signed-apps-enabled", false);
// Enable this pref to skip verification process. The packaged app
// will be considered signed no matter the package has a valid/invalid
// signature or no signature.
pref("network.http.packaged-apps-developer-mode", false);
pref("network.http.signed-packages.enabled", false);
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,

View File

@ -17,7 +17,7 @@ interface nsIPackagedAppVerifierListener;
* onStartRequest/onDataAvailable/onStopRequest.
*
*/
[scriptable, uuid(fbb28ef8-d3d0-4a2b-af98-8010163a2bfb)]
[scriptable, uuid(37a5c208-0fce-4ad6-8431-aeb904dfe543)]
interface nsIPackagedAppVerifier : nsIStreamListener
{
// The package identifier of the signed package. For a unsigned package, this
@ -50,6 +50,7 @@ interface nsIPackagedAppVerifier : nsIStreamListener
* The verifier init function.
*/
void init(in nsIPackagedAppVerifierListener aListener,
in ACString aPackageOrigin,
in ACString aSignature,
in nsICacheEntry aPackageCacheEntry);

View File

@ -447,6 +447,7 @@ PackagedAppService::PackagedAppDownloader::EnsureVerifier(nsIRequest *aRequest)
nsCOMPtr<nsICacheEntry> packageCacheEntry = GetPackageCacheEntry(aRequest);
mVerifier = new PackagedAppVerifier(this,
mPackageOrigin,
signature,
packageCacheEntry);
}

View File

@ -16,12 +16,11 @@
#include "mozilla/Preferences.h"
#include "nsIPackagedAppUtils.h"
#include "nsIInputStream.h"
#include "nsComponentManagerUtils.h"
#include "nsIURL.h"
static const short kResourceHashType = nsICryptoHash::SHA256;
// If it's true, all the verification will be skipped and the package will
// be treated signed.
static bool gDeveloperMode = false;
static bool gSignedAppEnabled = false;
namespace mozilla {
@ -40,14 +39,15 @@ PackagedAppVerifier::PackagedAppVerifier()
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
"PackagedAppVerifier::OnResourceVerified must be on main thread");
Init(nullptr, EmptyCString(), nullptr);
Init(nullptr, EmptyCString(), EmptyCString(), nullptr);
}
PackagedAppVerifier::PackagedAppVerifier(nsIPackagedAppVerifierListener* aListener,
const nsACString& aPackageOrigin,
const nsACString& aSignature,
nsICacheEntry* aPackageCacheEntry)
{
Init(aListener, aSignature, aPackageCacheEntry);
Init(aListener, aPackageOrigin, aSignature, aPackageCacheEntry);
}
PackagedAppVerifier::~PackagedAppVerifier()
@ -61,26 +61,29 @@ PackagedAppVerifier::~PackagedAppVerifier()
}
NS_IMETHODIMP PackagedAppVerifier::Init(nsIPackagedAppVerifierListener* aListener,
const nsACString& aPackageOrigin,
const nsACString& aSignature,
nsICacheEntry* aPackageCacheEntry)
{
static bool onceThru = false;
if (!onceThru) {
Preferences::AddBoolVarCache(&gDeveloperMode,
"network.http.packaged-apps-developer-mode", false);
Preferences::AddBoolVarCache(&gSignedAppEnabled,
"network.http.packaged-signed-apps-enabled", false);
"network.http.signed-packages.enabled", false);
onceThru = true;
}
mListener = aListener;
mState = STATE_UNKNOWN;
mPackageOrigin = aPackageOrigin;
mSignature = aSignature;
mIsPackageSigned = false;
mPackageCacheEntry = aPackageCacheEntry;
mIsFirstResource = true;
mManifest = EmptyCString();
mBypassVerification = (mPackageOrigin ==
Preferences::GetCString("network.http.signed-packages.trusted-origin"));
nsresult rv;
mPackagedAppUtils = do_CreateInstance(NS_PACKAGEDAPPUTILS_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
@ -277,8 +280,11 @@ PackagedAppVerifier::VerifyManifest(const ResourceCacheInfo* aInfo)
LOG(("Signature: length = %u\n%s", mSignature.Length(), mSignature.get()));
LOG(("Manifest: length = %u\n%s", mManifest.Length(), mManifest.get()));
bool useDeveloperRoot =
!Preferences::GetCString("network.http.signed-packages.developer-root").IsEmpty();
nsresult rv = mPackagedAppUtils->VerifyManifest(mSignature, mManifest,
this, gDeveloperMode);
this, useDeveloperRoot);
if (NS_FAILED(rv)) {
LOG(("VerifyManifest FAILED rv = %u", (unsigned)rv));
}
@ -305,6 +311,12 @@ PackagedAppVerifier::VerifyResource(const ResourceCacheInfo* aInfo)
MOZ_CRASH();
}
if (mBypassVerification) {
LOG(("Origin is trusted. Bypass integrity check."));
FireVerifiedEvent(false, true);
return;
}
if (mSignature.IsEmpty()) {
LOG(("No signature. No need to do resource integrity check."));
FireVerifiedEvent(false, true);
@ -339,7 +351,8 @@ PackagedAppVerifier::OnManifestVerified(bool aSuccess)
return;
}
if (!aSuccess && gDeveloperMode) {
if (!aSuccess && mBypassVerification) {
aSuccess = true;
LOG(("Developer mode! Treat junk signature valid."));
}

View File

@ -93,6 +93,7 @@ public:
PackagedAppVerifier();
PackagedAppVerifier(nsIPackagedAppVerifierListener* aListener,
const nsACString& aPackageOrigin,
const nsACString& aSignature,
nsICacheEntry* aPackageCacheEntry);
@ -170,6 +171,9 @@ private:
// Whether this package app is signed.
bool mIsPackageSigned;
// Whether we should bypass verification.
bool mBypassVerification;
// The package cache entry (e.g. http://foo.com/app.pak) used to store
// any necessarry signed package information.
nsCOMPtr<nsICacheEntry> mPackageCacheEntry;

View File

@ -21,8 +21,8 @@ var Cr = SpecialPowers.Cr;
SpecialPowers.pushPrefEnv(
{ "set": [["network.http.enable-packaged-apps", true],
["network.http.packaged-signed-apps-enabled", true],
["dom.ipc.processPriorityManager.testMode", true],
["network.http.signed-packages.enabled", true],
["dom.ipc.processPriorityManager.enabled", true],
["dom.ipc.tabs.disabled", false],
["dom.ipc.processCount", 3],

View File

@ -211,15 +211,16 @@ function run_test()
// Enable the feature and save the original pref value
originalPref = Services.prefs.getBoolPref("network.http.enable-packaged-apps");
originalSignedAppEnabled = Services.prefs.getBoolPref("network.http.packaged-signed-apps-enabled");
originalSignedAppEnabled = Services.prefs.getBoolPref("network.http.signed-packages.enabled");
Services.prefs.setBoolPref("network.http.enable-packaged-apps", true);
Services.prefs.setBoolPref("network.http.packaged-signed-apps-enabled", true);
Services.prefs.setBoolPref("network.http.signed-packages.enabled", true);
do_register_cleanup(reset_pref);
add_test(test_channel);
add_test(test_channel_no_notificationCallbacks);
add_test(test_channel_uris);
add_test(test_channel_with_bad_signature_from_trusted_origin);
add_test(test_channel_with_bad_signature);
add_test(test_channel_with_good_signature);
@ -236,6 +237,21 @@ function test_channel_with_bad_signature() {
}), null);
}
function test_channel_with_bad_signature_from_trusted_origin() {
let pref = "network.http.signed-packages.trusted-origin";
ok(!!Ci.nsISupportsString, "Ci.nsISupportsString");
let origin = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
origin.data = uri + "^appId=1024";
Services.prefs.setComplexValue(pref, Ci.nsISupportsString, origin);
var channel = make_channel(uri+"/package_with_bad_signature!//index.html");
channel.notificationCallbacks = new LoadContextCallback(1024, false, false, false);
channel.asyncOpen(new Listener(function(l) {
do_check_true(l.gotStopRequestOK);
Services.prefs.clearUserPref(pref);
run_next_test();
}), null);
}
function test_channel_with_good_signature() {
var channel = make_channel(uri+"/package_with_good_signature!//index.html");
channel.notificationCallbacks = new LoadContextCallback(1024, false, false, false);
@ -281,5 +297,5 @@ function check_regular_response(request, buffer) {
function reset_pref() {
// Set the pref to its original value
Services.prefs.setBoolPref("network.http.enable-packaged-apps", originalPref);
Services.prefs.setBoolPref("network.http.packaged-signed-apps-enabled", originalSignedAppEnabled);
Services.prefs.setBoolPref("network.http.signed-packages.enabled", originalSignedAppEnabled);
}

View File

@ -220,20 +220,15 @@ function run_test()
httpserver.registerPathHandler("/signedPackage", signedPackagedAppContentHandler);
httpserver.start(-1);
// We will enable the developer mode in 'test_signed_package_callback'.
// So restore it after testing.
//
// TODO: To be removed in Bug 1178518.
do_register_cleanup(function() {
gPrefs.clearUserPref("network.http.packaged-apps-developer-mode");
gPrefs.clearUserPref("network.http.packaged-signed-apps-enabled");
gPrefs.clearUserPref("network.http.signed-packages.enabled");
});
paservice = Cc["@mozilla.org/network/packaged-app-service;1"]
.getService(Ci.nsIPackagedAppService);
ok(!!paservice, "test service exists");
gPrefs.setBoolPref("network.http.packaged-signed-apps-enabled", true);
gPrefs.setBoolPref("network.http.signed-packages.enabled", true);
add_test(test_bad_args);

View File

@ -40,16 +40,6 @@ const kStatusCodeIdx = 1;
const kVerificationSuccessIdx = 2;
const kContentIdx = 3;
function enable_developer_mode()
{
gPrefs.setBoolPref("network.http.packaged-apps-developer-mode", true);
}
function reset_developer_mode()
{
gPrefs.clearUserPref("network.http.packaged-apps-developer-mode");
}
function createVerifierListener(aExpecetedCallbacks,
aExpectedPackageId,
aExpectedIsSigned,
@ -90,7 +80,6 @@ function createVerifierListener(aExpecetedCallbacks,
}
if (isLastPart) {
reset_developer_mode();
run_next_test();
}
},
@ -137,12 +126,12 @@ function createPackageCache(aPackageUriAsAscii, aLoadContextInfo) {
return cacheStorage.openTruncate(uri, '');
}
function test_no_signature(aDeveloperMode) {
function test_no_signature(aBypassVerification) {
const kOrigin = 'http://foo.com';
aDeveloperMode = !!aDeveloperMode;
aBypassVerification = !!aBypassVerification;
// If the package has no signature and not in developer mode, the package is unsigned
// If the package has no signature, the package is unsigned
// but the verification result is always true.
const expectedCallbacks = [
@ -158,7 +147,7 @@ function test_no_signature(aDeveloperMode) {
let isPackageSigned = false;
// We only require the package URL to be different in each test case.
let packageUriString = kOrigin + '/pak' + (aDeveloperMode ? '-dev' : '');
let packageUriString = kOrigin + '/pak' + (aBypassVerification ? '-dev' : '');
let packageCacheEntry =
createPackageCache(packageUriString, gLoadContextInfoFactory.default);
@ -168,21 +157,21 @@ function test_no_signature(aDeveloperMode) {
isPackageSigned,
packageCacheEntry);
gVerifier.init(verifierListener, '', packageCacheEntry);
gVerifier.init(verifierListener, kOrigin, '', packageCacheEntry);
feedResources(expectedCallbacks, '');
}
function test_invalid_signature(aDeveloperMode) {
function test_invalid_signature(aBypassVerification) {
const kOrigin = 'http://bar.com';
aDeveloperMode = !!aDeveloperMode;
aBypassVerification = !!aBypassVerification;
// Since we haven't implemented signature verification, the verification always
// fails if the signature exists.
let verificationResult = aDeveloperMode; // Verification always success in developer mode.
let isPackageSigned = aDeveloperMode; // Package is always considered as signed in developer mode.
let verificationResult = aBypassVerification; // Verification always success in developer mode.
let isPackageSigned = aBypassVerification; // Package is always considered as signed in developer mode.
const kPackagedId = '611FC2FE-491D-4A47-B3B3-43FBDF6F404F';
const kManifestContent = 'Content-Location: manifest.webapp\r\n' +
@ -193,35 +182,45 @@ function test_invalid_signature(aDeveloperMode) {
const expectedCallbacks = [
// URL statusCode verificationResult content
[kOrigin + '/manifest', Cr.NS_OK, verificationResult, kManifestContent],
[kOrigin + '/1.html', Cr.NS_OK, verificationResult],
[kOrigin + '/2.js', Cr.NS_OK, verificationResult],
[kOrigin + '/3.jpg', Cr.NS_OK, verificationResult],
[kOrigin + '/4.html', Cr.NS_OK, verificationResult],
[kOrigin + '/5.css', Cr.NS_OK, verificationResult],
[kOrigin + '/1.html', Cr.NS_OK, verificationResult, 'abc'],
[kOrigin + '/2.js', Cr.NS_OK, verificationResult, 'abc'],
[kOrigin + '/3.jpg', Cr.NS_OK, verificationResult, 'abc'],
[kOrigin + '/4.html', Cr.NS_OK, verificationResult, 'abc'],
[kOrigin + '/5.css', Cr.NS_OK, verificationResult, 'abc'],
];
let packageUriString = kOrigin + '/pak' + (aDeveloperMode ? '-dev' : '');
let packageUriString = kOrigin + '/pak' + (aBypassVerification ? '-dev' : '');
let packageCacheEntry =
createPackageCache(packageUriString, gLoadContextInfoFactory.private);
let verifierListener = createVerifierListener(expectedCallbacks,
aDeveloperMode ? kPackagedId : '',
aBypassVerification ? kPackagedId : '',
isPackageSigned,
packageCacheEntry);
let signature = 'manifest-signature: 11111111111111111111111';
gVerifier.init(verifierListener, signature, packageCacheEntry);
gVerifier.init(verifierListener, kOrigin, signature, packageCacheEntry);
feedResources(expectedCallbacks, signature);
}
function test_invalid_signature_bypass_verification() {
let pref = "network.http.signed-packages.trusted-origin";
ok(!!Ci.nsISupportsString, "Ci.nsISupportsString");
let origin = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
origin.data = "http://bar.com";
gPrefs.setComplexValue(pref, Ci.nsISupportsString, origin);
test_invalid_signature(true);
gPrefs.clearUserPref(pref);
}
function run_test()
{
ok(!!gVerifier);
// Test cases in non-developer mode.
add_test(test_no_signature);
add_test(test_invalid_signature);
add_test(test_invalid_signature_bypass_verification);
// run tests
run_next_test();

View File

@ -41,7 +41,7 @@ extern PRLogModuleInfo* gPIPNSSLog;
static const unsigned int DEFAULT_MIN_RSA_BITS = 2048;
static char kDevImportedDER[] =
"network.http.packaged-apps-developer-trusted-root";
"network.http.signed-packages.developer-root";
namespace mozilla { namespace psm {