content/base/src/nsCSPService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "prlog.h"
michael@0 7 #include "nsString.h"
michael@0 8 #include "nsCOMPtr.h"
michael@0 9 #include "nsIURI.h"
michael@0 10 #include "nsIPrincipal.h"
michael@0 11 #include "nsIObserver.h"
michael@0 12 #include "nsIContent.h"
michael@0 13 #include "nsCSPService.h"
michael@0 14 #include "nsIContentSecurityPolicy.h"
michael@0 15 #include "nsIChannelPolicy.h"
michael@0 16 #include "nsIChannelEventSink.h"
michael@0 17 #include "nsIPropertyBag2.h"
michael@0 18 #include "nsIWritablePropertyBag2.h"
michael@0 19 #include "nsError.h"
michael@0 20 #include "nsChannelProperties.h"
michael@0 21 #include "nsIAsyncVerifyRedirectCallback.h"
michael@0 22 #include "nsAsyncRedirectVerifyHelper.h"
michael@0 23 #include "mozilla/Preferences.h"
michael@0 24 #include "nsIScriptError.h"
michael@0 25 #include "nsContentUtils.h"
michael@0 26
michael@0 27 using namespace mozilla;
michael@0 28
michael@0 29 /* Keeps track of whether or not CSP is enabled */
michael@0 30 bool CSPService::sCSPEnabled = true;
michael@0 31
michael@0 32 #ifdef PR_LOGGING
michael@0 33 static PRLogModuleInfo* gCspPRLog;
michael@0 34 #endif
michael@0 35
michael@0 36 CSPService::CSPService()
michael@0 37 {
michael@0 38 Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
michael@0 39
michael@0 40 #ifdef PR_LOGGING
michael@0 41 if (!gCspPRLog)
michael@0 42 gCspPRLog = PR_NewLogModule("CSP");
michael@0 43 #endif
michael@0 44 }
michael@0 45
michael@0 46 CSPService::~CSPService()
michael@0 47 {
michael@0 48 mAppStatusCache.Clear();
michael@0 49 }
michael@0 50
michael@0 51 NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
michael@0 52
michael@0 53 /* nsIContentPolicy implementation */
michael@0 54 NS_IMETHODIMP
michael@0 55 CSPService::ShouldLoad(uint32_t aContentType,
michael@0 56 nsIURI *aContentLocation,
michael@0 57 nsIURI *aRequestOrigin,
michael@0 58 nsISupports *aRequestContext,
michael@0 59 const nsACString &aMimeTypeGuess,
michael@0 60 nsISupports *aExtra,
michael@0 61 nsIPrincipal *aRequestPrincipal,
michael@0 62 int16_t *aDecision)
michael@0 63 {
michael@0 64 if (!aContentLocation)
michael@0 65 return NS_ERROR_FAILURE;
michael@0 66
michael@0 67 #ifdef PR_LOGGING
michael@0 68 {
michael@0 69 nsAutoCString location;
michael@0 70 aContentLocation->GetSpec(location);
michael@0 71 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 72 ("CSPService::ShouldLoad called for %s", location.get()));
michael@0 73 }
michael@0 74 #endif
michael@0 75
michael@0 76 // default decision, CSP can revise it if there's a policy to enforce
michael@0 77 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 78
michael@0 79 // No need to continue processing if CSP is disabled
michael@0 80 if (!sCSPEnabled)
michael@0 81 return NS_OK;
michael@0 82
michael@0 83 // shortcut for about: chrome: and resource: and javascript: uris since
michael@0 84 // they're not subject to CSP content policy checks.
michael@0 85 bool schemeMatch = false;
michael@0 86 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
michael@0 87 if (schemeMatch)
michael@0 88 return NS_OK;
michael@0 89 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
michael@0 90 if (schemeMatch)
michael@0 91 return NS_OK;
michael@0 92 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
michael@0 93 if (schemeMatch)
michael@0 94 return NS_OK;
michael@0 95 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
michael@0 96 if (schemeMatch)
michael@0 97 return NS_OK;
michael@0 98
michael@0 99
michael@0 100 // These content types are not subject to CSP content policy checks:
michael@0 101 // TYPE_CSP_REPORT, TYPE_REFRESH, TYPE_DOCUMENT
michael@0 102 // (their mappings are null in contentSecurityPolicy.js)
michael@0 103 if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
michael@0 104 aContentType == nsIContentPolicy::TYPE_REFRESH ||
michael@0 105 aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
michael@0 106 return NS_OK;
michael@0 107 }
michael@0 108
michael@0 109 // ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
michael@0 110 // ----- PLEASE REMOVE ONCE bug 925004 LANDS. -----
michael@0 111
michael@0 112 // Cache the app status for this origin.
michael@0 113 uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
michael@0 114 nsAutoCString contentOrigin;
michael@0 115 aContentLocation->GetPrePath(contentOrigin);
michael@0 116 if (aRequestPrincipal && !mAppStatusCache.Get(contentOrigin, &status)) {
michael@0 117 aRequestPrincipal->GetAppStatus(&status);
michael@0 118 mAppStatusCache.Put(contentOrigin, status);
michael@0 119 }
michael@0 120
michael@0 121 if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
michael@0 122 // The CSP for certified apps is :
michael@0 123 // "default-src *; script-src 'self'; object-src 'none'; style-src 'self'"
michael@0 124 // That means we can optimize for this case by:
michael@0 125 // - loading only same origin scripts and stylesheets.
michael@0 126 // - never loading objects.
michael@0 127 // - accepting everything else.
michael@0 128
michael@0 129 switch (aContentType) {
michael@0 130 case nsIContentPolicy::TYPE_SCRIPT:
michael@0 131 case nsIContentPolicy::TYPE_STYLESHEET:
michael@0 132 {
michael@0 133 nsAutoCString sourceOrigin;
michael@0 134 aRequestOrigin->GetPrePath(sourceOrigin);
michael@0 135 if (!sourceOrigin.Equals(contentOrigin)) {
michael@0 136 *aDecision = nsIContentPolicy::REJECT_SERVER;
michael@0 137 }
michael@0 138 }
michael@0 139 break;
michael@0 140
michael@0 141 case nsIContentPolicy::TYPE_OBJECT:
michael@0 142 *aDecision = nsIContentPolicy::REJECT_SERVER;
michael@0 143 break;
michael@0 144
michael@0 145 default:
michael@0 146 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 147 }
michael@0 148
michael@0 149 // Only cache and return if we are successful. If not, we want the error
michael@0 150 // to be reported, and thus fallback to the slow path.
michael@0 151 if (*aDecision == nsIContentPolicy::ACCEPT) {
michael@0 152 return NS_OK;
michael@0 153 }
michael@0 154 }
michael@0 155
michael@0 156 // ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
michael@0 157
michael@0 158 // find the principal of the document that initiated this request and see
michael@0 159 // if it has a CSP policy object
michael@0 160 nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
michael@0 161 nsCOMPtr<nsIPrincipal> principal;
michael@0 162 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 163 if (node) {
michael@0 164 principal = node->NodePrincipal();
michael@0 165 principal->GetCsp(getter_AddRefs(csp));
michael@0 166
michael@0 167 if (csp) {
michael@0 168 #ifdef PR_LOGGING
michael@0 169 {
michael@0 170 int numPolicies = 0;
michael@0 171 nsresult rv = csp->GetPolicyCount(&numPolicies);
michael@0 172 if (NS_SUCCEEDED(rv)) {
michael@0 173 for (int i=0; i<numPolicies; i++) {
michael@0 174 nsAutoString policy;
michael@0 175 csp->GetPolicy(i, policy);
michael@0 176 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 177 ("Document has CSP[%d]: %s", i,
michael@0 178 NS_ConvertUTF16toUTF8(policy).get()));
michael@0 179 }
michael@0 180 }
michael@0 181 }
michael@0 182 #endif
michael@0 183 // obtain the enforcement decision
michael@0 184 // (don't pass aExtra, we use that slot for redirects)
michael@0 185 csp->ShouldLoad(aContentType,
michael@0 186 aContentLocation,
michael@0 187 aRequestOrigin,
michael@0 188 aRequestContext,
michael@0 189 aMimeTypeGuess,
michael@0 190 nullptr,
michael@0 191 aDecision);
michael@0 192 }
michael@0 193 }
michael@0 194 #ifdef PR_LOGGING
michael@0 195 else {
michael@0 196 nsAutoCString uriSpec;
michael@0 197 aContentLocation->GetSpec(uriSpec);
michael@0 198 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 199 ("COULD NOT get nsINode for location: %s", uriSpec.get()));
michael@0 200 }
michael@0 201 #endif
michael@0 202
michael@0 203 return NS_OK;
michael@0 204 }
michael@0 205
michael@0 206 NS_IMETHODIMP
michael@0 207 CSPService::ShouldProcess(uint32_t aContentType,
michael@0 208 nsIURI *aContentLocation,
michael@0 209 nsIURI *aRequestOrigin,
michael@0 210 nsISupports *aRequestContext,
michael@0 211 const nsACString &aMimeTypeGuess,
michael@0 212 nsISupports *aExtra,
michael@0 213 nsIPrincipal *aRequestPrincipal,
michael@0 214 int16_t *aDecision)
michael@0 215 {
michael@0 216 if (!aContentLocation)
michael@0 217 return NS_ERROR_FAILURE;
michael@0 218
michael@0 219 // default decision is to accept the item
michael@0 220 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 221
michael@0 222 // No need to continue processing if CSP is disabled
michael@0 223 if (!sCSPEnabled)
michael@0 224 return NS_OK;
michael@0 225
michael@0 226 // find the nsDocument that initiated this request and see if it has a
michael@0 227 // CSP policy object
michael@0 228 nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
michael@0 229 nsCOMPtr<nsIPrincipal> principal;
michael@0 230 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 231 if (node) {
michael@0 232 principal = node->NodePrincipal();
michael@0 233 principal->GetCsp(getter_AddRefs(csp));
michael@0 234
michael@0 235 if (csp) {
michael@0 236 #ifdef PR_LOGGING
michael@0 237 {
michael@0 238 int numPolicies = 0;
michael@0 239 nsresult rv = csp->GetPolicyCount(&numPolicies);
michael@0 240 if (NS_SUCCEEDED(rv)) {
michael@0 241 for (int i=0; i<numPolicies; i++) {
michael@0 242 nsAutoString policy;
michael@0 243 csp->GetPolicy(i, policy);
michael@0 244 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 245 ("shouldProcess - document has policy[%d]: %s", i,
michael@0 246 NS_ConvertUTF16toUTF8(policy).get()));
michael@0 247 }
michael@0 248 }
michael@0 249 }
michael@0 250 #endif
michael@0 251 // obtain the enforcement decision
michael@0 252 csp->ShouldProcess(aContentType,
michael@0 253 aContentLocation,
michael@0 254 aRequestOrigin,
michael@0 255 aRequestContext,
michael@0 256 aMimeTypeGuess,
michael@0 257 aExtra,
michael@0 258 aDecision);
michael@0 259 }
michael@0 260 }
michael@0 261 #ifdef PR_LOGGING
michael@0 262 else {
michael@0 263 nsAutoCString uriSpec;
michael@0 264 aContentLocation->GetSpec(uriSpec);
michael@0 265 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 266 ("COULD NOT get nsINode for location: %s", uriSpec.get()));
michael@0 267 }
michael@0 268 #endif
michael@0 269 return NS_OK;
michael@0 270 }
michael@0 271
michael@0 272 /* nsIChannelEventSink implementation */
michael@0 273 NS_IMETHODIMP
michael@0 274 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
michael@0 275 nsIChannel *newChannel,
michael@0 276 uint32_t flags,
michael@0 277 nsIAsyncVerifyRedirectCallback *callback)
michael@0 278 {
michael@0 279 nsAsyncRedirectAutoCallback autoCallback(callback);
michael@0 280
michael@0 281 // get the Content Security Policy and load type from the property bag
michael@0 282 nsCOMPtr<nsISupports> policyContainer;
michael@0 283 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
michael@0 284 if (!props)
michael@0 285 return NS_OK;
michael@0 286
michael@0 287 props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
michael@0 288 NS_GET_IID(nsISupports),
michael@0 289 getter_AddRefs(policyContainer));
michael@0 290
michael@0 291 // see if we have a valid nsIChannelPolicy containing CSP and load type
michael@0 292 nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
michael@0 293 if (!channelPolicy)
michael@0 294 return NS_OK;
michael@0 295
michael@0 296 nsCOMPtr<nsISupports> supports;
michael@0 297 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 298 channelPolicy->GetContentSecurityPolicy(getter_AddRefs(supports));
michael@0 299 csp = do_QueryInterface(supports);
michael@0 300 uint32_t loadType;
michael@0 301 channelPolicy->GetLoadType(&loadType);
michael@0 302
michael@0 303 // if no CSP in the channelPolicy, nothing for us to add to the channel
michael@0 304 if (!csp)
michael@0 305 return NS_OK;
michael@0 306
michael@0 307 /* Since redirecting channels don't call into nsIContentPolicy, we call our
michael@0 308 * Content Policy implementation directly when redirects occur. When channels
michael@0 309 * are created using NS_NewChannel(), callers can optionally pass in a
michael@0 310 * nsIChannelPolicy containing a CSP object and load type, which is placed in
michael@0 311 * the new channel's property bag. This container is propagated forward when
michael@0 312 * channels redirect.
michael@0 313 */
michael@0 314
michael@0 315 // Does the CSP permit this host for this type of load?
michael@0 316 // If not, cancel the load now.
michael@0 317 nsCOMPtr<nsIURI> newUri;
michael@0 318 newChannel->GetURI(getter_AddRefs(newUri));
michael@0 319 nsCOMPtr<nsIURI> originalUri;
michael@0 320 oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
michael@0 321 int16_t aDecision = nsIContentPolicy::ACCEPT;
michael@0 322 csp->ShouldLoad(loadType, // load type per nsIContentPolicy (uint32_t)
michael@0 323 newUri, // nsIURI
michael@0 324 nullptr, // nsIURI
michael@0 325 nullptr, // nsISupports
michael@0 326 EmptyCString(), // ACString - MIME guess
michael@0 327 originalUri, // nsISupports - extra
michael@0 328 &aDecision);
michael@0 329
michael@0 330 #ifdef PR_LOGGING
michael@0 331 if (newUri) {
michael@0 332 nsAutoCString newUriSpec("None");
michael@0 333 newUri->GetSpec(newUriSpec);
michael@0 334 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 335 ("CSPService::AsyncOnChannelRedirect called for %s",
michael@0 336 newUriSpec.get()));
michael@0 337 }
michael@0 338 if (aDecision == 1)
michael@0 339 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 340 ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
michael@0 341 else
michael@0 342 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
michael@0 343 ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
michael@0 344 #endif
michael@0 345
michael@0 346 // if ShouldLoad doesn't accept the load, cancel the request
michael@0 347 if (aDecision != 1) {
michael@0 348 autoCallback.DontCallback();
michael@0 349 return NS_BINDING_FAILED;
michael@0 350 }
michael@0 351
michael@0 352 // the redirect is permitted, so propagate the Content Security Policy
michael@0 353 // and load type to the redirecting channel
michael@0 354 nsresult rv;
michael@0 355 nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
michael@0 356 if (props2) {
michael@0 357 rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
michael@0 358 channelPolicy);
michael@0 359 if (NS_SUCCEEDED(rv)) {
michael@0 360 return NS_OK;
michael@0 361 }
michael@0 362 }
michael@0 363
michael@0 364 // The redirecting channel isn't a writable property bag, we won't be able
michael@0 365 // to enforce the load policy if it redirects again, so we stop it now.
michael@0 366 nsAutoCString newUriSpec;
michael@0 367 rv = newUri->GetSpec(newUriSpec);
michael@0 368 const char16_t *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
michael@0 369 if (NS_SUCCEEDED(rv)) {
michael@0 370 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
michael@0 371 NS_LITERAL_CSTRING("Redirect Error"), nullptr,
michael@0 372 nsContentUtils::eDOM_PROPERTIES,
michael@0 373 "InvalidRedirectChannelWarning",
michael@0 374 formatParams, 1);
michael@0 375 }
michael@0 376
michael@0 377 return NS_BINDING_FAILED;
michael@0 378 }

mercurial