LCOV - code coverage report
Current view: top level - security/manager/ssl - nsSiteSecurityService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 139 874 15.9 %
Date: 2017-07-14 16:53:18 Functions: 19 70 27.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "nsSiteSecurityService.h"
       6             : 
       7             : #include "CertVerifier.h"
       8             : #include "PublicKeyPinningService.h"
       9             : #include "ScopedNSSTypes.h"
      10             : #include "SharedCertVerifier.h"
      11             : #include "mozilla/Assertions.h"
      12             : #include "mozilla/Attributes.h"
      13             : #include "mozilla/Base64.h"
      14             : #include "mozilla/LinkedList.h"
      15             : #include "mozilla/Logging.h"
      16             : #include "mozilla/Preferences.h"
      17             : #include "mozilla/Tokenizer.h"
      18             : #include "mozilla/dom/PContent.h"
      19             : #include "mozilla/dom/ToJSValue.h"
      20             : #include "nsArrayEnumerator.h"
      21             : #include "nsCOMArray.h"
      22             : #include "nsISSLStatus.h"
      23             : #include "nsIScriptSecurityManager.h"
      24             : #include "nsISocketProvider.h"
      25             : #include "nsIURI.h"
      26             : #include "nsIX509Cert.h"
      27             : #include "nsNSSComponent.h"
      28             : #include "nsNetUtil.h"
      29             : #include "nsPromiseFlatString.h"
      30             : #include "nsReadableUtils.h"
      31             : #include "nsSecurityHeaderParser.h"
      32             : #include "nsThreadUtils.h"
      33             : #include "nsVariant.h"
      34             : #include "nsXULAppAPI.h"
      35             : #include "prnetdb.h"
      36             : 
      37             : // A note about the preload list:
      38             : // When a site specifically disables HSTS by sending a header with
      39             : // 'max-age: 0', we keep a "knockout" value that means "we have no information
      40             : // regarding the HSTS state of this host" (any ancestor of "this host" can still
      41             : // influence its HSTS status via include subdomains, however).
      42             : // This prevents the preload list from overriding the site's current
      43             : // desired HSTS status.
      44             : #include "nsSTSPreloadList.inc"
      45             : 
      46             : using namespace mozilla;
      47             : using namespace mozilla::psm;
      48             : 
      49             : static LazyLogModule gSSSLog("nsSSService");
      50             : 
      51             : #define SSSLOG(args) MOZ_LOG(gSSSLog, mozilla::LogLevel::Debug, args)
      52             : 
      53             : const char kHSTSKeySuffix[] = ":HSTS";
      54             : const char kHPKPKeySuffix[] = ":HPKP";
      55             : 
      56             : ////////////////////////////////////////////////////////////////////////////////
      57             : 
      58          32 : NS_IMPL_ISUPPORTS(SiteHSTSState, nsISiteSecurityState, nsISiteHSTSState)
      59             : 
      60             : namespace {
      61             : 
      62             : static bool
      63           0 : stringIsBase64EncodingOf256bitValue(const nsCString& encodedString) {
      64           0 :   nsAutoCString binaryValue;
      65           0 :   nsresult rv = Base64Decode(encodedString, binaryValue);
      66           0 :   if (NS_FAILED(rv)) {
      67           0 :     return false;
      68             :   }
      69             : 
      70           0 :   return binaryValue.Length() == SHA256_LENGTH;
      71             : }
      72             : 
      73          16 : class SSSTokenizer final : public Tokenizer
      74             : {
      75             : public:
      76          16 :   explicit SSSTokenizer(const nsACString& source)
      77          16 :     : Tokenizer(source)
      78             :   {
      79          16 :   }
      80             : 
      81             :   MOZ_MUST_USE bool
      82           0 :   ReadBool(/*out*/ bool& value)
      83             :   {
      84             :     uint8_t rawValue;
      85           0 :     if (!ReadInteger(&rawValue)) {
      86           0 :       return false;
      87             :     }
      88             : 
      89           0 :     if (rawValue != 0 && rawValue != 1) {
      90           0 :       return false;
      91             :     }
      92             : 
      93           0 :     value = (rawValue == 1);
      94           0 :     return true;
      95             :   }
      96             : 
      97             :   MOZ_MUST_USE bool
      98           0 :   ReadState(/*out*/ SecurityPropertyState& state)
      99             :   {
     100             :     uint32_t rawValue;
     101           0 :     if (!ReadInteger(&rawValue)) {
     102           0 :       return false;
     103             :     }
     104             : 
     105           0 :     state = static_cast<SecurityPropertyState>(rawValue);
     106           0 :     switch (state) {
     107             :       case SecurityPropertyKnockout:
     108             :       case SecurityPropertyNegative:
     109             :       case SecurityPropertySet:
     110             :       case SecurityPropertyUnset:
     111           0 :         break;
     112             :       default:
     113           0 :         return false;
     114             :     }
     115             : 
     116           0 :     return true;
     117             :   }
     118             : 
     119             :   MOZ_MUST_USE bool
     120           0 :   ReadSource(/*out*/ SecurityPropertySource& source)
     121             :   {
     122             :     uint32_t rawValue;
     123           0 :     if (!ReadInteger(&rawValue)) {
     124           0 :       return false;
     125             :     }
     126             : 
     127           0 :     source = static_cast<SecurityPropertySource>(rawValue);
     128           0 :     switch (source) {
     129             :       case SourceUnknown:
     130             :       case SourcePreload:
     131             :       case SourceOrganic:
     132             :       case SourceHSTSPriming:
     133           0 :         break;
     134             :       default:
     135           0 :         return false;
     136             :     }
     137             : 
     138           0 :     return true;
     139             :   }
     140             : 
     141             :   // Note: Ideally, this method would be able to read SHA256 strings without
     142             :   // reading all the way to EOF. Unfortunately, if a token starts with digits
     143             :   // mozilla::Tokenizer will by default not consider the digits part of the
     144             :   // string. This can be worked around by making mozilla::Tokenizer consider
     145             :   // digit characters as "word" characters as well, but this can't be changed at
     146             :   // run time, meaning parsing digits as a number will fail.
     147             :   MOZ_MUST_USE bool
     148           0 :   ReadUntilEOFAsSHA256Keys(/*out*/ nsTArray<nsCString>& keys)
     149             :   {
     150           0 :     nsAutoCString mergedKeys;
     151           0 :     if (!ReadUntil(Token::EndOfFile(), mergedKeys, EXCLUDE_LAST)) {
     152           0 :       return false;
     153             :     }
     154             : 
     155             :     // This check makes sure the Substring() calls below are always valid.
     156             :     static const uint32_t SHA256Base64Len = 44;
     157           0 :     if (mergedKeys.Length() % SHA256Base64Len != 0) {
     158           0 :       return false;
     159             :     }
     160             : 
     161           0 :     for (uint32_t i = 0; i < mergedKeys.Length(); i += SHA256Base64Len) {
     162           0 :       nsAutoCString key(Substring(mergedKeys, i, SHA256Base64Len));
     163           0 :       if (!stringIsBase64EncodingOf256bitValue(key)) {
     164           0 :         return false;
     165             :       }
     166           0 :       keys.AppendElement(key);
     167             :     }
     168             : 
     169           0 :     return !keys.IsEmpty();
     170             :   }
     171             : };
     172             : 
     173             : // Parses a state string like "1500918564034,1,1" into its constituent parts.
     174             : bool
     175          16 : ParseHSTSState(const nsCString& stateString,
     176             :        /*out*/ PRTime& expireTime,
     177             :        /*out*/ SecurityPropertyState& state,
     178             :        /*out*/ bool& includeSubdomains,
     179             :        /*out*/ SecurityPropertySource& source)
     180             : {
     181          32 :   SSSTokenizer tokenizer(stateString);
     182          16 :   SSSLOG(("Parsing state from %s", stateString.get()));
     183             : 
     184          16 :   if (!tokenizer.ReadInteger(&expireTime)) {
     185          16 :     return false;
     186             :   }
     187             : 
     188           0 :   if (!tokenizer.CheckChar(',')) {
     189           0 :     return false;
     190             :   }
     191             : 
     192           0 :   if (!tokenizer.ReadState(state)) {
     193           0 :     return false;
     194             :   }
     195             : 
     196           0 :   if (!tokenizer.CheckChar(',')) {
     197           0 :     return false;
     198             :   }
     199             : 
     200           0 :   if (!tokenizer.ReadBool(includeSubdomains)) {
     201           0 :     return false;
     202             :   }
     203             : 
     204           0 :   source = SourceUnknown;
     205           0 :   if (tokenizer.CheckChar(',')) {
     206           0 :     if (!tokenizer.ReadSource(source)) {
     207           0 :       return false;
     208             :     }
     209             :   }
     210             : 
     211           0 :   return tokenizer.CheckEOF();
     212             : }
     213             : 
     214             : } // namespace
     215             : 
     216          16 : SiteHSTSState::SiteHSTSState(const nsCString& aHost,
     217             :                              const OriginAttributes& aOriginAttributes,
     218          16 :                              const nsCString& aStateString)
     219             :   : mHostname(aHost)
     220             :   , mOriginAttributes(aOriginAttributes)
     221             :   , mHSTSExpireTime(0)
     222             :   , mHSTSState(SecurityPropertyUnset)
     223             :   , mHSTSIncludeSubdomains(false)
     224          16 :   , mHSTSSource(SourceUnknown)
     225             : {
     226          16 :   bool valid = ParseHSTSState(aStateString, mHSTSExpireTime, mHSTSState,
     227          16 :                               mHSTSIncludeSubdomains, mHSTSSource);
     228          16 :   if (!valid) {
     229          16 :     SSSLOG(("%s is not a valid SiteHSTSState", aStateString.get()));
     230          16 :     mHSTSExpireTime = 0;
     231          16 :     mHSTSState = SecurityPropertyUnset;
     232          16 :     mHSTSIncludeSubdomains = false;
     233          16 :     mHSTSSource = SourceUnknown;
     234             :   }
     235          16 : }
     236             : 
     237           0 : SiteHSTSState::SiteHSTSState(const nsCString& aHost,
     238             :                              const OriginAttributes& aOriginAttributes,
     239             :                              PRTime aHSTSExpireTime,
     240             :                              SecurityPropertyState aHSTSState,
     241             :                              bool aHSTSIncludeSubdomains,
     242           0 :                              SecurityPropertySource aSource)
     243             : 
     244             :   : mHostname(aHost)
     245             :   , mOriginAttributes(aOriginAttributes)
     246             :   , mHSTSExpireTime(aHSTSExpireTime)
     247             :   , mHSTSState(aHSTSState)
     248             :   , mHSTSIncludeSubdomains(aHSTSIncludeSubdomains)
     249           0 :   , mHSTSSource(aSource)
     250             : {
     251           0 : }
     252             : 
     253             : void
     254           0 : SiteHSTSState::ToString(nsCString& aString)
     255             : {
     256           0 :   aString.Truncate();
     257           0 :   aString.AppendInt(mHSTSExpireTime);
     258           0 :   aString.Append(',');
     259           0 :   aString.AppendInt(mHSTSState);
     260           0 :   aString.Append(',');
     261           0 :   aString.AppendInt(static_cast<uint32_t>(mHSTSIncludeSubdomains));
     262           0 :   aString.Append(',');
     263           0 :   aString.AppendInt(mHSTSSource);
     264           0 : }
     265             : 
     266             : NS_IMETHODIMP
     267           0 : SiteHSTSState::GetHostname(nsACString& aHostname)
     268             : {
     269           0 :   aHostname = mHostname;
     270           0 :   return NS_OK;
     271             : }
     272             : 
     273             : NS_IMETHODIMP
     274           0 : SiteHSTSState::GetExpireTime(int64_t* aExpireTime)
     275             : {
     276           0 :   NS_ENSURE_ARG(aExpireTime);
     277           0 :   *aExpireTime = mHSTSExpireTime;
     278           0 :   return NS_OK;
     279             : }
     280             : 
     281             : NS_IMETHODIMP
     282           0 : SiteHSTSState::GetSecurityPropertyState(int16_t* aSecurityPropertyState)
     283             : {
     284           0 :   NS_ENSURE_ARG(aSecurityPropertyState);
     285           0 :   *aSecurityPropertyState = mHSTSState;
     286           0 :   return NS_OK;
     287             : }
     288             : 
     289             : NS_IMETHODIMP
     290           0 : SiteHSTSState::GetIncludeSubdomains(bool* aIncludeSubdomains)
     291             : {
     292           0 :   NS_ENSURE_ARG(aIncludeSubdomains);
     293           0 :   *aIncludeSubdomains = mHSTSIncludeSubdomains;
     294           0 :   return NS_OK;
     295             : }
     296             : 
     297             : NS_IMETHODIMP
     298           0 : SiteHSTSState::GetOriginAttributes(JSContext* aCx,
     299             :   JS::MutableHandle<JS::Value> aOriginAttributes)
     300             : {
     301           0 :   if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
     302           0 :     return NS_ERROR_FAILURE;
     303             :   }
     304           0 :   return NS_OK;
     305             : }
     306             : 
     307             : ////////////////////////////////////////////////////////////////////////////////
     308             : 
     309           0 : NS_IMPL_ISUPPORTS(SiteHPKPState, nsISiteSecurityState, nsISiteHPKPState)
     310             : 
     311             : namespace {
     312             : 
     313             : // Parses a state string like
     314             : // "1494603034103,1,1,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" into its
     315             : // constituent parts.
     316             : bool
     317           0 : ParseHPKPState(const nsCString& stateString,
     318             :        /*out*/ PRTime& expireTime,
     319             :        /*out*/ SecurityPropertyState& state,
     320             :        /*out*/ bool& includeSubdomains,
     321             :        /*out*/ nsTArray<nsCString>& sha256keys)
     322             : {
     323           0 :   SSSTokenizer tokenizer(stateString);
     324             : 
     325           0 :   if (!tokenizer.ReadInteger(&expireTime)) {
     326           0 :     return false;
     327             :   }
     328             : 
     329           0 :   if (!tokenizer.CheckChar(',')) {
     330           0 :     return false;
     331             :   }
     332             : 
     333           0 :   if (!tokenizer.ReadState(state)) {
     334           0 :     return false;
     335             :   }
     336             : 
     337             :   // SecurityPropertyNegative isn't a valid state for HPKP.
     338           0 :   switch (state) {
     339             :     case SecurityPropertyKnockout:
     340             :     case SecurityPropertySet:
     341             :     case SecurityPropertyUnset:
     342           0 :       break;
     343             :     case SecurityPropertyNegative:
     344             :     default:
     345           0 :       return false;
     346             :   }
     347             : 
     348           0 :   if (!tokenizer.CheckChar(',')) {
     349           0 :     return false;
     350             :   }
     351             : 
     352           0 :   if (!tokenizer.ReadBool(includeSubdomains)) {
     353           0 :     return false;
     354             :   }
     355             : 
     356           0 :   if (!tokenizer.CheckChar(',')) {
     357           0 :     return false;
     358             :   }
     359             : 
     360           0 :   if (state == SecurityPropertySet) {
     361             :     // This reads to the end of input, so there's no need to explicitly check
     362             :     // for EOF.
     363           0 :     return tokenizer.ReadUntilEOFAsSHA256Keys(sha256keys);
     364             :   }
     365             : 
     366           0 :   return tokenizer.CheckEOF();
     367             : }
     368             : 
     369             : } // namespace
     370             : 
     371           0 : SiteHPKPState::SiteHPKPState()
     372             :   : mExpireTime(0)
     373             :   , mState(SecurityPropertyUnset)
     374           0 :   , mIncludeSubdomains(false)
     375             : {
     376           0 : }
     377             : 
     378           0 : SiteHPKPState::SiteHPKPState(const nsCString& aHost,
     379             :                              const OriginAttributes& aOriginAttributes,
     380           0 :                              const nsCString& aStateString)
     381             :   : mHostname(aHost)
     382             :   , mOriginAttributes(aOriginAttributes)
     383             :   , mExpireTime(0)
     384             :   , mState(SecurityPropertyUnset)
     385           0 :   , mIncludeSubdomains(false)
     386             : {
     387           0 :   bool valid = ParseHPKPState(aStateString, mExpireTime, mState,
     388           0 :                               mIncludeSubdomains, mSHA256keys);
     389           0 :   if (!valid) {
     390           0 :     SSSLOG(("%s is not a valid SiteHPKPState", aStateString.get()));
     391           0 :     mExpireTime = 0;
     392           0 :     mState = SecurityPropertyUnset;
     393           0 :     mIncludeSubdomains = false;
     394           0 :     if (!mSHA256keys.IsEmpty()) {
     395           0 :       mSHA256keys.Clear();
     396             :     }
     397             :   }
     398           0 : }
     399             : 
     400           0 : SiteHPKPState::SiteHPKPState(const nsCString& aHost,
     401             :                              const OriginAttributes& aOriginAttributes,
     402             :                              PRTime aExpireTime,
     403             :                              SecurityPropertyState aState,
     404             :                              bool aIncludeSubdomains,
     405           0 :                              nsTArray<nsCString>& aSHA256keys)
     406             :   : mHostname(aHost)
     407             :   , mOriginAttributes(aOriginAttributes)
     408             :   , mExpireTime(aExpireTime)
     409             :   , mState(aState)
     410             :   , mIncludeSubdomains(aIncludeSubdomains)
     411           0 :   , mSHA256keys(aSHA256keys)
     412             : {
     413           0 : }
     414             : 
     415             : NS_IMETHODIMP
     416           0 : SiteHPKPState::GetHostname(nsACString& aHostname)
     417             : {
     418           0 :   aHostname = mHostname;
     419           0 :   return NS_OK;
     420             : }
     421             : 
     422             : NS_IMETHODIMP
     423           0 : SiteHPKPState::GetExpireTime(int64_t* aExpireTime)
     424             : {
     425           0 :   NS_ENSURE_ARG(aExpireTime);
     426           0 :   *aExpireTime = mExpireTime;
     427           0 :   return NS_OK;
     428             : }
     429             : 
     430             : NS_IMETHODIMP
     431           0 : SiteHPKPState::GetSecurityPropertyState(int16_t* aSecurityPropertyState)
     432             : {
     433           0 :   NS_ENSURE_ARG(aSecurityPropertyState);
     434           0 :   *aSecurityPropertyState = mState;
     435           0 :   return NS_OK;
     436             : }
     437             : 
     438             : NS_IMETHODIMP
     439           0 : SiteHPKPState::GetIncludeSubdomains(bool* aIncludeSubdomains)
     440             : {
     441           0 :   NS_ENSURE_ARG(aIncludeSubdomains);
     442           0 :   *aIncludeSubdomains = mIncludeSubdomains;
     443           0 :   return NS_OK;
     444             : }
     445             : 
     446             : void
     447           0 : SiteHPKPState::ToString(nsCString& aString)
     448             : {
     449           0 :   aString.Truncate();
     450           0 :   aString.AppendInt(mExpireTime);
     451           0 :   aString.Append(',');
     452           0 :   aString.AppendInt(mState);
     453           0 :   aString.Append(',');
     454           0 :   aString.AppendInt(static_cast<uint32_t>(mIncludeSubdomains));
     455           0 :   aString.Append(',');
     456           0 :   for (unsigned int i = 0; i < mSHA256keys.Length(); i++) {
     457           0 :     aString.Append(mSHA256keys[i]);
     458             :   }
     459           0 : }
     460             : 
     461             : NS_IMETHODIMP
     462           0 : SiteHPKPState::GetSha256Keys(nsISimpleEnumerator** aSha256Keys)
     463             : {
     464           0 :   NS_ENSURE_ARG(aSha256Keys);
     465             : 
     466           0 :   nsCOMArray<nsIVariant> keys;
     467           0 :   for (const nsCString& key : mSHA256keys) {
     468           0 :     nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
     469           0 :     nsresult rv = variant->SetAsAUTF8String(key);
     470           0 :     if (NS_FAILED(rv)) {
     471           0 :       return rv;
     472             :     }
     473           0 :     if (!keys.AppendObject(variant)) {
     474           0 :       return NS_ERROR_FAILURE;
     475             :     }
     476             :   }
     477           0 :   return NS_NewArrayEnumerator(aSha256Keys, keys);
     478             : }
     479             : 
     480             : NS_IMETHODIMP
     481           0 : SiteHPKPState::GetOriginAttributes(JSContext* aCx,
     482             :   JS::MutableHandle<JS::Value> aOriginAttributes)
     483             : {
     484           0 :   if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
     485           0 :     return NS_ERROR_FAILURE;
     486             :   }
     487           0 :   return NS_OK;
     488             : }
     489             : 
     490             : ////////////////////////////////////////////////////////////////////////////////
     491             : 
     492             : const uint64_t kSixtyDaysInSeconds = 60 * 24 * 60 * 60;
     493             : 
     494           2 : nsSiteSecurityService::nsSiteSecurityService()
     495             :   : mMaxMaxAge(kSixtyDaysInSeconds)
     496             :   , mUsePreloadList(true)
     497           2 :   , mPreloadListTimeOffset(0)
     498             : {
     499           2 : }
     500             : 
     501           0 : nsSiteSecurityService::~nsSiteSecurityService()
     502             : {
     503           0 : }
     504             : 
     505          78 : NS_IMPL_ISUPPORTS(nsSiteSecurityService,
     506             :                   nsIObserver,
     507             :                   nsISiteSecurityService)
     508             : 
     509             : nsresult
     510           2 : nsSiteSecurityService::Init()
     511             : {
     512             :   // Don't access Preferences off the main thread.
     513           2 :   if (!NS_IsMainThread()) {
     514           0 :     MOZ_ASSERT_UNREACHABLE("nsSiteSecurityService initialized off main thread");
     515             :     return NS_ERROR_NOT_SAME_THREAD;
     516             :   }
     517             : 
     518           2 :   mMaxMaxAge = mozilla::Preferences::GetInt(
     519             :     "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
     520           2 :   mozilla::Preferences::AddStrongObserver(this,
     521           2 :     "security.cert_pinning.max_max_age_seconds");
     522           2 :   mUsePreloadList = mozilla::Preferences::GetBool(
     523             :     "network.stricttransportsecurity.preloadlist", true);
     524           2 :   mozilla::Preferences::AddStrongObserver(this,
     525           2 :     "network.stricttransportsecurity.preloadlist");
     526           2 :   mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
     527             :     "security.cert_pinning.process_headers_from_non_builtin_roots", false);
     528           2 :   mozilla::Preferences::AddStrongObserver(this,
     529           2 :     "security.cert_pinning.process_headers_from_non_builtin_roots");
     530           2 :   mPreloadListTimeOffset = mozilla::Preferences::GetInt(
     531             :     "test.currentTimeOffsetSeconds", 0);
     532           2 :   mozilla::Preferences::AddStrongObserver(this,
     533           2 :     "test.currentTimeOffsetSeconds");
     534             :   mSiteStateStorage =
     535           2 :     mozilla::DataStorage::Get(DataStorageClass::SiteSecurityServiceState);
     536             :   mPreloadStateStorage =
     537           2 :     mozilla::DataStorage::Get(DataStorageClass::SecurityPreloadState);
     538           2 :   bool storageWillPersist = false;
     539           2 :   bool preloadStorageWillPersist = false;
     540           2 :   nsresult rv = mSiteStateStorage->Init(storageWillPersist);
     541           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     542           0 :     return rv;
     543             :   }
     544           2 :   rv = mPreloadStateStorage->Init(preloadStorageWillPersist);
     545           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     546           0 :     return rv;
     547             :   }
     548             :   // This is not fatal. There are some cases where there won't be a
     549             :   // profile directory (e.g. running xpcshell). There isn't the
     550             :   // expectation that site information will be presisted in those cases.
     551           2 :   if (!storageWillPersist || !preloadStorageWillPersist) {
     552           1 :     NS_WARNING("site security information will not be persisted");
     553             :   }
     554             : 
     555           2 :   return NS_OK;
     556             : }
     557             : 
     558             : nsresult
     559           9 : nsSiteSecurityService::GetHost(nsIURI* aURI, nsACString& aResult)
     560             : {
     561          18 :   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
     562           9 :   if (!innerURI) {
     563           0 :     return NS_ERROR_FAILURE;
     564             :   }
     565             : 
     566          18 :   nsAutoCString host;
     567           9 :   nsresult rv = innerURI->GetAsciiHost(host);
     568           9 :   if (NS_FAILED(rv)) {
     569           0 :     return rv;
     570             :   }
     571             : 
     572           9 :   aResult.Assign(PublicKeyPinningService::CanonicalizeHostname(host.get()));
     573           9 :   if (aResult.IsEmpty()) {
     574           0 :     return NS_ERROR_UNEXPECTED;
     575             :   }
     576             : 
     577           9 :   return NS_OK;
     578             : }
     579             : 
     580             : static void
     581          16 : SetStorageKey(const nsACString& hostname, uint32_t aType,
     582             :               const OriginAttributes& aOriginAttributes,
     583             :               /*out*/ nsAutoCString& storageKey)
     584             : {
     585          16 :   storageKey = hostname;
     586             : 
     587             :   // Don't isolate by userContextId.
     588          32 :   OriginAttributes originAttributesNoUserContext = aOriginAttributes;
     589          16 :   originAttributesNoUserContext.mUserContextId =
     590             :     nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
     591          32 :   nsAutoCString originAttributesSuffix;
     592          16 :   originAttributesNoUserContext.CreateSuffix(originAttributesSuffix);
     593          16 :   storageKey.Append(originAttributesSuffix);
     594          16 :   switch (aType) {
     595             :     case nsISiteSecurityService::HEADER_HSTS:
     596          16 :       storageKey.AppendASCII(kHSTSKeySuffix);
     597          16 :       break;
     598             :     case nsISiteSecurityService::HEADER_HPKP:
     599           0 :       storageKey.AppendASCII(kHPKPKeySuffix);
     600           0 :       break;
     601             :     default:
     602           0 :       MOZ_ASSERT_UNREACHABLE("SSS:SetStorageKey got invalid type");
     603             :   }
     604          16 : }
     605             : 
     606             : // Expire times are in millis.  Since Headers max-age is in seconds, and
     607             : // PR_Now() is in micros, normalize the units at milliseconds.
     608             : static int64_t
     609           0 : ExpireTimeFromMaxAge(uint64_t maxAge)
     610             : {
     611           0 :   return (PR_Now() / PR_USEC_PER_MSEC) + ((int64_t)maxAge * PR_MSEC_PER_SEC);
     612             : }
     613             : 
     614             : nsresult
     615           0 : nsSiteSecurityService::SetHSTSState(uint32_t aType,
     616             :                                     const char* aHost,
     617             :                                     int64_t maxage,
     618             :                                     bool includeSubdomains,
     619             :                                     uint32_t flags,
     620             :                                     SecurityPropertyState aHSTSState,
     621             :                                     SecurityPropertySource aSource,
     622             :                                     const OriginAttributes& aOriginAttributes)
     623             : {
     624           0 :   nsAutoCString hostname(aHost);
     625           0 :   bool isPreload = (aSource == SourcePreload);
     626             :   // If max-age is zero, that's an indication to immediately remove the
     627             :   // security state, so here's a shortcut.
     628           0 :   if (!maxage) {
     629           0 :     return RemoveStateInternal(aType, hostname, flags, isPreload,
     630           0 :                                aOriginAttributes);
     631             :   }
     632             : 
     633           0 :   MOZ_ASSERT((aHSTSState == SecurityPropertySet ||
     634             :               aHSTSState == SecurityPropertyNegative),
     635             :       "HSTS State must be SecurityPropertySet or SecurityPropertyNegative");
     636           0 :   if (isPreload && aOriginAttributes != OriginAttributes()) {
     637           0 :     return NS_ERROR_INVALID_ARG;
     638             :   }
     639             : 
     640           0 :   int64_t expiretime = ExpireTimeFromMaxAge(maxage);
     641             :   RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
     642             :     hostname, aOriginAttributes, expiretime, aHSTSState, includeSubdomains,
     643           0 :     aSource);
     644           0 :   nsAutoCString stateString;
     645           0 :   siteState->ToString(stateString);
     646           0 :   SSSLOG(("SSS: setting state for %s", hostname.get()));
     647           0 :   bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
     648             :   mozilla::DataStorageType storageType = isPrivate
     649           0 :                                          ? mozilla::DataStorage_Private
     650           0 :                                          : mozilla::DataStorage_Persistent;
     651           0 :   nsAutoCString storageKey;
     652           0 :   SetStorageKey(hostname, aType, aOriginAttributes, storageKey);
     653             :   nsresult rv;
     654           0 :   if (isPreload) {
     655           0 :     SSSLOG(("SSS: storing entry for %s in dynamic preloads", hostname.get()));
     656           0 :     rv = mPreloadStateStorage->Put(storageKey, stateString,
     657           0 :                                    mozilla::DataStorage_Persistent);
     658             :   } else {
     659           0 :     SSSLOG(("SSS: storing HSTS site entry for %s", hostname.get()));
     660           0 :     nsCString value = mSiteStateStorage->Get(storageKey, storageType);
     661             :     RefPtr<SiteHSTSState> curSiteState =
     662           0 :       new SiteHSTSState(hostname, aOriginAttributes, value);
     663           0 :     if (curSiteState->mHSTSState != SecurityPropertyUnset &&
     664           0 :         curSiteState->mHSTSSource != SourceUnknown) {
     665             :       // don't override the source
     666           0 :       siteState->mHSTSSource = curSiteState->mHSTSSource;
     667           0 :       siteState->ToString(stateString);
     668             :     }
     669           0 :     rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
     670             :   }
     671           0 :   NS_ENSURE_SUCCESS(rv, rv);
     672             : 
     673           0 :   return NS_OK;
     674             : }
     675             : 
     676             : NS_IMETHODIMP
     677           0 : nsSiteSecurityService::CacheNegativeHSTSResult(
     678             :   nsIURI* aSourceURI,
     679             :   uint64_t aMaxAge,
     680             :   const OriginAttributes& aOriginAttributes)
     681             : {
     682           0 :   nsAutoCString hostname;
     683           0 :   nsresult rv = GetHost(aSourceURI, hostname);
     684           0 :   NS_ENSURE_SUCCESS(rv, rv);
     685             :   // SecurityPropertyNegative results only come from HSTS priming
     686           0 :   return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, hostname.get(),
     687             :                       aMaxAge, false, 0, SecurityPropertyNegative,
     688           0 :                       SourceHSTSPriming, aOriginAttributes);
     689             : }
     690             : 
     691             : nsresult
     692           0 : nsSiteSecurityService::RemoveStateInternal(
     693             :   uint32_t aType, nsIURI* aURI, uint32_t aFlags,
     694             :   const OriginAttributes& aOriginAttributes)
     695             : {
     696           0 :   nsAutoCString hostname;
     697           0 :   GetHost(aURI, hostname);
     698           0 :   return RemoveStateInternal(aType, hostname, aFlags, false, aOriginAttributes);
     699             : }
     700             : 
     701             : nsresult
     702           0 : nsSiteSecurityService::RemoveStateInternal(
     703             :   uint32_t aType,
     704             :   const nsAutoCString& aHost,
     705             :   uint32_t aFlags, bool aIsPreload,
     706             :   const OriginAttributes& aOriginAttributes)
     707             : {
     708             :    // Child processes are not allowed direct access to this.
     709           0 :    if (!XRE_IsParentProcess()) {
     710           0 :      MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::RemoveStateInternal");
     711             :    }
     712             : 
     713             :   // Only HSTS is supported at the moment.
     714           0 :   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
     715             :                  aType == nsISiteSecurityService::HEADER_HPKP,
     716             :                  NS_ERROR_NOT_IMPLEMENTED);
     717           0 :   if (aIsPreload && aOriginAttributes != OriginAttributes()) {
     718           0 :     return NS_ERROR_INVALID_ARG;
     719             :   }
     720             : 
     721           0 :   bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
     722             :   mozilla::DataStorageType storageType = isPrivate
     723           0 :                                          ? mozilla::DataStorage_Private
     724           0 :                                          : mozilla::DataStorage_Persistent;
     725             :   // If this host is in the preload list, we have to store a knockout entry.
     726           0 :   nsAutoCString storageKey;
     727           0 :   SetStorageKey(aHost, aType, aOriginAttributes, storageKey);
     728             : 
     729             :   nsCString value = mPreloadStateStorage->Get(storageKey,
     730           0 :                                               mozilla::DataStorage_Persistent);
     731             :   RefPtr<SiteHSTSState> dynamicState =
     732           0 :     new SiteHSTSState(aHost, aOriginAttributes, value);
     733           0 :   if (GetPreloadListEntry(aHost.get()) ||
     734           0 :       dynamicState->mHSTSState != SecurityPropertyUnset) {
     735           0 :     SSSLOG(("SSS: storing knockout entry for %s", aHost.get()));
     736             :     RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
     737             :       aHost, aOriginAttributes, 0, SecurityPropertyKnockout, false,
     738           0 :       SourceUnknown);
     739           0 :     nsAutoCString stateString;
     740           0 :     siteState->ToString(stateString);
     741             :     nsresult rv;
     742           0 :     if (aIsPreload) {
     743           0 :       rv = mPreloadStateStorage->Put(storageKey, stateString,
     744           0 :                                      mozilla::DataStorage_Persistent);
     745             :     } else {
     746           0 :       rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
     747             :     }
     748           0 :     NS_ENSURE_SUCCESS(rv, rv);
     749             :   } else {
     750           0 :     SSSLOG(("SSS: removing entry for %s", aHost.get()));
     751           0 :     if (aIsPreload) {
     752           0 :       mPreloadStateStorage->Remove(storageKey, mozilla::DataStorage_Persistent);
     753             :     } else {
     754           0 :       mSiteStateStorage->Remove(storageKey, storageType);
     755             :     }
     756             :   }
     757             : 
     758           0 :   return NS_OK;
     759             : }
     760             : 
     761             : NS_IMETHODIMP
     762           0 : nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI,
     763             :                                    uint32_t aFlags,
     764             :                                    JS::HandleValue aOriginAttributes,
     765             :                                    JSContext* aCx, uint8_t aArgc)
     766             : {
     767           0 :   OriginAttributes originAttributes;
     768           0 :   if (aArgc > 0) {
     769             :     // OriginAttributes were passed in.
     770           0 :     if (!aOriginAttributes.isObject() ||
     771           0 :         !originAttributes.Init(aCx, aOriginAttributes)) {
     772           0 :       return NS_ERROR_INVALID_ARG;
     773             :     }
     774             :   }
     775           0 :   return RemoveStateInternal(aType, aURI, aFlags, originAttributes);
     776             : }
     777             : 
     778             : static bool
     779          17 : HostIsIPAddress(const nsCString& hostname)
     780             : {
     781             :   PRNetAddr hostAddr;
     782          17 :   PRErrorCode prv = PR_StringToNetAddr(hostname.get(), &hostAddr);
     783          17 :   return (prv == PR_SUCCESS);
     784             : }
     785             : 
     786             : NS_IMETHODIMP
     787           0 : nsSiteSecurityService::ProcessHeaderScriptable(
     788             :   uint32_t aType,
     789             :   nsIURI* aSourceURI,
     790             :   const nsACString& aHeader,
     791             :   nsISSLStatus* aSSLStatus,
     792             :   uint32_t aFlags,
     793             :   uint32_t aSource,
     794             :   JS::HandleValue aOriginAttributes,
     795             :   uint64_t* aMaxAge,
     796             :   bool* aIncludeSubdomains,
     797             :   uint32_t* aFailureResult,
     798             :   JSContext* aCx,
     799             :   uint8_t aArgc)
     800             : {
     801           0 :   OriginAttributes originAttributes;
     802           0 :   if (aArgc > 0) {
     803           0 :     if (!aOriginAttributes.isObject() ||
     804           0 :         !originAttributes.Init(aCx, aOriginAttributes)) {
     805           0 :       return NS_ERROR_INVALID_ARG;
     806             :     }
     807             :   }
     808             :   return ProcessHeader(aType, aSourceURI, aHeader, aSSLStatus, aFlags,
     809             :                        aSource, originAttributes, aMaxAge, aIncludeSubdomains,
     810           0 :                        aFailureResult);
     811             : }
     812             : 
     813             : NS_IMETHODIMP
     814           0 : nsSiteSecurityService::ProcessHeader(uint32_t aType,
     815             :                                      nsIURI* aSourceURI,
     816             :                                      const nsACString& aHeader,
     817             :                                      nsISSLStatus* aSSLStatus,
     818             :                                      uint32_t aFlags,
     819             :                                      uint32_t aHeaderSource,
     820             :                                      const OriginAttributes& aOriginAttributes,
     821             :                                      uint64_t* aMaxAge,
     822             :                                      bool* aIncludeSubdomains,
     823             :                                      uint32_t* aFailureResult)
     824             : {
     825             :   // Child processes are not allowed direct access to this.
     826           0 :   if (!XRE_IsParentProcess()) {
     827           0 :     MOZ_CRASH("Child process: no direct access to "
     828             :               "nsISiteSecurityService::ProcessHeader");
     829             :   }
     830             : 
     831           0 :   if (aFailureResult) {
     832           0 :     *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
     833             :   }
     834           0 :   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
     835             :                  aType == nsISiteSecurityService::HEADER_HPKP,
     836             :                  NS_ERROR_NOT_IMPLEMENTED);
     837           0 :   SecurityPropertySource source = static_cast<SecurityPropertySource>(aHeaderSource);
     838           0 :   switch (source) {
     839             :     case SourceUnknown:
     840             :     case SourcePreload:
     841             :     case SourceOrganic:
     842             :     case SourceHSTSPriming:
     843           0 :       break;
     844             :     default:
     845           0 :       return NS_ERROR_INVALID_ARG;
     846             :   }
     847             : 
     848           0 :   NS_ENSURE_ARG(aSSLStatus);
     849           0 :   return ProcessHeaderInternal(aType, aSourceURI, PromiseFlatCString(aHeader),
     850             :                                aSSLStatus, aFlags, source, aOriginAttributes,
     851           0 :                                aMaxAge, aIncludeSubdomains, aFailureResult);
     852             : }
     853             : 
     854             : nsresult
     855           0 : nsSiteSecurityService::ProcessHeaderInternal(
     856             :   uint32_t aType,
     857             :   nsIURI* aSourceURI,
     858             :   const nsCString& aHeader,
     859             :   nsISSLStatus* aSSLStatus,
     860             :   uint32_t aFlags,
     861             :   SecurityPropertySource aSource,
     862             :   const OriginAttributes& aOriginAttributes,
     863             :   uint64_t* aMaxAge,
     864             :   bool* aIncludeSubdomains,
     865             :   uint32_t* aFailureResult)
     866             : {
     867           0 :   if (aFailureResult) {
     868           0 :     *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
     869             :   }
     870             :   // Only HSTS and HPKP are supported at the moment.
     871           0 :   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
     872             :                  aType == nsISiteSecurityService::HEADER_HPKP,
     873             :                  NS_ERROR_NOT_IMPLEMENTED);
     874             : 
     875           0 :   if (aMaxAge != nullptr) {
     876           0 :     *aMaxAge = 0;
     877             :   }
     878             : 
     879           0 :   if (aIncludeSubdomains != nullptr) {
     880           0 :     *aIncludeSubdomains = false;
     881             :   }
     882             : 
     883           0 :   if (aSSLStatus) {
     884           0 :     bool tlsIsBroken = false;
     885             :     bool trustcheck;
     886             :     nsresult rv;
     887           0 :     rv = aSSLStatus->GetIsDomainMismatch(&trustcheck);
     888           0 :     NS_ENSURE_SUCCESS(rv, rv);
     889           0 :     tlsIsBroken = tlsIsBroken || trustcheck;
     890             : 
     891           0 :     rv = aSSLStatus->GetIsNotValidAtThisTime(&trustcheck);
     892           0 :     NS_ENSURE_SUCCESS(rv, rv);
     893           0 :     tlsIsBroken = tlsIsBroken || trustcheck;
     894             : 
     895           0 :     rv = aSSLStatus->GetIsUntrusted(&trustcheck);
     896           0 :     NS_ENSURE_SUCCESS(rv, rv);
     897           0 :     tlsIsBroken = tlsIsBroken || trustcheck;
     898           0 :     if (tlsIsBroken) {
     899           0 :        SSSLOG(("SSS: discarding header from untrustworthy connection"));
     900           0 :        if (aFailureResult) {
     901           0 :          *aFailureResult = nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION;
     902             :        }
     903           0 :       return NS_ERROR_FAILURE;
     904             :     }
     905             :   }
     906             : 
     907           0 :   nsAutoCString host;
     908           0 :   nsresult rv = GetHost(aSourceURI, host);
     909           0 :   NS_ENSURE_SUCCESS(rv, rv);
     910           0 :   if (HostIsIPAddress(host)) {
     911             :     /* Don't process headers if a site is accessed by IP address. */
     912           0 :     return NS_OK;
     913             :   }
     914             : 
     915           0 :   switch (aType) {
     916             :     case nsISiteSecurityService::HEADER_HSTS:
     917             :       rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aSource,
     918             :                             aOriginAttributes, aMaxAge, aIncludeSubdomains,
     919           0 :                             aFailureResult);
     920           0 :       break;
     921             :     case nsISiteSecurityService::HEADER_HPKP:
     922             :       rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags,
     923             :                             aOriginAttributes, aMaxAge, aIncludeSubdomains,
     924           0 :                             aFailureResult);
     925           0 :       break;
     926             :     default:
     927           0 :       MOZ_CRASH("unexpected header type");
     928             :   }
     929           0 :   return rv;
     930             : }
     931             : 
     932             : static uint32_t
     933           0 : ParseSSSHeaders(uint32_t aType,
     934             :                 const nsCString& aHeader,
     935             :                 bool& foundIncludeSubdomains,
     936             :                 bool& foundMaxAge,
     937             :                 bool& foundUnrecognizedDirective,
     938             :                 uint64_t& maxAge,
     939             :                 nsTArray<nsCString>& sha256keys)
     940             : {
     941             :   // Strict transport security and Public Key Pinning have very similar
     942             :   // Header formats.
     943             : 
     944             :   // "Strict-Transport-Security" ":" OWS
     945             :   //      STS-d  *( OWS ";" OWS STS-d  OWS)
     946             :   //
     947             :   //  ; STS directive
     948             :   //  STS-d      = maxAge / includeSubDomains
     949             :   //
     950             :   //  maxAge     = "max-age" "=" delta-seconds v-ext
     951             :   //
     952             :   //  includeSubDomains = [ "includeSubDomains" ]
     953             :   //
     954             : 
     955             :   // "Public-Key-Pins ":" OWS
     956             :   //      PKP-d  *( OWS ";" OWS PKP-d  OWS)
     957             :   //
     958             :   //  ; PKP directive
     959             :   //  PKP-d      = maxAge / includeSubDomains / reportUri / pin-directive
     960             :   //
     961             :   //  maxAge     = "max-age" "=" delta-seconds v-ext
     962             :   //
     963             :   //  includeSubDomains = [ "includeSubDomains" ]
     964             :   //
     965             :   //  reportURi  = "report-uri" "=" quoted-string
     966             :   //
     967             :   //  pin-directive = "pin-" token "=" quoted-string
     968             :   //
     969             :   //  the only valid token currently specified is sha256
     970             :   //  the quoted string for a pin directive is the base64 encoding
     971             :   //  of the hash of the public key of the fingerprint
     972             :   //
     973             : 
     974             :   //  The order of the directives is not significant.
     975             :   //  All directives must appear only once.
     976             :   //  Directive names are case-insensitive.
     977             :   //  The entire header is invalid if a directive not conforming to the
     978             :   //  syntax is encountered.
     979             :   //  Unrecognized directives (that are otherwise syntactically valid) are
     980             :   //  ignored, and the rest of the header is parsed as normal.
     981             : 
     982           0 :   bool foundReportURI = false;
     983             : 
     984           0 :   NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
     985           0 :   NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
     986           0 :   NS_NAMED_LITERAL_CSTRING(pin_sha256_var, "pin-sha256");
     987           0 :   NS_NAMED_LITERAL_CSTRING(report_uri_var, "report-uri");
     988             : 
     989           0 :   nsSecurityHeaderParser parser(aHeader);
     990           0 :   nsresult rv = parser.Parse();
     991           0 :   if (NS_FAILED(rv)) {
     992           0 :     SSSLOG(("SSS: could not parse header"));
     993           0 :     return nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER;
     994             :   }
     995           0 :   mozilla::LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
     996             : 
     997           0 :   for (nsSecurityHeaderDirective* directive = directives->getFirst();
     998           0 :        directive != nullptr; directive = directive->getNext()) {
     999           0 :     SSSLOG(("SSS: found directive %s\n", directive->mName.get()));
    1000           0 :     if (directive->mName.Length() == max_age_var.Length() &&
    1001           0 :         directive->mName.EqualsIgnoreCase(max_age_var.get(),
    1002           0 :                                           max_age_var.Length())) {
    1003           0 :       if (foundMaxAge) {
    1004           0 :         SSSLOG(("SSS: found two max-age directives"));
    1005           0 :         return nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES;
    1006             :       }
    1007             : 
    1008           0 :       SSSLOG(("SSS: found max-age directive"));
    1009           0 :       foundMaxAge = true;
    1010             : 
    1011           0 :       Tokenizer tokenizer(directive->mValue);
    1012           0 :       if (!tokenizer.ReadInteger(&maxAge)) {
    1013           0 :         SSSLOG(("SSS: could not parse delta-seconds"));
    1014           0 :         return nsISiteSecurityService::ERROR_INVALID_MAX_AGE;
    1015             :       }
    1016             : 
    1017           0 :       if (!tokenizer.CheckEOF()) {
    1018           0 :         SSSLOG(("SSS: invalid value for max-age directive"));
    1019           0 :         return nsISiteSecurityService::ERROR_INVALID_MAX_AGE;
    1020             :       }
    1021             : 
    1022           0 :       SSSLOG(("SSS: parsed delta-seconds: %" PRIu64, maxAge));
    1023           0 :     } else if (directive->mName.Length() == include_subd_var.Length() &&
    1024           0 :                directive->mName.EqualsIgnoreCase(include_subd_var.get(),
    1025           0 :                                                  include_subd_var.Length())) {
    1026           0 :       if (foundIncludeSubdomains) {
    1027           0 :         SSSLOG(("SSS: found two includeSubdomains directives"));
    1028           0 :         return nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS;
    1029             :       }
    1030             : 
    1031           0 :       SSSLOG(("SSS: found includeSubdomains directive"));
    1032           0 :       foundIncludeSubdomains = true;
    1033             : 
    1034           0 :       if (directive->mValue.Length() != 0) {
    1035           0 :         SSSLOG(("SSS: includeSubdomains directive unexpectedly had value '%s'",
    1036             :                 directive->mValue.get()));
    1037           0 :         return nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS;
    1038             :       }
    1039           0 :     } else if (aType == nsISiteSecurityService::HEADER_HPKP &&
    1040           0 :                directive->mName.Length() == pin_sha256_var.Length() &&
    1041           0 :                directive->mName.EqualsIgnoreCase(pin_sha256_var.get(),
    1042           0 :                                                  pin_sha256_var.Length())) {
    1043           0 :        SSSLOG(("SSS: found pinning entry '%s' length=%d",
    1044             :                directive->mValue.get(), directive->mValue.Length()));
    1045           0 :        if (!stringIsBase64EncodingOf256bitValue(directive->mValue)) {
    1046           0 :          return nsISiteSecurityService::ERROR_INVALID_PIN;
    1047             :        }
    1048           0 :        sha256keys.AppendElement(directive->mValue);
    1049           0 :    } else if (aType == nsISiteSecurityService::HEADER_HPKP &&
    1050           0 :               directive->mName.Length() == report_uri_var.Length() &&
    1051           0 :               directive->mName.EqualsIgnoreCase(report_uri_var.get(),
    1052           0 :                                                 report_uri_var.Length())) {
    1053             :        // We don't support the report-uri yet, but to avoid unrecognized
    1054             :        // directive warnings, we still have to handle its presence
    1055           0 :       if (foundReportURI) {
    1056           0 :         SSSLOG(("SSS: found two report-uri directives"));
    1057           0 :         return nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS;
    1058             :       }
    1059           0 :       SSSLOG(("SSS: found report-uri directive"));
    1060           0 :       foundReportURI = true;
    1061             :    } else {
    1062           0 :       SSSLOG(("SSS: ignoring unrecognized directive '%s'",
    1063             :               directive->mName.get()));
    1064           0 :       foundUnrecognizedDirective = true;
    1065             :     }
    1066             :   }
    1067           0 :   return nsISiteSecurityService::Success;
    1068             : }
    1069             : 
    1070             : nsresult
    1071           0 : nsSiteSecurityService::ProcessPKPHeader(
    1072             :   nsIURI* aSourceURI,
    1073             :   const nsCString& aHeader,
    1074             :   nsISSLStatus* aSSLStatus,
    1075             :   uint32_t aFlags,
    1076             :   const OriginAttributes& aOriginAttributes,
    1077             :   uint64_t* aMaxAge,
    1078             :   bool* aIncludeSubdomains,
    1079             :   uint32_t* aFailureResult)
    1080             : {
    1081           0 :   if (aFailureResult) {
    1082           0 :     *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
    1083             :   }
    1084           0 :   SSSLOG(("SSS: processing HPKP header '%s'", aHeader.get()));
    1085           0 :   NS_ENSURE_ARG(aSSLStatus);
    1086             : 
    1087           0 :   const uint32_t aType = nsISiteSecurityService::HEADER_HPKP;
    1088           0 :   bool foundMaxAge = false;
    1089           0 :   bool foundIncludeSubdomains = false;
    1090           0 :   bool foundUnrecognizedDirective = false;
    1091           0 :   uint64_t maxAge = 0;
    1092           0 :   nsTArray<nsCString> sha256keys;
    1093             :   uint32_t sssrv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains,
    1094             :                                    foundMaxAge, foundUnrecognizedDirective,
    1095           0 :                                    maxAge, sha256keys);
    1096           0 :   if (sssrv != nsISiteSecurityService::Success) {
    1097           0 :     if (aFailureResult) {
    1098           0 :       *aFailureResult = sssrv;
    1099             :     }
    1100           0 :     return NS_ERROR_FAILURE;
    1101             :   }
    1102             : 
    1103             :   // after processing all the directives, make sure we came across max-age
    1104             :   // somewhere.
    1105           0 :   if (!foundMaxAge) {
    1106           0 :     SSSLOG(("SSS: did not encounter required max-age directive"));
    1107           0 :     if (aFailureResult) {
    1108           0 :       *aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
    1109             :     }
    1110           0 :     return NS_ERROR_FAILURE;
    1111             :   }
    1112             : 
    1113             :   // before we add the pin we need to ensure it will not break the site as
    1114             :   // currently visited so:
    1115             :   // 1. recompute a valid chain (no external ocsp)
    1116             :   // 2. use this chain to check if things would have broken!
    1117           0 :   nsAutoCString host;
    1118           0 :   nsresult rv = GetHost(aSourceURI, host);
    1119           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1120           0 :   nsCOMPtr<nsIX509Cert> cert;
    1121           0 :   rv = aSSLStatus->GetServerCert(getter_AddRefs(cert));
    1122           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1123           0 :   NS_ENSURE_TRUE(cert, NS_ERROR_FAILURE);
    1124           0 :   UniqueCERTCertificate nssCert(cert->GetCert());
    1125           0 :   NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE);
    1126             : 
    1127           0 :   mozilla::pkix::Time now(mozilla::pkix::Now());
    1128           0 :   UniqueCERTCertList certList;
    1129           0 :   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
    1130           0 :   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
    1131             :   // We don't want this verification to cause any network traffic that would
    1132             :   // block execution. Also, since we don't have access to the original stapled
    1133             :   // OCSP response, we can't enforce this aspect of the TLS Feature extension.
    1134             :   // This is ok, because it will have been enforced when we originally connected
    1135             :   // to the site (or it's disabled, in which case we wouldn't want to enforce it
    1136             :   // anyway).
    1137           0 :   CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
    1138           0 :                               CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
    1139           0 :   if (certVerifier->VerifySSLServerCert(nssCert,
    1140             :                                         nullptr, // stapledOCSPResponse
    1141             :                                         nullptr, // sctsFromTLSExtension
    1142             :                                         now, nullptr, // pinarg
    1143             :                                         host, // hostname
    1144             :                                         certList,
    1145             :                                         nullptr, // no peerCertChain
    1146             :                                         false, // don't store intermediates
    1147             :                                         flags,
    1148             :                                         aOriginAttributes)
    1149             :         != mozilla::pkix::Success) {
    1150           0 :     return NS_ERROR_FAILURE;
    1151             :   }
    1152             : 
    1153           0 :   CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
    1154           0 :   if (CERT_LIST_END(rootNode, certList)) {
    1155           0 :     return NS_ERROR_FAILURE;
    1156             :   }
    1157           0 :   bool isBuiltIn = false;
    1158           0 :   mozilla::pkix::Result result = IsCertBuiltInRoot(rootNode->cert, isBuiltIn);
    1159           0 :   if (result != mozilla::pkix::Success) {
    1160           0 :     return NS_ERROR_FAILURE;
    1161             :   }
    1162             : 
    1163           0 :   if (!isBuiltIn && !mProcessPKPHeadersFromNonBuiltInRoots) {
    1164           0 :     if (aFailureResult) {
    1165           0 :       *aFailureResult = nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN;
    1166             :     }
    1167           0 :     return NS_ERROR_FAILURE;
    1168             :   }
    1169             : 
    1170             :   // if maxAge == 0 we must delete all state, for now no hole-punching
    1171           0 :   if (maxAge == 0) {
    1172           0 :     return RemoveStateInternal(aType, aSourceURI, aFlags, aOriginAttributes);
    1173             :   }
    1174             : 
    1175             :   // clamp maxAge to the maximum set by pref
    1176           0 :   if (maxAge > mMaxMaxAge) {
    1177           0 :     maxAge = mMaxMaxAge;
    1178             :   }
    1179             : 
    1180             :   bool chainMatchesPinset;
    1181             :   rv = PublicKeyPinningService::ChainMatchesPinset(certList, sha256keys,
    1182           0 :                                                    chainMatchesPinset);
    1183           0 :   if (NS_FAILED(rv)) {
    1184           0 :     return rv;
    1185             :   }
    1186           0 :   if (!chainMatchesPinset) {
    1187             :     // is invalid
    1188           0 :     SSSLOG(("SSS: Pins provided by %s are invalid no match with certList\n", host.get()));
    1189           0 :     if (aFailureResult) {
    1190           0 :       *aFailureResult = nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN;
    1191             :     }
    1192           0 :     return NS_ERROR_FAILURE;
    1193             :   }
    1194             : 
    1195             :   // finally we need to ensure that there is a "backup pin" ie. There must be
    1196             :   // at least one fingerprint hash that does NOT validate against the verified
    1197             :   // chain (Section 2.5 of the spec)
    1198           0 :   bool hasBackupPin = false;
    1199           0 :   for (uint32_t i = 0; i < sha256keys.Length(); i++) {
    1200           0 :     nsTArray<nsCString> singlePin;
    1201           0 :     singlePin.AppendElement(sha256keys[i]);
    1202             :     rv = PublicKeyPinningService::ChainMatchesPinset(certList, singlePin,
    1203           0 :                                                      chainMatchesPinset);
    1204           0 :     if (NS_FAILED(rv)) {
    1205           0 :       return rv;
    1206             :     }
    1207           0 :     if (!chainMatchesPinset) {
    1208           0 :       hasBackupPin = true;
    1209             :     }
    1210             :   }
    1211           0 :   if (!hasBackupPin) {
    1212             :      // is invalid
    1213           0 :     SSSLOG(("SSS: Pins provided by %s are invalid no backupPin\n", host.get()));
    1214           0 :     if (aFailureResult) {
    1215           0 :       *aFailureResult = nsISiteSecurityService::ERROR_NO_BACKUP_PIN;
    1216             :     }
    1217           0 :     return NS_ERROR_FAILURE;
    1218             :   }
    1219             : 
    1220           0 :   int64_t expireTime = ExpireTimeFromMaxAge(maxAge);
    1221             :   RefPtr<SiteHPKPState> dynamicEntry =
    1222             :     new SiteHPKPState(host, aOriginAttributes, expireTime, SecurityPropertySet,
    1223           0 :                       foundIncludeSubdomains, sha256keys);
    1224           0 :   SSSLOG(("SSS: about to set pins for  %s, expires=%" PRId64 " now=%" PRId64 " maxAge=%" PRIu64 "\n",
    1225             :            host.get(), expireTime, PR_Now() / PR_USEC_PER_MSEC, maxAge));
    1226             : 
    1227           0 :   rv = SetHPKPState(host.get(), *dynamicEntry, aFlags, false, aOriginAttributes);
    1228           0 :   if (NS_FAILED(rv)) {
    1229           0 :     SSSLOG(("SSS: failed to set pins for %s\n", host.get()));
    1230           0 :     if (aFailureResult) {
    1231           0 :       *aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
    1232             :     }
    1233           0 :     return rv;
    1234             :   }
    1235             : 
    1236           0 :   if (aMaxAge != nullptr) {
    1237           0 :     *aMaxAge = maxAge;
    1238             :   }
    1239             : 
    1240           0 :   if (aIncludeSubdomains != nullptr) {
    1241           0 :     *aIncludeSubdomains = foundIncludeSubdomains;
    1242             :   }
    1243             : 
    1244             :   return foundUnrecognizedDirective
    1245           0 :            ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
    1246           0 :            : NS_OK;
    1247             : }
    1248             : 
    1249             : nsresult
    1250           0 : nsSiteSecurityService::ProcessSTSHeader(
    1251             :   nsIURI* aSourceURI,
    1252             :   const nsCString& aHeader,
    1253             :   uint32_t aFlags,
    1254             :   SecurityPropertySource aSource,
    1255             :   const OriginAttributes& aOriginAttributes,
    1256             :   uint64_t* aMaxAge,
    1257             :   bool* aIncludeSubdomains,
    1258             :   uint32_t* aFailureResult)
    1259             : {
    1260           0 :   if (aFailureResult) {
    1261           0 :     *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
    1262             :   }
    1263           0 :   SSSLOG(("SSS: processing HSTS header '%s'", aHeader.get()));
    1264             : 
    1265           0 :   const uint32_t aType = nsISiteSecurityService::HEADER_HSTS;
    1266           0 :   bool foundMaxAge = false;
    1267           0 :   bool foundIncludeSubdomains = false;
    1268           0 :   bool foundUnrecognizedDirective = false;
    1269           0 :   uint64_t maxAge = 0;
    1270           0 :   nsTArray<nsCString> unusedSHA256keys; // Required for sane internal interface
    1271             : 
    1272             :   uint32_t sssrv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains,
    1273             :                                    foundMaxAge, foundUnrecognizedDirective,
    1274           0 :                                    maxAge, unusedSHA256keys);
    1275           0 :   if (sssrv != nsISiteSecurityService::Success) {
    1276           0 :     if (aFailureResult) {
    1277           0 :       *aFailureResult = sssrv;
    1278             :     }
    1279           0 :     return NS_ERROR_FAILURE;
    1280             :   }
    1281             : 
    1282             :   // after processing all the directives, make sure we came across max-age
    1283             :   // somewhere.
    1284           0 :   if (!foundMaxAge) {
    1285           0 :     SSSLOG(("SSS: did not encounter required max-age directive"));
    1286           0 :     if (aFailureResult) {
    1287           0 :       *aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
    1288             :     }
    1289           0 :     return NS_ERROR_FAILURE;
    1290             :   }
    1291             : 
    1292           0 :   nsAutoCString hostname;
    1293           0 :   nsresult rv = GetHost(aSourceURI, hostname);
    1294           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1295             : 
    1296             :   // record the successfully parsed header data.
    1297           0 :   rv = SetHSTSState(aType, hostname.get(), maxAge, foundIncludeSubdomains,
    1298           0 :                     aFlags, SecurityPropertySet, aSource, aOriginAttributes);
    1299           0 :   if (NS_FAILED(rv)) {
    1300           0 :     SSSLOG(("SSS: failed to set STS state"));
    1301           0 :     if (aFailureResult) {
    1302           0 :       *aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
    1303             :     }
    1304           0 :     return rv;
    1305             :   }
    1306             : 
    1307           0 :   if (aMaxAge != nullptr) {
    1308           0 :     *aMaxAge = maxAge;
    1309             :   }
    1310             : 
    1311           0 :   if (aIncludeSubdomains != nullptr) {
    1312           0 :     *aIncludeSubdomains = foundIncludeSubdomains;
    1313             :   }
    1314             : 
    1315             :   return foundUnrecognizedDirective
    1316           0 :            ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
    1317           0 :            : NS_OK;
    1318             : }
    1319             : 
    1320             : NS_IMETHODIMP
    1321           0 : nsSiteSecurityService::IsSecureURIScriptable(uint32_t aType, nsIURI* aURI,
    1322             :                                              uint32_t aFlags,
    1323             :                                              JS::HandleValue aOriginAttributes,
    1324             :                                              bool* aCached,
    1325             :                                              uint32_t* aSource, JSContext* aCx,
    1326             :                                              uint8_t aArgc,  bool* aResult)
    1327             : {
    1328           0 :   OriginAttributes originAttributes;
    1329           0 :   if (aArgc > 0) {
    1330           0 :     if (!aOriginAttributes.isObject() ||
    1331           0 :         !originAttributes.Init(aCx, aOriginAttributes)) {
    1332           0 :       return NS_ERROR_INVALID_ARG;
    1333             :     }
    1334             :   }
    1335           0 :   return IsSecureURI(aType, aURI, aFlags, originAttributes, aCached, aSource, aResult);
    1336             : }
    1337             : 
    1338             : NS_IMETHODIMP
    1339           9 : nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
    1340             :                                    uint32_t aFlags,
    1341             :                                    const OriginAttributes& aOriginAttributes,
    1342             :                                    bool* aCached,
    1343             :                                    uint32_t* aSource, bool* aResult)
    1344             : {
    1345             :    // Child processes are not allowed direct access to this.
    1346           9 :    if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
    1347           0 :      MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureURI for non-HSTS entries");
    1348             :    }
    1349             : 
    1350           9 :   NS_ENSURE_ARG(aURI);
    1351           9 :   NS_ENSURE_ARG(aResult);
    1352             : 
    1353             :   // Only HSTS and HPKP are supported at the moment.
    1354           9 :   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
    1355             :                  aType == nsISiteSecurityService::HEADER_HPKP,
    1356             :                  NS_ERROR_NOT_IMPLEMENTED);
    1357             : 
    1358          18 :   nsAutoCString hostname;
    1359           9 :   nsresult rv = GetHost(aURI, hostname);
    1360           9 :   NS_ENSURE_SUCCESS(rv, rv);
    1361             :   /* An IP address never qualifies as a secure URI. */
    1362           9 :   if (HostIsIPAddress(hostname)) {
    1363           1 :     *aResult = false;
    1364           1 :     return NS_OK;
    1365             :   }
    1366             : 
    1367           8 :   SecurityPropertySource* source = BitwiseCast<SecurityPropertySource*>(aSource);
    1368             : 
    1369             :   return IsSecureHost(aType, hostname, aFlags, aOriginAttributes, aCached,
    1370           8 :                       source, aResult);
    1371             : }
    1372             : 
    1373         120 : int STSPreloadCompare(const void *key, const void *entry)
    1374             : {
    1375         120 :   const char *keyStr = (const char *)key;
    1376         120 :   const nsSTSPreload *preloadEntry = (const nsSTSPreload *)entry;
    1377         120 :   return strcmp(keyStr, &kSTSHostTable[preloadEntry->mHostIndex]);
    1378             : }
    1379             : 
    1380             : // Returns the preload list entry for the given host, if it exists.
    1381             : // Only does exact host matching - the user must decide how to use the returned
    1382             : // data. May return null.
    1383             : const nsSTSPreload *
    1384           8 : nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
    1385             : {
    1386           8 :   PRTime currentTime = PR_Now() + (mPreloadListTimeOffset * PR_USEC_PER_SEC);
    1387           8 :   if (mUsePreloadList && currentTime < gPreloadListExpirationTime) {
    1388           8 :     return (const nsSTSPreload *) bsearch(aHost,
    1389             :                                           kSTSPreloadList,
    1390             :                                           mozilla::ArrayLength(kSTSPreloadList),
    1391             :                                           sizeof(nsSTSPreload),
    1392           8 :                                           STSPreloadCompare);
    1393             :   }
    1394             : 
    1395           0 :   return nullptr;
    1396             : }
    1397             : 
    1398             : // Allows us to determine if we have an HSTS entry for a given host (and, if
    1399             : // so, what that state is). The return value says whether or not we know
    1400             : // anything about this host (true if the host has an HSTS entry). aHost is
    1401             : // the host which we wish to deteming HSTS information on,
    1402             : // aRequireIncludeSubdomains specifies whether we require includeSubdomains
    1403             : // to be set on the entry (with the other parameters being as per IsSecureHost).
    1404             : bool
    1405           8 : nsSiteSecurityService::HostHasHSTSEntry(
    1406             :   const nsAutoCString& aHost, bool aRequireIncludeSubdomains, uint32_t aFlags,
    1407             :   const OriginAttributes& aOriginAttributes, bool* aResult, bool* aCached,
    1408             :   SecurityPropertySource* aSource)
    1409             : {
    1410           8 :   if (aSource) {
    1411           8 :     *aSource = SourceUnknown;
    1412             :   }
    1413           8 :   if (aCached) {
    1414           0 :     *aCached = false;
    1415             :   }
    1416             :   // First we check for an entry in site security storage. If that entry exists,
    1417             :   // we don't want to check in the preload lists. We only want to use the
    1418             :   // stored value if it is not a knockout entry, however.
    1419             :   // Additionally, if it is a knockout entry, we want to stop looking for data
    1420             :   // on the host, because the knockout entry indicates "we have no information
    1421             :   // regarding the security status of this host".
    1422           8 :   bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
    1423             :   mozilla::DataStorageType storageType = isPrivate
    1424           8 :                                          ? mozilla::DataStorage_Private
    1425           8 :                                          : mozilla::DataStorage_Persistent;
    1426          16 :   nsAutoCString storageKey;
    1427           8 :   SSSLOG(("Seeking HSTS entry for %s", aHost.get()));
    1428           8 :   SetStorageKey(aHost, nsISiteSecurityService::HEADER_HSTS, aOriginAttributes,
    1429           8 :                 storageKey);
    1430          16 :   nsAutoCString preloadKey;
    1431          16 :   SetStorageKey(aHost, nsISiteSecurityService::HEADER_HSTS, OriginAttributes(),
    1432           8 :                 preloadKey);
    1433          16 :   nsCString value = mSiteStateStorage->Get(storageKey, storageType);
    1434             :   RefPtr<SiteHSTSState> siteState =
    1435          16 :     new SiteHSTSState(aHost, aOriginAttributes, value);
    1436           8 :   if (siteState->mHSTSState != SecurityPropertyUnset) {
    1437           0 :     SSSLOG(("Found HSTS entry for %s", aHost.get()));
    1438           0 :     bool expired = siteState->IsExpired(nsISiteSecurityService::HEADER_HSTS);
    1439           0 :     if (!expired) {
    1440           0 :       SSSLOG(("Entry for %s is not expired", aHost.get()));
    1441           0 :       if (siteState->mHSTSState == SecurityPropertySet) {
    1442           0 :         *aResult = aRequireIncludeSubdomains ? siteState->mHSTSIncludeSubdomains
    1443             :                                              : true;
    1444           0 :         if (aCached) {
    1445             :           // Only set cached if this includes subdomains
    1446           0 :           *aCached = aRequireIncludeSubdomains ? siteState->mHSTSIncludeSubdomains
    1447             :                                                : true;
    1448             :         }
    1449           0 :         if (aSource) {
    1450           0 :           *aSource = siteState->mHSTSSource;
    1451             :         }
    1452           0 :         return true;
    1453           0 :       } else if (siteState->mHSTSState == SecurityPropertyNegative) {
    1454           0 :         *aResult = false;
    1455           0 :         if (aCached) {
    1456             :           // if it's negative, it is always cached
    1457           0 :           SSSLOG(("Marking HSTS as as cached (SecurityPropertyNegative)"));
    1458           0 :           *aCached = true;
    1459             :         }
    1460           0 :         if (aSource) {
    1461           0 :           *aSource = siteState->mHSTSSource;
    1462             :         }
    1463           0 :         return true;
    1464             :       }
    1465             :     }
    1466             : 
    1467           0 :     if (expired) {
    1468           0 :       SSSLOG(("Entry %s is expired - checking for preload state", aHost.get()));
    1469             :       // If the entry is expired and is not in either the static or dynamic
    1470             :       // preload lists, we can remove it.
    1471             :       // First, check the dynamic preload list.
    1472           0 :       value = mPreloadStateStorage->Get(preloadKey,
    1473           0 :                                         mozilla::DataStorage_Persistent);
    1474             :       RefPtr<SiteHSTSState> dynamicState =
    1475           0 :         new SiteHSTSState(aHost, aOriginAttributes, value);
    1476           0 :       if (dynamicState->mHSTSState == SecurityPropertyUnset) {
    1477           0 :         SSSLOG(("No dynamic preload - checking for static preload"));
    1478             :         // Now check the static preload list.
    1479           0 :         if (!GetPreloadListEntry(aHost.get())) {
    1480           0 :           SSSLOG(("No static preload - removing expired entry"));
    1481           0 :           mSiteStateStorage->Remove(storageKey, storageType);
    1482             :         }
    1483             :       }
    1484             :     }
    1485           0 :     return false;
    1486             :   }
    1487             : 
    1488             :   // Next, look in the dynamic preload list.
    1489          16 :   value = mPreloadStateStorage->Get(preloadKey,
    1490           8 :                                     mozilla::DataStorage_Persistent);
    1491             :   RefPtr<SiteHSTSState> dynamicState =
    1492          16 :     new SiteHSTSState(aHost, aOriginAttributes, value);
    1493           8 :   if (dynamicState->mHSTSState != SecurityPropertyUnset) {
    1494           0 :     SSSLOG(("Found dynamic preload entry for %s", aHost.get()));
    1495           0 :     bool expired = dynamicState->IsExpired(nsISiteSecurityService::HEADER_HSTS);
    1496           0 :     if (!expired) {
    1497           0 :       if (dynamicState->mHSTSState == SecurityPropertySet) {
    1498           0 :         *aResult = aRequireIncludeSubdomains ? dynamicState->mHSTSIncludeSubdomains
    1499             :                                              : true;
    1500           0 :         if (aCached) {
    1501             :           // Only set cached if this includes subdomains
    1502           0 :           *aCached = aRequireIncludeSubdomains ? dynamicState->mHSTSIncludeSubdomains
    1503             :                                                : true;
    1504             :         }
    1505           0 :         if (aSource) {
    1506           0 :           *aSource = dynamicState->mHSTSSource;
    1507             :         }
    1508           0 :         return true;
    1509           0 :       } else if (dynamicState->mHSTSState == SecurityPropertyNegative) {
    1510           0 :         *aResult = false;
    1511           0 :         if (aCached) {
    1512             :           // if it's negative, it is always cached
    1513           0 :           *aCached = true;
    1514             :         }
    1515           0 :         if (aSource) {
    1516           0 :           *aSource = dynamicState->mHSTSSource;
    1517             :         }
    1518           0 :         return true;
    1519             :       }
    1520             :     } else {
    1521             :       // if a dynamic preload has expired and is not in the static preload
    1522             :       // list, we can remove it.
    1523           0 :       if (!GetPreloadListEntry(aHost.get())) {
    1524           0 :         mPreloadStateStorage->Remove(preloadKey,
    1525           0 :                                      mozilla::DataStorage_Persistent);
    1526             :       }
    1527             :     }
    1528           0 :     return false;
    1529             :   }
    1530             : 
    1531           8 :   const nsSTSPreload* preload = nullptr;
    1532             : 
    1533             :   // Finally look in the static preload list.
    1534          24 :   if (siteState->mHSTSState == SecurityPropertyUnset &&
    1535          16 :       dynamicState->mHSTSState == SecurityPropertyUnset &&
    1536           8 :       (preload = GetPreloadListEntry(aHost.get())) != nullptr) {
    1537           0 :     SSSLOG(("%s is a preloaded HSTS host", aHost.get()));
    1538           0 :     *aResult = aRequireIncludeSubdomains ? preload->mIncludeSubdomains
    1539           0 :                                          : true;
    1540           0 :     if (aCached) {
    1541             :       // Only set cached if this includes subdomains
    1542           0 :       *aCached = aRequireIncludeSubdomains ? preload->mIncludeSubdomains
    1543           0 :                                            : true;
    1544             :     }
    1545           0 :     if (aSource) {
    1546           0 :       *aSource = SourcePreload;
    1547             :     }
    1548           0 :     return true;
    1549             :   }
    1550             : 
    1551           8 :   return false;
    1552             : }
    1553             : 
    1554             : nsresult
    1555           8 : nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
    1556             :                                     uint32_t aFlags,
    1557             :                                     const OriginAttributes& aOriginAttributes,
    1558             :                                     bool* aCached,
    1559             :                                     SecurityPropertySource* aSource,
    1560             :                                     bool* aResult)
    1561             : {
    1562             :   // Child processes are not allowed direct access to this.
    1563           8 :   if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
    1564           0 :     MOZ_CRASH("Child process: no direct access to "
    1565             :               "nsISiteSecurityService::IsSecureHost for non-HSTS entries");
    1566             :   }
    1567             : 
    1568           8 :   NS_ENSURE_ARG(aResult);
    1569             : 
    1570             :   // Only HSTS and HPKP are supported at the moment.
    1571           8 :   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
    1572             :                  aType == nsISiteSecurityService::HEADER_HPKP,
    1573             :                  NS_ERROR_NOT_IMPLEMENTED);
    1574             : 
    1575             :   // set default in case if we can't find any STS information
    1576           8 :   *aResult = false;
    1577             : 
    1578             :   /* An IP address never qualifies as a secure URI. */
    1579          16 :   const nsCString& flatHost = PromiseFlatCString(aHost);
    1580           8 :   if (HostIsIPAddress(flatHost)) {
    1581           0 :     return NS_OK;
    1582             :   }
    1583             : 
    1584           8 :   if (aType == nsISiteSecurityService::HEADER_HPKP) {
    1585           0 :     RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
    1586           0 :     if (!certVerifier) {
    1587           0 :       return NS_ERROR_FAILURE;
    1588             :     }
    1589           0 :     if (certVerifier->mPinningMode ==
    1590             :         CertVerifier::PinningMode::pinningDisabled) {
    1591           0 :       return NS_OK;
    1592             :     }
    1593           0 :     bool enforceTestMode = certVerifier->mPinningMode ==
    1594           0 :                            CertVerifier::PinningMode::pinningEnforceTestMode;
    1595           0 :     return PublicKeyPinningService::HostHasPins(flatHost.get(),
    1596             :                                                 mozilla::pkix::Now(),
    1597             :                                                 enforceTestMode, aOriginAttributes,
    1598           0 :                                                 *aResult);
    1599             :   }
    1600             : 
    1601             :   // Holepunch chart.apis.google.com and subdomains.
    1602             :   nsAutoCString host(
    1603          16 :     PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
    1604          32 :   if (host.EqualsLiteral("chart.apis.google.com") ||
    1605          32 :       StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
    1606           0 :     if (aCached) {
    1607           0 :       *aCached = true;
    1608             :     }
    1609           0 :     if (aSource) {
    1610           0 :       *aSource = SourcePreload;
    1611             :     }
    1612           0 :     return NS_OK;
    1613             :   }
    1614             : 
    1615             :   // First check the exact host.
    1616           8 :   if (HostHasHSTSEntry(host, false, aFlags, aOriginAttributes, aResult,
    1617             :                        aCached, aSource)) {
    1618           0 :     return NS_OK;
    1619             :   }
    1620             : 
    1621             : 
    1622           8 :   SSSLOG(("no HSTS data for %s found, walking up domain", host.get()));
    1623             :   const char *subdomain;
    1624             : 
    1625           8 :   uint32_t offset = 0;
    1626           8 :   for (offset = host.FindChar('.', offset) + 1;
    1627           8 :        offset > 0;
    1628           0 :        offset = host.FindChar('.', offset) + 1) {
    1629             : 
    1630           0 :     subdomain = host.get() + offset;
    1631             : 
    1632             :     // If we get an empty string, don't continue.
    1633           0 :     if (strlen(subdomain) < 1) {
    1634           0 :       break;
    1635             :     }
    1636             : 
    1637             :     // Do the same thing as with the exact host except now we're looking at
    1638             :     // ancestor domains of the original host and, therefore, we have to require
    1639             :     // that the entry includes subdomains.
    1640           0 :     nsAutoCString subdomainString(subdomain);
    1641             : 
    1642           0 :     if (HostHasHSTSEntry(subdomainString, true, aFlags, aOriginAttributes, aResult,
    1643             :                          aCached, aSource)) {
    1644           0 :       break;
    1645             :     }
    1646             : 
    1647           0 :     SSSLOG(("no HSTS data for %s found, walking up domain", subdomain));
    1648             :   }
    1649             : 
    1650             :   // Use whatever we ended up with, which defaults to false.
    1651           8 :   return NS_OK;
    1652             : }
    1653             : 
    1654             : NS_IMETHODIMP
    1655           0 : nsSiteSecurityService::ClearAll()
    1656             : {
    1657             :    // Child processes are not allowed direct access to this.
    1658           0 :    if (!XRE_IsParentProcess()) {
    1659           0 :      MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ClearAll");
    1660             :    }
    1661             : 
    1662           0 :   return mSiteStateStorage->Clear();
    1663             : }
    1664             : 
    1665             : NS_IMETHODIMP
    1666           0 : nsSiteSecurityService::ClearPreloads()
    1667             : {
    1668             :   // Child processes are not allowed direct access to this.
    1669           0 :   if (!XRE_IsParentProcess()) {
    1670           0 :     MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ClearPreloads");
    1671             :   }
    1672             : 
    1673           0 :   return mPreloadStateStorage->Clear();
    1674             : }
    1675             : 
    1676           0 : bool entryStateNotOK(SiteHPKPState& state, mozilla::pkix::Time& aEvalTime) {
    1677           0 :   return state.mState != SecurityPropertySet || state.IsExpired(aEvalTime) ||
    1678           0 :          state.mSHA256keys.Length() < 1;
    1679             : }
    1680             : 
    1681             : NS_IMETHODIMP
    1682           0 : nsSiteSecurityService::GetKeyPinsForHostname(
    1683             :   const nsACString& aHostname,
    1684             :   mozilla::pkix::Time& aEvalTime,
    1685             :   const OriginAttributes& aOriginAttributes,
    1686             :   /*out*/ nsTArray<nsCString>& pinArray,
    1687             :   /*out*/ bool* aIncludeSubdomains,
    1688             :   /*out*/ bool* afound)
    1689             : {
    1690             :   // Child processes are not allowed direct access to this.
    1691           0 :   if (!XRE_IsParentProcess()) {
    1692           0 :     MOZ_CRASH("Child process: no direct access to "
    1693             :               "nsISiteSecurityService::GetKeyPinsForHostname");
    1694             :   }
    1695             : 
    1696           0 :   NS_ENSURE_ARG(afound);
    1697             : 
    1698           0 :   const nsCString& flatHostname = PromiseFlatCString(aHostname);
    1699           0 :   SSSLOG(("Top of GetKeyPinsForHostname for %s", flatHostname.get()));
    1700           0 :   *afound = false;
    1701           0 :   *aIncludeSubdomains = false;
    1702           0 :   pinArray.Clear();
    1703             : 
    1704             :   nsAutoCString host(
    1705           0 :     PublicKeyPinningService::CanonicalizeHostname(flatHostname.get()));
    1706           0 :   nsAutoCString storageKey;
    1707             :   SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
    1708           0 :                 storageKey);
    1709             : 
    1710           0 :   SSSLOG(("storagekey '%s'\n", storageKey.get()));
    1711           0 :   mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent;
    1712           0 :   nsCString value = mSiteStateStorage->Get(storageKey, storageType);
    1713             : 
    1714             :   // decode now
    1715             :   RefPtr<SiteHPKPState> foundEntry =
    1716           0 :     new SiteHPKPState(host, aOriginAttributes, value);
    1717           0 :   if (entryStateNotOK(*foundEntry, aEvalTime)) {
    1718             :     // not in permanent storage, try now private
    1719           0 :     value = mSiteStateStorage->Get(storageKey, mozilla::DataStorage_Private);
    1720             :     RefPtr<SiteHPKPState> privateEntry =
    1721           0 :       new SiteHPKPState(host, aOriginAttributes, value);
    1722           0 :     if (entryStateNotOK(*privateEntry, aEvalTime)) {
    1723             :       // not in private storage, try dynamic preload
    1724           0 :       nsAutoCString preloadKey;
    1725             :       SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP,
    1726           0 :                     OriginAttributes(), preloadKey);
    1727           0 :       value = mPreloadStateStorage->Get(preloadKey,
    1728           0 :                                         mozilla::DataStorage_Persistent);
    1729             :       RefPtr<SiteHPKPState> preloadEntry =
    1730           0 :         new SiteHPKPState(host, aOriginAttributes, value);
    1731           0 :       if (entryStateNotOK(*preloadEntry, aEvalTime)) {
    1732           0 :         return NS_OK;
    1733             :       }
    1734           0 :       foundEntry = preloadEntry;
    1735             :     } else {
    1736           0 :       foundEntry = privateEntry;
    1737             :     }
    1738             :   }
    1739           0 :   pinArray = foundEntry->mSHA256keys;
    1740           0 :   *aIncludeSubdomains = foundEntry->mIncludeSubdomains;
    1741           0 :   *afound = true;
    1742           0 :   return NS_OK;
    1743             : }
    1744             : 
    1745             : NS_IMETHODIMP
    1746           0 : nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
    1747             :                                   bool aIncludeSubdomains,
    1748             :                                   int64_t aExpires, uint32_t aPinCount,
    1749             :                                   const char** aSha256Pins,
    1750             :                                   bool aIsPreload,
    1751             :                                   JS::HandleValue aOriginAttributes,
    1752             :                                   JSContext* aCx,
    1753             :                                   uint8_t aArgc,
    1754             :                                   /*out*/ bool* aResult)
    1755             : {
    1756             :   // Child processes are not allowed direct access to this.
    1757           0 :   if (!XRE_IsParentProcess()) {
    1758           0 :     MOZ_CRASH("Child process: no direct access to "
    1759             :               "nsISiteSecurityService::SetKeyPins");
    1760             :   }
    1761             : 
    1762           0 :   NS_ENSURE_ARG_POINTER(aResult);
    1763           0 :   NS_ENSURE_ARG_POINTER(aSha256Pins);
    1764           0 :   OriginAttributes originAttributes;
    1765           0 :   if (aArgc > 1) {
    1766             :     // OriginAttributes were passed in.
    1767           0 :     if (!aOriginAttributes.isObject() ||
    1768           0 :         !originAttributes.Init(aCx, aOriginAttributes)) {
    1769           0 :       return NS_ERROR_INVALID_ARG;
    1770             :     }
    1771             :   }
    1772           0 :   if (aIsPreload && originAttributes != OriginAttributes()) {
    1773           0 :     return NS_ERROR_INVALID_ARG;
    1774             :   }
    1775             : 
    1776           0 :   SSSLOG(("Top of SetKeyPins"));
    1777             : 
    1778           0 :   nsTArray<nsCString> sha256keys;
    1779           0 :   for (unsigned int i = 0; i < aPinCount; i++) {
    1780           0 :     nsAutoCString pin(aSha256Pins[i]);
    1781           0 :     SSSLOG(("SetPins pin=%s\n", pin.get()));
    1782           0 :     if (!stringIsBase64EncodingOf256bitValue(pin)) {
    1783           0 :       return NS_ERROR_INVALID_ARG;
    1784             :     }
    1785           0 :     sha256keys.AppendElement(pin);
    1786             :   }
    1787             :   // we always store data in permanent storage (ie no flags)
    1788           0 :   const nsCString& flatHost = PromiseFlatCString(aHost);
    1789             :   nsAutoCString host(
    1790           0 :     PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
    1791             :   RefPtr<SiteHPKPState> dynamicEntry = new SiteHPKPState(host, originAttributes,
    1792           0 :     aExpires, SecurityPropertySet, aIncludeSubdomains, sha256keys);
    1793           0 :   return SetHPKPState(host.get(), *dynamicEntry, 0, aIsPreload, originAttributes);
    1794             : }
    1795             : 
    1796             : NS_IMETHODIMP
    1797           0 : nsSiteSecurityService::SetHSTSPreload(const nsACString& aHost,
    1798             :                                       bool aIncludeSubdomains,
    1799             :                                       int64_t aExpires,
    1800             :                               /*out*/ bool* aResult)
    1801             : {
    1802             :   // Child processes are not allowed direct access to this.
    1803           0 :   if (!XRE_IsParentProcess()) {
    1804           0 :     MOZ_CRASH("Child process: no direct access to "
    1805             :               "nsISiteSecurityService::SetHSTSPreload");
    1806             :   }
    1807             : 
    1808           0 :   NS_ENSURE_ARG_POINTER(aResult);
    1809             : 
    1810           0 :   SSSLOG(("Top of SetHSTSPreload"));
    1811             : 
    1812           0 :   const nsCString& flatHost = PromiseFlatCString(aHost);
    1813             :   nsAutoCString host(
    1814           0 :     PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
    1815           0 :   return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, host.get(), aExpires,
    1816             :                       aIncludeSubdomains, 0, SecurityPropertySet,
    1817           0 :                       SourcePreload, OriginAttributes());
    1818             : }
    1819             : 
    1820             : nsresult
    1821           0 : nsSiteSecurityService::SetHPKPState(const char* aHost, SiteHPKPState& entry,
    1822             :                                     uint32_t aFlags, bool aIsPreload,
    1823             :                                     const OriginAttributes& aOriginAttributes)
    1824             : {
    1825           0 :   if (aIsPreload && aOriginAttributes != OriginAttributes()) {
    1826           0 :     return NS_ERROR_INVALID_ARG;
    1827             :   }
    1828           0 :   SSSLOG(("Top of SetPKPState"));
    1829           0 :   nsAutoCString host(aHost);
    1830           0 :   nsAutoCString storageKey;
    1831             :   SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
    1832           0 :                 storageKey);
    1833           0 :   bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
    1834             :   mozilla::DataStorageType storageType = isPrivate
    1835           0 :                                          ? mozilla::DataStorage_Private
    1836           0 :                                          : mozilla::DataStorage_Persistent;
    1837           0 :   nsAutoCString stateString;
    1838           0 :   entry.ToString(stateString);
    1839             : 
    1840             :   nsresult rv;
    1841           0 :   if (aIsPreload) {
    1842           0 :     rv = mPreloadStateStorage->Put(storageKey, stateString,
    1843           0 :                                    mozilla::DataStorage_Persistent);
    1844             :   } else {
    1845           0 :     rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
    1846             :   }
    1847           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1848           0 :   return NS_OK;
    1849             : }
    1850             : 
    1851             : NS_IMETHODIMP
    1852           0 : nsSiteSecurityService::Enumerate(uint32_t aType,
    1853             :                                  nsISimpleEnumerator** aEnumerator)
    1854             : {
    1855           0 :   NS_ENSURE_ARG(aEnumerator);
    1856             : 
    1857           0 :   nsAutoCString keySuffix;
    1858           0 :   switch (aType) {
    1859             :     case nsISiteSecurityService::HEADER_HSTS:
    1860           0 :       keySuffix.AssignASCII(kHSTSKeySuffix);
    1861           0 :       break;
    1862             :     case nsISiteSecurityService::HEADER_HPKP:
    1863           0 :       keySuffix.AssignASCII(kHPKPKeySuffix);
    1864           0 :       break;
    1865             :     default:
    1866           0 :       return NS_ERROR_INVALID_ARG;
    1867             :   }
    1868             : 
    1869           0 :   InfallibleTArray<mozilla::dom::DataStorageItem> items;
    1870           0 :   mSiteStateStorage->GetAll(&items);
    1871             : 
    1872           0 :   nsCOMArray<nsISiteSecurityState> states;
    1873           0 :   for (const mozilla::dom::DataStorageItem& item : items) {
    1874           0 :     if (!StringEndsWith(item.key(), keySuffix)) {
    1875             :       // The key does not end with correct suffix, so is not the type we want.
    1876           0 :       continue;
    1877             :     }
    1878             : 
    1879             :     nsCString origin(
    1880           0 :       StringHead(item.key(), item.key().Length() - keySuffix.Length()));
    1881           0 :     nsAutoCString hostname;
    1882           0 :     OriginAttributes originAttributes;
    1883           0 :     if (!originAttributes.PopulateFromOrigin(origin, hostname)) {
    1884           0 :       return NS_ERROR_FAILURE;
    1885             :     }
    1886             : 
    1887           0 :     nsCOMPtr<nsISiteSecurityState> state;
    1888           0 :     switch(aType) {
    1889             :       case nsISiteSecurityService::HEADER_HSTS:
    1890           0 :         state = new SiteHSTSState(hostname, originAttributes, item.value());
    1891           0 :         break;
    1892             :       case nsISiteSecurityService::HEADER_HPKP:
    1893           0 :         state = new SiteHPKPState(hostname, originAttributes, item.value());
    1894           0 :         break;
    1895             :       default:
    1896           0 :         MOZ_ASSERT_UNREACHABLE("SSS:Enumerate got invalid type");
    1897             :     }
    1898             : 
    1899           0 :     states.AppendObject(state);
    1900             :   }
    1901             : 
    1902           0 :   NS_NewArrayEnumerator(aEnumerator, states);
    1903           0 :   return NS_OK;
    1904             : }
    1905             : 
    1906             : //------------------------------------------------------------
    1907             : // nsSiteSecurityService::nsIObserver
    1908             : //------------------------------------------------------------
    1909             : 
    1910             : NS_IMETHODIMP
    1911           0 : nsSiteSecurityService::Observe(nsISupports* /*subject*/, const char* topic,
    1912             :                                const char16_t* /*data*/)
    1913             : {
    1914             :   // Don't access Preferences off the main thread.
    1915           0 :   if (!NS_IsMainThread()) {
    1916           0 :     MOZ_ASSERT_UNREACHABLE("Preferences accessed off main thread");
    1917             :     return NS_ERROR_NOT_SAME_THREAD;
    1918             :   }
    1919             : 
    1920           0 :   if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
    1921           0 :     mUsePreloadList = mozilla::Preferences::GetBool(
    1922             :       "network.stricttransportsecurity.preloadlist", true);
    1923           0 :     mPreloadListTimeOffset =
    1924           0 :       mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
    1925           0 :     mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
    1926             :       "security.cert_pinning.process_headers_from_non_builtin_roots", false);
    1927           0 :     mMaxMaxAge = mozilla::Preferences::GetInt(
    1928             :       "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
    1929             :   }
    1930             : 
    1931           0 :   return NS_OK;
    1932             : }

Generated by: LCOV version 1.13