From 4ed4e15d17db650c38b91c663744eb2aaf40fabe Mon Sep 17 00:00:00 2001 From: "mstoltz%netscape.com" Date: Mon, 14 Feb 2000 01:57:01 +0000 Subject: [PATCH] Crasher fixes and optimizations to jar signature verification. bug=7270 r=norris git-svn-id: svn://10.0.0.236/trunk@60679 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/modules/libjar/nsIZipReader.idl | 19 +- mozilla/modules/libjar/nsJAR.cpp | 535 ++++++++++---------- mozilla/modules/libjar/nsJAR.h | 90 +--- mozilla/modules/libjar/nsJARInputStream.cpp | 52 +- mozilla/modules/libjar/nsJARInputStream.h | 6 +- mozilla/modules/libjar/nsJARStubs.cpp | 14 +- mozilla/modules/libjar/nsZipArchive.cpp | 153 ++---- mozilla/modules/libjar/nsZipArchive.h | 12 +- 8 files changed, 350 insertions(+), 531 deletions(-) diff --git a/mozilla/modules/libjar/nsIZipReader.idl b/mozilla/modules/libjar/nsIZipReader.idl index 580a2a00fef..2b09ec43b60 100644 --- a/mozilla/modules/libjar/nsIZipReader.idl +++ b/mozilla/modules/libjar/nsIZipReader.idl @@ -90,23 +90,12 @@ interface nsIZipReader : nsISupports /** * Returns an object describing the owner (origin and/or signer) of * an entry. parseManifest must be called first. If aEntryName is an - * entry in the jar, it must have been read to its end by an input - * stream returned through getInputStream. If aEntryName is an external - * file with meta-information stored in the jar, verifyExternalFile - * must be called before getPrincipal. + * entry in the jar, getInputStream must be called after parseManifest. + * If aEntryName is an external file which has meta-information + * stored in the jar, verifyExternalFile (not yet implemented) must + * be called before getPrincipal. */ nsIPrincipal getPrincipal(in string aEntryName); - - /** - * Compares a file which is not in the jar to meta-information - * about the file which is stored in the jar. Must be called after - * parseManifest and before - * calling getPrincipal on an external file (such as inline Javascript). - * There must be an "external" entry in the manifest corresponding to - * aFilename. - */ - void verifyExternalFile(in string aFilename, in string aBuf, - in unsigned long aBufLength); }; %{C++ diff --git a/mozilla/modules/libjar/nsJAR.cpp b/mozilla/modules/libjar/nsJAR.cpp index a30b10f0cdb..88359ac9cdd 100644 --- a/mozilla/modules/libjar/nsJAR.cpp +++ b/mozilla/modules/libjar/nsJAR.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -31,7 +31,9 @@ #include "nsJAR.h" #include "nsXPIDLString.h" - +//---------------------------------------------- +// Errors and other utility definitions +//---------------------------------------------- #ifndef __gen_nsIFile_h__ #define NS_ERROR_FILE_UNRECONGNIZED_PATH NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 1) #define NS_ERROR_FILE_UNRESOLVABLE_SYMLINK NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 2) @@ -69,17 +71,84 @@ ziperr2nsresult(PRInt32 ziperr) _ptr = nsnull; \ } -//-- Used by the JAR hashtables to delete their entries. +//---------------------------------------------- +// nsJARManifestItem declaration +//---------------------------------------------- +/* + * nsJARManifestItem contains meta-information pertaining + * to an individual JAR entry, taken from the + * META-INF/MANIFEST.MF and META-INF/ *.SF files. + * This is security-critical information, defined here so it is not + * accessible from anywhere else. + */ +typedef enum +{ + JAR_INVALID = 1, + JAR_GLOBAL = 2, + JAR_INTERNAL = 3, + JAR_EXTERNAL = 4 +} JARManifestItemType; + +// Use this macro to look up global data (from the manifest file header) +#define JAR_GLOBALMETA "" + +class nsJARManifestItem +{ +public: + JARManifestItemType mType; + + // the entity which signed this item + nsIPrincipal* mPrincipal; + + // True if the second step of verification (VerifyEntryDigests) + // has taken place: + PRBool step2Complete; + + // True unless one or more verification steps failed + PRBool valid; + + // Internal storage of digests + char* calculatedSectionDigest; + char* storedEntryDigest; + + nsJARManifestItem(); + virtual ~nsJARManifestItem(); +}; + +//------------------------------------------------- +// nsJARManifestItem constructors and destructor +//------------------------------------------------- +nsJARManifestItem::nsJARManifestItem(): mType(JAR_INTERNAL), + mPrincipal(nsnull), + step2Complete(PR_FALSE), + valid(PR_TRUE), + calculatedSectionDigest(nsnull), + storedEntryDigest(nsnull) +{ +} + +nsJARManifestItem::~nsJARManifestItem() +{ + // Delete digests if necessary + PR_FREEIF(calculatedSectionDigest); + PR_FREEIF(storedEntryDigest); + NS_IF_RELEASE(mPrincipal); +} + +//---------------------------------------------- +// nsJAR constructor/destructor +//---------------------------------------------- PR_STATIC_CALLBACK(PRBool) DeleteManifestEntry(nsHashKey* aKey, void* aData, void* closure) { +//-- deletes an entry in mManifestData. PR_FREEIF(aData); return PR_TRUE; } // The following initialization makes a guess of 25 entries per jarfile. nsJAR::nsJAR(): mManifestData(nsnull, nsnull, DeleteManifestEntry, nsnull, 25), - manifestParsed(PR_FALSE), + step1Complete(PR_FALSE), mVerificationService(nsnull) { NS_INIT_REFCNT(); @@ -92,6 +161,9 @@ nsJAR::~nsJAR() NS_IMPL_ISUPPORTS1(nsJAR, nsIZipReader); +//---------------------------------------------- +// nsJAR public implementation +//---------------------------------------------- NS_IMETHODIMP nsJAR::Init(nsIFile* zipFile) { @@ -156,9 +228,9 @@ nsJAR::GetEntry(const char *zipEntry, nsIZipEntry* *result) } NS_IMETHODIMP -nsJAR::FindEntries(const char *aPattern, nsISimpleEnumerator **_retval) +nsJAR::FindEntries(const char *aPattern, nsISimpleEnumerator **result) { - if (!_retval) + if (!result) return NS_ERROR_INVALID_POINTER; nsZipFind *find = mZip.FindInit(aPattern); @@ -170,18 +242,20 @@ nsJAR::FindEntries(const char *aPattern, nsISimpleEnumerator **_retval) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF( zipEnum ); - *_retval = zipEnum; + *result = zipEnum; return NS_OK; } NS_IMETHODIMP -nsJAR::GetInputStream(const char *aFilename, nsIInputStream **_retval) +nsJAR::GetInputStream(const char *aFilename, nsIInputStream **result) { - if (!_retval) + if (!result) return NS_OK; - return CreateInputStream(aFilename, this, _retval); + return CreateInputStream(aFilename, this, result); } +//-- The following #defines are used by ParseManifest() +// and ParseOneFile(). The header strings are defined in the JAR specification. #define JAR_MF 1 #define JAR_SF 2 #define JAR_MF_SEARCH_STRING "(M|/M)ETA-INF/(M|m)(ANIFEST|anifest).(MF|mf)$" @@ -192,28 +266,31 @@ nsJAR::GetInputStream(const char *aFilename, nsIInputStream **_retval) NS_IMETHODIMP nsJAR::ParseManifest() { - if (manifestParsed || !SupportsRSAVerification()) + //-- Verification Step 1 + if (step1Complete || !SupportsRSAVerification()) return NS_OK; nsresult rv; + nsCOMPtr files; + nsCOMPtr file; char* manifestBuffer = nsnull; char* rsaBuffer = nsnull; PRUint32 rsaLen; - nsCOMPtr files; - nsCOMPtr file; nsXPIDLCString manifestFilename; + nsCAutoString rsaFilename; + nsCOMPtr principal; //-- (1)Manifest (MF) file rv = FindEntries(JAR_MF_SEARCH_STRING, getter_AddRefs(files)); - if (NS_FAILED(rv) || !files) goto cleanup; + if (!files) rv = NS_ERROR_FAILURE; + if (NS_FAILED(rv)) goto cleanup; //-- Load the file into memory rv = files->GetNext(getter_AddRefs(file)); - if (NS_FAILED(rv)) goto cleanup; - if (!file) { rv = NS_ERROR_FILE_CORRUPTED; goto cleanup; } // No MF file + if (NS_FAILED(rv) || !file) goto cleanup; PRBool more; rv = files->HasMoreElements(&more); - if (NS_FAILED(rv)) return rv; - if (more) { rv = NS_ERROR_FILE_CORRUPTED; goto cleanup; } // More than one MF file + if (NS_FAILED(rv)) goto cleanup; + if (more) { rv = NS_ERROR_FILE_CORRUPTED; goto cleanup; } // More than one MF file rv = file->GetName(getter_Copies(manifestFilename)); if (!manifestFilename || NS_FAILED(rv)) return rv; rv = LoadEntry(manifestFilename, (const char**)&manifestBuffer); @@ -223,159 +300,92 @@ nsJAR::ParseManifest() rv = ParseOneFile(manifestBuffer, JAR_MF); if (NS_FAILED(rv)) goto cleanup; JAR_NULLFREE(manifestBuffer) + DumpMetadata("PM Pass 1 End"); //-- (2)Signature (SF) file - // For now, we only read one SF file, even if the jar has multiple signatures. + // If there are multiple signatures, we select one at random. rv = FindEntries(JAR_SF_SEARCH_STRING, getter_AddRefs(files)); - if (NS_FAILED(rv) || !files) goto cleanup; - - { - //-- Get an SF file - rv = files->GetNext(getter_AddRefs(file)); - if (NS_FAILED(rv) || !file) goto cleanup; - rv = file->GetName(getter_Copies(manifestFilename)); - if (NS_FAILED(rv)) goto cleanup; - - rv = LoadEntry(manifestFilename, (const char**)&manifestBuffer); - if (NS_FAILED(rv)) goto cleanup; - - //-- Get its corresponding RSA file - nsCAutoString rsaFilename(manifestFilename); - PRInt32 extension = rsaFilename.RFindChar('.') + 1; - NS_ASSERTION(extension != 0, "Manifest Parser: Missing file extension."); - (void)rsaFilename.Cut(extension, 2); - (void)rsaFilename.Append("rsa"); - rv = LoadEntry(rsaFilename, (const char**)&rsaBuffer, &rsaLen); - if (NS_FAILED(rv)) // Try uppercase - { - JAR_NULLFREE(rsaBuffer) + if (!files) rv = NS_ERROR_FAILURE; + if (NS_FAILED(rv)) goto cleanup; + //-- Get an SF file + rv = files->GetNext(getter_AddRefs(file)); + if (NS_FAILED(rv) || !file) goto cleanup; + rv = file->GetName(getter_Copies(manifestFilename)); + if (NS_FAILED(rv)) goto cleanup; + + rv = LoadEntry(manifestFilename, (const char**)&manifestBuffer); + if (NS_FAILED(rv)) goto cleanup; + + //-- Get its corresponding RSA file + rsaFilename = manifestFilename; + PRInt32 extension = rsaFilename.RFindChar('.') + 1; + NS_ASSERTION(extension != 0, "Manifest Parser: Missing file extension."); + (void)rsaFilename.Cut(extension, 2); + (void)rsaFilename.Append("rsa"); + rv = LoadEntry(rsaFilename, (const char**)&rsaBuffer, &rsaLen); + if (NS_FAILED(rv)) // Try uppercase + { + JAR_NULLFREE(rsaBuffer) (void)rsaFilename.Cut(extension, 3); - (void)rsaFilename.Append("RSA"); - rv = LoadEntry(rsaFilename, (const char**)&rsaBuffer, &rsaLen); - } - if (NS_FAILED(rv)) goto cleanup; - - //-- Verify that the RSA file is a valid signature of the SF file - nsCOMPtr principal; - rv = VerifySignature(manifestBuffer, rsaBuffer, rsaLen, getter_AddRefs(principal)); - if (NS_FAILED(rv)) goto cleanup; - JAR_NULLFREE(rsaBuffer); - - //-- Parse the SF file. If the verification above failed, principal - // is null, and ParseOneFile will mark the relevant entries as invalid. - // if ParseOneFile fails, then it has no effect, and we can safely - // continue to the next SF file, or return. - rv = ParseOneFile(manifestBuffer, JAR_SF, principal); - JAR_NULLFREE(manifestBuffer) - // End of signature file parsing + (void)rsaFilename.Append("RSA"); + rv = LoadEntry(rsaFilename, (const char**)&rsaBuffer, &rsaLen); } + if (NS_FAILED(rv)) goto cleanup; + + //-- Verify that the RSA file is a valid signature of the SF file + rv = VerifySignature(manifestBuffer, rsaBuffer, rsaLen, getter_AddRefs(principal)); + if (NS_FAILED(rv)) goto cleanup; + JAR_NULLFREE(rsaBuffer); + + //-- Parse the SF file. If the verification above failed, principal + // is null, and ParseOneFile will mark the relevant entries as invalid. + // if ParseOneFile fails, then it has no effect, and we can safely + // continue to the next SF file, or return. + ParseOneFile(manifestBuffer, JAR_SF, principal); + JAR_NULLFREE(manifestBuffer) + DumpMetadata("PM Pass 2 End"); + // End of signature file parsing cleanup: - DumpMetadata(); JAR_NULLFREE(manifestBuffer) JAR_NULLFREE(rsaBuffer) - manifestParsed = NS_SUCCEEDED(rv); + step1Complete = NS_SUCCEEDED(rv); return rv; } -NS_IMETHODIMP -nsJAR::GetPrincipal(const char* aEntryName, nsIPrincipal** _retval) +NS_IMETHODIMP +nsJAR::GetPrincipal(const char* aFilename, nsIPrincipal** result) { - // Check that verification is supported and manifest has been parsed - if (!SupportsRSAVerification() || !manifestParsed) - return NS_ERROR_FAILURE; - // Parameter check - if (!_retval) + //-- Parameter check + if (!aFilename) + return NS_ERROR_ILLEGAL_VALUE; + if (!result) return NS_ERROR_NULL_POINTER; - if(!aEntryName) // Caller wants global file principal - aEntryName = JAR_GLOBALMETA; - - nsStringKey key(aEntryName); - nsJARManifestItem* manItem = (nsJARManifestItem*)mManifestData.Get(&key); - if (!manItem) return NS_ERROR_FAILURE; - - //-- Verify the file digest if we haven't done so already. - if (manItem->mType != JAR_GLOBAL && (!manItem->verifyComplete)) - { - if (manItem->hasPrincipal() && manItem->valid) - { - //-- Check if entry digests have been calculated (by nsJARInputStream) - if (!manItem->entryDigestsCalculated) - return NS_ERROR_FAILURE; - - PRBool foundDigest = PR_FALSE; - for (PRInt32 a=0; avalid = manItem->valid && - ( (!manItem->storedEntryDigests[a]) || - PL_strcmp(manItem->storedEntryDigests[a], - manItem->calculatedEntryDigests[a]) == 0 ); - foundDigest = foundDigest || (manItem->storedEntryDigests[a]); - } - if (!foundDigest) - // No digests in manifest file. Entry is valid but has no owner - manItem->valid = PR_TRUE; - if (!manItem->valid) - manItem->setPrincipal(nsnull); - } - // Free up stored digests - for(PRInt32 b = 0; bcalculatedEntryDigests[b]) - JAR_NULLFREE(manItem->storedEntryDigests[b]) - } - manItem->verifyComplete = PR_TRUE; - } - - if (manItem && manItem->valid) - { - manItem->getPrincipal(_retval); - return NS_OK; - } - else if (!manItem) - { - *_retval = nsnull; - return NS_OK; - } - else // Not valid - return NS_ERROR_FAILURE; // XXX: Should this be a security error? -} - -NS_IMETHODIMP -nsJAR::VerifyExternalFile(const char* aFilename, const char* aBuf, - PRUint32 aLen) -{ - //-- Check if verification is supported - if (!SupportsRSAVerification()) - return NS_OK; - //-- Parameter check - if (!aFilename || !aBuf) - return NS_ERROR_ILLEGAL_VALUE; - + DumpMetadata("GetPrincipal"); + //-- Find the item nsStringKey key(aFilename); nsJARManifestItem* manItem = (nsJARManifestItem*)mManifestData.Get(&key); - if (!manItem) - return NS_ERROR_FAILURE; - if (!manItem->entryDigestsCalculated) + if(!manItem) { - nsresult rv; - for (PRInt32 b=0; bcalculatedEntryDigests[b])); - } - if (NS_SUCCEEDED(rv)) - manItem->entryDigestsCalculated = PR_TRUE; - } - if (manItem->entryDigestsCalculated) + *result = nsnull; return NS_OK; + } + + NS_ASSERTION(step1Complete && manItem->step2Complete, + "Attempt to get principal before verifying signature."); + + if (step1Complete && manItem->step2Complete && manItem->valid) + { + *result = manItem->mPrincipal; + NS_IF_ADDREF(*result); + return NS_OK; + } else + { + *result = nsnull; return NS_ERROR_FAILURE; + } } //---------------------------------------------- @@ -398,7 +408,8 @@ nsJAR::CreateInputStream(const char* aFilename, nsJAR* aJAR, nsIInputStream **is return NS_OK; } -nsresult nsJAR::LoadEntry(const char* aFilename, const char** aBuf, +nsresult +nsJAR::LoadEntry(const char* aFilename, const char** aBuf, PRUint32* aBufLen) { //-- Get a stream for reading the manifest file @@ -412,7 +423,7 @@ nsresult nsJAR::LoadEntry(const char* aFilename, const char** aBuf, PRUint32 len; rv = manifestStream->Available(&len); if (NS_FAILED(rv)) return rv; - buf = (char*)PR_MALLOC(len); + buf = (char*)PR_MALLOC(len+1); if (!buf) return NS_ERROR_OUT_OF_MEMORY; PRUint32 bytesRead; rv = manifestStream->Read(buf, len, &bytesRead); @@ -420,6 +431,7 @@ nsresult nsJAR::LoadEntry(const char* aFilename, const char** aBuf, rv = NS_ERROR_FILE_CORRUPTED; if (NS_FAILED(rv)) return rv; (void)manifestStream->Close(); + buf[len] = '\0'; //Null-terminate the buffer *aBuf = buf; if (aBufLen) *aBufLen = len; @@ -427,7 +439,8 @@ nsresult nsJAR::LoadEntry(const char* aFilename, const char** aBuf, } -PRInt32 nsJAR::ReadLine(const char** src) +PRInt32 +nsJAR::ReadLine(const char** src) { //--Moves pointer to beginning of next line and returns line length // not including CR/LF. @@ -453,9 +466,9 @@ PRInt32 nsJAR::ReadLine(const char** src) return length; } -nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, - nsIPrincipal* aPrincipal, - PRBool aLastSFFile) // Allows us to free up resources +nsresult +nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, + nsIPrincipal* aPrincipal) { //-- Set up parsing variables nsJARManifestItem* curItem; @@ -471,7 +484,7 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, const char* nextLineStart = filebuf; nsCAutoString curLine; PRInt32 linelen; - nsCAutoString storedSectionDigests[JAR_DIGEST_COUNT]; + nsCAutoString storedSectionDigest; //-- Check file header linelen = ReadLine(&nextLineStart); @@ -492,7 +505,7 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, { if (curItem->mType != JAR_INVALID) { - //-- Did this section have a name: line (or the global?) + //-- Did this section have a name: line? if(!foundName) curItem->mType = JAR_INVALID; else @@ -513,20 +526,14 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, } } - if (curItem->mType == JAR_INVALID) + if (curItem->mType == JAR_INVALID || + curItem->mType == JAR_GLOBAL) delete curItem; - else //-- calculate section digests + else //-- calculate section digest { PRUint32 sectionLength = curPos - sectionStart; - for (PRInt32 d=0; dcalculatedSectionDigests[d])); - } + CalculateDigest(sectionStart, sectionLength, + &(curItem->calculatedSectionDigest)); //-- Save item in the hashtable nsStringKey itemKey(curItemName); mManifestData.Put(&itemKey, (void*)curItem); @@ -548,31 +555,25 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, curSFItem = (nsJARManifestItem*)mManifestData.Get(&key); if(curSFItem) { - PRBool valid = PR_TRUE; if (aPrincipal == nsnull) // SF file failed verification - valid = PR_FALSE; - else // Compare calculated digests + curSFItem->valid = PR_FALSE; + else // Compare digests { - PRBool foundDigest = PR_FALSE; - for (int a=0; a 0) { - valid = valid && - ( (storedSectionDigests[a].Length() == 0) || - storedSectionDigests[a] == - (const char*)curSFItem->calculatedSectionDigests[a] ); - foundDigest = foundDigest || (storedSectionDigests[a].Length() != 0); + if (storedSectionDigest != + (const char*)curSFItem->calculatedSectionDigest) + curSFItem->valid = PR_FALSE; + else + { + curSFItem->mPrincipal = aPrincipal; + NS_ADDREF(curSFItem->mPrincipal); + } + JAR_NULLFREE(curSFItem->calculatedSectionDigest) + storedSectionDigest = ""; } - if (foundDigest && valid) - { - curSFItem->setPrincipal(aPrincipal); - } - } - curSFItem->valid = valid; - if (aLastSFFile) - // Free the calculated digests; we won't need them again. - for(PRInt16 countB=0; countBcalculatedSectionDigests[countB]) + } // (aPrincipal != nsnull) } // if(curSFItem) } // if(foundName) @@ -589,7 +590,7 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, { curPos = nextLineStart; PRInt32 continuationLen = ReadLine(&nextLineStart) - 1; - nsAutoString continuation(curPos+1, continuationLen); + nsCAutoString continuation(curPos+1, continuationLen); curLine += continuation; linelen += continuationLen; } @@ -605,36 +606,24 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, curLine.Mid(lineData, colonPos+2, linelen - (colonPos+2)); //-- Lines to look for: - // (1) *-Digest: - if (lineName.Find("-Digest",PR_TRUE) != -1) + // (1) Digest: + if (lineName.Compare("SHA1-Digest",PR_TRUE) == 0) //-- This is a digest line, save the data in the appropriate place { - PRInt32 digest; - //** Note that in the following comparisons, the third argument to - // Compare MUST be the length of the first argument. ** - if (lineName.Compare("MD5-", PR_TRUE, 4) == 0) - digest = JAR_MD5; - else if ( lineName.Compare("SHA1-", PR_TRUE, 5) == 0 || - lineName.Compare("SHA-", PR_TRUE, 4) == 0 ) - digest = JAR_SHA1; - else // Unsupported algorithm - continue; - if(aFileType == JAR_MF) { - curItem->storedEntryDigests[digest] = - (char*)PR_MALLOC(lineData.Length()+1); - if (!(curItem->storedEntryDigests[digest])) - return NS_ERROR_OUT_OF_MEMORY; - (void)PL_strcpy(curItem->storedEntryDigests[digest], lineData); + curItem->storedEntryDigest = (char*)PR_MALLOC(lineData.Length()+1); + if (!(curItem->storedEntryDigest)) + continue; // Out of memory, just skip this line instead of returning. + PL_strcpy(curItem->storedEntryDigest, lineData); } else - storedSectionDigests[digest] = lineData; + storedSectionDigest = lineData; continue; } // (2) Name: associates this manifest section with a file in the jar. - if (lineName.Compare("Name", PR_TRUE) == 0) + if (!foundName && lineName.Compare("Name", PR_TRUE) == 0) { if (!(aFileType == JAR_MF && curItem->mType == JAR_GLOBAL)) curItemName = lineData; @@ -645,8 +634,8 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, // (3) Magic: this may be an inline Javascript. // We can't do any other kind of magic. if ( aFileType == JAR_MF && - lineName.Compare("Magic", PR_TRUE) == 0 && - curItem->mType != JAR_GLOBAL ) + curItem->mType == JAR_INTERNAL && + lineName.Compare("Magic", PR_TRUE) == 0) { if(lineData.Compare("javascript",PR_TRUE) == 0) curItem->mType = JAR_EXTERNAL; @@ -659,6 +648,53 @@ nsresult nsJAR::ParseOneFile(const char* filebuf, PRInt16 aFileType, return NS_OK; } //ParseOneFile() +nsresult +nsJAR::VerifyEntry(const char* aEntryName, char* aEntryData, + PRUint32 aLen) +{ + //-- Verification Step 2 + // Check that verification is supported and step 1 has been done + + if (!SupportsRSAVerification()) return NS_OK; + NS_ASSERTION(step1Complete, + "Verification step 2 called before step 1 complete"); + if (!step1Complete) return NS_ERROR_FAILURE; + + //-- Get the manifest item + nsStringKey key(aEntryName); + nsJARManifestItem* manItem = (nsJARManifestItem*)mManifestData.Get(&key); + if (!manItem) + return NS_OK; + if (manItem->mPrincipal && manItem->valid) + { + if(!manItem->storedEntryDigest) + { // No entry digests in manifest file. Entry is unsigned. + NS_IF_RELEASE(manItem->mPrincipal); + manItem->mPrincipal = nsnull; + } + else + { //-- Calculate and compare digests + char* calculatedEntryDigest; + nsresult rv = CalculateDigest(aEntryData, aLen, &calculatedEntryDigest); + if (NS_FAILED(rv)) return rv; + if (PL_strcmp(manItem->storedEntryDigest, calculatedEntryDigest) != 0) + { + manItem->valid = PR_FALSE; + NS_IF_RELEASE(manItem->mPrincipal); + manItem->mPrincipal = nsnull; + } + JAR_NULLFREE(calculatedEntryDigest) + JAR_NULLFREE(manItem->storedEntryDigest) + NS_IF_RELEASE(mVerificationService); + mVerificationService = nsnull; + } + } + + manItem->step2Complete = PR_TRUE; + DumpMetadata("VerifyEntry end"); + return NS_OK; +} + //---------------------------------------------- // Debugging functions //---------------------------------------------- @@ -671,8 +707,10 @@ PrintManItem(nsHashKey* aKey, void* aData, void* closure) if (manItem) { nsStringKey* key2 = (nsStringKey*)aKey; - printf("------------\nName:%s.\n",key2->GetString().ToNewCString()); + char* name = key2->GetString().ToNewCString(); + if (PL_strcmp(name, "") != 0) { + printf("------------\nName:%s.\n",name); if (manItem->mPrincipal) { char* toStr; @@ -683,25 +721,26 @@ PrintManItem(nsHashKey* aKey, void* aData, void* closure) } else printf("No Principal.\n"); - printf("verifyComplete:%i.\n",manItem->verifyComplete); + printf("step2Complete:%i.\n",manItem->step2Complete); printf("valid:%i.\n",manItem->valid); - printf("entryDigestsCalculated:%i.\n",manItem->entryDigestsCalculated); + /* for (PRInt32 x=0; xcalculatedSectionDigests[x]); for (PRInt32 y=0; ystoredEntryDigests[y]); + */ } } return PR_TRUE; } #endif -void nsJAR::DumpMetadata() +void nsJAR::DumpMetadata(const char* aMessage) { #if 0 - printf("######## nsJAR::DumpMetadata Start ############\n"); + printf("### nsJAR::DumpMetadata at %s ###\n", aMessage); mManifestData.Enumerate(PrintManItem); printf("######## nsJAR::DumpMetadata End ############\n"); #endif @@ -710,7 +749,6 @@ void nsJAR::DumpMetadata() //---------------------------------------------- // nsJAREnumerator constructor and destructor //---------------------------------------------- - nsJAREnumerator::nsJAREnumerator(nsZipFind *aFind) : mFind(aFind), mCurr(nsnull), @@ -793,13 +831,9 @@ nsJAREnumerator::GetNext(nsISupports** aResult) return NS_ERROR_OUT_OF_MEMORY; } - - - //------------------------------------------------- // nsJARItem constructors and destructor //------------------------------------------------- - nsJARItem::nsJARItem() { NS_INIT_ISUPPORTS(); @@ -897,60 +931,3 @@ nsJARItem::GetCRC32(PRUint32 *aCrc32) return NS_OK; } - -//------------------------------------------------- -// nsJARManifestItem constructors and destructor -//------------------------------------------------- -nsJARManifestItem::nsJARManifestItem(): mType(JAR_INTERNAL), - verifyComplete(PR_FALSE), - valid(PR_TRUE), - entryDigestsCalculated(PR_FALSE), - mPrincipal(nsnull) -{ - // Initialize digests to null - for(PRInt32 count = 0; count mZipFile; - nsZipArchive mZip; - nsObjectHashtable mManifestData; - PRBool manifestParsed; + nsCOMPtr mZipFile; // The zip/jar file on disk + nsZipArchive mZip; // The underlying zip archive + nsObjectHashtable mManifestData; // Stores metadata for each entry + PRBool step1Complete; // True if manifest has been parsed nsISupports* mVerificationService; //-- Private functions - nsresult CreateInputStream(const char* aFilename, nsJAR* aJAR, nsIInputStream** is); + nsresult CreateInputStream(const char* aFilename, nsJAR* aJAR, + nsIInputStream** is); nsresult LoadEntry(const char* aFilename, const char** aBuf, PRUint32* aBufLen = nsnull); PRInt32 ReadLine(const char** src); nsresult ParseOneFile(const char* filebuf, PRInt16 aFileType, - nsIPrincipal* aPrincipal = nsnull, - PRBool aLastSFFile = PR_TRUE); + nsIPrincipal* aPrincipal = nsnull); + nsresult VerifyEntry(const char* aEntryName, char* aEntryData, + PRUint32 aLen); + + //-- Debugging + void DumpMetadata(const char* aMessage); //-- The following private functions are implemented in nsJARStubs.cpp static PRBool SupportsRSAVerification(); - nsresult DigestBegin(PRUint32* id, PRInt32 alg); - nsresult DigestData(PRUint32 id, const char* data, PRUint32 length); - nsresult CalculateDigest(PRUint32 id, char** digest); + nsresult CalculateDigest(const char* aInBuf, PRUint32 aInBufLen, + char** digest); nsresult VerifySignature(const char* sfBuf, const char* rsaBuf, PRUint32 rsaBufLen, nsIPrincipal** aPrincipal); - - void DumpMetadata(); // Debugging }; /** @@ -130,59 +125,6 @@ public: nsZipItem* mZipItem; }; -/** - * nsJARManifestItem - * - * Meta-information pertaining to an individual JAR entry, taken - * from the META-INF/MANIFEST.MF and META-INF/ *.SF files. Ideally, - * this would be part of nsJARItem, but nsJARItems are stored in the - * underlying nsZipArchive which doesn't know about metadata. - */ -typedef enum -{ - JAR_INVALID = 1, - JAR_GLOBAL = 2, - JAR_INTERNAL = 3, - JAR_EXTERNAL = 4 - // Maybe we'll want jar files to sign URL's eventually. -} JARManifestItemType; - -// Use this macro to look up global data (from the manifest file header) -#define JAR_GLOBALMETA "" - -class nsJARManifestItem -{ -public: - JARManifestItemType mType; - - // True if both steps of verification have taken place: - // ParseManifest and reading the item into memory via GetInputStream - PRBool verifyComplete; - - // True unless one or more verification steps failed - PRBool valid; - - // Internal storage of digests - char* calculatedSectionDigests[JAR_DIGEST_COUNT]; - char* calculatedEntryDigests[JAR_DIGEST_COUNT]; - char* storedEntryDigests[JAR_DIGEST_COUNT]; - PRBool entryDigestsCalculated; - - void setPrincipal(nsIPrincipal *aPrincipal); - - void getPrincipal(nsIPrincipal **result); - - PRBool hasPrincipal(); - - nsJARManifestItem(); - virtual ~nsJARManifestItem(); - -private: - // the entity which signed this item - nsIPrincipal* mPrincipal; - -}; - /** * nsJAREnumerator * diff --git a/mozilla/modules/libjar/nsJARInputStream.cpp b/mozilla/modules/libjar/nsJARInputStream.cpp index 0eb69397146..c9d3e719a38 100644 --- a/mozilla/modules/libjar/nsJARInputStream.cpp +++ b/mozilla/modules/libjar/nsJARInputStream.cpp @@ -46,48 +46,18 @@ nsJARInputStream::Available(PRUint32 *_retval) } NS_IMETHODIMP -nsJARInputStream::Read(char* buf, PRUint32 count, PRUint32 *_retval) +nsJARInputStream::Read(char* buf, PRUint32 count, PRUint32 *bytesRead) { if (mZip == nsnull) { - *_retval = 0; + *bytesRead = 0; return NS_OK; } - if( (mZip->Read(mReadInfo, buf, count, _retval)) != ZIP_OK ) + if( (mZip->Read(mReadInfo, buf, count, bytesRead)) != ZIP_OK ) return NS_ERROR_FAILURE; - - // Pass the buffer on to the JAR parser - if (mJAR && mJAR->SupportsRSAVerification()) - { - nsStringKey key(mEntryName); - nsJARManifestItem* manItem = - (nsJARManifestItem*)mJAR->mManifestData.Get(&key); - if (manItem && !manItem->entryDigestsCalculated) - { - nsresult rv = NS_OK; - if (digestContexts[0] == 0) - for (PRInt32 b=0; bDigestBegin(&digestContexts[b], b); - for (PRInt32 c=0; cDigestData(digestContexts[c], buf, *_retval); - if (NS_SUCCEEDED(rv)) - { - PRUint32 available; - rv = Available(&available); - if (available == 0) - { - for (PRInt32 d=0; dCalculateDigest(digestContexts[d], - &(manItem->calculatedEntryDigests[d])); - if (NS_SUCCEEDED(rv)) - manItem->entryDigestsCalculated = PR_TRUE; - } - } - return rv; - } - } - return NS_OK; + else + return NS_OK; } NS_IMETHODIMP @@ -103,17 +73,18 @@ nsJARInputStream::Init(nsJAR* aJAR, nsZipArchive* aZip, const char* aFilename) if (!(aZip && aFilename)) return NS_ERROR_NULL_POINTER; mZip = aZip; - mJAR = aJAR; mEntryName = (char*)aFilename; - for (PRInt32 a=0; aReadInit(mEntryName, &mReadInfo); if (result != ZIP_OK) return NS_ERROR_FAILURE; - else - return NS_OK; + + // Pass the file (already in memory) on to the JAR parser + if (aJAR) + return aJAR->VerifyEntry(mEntryName, mReadInfo->mFileBuffer, + mReadInfo->mItem->realsize); + return NS_OK; } NS_METHOD @@ -135,7 +106,6 @@ nsJARInputStream::Create(nsISupports* ignored, const nsIID& aIID, void* *aResult nsJARInputStream::nsJARInputStream() { NS_INIT_REFCNT(); - mJAR = nsnull; } nsJARInputStream::~nsJARInputStream() diff --git a/mozilla/modules/libjar/nsJARInputStream.h b/mozilla/modules/libjar/nsJARInputStream.h index 6d8e8eecd76..b76490462d5 100644 --- a/mozilla/modules/libjar/nsJARInputStream.h +++ b/mozilla/modules/libjar/nsJARInputStream.h @@ -30,9 +30,7 @@ {0x90, 0xd8, 0x9c, 0x98, 0xfc, 0x2b, 0x7a, 0xc0}} #include "nsIInputStream.h" -#include "prio.h" // XXX: Needed by nsZipArchive.h #include "nsJAR.h" -//#include "nsZipArchive.h" /*------------------------------------------------------------------------- * Class nsJARInputStream declaration. This class defines objects returned @@ -61,9 +59,7 @@ class nsJARInputStream : public nsIInputStream Init(nsJAR* aParser, nsZipArchive* aZip, const char* aFilename); private: - - nsJAR* mJAR; - PRUint32 digestContexts[JAR_DIGEST_COUNT]; + char* mEntryName; nsZipArchive* mZip; nsZipRead* mReadInfo; diff --git a/mozilla/modules/libjar/nsJARStubs.cpp b/mozilla/modules/libjar/nsJARStubs.cpp index 844b884d583..dd9efd86b57 100644 --- a/mozilla/modules/libjar/nsJARStubs.cpp +++ b/mozilla/modules/libjar/nsJARStubs.cpp @@ -33,23 +33,15 @@ PRBool nsJAR::SupportsRSAVerification() return PR_FALSE; } -nsresult nsJAR::DigestBegin(PRUint32* id, PRInt32 alg) +nsresult nsJAR::CalculateDigest(const char* aInBuf, PRUint32 aInBufLen, + char** digest) { return NS_ERROR_NOT_IMPLEMENTED; } -nsresult nsJAR::DigestData(PRUint32 id, const char* data, PRUint32 length) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult nsJAR::CalculateDigest(PRUint32 id, char** digest) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - nsresult nsJAR::VerifySignature(const char* sfBuf, const char* rsaBuf, PRUint32 rsaBufLen, nsIPrincipal** aPrincipal) { return NS_ERROR_NOT_IMPLEMENTED; } + diff --git a/mozilla/modules/libjar/nsZipArchive.cpp b/mozilla/modules/libjar/nsZipArchive.cpp index ac85c94e607..b70d7eb9bb5 100644 --- a/mozilla/modules/libjar/nsZipArchive.cpp +++ b/mozilla/modules/libjar/nsZipArchive.cpp @@ -382,7 +382,7 @@ PRInt32 nsZipArchive::ReadInit(const char* zipEntry, nsZipRead** aRead) //-- find item and seek to file position nsZipItem* item; - result = ReadInitImpl(zipEntry, &item); + result = SeekToItem(zipEntry, &item); if (result != ZIP_OK) return result; //-- Create nsZipRead object @@ -393,56 +393,57 @@ PRInt32 nsZipArchive::ReadInit(const char* zipEntry, nsZipRead** aRead) //-- initialize crc (*aRead)->mCRC32 = crc32(0L, Z_NULL, 0); - //-- If file is deflated, do the inflation now. - // We save the complete inflated item in mInflatedFileBuffer - // to be copied later. - if (item->compression == DEFLATED) + //-- Read the item into memory + // Inflate if necessary and save in mInflatedFileBuffer + // for sequential reading. + // (nsJAR needs the whole file in memory before passing it on) + char* buf = (char*)PR_Malloc(item->realsize); + if (!buf) return ZIP_ERR_MEMORY; + switch(item->compression) { - char* buf = (char*)PR_Malloc(item->realsize); - result = InflateItem(item, 0, buf); - if (result != ZIP_OK) return result; - (*aRead)->mInflatedFileBuffer = buf; + case DEFLATED: + result = InflateItem(item, 0, buf); + break; + case STORED: + result = CopyItemToBuffer(item, buf); + break; + default: + return ZIP_ERR_UNSUPPORTED; } - return ZIP_OK; + if (result == ZIP_OK) + (*aRead)->mFileBuffer = buf; + return result; } - -//--------------------------------------------- +//------------------------------------------ // nsZipArchive::Read -//--------------------------------------------- -PRInt32 nsZipArchive::Read(nsZipRead* aRead, char* aBuf, +//------------------------------------------ +PRInt32 nsZipArchive::Read(nsZipRead* aRead, char* aOutBuf, PRUint32 aCount, PRUint32* aBytesRead) { - //-- Parameter validity check - /* - * Note the nsZipRead passed in must have been created - * by this nsZipArchive. - */ - if (aBytesRead == 0 || aRead == 0 || aBuf == 0 || + //-- Copies data from aRead->mFileBuffer + //-- Parameter check + if (aBytesRead == 0 || aRead == 0 || aOutBuf == 0 || aRead->mArchive != this) return ZIP_ERR_PARAM; - //-- extract the file using appropriate method - switch( aRead->mItem->compression ) - { - case STORED: - //-- Read from the zip file directly into the caller's buffer - return ReadItem( aRead, aBuf, aCount, aBytesRead ); + if(!aRead->mItem || !aRead->mFileBuffer) + return ZIP_ERR_GENERAL; - case DEFLATED: - //-- We've already done the inflation; copy from mInflatedFileBuffer - // into the caller's buffer - return ReadInflatedItem( aRead->mItem, aRead->mInflatedFileBuffer, aBuf, - &(aRead->mCurPos), aCount, aBytesRead); + //-- Set up the copy + PRUint32 bigBufSize = aRead->mItem->realsize; + PRUint32 curPos = aRead->mCurPos; + *aBytesRead = (curPos + aCount) < bigBufSize ? aCount : bigBufSize - curPos; + char* src = aRead->mFileBuffer + curPos; - default: - //-- unsupported compression type - return ZIP_ERR_UNSUPPORTED; - } + //-- Do the copy and record number of bytes copied + memcpy(aOutBuf, src, *aBytesRead); + aRead->mCurPos += *aBytesRead; + + return ZIP_OK; } - //--------------------------------------------- // nsZipArchive::Available //--------------------------------------------- @@ -496,7 +497,7 @@ nsZipArchive::ExtractFileToFileDesc(const char * zipEntry, PRFileDesc* outFD, nsZipItem* item; //-- Find item in archive and seek to it - status = ReadInitImpl( zipEntry, &item ); + status = SeekToItem( zipEntry, &item ); if (status != ZIP_OK) return status; @@ -876,9 +877,9 @@ PRUint32 nsZipArchive::HashName( const char* aName ) } //--------------------------------------------- -// nsZipArchive::ReadInitImpl +// nsZipArchive::SeekToItem //--------------------------------------------- -PRInt32 nsZipArchive::ReadInitImpl(const char* zipEntry, nsZipItem** aItem) +PRInt32 nsZipArchive::SeekToItem(const char* zipEntry, nsZipItem** aItem) { PR_ASSERT (zipEntry != 0); @@ -901,39 +902,24 @@ PRInt32 nsZipArchive::ReadInitImpl(const char* zipEntry, nsZipItem** aItem) } //--------------------------------------------- -// nsZipArchive::ReadItem +// nsZipArchive::CopyItemToBuffer //--------------------------------------------- -PRInt32 nsZipArchive::ReadItem( nsZipRead* aRead, char* aBuf, - PRUint32 aCount, PRUint32* aActual) +PRInt32 nsZipArchive::CopyItemToBuffer(const nsZipItem* aItem, char* aOutBuf) { - PRUint32 chunk; - nsZipItem *item = aRead->mItem; - PRUint32 *curpos = &(aRead->mCurPos); - PRUint32 *crc = &(aRead->mCRC32); - - PR_ASSERT(aBuf != 0 && item != 0); - - // Seek to position in file - if ( PR_Seek( mFd, (PROffset32)(item->offset + *curpos), PR_SEEK_SET ) != - (PROffset32)(item->offset + *curpos) ) - return ZIP_ERR_CORRUPT; + PR_ASSERT(aOutBuf != 0 && aItem != 0); + //-- Already at the correct point in the file //-- Read from file - chunk = (aCount <= ((item->size) - *curpos) ) ? aCount - : ((item->size) - *curpos); - *aActual = PR_Read( mFd, aBuf, chunk ); - *curpos += *aActual; - - //-- incrementally update crc32 - *crc = crc32(*crc,(const unsigned char*)aBuf, chunk); + PRUint32 actual = PR_Read( mFd, aOutBuf, aItem->realsize); + if (actual != aItem->realsize) + return ZIP_ERR_CORRUPT; //-- verify crc32 - if (0 == (item->size - *curpos)) // eof - { - if (*crc != item->crc32) - return ZIP_ERR_CORRUPT; - } - + PRUint32 calculatedCRC = crc32(0L, Z_NULL, 0); + calculatedCRC = crc32( calculatedCRC,(const unsigned char*)aOutBuf, + aItem->realsize); + if (calculatedCRC != aItem->crc32) + return ZIP_ERR_CORRUPT; return ZIP_OK; } @@ -953,7 +939,7 @@ PRInt32 nsZipArchive::CopyItemToDisk(const nsZipItem* aItem, PRFileDesc* fOut) return ZIP_ERR_MEMORY; //-- We should already be at the correct spot in the archive. - //-- ReadInitImpl did the seek(). + //-- SeekToItem did the seek(). //-- initialize crc crc = crc32(0L, Z_NULL, 0); @@ -1047,7 +1033,7 @@ PRInt32 nsZipArchive::InflateItem( const nsZipItem* aItem, PRFileDesc* fOut, } //-- We should already be at the correct spot in the archive. - //-- ReadInitImpl did the seek(). + //-- SeekToItem did the seek(). //-- set up the inflate memset( &zs, 0, sizeof(zs) ); @@ -1185,33 +1171,6 @@ cleanup: return status; } -//------------------------------------------ -// nsZipArchive::ReadInflatedItem -//------------------------------------------ -PRInt32 nsZipArchive::ReadInflatedItem( const nsZipItem* aItem, - char* inflatedBuf, - char* outbuf, - PRUint32* aCurPos, - PRUint32 count, - PRUint32* actual) -{ - //-- Parameter check - PR_ASSERT(inflatedBuf != 0 && outbuf != 0 && aItem != 0); - - //-- Set up the copy - PRUint32 bigBufSize = aItem->realsize; - PRUint32 c = ((*aCurPos + count) < bigBufSize) ? count : bigBufSize - *aCurPos; - char* src = inflatedBuf + (*aCurPos); - - //-- Do the copy and record number of bytes copied - memcpy(outbuf, src, c); - *actual = c; - *aCurPos += *actual; - - return ZIP_OK; -} - - //------------------------------------------ // nsZipArchive constructor and destructor //------------------------------------------ @@ -1283,7 +1242,7 @@ nsZipRead::nsZipRead( nsZipArchive* aZipArchive, nsZipItem* aZipItem ) : mArchive(aZipArchive), mItem(aZipItem), mCurPos(0), - mInflatedFileBuffer(0) + mFileBuffer(0) { #ifndef STANDALONE MOZ_COUNT_CTOR(nsZipRead); @@ -1292,7 +1251,7 @@ nsZipRead::nsZipRead( nsZipArchive* aZipArchive, nsZipItem* aZipItem ) nsZipRead::~nsZipRead() { - PR_FREEIF(mInflatedFileBuffer); + PR_FREEIF(mFileBuffer); #ifndef STANDALONE MOZ_COUNT_DTOR(nsZipRead); diff --git a/mozilla/modules/libjar/nsZipArchive.h b/mozilla/modules/libjar/nsZipArchive.h index 9f06877b322..86e09f8ab16 100644 --- a/mozilla/modules/libjar/nsZipArchive.h +++ b/mozilla/modules/libjar/nsZipArchive.h @@ -201,16 +201,12 @@ private: nsZipItem* GetFileItem( const char * zipEntry ); PRUint32 HashName( const char* aName ); - PRInt32 ReadInitImpl(const char* zipEntry, nsZipItem** aItem); - PRInt32 ReadItem( nsZipRead* aRead, char* aBuf, - PRUint32 aCount, PRUint32* aActual ); + PRInt32 SeekToItem(const char* zipEntry, nsZipItem** aItem); + PRInt32 CopyItemToBuffer(const nsZipItem* aItem, char* aBuf); PRInt32 CopyItemToDisk( const nsZipItem* aItem, PRFileDesc* outFD ); PRInt32 InflateItem( const nsZipItem* aItem, PRFileDesc* outFD, char* buf ); - PRInt32 ReadInflatedItem( const nsZipItem* aItem, - char* inflatedBuf, char* outbuf, - PRUint32* aCurPos, PRUint32 count, PRUint32* actual); }; /** @@ -220,7 +216,6 @@ private: */ class nsZipRead { - public: nsZipRead( nsZipArchive* aZip, nsZipItem* item ); @@ -229,7 +224,7 @@ public: nsZipArchive* mArchive; nsZipItem* mItem; PRUint32 mCurPos; - char* mInflatedFileBuffer; + char* mFileBuffer; PRUint32 mCRC32; private: @@ -238,7 +233,6 @@ private: nsZipRead(const nsZipFind& rhs); }; - /** * nsZipFind *