LCOV - code coverage report
Current view: top level - dom/security - ContentVerifier.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 94 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "ContentVerifier.h"
       7             : 
       8             : #include "mozilla/fallible.h"
       9             : #include "mozilla/Logging.h"
      10             : #include "MainThreadUtils.h"
      11             : #include "nsIInputStream.h"
      12             : #include "nsIRequest.h"
      13             : #include "nsServiceManagerUtils.h"
      14             : #include "nsStringStream.h"
      15             : 
      16             : using namespace mozilla;
      17             : 
      18             : static LazyLogModule gContentVerifierPRLog("ContentVerifier");
      19             : #define CSV_LOG(args) MOZ_LOG(gContentVerifierPRLog, LogLevel::Debug, args)
      20             : 
      21           0 : NS_IMPL_ISUPPORTS(ContentVerifier,
      22             :                   nsIContentSignatureReceiverCallback,
      23             :                   nsIStreamListener);
      24             : 
      25             : nsresult
      26           0 : ContentVerifier::Init(const nsACString& aContentSignatureHeader,
      27             :                       nsIRequest* aRequest, nsISupports* aContext)
      28             : {
      29           0 :   MOZ_ASSERT(NS_IsMainThread());
      30           0 :   if (aContentSignatureHeader.IsEmpty()) {
      31           0 :     CSV_LOG(("Content-Signature header must not be empty!\n"));
      32           0 :     return NS_ERROR_INVALID_SIGNATURE;
      33             :   }
      34             : 
      35             :   // initialise the content signature "service"
      36             :   nsresult rv;
      37             :   mVerifier =
      38           0 :     do_CreateInstance("@mozilla.org/security/contentsignatureverifier;1", &rv);
      39           0 :   if (NS_FAILED(rv) || !mVerifier) {
      40           0 :     return NS_ERROR_INVALID_SIGNATURE;
      41             :   }
      42             : 
      43             :   // Keep references to the request and context. We need them in FinishSignature
      44             :   // and the ContextCreated callback.
      45           0 :   mContentRequest = aRequest;
      46           0 :   mContentContext = aContext;
      47             : 
      48           0 :   rv = mVerifier->CreateContextWithoutCertChain(
      49             :     this, aContentSignatureHeader,
      50           0 :     NS_LITERAL_CSTRING("remotenewtab.content-signature.mozilla.org"));
      51           0 :   if (NS_FAILED(rv)){
      52           0 :     mVerifier = nullptr;
      53             :   }
      54           0 :   return rv;
      55             : }
      56             : 
      57             : /**
      58             :  * Implement nsIStreamListener
      59             :  * We buffer the entire content here and kick off verification
      60             :  */
      61             : nsresult
      62           0 : AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
      63             :                   const char* aRawSegment, uint32_t aToOffset, uint32_t aCount,
      64             :                   uint32_t* outWrittenCount)
      65             : {
      66             :   FallibleTArray<nsCString>* decodedData =
      67           0 :     static_cast<FallibleTArray<nsCString>*>(aClosure);
      68           0 :   nsDependentCSubstring segment(aRawSegment, aCount);
      69           0 :   if (!decodedData->AppendElement(segment, fallible)) {
      70           0 :     return NS_ERROR_OUT_OF_MEMORY;
      71             :   }
      72           0 :   *outWrittenCount = aCount;
      73           0 :   return NS_OK;
      74             : }
      75             : 
      76             : void
      77           0 : ContentVerifier::FinishSignature()
      78             : {
      79           0 :   MOZ_ASSERT(NS_IsMainThread());
      80           0 :   nsCOMPtr<nsIStreamListener> nextListener;
      81           0 :   nextListener.swap(mNextListener);
      82             : 
      83             :   // Verify the content:
      84             :   // If this fails, we return an invalid signature error to load a fallback page.
      85             :   // If everthing is good, we return a new stream to the next listener and kick
      86             :   // that one off.
      87           0 :   bool verified = false;
      88           0 :   nsresult rv = NS_OK;
      89             : 
      90             :   // If the content signature check fails, stop the load
      91             :   // and return a signature error. NSS resources are freed by the
      92             :   // ContentSignatureVerifier on destruction.
      93           0 :   if (NS_FAILED(mVerifier->End(&verified)) || !verified) {
      94           0 :     CSV_LOG(("failed to verify content\n"));
      95           0 :     (void)nextListener->OnStopRequest(mContentRequest, mContentContext,
      96           0 :                                       NS_ERROR_INVALID_SIGNATURE);
      97           0 :     return;
      98             :   }
      99           0 :   CSV_LOG(("Successfully verified content signature.\n"));
     100             : 
     101             :   // We emptied the input stream so we have to create a new one from mContent
     102             :   // to hand it to the consuming listener.
     103           0 :   uint64_t offset = 0;
     104           0 :   for (uint32_t i = 0; i < mContent.Length(); ++i) {
     105           0 :     nsCOMPtr<nsIInputStream> oInStr;
     106           0 :     rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
     107           0 :     if (NS_FAILED(rv)) {
     108           0 :       break;
     109             :     }
     110             :     // let the next listener know that there is data in oInStr
     111           0 :     rv = nextListener->OnDataAvailable(mContentRequest, mContentContext, oInStr,
     112           0 :                                        offset, mContent[i].Length());
     113           0 :     offset += mContent[i].Length();
     114           0 :     if (NS_FAILED(rv)) {
     115           0 :       break;
     116             :     }
     117             :   }
     118             : 
     119             :   // propagate OnStopRequest and return
     120           0 :   nextListener->OnStopRequest(mContentRequest, mContentContext, rv);
     121             : }
     122             : 
     123             : NS_IMETHODIMP
     124           0 : ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
     125             : {
     126           0 :   MOZ_CRASH("This OnStartRequest should've never been called!");
     127             :   return NS_OK;
     128             : }
     129             : 
     130             : NS_IMETHODIMP
     131           0 : ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
     132             :                                nsresult aStatus)
     133             : {
     134             :   // If we don't have a next listener, we handed off this request already.
     135             :   // Return, there's nothing to do here.
     136           0 :   if (!mNextListener) {
     137           0 :     return NS_OK;
     138             :   }
     139             : 
     140           0 :   if (NS_FAILED(aStatus)) {
     141           0 :     CSV_LOG(("Stream failed\n"));
     142           0 :     nsCOMPtr<nsIStreamListener> nextListener;
     143           0 :     nextListener.swap(mNextListener);
     144           0 :     return nextListener->OnStopRequest(aRequest, aContext, aStatus);
     145             :   }
     146             : 
     147           0 :   mContentRead = true;
     148             : 
     149             :   // If the ContentSignatureVerifier is initialised, finish the verification.
     150           0 :   if (mContextCreated) {
     151           0 :     FinishSignature();
     152           0 :     return aStatus;
     153             :   }
     154             : 
     155           0 :   return NS_OK;
     156             : }
     157             : 
     158             : NS_IMETHODIMP
     159           0 : ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
     160             :                                  nsIInputStream* aInputStream, uint64_t aOffset,
     161             :                                  uint32_t aCount)
     162             : {
     163             :   // buffer the entire stream
     164             :   uint32_t read;
     165           0 :   nsresult rv = aInputStream->ReadSegments(AppendNextSegment, &mContent, aCount,
     166           0 :                                            &read);
     167           0 :   if (NS_FAILED(rv)) {
     168           0 :     return rv;
     169             :   }
     170             : 
     171             :   // Update the signature verifier if the context has been created.
     172           0 :   if (mContextCreated) {
     173           0 :     return mVerifier->Update(mContent.LastElement());
     174             :   }
     175             : 
     176           0 :   return NS_OK;
     177             : }
     178             : 
     179             : NS_IMETHODIMP
     180           0 : ContentVerifier::ContextCreated(bool successful)
     181             : {
     182           0 :   MOZ_ASSERT(NS_IsMainThread());
     183           0 :   if (!successful) {
     184             :     // If we don't have a next listener, the request has been handed off already.
     185           0 :     if (!mNextListener) {
     186           0 :       return NS_OK;
     187             :     }
     188             :     // Get local reference to mNextListener and null it to ensure that we don't
     189             :     // call it twice.
     190           0 :     nsCOMPtr<nsIStreamListener> nextListener;
     191           0 :     nextListener.swap(mNextListener);
     192             : 
     193             :     // Make sure that OnStartRequest was called and we have a request.
     194           0 :     MOZ_ASSERT(mContentRequest);
     195             : 
     196             :     // In this case something went wrong with the cert. Let's stop this load.
     197           0 :     CSV_LOG(("failed to get a valid cert chain\n"));
     198           0 :     if (mContentRequest && nextListener) {
     199           0 :       mContentRequest->Cancel(NS_ERROR_INVALID_SIGNATURE);
     200           0 :       nsresult rv = nextListener->OnStopRequest(mContentRequest, mContentContext,
     201           0 :                                                 NS_ERROR_INVALID_SIGNATURE);
     202           0 :       mContentRequest = nullptr;
     203           0 :       mContentContext = nullptr;
     204           0 :       return rv;
     205             :     }
     206             : 
     207             :     // We should never get here!
     208           0 :     MOZ_ASSERT_UNREACHABLE(
     209             :       "ContentVerifier was used without getting OnStartRequest!");
     210             :     return NS_OK;
     211             :   }
     212             : 
     213             :   // In this case the content verifier is initialised and we have to feed it
     214             :   // the buffered content.
     215           0 :   mContextCreated = true;
     216           0 :   for (size_t i = 0; i < mContent.Length(); ++i) {
     217           0 :     if (NS_FAILED(mVerifier->Update(mContent[i]))) {
     218             :       // Bail out if this fails. We can't return an error here, but if this
     219             :       // failed, NS_ERROR_INVALID_SIGNATURE is returned in FinishSignature.
     220           0 :       break;
     221             :     }
     222             :   }
     223             : 
     224             :   // We read all content, let's verify the signature.
     225           0 :   if (mContentRead) {
     226           0 :     FinishSignature();
     227             :   }
     228             : 
     229           0 :   return NS_OK;
     230             : }

Generated by: LCOV version 1.13