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 : }
|