diff --git a/security/apps/AppSignatureVerification.cpp b/security/apps/AppSignatureVerification.cpp index ec73b129d92d..0c53e23c7850 100644 --- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -20,7 +20,6 @@ #include "nsIFileStreams.h" #include "nsIInputStream.h" #include "nsIStringEnumerator.h" -#include "nsIDirectoryEnumerator.h" #include "nsIZipReader.h" #include "nsNetUtil.h" #include "nsNSSCertificate.h" @@ -99,7 +98,7 @@ ReadStream(const nsCOMPtr& stream, /*out*/ SECItem& buf) return NS_OK; } -// Finds exactly one (signature metadata) JAR entry that matches the given +// Finds exactly one (signature metadata) entry that matches the given // search pattern, and then load it. Fails if there are no matches or if // there is more than one match. If bugDigest is not null then on success // bufDigest will contain the SHA-1 digeset of the entry. @@ -154,22 +153,27 @@ FindAndLoadOneEntry(nsIZipReader * zip, // at once, which would require memory in proportion to the size of the largest // entry. Instead, we require only a small, fixed amount of memory. // -// @param stream an input stream from a JAR entry or file depending on whether -// it is from a signed archive or unpacked into a directory // @param digestFromManifest The digest that we're supposed to check the file's // contents against, from the manifest // @param buf A scratch buffer that we use for doing the I/O, which must have // already been allocated. The size of this buffer is the unit // size of our I/O. nsresult -VerifyStreamContentDigest(nsIInputStream* stream, - const SECItem& digestFromManifest, SECItem& buf) +VerifyEntryContentDigest(nsIZipReader * zip, const nsACString & aFilename, + const SECItem & digestFromManifest, SECItem & buf) { MOZ_ASSERT(buf.len > 0); if (digestFromManifest.len != SHA1_LENGTH) return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; nsresult rv; + + nsCOMPtr stream; + rv = zip->GetInputStream(aFilename, getter_AddRefs(stream)); + if (NS_FAILED(rv)) { + return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; + } + uint64_t len64; rv = stream->Available(&len64); NS_ENSURE_SUCCESS(rv, rv); @@ -222,81 +226,6 @@ VerifyStreamContentDigest(nsIInputStream* stream, return NS_OK; } -nsresult -VerifyEntryContentDigest(nsIZipReader* zip, const nsACString& aFilename, - const SECItem& digestFromManifest, SECItem& buf) -{ - nsCOMPtr stream; - nsresult rv = zip->GetInputStream(aFilename, getter_AddRefs(stream)); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; - } - - return VerifyStreamContentDigest(stream, digestFromManifest, buf); -} - -// @oaram aDir directory containing the unpacked signed archive -// @param aFilename path of the target file relative to aDir -// @param digestFromManifest The digest that we're supposed to check the file's -// contents against, from the manifest -// @param buf A scratch buffer that we use for doing the I/O -nsresult -VerifyFileContentDigest(nsIFile* aDir, const nsAString& aFilename, - const SECItem& digestFromManifest, SECItem& buf) -{ - // Find the file corresponding to the manifest path - nsCOMPtr file; - nsresult rv = aDir->Clone(getter_AddRefs(file)); - if (NS_FAILED(rv)) { - return rv; - } - - // We don't know how to handle JARs with signed directory entries. - // It's technically possible in the manifest but makes no sense on disk. - // Inside an archive we just ignore them, but here we have to treat it - // as an error because the signed bytes never got unpacked. - int32_t pos = 0; - int32_t slash; - int32_t namelen = aFilename.Length(); - if (namelen == 0 || aFilename[namelen - 1] == '/') { - return NS_ERROR_SIGNED_JAR_ENTRY_INVALID; - } - - // Append path segments one by one - do { - slash = aFilename.FindChar('/', pos); - int32_t segend = (slash == kNotFound) ? namelen : slash; - rv = file->Append(Substring(aFilename, pos, (segend - pos))); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_ENTRY_INVALID; - } - pos = slash + 1; - } while (pos < namelen && slash != kNotFound); - - bool exists; - rv = file->Exists(&exists); - if (NS_FAILED(rv) || !exists) { - return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; - } - - bool isDir; - rv = file->IsDirectory(&isDir); - if (NS_FAILED(rv) || isDir) { - // We only support signed files, not directory entries - return NS_ERROR_SIGNED_JAR_ENTRY_INVALID; - } - - // Open an input stream for that file and verify it. - nsCOMPtr stream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file, -1, -1, - nsIFileInputStream::CLOSE_ON_EOF); - if (NS_FAILED(rv) || !stream) { - return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; - } - - return VerifyStreamContentDigest(stream, digestFromManifest, buf); -} - // On input, nextLineStart is the start of the current line. On output, // nextLineStart is the start of the next line. nsresult @@ -357,7 +286,6 @@ ReadLine(/*in/out*/ const char* & nextLineStart, /*out*/ nsCString & line, #define JAR_MF_SEARCH_STRING "(M|/M)ETA-INF/(M|m)(ANIFEST|anifest).(MF|mf)$" #define JAR_SF_SEARCH_STRING "(M|/M)ETA-INF/*.(SF|sf)$" #define JAR_RSA_SEARCH_STRING "(M|/M)ETA-INF/*.(RSA|rsa)$" -#define JAR_META_DIR "META-INF" #define JAR_MF_HEADER "Manifest-Version: 1.0" #define JAR_SF_HEADER "Signature-Version: 1.0" @@ -1021,497 +949,3 @@ nsNSSCertificateDB::VerifySignedManifestAsync( aSignatureStream, aCallback)); return task->Dispatch("SignedManifest"); } - - -// -// Signature verification for archives unpacked into a file structure -// - -// Finds the "*.rsa" signature file in the META-INF directory and returns -// the name. It is an error if there are none or more than one .rsa file -nsresult -FindSignatureFilename(nsIFile* aMetaDir, - /*out*/ nsAString& aFilename) -{ - nsCOMPtr entries; - nsresult rv = aMetaDir->GetDirectoryEntries(getter_AddRefs(entries)); - nsCOMPtr files = do_QueryInterface(entries); - if (NS_FAILED(rv) || !files) { - return NS_ERROR_SIGNED_JAR_NOT_SIGNED; - } - - bool found = false; - nsCOMPtr file; - rv = files->GetNextFile(getter_AddRefs(file)); - - while (NS_SUCCEEDED(rv) && file) { - nsAutoString leafname; - rv = file->GetLeafName(leafname); - if (NS_SUCCEEDED(rv)) { - if (StringEndsWith(leafname, NS_LITERAL_STRING(".rsa"))) { - if (!found) { - found = true; - aFilename = leafname; - } else { - // second signature file is an error - rv = NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - break; - } - } - rv = files->GetNextFile(getter_AddRefs(file)); - } - } - - if (!found) { - rv = NS_ERROR_SIGNED_JAR_NOT_SIGNED; - } - - files->Close(); - return rv; -} - -// Loads the signature metadata file that matches the given filename in -// the passed-in Meta-inf directory. If bufDigest is not null then on -// success bufDigest will contain the SHA-1 digest of the entry. -nsresult -LoadOneMetafile(nsIFile* aMetaDir, - const nsAString& aFilename, - /*out*/ SECItem& aBuf, - /*optional, out*/ Digest* aBufDigest) -{ - nsCOMPtr metafile; - nsresult rv = aMetaDir->Clone(getter_AddRefs(metafile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = metafile->Append(aFilename); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = metafile->Exists(&exists); - if (NS_FAILED(rv) || !exists) { - // we can call a missing .rsa file "unsigned" but FindSignatureFilename() - // already found one: missing other metadata files means a broken signature. - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - nsCOMPtr stream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), metafile); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadStream(stream, aBuf); - stream->Close(); - NS_ENSURE_SUCCESS(rv, rv); - - if (aBufDigest) { - rv = aBufDigest->DigestBuf(SEC_OID_SHA1, aBuf.data, aBuf.len - 1); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -// Parses MANIFEST.MF and verifies the contents of the unpacked files -// listed in the manifest. -// The filenames of all entries will be returned in aMfItems. aBuf must -// be a pre-allocated scratch buffer that is used for doing I/O. -nsresult -ParseMFUnpacked(const char* aFilebuf, nsIFile* aDir, - /*out*/ nsTHashtable& aMfItems, - ScopedAutoSECItem& aBuf) -{ - nsresult rv; - - const char* nextLineStart = aFilebuf; - - rv = CheckManifestVersion(nextLineStart, NS_LITERAL_CSTRING(JAR_MF_HEADER)); - if (NS_FAILED(rv)) { - return rv; - } - - // Skip the rest of the header section, which ends with a blank line. - { - nsAutoCString line; - do { - rv = ReadLine(nextLineStart, line); - if (NS_FAILED(rv)) { - return rv; - } - } while (line.Length() > 0); - - // Manifest containing no file entries is OK, though useless. - if (*nextLineStart == '\0') { - return NS_OK; - } - } - - nsAutoString curItemName; - ScopedAutoSECItem digest; - - for (;;) { - nsAutoCString curLine; - rv = ReadLine(nextLineStart, curLine); - if (NS_FAILED(rv)) { - return rv; - } - - if (curLine.Length() == 0) { - // end of section (blank line or end-of-file) - - if (curItemName.Length() == 0) { - // '...Each section must start with an attribute with the name as - // "Name",...', so every section must have a Name attribute. - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - if (digest.len == 0) { - // We require every entry to have a digest, since we require every - // entry to be signed and we don't allow duplicate entries. - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - if (aMfItems.Contains(curItemName)) { - // Duplicate entry - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - // Verify that the file's content digest matches the digest from this - // MF section. - rv = VerifyFileContentDigest(aDir, curItemName, digest, aBuf); - if (NS_FAILED(rv)) { - return rv; - } - - aMfItems.PutEntry(curItemName); - - if (*nextLineStart == '\0') { - // end-of-file - break; - } - - // reset so we know we haven't encountered either of these for the next - // item yet. - curItemName.Truncate(); - digest.reset(); - - continue; // skip the rest of the loop below - } - - nsAutoCString attrName; - nsAutoCString attrValue; - rv = ParseAttribute(curLine, attrName, attrValue); - if (NS_FAILED(rv)) { - return rv; - } - - // Lines to look for: - - // (1) Digest: - if (attrName.LowerCaseEqualsLiteral("sha1-digest")) { - if (digest.len > 0) { - // multiple SHA1 digests in section - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - rv = MapSECStatus(ATOB_ConvertAsciiToItem(&digest, attrValue.get())); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - continue; - } - - // (2) Name: associates this manifest section with a file in the jar. - if (attrName.LowerCaseEqualsLiteral("name")) { - if (MOZ_UNLIKELY(curItemName.Length() > 0)) { - // multiple names in section - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - if (MOZ_UNLIKELY(attrValue.Length() == 0)) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - curItemName = NS_ConvertUTF8toUTF16(attrValue); - - continue; - } - - // (3) Magic: the only other must-understand attribute - if (attrName.LowerCaseEqualsLiteral("magic")) { - // We don't understand any magic, so we can't verify an entry that - // requires magic. Since we require every entry to have a valid - // signature, we have no choice but to reject the entry. - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - // unrecognized attributes must be ignored - } - - return NS_OK; -} - -// recursively check a directory tree for files not in the list of -// verified files we found in the manifest. For each file we find -// Check it against the files found in the manifest. If the file wasn't -// in the manifest then it's unsigned and we can stop looking. Otherwise -// remove it from the collection so we can check leftovers later. -// -// @param aDir Directory to check -// @param aPath Relative path to that directory (to check against aItems) -// @param aItems All the files found -// @param *Filename signature files that won't be in the manifest -nsresult -CheckDirForUnsignedFiles(nsIFile* aDir, - const nsString& aPath, - /* in/out */ nsTHashtable& aItems, - const nsAString& sigFilename, - const nsAString& sfFilename, - const nsAString& mfFilename) -{ - nsCOMPtr entries; - nsresult rv = aDir->GetDirectoryEntries(getter_AddRefs(entries)); - nsCOMPtr files = do_QueryInterface(entries); - if (NS_FAILED(rv) || !files) { - return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; - } - - bool inMeta = StringBeginsWith(aPath, NS_LITERAL_STRING(JAR_META_DIR)); - - while (NS_SUCCEEDED(rv)) { - nsCOMPtr file; - rv = files->GetNextFile(getter_AddRefs(file)); - if (NS_FAILED(rv) || !file) { - break; - } - - nsAutoString leafname; - rv = file->GetLeafName(leafname); - if (NS_FAILED(rv)) { - return rv; - } - - nsAutoString curName(aPath + leafname); - - bool isDir; - rv = file->IsDirectory(&isDir); - if (NS_FAILED(rv)) { - return rv; - } - - // if it's a directory we need to recurse - if (isDir) { - curName.Append(NS_LITERAL_STRING("/")); - rv = CheckDirForUnsignedFiles(file, curName, aItems, - sigFilename, sfFilename, mfFilename); - } else { - // The files that comprise the signature mechanism are not covered by the - // signature. - // - // XXX: This is OK for a single signature, but doesn't work for - // multiple signatures because the metadata for the other signatures - // is not signed either. - if (inMeta && ( leafname == sigFilename || - leafname == sfFilename || - leafname == mfFilename )) { - continue; - } - - // make sure the current file was found in the manifest - nsStringHashKey* item = aItems.GetEntry(curName); - if (!item) { - return NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY; - } - - // Remove the item so we can check for leftover items later - aItems.RemoveEntry(curName); - } - } - files->Close(); - return rv; -} - -/* - * Verify the signature of a directory structure as if it were a - * signed JAR file (used for unpacked JARs) - */ -nsresult -VerifySignedDirectory(AppTrustedRoot aTrustedRoot, - nsIFile* aDirectory, - /*out, optional */ nsIX509Cert** aSignerCert) -{ - NS_ENSURE_ARG_POINTER(aDirectory); - - if (aSignerCert) { - *aSignerCert = nullptr; - } - - // Make sure there's a META-INF directory - - nsCOMPtr metaDir; - nsresult rv = aDirectory->Clone(getter_AddRefs(metaDir)); - if (NS_FAILED(rv)) { - return rv; - } - rv = metaDir->Append(NS_LITERAL_STRING(JAR_META_DIR)); - if (NS_FAILED(rv)) { - return rv; - } - - bool exists; - rv = metaDir->Exists(&exists); - if (NS_FAILED(rv) || !exists) { - return NS_ERROR_SIGNED_JAR_NOT_SIGNED; - } - bool isDirectory; - rv = metaDir->IsDirectory(&isDirectory); - if (NS_FAILED(rv) || !isDirectory) { - return NS_ERROR_SIGNED_JAR_NOT_SIGNED; - } - - // Find and load the Signature (RSA) file - - nsAutoString sigFilename; - rv = FindSignatureFilename(metaDir, sigFilename); - if (NS_FAILED(rv)) { - return rv; - } - - ScopedAutoSECItem sigBuffer; - rv = LoadOneMetafile(metaDir, sigFilename, sigBuffer, nullptr); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_NOT_SIGNED; - } - - // Load the signature (SF) file and verify the signature. - // The .sf and .rsa files must have the same name apart from the extension. - - nsAutoString sfFilename(Substring(sigFilename, 0, sigFilename.Length() - 3) - + NS_LITERAL_STRING("sf")); - - ScopedAutoSECItem sfBuffer; - Digest sfCalculatedDigest; - rv = LoadOneMetafile(metaDir, sfFilename, sfBuffer, &sfCalculatedDigest); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - sigBuffer.type = siBuffer; - ScopedCERTCertList builtChain; - rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(), - builtChain); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - // Get the expected manifest hash from the signed .sf file - - ScopedAutoSECItem mfDigest; - rv = ParseSF(char_ptr_cast(sfBuffer.data), mfDigest); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - // Load manifest (MF) file and verify signature - - nsAutoString mfFilename(NS_LITERAL_STRING("manifest.mf")); - ScopedAutoSECItem manifestBuffer; - Digest mfCalculatedDigest; - rv = LoadOneMetafile(metaDir, mfFilename, manifestBuffer, &mfCalculatedDigest); - if (NS_FAILED(rv)) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - if (SECITEM_CompareItem(&mfDigest, &mfCalculatedDigest.get()) != SECEqual) { - return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; - } - - // Parse manifest and verify signed hash of all listed files - - // Allocate the I/O buffer only once per JAR, instead of once per entry, in - // order to minimize malloc/free calls and in order to avoid fragmenting - // memory. - ScopedAutoSECItem buf(128 * 1024); - - nsTHashtable items; - rv = ParseMFUnpacked(char_ptr_cast(manifestBuffer.data), - aDirectory, items, buf); - if (NS_FAILED(rv)){ - return rv; - } - - // We've checked that everything listed in the manifest exists and is signed - // correctly. Now check on disk for extra (unsigned) files. - // Deletes found entries from items as it goes. - rv = CheckDirForUnsignedFiles(aDirectory, EmptyString(), items, - sigFilename, sfFilename, mfFilename); - if (NS_FAILED(rv)) { - return rv; - } - - // We verified that every entry that we require to be signed is signed. But, - // were there any missing entries--that is, entries that are mentioned in the - // manifest but missing from the directory tree? (There shouldn't be given - // ParseMFUnpacked() checking them all, but it's a cheap sanity check.) - if (items.Count() != 0) { - return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; - } - - // Return the signer's certificate to the reader if they want it. - // XXX: We should return an nsIX509CertList with the whole validated chain. - if (aSignerCert) { - MOZ_ASSERT(CERT_LIST_HEAD(builtChain)); - nsCOMPtr signerCert = - nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert); - NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY); - signerCert.forget(aSignerCert); - } - - return NS_OK; -} - -class VerifySignedDirectoryTask final : public CryptoTask -{ -public: - VerifySignedDirectoryTask(AppTrustedRoot aTrustedRoot, nsIFile* aUnpackedJar, - nsIVerifySignedDirectoryCallback* aCallback) - : mTrustedRoot(aTrustedRoot) - , mDirectory(aUnpackedJar) - , mCallback(new nsMainThreadPtrHolder(aCallback)) - { - } - -private: - virtual nsresult CalculateResult() override - { - return VerifySignedDirectory(mTrustedRoot, - mDirectory, - getter_AddRefs(mSignerCert)); - } - - // This class doesn't directly hold NSS resources so there's nothing that - // needs to be released - virtual void ReleaseNSSResources() override { } - - virtual void CallCallback(nsresult rv) override - { - (void) mCallback->VerifySignedDirectoryFinished(rv, mSignerCert); - } - - const AppTrustedRoot mTrustedRoot; - const nsCOMPtr mDirectory; - nsMainThreadPtrHandle mCallback; - nsCOMPtr mSignerCert; // out -}; - -NS_IMETHODIMP -nsNSSCertificateDB::VerifySignedDirectoryAsync( - AppTrustedRoot aTrustedRoot, nsIFile* aUnpackedJar, - nsIVerifySignedDirectoryCallback* aCallback) -{ - NS_ENSURE_ARG_POINTER(aUnpackedJar); - NS_ENSURE_ARG_POINTER(aCallback); - RefPtr task(new VerifySignedDirectoryTask(aTrustedRoot, - aUnpackedJar, - aCallback)); - return task->Dispatch("UnpackedJar"); -} diff --git a/security/manager/ssl/public/nsIX509CertDB.idl b/security/manager/ssl/public/nsIX509CertDB.idl index c4cc232c9227..9a4e3f53c207 100644 --- a/security/manager/ssl/public/nsIX509CertDB.idl +++ b/security/manager/ssl/public/nsIX509CertDB.idl @@ -28,13 +28,6 @@ interface nsIOpenSignedAppFileCallback : nsISupports in nsIX509Cert aSignerCert); }; -[scriptable, function, uuid(d5f97827-622a-488f-be08-d850432ac8ec)] -interface nsIVerifySignedDirectoryCallback : nsISupports -{ - void verifySignedDirectoryFinished(in nsresult rv, - in nsIX509Cert aSignerCert); -}; - [scriptable, function, uuid(3d6a9c87-5c5f-46fc-9410-96da6092f0f2)] interface nsIVerifySignedManifestCallback : nsISupports { @@ -46,7 +39,7 @@ interface nsIVerifySignedManifestCallback : nsISupports * This represents a service to access and manipulate * X.509 certificates stored in a database. */ -[scriptable, uuid(6d27211b-7119-4777-9c62-f29310eeb262)] +[scriptable, uuid(560bc9ac-3e71-472e-9b08-2270d0c71878)] interface nsIX509CertDB : nsISupports { /** @@ -324,22 +317,6 @@ interface nsIX509CertDB : nsISupports { in nsIFile aJarFile, in nsIOpenSignedAppFileCallback callback); - /** - * Verifies the signature on a directory representing an unpacked signed - * JAR file. To be considered valid, there must be exactly one signature - * on the directory structure and that signature must have signed every - * entry. Further, the signature must come from a certificate that - * is trusted for code signing. - * - * On success NS_OK and the trusted certificate that signed the - * unpacked JAR are returned. - * - * On failure, an error code is returned. - */ - void verifySignedDirectoryAsync(in AppTrustedRoot trustedRoot, - in nsIFile aUnpackedDir, - in nsIVerifySignedDirectoryCallback callback); - /** * Given streams containing a signature and a manifest file, verifies * that the signature is valid for the manifest. The signature must diff --git a/security/manager/ssl/tests/unit/test_signed_apps/sslcontrol.xpi b/security/manager/ssl/tests/unit/test_signed_apps/sslcontrol.xpi deleted file mode 100644 index c212c70eebb9..000000000000 Binary files a/security/manager/ssl/tests/unit/test_signed_apps/sslcontrol.xpi and /dev/null differ diff --git a/security/manager/ssl/tests/unit/test_signed_dir.js b/security/manager/ssl/tests/unit/test_signed_dir.js deleted file mode 100644 index 4c37374edbac..000000000000 --- a/security/manager/ssl/tests/unit/test_signed_dir.js +++ /dev/null @@ -1,173 +0,0 @@ -"use strict"; - -Components.utils.import("resource://gre/modules/ZipUtils.jsm"); - -do_get_profile(); // must be called before getting nsIX509CertDB -const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB); - -var gSignedXPI = do_get_file("test_signed_apps/sslcontrol.xpi", false); -var gTarget = FileUtils.getDir("TmpD", ["test_signed_dir"]); -gTarget.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); - - -// tamper data structure, each element is optional -// { copy: [[path,newname], [path2,newname2], ...], -// delete: [path, path2, ...], -// corrupt: [path, path2, ...] -// } - -function prepare(tamper) { - ZipUtils.extractFiles(gSignedXPI, gTarget); - - // copy files - if (tamper.copy) { - tamper.copy.forEach(i => { - let f = gTarget.clone(); - i[0].split('/').forEach(seg => {f.append(seg);}); - f.copyTo(null, i[1]); - }); - } - - // delete files - if (tamper.delete) { - tamper.delete.forEach(i => { - let f = gTarget.clone(); - i.split('/').forEach(seg => {f.append(seg);}); - f.remove(true); - }); - } - - // corrupt files - if (tamper.corrupt) { - tamper.corrupt.forEach(i => { - let f = gTarget.clone(); - i.split('/').forEach(seg => {f.append(seg);}); - let s = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY); - const str = "Kilroy was here"; - s.write(str, str.length); - s.close(); - }); - } - - return gTarget; -} - - -function check_result(name, expectedRv, dir) { - return function verifySignedDirCallback(rv, aSignerCert) { - equal(rv, expectedRv, name + " rv:"); - equal(aSignerCert != null, Components.isSuccessCode(expectedRv), - "expecting certificate:"); - // cleanup and kick off next test - dir.remove(true); - run_next_test(); - }; -} - -function verifyDirAsync(name, expectedRv, tamper) { - let targetDir = prepare(tamper); - certdb.verifySignedDirectoryAsync( - Ci.nsIX509CertDB.AddonsPublicRoot, targetDir, - check_result(name, expectedRv, targetDir)); -} - - -// -// the tests -// - -add_test(function() { - verifyDirAsync("'valid'", Cr.NS_OK, {} /* no tampering */ ); -}); - -add_test(function() { - verifyDirAsync("'no meta dir'", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, - {delete: ["META-INF"]}); -}); - -add_test(function() { - verifyDirAsync("'empty meta dir'", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, - {delete: ["META-INF/mozilla.rsa", - "META-INF/mozilla.sf", - "META-INF/manifest.mf"]}); -}); - -add_test(function() { - verifyDirAsync("'two rsa files'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, - {copy: [["META-INF/mozilla.rsa","extra.rsa"]]}); -}); - -add_test(function() { - verifyDirAsync("'corrupt rsa file'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, - {corrupt: ["META-INF/mozilla.rsa"]}); -}); - -add_test(function() { - verifyDirAsync("'missing sf file'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, - {delete: ["META-INF/mozilla.sf"]}); -}); - -add_test(function() { - verifyDirAsync("'corrupt sf file'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, - {corrupt: ["META-INF/mozilla.sf"]}); -}); - -add_test(function() { - verifyDirAsync("'extra .sf file (invalid)'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, - {copy: [["META-INF/mozilla.rsa","extra.sf"]]}); -}); - -add_test(function() { - verifyDirAsync("'extra .sf file (valid)'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, - {copy: [["META-INF/mozilla.sf","extra.sf"]]}); -}); - -add_test(function() { - verifyDirAsync("'missing manifest'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, - {delete: ["META-INF/manifest.mf"]}); -}); - -add_test(function() { - verifyDirAsync("'corrupt manifest'", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, - {corrupt: ["META-INF/manifest.mf"]}); -}); - -add_test(function() { - verifyDirAsync("'missing file'", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, - {delete: ["bootstrap.js"]}); -}); - -add_test(function() { - verifyDirAsync("'corrupt file'", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, - {corrupt: ["bootstrap.js"]}); -}); - -add_test(function() { - verifyDirAsync("'extra file'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, - {copy: [["bootstrap.js","extra"]]}); -}); - -add_test(function() { - verifyDirAsync("'missing file in dir'", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, - {delete: ["content/options.xul"]}); -}); - -add_test(function() { - verifyDirAsync("'corrupt file in dir'", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, - {corrupt: ["content/options.xul"]}); -}); - -add_test(function() { - verifyDirAsync("'extra file in dir'", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, - {copy: [["content/options.xul","extra"]]}); -}); - -do_register_cleanup(function() { - if (gTarget.exists()) { - gTarget.remove(true); - } -}); - -function run_test() { - run_next_test(); -} \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 2e340c8c210b..4f0aedf9a797 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -70,7 +70,6 @@ run-sequentially = hardcoded ports [test_cert_version.js] [test_signed_apps.js] [test_signed_apps-marketplace.js] -[test_signed_dir.js] [test_cert_eku-CA_EP.js] [test_cert_eku-CA_EP_NS_OS_SA_TS.js]