LCOV - code coverage report
Current view: top level - netwerk/protocol/http - HSTSPrimerListener.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 176 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 12 0.0 %
Legend: Lines: hit not hit

          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             : #include "HttpLog.h"
       7             : 
       8             : #include "nsHttp.h"
       9             : 
      10             : #include "HSTSPrimerListener.h"
      11             : #include "nsIHstsPrimingCallback.h"
      12             : #include "nsIPrincipal.h"
      13             : #include "nsSecurityHeaderParser.h"
      14             : #include "nsISiteSecurityService.h"
      15             : #include "nsISocketProvider.h"
      16             : #include "nsISSLStatus.h"
      17             : #include "nsISSLStatusProvider.h"
      18             : #include "nsStreamUtils.h"
      19             : #include "nsStreamListenerWrapper.h"
      20             : #include "nsHttpChannel.h"
      21             : #include "LoadInfo.h"
      22             : #include "mozilla/Unused.h"
      23             : #include "nsQueryObject.h"
      24             : 
      25             : namespace mozilla {
      26             : namespace net {
      27             : 
      28             : using namespace mozilla;
      29             : 
      30           0 : NS_IMPL_ISUPPORTS(HSTSPrimingListener, nsIStreamListener,
      31             :                   nsIRequestObserver, nsIInterfaceRequestor,
      32             :                   nsITimerCallback)
      33             : 
      34             : // default to 2000ms, same as the preference
      35             : // security.mixed_content.hsts_priming_request_timeout
      36             : uint32_t HSTSPrimingListener::sHSTSPrimingTimeout = 2000;
      37             : 
      38             : 
      39           0 : HSTSPrimingListener::HSTSPrimingListener(nsIHstsPrimingCallback* aCallback)
      40           0 :   : mCallback(aCallback)
      41             : {
      42             :   static nsresult rv =
      43           0 :     Preferences::AddUintVarCache(&sHSTSPrimingTimeout,
      44           0 :         "security.mixed_content.hsts_priming_request_timeout");
      45             :   Unused << rv;
      46           0 : }
      47             : 
      48             : NS_IMETHODIMP
      49           0 : HSTSPrimingListener::GetInterface(const nsIID & aIID, void **aResult)
      50             : {
      51           0 :   return QueryInterface(aIID, aResult);
      52             : }
      53             : 
      54             : void
      55           0 : HSTSPrimingListener::ReportTiming(nsIHstsPrimingCallback* aCallback, nsresult aResult)
      56             : {
      57             :   nsCOMPtr<nsITimedChannel> timingChannel =
      58           0 :     do_QueryInterface(aCallback);
      59           0 :   if (!timingChannel) {
      60           0 :     LOG(("HSTS priming: mCallback is not an nsITimedChannel!"));
      61           0 :     return;
      62             :   }
      63             : 
      64           0 :   TimeStamp channelCreationTime;
      65           0 :   nsresult rv = timingChannel->GetChannelCreation(&channelCreationTime);
      66           0 :   if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
      67             :     PRUint32 interval =
      68           0 :       (PRUint32) (TimeStamp::Now() - channelCreationTime).ToMilliseconds();
      69           0 :     Telemetry::Accumulate(Telemetry::HSTS_PRIMING_REQUEST_DURATION,
      70           0 :         (NS_SUCCEEDED(aResult)) ? NS_LITERAL_CSTRING("success")
      71           0 :                                 : NS_LITERAL_CSTRING("failure"),
      72           0 :         interval);
      73             :   }
      74             : }
      75             : 
      76             : NS_IMETHODIMP
      77           0 : HSTSPrimingListener::OnStartRequest(nsIRequest *aRequest,
      78             :                                     nsISupports *aContext)
      79             : {
      80           0 :   nsCOMPtr<nsIHstsPrimingCallback> callback;
      81           0 :   callback.swap(mCallback);
      82             : 
      83           0 :   if (mHSTSPrimingTimer) {
      84           0 :     Unused << mHSTSPrimingTimer->Cancel();
      85           0 :     mHSTSPrimingTimer = nullptr;
      86             :   }
      87             : 
      88             :   // if callback is null, we have already canceled this request and reported
      89             :   // the failure
      90           0 :   if (!callback) {
      91           0 :     return NS_OK;
      92             :   }
      93             : 
      94           0 :   nsresult primingResult = CheckHSTSPrimingRequestStatus(aRequest);
      95           0 :   ReportTiming(callback, primingResult);
      96             : 
      97           0 :   if (NS_FAILED(primingResult)) {
      98           0 :     LOG(("HSTS Priming Failed (request was not approved)"));
      99           0 :     return callback->OnHSTSPrimingFailed(primingResult, false);
     100             :   }
     101             : 
     102           0 :   LOG(("HSTS Priming Succeeded (request was approved)"));
     103           0 :   return callback->OnHSTSPrimingSucceeded(false);
     104             : }
     105             : 
     106             : NS_IMETHODIMP
     107           0 : HSTSPrimingListener::OnStopRequest(nsIRequest *aRequest,
     108             :                                    nsISupports *aContext,
     109             :                                    nsresult aStatus)
     110             : {
     111           0 :   return NS_OK;
     112             : }
     113             : 
     114             : nsresult
     115           0 : HSTSPrimingListener::CheckHSTSPrimingRequestStatus(nsIRequest* aRequest)
     116             : {
     117             :   nsresult status;
     118           0 :   nsresult rv = aRequest->GetStatus(&status);
     119           0 :   NS_ENSURE_SUCCESS(rv, rv);
     120           0 :   if (NS_FAILED(status)) {
     121           0 :     return NS_ERROR_CONTENT_BLOCKED;
     122             :   }
     123             : 
     124             :   // Test that things worked on a HTTP level
     125           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
     126           0 :   NS_ENSURE_STATE(httpChannel);
     127           0 :   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
     128           0 :   NS_ENSURE_STATE(internal);
     129             : 
     130             :   bool succeedded;
     131           0 :   rv = httpChannel->GetRequestSucceeded(&succeedded);
     132           0 :   if (NS_FAILED(rv) || !succeedded) {
     133             :     // If the request did not return a 2XX response, don't process it
     134           0 :     return NS_ERROR_CONTENT_BLOCKED;
     135             :   }
     136             : 
     137           0 :   bool synthesized = false;
     138           0 :   RefPtr<nsHttpChannel> rawHttpChannel = do_QueryObject(httpChannel);
     139           0 :   NS_ENSURE_STATE(rawHttpChannel);
     140             : 
     141           0 :   rv = rawHttpChannel->GetResponseSynthesized(&synthesized);
     142           0 :   NS_ENSURE_SUCCESS(rv, rv);
     143           0 :   if (synthesized) {
     144             :     // Don't consider synthesized responses
     145           0 :     return NS_ERROR_CONTENT_BLOCKED;
     146             :   }
     147             : 
     148             :   // check to see if the HSTS cache was updated
     149           0 :   nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
     150           0 :   NS_ENSURE_SUCCESS(rv, rv);
     151             : 
     152           0 :   nsCOMPtr<nsIURI> uri;
     153           0 :   rv = httpChannel->GetURI(getter_AddRefs(uri));
     154           0 :   NS_ENSURE_SUCCESS(rv, rv);
     155           0 :   NS_ENSURE_TRUE(uri, NS_ERROR_CONTENT_BLOCKED);
     156             : 
     157           0 :   OriginAttributes originAttributes;
     158           0 :   NS_GetOriginAttributes(httpChannel, originAttributes);
     159             : 
     160             :   bool hsts;
     161           0 :   rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0,
     162           0 :                         originAttributes, nullptr, nullptr, &hsts);
     163           0 :   NS_ENSURE_SUCCESS(rv, rv);
     164             : 
     165           0 :   if (hsts) {
     166             :     // An HSTS upgrade was found
     167           0 :     return NS_OK;
     168             :   }
     169             : 
     170             :   // There is no HSTS upgrade available
     171           0 :   return NS_ERROR_CONTENT_BLOCKED;
     172             : }
     173             : 
     174             : /** nsIStreamListener methods **/
     175             : 
     176             : NS_IMETHODIMP
     177           0 : HSTSPrimingListener::OnDataAvailable(nsIRequest *aRequest,
     178             :                                      nsISupports *ctxt,
     179             :                                      nsIInputStream *inStr,
     180             :                                      uint64_t sourceOffset,
     181             :                                      uint32_t count)
     182             : {
     183             :   uint32_t totalRead;
     184           0 :   return inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &totalRead);
     185             : }
     186             : 
     187             : /** nsITimerCallback **/
     188             : NS_IMETHODIMP
     189           0 : HSTSPrimingListener::Notify(nsITimer* timer)
     190             : {
     191             :   nsresult rv;
     192           0 :   nsCOMPtr<nsIHstsPrimingCallback> callback;
     193           0 :   callback.swap(mCallback);
     194           0 :   if (!callback) {
     195             :     // we already processed this channel
     196           0 :     return NS_OK;
     197             :   }
     198             : 
     199           0 :   ReportTiming(callback, NS_ERROR_HSTS_PRIMING_TIMEOUT);
     200             : 
     201           0 :   if (mPrimingChannel) {
     202           0 :     rv = mPrimingChannel->Cancel(NS_ERROR_HSTS_PRIMING_TIMEOUT);
     203           0 :     if (NS_FAILED(rv)) {
     204           0 :       NS_ERROR("HSTS Priming timed out, and we got an error canceling the priming channel.");
     205             :     }
     206             :   }
     207             : 
     208           0 :   rv = callback->OnHSTSPrimingFailed(NS_ERROR_HSTS_PRIMING_TIMEOUT, false);
     209           0 :   if (NS_FAILED(rv)) {
     210           0 :     NS_ERROR("HSTS Priming timed out, and we got an error reporting the failure.");
     211             :   }
     212             : 
     213           0 :   return NS_OK; // unused
     214             : }
     215             : 
     216             : // static
     217             : nsresult
     218           0 : HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
     219             :                                       nsIHstsPrimingCallback* aCallback)
     220             : {
     221           0 :   nsCOMPtr<nsIURI> finalChannelURI;
     222           0 :   nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(finalChannelURI));
     223           0 :   NS_ENSURE_SUCCESS(rv, rv);
     224             : 
     225           0 :   nsCOMPtr<nsIURI> uri;
     226           0 :   rv = NS_GetSecureUpgradedURI(finalChannelURI, getter_AddRefs(uri));
     227           0 :   NS_ENSURE_SUCCESS(rv,rv);
     228             : 
     229             :   // check the HSTS cache
     230             :   bool hsts;
     231             :   bool hstsCached;
     232           0 :   nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
     233           0 :   NS_ENSURE_SUCCESS(rv, rv);
     234             : 
     235           0 :   OriginAttributes originAttributes;
     236           0 :   NS_GetOriginAttributes(aRequestChannel, originAttributes);
     237             : 
     238           0 :   rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0,
     239           0 :                         originAttributes, &hstsCached, nullptr, &hsts);
     240           0 :   NS_ENSURE_SUCCESS(rv, rv);
     241             : 
     242           0 :   if (hsts) {
     243             :     // already saw this host and will upgrade if allowed by preferences
     244             :     Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     245           0 :                           HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_CACHED_HSTS);
     246           0 :     return aCallback->OnHSTSPrimingSucceeded(true);
     247             :   }
     248             : 
     249           0 :   if (hstsCached) {
     250             :     // there is a non-expired entry in the cache that doesn't allow us to
     251             :     // upgrade, so go ahead and fail early.
     252             :     Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     253           0 :                           HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_CACHED_NO_HSTS);
     254           0 :     return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
     255             :   }
     256             : 
     257             :   // Either it wasn't cached or the cached result has expired. Build a
     258             :   // channel for the HEAD request.
     259             : 
     260           0 :   nsCOMPtr<nsILoadInfo> originalLoadInfo = aRequestChannel->GetLoadInfo();
     261           0 :   MOZ_ASSERT(originalLoadInfo, "can not perform HSTS priming without a loadInfo");
     262           0 :   if (!originalLoadInfo) {
     263           0 :     return NS_ERROR_FAILURE;
     264             :   }
     265             : 
     266             :   nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
     267           0 :     (originalLoadInfo.get())->CloneForNewRequest();
     268           0 :   loadInfo->SetIsHSTSPriming(true);
     269             : 
     270             :   // the LoadInfo must have a security flag set in order to pass through priming
     271             :   // if none of these security flags are set, go ahead and fail now instead of
     272             :   // crashing in nsContentSecurityManager::ValidateSecurityFlags
     273           0 :   nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
     274           0 :   if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
     275           0 :       securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
     276           0 :       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
     277           0 :       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
     278             :       securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
     279           0 :     return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
     280             :   }
     281             : 
     282           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
     283           0 :   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
     284           0 :   NS_ENSURE_SUCCESS(rv, rv);
     285             : 
     286             :   nsLoadFlags loadFlags;
     287           0 :   rv = aRequestChannel->GetLoadFlags(&loadFlags);
     288           0 :   NS_ENSURE_SUCCESS(rv, rv);
     289             : 
     290           0 :   loadFlags &= HttpBaseChannel::INHIBIT_CACHING |
     291             :                HttpBaseChannel::INHIBIT_PERSISTENT_CACHING |
     292             :                HttpBaseChannel::LOAD_BYPASS_CACHE |
     293             :                HttpBaseChannel::LOAD_FROM_CACHE |
     294           0 :                HttpBaseChannel::VALIDATE_ALWAYS;
     295             :   // Priming requests should never be intercepted by service workers and
     296             :   // are always anonymous.
     297           0 :   loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
     298           0 :                nsIRequest::LOAD_ANONYMOUS;
     299             : 
     300             :   // Create a new channel to send the priming request
     301           0 :   nsCOMPtr<nsIChannel> primingChannel;
     302           0 :   rv = NS_NewChannelInternal(getter_AddRefs(primingChannel),
     303             :                              uri,
     304             :                              loadInfo,
     305             :                              loadGroup,
     306             :                              nullptr,   // aCallbacks are set later
     307             :                              loadFlags);
     308           0 :   NS_ENSURE_SUCCESS(rv, rv);
     309             : 
     310             :   // Set method and headers
     311           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(primingChannel);
     312           0 :   if (!httpChannel) {
     313           0 :     NS_ERROR("HSTSPrimingListener: Failed to QI to nsIHttpChannel!");
     314           0 :     return NS_ERROR_FAILURE;
     315             :   }
     316           0 :   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(primingChannel);
     317           0 :   NS_ENSURE_STATE(internal);
     318             : 
     319             :   // Since this is a perfomrance critical request (blocks the page load) we
     320             :   // want to get the response ASAP.
     321           0 :   nsCOMPtr<nsIClassOfService> classOfService(do_QueryInterface(primingChannel));
     322           0 :   if (classOfService) {
     323           0 :     classOfService->AddClassFlags(nsIClassOfService::UrgentStart);
     324             :   }
     325             : 
     326             :   // Currently using HEAD per the draft, but under discussion to change to GET
     327             :   // with credentials so if the upgrade is approved the result is already cached.
     328           0 :   rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
     329           0 :   NS_ENSURE_SUCCESS(rv, rv);
     330             : 
     331           0 :   rv = httpChannel->
     332           0 :     SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
     333           0 :                      NS_LITERAL_CSTRING("1"), false);
     334           0 :   NS_ENSURE_SUCCESS(rv, rv);
     335             : 
     336             :   // attempt to set the class of service flags on the new channel
     337           0 :   nsCOMPtr<nsIClassOfService> requestClass = do_QueryInterface(aRequestChannel);
     338           0 :   if (!requestClass) {
     339           0 :     NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
     340           0 :     return NS_ERROR_FAILURE;
     341             :   }
     342           0 :   nsCOMPtr<nsIClassOfService> primingClass = do_QueryInterface(httpChannel);
     343           0 :   if (!primingClass) {
     344           0 :     NS_ERROR("HSTSPrimingListener: httpChannel is not an nsIClassOfService");
     345           0 :     return NS_ERROR_FAILURE;
     346             :   }
     347             : 
     348           0 :   uint32_t classFlags = 0;
     349           0 :   rv = requestClass ->GetClassFlags(&classFlags);
     350           0 :   NS_ENSURE_SUCCESS(rv, rv);
     351           0 :   rv = primingClass->SetClassFlags(classFlags);
     352           0 :   NS_ENSURE_SUCCESS(rv, rv);
     353             : 
     354             :   // The priming channel should have highest priority so that it completes as
     355             :   // quickly as possible, allowing the load to proceed.
     356           0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(primingChannel);
     357           0 :   if (p) {
     358           0 :     uint32_t priority = nsISupportsPriority::PRIORITY_HIGHEST;
     359             : 
     360           0 :     p->SetPriority(priority);
     361             :   }
     362             : 
     363             :   // Set up listener which will start the original channel
     364           0 :   HSTSPrimingListener* listener = new HSTSPrimingListener(aCallback);
     365             :   // Start priming
     366           0 :   rv = primingChannel->AsyncOpen2(listener);
     367           0 :   NS_ENSURE_SUCCESS(rv, rv);
     368           0 :   listener->mPrimingChannel.swap(primingChannel);
     369             : 
     370           0 :   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
     371           0 :   NS_ENSURE_STATE(timer);
     372             : 
     373           0 :   rv = timer->InitWithCallback(listener,
     374             :                                sHSTSPrimingTimeout,
     375           0 :                                nsITimer::TYPE_ONE_SHOT);
     376           0 :   if (NS_FAILED(rv)) {
     377           0 :     NS_ERROR("HSTS Priming failed to initialize channel cancellation timer");
     378             :   }
     379             : 
     380           0 :   listener->mHSTSPrimingTimer.swap(timer);
     381             : 
     382             :   Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     383           0 :                         HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_SENT);
     384             : 
     385           0 :   return NS_OK;
     386             : }
     387             : 
     388             : } // namespace net
     389             : } // namespace mozilla

Generated by: LCOV version 1.13