Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: set sw=2 sts=2 ts=2 et tw=80:
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "PSMContentListener.h"
9 :
10 : #include "mozilla/Casting.h"
11 : #include "mozilla/Logging.h"
12 : #include "mozilla/Services.h"
13 : #include "mozilla/Unused.h"
14 : #include "mozilla/dom/ContentChild.h"
15 : #include "mozilla/net/ChannelDiverterChild.h"
16 : #include "mozilla/net/ChannelDiverterParent.h"
17 : #include "nsDependentString.h"
18 : #include "nsIChannel.h"
19 : #include "nsIDivertableChannel.h"
20 : #include "nsIInputStream.h"
21 : #include "nsIStreamListener.h"
22 : #include "nsIURI.h"
23 : #include "nsIX509CertDB.h"
24 : #include "nsIXULAppInfo.h"
25 : #include "nsNSSHelper.h"
26 : #include "nsNetUtil.h"
27 : #include "nsPromiseFlatString.h"
28 :
29 : extern mozilla::LazyLogModule gPIPNSSLog;
30 :
31 : namespace mozilla { namespace psm {
32 :
33 : namespace {
34 :
35 : const int32_t kDefaultCertAllocLength = 2048;
36 :
37 : enum {
38 : UNKNOWN_TYPE = 0,
39 : X509_CA_CERT = 1,
40 : X509_USER_CERT = 2,
41 : X509_EMAIL_CERT = 3,
42 : X509_SERVER_CERT = 4
43 : };
44 :
45 : /* other mime types that we should handle sometime:
46 :
47 : application/x-pkcs7-mime
48 : application/pkcs7-signature
49 : application/pre-encrypted
50 :
51 : */
52 :
53 : uint32_t
54 0 : getPSMContentType(const nsCString& aContentType)
55 : {
56 : // Don't forget to update the registration of content listeners in nsNSSModule.cpp
57 : // for every supported content type.
58 :
59 0 : if (aContentType.EqualsIgnoreCase("application/x-x509-ca-cert")) {
60 0 : return X509_CA_CERT;
61 : }
62 0 : if (aContentType.EqualsIgnoreCase("application/x-x509-server-cert")) {
63 0 : return X509_SERVER_CERT;
64 : }
65 0 : if (aContentType.EqualsIgnoreCase("application/x-x509-user-cert")) {
66 0 : return X509_USER_CERT;
67 : }
68 0 : if (aContentType.EqualsIgnoreCase("application/x-x509-email-cert")) {
69 0 : return X509_EMAIL_CERT;
70 : }
71 :
72 0 : return UNKNOWN_TYPE;
73 : }
74 :
75 : int64_t
76 0 : ComputeContentLength(nsIRequest* request)
77 : {
78 0 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
79 0 : if (!channel) {
80 0 : return -1;
81 : }
82 :
83 : int64_t contentLength;
84 0 : nsresult rv = channel->GetContentLength(&contentLength);
85 0 : if (NS_FAILED(rv) || contentLength <= 0) {
86 0 : return kDefaultCertAllocLength;
87 : }
88 :
89 0 : if (contentLength > INT32_MAX) {
90 0 : return -1;
91 : }
92 :
93 0 : return contentLength;
94 : }
95 :
96 : } // unnamed namespace
97 :
98 : /* ------------------------
99 : * PSMContentStreamListener
100 : * ------------------------ */
101 :
102 0 : PSMContentStreamListener::PSMContentStreamListener(uint32_t type)
103 0 : : mType(type)
104 : {
105 0 : }
106 :
107 0 : PSMContentStreamListener::~PSMContentStreamListener()
108 : {
109 0 : }
110 :
111 0 : NS_IMPL_ISUPPORTS(PSMContentStreamListener, nsIStreamListener, nsIRequestObserver)
112 :
113 : NS_IMETHODIMP
114 0 : PSMContentStreamListener::OnStartRequest(nsIRequest* request, nsISupports* context)
115 : {
116 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnStartRequest\n"));
117 :
118 0 : int64_t contentLength = ComputeContentLength(request);
119 0 : if (contentLength < 0) {
120 0 : return NS_ERROR_FAILURE;
121 : }
122 :
123 0 : mByteData.SetCapacity(contentLength);
124 0 : return NS_OK;
125 : }
126 :
127 : NS_IMETHODIMP
128 0 : PSMContentStreamListener::OnDataAvailable(nsIRequest* request,
129 : nsISupports* context,
130 : nsIInputStream* aIStream,
131 : uint64_t aSourceOffset,
132 : uint32_t aLength)
133 : {
134 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnDataAvailable\n"));
135 :
136 0 : nsCString chunk;
137 0 : nsresult rv = NS_ReadInputStreamToString(aIStream, chunk, aLength);
138 0 : if (NS_FAILED(rv)) {
139 0 : return rv;
140 : }
141 :
142 0 : mByteData.Append(chunk);
143 0 : return NS_OK;
144 : }
145 :
146 : NS_IMETHODIMP
147 0 : PSMContentStreamListener::OnStopRequest(nsIRequest* request,
148 : nsISupports* context,
149 : nsresult aStatus)
150 : {
151 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnStopRequest\n"));
152 :
153 : // Because importing the cert can spin the event loop (via alerts), we can't
154 : // do it here. Do it off the event loop instead.
155 : nsCOMPtr<nsIRunnable> r =
156 0 : NewRunnableMethod("psm::PSMContentStreamListener::ImportCertificate",
157 : this,
158 0 : &PSMContentStreamListener::ImportCertificate);
159 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
160 :
161 0 : return NS_OK;
162 : }
163 :
164 : void
165 0 : PSMContentStreamListener::ImportCertificate()
166 : {
167 0 : nsCOMPtr<nsIX509CertDB> certdb;
168 :
169 0 : nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
170 :
171 0 : switch (mType) {
172 : case X509_CA_CERT:
173 : case X509_USER_CERT:
174 : case X509_EMAIL_CERT:
175 0 : certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
176 0 : break;
177 :
178 : default:
179 0 : break;
180 : }
181 :
182 0 : if (!certdb) {
183 0 : return;
184 : }
185 :
186 0 : switch (mType) {
187 : case X509_CA_CERT:
188 0 : certdb->ImportCertificates(BitwiseCast<uint8_t*, char*>(
189 : mByteData.BeginWriting()),
190 0 : mByteData.Length(), mType, ctx);
191 0 : break;
192 :
193 : case X509_USER_CERT:
194 0 : certdb->ImportUserCertificate(BitwiseCast<uint8_t*, char*>(
195 : mByteData.BeginWriting()),
196 0 : mByteData.Length(), ctx);
197 0 : break;
198 :
199 : case X509_EMAIL_CERT:
200 0 : certdb->ImportEmailCertificate(BitwiseCast<uint8_t*, char*>(
201 : mByteData.BeginWriting()),
202 0 : mByteData.Length(), ctx);
203 0 : break;
204 :
205 : default:
206 0 : break;
207 : }
208 : }
209 :
210 : /* ------------------------
211 : * PSMContentDownloaderParent
212 : * ------------------------ */
213 :
214 0 : PSMContentDownloaderParent::PSMContentDownloaderParent(uint32_t type)
215 : : PSMContentStreamListener(type)
216 0 : , mIPCOpen(true)
217 : {
218 0 : }
219 :
220 0 : PSMContentDownloaderParent::~PSMContentDownloaderParent()
221 : {
222 0 : }
223 :
224 : mozilla::ipc::IPCResult
225 0 : PSMContentDownloaderParent::RecvOnStartRequest(const uint32_t& contentLength)
226 : {
227 0 : mByteData.SetCapacity(contentLength);
228 0 : return IPC_OK();
229 : }
230 :
231 : mozilla::ipc::IPCResult
232 0 : PSMContentDownloaderParent::RecvOnDataAvailable(const nsCString& data,
233 : const uint64_t& offset,
234 : const uint32_t& count)
235 : {
236 0 : mByteData.Append(data);
237 0 : return IPC_OK();
238 : }
239 :
240 : mozilla::ipc::IPCResult
241 0 : PSMContentDownloaderParent::RecvOnStopRequest(const nsresult& code)
242 : {
243 0 : if (NS_SUCCEEDED(code)) {
244 : // See also PSMContentStreamListener::OnStopRequest. In this case, we don't
245 : // have to dispatch ImportCertificate off of an event because we don't have
246 : // to worry about Necko sending "clean up" events and destroying us if
247 : // ImportCertificate spins the event loop.
248 0 : ImportCertificate();
249 : }
250 :
251 0 : if (mIPCOpen) {
252 0 : mozilla::Unused << Send__delete__(this);
253 : }
254 0 : return IPC_OK();
255 : }
256 :
257 : NS_IMETHODIMP
258 0 : PSMContentDownloaderParent::OnStopRequest(nsIRequest* request, nsISupports* context, nsresult code)
259 : {
260 0 : nsresult rv = PSMContentStreamListener::OnStopRequest(request, context, code);
261 :
262 0 : if (mIPCOpen) {
263 0 : mozilla::Unused << Send__delete__(this);
264 : }
265 0 : return rv;
266 : }
267 :
268 : mozilla::ipc::IPCResult
269 0 : PSMContentDownloaderParent::RecvDivertToParentUsing(mozilla::net::PChannelDiverterParent* diverter)
270 : {
271 0 : MOZ_ASSERT(diverter);
272 0 : auto p = static_cast<mozilla::net::ChannelDiverterParent*>(diverter);
273 0 : p->DivertTo(this);
274 0 : mozilla::Unused << p->Send__delete__(p);
275 0 : return IPC_OK();
276 : }
277 :
278 : void
279 0 : PSMContentDownloaderParent::ActorDestroy(ActorDestroyReason why)
280 : {
281 0 : mIPCOpen = false;
282 0 : }
283 :
284 : /* ------------------------
285 : * PSMContentDownloaderChild
286 : * ------------------------ */
287 :
288 0 : NS_IMPL_ISUPPORTS(PSMContentDownloaderChild, nsIStreamListener)
289 :
290 0 : PSMContentDownloaderChild::PSMContentDownloaderChild()
291 : {
292 0 : }
293 :
294 0 : PSMContentDownloaderChild::~PSMContentDownloaderChild()
295 : {
296 0 : }
297 :
298 : NS_IMETHODIMP
299 0 : PSMContentDownloaderChild::OnStartRequest(nsIRequest* request, nsISupports* context)
300 : {
301 0 : nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
302 0 : if (divertable) {
303 0 : mozilla::net::ChannelDiverterChild* diverter = nullptr;
304 0 : nsresult rv = divertable->DivertToParent(&diverter);
305 0 : if (NS_FAILED(rv)) {
306 0 : return rv;
307 : }
308 0 : MOZ_ASSERT(diverter);
309 :
310 0 : return SendDivertToParentUsing(diverter) ? NS_OK : NS_ERROR_FAILURE;
311 : }
312 :
313 0 : int64_t contentLength = ComputeContentLength(request);
314 0 : if (contentLength < 0) {
315 0 : return NS_ERROR_FAILURE;
316 : }
317 :
318 0 : mozilla::Unused << SendOnStartRequest(contentLength);
319 0 : return NS_OK;
320 : }
321 :
322 : NS_IMETHODIMP
323 0 : PSMContentDownloaderChild::OnDataAvailable(nsIRequest* request,
324 : nsISupports* context,
325 : nsIInputStream* aIStream,
326 : uint64_t aSourceOffset,
327 : uint32_t aLength)
328 : {
329 0 : nsCString chunk;
330 0 : nsresult rv = NS_ReadInputStreamToString(aIStream, chunk, aLength);
331 0 : if (NS_FAILED(rv)) {
332 0 : return rv;
333 : }
334 :
335 0 : mozilla::Unused << SendOnDataAvailable(chunk, aSourceOffset, aLength);
336 0 : return NS_OK;
337 : }
338 :
339 : NS_IMETHODIMP
340 0 : PSMContentDownloaderChild::OnStopRequest(nsIRequest* request,
341 : nsISupports* context,
342 : nsresult aStatus)
343 : {
344 0 : mozilla::Unused << SendOnStopRequest(aStatus);
345 0 : return NS_OK;
346 : }
347 :
348 : /* ------------------------
349 : * PSMContentListener
350 : * ------------------------ */
351 :
352 0 : NS_IMPL_ISUPPORTS(PSMContentListener,
353 : nsIURIContentListener,
354 : nsISupportsWeakReference)
355 :
356 0 : PSMContentListener::PSMContentListener()
357 : {
358 0 : mLoadCookie = nullptr;
359 0 : mParentContentListener = nullptr;
360 0 : }
361 :
362 0 : PSMContentListener::~PSMContentListener()
363 : {
364 0 : }
365 :
366 : nsresult
367 0 : PSMContentListener::init()
368 : {
369 0 : return NS_OK;
370 : }
371 :
372 : NS_IMETHODIMP
373 0 : PSMContentListener::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
374 : {
375 : //if we don't want to handle the URI, return true in
376 : //*aAbortOpen
377 0 : return NS_OK;
378 : }
379 :
380 : NS_IMETHODIMP
381 0 : PSMContentListener::IsPreferred(const char* aContentType,
382 : char** aDesiredContentType,
383 : bool* aCanHandleContent)
384 : {
385 : return CanHandleContent(aContentType, true,
386 0 : aDesiredContentType, aCanHandleContent);
387 : }
388 :
389 : NS_IMETHODIMP
390 0 : PSMContentListener::CanHandleContent(const char* aContentType,
391 : bool /*aIsContentPreferred*/,
392 : /*out*/ char** aDesiredContentType,
393 : /*out*/ bool* aCanHandleContent)
394 : {
395 0 : NS_ENSURE_ARG(aDesiredContentType);
396 0 : NS_ENSURE_ARG(aCanHandleContent);
397 :
398 0 : *aDesiredContentType = nullptr;
399 :
400 0 : uint32_t type = getPSMContentType(nsDependentCString(aContentType));
401 0 : *aCanHandleContent = (type != UNKNOWN_TYPE);
402 0 : return NS_OK;
403 : }
404 :
405 : NS_IMETHODIMP
406 0 : PSMContentListener::DoContent(const nsACString& aContentType,
407 : bool /*aIsContentPreferred*/,
408 : nsIRequest* /*aRequest*/,
409 : /*out*/ nsIStreamListener** aContentHandler,
410 : /*out*/ bool* aAbortProcess)
411 : {
412 0 : NS_ENSURE_ARG(aContentHandler);
413 0 : NS_ENSURE_ARG(aAbortProcess);
414 :
415 0 : *aAbortProcess = false;
416 :
417 0 : uint32_t type = getPSMContentType(PromiseFlatCString(aContentType));
418 0 : if (gPIPNSSLog) {
419 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PSMContentListener::DoContent\n"));
420 : }
421 0 : if (type != UNKNOWN_TYPE) {
422 0 : nsCOMPtr<nsIStreamListener> downloader;
423 0 : if (XRE_IsParentProcess()) {
424 0 : downloader = new PSMContentStreamListener(type);
425 : } else {
426 : downloader = static_cast<PSMContentDownloaderChild*>(
427 0 : dom::ContentChild::GetSingleton()->SendPPSMContentDownloaderConstructor(type));
428 : }
429 :
430 0 : downloader.forget(aContentHandler);
431 0 : return NS_OK;
432 : }
433 0 : return NS_ERROR_FAILURE;
434 : }
435 :
436 : NS_IMETHODIMP
437 0 : PSMContentListener::GetLoadCookie(nsISupports** aLoadCookie)
438 : {
439 0 : nsCOMPtr<nsISupports> loadCookie(mLoadCookie);
440 0 : loadCookie.forget(aLoadCookie);
441 0 : return NS_OK;
442 : }
443 :
444 : NS_IMETHODIMP
445 0 : PSMContentListener::SetLoadCookie(nsISupports* aLoadCookie)
446 : {
447 0 : mLoadCookie = aLoadCookie;
448 0 : return NS_OK;
449 : }
450 :
451 : NS_IMETHODIMP
452 0 : PSMContentListener::GetParentContentListener(nsIURIContentListener** aContentListener)
453 : {
454 0 : nsCOMPtr<nsIURIContentListener> listener(mParentContentListener);
455 0 : listener.forget(aContentListener);
456 0 : return NS_OK;
457 : }
458 :
459 : NS_IMETHODIMP
460 0 : PSMContentListener::SetParentContentListener(nsIURIContentListener* aContentListener)
461 : {
462 0 : mParentContentListener = aContentListener;
463 0 : return NS_OK;
464 : }
465 :
466 : } } // namespace mozilla::psm
|