Line data Source code
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 "mozilla/dom/cache/PrincipalVerifier.h"
8 :
9 : #include "mozilla/dom/ContentParent.h"
10 : #include "mozilla/dom/cache/ManagerId.h"
11 : #include "mozilla/ipc/BackgroundParent.h"
12 : #include "mozilla/ipc/PBackgroundParent.h"
13 : #include "mozilla/ipc/BackgroundUtils.h"
14 : #include "nsContentUtils.h"
15 : #include "nsIPrincipal.h"
16 : #include "nsIScriptSecurityManager.h"
17 : #include "nsNetUtil.h"
18 :
19 : namespace mozilla {
20 : namespace dom {
21 : namespace cache {
22 :
23 : using mozilla::ipc::AssertIsOnBackgroundThread;
24 : using mozilla::ipc::BackgroundParent;
25 : using mozilla::ipc::PBackgroundParent;
26 : using mozilla::ipc::PrincipalInfo;
27 : using mozilla::ipc::PrincipalInfoToPrincipal;
28 :
29 : // static
30 : already_AddRefed<PrincipalVerifier>
31 0 : PrincipalVerifier::CreateAndDispatch(Listener* aListener,
32 : PBackgroundParent* aActor,
33 : const PrincipalInfo& aPrincipalInfo)
34 : {
35 : // We must get the ContentParent actor from the PBackgroundParent. This
36 : // only works on the PBackground thread.
37 0 : AssertIsOnBackgroundThread();
38 :
39 : RefPtr<PrincipalVerifier> verifier = new PrincipalVerifier(aListener,
40 : aActor,
41 0 : aPrincipalInfo);
42 :
43 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(verifier));
44 :
45 0 : return verifier.forget();
46 : }
47 :
48 : void
49 0 : PrincipalVerifier::AddListener(Listener* aListener)
50 : {
51 0 : AssertIsOnBackgroundThread();
52 0 : MOZ_DIAGNOSTIC_ASSERT(aListener);
53 0 : MOZ_ASSERT(!mListenerList.Contains(aListener));
54 0 : mListenerList.AppendElement(aListener);
55 0 : }
56 :
57 : void
58 0 : PrincipalVerifier::RemoveListener(Listener* aListener)
59 : {
60 0 : AssertIsOnBackgroundThread();
61 0 : MOZ_DIAGNOSTIC_ASSERT(aListener);
62 0 : MOZ_ALWAYS_TRUE(mListenerList.RemoveElement(aListener));
63 0 : }
64 :
65 0 : PrincipalVerifier::PrincipalVerifier(Listener* aListener,
66 : PBackgroundParent* aActor,
67 0 : const PrincipalInfo& aPrincipalInfo)
68 : : Runnable("dom::cache::PrincipalVerifier")
69 0 : , mActor(BackgroundParent::GetContentParent(aActor))
70 : , mPrincipalInfo(aPrincipalInfo)
71 0 : , mInitiatingEventTarget(GetCurrentThreadSerialEventTarget())
72 0 : , mResult(NS_OK)
73 : {
74 0 : AssertIsOnBackgroundThread();
75 0 : MOZ_DIAGNOSTIC_ASSERT(mInitiatingEventTarget);
76 0 : MOZ_DIAGNOSTIC_ASSERT(aListener);
77 :
78 0 : mListenerList.AppendElement(aListener);
79 0 : }
80 :
81 0 : PrincipalVerifier::~PrincipalVerifier()
82 : {
83 : // Since the PrincipalVerifier is a Runnable that executes on multiple
84 : // threads, its a race to see which thread de-refs us last. Therefore
85 : // we cannot guarantee which thread we destruct on.
86 :
87 0 : MOZ_DIAGNOSTIC_ASSERT(mListenerList.IsEmpty());
88 :
89 : // We should always be able to explicitly release the actor on the main
90 : // thread.
91 0 : MOZ_DIAGNOSTIC_ASSERT(!mActor);
92 0 : }
93 :
94 : NS_IMETHODIMP
95 0 : PrincipalVerifier::Run()
96 : {
97 : // Executed twice. First, on the main thread and then back on the
98 : // originating thread.
99 :
100 0 : if (NS_IsMainThread()) {
101 0 : VerifyOnMainThread();
102 0 : return NS_OK;
103 : }
104 :
105 0 : CompleteOnInitiatingThread();
106 0 : return NS_OK;
107 : }
108 :
109 : void
110 0 : PrincipalVerifier::VerifyOnMainThread()
111 : {
112 0 : MOZ_ASSERT(NS_IsMainThread());
113 :
114 : // No matter what happens, we need to release the actor before leaving
115 : // this method.
116 0 : RefPtr<ContentParent> actor;
117 0 : actor.swap(mActor);
118 :
119 : nsresult rv;
120 0 : RefPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo,
121 0 : &rv);
122 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
123 0 : DispatchToInitiatingThread(rv);
124 0 : return;
125 : }
126 :
127 : // We disallow null principal on the client side, but double-check here.
128 0 : if (NS_WARN_IF(principal->GetIsNullPrincipal())) {
129 0 : DispatchToInitiatingThread(NS_ERROR_FAILURE);
130 0 : return;
131 : }
132 :
133 0 : nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
134 0 : if (NS_WARN_IF(!ssm)) {
135 0 : DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
136 0 : return;
137 : }
138 :
139 : // Verify if a child process uses system principal, which is not allowed
140 : // to prevent system principal is spoofed.
141 0 : if (NS_WARN_IF(actor && ssm->IsSystemPrincipal(principal))) {
142 0 : DispatchToInitiatingThread(NS_ERROR_FAILURE);
143 0 : return;
144 : }
145 :
146 0 : actor = nullptr;
147 :
148 : #ifdef DEBUG
149 : // Sanity check principal origin by using it to construct a URI and security
150 : // checking it. Don't do this for the system principal, though, as its origin
151 : // is a synthetic [System Principal] string.
152 0 : if (!ssm->IsSystemPrincipal(principal)) {
153 0 : nsAutoCString origin;
154 0 : rv = principal->GetOriginNoSuffix(origin);
155 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
156 0 : DispatchToInitiatingThread(rv);
157 0 : return;
158 : }
159 0 : nsCOMPtr<nsIURI> uri;
160 0 : rv = NS_NewURI(getter_AddRefs(uri), origin);
161 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
162 0 : DispatchToInitiatingThread(rv);
163 0 : return;
164 : }
165 0 : rv = principal->CheckMayLoad(uri, false, false);
166 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
167 0 : DispatchToInitiatingThread(rv);
168 0 : return;
169 : }
170 : }
171 : #endif
172 :
173 0 : rv = ManagerId::Create(principal, getter_AddRefs(mManagerId));
174 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
175 0 : DispatchToInitiatingThread(rv);
176 0 : return;
177 : }
178 :
179 0 : DispatchToInitiatingThread(NS_OK);
180 : }
181 :
182 : void
183 0 : PrincipalVerifier::CompleteOnInitiatingThread()
184 : {
185 0 : AssertIsOnBackgroundThread();
186 0 : ListenerList::ForwardIterator iter(mListenerList);
187 0 : while (iter.HasMore()) {
188 0 : iter.GetNext()->OnPrincipalVerified(mResult, mManagerId);
189 : }
190 :
191 : // The listener must clear its reference in OnPrincipalVerified()
192 0 : MOZ_DIAGNOSTIC_ASSERT(mListenerList.IsEmpty());
193 0 : }
194 :
195 : void
196 0 : PrincipalVerifier::DispatchToInitiatingThread(nsresult aRv)
197 : {
198 0 : MOZ_ASSERT(NS_IsMainThread());
199 :
200 0 : mResult = aRv;
201 :
202 : // The Cache ShutdownObserver does not track all principal verifiers, so we
203 : // cannot ensure this always succeeds. Instead, simply warn on failures.
204 : // This will result in a new CacheStorage object delaying operations until
205 : // shutdown completes and the browser goes away. This is as graceful as
206 : // we can get here.
207 0 : nsresult rv = mInitiatingEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL);
208 0 : if (NS_FAILED(rv)) {
209 0 : NS_WARNING("Cache unable to complete principal verification due to shutdown.");
210 : }
211 0 : }
212 :
213 : } // namespace cache
214 : } // namespace dom
215 : } // namespace mozilla
|