Coverage Report

Created: 2018-09-25 14:53

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