security/manager/boot/src/nsSiteSecurityService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     5 #include "plstr.h"
     6 #include "prlog.h"
     7 #include "prprf.h"
     8 #include "prnetdb.h"
     9 #include "nsCRTGlue.h"
    10 #include "nsIPermissionManager.h"
    11 #include "nsISSLStatus.h"
    12 #include "nsISSLStatusProvider.h"
    13 #include "nsSiteSecurityService.h"
    14 #include "nsIURI.h"
    15 #include "nsNetUtil.h"
    16 #include "nsThreadUtils.h"
    17 #include "nsString.h"
    18 #include "nsIScriptSecurityManager.h"
    19 #include "nsISocketProvider.h"
    20 #include "mozilla/Preferences.h"
    21 #include "mozilla/LinkedList.h"
    22 #include "nsSecurityHeaderParser.h"
    24 // A note about the preload list:
    25 // When a site specifically disables sts by sending a header with
    26 // 'max-age: 0', we keep a "knockout" value that means "we have no information
    27 // regarding the sts state of this host" (any ancestor of "this host" can still
    28 // influence its sts status via include subdomains, however).
    29 // This prevents the preload list from overriding the site's current
    30 // desired sts status. Knockout values are indicated by permission values of
    31 // STS_KNOCKOUT.
    32 #include "nsSTSPreloadList.inc"
    34 #define STS_SET (nsIPermissionManager::ALLOW_ACTION)
    35 #define STS_UNSET (nsIPermissionManager::UNKNOWN_ACTION)
    36 #define STS_KNOCKOUT (nsIPermissionManager::DENY_ACTION)
    38 #if defined(PR_LOGGING)
    39 static PRLogModuleInfo *
    40 GetSSSLog()
    41 {
    42   static PRLogModuleInfo *gSSSLog;
    43   if (!gSSSLog)
    44     gSSSLog = PR_NewLogModule("nsSSService");
    45   return gSSSLog;
    46 }
    47 #endif
    49 #define SSSLOG(args) PR_LOG(GetSSSLog(), 4, args)
    51 ////////////////////////////////////////////////////////////////////////////////
    53 nsSSSHostEntry::nsSSSHostEntry(const char* aHost)
    54   : mHost(aHost)
    55   , mExpireTime(0)
    56   , mStsPermission(STS_UNSET)
    57   , mExpired(false)
    58   , mIncludeSubdomains(false)
    59 {
    60 }
    62 nsSSSHostEntry::nsSSSHostEntry(const nsSSSHostEntry& toCopy)
    63   : mHost(toCopy.mHost)
    64   , mExpireTime(toCopy.mExpireTime)
    65   , mStsPermission(toCopy.mStsPermission)
    66   , mExpired(toCopy.mExpired)
    67   , mIncludeSubdomains(toCopy.mIncludeSubdomains)
    68 {
    69 }
    71 ////////////////////////////////////////////////////////////////////////////////
    74 nsSiteSecurityService::nsSiteSecurityService()
    75   : mUsePreloadList(true)
    76 {
    77 }
    79 nsSiteSecurityService::~nsSiteSecurityService()
    80 {
    81 }
    83 NS_IMPL_ISUPPORTS(nsSiteSecurityService,
    84                   nsIObserver,
    85                   nsISiteSecurityService)
    87 nsresult
    88 nsSiteSecurityService::Init()
    89 {
    90    nsresult rv;
    92    mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    93    NS_ENSURE_SUCCESS(rv, rv);
    95    mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
    96    mozilla::Preferences::AddStrongObserver(this, "network.stricttransportsecurity.preloadlist");
    97    mObserverService = mozilla::services::GetObserverService();
    98    if (mObserverService)
    99      mObserverService->AddObserver(this, "last-pb-context-exited", false);
   101    return NS_OK;
   102 }
   104 nsresult
   105 nsSiteSecurityService::GetHost(nsIURI *aURI, nsACString &aResult)
   106 {
   107   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
   108   if (!innerURI) return NS_ERROR_FAILURE;
   110   nsresult rv = innerURI->GetAsciiHost(aResult);
   112   if (NS_FAILED(rv) || aResult.IsEmpty())
   113     return NS_ERROR_UNEXPECTED;
   115   return NS_OK;
   116 }
   118 nsresult
   119 nsSiteSecurityService::GetPrincipalForURI(nsIURI* aURI,
   120                                           nsIPrincipal** aPrincipal)
   121 {
   122   nsresult rv;
   123   nsCOMPtr<nsIScriptSecurityManager> securityManager =
   124      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   125   NS_ENSURE_SUCCESS(rv, rv);
   127   // We have to normalize the scheme of the URIs we're using, so just use https.
   128   // HSTS information is shared across all ports for a given host.
   129   nsAutoCString host;
   130   rv = GetHost(aURI, host);
   131   NS_ENSURE_SUCCESS(rv, rv);
   132   nsCOMPtr<nsIURI> uri;
   133   rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + host);
   134   NS_ENSURE_SUCCESS(rv, rv);
   136   // We want all apps to share HSTS state, so this is one of the few places
   137   // where we do not silo persistent state by extended origin.
   138   return securityManager->GetNoAppCodebasePrincipal(uri, aPrincipal);
   139 }
   141 nsresult
   142 nsSiteSecurityService::SetState(uint32_t aType,
   143                                 nsIURI* aSourceURI,
   144                                 int64_t maxage,
   145                                 bool includeSubdomains,
   146                                 uint32_t flags)
   147 {
   148   // If max-age is zero, that's an indication to immediately remove the
   149   // permissions, so here's a shortcut.
   150   if (!maxage) {
   151     return RemoveState(aType, aSourceURI, flags);
   152   }
   154   // Expire time is millis from now.  Since STS max-age is in seconds, and
   155   // PR_Now() is in micros, must equalize the units at milliseconds.
   156   int64_t expiretime = (PR_Now() / PR_USEC_PER_MSEC) +
   157                        (maxage * PR_MSEC_PER_SEC);
   159   bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
   161   // record entry for this host with max-age in the permissions manager
   162   SSSLOG(("SSS: maxage permission SET, adding permission\n"));
   163   nsresult rv = AddPermission(aSourceURI,
   164                               STS_PERMISSION,
   165                               (uint32_t) STS_SET,
   166                               (uint32_t) nsIPermissionManager::EXPIRE_TIME,
   167                               expiretime,
   168                               isPrivate);
   169   NS_ENSURE_SUCCESS(rv, rv);
   171   if (includeSubdomains) {
   172     // record entry for this host with include subdomains in the permissions manager
   173     SSSLOG(("SSS: subdomains permission SET, adding permission\n"));
   174     rv = AddPermission(aSourceURI,
   175                        STS_SUBDOMAIN_PERMISSION,
   176                        (uint32_t) STS_SET,
   177                        (uint32_t) nsIPermissionManager::EXPIRE_TIME,
   178                        expiretime,
   179                        isPrivate);
   180     NS_ENSURE_SUCCESS(rv, rv);
   181   } else { // !includeSubdomains
   182     nsAutoCString hostname;
   183     rv = GetHost(aSourceURI, hostname);
   184     NS_ENSURE_SUCCESS(rv, rv);
   186     SSSLOG(("SSS: subdomains permission UNSET, removing any existing ones\n"));
   187     rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
   188     NS_ENSURE_SUCCESS(rv, rv);
   189   }
   190   return NS_OK;
   191 }
   193 NS_IMETHODIMP
   194 nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags)
   195 {
   196   // Should be called on the main thread (or via proxy) since the permission
   197   // manager is used and it's not threadsafe.
   198   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   199   // Only HSTS is supported at the moment.
   200   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   201                  NS_ERROR_NOT_IMPLEMENTED);
   203   nsAutoCString hostname;
   204   nsresult rv = GetHost(aURI, hostname);
   205   NS_ENSURE_SUCCESS(rv, rv);
   207   bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
   209   rv = RemovePermission(hostname, STS_PERMISSION, isPrivate);
   210   NS_ENSURE_SUCCESS(rv, rv);
   211   SSSLOG(("SSS: deleted maxage permission\n"));
   213   rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
   214   NS_ENSURE_SUCCESS(rv, rv);
   215   SSSLOG(("SSS: deleted subdomains permission\n"));
   217   return NS_OK;
   218 }
   220 static bool
   221 HostIsIPAddress(const char *hostname)
   222 {
   223   PRNetAddr hostAddr;
   224   return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
   225 }
   227 NS_IMETHODIMP
   228 nsSiteSecurityService::ProcessHeader(uint32_t aType,
   229                                      nsIURI* aSourceURI,
   230                                      const char* aHeader,
   231                                      uint32_t aFlags,
   232                                      uint64_t *aMaxAge,
   233                                      bool *aIncludeSubdomains)
   234 {
   235   // Should be called on the main thread (or via proxy) since the permission
   236   // manager is used and it's not threadsafe.
   237   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   238   // Only HSTS is supported at the moment.
   239   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   240                  NS_ERROR_NOT_IMPLEMENTED);
   242   if (aMaxAge != nullptr) {
   243     *aMaxAge = 0;
   244   }
   246   if (aIncludeSubdomains != nullptr) {
   247     *aIncludeSubdomains = false;
   248   }
   250   nsAutoCString host;
   251   nsresult rv = GetHost(aSourceURI, host);
   252   NS_ENSURE_SUCCESS(rv, rv);
   253   if (HostIsIPAddress(host.get())) {
   254     /* Don't process headers if a site is accessed by IP address. */
   255     return NS_OK;
   256   }
   258   char * header = NS_strdup(aHeader);
   259   if (!header) return NS_ERROR_OUT_OF_MEMORY;
   260   rv = ProcessHeaderMutating(aType, aSourceURI, header, aFlags,
   261                              aMaxAge, aIncludeSubdomains);
   262   NS_Free(header);
   263   return rv;
   264 }
   266 nsresult
   267 nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType,
   268                                              nsIURI* aSourceURI,
   269                                              char* aHeader,
   270                                              uint32_t aFlags,
   271                                              uint64_t *aMaxAge,
   272                                              bool *aIncludeSubdomains)
   273 {
   274   SSSLOG(("SSS: processing header '%s'", aHeader));
   276   // "Strict-Transport-Security" ":" OWS
   277   //      STS-d  *( OWS ";" OWS STS-d  OWS)
   278   //
   279   //  ; STS directive
   280   //  STS-d      = maxAge / includeSubDomains
   281   //
   282   //  maxAge     = "max-age" "=" delta-seconds v-ext
   283   //
   284   //  includeSubDomains = [ "includeSubDomains" ]
   285   //
   286   //  The order of the directives is not significant.
   287   //  All directives must appear only once.
   288   //  Directive names are case-insensitive.
   289   //  The entire header is invalid if a directive not conforming to the
   290   //  syntax is encountered.
   291   //  Unrecognized directives (that are otherwise syntactically valid) are
   292   //  ignored, and the rest of the header is parsed as normal.
   294   bool foundMaxAge = false;
   295   bool foundIncludeSubdomains = false;
   296   bool foundUnrecognizedDirective = false;
   297   int64_t maxAge = 0;
   299   NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
   300   NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
   303   nsSecurityHeaderParser parser(aHeader);
   304   nsresult rv = parser.Parse();
   305   if (NS_FAILED(rv)) {
   306     SSSLOG(("SSS: could not parse header"));
   307     return rv;
   308   }
   309   mozilla::LinkedList<nsSecurityHeaderDirective> *directives = parser.GetDirectives();
   311   for (nsSecurityHeaderDirective *directive = directives->getFirst();
   312        directive != nullptr; directive = directive->getNext()) {
   313     if (directive->mName.Length() == max_age_var.Length() &&
   314         directive->mName.EqualsIgnoreCase(max_age_var.get(),
   315                                           max_age_var.Length())) {
   316       if (foundMaxAge) {
   317         SSSLOG(("SSS: found two max-age directives"));
   318         return NS_ERROR_FAILURE;
   319       }
   321       SSSLOG(("SSS: found max-age directive"));
   322       foundMaxAge = true;
   324       size_t len = directive->mValue.Length();
   325       for (size_t i = 0; i < len; i++) {
   326         char chr = directive->mValue.CharAt(i);
   327         if (chr < '0' || chr > '9') {
   328           SSSLOG(("SSS: invalid value for max-age directive"));
   329           return NS_ERROR_FAILURE;
   330         }
   331       }
   333       if (PR_sscanf(directive->mValue.get(), "%lld", &maxAge) != 1) {
   334         SSSLOG(("SSS: could not parse delta-seconds"));
   335         return NS_ERROR_FAILURE;
   336       }
   338       SSSLOG(("SSS: parsed delta-seconds: %lld", maxAge));
   339     } else if (directive->mName.Length() == include_subd_var.Length() &&
   340                directive->mName.EqualsIgnoreCase(include_subd_var.get(),
   341                                                  include_subd_var.Length())) {
   342       if (foundIncludeSubdomains) {
   343         SSSLOG(("SSS: found two includeSubdomains directives"));
   344         return NS_ERROR_FAILURE;
   345       }
   347       SSSLOG(("SSS: found includeSubdomains directive"));
   348       foundIncludeSubdomains = true;
   350       if (directive->mValue.Length() != 0) {
   351         SSSLOG(("SSS: includeSubdomains directive unexpectedly had value '%s'", directive->mValue.get()));
   352         return NS_ERROR_FAILURE;
   353       }
   354     } else {
   355       SSSLOG(("SSS: ignoring unrecognized directive '%s'", directive->mName.get()));
   356       foundUnrecognizedDirective = true;
   357     }
   358   }
   360   // after processing all the directives, make sure we came across max-age
   361   // somewhere.
   362   if (!foundMaxAge) {
   363     SSSLOG(("SSS: did not encounter required max-age directive"));
   364     return NS_ERROR_FAILURE;
   365   }
   367   // record the successfully parsed header data.
   368   SetState(aType, aSourceURI, maxAge, foundIncludeSubdomains, aFlags);
   370   if (aMaxAge != nullptr) {
   371     *aMaxAge = (uint64_t)maxAge;
   372   }
   374   if (aIncludeSubdomains != nullptr) {
   375     *aIncludeSubdomains = foundIncludeSubdomains;
   376   }
   378   return foundUnrecognizedDirective ?
   379          NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA :
   380          NS_OK;
   381 }
   383 NS_IMETHODIMP
   384 nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
   385                                     uint32_t aFlags, bool* aResult)
   386 {
   387   // Should be called on the main thread (or via proxy) since the permission
   388   // manager is used and it's not threadsafe.
   389   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   390   // Only HSTS is supported at the moment.
   391   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   392                  NS_ERROR_NOT_IMPLEMENTED);
   394   /* An IP address never qualifies as a secure URI. */
   395   if (HostIsIPAddress(aHost)) {
   396     *aResult = false;
   397     return NS_OK;
   398   }
   400   nsCOMPtr<nsIURI> uri;
   401   nsDependentCString hostString(aHost);
   402   nsresult rv = NS_NewURI(getter_AddRefs(uri),
   403                           NS_LITERAL_CSTRING("https://") + hostString);
   404   NS_ENSURE_SUCCESS(rv, rv);
   405   return IsSecureURI(aType, uri, aFlags, aResult);
   406 }
   408 int STSPreloadCompare(const void *key, const void *entry)
   409 {
   410   const char *keyStr = (const char *)key;
   411   const nsSTSPreload *preloadEntry = (const nsSTSPreload *)entry;
   412   return strcmp(keyStr, preloadEntry->mHost);
   413 }
   415 // Returns the preload list entry for the given host, if it exists.
   416 // Only does exact host matching - the user must decide how to use the returned
   417 // data. May return null.
   418 const nsSTSPreload *
   419 nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
   420 {
   421   PRTime currentTime = PR_Now();
   422   int32_t timeOffset = 0;
   423   nsresult rv = mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds",
   424                                              &timeOffset);
   425   if (NS_SUCCEEDED(rv)) {
   426     currentTime += (PRTime(timeOffset) * PR_USEC_PER_SEC);
   427   }
   429   if (mUsePreloadList && currentTime < gPreloadListExpirationTime) {
   430     return (const nsSTSPreload *) bsearch(aHost,
   431                                           kSTSPreloadList,
   432                                           mozilla::ArrayLength(kSTSPreloadList),
   433                                           sizeof(nsSTSPreload),
   434                                           STSPreloadCompare);
   435   }
   437   return nullptr;
   438 }
   440 NS_IMETHODIMP
   441 nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
   442                                    uint32_t aFlags, bool* aResult)
   443 {
   444   // Should be called on the main thread (or via proxy) since the permission
   445   // manager is used and it's not threadsafe.
   446   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   447   // Only HSTS is supported at the moment.
   448   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   449                  NS_ERROR_NOT_IMPLEMENTED);
   451   // set default in case if we can't find any STS information
   452   *aResult = false;
   454   nsAutoCString host;
   455   nsresult rv = GetHost(aURI, host);
   456   NS_ENSURE_SUCCESS(rv, rv);
   458   /* An IP address never qualifies as a secure URI. */
   459   if (HostIsIPAddress(host.BeginReading())) {
   460     return NS_OK;
   461   }
   463   // Holepunch chart.apis.google.com and subdomains.
   464   if (host.Equals(NS_LITERAL_CSTRING("chart.apis.google.com")) ||
   465       StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
   466     return NS_OK;
   467   }
   469   const nsSTSPreload *preload = nullptr;
   470   nsSSSHostEntry *pbEntry = nullptr;
   472   bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
   473   if (isPrivate) {
   474     pbEntry = mPrivateModeHostTable.GetEntry(host.get());
   475   }
   477   nsCOMPtr<nsIPrincipal> principal;
   478   rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
   479   NS_ENSURE_SUCCESS(rv, rv);
   481   uint32_t permMgrPermission;
   482   rv = mPermMgr->TestExactPermissionFromPrincipal(principal, STS_PERMISSION,
   483                                                   &permMgrPermission);
   484   NS_ENSURE_SUCCESS(rv, rv);
   486   // First check the exact host. This involves first checking for an entry in
   487   // the private browsing table. If that entry exists, we don't want to check
   488   // in either the permission manager or the preload list. We only want to use
   489   // the stored permission if it is not a knockout entry, however.
   490   // Additionally, if it is a knockout entry, we want to stop looking for data
   491   // on the host, because the knockout entry indicates "we have no information
   492   // regarding the sts status of this host".
   493   if (pbEntry && pbEntry->mStsPermission != STS_UNSET) {
   494     SSSLOG(("Found private browsing table entry for %s", host.get()));
   495     if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) {
   496       *aResult = true;
   497       return NS_OK;
   498     }
   499   }
   500   // Next we look in the permission manager. Same story here regarding
   501   // knockout entries.
   502   else if (permMgrPermission != STS_UNSET) {
   503     SSSLOG(("Found permission manager entry for %s", host.get()));
   504     if (permMgrPermission == STS_SET) {
   505       *aResult = true;
   506       return NS_OK;
   507     }
   508   }
   509   // Finally look in the preloaded list. This is the exact host,
   510   // so if an entry exists at all, this host is sts.
   511   else if (GetPreloadListEntry(host.get())) {
   512     SSSLOG(("%s is a preloaded STS host", host.get()));
   513     *aResult = true;
   514     return NS_OK;
   515   }
   517   // Used for testing permissions as we walk up the domain tree.
   518   nsCOMPtr<nsIURI> domainWalkURI;
   519   nsCOMPtr<nsIPrincipal> domainWalkPrincipal;
   520   const char *subdomain;
   522   SSSLOG(("no HSTS data for %s found, walking up domain", host.get()));
   523   uint32_t offset = 0;
   524   for (offset = host.FindChar('.', offset) + 1;
   525        offset > 0;
   526        offset = host.FindChar('.', offset) + 1) {
   528     subdomain = host.get() + offset;
   530     // If we get an empty string, don't continue.
   531     if (strlen(subdomain) < 1) {
   532       break;
   533     }
   535     if (isPrivate) {
   536       pbEntry = mPrivateModeHostTable.GetEntry(subdomain);
   537     }
   539     // normalize all URIs with https://
   540     rv = NS_NewURI(getter_AddRefs(domainWalkURI),
   541                    NS_LITERAL_CSTRING("https://") + Substring(host, offset));
   542     NS_ENSURE_SUCCESS(rv, rv);
   544     rv = GetPrincipalForURI(domainWalkURI, getter_AddRefs(domainWalkPrincipal));
   545     NS_ENSURE_SUCCESS(rv, rv);
   547     rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal,
   548                                                     STS_PERMISSION,
   549                                                     &permMgrPermission);
   550     NS_ENSURE_SUCCESS(rv, rv);
   552     // Do the same thing as with the exact host, except now we're looking at
   553     // ancestor domains of the original host. So, we have to look at the
   554     // include subdomains permissions (although we still have to check for the
   555     // STS_PERMISSION first to check that this is an sts host and not a
   556     // knockout entry - and again, if it is a knockout entry, we stop looking
   557     // for data on it and skip to the next higher up ancestor domain).
   558     if (pbEntry && pbEntry->mStsPermission != STS_UNSET) {
   559       SSSLOG(("Found private browsing table entry for %s", subdomain));
   560       if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) {
   561         *aResult = pbEntry->mIncludeSubdomains;
   562         break;
   563       }
   564     }
   565     else if (permMgrPermission != STS_UNSET) {
   566       SSSLOG(("Found permission manager entry for %s", subdomain));
   567       if (permMgrPermission == STS_SET) {
   568         uint32_t subdomainPermission;
   569         rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal,
   570                                                         STS_SUBDOMAIN_PERMISSION,
   571                                                         &subdomainPermission);
   572         NS_ENSURE_SUCCESS(rv, rv);
   573         *aResult = (subdomainPermission == STS_SET);
   574         break;
   575       }
   576     }
   577     // This is an ancestor, so if we get a match, we have to check if the
   578     // preloaded entry includes subdomains.
   579     else if ((preload = GetPreloadListEntry(subdomain)) != nullptr) {
   580       if (preload->mIncludeSubdomains) {
   581         SSSLOG(("%s is a preloaded STS host", subdomain));
   582         *aResult = true;
   583         break;
   584       }
   585     }
   587     SSSLOG(("no HSTS data for %s found, walking up domain", subdomain));
   588   }
   590   // Use whatever we ended up with, which defaults to false.
   591   return NS_OK;
   592 }
   595 // Verify the trustworthiness of the security info (are there any cert errors?)
   596 NS_IMETHODIMP
   597 nsSiteSecurityService::ShouldIgnoreHeaders(nsISupports* aSecurityInfo,
   598                                            bool* aResult)
   599 {
   600   nsresult rv;
   601   bool tlsIsBroken = false;
   602   nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(aSecurityInfo);
   603   NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
   605   nsCOMPtr<nsISSLStatus> sslstat;
   606   rv = sslprov->GetSSLStatus(getter_AddRefs(sslstat));
   607   NS_ENSURE_SUCCESS(rv, rv);
   608   NS_ENSURE_TRUE(sslstat, NS_ERROR_FAILURE);
   610   bool trustcheck;
   611   rv = sslstat->GetIsDomainMismatch(&trustcheck);
   612   NS_ENSURE_SUCCESS(rv, rv);
   613   tlsIsBroken = tlsIsBroken || trustcheck;
   615   rv = sslstat->GetIsNotValidAtThisTime(&trustcheck);
   616   NS_ENSURE_SUCCESS(rv, rv);
   617   tlsIsBroken = tlsIsBroken || trustcheck;
   619   rv = sslstat->GetIsUntrusted(&trustcheck);
   620   NS_ENSURE_SUCCESS(rv, rv);
   621   tlsIsBroken = tlsIsBroken || trustcheck;
   623   *aResult = tlsIsBroken;
   624   return NS_OK;
   625 }
   627 //------------------------------------------------------------
   628 // nsSiteSecurityService::nsIObserver
   629 //------------------------------------------------------------
   631 NS_IMETHODIMP
   632 nsSiteSecurityService::Observe(nsISupports *subject,
   633                                const char *topic,
   634                                const char16_t *data)
   635 {
   636   if (strcmp(topic, "last-pb-context-exited") == 0) {
   637     mPrivateModeHostTable.Clear();
   638   }
   639   else if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
   640     mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
   641   }
   643   return NS_OK;
   644 }
   646 //------------------------------------------------------------
   647 // Functions to overlay the permission manager calls in case
   648 // we're in private browsing mode.
   649 //------------------------------------------------------------
   650 nsresult
   651 nsSiteSecurityService::AddPermission(nsIURI     *aURI,
   652                                      const char *aType,
   653                                      uint32_t   aPermission,
   654                                      uint32_t   aExpireType,
   655                                      int64_t    aExpireTime,
   656                                      bool       aIsPrivate)
   657 {
   658     // Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let
   659     // those be stored persistently.
   660     if (!aIsPrivate || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
   661       // Not in private mode, or manually-set permission
   662       nsCOMPtr<nsIPrincipal> principal;
   663       nsresult rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
   664       NS_ENSURE_SUCCESS(rv, rv);
   666       return mPermMgr->AddFromPrincipal(principal, aType, aPermission,
   667                                         aExpireType, aExpireTime);
   668     }
   670     nsAutoCString host;
   671     nsresult rv = GetHost(aURI, host);
   672     NS_ENSURE_SUCCESS(rv, rv);
   673     SSSLOG(("AddPermission for entry for %s", host.get()));
   675     // Update in mPrivateModeHostTable only, so any changes will be rolled
   676     // back when exiting private mode.
   678     // Note: EXPIRE_NEVER permissions should trump anything that shows up in
   679     // the HTTP header, so if there's an EXPIRE_NEVER permission already
   680     // don't store anything new.
   681     // Currently there's no way to get the type of expiry out of the
   682     // permission manager, but that's okay since there's nothing that stores
   683     // EXPIRE_NEVER permissions.
   685     // PutEntry returns an existing entry if there already is one, or it
   686     // creates a new one if there isn't.
   687     nsSSSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get());
   688     if (!entry) {
   689       return NS_ERROR_OUT_OF_MEMORY;
   690     }
   691     SSSLOG(("Created private mode entry for %s", host.get()));
   693     // AddPermission() will be called twice if the STS header encountered has
   694     // includeSubdomains (first for the main permission and second for the
   695     // subdomains permission). If AddPermission() gets called a second time
   696     // with the STS_SUBDOMAIN_PERMISSION, we just have to flip that bit in
   697     // the nsSSSHostEntry.
   698     if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
   699       entry->mIncludeSubdomains = true;
   700     }
   701     else if (strcmp(aType, STS_PERMISSION) == 0) {
   702       entry->mStsPermission = aPermission;
   703     }
   705     // Also refresh the expiration time.
   706     entry->SetExpireTime(aExpireTime);
   707     return NS_OK;
   708 }
   710 nsresult
   711 nsSiteSecurityService::RemovePermission(const nsCString  &aHost,
   712                                         const char       *aType,
   713                                         bool aIsPrivate)
   714 {
   715     // Build up a principal for use with the permission manager.
   716     // normalize all URIs with https://
   717     nsCOMPtr<nsIURI> uri;
   718     nsresult rv = NS_NewURI(getter_AddRefs(uri),
   719                             NS_LITERAL_CSTRING("https://") + aHost);
   720     NS_ENSURE_SUCCESS(rv, rv);
   722     nsCOMPtr<nsIPrincipal> principal;
   723     rv = GetPrincipalForURI(uri, getter_AddRefs(principal));
   724     NS_ENSURE_SUCCESS(rv, rv);
   726     if (!aIsPrivate) {
   727       // Not in private mode: remove permissions persistently.
   728       // This means setting the permission to STS_KNOCKOUT in case
   729       // this host is on the preload list (so we can override it).
   730       return mPermMgr->AddFromPrincipal(principal, aType,
   731                                         STS_KNOCKOUT,
   732                                         nsIPermissionManager::EXPIRE_NEVER, 0);
   733     }
   735     // Make changes in mPrivateModeHostTable only, so any changes will be
   736     // rolled back when exiting private mode.
   737     nsSSSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get());
   739     if (!entry) {
   740       entry = mPrivateModeHostTable.PutEntry(aHost.get());
   741       if (!entry) {
   742         return NS_ERROR_OUT_OF_MEMORY;
   743       }
   744       SSSLOG(("Created private mode deleted mask for %s", aHost.get()));
   745     }
   747     if (strcmp(aType, STS_PERMISSION) == 0) {
   748       entry->mStsPermission = STS_KNOCKOUT;
   749     }
   750     else if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
   751       entry->mIncludeSubdomains = false;
   752     }
   754     return NS_OK;
   755 }

mercurial