From 077ea1aeb2d85dbd548b8d2bccbd7b5744d76b6b Mon Sep 17 00:00:00 2001 From: Jonathan Hao Date: Thu, 24 Sep 2015 10:07:01 +0800 Subject: [PATCH] Bug 1178518 - Verify signed packages. r=valentin --- netwerk/protocol/http/PackagedAppService.cpp | 13 +-- netwerk/protocol/http/PackagedAppVerifier.cpp | 95 +++++++++++++++---- netwerk/protocol/http/PackagedAppVerifier.h | 23 ++++- 3 files changed, 104 insertions(+), 27 deletions(-) diff --git a/netwerk/protocol/http/PackagedAppService.cpp b/netwerk/protocol/http/PackagedAppService.cpp index a9205370954a..0e7be513d5c2 100644 --- a/netwerk/protocol/http/PackagedAppService.cpp +++ b/netwerk/protocol/http/PackagedAppService.cpp @@ -225,15 +225,12 @@ CreateSharedStringStream(const char* aData, uint32_t aCount) static bool GetOriginalResponseHeader(nsIRequest* aRequest, nsACString& aHeader) { - // TODO: The flattened http header might be different from the original. - // See Bug 1198669 for further information. + nsCOMPtr multiPartChannel(do_QueryInterface(aRequest)); + if (!multiPartChannel) { + return false; + } - nsCOMPtr headerProvider(do_QueryInterface(aRequest)); - nsHttpResponseHead *responseHead = headerProvider->GetResponseHead(); - NS_ENSURE_TRUE(responseHead, false); - - responseHead->Flatten(aHeader, true); - aHeader.Append("\r\n"); + multiPartChannel->GetOriginalResponseHeader(aHeader); return true; } diff --git a/netwerk/protocol/http/PackagedAppVerifier.cpp b/netwerk/protocol/http/PackagedAppVerifier.cpp index ab94245272ba..b30bb71ea893 100644 --- a/netwerk/protocol/http/PackagedAppVerifier.cpp +++ b/netwerk/protocol/http/PackagedAppVerifier.cpp @@ -14,6 +14,8 @@ #include "nsITimer.h" #include "nsIPackagedAppVerifier.h" #include "mozilla/Preferences.h" +#include "nsIPackagedAppUtils.h" +#include "nsIInputStream.h" static const short kResourceHashType = nsICryptoHash::SHA256; @@ -26,7 +28,7 @@ namespace net { /////////////////////////////////////////////////////////////////////////////// -NS_IMPL_ISUPPORTS(PackagedAppVerifier, nsIPackagedAppVerifier) +NS_IMPL_ISUPPORTS(PackagedAppVerifier, nsIPackagedAppVerifier, nsIVerificationCallback) NS_IMPL_ISUPPORTS(PackagedAppVerifier::ResourceCacheInfo, nsISupports) @@ -76,6 +78,14 @@ NS_IMETHODIMP PackagedAppVerifier::Init(nsIPackagedAppVerifierListener* aListene mSignature = aSignature; mIsPackageSigned = false; mPackageCacheEntry = aPackageCacheEntry; + mIsFirstResource = true; + + nsresult rv; + mPackagedAppUtils = do_CreateInstance(NS_PACKAGEDAPPUTILS_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + LOG(("create packaged app utils failed")); + return rv; + } return NS_OK; } @@ -90,6 +100,11 @@ NS_IMETHODIMP PackagedAppVerifier::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) { + if (mIsFirstResource) { + // The first resource must be the manifest, hashes not needed + return NS_OK; + } + if (!mHasher) { mHasher = do_CreateInstance("@mozilla.org/security/hash;1"); } @@ -103,6 +118,22 @@ PackagedAppVerifier::OnStartRequest(nsIRequest *aRequest, return mHasher->Init(kResourceHashType); } +NS_METHOD +PackagedAppVerifier::WriteManifest(nsIInputStream* aStream, + void* aManifest, + const char* aFromRawSegment, + uint32_t aToOffset, + uint32_t aCount, + uint32_t* aWriteCount) +{ + LOG(("WriteManifest: length %u", aCount)); + LOG(("%s", aFromRawSegment)); + nsCString* manifest = static_cast(aManifest); + manifest->AppendASCII(aFromRawSegment, aCount); + *aWriteCount = aCount; + return NS_OK; +} + // @param aRequest nullptr. // @param aContext nullptr. // @param aInputStream as-is. @@ -115,6 +146,16 @@ PackagedAppVerifier::OnDataAvailable(nsIRequest *aRequest, uint64_t aOffset, uint32_t aCount) { + if (mIsFirstResource) { + // The first resource must be the manifest, hash value not needed. + // Instead, we read from the input stream and append to mManifest. + uint32_t count; + LOG(("ReadSegments: size = %u", aCount)); + nsresult rv = aInputStream->ReadSegments(WriteManifest, &mManifest, aCount, &count); + MOZ_ASSERT(count == aCount, "Bytes read by ReadSegments don't match"); + return rv; + } + MOZ_ASSERT(!mHashingResourceURI.IsEmpty(), "MUST call BeginResourceHash first."); NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE); return mHasher->UpdateFromStream(aInputStream, aCount); @@ -130,17 +171,22 @@ PackagedAppVerifier::OnStopRequest(nsIRequest* aRequest, { MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mHashingResourceURI is not thread safe."); - NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE); + if (mIsFirstResource) { + // The first resource must be the manifest, hash value not needed + mIsFirstResource = false; + } else { + NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE); - nsAutoCString hash; - nsresult rv = mHasher->Finish(true, hash); - NS_ENSURE_SUCCESS(rv, rv); + nsAutoCString hash; + nsresult rv = mHasher->Finish(true, hash); + NS_ENSURE_SUCCESS(rv, rv); - LOG(("Hash of %s is %s", mHashingResourceURI.get(), hash.get())); + LOG(("Hash of %s is %s", mHashingResourceURI.get(), hash.get())); - // Store the computated hash associated with the resource URI. - mResourceHashStore.Put(mHashingResourceURI, new nsCString(hash)); - mHashingResourceURI = EmptyCString(); + // Store the computated hash associated with the resource URI. + mResourceHashStore.Put(mHashingResourceURI, new nsCString(hash)); + mHashingResourceURI = EmptyCString(); + } // Get a internal copy and take over the life cycle handling // since the linked list we use only supports pointer-based element. @@ -186,9 +232,10 @@ PackagedAppVerifier::ProcessResourceCache(const ResourceCacheInfo* aInfo) } } -void +NS_IMETHODIMP PackagedAppVerifier::FireVerifiedEvent(bool aForManifest, bool aSuccess) { + LOG(("FireVerifiedEvent aForManifest=%d aSuccess=%d", aForManifest, aSuccess)); nsCOMPtr r; if (aForManifest) { @@ -202,6 +249,8 @@ PackagedAppVerifier::FireVerifiedEvent(bool aForManifest, bool aSuccess) } NS_DispatchToMainThread(r); + + return NS_OK; } void @@ -231,9 +280,12 @@ PackagedAppVerifier::VerifyManifest(const ResourceCacheInfo* aInfo) return; } - // TODO: Implement manifest verification. - LOG(("Manifest verification not implemented yet. See Bug 1178518.")); - FireVerifiedEvent(true, false); + LOG(("Signature: length = %u\n%s", mSignature.Length(), mSignature.get())); + LOG(("Manifest: length = %u\n%s", mManifest.Length(), mManifest.get())); + nsresult rv = mPackagedAppUtils->VerifyManifest(mSignature, mManifest, this); + if (NS_FAILED(rv)) { + LOG(("VerifyManifest FAILED rv = %u", (unsigned)rv)); + } } void @@ -269,9 +321,20 @@ PackagedAppVerifier::VerifyResource(const ResourceCacheInfo* aInfo) return; } - // TODO: Implement resource integrity check. - LOG(("Resource integrity check not implemented yet. See Bug 1178518.")); - FireVerifiedEvent(false, false); + nsAutoCString path; + nsCOMPtr url(do_QueryInterface(aInfo->mURI)); + if (url) { + url->GetFilePath(path); + } + int32_t pos = path.Find("!//"); + if (pos == kNotFound) { + FireVerifiedEvent(false, false); + return; + } + // Only keep the part after "!//" + path.Cut(0, pos + 3); + + mPackagedAppUtils->CheckIntegrity(path, *resourceHash, this); } void diff --git a/netwerk/protocol/http/PackagedAppVerifier.h b/netwerk/protocol/http/PackagedAppVerifier.h index 7049dbcac35c..4166097609e9 100644 --- a/netwerk/protocol/http/PackagedAppVerifier.h +++ b/netwerk/protocol/http/PackagedAppVerifier.h @@ -14,18 +14,21 @@ #include "nsICryptoHash.h" #include "nsIPackagedAppVerifier.h" #include "mozilla/LinkedList.h" +#include "nsIPackagedAppUtils.h" namespace mozilla { namespace net { class PackagedAppVerifier final : public nsIPackagedAppVerifier + , public nsIVerificationCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIPACKAGEDAPPVERIFIER + NS_DECL_NSIVERIFICATIONCALLBACK public: enum EState { @@ -120,6 +123,14 @@ private: // void ProcessResourceCache(const ResourceCacheInfo* aInfo); + // Callback for nsIInputStream::ReadSegment() to read manifest + static NS_METHOD WriteManifest(nsIInputStream* aStream, + void* aManifest, + const char* aFromRawSegment, + uint32_t aToOffset, + uint32_t aCount, + uint32_t* aWriteCount); + // This two functions would call the actual verifier. void VerifyManifest(const ResourceCacheInfo* aInfo); void VerifyResource(const ResourceCacheInfo* aInfo); @@ -127,9 +138,6 @@ private: void OnManifestVerified(bool aSuccess); void OnResourceVerified(bool aSuccess); - // Fire a async event to notify the verification result. - void FireVerifiedEvent(bool aForManifest, bool aSuccess); - // To notify that either manifest or resource check is done. nsCOMPtr mListener; @@ -142,6 +150,12 @@ private: // The signature of the package. nsCString mSignature; + // The app manfiest of the package + nsCString mManifest; + + // Whether we're processing the first resource, which is the manfiest + bool mIsFirstResource; + // Whether this package app is signed. bool mIsPackageSigned; @@ -159,6 +173,9 @@ private: // |EndResourceHash| call. nsCString mLastComputedResourceHash; + // This will help to verify manifests and resource integrity + nsCOMPtr mPackagedAppUtils; + // A list of pending resource that is downloaded but not verified yet. mozilla::LinkedList mPendingResourceCacheInfoList;