Bug 896620: Make marketplace certs work on in all products, r=keeler

--HG--
extra : source : 86ec7137a8892f75918c77e605df970f5b96ef62
extra : histedit_source : 33326790804d49e6ec658626116ebf870d94d445
This commit is contained in:
Brian Smith 2014-02-14 14:37:07 -08:00
parent 6f0d9dfd0a
commit 83e4eaa908
19 changed files with 496 additions and 130 deletions

View File

@ -3086,8 +3086,8 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
_openSignedPackage: function(aZipFile, aCertDb) {
let deferred = Promise.defer();
aCertDb.openSignedJARFileAsync(
aZipFile,
aCertDb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppMarketplaceProdPublicRoot, aZipFile,
function(aRv, aZipReader) {
deferred.resolve([aRv, aZipReader]);
}

View File

@ -10,8 +10,10 @@
#include "nsNSSCertificateDB.h"
#include "insanity/pkix.h"
#include "mozilla/RefPtr.h"
#include "CryptoTask.h"
#include "AppTrustDomain.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
#include "nsHashKeys.h"
@ -26,11 +28,14 @@
#include "ScopedNSSTypes.h"
#include "base64.h"
#include "certdb.h"
#include "secmime.h"
#include "plstr.h"
#include "prlog.h"
using namespace insanity::pkix;
using namespace mozilla;
using namespace mozilla::psm;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gPIPNSSLog;
@ -517,31 +522,109 @@ ParseMF(const char* filebuf, nsIZipReader * zip,
return NS_OK;
}
// Callback functions for decoder. For now, use empty/default functions.
void
ContentCallback(void *arg, const char *buf, unsigned long len)
nsresult
VerifySignature(AppTrustedRoot trustedRoot,
const SECItem& buffer, const SECItem& detachedDigest,
/*out*/ insanity::pkix::ScopedCERTCertList& builtChain)
{
}
PK11SymKey *
GetDecryptKeyCallback(void *, SECAlgorithmID *)
{
return nullptr;
}
PRBool
DecryptionAllowedCallback(SECAlgorithmID *algid, PK11SymKey *bulkkey)
{
return false;
}
void *
GetPasswordKeyCallback(void *arg, void *handle)
{
return nullptr;
insanity::pkix::ScopedPtr<NSSCMSMessage, NSS_CMSMessage_Destroy>
cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr));
if (!cmsMsg) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CMS message isn't signed"));
return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
}
NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0);
if (!cinfo) {
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
}
// signedData is non-owning
NSSCMSSignedData* signedData =
reinterpret_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo));
if (!signedData) {
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
}
// Set digest value.
if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1,
const_cast<SECItem*>(&detachedDigest))) {
return NS_ERROR_CMS_VERIFY_BAD_DIGEST;
}
// Parse the certificates into CERTCertificate objects held in memory, so that
// AppTrustDomain will be able to find them during path building.
insanity::pkix::ScopedCERTCertList certs(CERT_NewCertList());
if (!certs) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (signedData->rawCerts) {
for (size_t i = 0; signedData->rawCerts[i]; ++i) {
insanity::pkix::ScopedCERTCertificate
cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
signedData->rawCerts[i], nullptr, false,
true));
// Skip certificates that fail to parse
if (cert) {
if (CERT_AddCertToListTail(certs.get(), cert.get()) == SECSuccess) {
cert.release(); // ownership transfered
} else {
return NS_ERROR_OUT_OF_MEMORY;
}
}
}
}
// Get the end-entity certificate.
int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData);
if (NS_WARN_IF(numSigners != 1)) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
// signer is non-owning.
NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0);
if (NS_WARN_IF(!signer)) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
// cert is signerCert
CERTCertificate* signerCert =
NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB());
if (!signerCert) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
// Verify certificate.
AppTrustDomain trustDomain(nullptr); // TODO: null pinArg
if (trustDomain.SetTrustedRoot(trustedRoot) != SECSuccess) {
return MapSECStatus(SECFailure);
}
if (BuildCertChain(trustDomain, signerCert, PR_Now(), MustBeEndEntity,
KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
builtChain) != SECSuccess) {
return MapSECStatus(SECFailure);
}
// See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
SECOidData* contentTypeOidData =
SECOID_FindOID(&signedData->contentInfo.contentType);
if (!contentTypeOidData) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
return MapSECStatus(NSS_CMSSignerInfo_Verify(signer,
const_cast<SECItem*>(&detachedDigest),
&contentTypeOidData->oid));
}
NS_IMETHODIMP
OpenSignedJARFile(nsIFile * aJarFile,
/*out, optional */ nsIZipReader ** aZipReader,
/*out, optional */ nsIX509Cert3 ** aSignerCert)
OpenSignedAppFile(AppTrustedRoot aTrustedRoot, nsIFile* aJarFile,
/*out, optional */ nsIZipReader** aZipReader,
/*out, optional */ nsIX509Cert3** aSignerCert)
{
NS_ENSURE_ARG_POINTER(aJarFile);
@ -571,20 +654,6 @@ OpenSignedJARFile(nsIFile * aJarFile,
return NS_ERROR_SIGNED_JAR_NOT_SIGNED;
}
sigBuffer.type = siBuffer;
ScopedSEC_PKCS7ContentInfo p7_info(SEC_PKCS7DecodeItem(&sigBuffer,
ContentCallback, nullptr,
GetPasswordKeyCallback, nullptr,
GetDecryptKeyCallback, nullptr,
DecryptionAllowedCallback));
if (!p7_info) {
PRErrorCode error = PR_GetError();
const char * errorName = PR_ErrorToName(error);
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Failed to decode PKCS#7 item: %s",
errorName));
return PRErrorCode_to_nsresult(error);
}
// Signature (SF) file
nsAutoCString sfFilename;
ScopedAutoSECItem sfBuffer;
@ -595,15 +664,11 @@ OpenSignedJARFile(nsIFile * aJarFile,
return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
}
// Verify that the signature file is a valid signature of the SF file
if (!SEC_PKCS7VerifyDetachedSignatureAtTime(p7_info, certUsageObjectSigner,
&sfCalculatedDigest.get(),
HASH_AlgSHA1, false, PR_Now())) {
PRErrorCode error = PR_GetError();
const char * errorName = PR_ErrorToName(error);
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Failed to verify detached signature: %s",
errorName));
rv = PRErrorCode_to_nsresult(error);
sigBuffer.type = siBuffer;
insanity::pkix::ScopedCERTCertList builtChain;
rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(),
builtChain);
if (NS_FAILED(rv)) {
return rv;
}
@ -717,33 +782,32 @@ OpenSignedJARFile(nsIFile * aJarFile,
// XXX: We should return an nsIX509CertList with the whole validated chain,
// but we can't do that until we switch to libpkix.
if (aSignerCert) {
CERTCertificate *rawSignerCert
= p7_info->content.signedData->signerInfos[0]->cert;
NS_ENSURE_TRUE(rawSignerCert, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIX509Cert3> signerCert = nsNSSCertificate::Create(rawSignerCert);
MOZ_ASSERT(CERT_LIST_HEAD(builtChain));
nsCOMPtr<nsIX509Cert3> signerCert =
nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert);
NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY);
signerCert.forget(aSignerCert);
}
return NS_OK;
}
class OpenSignedJARFileTask MOZ_FINAL : public CryptoTask
class OpenSignedAppFileTask MOZ_FINAL : public CryptoTask
{
public:
OpenSignedJARFileTask(nsIFile * aJarFile,
nsIOpenSignedJARFileCallback * aCallback)
: mJarFile(aJarFile)
, mCallback(new nsMainThreadPtrHolder<nsIOpenSignedJARFileCallback>(aCallback))
OpenSignedAppFileTask(AppTrustedRoot aTrustedRoot, nsIFile* aJarFile,
nsIOpenSignedAppFileCallback* aCallback)
: mTrustedRoot(aTrustedRoot)
, mJarFile(aJarFile)
, mCallback(new nsMainThreadPtrHolder<nsIOpenSignedAppFileCallback>(aCallback))
{
}
private:
virtual nsresult CalculateResult() MOZ_OVERRIDE
{
return OpenSignedJARFile(mJarFile, getter_AddRefs(mZipReader),
return OpenSignedAppFile(mTrustedRoot, mJarFile,
getter_AddRefs(mZipReader),
getter_AddRefs(mSignerCert));
}
@ -753,11 +817,12 @@ private:
virtual void CallCallback(nsresult rv)
{
(void) mCallback->OpenSignedJARFileFinished(rv, mZipReader, mSignerCert);
(void) mCallback->OpenSignedAppFileFinished(rv, mZipReader, mSignerCert);
}
const AppTrustedRoot mTrustedRoot;
const nsCOMPtr<nsIFile> mJarFile;
nsMainThreadPtrHandle<nsIOpenSignedJARFileCallback> mCallback;
nsMainThreadPtrHandle<nsIOpenSignedAppFileCallback> mCallback;
nsCOMPtr<nsIZipReader> mZipReader; // out
nsCOMPtr<nsIX509Cert3> mSignerCert; // out
};
@ -765,12 +830,14 @@ private:
} // unnamed namespace
NS_IMETHODIMP
nsNSSCertificateDB::OpenSignedJARFileAsync(
nsIFile * aJarFile, nsIOpenSignedJARFileCallback * aCallback)
nsNSSCertificateDB::OpenSignedAppFileAsync(
AppTrustedRoot aTrustedRoot, nsIFile* aJarFile,
nsIOpenSignedAppFileCallback* aCallback)
{
NS_ENSURE_ARG_POINTER(aJarFile);
NS_ENSURE_ARG_POINTER(aCallback);
RefPtr<OpenSignedJARFileTask> task(new OpenSignedJARFileTask(aJarFile,
RefPtr<OpenSignedAppFileTask> task(new OpenSignedAppFileTask(aTrustedRoot,
aJarFile,
aCallback));
return task->Dispatch("SignedJAR");
}

View File

@ -0,0 +1,181 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifdef MOZ_LOGGING
#define FORCE_PR_LOG 1
#endif
#include "AppTrustDomain.h"
#include "certdb.h"
#include "insanity/pkix.h"
#include "mozilla/ArrayUtils.h"
#include "nsIX509CertDB.h"
#include "prerror.h"
#include "secerr.h"
// Generated in Makefile.in
#include "marketplace-prod-public.inc"
#include "marketplace-prod-reviewers.inc"
#include "marketplace-dev-public.inc"
#include "marketplace-dev-reviewers.inc"
#include "xpcshell.inc"
using namespace insanity::pkix;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gPIPNSSLog;
#endif
namespace mozilla { namespace psm {
AppTrustDomain::AppTrustDomain(void* pinArg)
: mPinArg(pinArg)
{
}
SECStatus
AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot)
{
SECItem trustedDER;
// Load the trusted certificate into the in-memory NSS database so that
// CERT_CreateSubjectCertList can find it.
switch (trustedRoot)
{
case nsIX509CertDB::AppMarketplaceProdPublicRoot:
trustedDER.data = const_cast<uint8_t*>(marketplaceProdPublicRoot);
trustedDER.len = mozilla::ArrayLength(marketplaceProdPublicRoot);
break;
case nsIX509CertDB::AppMarketplaceProdReviewersRoot:
trustedDER.data = const_cast<uint8_t*>(marketplaceProdReviewersRoot);
trustedDER.len = mozilla::ArrayLength(marketplaceProdReviewersRoot);
break;
case nsIX509CertDB::AppMarketplaceDevPublicRoot:
trustedDER.data = const_cast<uint8_t*>(marketplaceDevPublicRoot);
trustedDER.len = mozilla::ArrayLength(marketplaceDevPublicRoot);
break;
case nsIX509CertDB::AppMarketplaceDevReviewersRoot:
trustedDER.data = const_cast<uint8_t*>(marketplaceDevReviewersRoot);
trustedDER.len = mozilla::ArrayLength(marketplaceDevReviewersRoot);
break;
case nsIX509CertDB::AppXPCShellRoot:
trustedDER.data = const_cast<uint8_t*>(xpcshellRoot);
trustedDER.len = mozilla::ArrayLength(xpcshellRoot);
break;
default:
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
mTrustedRoot = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
&trustedDER, nullptr, false, true);
if (!mTrustedRoot) {
return SECFailure;
}
return SECSuccess;
}
SECStatus
AppTrustDomain::FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ insanity::pkix::ScopedCERTCertList& results)
{
MOZ_ASSERT(mTrustedRoot);
if (!mTrustedRoot) {
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
encodedIssuerName, time, true);
if (!results) {
// NSS sometimes returns this unhelpful error code upon failing to find any
// candidate certificates.
if (PR_GetError() == SEC_ERROR_BAD_DATABASE) {
PR_SetError(SEC_ERROR_UNKNOWN_ISSUER, 0);
}
return SECFailure;
}
return SECSuccess;
}
SECStatus
AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
const CERTCertificate* candidateCert,
/*out*/ TrustLevel* trustLevel)
{
MOZ_ASSERT(candidateCert);
MOZ_ASSERT(trustLevel);
MOZ_ASSERT(mTrustedRoot);
if (!candidateCert || !trustLevel) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
if (!mTrustedRoot) {
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
// Handle active distrust of the certificate.
CERTCertTrust trust;
if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) {
PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning);
// For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
// because we can have active distrust for either type of cert. Note that
// CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
// relevant trust bit isn't set then that means the cert must be considered
// distrusted.
PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA
? CERTDB_TRUSTED_CA
: CERTDB_TRUSTED;
if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD)))
== CERTDB_TERMINAL_RECORD) {
*trustLevel = ActivelyDistrusted;
return SECSuccess;
}
#ifdef MOZ_B2G_CERTDATA
// XXX(Bug 972201): We have to allow the old way of supporting additional
// roots until we fix bug 889744. Remove this along with the rest of the
// MOZ_B2G_CERTDATA stuff.
// For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
// needed to consider end-entity certs to be their own trust anchors since
// Gecko implemented nsICertOverrideService.
if (flags & CERTDB_TRUSTED_CA) {
*trustLevel = TrustAnchor;
return SECSuccess;
}
#endif
}
// mTrustedRoot is the only trust anchor for this validation.
if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert)) {
*trustLevel = TrustAnchor;
return SECSuccess;
}
*trustLevel = InheritsTrust;
return SECSuccess;
}
SECStatus
AppTrustDomain::VerifySignedData(const CERTSignedData* signedData,
const CERTCertificate* cert)
{
return ::insanity::pkix::VerifySignedData(signedData, cert, mPinArg);
}
} }

