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