PrincipalVerifier.cpp (5740B)
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 "CacheCommon.h" 10 #include "ErrorList.h" 11 #include "mozilla/BasePrincipal.h" 12 #include "mozilla/dom/ContentParent.h" 13 #include "mozilla/dom/QMResult.h" 14 #include "mozilla/dom/cache/ManagerId.h" 15 #include "mozilla/dom/quota/ResultExtensions.h" 16 #include "mozilla/ipc/BackgroundParent.h" 17 #include "mozilla/ipc/BackgroundUtils.h" 18 #include "mozilla/ipc/PBackgroundParent.h" 19 #include "nsCOMPtr.h" 20 #include "nsContentUtils.h" 21 #include "nsIPrincipal.h" 22 #include "nsNetUtil.h" 23 24 namespace mozilla::dom::cache { 25 26 using mozilla::ipc::AssertIsOnBackgroundThread; 27 using mozilla::ipc::BackgroundParent; 28 using mozilla::ipc::PBackgroundParent; 29 using mozilla::ipc::PrincipalInfo; 30 using mozilla::ipc::PrincipalInfoToPrincipal; 31 32 // static 33 already_AddRefed<PrincipalVerifier> PrincipalVerifier::CreateAndDispatch( 34 Listener& aListener, PBackgroundParent* aActor, 35 const PrincipalInfo& aPrincipalInfo) { 36 // We must get the ContentParent actor from the PBackgroundParent. This 37 // only works on the PBackground thread. 38 AssertIsOnBackgroundThread(); 39 40 RefPtr<PrincipalVerifier> verifier = 41 new PrincipalVerifier(aListener, aActor, aPrincipalInfo); 42 43 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(verifier)); 44 45 return verifier.forget(); 46 } 47 48 void PrincipalVerifier::AddListener(Listener& aListener) { 49 AssertIsOnBackgroundThread(); 50 MOZ_ASSERT(!mListenerList.Contains(&aListener)); 51 mListenerList.AppendElement(WrapNotNullUnchecked(&aListener)); 52 } 53 54 void PrincipalVerifier::RemoveListener(Listener& aListener) { 55 AssertIsOnBackgroundThread(); 56 MOZ_ALWAYS_TRUE(mListenerList.RemoveElement(&aListener)); 57 } 58 59 PrincipalVerifier::PrincipalVerifier(Listener& aListener, 60 PBackgroundParent* aActor, 61 const PrincipalInfo& aPrincipalInfo) 62 : Runnable("dom::cache::PrincipalVerifier"), 63 mHandle(BackgroundParent::GetContentParentHandle(aActor)), 64 mPrincipalInfo(aPrincipalInfo), 65 mInitiatingEventTarget(GetCurrentSerialEventTarget()), 66 mResult(NS_OK) { 67 AssertIsOnBackgroundThread(); 68 MOZ_DIAGNOSTIC_ASSERT(mInitiatingEventTarget); 69 70 AddListener(aListener); 71 } 72 73 PrincipalVerifier::~PrincipalVerifier() { 74 // Since the PrincipalVerifier is a Runnable that executes on multiple 75 // threads, its a race to see which thread de-refs us last. Therefore 76 // we cannot guarantee which thread we destruct on. 77 78 MOZ_DIAGNOSTIC_ASSERT(mListenerList.IsEmpty()); 79 } 80 81 NS_IMETHODIMP 82 PrincipalVerifier::Run() { 83 // Executed twice. First, on the main thread and then back on the 84 // originating thread. 85 86 if (NS_IsMainThread()) { 87 VerifyOnMainThread(); 88 return NS_OK; 89 } 90 91 CompleteOnInitiatingThread(); 92 return NS_OK; 93 } 94 95 void PrincipalVerifier::VerifyOnMainThread() { 96 MOZ_ASSERT(NS_IsMainThread()); 97 98 QM_TRY_INSPECT( 99 const auto& principal, PrincipalInfoToPrincipal(mPrincipalInfo), QM_VOID, 100 [this](const nsresult result) { DispatchToInitiatingThread(result); }); 101 102 // We disallow null principal on the client side, but double-check here. 103 if (NS_WARN_IF(principal->GetIsNullPrincipal())) { 104 DispatchToInitiatingThread(NS_ERROR_FAILURE); 105 return; 106 } 107 108 // Verify if a child process uses system principal, which is not allowed 109 // to prevent system principal is spoofed. 110 if (NS_WARN_IF(mHandle && principal->IsSystemPrincipal())) { 111 DispatchToInitiatingThread(NS_ERROR_FAILURE); 112 return; 113 } 114 115 #ifdef DEBUG 116 nsresult rv = NS_OK; 117 // Sanity check principal origin by using it to construct a URI and security 118 // checking it. Don't do this for the system principal, though, as its origin 119 // is a synthetic [System Principal] string. 120 if (!principal->IsSystemPrincipal()) { 121 nsAutoCString origin; 122 rv = principal->GetOriginNoSuffix(origin); 123 if (NS_WARN_IF(NS_FAILED(rv))) { 124 DispatchToInitiatingThread(rv); 125 return; 126 } 127 nsCOMPtr<nsIURI> uri; 128 rv = NS_NewURI(getter_AddRefs(uri), origin); 129 if (NS_WARN_IF(NS_FAILED(rv))) { 130 DispatchToInitiatingThread(rv); 131 return; 132 } 133 rv = principal->CheckMayLoad(uri, false); 134 if (NS_WARN_IF(NS_FAILED(rv))) { 135 DispatchToInitiatingThread(rv); 136 return; 137 } 138 } 139 #endif 140 141 auto managerIdOrErr = ManagerId::Create(principal); 142 if (NS_WARN_IF(managerIdOrErr.isErr())) { 143 DispatchToInitiatingThread(managerIdOrErr.unwrapErr()); 144 return; 145 } 146 mManagerId = managerIdOrErr.unwrap(); 147 148 DispatchToInitiatingThread(NS_OK); 149 } 150 151 void PrincipalVerifier::CompleteOnInitiatingThread() { 152 AssertIsOnBackgroundThread(); 153 154 for (const auto& listener : mListenerList.ForwardRange()) { 155 listener->OnPrincipalVerified(mResult, mManagerId); 156 } 157 158 // The listener must clear its reference in OnPrincipalVerified() 159 MOZ_DIAGNOSTIC_ASSERT(mListenerList.IsEmpty()); 160 } 161 162 void PrincipalVerifier::DispatchToInitiatingThread(nsresult aRv) { 163 MOZ_ASSERT(NS_IsMainThread()); 164 165 mResult = aRv; 166 167 // The Cache ShutdownObserver does not track all principal verifiers, so we 168 // cannot ensure this always succeeds. Instead, simply warn on failures. 169 // This will result in a new CacheStorage object delaying operations until 170 // shutdown completes and the browser goes away. This is as graceful as 171 // we can get here. 172 QM_WARNONLY_TRY(QM_TO_RESULT( 173 mInitiatingEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL))); 174 } 175 176 } // namespace mozilla::dom::cache