View File

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 mozilla_psm_AppsTrustDomain_h
#define mozilla_psm_AppsTrustDomain_h
#include "insanity/pkixtypes.h"
#include "nsDebug.h"
#include "nsIX509CertDB.h"
namespace mozilla { namespace psm {
class AppTrustDomain MOZ_FINAL : public insanity::pkix::TrustDomain
{
public:
AppTrustDomain(void* pinArg);
SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot);
SECStatus GetCertTrust(insanity::pkix::EndEntityOrCA endEntityOrCA,
const CERTCertificate* candidateCert,
/*out*/ TrustLevel* trustLevel) MOZ_OVERRIDE;
SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ insanity::pkix::ScopedCERTCertList& results)
MOZ_OVERRIDE;
SECStatus VerifySignedData(const CERTSignedData* signedData,
const CERTCertificate* cert) MOZ_OVERRIDE;
private:
void* mPinArg; // non-owning!
insanity::pkix::ScopedCERTCertificate mTrustedRoot;
};
} } // namespace mozilla::psm
#endif // mozilla_psm_AppsTrustDomain_h

29
security/apps/Makefile.in Normal file
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/.
GEN_CERT_HEADER = $(srcdir)/gen_cert_header.py
marketplace-prod-public.inc: marketplace-prod-public.crt $(GEN_CERT_HEADER)
$(PYTHON) $(GEN_CERT_HEADER) marketplaceProdPublicRoot $< > $@
marketplace-prod-reviewers.inc: marketplace-prod-reviewers.crt $(GEN_CERT_HEADER)
$(PYTHON) $(GEN_CERT_HEADER) marketplaceProdReviewersRoot $< > $@
marketplace-dev-public.inc: marketplace-dev-public.crt $(GEN_CERT_HEADER)
$(PYTHON) $(GEN_CERT_HEADER) marketplaceDevPublicRoot $< > $@
marketplace-dev-reviewers.inc: marketplace-dev-reviewers.crt $(GEN_CERT_HEADER)
$(PYTHON) $(GEN_CERT_HEADER) marketplaceDevReviewersRoot $< > $@
xpcshell.inc: $(srcdir)/../manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der $(GEN_CERT_HEADER)
$(PYTHON) $(GEN_CERT_HEADER) xpcshellRoot $< > $@
export:: \
marketplace-prod-public.inc \
marketplace-prod-reviewers.inc \
marketplace-dev-public.inc \
marketplace-dev-reviewers.inc \
xpcshell.inc \
$(NULL)

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/.
import sys
import binascii
def file_byte_generator(filename, block_size = 512):
with open(filename, "rb") as f:
while True:
block = f.read(block_size)
if block:
for byte in block:
yield byte
else:
break
def create_header(array_name, in_filename):
hexified = ["0x" + binascii.hexlify(byte) for byte in file_byte_generator(in_filename)]
print "const uint8_t " + array_name + "[] = {"
print ", ".join(hexified)
print "};"
return 0
if __name__ == '__main__':
if len(sys.argv) < 3:
print 'ERROR: usage: gen_cert_header.py array_name in_filename'
sys.exit(1);
sys.exit(create_header(sys.argv[1], sys.argv[2]))

Binary file not shown.

Binary file not shown.

Binary file not shown.

25
security/apps/moz.build Normal file
View File

@ -0,0 +1,25 @@
# -*- 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/.
# These cannot be built in unified mode because they force NSPR logging.
SOURCES += [
'AppSignatureVerification.cpp',
'AppTrustDomain.cpp',
]
FAIL_ON_WARNINGS = True
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'../certverifier',
'../insanity/include',
'../manager/ssl/src',
]
DEFINES['NSS_ENABLE_ECC'] = 'True'
for var in ('DLL_PREFIX', 'DLL_SUFFIX'):
DEFINES[var] = '"%s"' % CONFIG[var]

View File

@ -59,6 +59,11 @@ NSSCertDBTrustDomain::FindPotentialIssuers(
results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
encodedIssuerName, time, true);
if (!results) {
// NSS sometimes returns this unhelpful error code upon failing to find any
// candidate certificates.
if (PR_GetError() == SEC_ERROR_BAD_DATABASE) {
PR_SetError(SEC_ERROR_UNKNOWN_ISSUER, 0);
}
return SECFailure;
}

View File

@ -19,10 +19,12 @@ interface nsIX509CertList;
#define NS_X509CERTDB_CONTRACTID "@mozilla.org/security/x509certdb;1"
%}
[scriptable, function, uuid(25a048e8-bb1c-4c33-ad3a-eacf2ad9e9ee)]
interface nsIOpenSignedJARFileCallback : nsISupports
typedef uint32_t AppTrustedRoot;
[scriptable, function, uuid(e12aec59-7153-4e84-9376-2e851311b7a3)]
interface nsIOpenSignedAppFileCallback : nsISupports
{
void openSignedJARFileFinished(in nsresult rv,
void openSignedAppFileFinished(in nsresult rv,
in nsIZipReader aZipReader,
in nsIX509Cert3 aSignerCert);
};
@ -31,7 +33,7 @@ interface nsIOpenSignedJARFileCallback : nsISupports
* This represents a service to access and manipulate
* X.509 certificates stored in a database.
*/
[scriptable, uuid(38463592-8527-11e3-b240-180373d97f23)]
[scriptable, uuid(398dfa21-f29d-4530-bb42-136c6e7d9486)]
interface nsIX509CertDB : nsISupports {
/**
@ -297,8 +299,14 @@ interface nsIX509CertDB : nsISupports {
* as input, to encourage users of the API to verify the signature as the
* first step in opening the JAR.
*/
void openSignedJARFileAsync(in nsIFile aJarFile,
in nsIOpenSignedJARFileCallback callback);
const AppTrustedRoot AppMarketplaceProdPublicRoot = 1;
const AppTrustedRoot AppMarketplaceProdReviewersRoot = 2;
const AppTrustedRoot AppMarketplaceDevPublicRoot = 3;
const AppTrustedRoot AppMarketplaceDevReviewersRoot = 4;
const AppTrustedRoot AppXPCShellRoot = 5;
void openSignedAppFileAsync(in AppTrustedRoot trustedRoot,
in nsIFile aJarFile,
in nsIOpenSignedAppFileCallback callback);
/*
* Add a cert to a cert DB from a binary string.

View File

@ -68,7 +68,6 @@ UNIFIED_SOURCES += [
# The rest cannot be built in unified mode because they want to force NSPR
# logging.
SOURCES += [
'JARSignatureVerification.cpp',
'nsCryptoHash.cpp',
'nsNSSCertificateDB.cpp',
'nsNSSComponent.cpp',

View File

@ -70,6 +70,7 @@
#include "certdb.h"
#include "secmod.h"
#include "ScopedNSSTypes.h"
#include "insanity/pkixtypes.h"
#include "ssl.h" // For SSL_ClearSessionCache
@ -2384,9 +2385,10 @@ nsCrypto::ImportUserCertificates(const nsAString& aNickname,
//Import the root chain into the cert db.
{
ScopedCERTCertList caPubs(CMMF_CertRepContentGetCAPubs(certRepContent));
insanity::pkix::ScopedCERTCertList
caPubs(CMMF_CertRepContentGetCAPubs(certRepContent));
if (caPubs) {
int32_t numCAs = nsCertListCount(caPubs);
int32_t numCAs = nsCertListCount(caPubs.get());
NS_ASSERTION(numCAs > 0, "Invalid number of CA's");
if (numCAs > 0) {

View File

@ -22,6 +22,7 @@ const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
// Sort in numerical order
const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12;
const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
const SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18;
const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20;
const SEC_ERROR_EXTENSION_NOT_FOUND = SEC_ERROR_BASE + 35;

View File

@ -10,28 +10,9 @@ function run_test() {
run_next_test();
}
// XXX: NSS has many possible error codes for this, e.g.
// SEC_ERROR_UNTRUSTED_ISSUER and others are also reasonable. Future
// versions of NSS may return one of these alternate errors; in that case
// we need to update this test.
//
// XXX (bug 812089): Cr.NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER is undefined.
//
// XXX: Cannot use operator| instead of operator+ to combine bits because
// bit 31 trigger's JavaScript's crazy interpretation of the numbers as
// two's complement negative integers.
const NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER = 0x80000000 /*unsigned (1 << 31)*/
+ ( (0x45 + 21) << 16)
+ (-(-0x2000 + 13) );
function check_open_result(name, expectedRv) {
if (expectedRv == Cr.NS_OK && !isB2G) {
// We do not trust the marketplace trust anchor on non-B2G builds
expectedRv = NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER;
}
return function openSignedJARFileCallback(rv, aZipReader, aSignerCert) {
do_print("openSignedJARFileCallback called for " + name);
return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) {
do_print("openSignedAppFileCallback called for " + name);
do_check_eq(rv, expectedRv);
do_check_eq(aZipReader != null, Components.isSuccessCode(expectedRv));
do_check_eq(aSignerCert != null, Components.isSuccessCode(expectedRv));
@ -46,15 +27,17 @@ function original_app_path(test_name) {
// Test that we no longer trust the test root cert that was originally used
// during development of B2G 1.0.
add_test(function () {
certdb.openSignedJARFileAsync(
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppMarketplaceProdPublicRoot,
original_app_path("test-privileged-app-test-1.0"),
check_open_result("test-privileged-app-test-1.0",
NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER));
getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)));
});
// Test that we trust the root cert used by by the Firefox Marketplace.
add_test(function () {
certdb.openSignedJARFileAsync(
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppMarketplaceProdPublicRoot,
original_app_path("privileged-app-test-1.0"),
check_open_result("privileged-app-test-1.0", Cr.NS_OK));
});

View File

@ -111,16 +111,12 @@ function truncateEntry(entry, entryInput) {
}
function run_test() {
var root_cert_der =
do_get_file("test_signed_apps/trusted_ca1.der", false);
var der = readFile(root_cert_der);
certdb.addCert(der, ",,CTu", "test-root");
run_next_test();
}
function check_open_result(name, expectedRv) {
return function openSignedJARFileCallback(rv, aZipReader, aSignerCert) {
do_print("openSignedJARFileCallback called for " + name);
return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) {
do_print("openSignedAppFileCallback called for " + name);
do_check_eq(rv, expectedRv);
do_check_eq(aZipReader != null, Components.isSuccessCode(expectedRv));
do_check_eq(aSignerCert != null, Components.isSuccessCode(expectedRv));
@ -137,60 +133,54 @@ function tampered_app_path(test_name) {
}
add_test(function () {
certdb.openSignedJARFileAsync(original_app_path("valid"),
check_open_result("valid", Cr.NS_OK));
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid"),
check_open_result("valid", Cr.NS_OK));
});
add_test(function () {
certdb.openSignedJARFileAsync(original_app_path("unsigned"),
check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned"),
check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
});
add_test(function () {
// XXX: NSS has many possible error codes for this, e.g.
// SEC_ERROR_UNTRUSTED_ISSUER and others are also reasonable. Future versions
// of NSS may return one of these alternate errors; in that case, we need to
// update this test.
//
// XXX (bug 812089): Cr.NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER is undefined.
//
// XXX: Cannot use operator| instead of operator+ to combine bits because
// bit 31 trigger's JavaScript's crazy interpretation of the numbers as
// two's complement negative integers.
const NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER = 0x80000000 /* unsigned (1 << 31) */
+ ( (0x45 + 21) << 16)
+ (-(-0x2000 + 13) );
certdb.openSignedJARFileAsync(original_app_path("unknown_issuer"),
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer"),
check_open_result("unknown_issuer",
/*Cr.*/NS_ERROR_SEC_ERROR_UNKNOWN_ISSUER));
getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)));
});
// Sanity check to ensure a no-op tampering gives a valid result
add_test(function () {
var tampered = tampered_app_path("identity_tampering");
tamper(original_app_path("valid"), tampered, { }, []);
certdb.openSignedJARFileAsync(original_app_path("valid"),
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid"),
check_open_result("identity_tampering", Cr.NS_OK));
});
add_test(function () {
var tampered = tampered_app_path("missing_rsa");
tamper(original_app_path("valid"), tampered, { "META-INF/A.RSA" : removeEntry }, []);
certdb.openSignedJARFileAsync(tampered,
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
});
add_test(function () {
var tampered = tampered_app_path("missing_sf");
tamper(original_app_path("valid"), tampered, { "META-INF/A.SF" : removeEntry }, []);
certdb.openSignedJARFileAsync(tampered,
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
});
add_test(function () {
var tampered = tampered_app_path("missing_manifest_mf");
tamper(original_app_path("valid"), tampered, { "META-INF/MANIFEST.MF" : removeEntry }, []);
certdb.openSignedJARFileAsync(tampered,
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_manifest_mf",
Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
});
@ -198,14 +188,16 @@ add_test(function () {
add_test(function () {
var tampered = tampered_app_path("missing_entry");
tamper(original_app_path("valid"), tampered, { "manifest.webapp" : removeEntry }, []);
certdb.openSignedJARFileAsync(tampered,
check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
});
add_test(function () {
var tampered = tampered_app_path("truncated_entry");
tamper(original_app_path("valid"), tampered, { "manifest.webapp" : truncateEntry }, []);
certdb.openSignedJARFileAsync(tampered,
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
});
@ -213,7 +205,8 @@ add_test(function () {
var tampered = tampered_app_path("unsigned_entry");
tamper(original_app_path("valid"), tampered, {},
[ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
certdb.openSignedJARFileAsync(tampered,
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
});
@ -221,7 +214,8 @@ add_test(function () {
var tampered = tampered_app_path("unsigned_metainf_entry");
tamper(original_app_path("valid"), tampered, {},
[ { name: "META-INF/unsigned.txt", content: "unsigned content!" } ]);
certdb.openSignedJARFileAsync(tampered,
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("unsigned_metainf_entry",
Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
});

View File

@ -13,6 +13,9 @@ if CONFIG['MOZ_CONTENT_SANDBOX']:
# builds fail.
add_tier_dir('platform', 'security/certverifier')
# Depends on certverifier
add_tier_dir('platform', 'security/apps')
# the signing related bits of libmar depend on nss
if CONFIG['MOZ_UPDATER']:
add_tier_dir('platform', 'modules/libmar')