security/manager/boot/src/nsSiteSecurityService.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/boot/src/nsSiteSecurityService.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,755 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "plstr.h"
     1.9 +#include "prlog.h"
    1.10 +#include "prprf.h"
    1.11 +#include "prnetdb.h"
    1.12 +#include "nsCRTGlue.h"
    1.13 +#include "nsIPermissionManager.h"
    1.14 +#include "nsISSLStatus.h"
    1.15 +#include "nsISSLStatusProvider.h"
    1.16 +#include "nsSiteSecurityService.h"
    1.17 +#include "nsIURI.h"
    1.18 +#include "nsNetUtil.h"
    1.19 +#include "nsThreadUtils.h"
    1.20 +#include "nsString.h"
    1.21 +#include "nsIScriptSecurityManager.h"
    1.22 +#include "nsISocketProvider.h"
    1.23 +#include "mozilla/Preferences.h"
    1.24 +#include "mozilla/LinkedList.h"
    1.25 +#include "nsSecurityHeaderParser.h"
    1.26 +
    1.27 +// A note about the preload list:
    1.28 +// When a site specifically disables sts by sending a header with
    1.29 +// 'max-age: 0', we keep a "knockout" value that means "we have no information
    1.30 +// regarding the sts state of this host" (any ancestor of "this host" can still
    1.31 +// influence its sts status via include subdomains, however).
    1.32 +// This prevents the preload list from overriding the site's current
    1.33 +// desired sts status. Knockout values are indicated by permission values of
    1.34 +// STS_KNOCKOUT.
    1.35 +#include "nsSTSPreloadList.inc"
    1.36 +
    1.37 +#define STS_SET (nsIPermissionManager::ALLOW_ACTION)
    1.38 +#define STS_UNSET (nsIPermissionManager::UNKNOWN_ACTION)
    1.39 +#define STS_KNOCKOUT (nsIPermissionManager::DENY_ACTION)
    1.40 +
    1.41 +#if defined(PR_LOGGING)
    1.42 +static PRLogModuleInfo *
    1.43 +GetSSSLog()
    1.44 +{
    1.45 +  static PRLogModuleInfo *gSSSLog;
    1.46 +  if (!gSSSLog)
    1.47 +    gSSSLog = PR_NewLogModule("nsSSService");
    1.48 +  return gSSSLog;
    1.49 +}
    1.50 +#endif
    1.51 +
    1.52 +#define SSSLOG(args) PR_LOG(GetSSSLog(), 4, args)
    1.53 +
    1.54 +////////////////////////////////////////////////////////////////////////////////
    1.55 +
    1.56 +nsSSSHostEntry::nsSSSHostEntry(const char* aHost)
    1.57 +  : mHost(aHost)
    1.58 +  , mExpireTime(0)
    1.59 +  , mStsPermission(STS_UNSET)
    1.60 +  , mExpired(false)
    1.61 +  , mIncludeSubdomains(false)
    1.62 +{
    1.63 +}
    1.64 +
    1.65 +nsSSSHostEntry::nsSSSHostEntry(const nsSSSHostEntry& toCopy)
    1.66 +  : mHost(toCopy.mHost)
    1.67 +  , mExpireTime(toCopy.mExpireTime)
    1.68 +  , mStsPermission(toCopy.mStsPermission)
    1.69 +  , mExpired(toCopy.mExpired)
    1.70 +  , mIncludeSubdomains(toCopy.mIncludeSubdomains)
    1.71 +{
    1.72 +}
    1.73 +
    1.74 +////////////////////////////////////////////////////////////////////////////////
    1.75 +
    1.76 +
    1.77 +nsSiteSecurityService::nsSiteSecurityService()
    1.78 +  : mUsePreloadList(true)
    1.79 +{
    1.80 +}
    1.81 +
    1.82 +nsSiteSecurityService::~nsSiteSecurityService()
    1.83 +{
    1.84 +}
    1.85 +
    1.86 +NS_IMPL_ISUPPORTS(nsSiteSecurityService,
    1.87 +                  nsIObserver,
    1.88 +                  nsISiteSecurityService)
    1.89 +
    1.90 +nsresult
    1.91 +nsSiteSecurityService::Init()
    1.92 +{
    1.93 +   nsresult rv;
    1.94 +
    1.95 +   mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    1.96 +   NS_ENSURE_SUCCESS(rv, rv);
    1.97 +
    1.98 +   mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
    1.99 +   mozilla::Preferences::AddStrongObserver(this, "network.stricttransportsecurity.preloadlist");
   1.100 +   mObserverService = mozilla::services::GetObserverService();
   1.101 +   if (mObserverService)
   1.102 +     mObserverService->AddObserver(this, "last-pb-context-exited", false);
   1.103 +
   1.104 +   return NS_OK;
   1.105 +}
   1.106 +
   1.107 +nsresult
   1.108 +nsSiteSecurityService::GetHost(nsIURI *aURI, nsACString &aResult)
   1.109 +{
   1.110 +  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
   1.111 +  if (!innerURI) return NS_ERROR_FAILURE;
   1.112 +
   1.113 +  nsresult rv = innerURI->GetAsciiHost(aResult);
   1.114 +
   1.115 +  if (NS_FAILED(rv) || aResult.IsEmpty())
   1.116 +    return NS_ERROR_UNEXPECTED;
   1.117 +
   1.118 +  return NS_OK;
   1.119 +}
   1.120 +
   1.121 +nsresult
   1.122 +nsSiteSecurityService::GetPrincipalForURI(nsIURI* aURI,
   1.123 +                                          nsIPrincipal** aPrincipal)
   1.124 +{
   1.125 +  nsresult rv;
   1.126 +  nsCOMPtr<nsIScriptSecurityManager> securityManager =
   1.127 +     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   1.128 +  NS_ENSURE_SUCCESS(rv, rv);
   1.129 +
   1.130 +  // We have to normalize the scheme of the URIs we're using, so just use https.
   1.131 +  // HSTS information is shared across all ports for a given host.
   1.132 +  nsAutoCString host;
   1.133 +  rv = GetHost(aURI, host);
   1.134 +  NS_ENSURE_SUCCESS(rv, rv);
   1.135 +  nsCOMPtr<nsIURI> uri;
   1.136 +  rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + host);
   1.137 +  NS_ENSURE_SUCCESS(rv, rv);
   1.138 +
   1.139 +  // We want all apps to share HSTS state, so this is one of the few places
   1.140 +  // where we do not silo persistent state by extended origin.
   1.141 +  return securityManager->GetNoAppCodebasePrincipal(uri, aPrincipal);
   1.142 +}
   1.143 +
   1.144 +nsresult
   1.145 +nsSiteSecurityService::SetState(uint32_t aType,
   1.146 +                                nsIURI* aSourceURI,
   1.147 +                                int64_t maxage,
   1.148 +                                bool includeSubdomains,
   1.149 +                                uint32_t flags)
   1.150 +{
   1.151 +  // If max-age is zero, that's an indication to immediately remove the
   1.152 +  // permissions, so here's a shortcut.
   1.153 +  if (!maxage) {
   1.154 +    return RemoveState(aType, aSourceURI, flags);
   1.155 +  }
   1.156 +
   1.157 +  // Expire time is millis from now.  Since STS max-age is in seconds, and
   1.158 +  // PR_Now() is in micros, must equalize the units at milliseconds.
   1.159 +  int64_t expiretime = (PR_Now() / PR_USEC_PER_MSEC) +
   1.160 +                       (maxage * PR_MSEC_PER_SEC);
   1.161 +
   1.162 +  bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
   1.163 +
   1.164 +  // record entry for this host with max-age in the permissions manager
   1.165 +  SSSLOG(("SSS: maxage permission SET, adding permission\n"));
   1.166 +  nsresult rv = AddPermission(aSourceURI,
   1.167 +                              STS_PERMISSION,
   1.168 +                              (uint32_t) STS_SET,
   1.169 +                              (uint32_t) nsIPermissionManager::EXPIRE_TIME,
   1.170 +                              expiretime,
   1.171 +                              isPrivate);
   1.172 +  NS_ENSURE_SUCCESS(rv, rv);
   1.173 +
   1.174 +  if (includeSubdomains) {
   1.175 +    // record entry for this host with include subdomains in the permissions manager
   1.176 +    SSSLOG(("SSS: subdomains permission SET, adding permission\n"));
   1.177 +    rv = AddPermission(aSourceURI,
   1.178 +                       STS_SUBDOMAIN_PERMISSION,
   1.179 +                       (uint32_t) STS_SET,
   1.180 +                       (uint32_t) nsIPermissionManager::EXPIRE_TIME,
   1.181 +                       expiretime,
   1.182 +                       isPrivate);
   1.183 +    NS_ENSURE_SUCCESS(rv, rv);
   1.184 +  } else { // !includeSubdomains
   1.185 +    nsAutoCString hostname;
   1.186 +    rv = GetHost(aSourceURI, hostname);
   1.187 +    NS_ENSURE_SUCCESS(rv, rv);
   1.188 +
   1.189 +    SSSLOG(("SSS: subdomains permission UNSET, removing any existing ones\n"));
   1.190 +    rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
   1.191 +    NS_ENSURE_SUCCESS(rv, rv);
   1.192 +  }
   1.193 +  return NS_OK;
   1.194 +}
   1.195 +
   1.196 +NS_IMETHODIMP
   1.197 +nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags)
   1.198 +{
   1.199 +  // Should be called on the main thread (or via proxy) since the permission
   1.200 +  // manager is used and it's not threadsafe.
   1.201 +  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   1.202 +  // Only HSTS is supported at the moment.
   1.203 +  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   1.204 +                 NS_ERROR_NOT_IMPLEMENTED);
   1.205 +
   1.206 +  nsAutoCString hostname;
   1.207 +  nsresult rv = GetHost(aURI, hostname);
   1.208 +  NS_ENSURE_SUCCESS(rv, rv);
   1.209 +
   1.210 +  bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
   1.211 +
   1.212 +  rv = RemovePermission(hostname, STS_PERMISSION, isPrivate);
   1.213 +  NS_ENSURE_SUCCESS(rv, rv);
   1.214 +  SSSLOG(("SSS: deleted maxage permission\n"));
   1.215 +
   1.216 +  rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
   1.217 +  NS_ENSURE_SUCCESS(rv, rv);
   1.218 +  SSSLOG(("SSS: deleted subdomains permission\n"));
   1.219 +
   1.220 +  return NS_OK;
   1.221 +}
   1.222 +
   1.223 +static bool
   1.224 +HostIsIPAddress(const char *hostname)
   1.225 +{
   1.226 +  PRNetAddr hostAddr;
   1.227 +  return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
   1.228 +}
   1.229 +
   1.230 +NS_IMETHODIMP
   1.231 +nsSiteSecurityService::ProcessHeader(uint32_t aType,
   1.232 +                                     nsIURI* aSourceURI,
   1.233 +                                     const char* aHeader,
   1.234 +                                     uint32_t aFlags,
   1.235 +                                     uint64_t *aMaxAge,
   1.236 +                                     bool *aIncludeSubdomains)
   1.237 +{
   1.238 +  // Should be called on the main thread (or via proxy) since the permission
   1.239 +  // manager is used and it's not threadsafe.
   1.240 +  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   1.241 +  // Only HSTS is supported at the moment.
   1.242 +  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   1.243 +                 NS_ERROR_NOT_IMPLEMENTED);
   1.244 +
   1.245 +  if (aMaxAge != nullptr) {
   1.246 +    *aMaxAge = 0;
   1.247 +  }
   1.248 +
   1.249 +  if (aIncludeSubdomains != nullptr) {
   1.250 +    *aIncludeSubdomains = false;
   1.251 +  }
   1.252 +
   1.253 +  nsAutoCString host;
   1.254 +  nsresult rv = GetHost(aSourceURI, host);
   1.255 +  NS_ENSURE_SUCCESS(rv, rv);
   1.256 +  if (HostIsIPAddress(host.get())) {
   1.257 +    /* Don't process headers if a site is accessed by IP address. */
   1.258 +    return NS_OK;
   1.259 +  }
   1.260 +
   1.261 +  char * header = NS_strdup(aHeader);
   1.262 +  if (!header) return NS_ERROR_OUT_OF_MEMORY;
   1.263 +  rv = ProcessHeaderMutating(aType, aSourceURI, header, aFlags,
   1.264 +                             aMaxAge, aIncludeSubdomains);
   1.265 +  NS_Free(header);
   1.266 +  return rv;
   1.267 +}
   1.268 +
   1.269 +nsresult
   1.270 +nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType,
   1.271 +                                             nsIURI* aSourceURI,
   1.272 +                                             char* aHeader,
   1.273 +                                             uint32_t aFlags,
   1.274 +                                             uint64_t *aMaxAge,
   1.275 +                                             bool *aIncludeSubdomains)
   1.276 +{
   1.277 +  SSSLOG(("SSS: processing header '%s'", aHeader));
   1.278 +
   1.279 +  // "Strict-Transport-Security" ":" OWS
   1.280 +  //      STS-d  *( OWS ";" OWS STS-d  OWS)
   1.281 +  //
   1.282 +  //  ; STS directive
   1.283 +  //  STS-d      = maxAge / includeSubDomains
   1.284 +  //
   1.285 +  //  maxAge     = "max-age" "=" delta-seconds v-ext
   1.286 +  //
   1.287 +  //  includeSubDomains = [ "includeSubDomains" ]
   1.288 +  //
   1.289 +  //  The order of the directives is not significant.
   1.290 +  //  All directives must appear only once.
   1.291 +  //  Directive names are case-insensitive.
   1.292 +  //  The entire header is invalid if a directive not conforming to the
   1.293 +  //  syntax is encountered.
   1.294 +  //  Unrecognized directives (that are otherwise syntactically valid) are
   1.295 +  //  ignored, and the rest of the header is parsed as normal.
   1.296 +
   1.297 +  bool foundMaxAge = false;
   1.298 +  bool foundIncludeSubdomains = false;
   1.299 +  bool foundUnrecognizedDirective = false;
   1.300 +  int64_t maxAge = 0;
   1.301 +
   1.302 +  NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
   1.303 +  NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
   1.304 +
   1.305 +
   1.306 +  nsSecurityHeaderParser parser(aHeader);
   1.307 +  nsresult rv = parser.Parse();
   1.308 +  if (NS_FAILED(rv)) {
   1.309 +    SSSLOG(("SSS: could not parse header"));
   1.310 +    return rv;
   1.311 +  }
   1.312 +  mozilla::LinkedList<nsSecurityHeaderDirective> *directives = parser.GetDirectives();
   1.313 +
   1.314 +  for (nsSecurityHeaderDirective *directive = directives->getFirst();
   1.315 +       directive != nullptr; directive = directive->getNext()) {
   1.316 +    if (directive->mName.Length() == max_age_var.Length() &&
   1.317 +        directive->mName.EqualsIgnoreCase(max_age_var.get(),
   1.318 +                                          max_age_var.Length())) {
   1.319 +      if (foundMaxAge) {
   1.320 +        SSSLOG(("SSS: found two max-age directives"));
   1.321 +        return NS_ERROR_FAILURE;
   1.322 +      }
   1.323 +
   1.324 +      SSSLOG(("SSS: found max-age directive"));
   1.325 +      foundMaxAge = true;
   1.326 +
   1.327 +      size_t len = directive->mValue.Length();
   1.328 +      for (size_t i = 0; i < len; i++) {
   1.329 +        char chr = directive->mValue.CharAt(i);
   1.330 +        if (chr < '0' || chr > '9') {
   1.331 +          SSSLOG(("SSS: invalid value for max-age directive"));
   1.332 +          return NS_ERROR_FAILURE;
   1.333 +        }
   1.334 +      }
   1.335 +
   1.336 +      if (PR_sscanf(directive->mValue.get(), "%lld", &maxAge) != 1) {
   1.337 +        SSSLOG(("SSS: could not parse delta-seconds"));
   1.338 +        return NS_ERROR_FAILURE;
   1.339 +      }
   1.340 +
   1.341 +      SSSLOG(("SSS: parsed delta-seconds: %lld", maxAge));
   1.342 +    } else if (directive->mName.Length() == include_subd_var.Length() &&
   1.343 +               directive->mName.EqualsIgnoreCase(include_subd_var.get(),
   1.344 +                                                 include_subd_var.Length())) {
   1.345 +      if (foundIncludeSubdomains) {
   1.346 +        SSSLOG(("SSS: found two includeSubdomains directives"));
   1.347 +        return NS_ERROR_FAILURE;
   1.348 +      }
   1.349 +
   1.350 +      SSSLOG(("SSS: found includeSubdomains directive"));
   1.351 +      foundIncludeSubdomains = true;
   1.352 +
   1.353 +      if (directive->mValue.Length() != 0) {
   1.354 +        SSSLOG(("SSS: includeSubdomains directive unexpectedly had value '%s'", directive->mValue.get()));
   1.355 +        return NS_ERROR_FAILURE;
   1.356 +      }
   1.357 +    } else {
   1.358 +      SSSLOG(("SSS: ignoring unrecognized directive '%s'", directive->mName.get()));
   1.359 +      foundUnrecognizedDirective = true;
   1.360 +    }
   1.361 +  }
   1.362 +
   1.363 +  // after processing all the directives, make sure we came across max-age
   1.364 +  // somewhere.
   1.365 +  if (!foundMaxAge) {
   1.366 +    SSSLOG(("SSS: did not encounter required max-age directive"));
   1.367 +    return NS_ERROR_FAILURE;
   1.368 +  }
   1.369 +
   1.370 +  // record the successfully parsed header data.
   1.371 +  SetState(aType, aSourceURI, maxAge, foundIncludeSubdomains, aFlags);
   1.372 +
   1.373 +  if (aMaxAge != nullptr) {
   1.374 +    *aMaxAge = (uint64_t)maxAge;
   1.375 +  }
   1.376 +
   1.377 +  if (aIncludeSubdomains != nullptr) {
   1.378 +    *aIncludeSubdomains = foundIncludeSubdomains;
   1.379 +  }
   1.380 +
   1.381 +  return foundUnrecognizedDirective ?
   1.382 +         NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA :
   1.383 +         NS_OK;
   1.384 +}
   1.385 +
   1.386 +NS_IMETHODIMP
   1.387 +nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
   1.388 +                                    uint32_t aFlags, bool* aResult)
   1.389 +{
   1.390 +  // Should be called on the main thread (or via proxy) since the permission
   1.391 +  // manager is used and it's not threadsafe.
   1.392 +  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   1.393 +  // Only HSTS is supported at the moment.
   1.394 +  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   1.395 +                 NS_ERROR_NOT_IMPLEMENTED);
   1.396 +
   1.397 +  /* An IP address never qualifies as a secure URI. */
   1.398 +  if (HostIsIPAddress(aHost)) {
   1.399 +    *aResult = false;
   1.400 +    return NS_OK;
   1.401 +  }
   1.402 +
   1.403 +  nsCOMPtr<nsIURI> uri;
   1.404 +  nsDependentCString hostString(aHost);
   1.405 +  nsresult rv = NS_NewURI(getter_AddRefs(uri),
   1.406 +                          NS_LITERAL_CSTRING("https://") + hostString);
   1.407 +  NS_ENSURE_SUCCESS(rv, rv);
   1.408 +  return IsSecureURI(aType, uri, aFlags, aResult);
   1.409 +}
   1.410 +
   1.411 +int STSPreloadCompare(const void *key, const void *entry)
   1.412 +{
   1.413 +  const char *keyStr = (const char *)key;
   1.414 +  const nsSTSPreload *preloadEntry = (const nsSTSPreload *)entry;
   1.415 +  return strcmp(keyStr, preloadEntry->mHost);
   1.416 +}
   1.417 +
   1.418 +// Returns the preload list entry for the given host, if it exists.
   1.419 +// Only does exact host matching - the user must decide how to use the returned
   1.420 +// data. May return null.
   1.421 +const nsSTSPreload *
   1.422 +nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
   1.423 +{
   1.424 +  PRTime currentTime = PR_Now();
   1.425 +  int32_t timeOffset = 0;
   1.426 +  nsresult rv = mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds",
   1.427 +                                             &timeOffset);
   1.428 +  if (NS_SUCCEEDED(rv)) {
   1.429 +    currentTime += (PRTime(timeOffset) * PR_USEC_PER_SEC);
   1.430 +  }
   1.431 +
   1.432 +  if (mUsePreloadList && currentTime < gPreloadListExpirationTime) {
   1.433 +    return (const nsSTSPreload *) bsearch(aHost,
   1.434 +                                          kSTSPreloadList,
   1.435 +                                          mozilla::ArrayLength(kSTSPreloadList),
   1.436 +                                          sizeof(nsSTSPreload),
   1.437 +                                          STSPreloadCompare);
   1.438 +  }
   1.439 +
   1.440 +  return nullptr;
   1.441 +}
   1.442 +
   1.443 +NS_IMETHODIMP
   1.444 +nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
   1.445 +                                   uint32_t aFlags, bool* aResult)
   1.446 +{
   1.447 +  // Should be called on the main thread (or via proxy) since the permission
   1.448 +  // manager is used and it's not threadsafe.
   1.449 +  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   1.450 +  // Only HSTS is supported at the moment.
   1.451 +  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
   1.452 +                 NS_ERROR_NOT_IMPLEMENTED);
   1.453 +
   1.454 +  // set default in case if we can't find any STS information
   1.455 +  *aResult = false;
   1.456 +
   1.457 +  nsAutoCString host;
   1.458 +  nsresult rv = GetHost(aURI, host);
   1.459 +  NS_ENSURE_SUCCESS(rv, rv);
   1.460 +
   1.461 +  /* An IP address never qualifies as a secure URI. */
   1.462 +  if (HostIsIPAddress(host.BeginReading())) {
   1.463 +    return NS_OK;
   1.464 +  }
   1.465 +
   1.466 +  // Holepunch chart.apis.google.com and subdomains.
   1.467 +  if (host.Equals(NS_LITERAL_CSTRING("chart.apis.google.com")) ||
   1.468 +      StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
   1.469 +    return NS_OK;
   1.470 +  }
   1.471 +
   1.472 +  const nsSTSPreload *preload = nullptr;
   1.473 +  nsSSSHostEntry *pbEntry = nullptr;
   1.474 +
   1.475 +  bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
   1.476 +  if (isPrivate) {
   1.477 +    pbEntry = mPrivateModeHostTable.GetEntry(host.get());
   1.478 +  }
   1.479 +
   1.480 +  nsCOMPtr<nsIPrincipal> principal;
   1.481 +  rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
   1.482 +  NS_ENSURE_SUCCESS(rv, rv);
   1.483 +
   1.484 +  uint32_t permMgrPermission;
   1.485 +  rv = mPermMgr->TestExactPermissionFromPrincipal(principal, STS_PERMISSION,
   1.486 +                                                  &permMgrPermission);
   1.487 +  NS_ENSURE_SUCCESS(rv, rv);
   1.488 +
   1.489 +  // First check the exact host. This involves first checking for an entry in
   1.490 +  // the private browsing table. If that entry exists, we don't want to check
   1.491 +  // in either the permission manager or the preload list. We only want to use
   1.492 +  // the stored permission if it is not a knockout entry, however.
   1.493 +  // Additionally, if it is a knockout entry, we want to stop looking for data
   1.494 +  // on the host, because the knockout entry indicates "we have no information
   1.495 +  // regarding the sts status of this host".
   1.496 +  if (pbEntry && pbEntry->mStsPermission != STS_UNSET) {
   1.497 +    SSSLOG(("Found private browsing table entry for %s", host.get()));
   1.498 +    if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) {
   1.499 +      *aResult = true;
   1.500 +      return NS_OK;
   1.501 +    }
   1.502 +  }
   1.503 +  // Next we look in the permission manager. Same story here regarding
   1.504 +  // knockout entries.
   1.505 +  else if (permMgrPermission != STS_UNSET) {
   1.506 +    SSSLOG(("Found permission manager entry for %s", host.get()));
   1.507 +    if (permMgrPermission == STS_SET) {
   1.508 +      *aResult = true;
   1.509 +      return NS_OK;
   1.510 +    }
   1.511 +  }
   1.512 +  // Finally look in the preloaded list. This is the exact host,
   1.513 +  // so if an entry exists at all, this host is sts.
   1.514 +  else if (GetPreloadListEntry(host.get())) {
   1.515 +    SSSLOG(("%s is a preloaded STS host", host.get()));
   1.516 +    *aResult = true;
   1.517 +    return NS_OK;
   1.518 +  }
   1.519 +
   1.520 +  // Used for testing permissions as we walk up the domain tree.
   1.521 +  nsCOMPtr<nsIURI> domainWalkURI;
   1.522 +  nsCOMPtr<nsIPrincipal> domainWalkPrincipal;
   1.523 +  const char *subdomain;
   1.524 +
   1.525 +  SSSLOG(("no HSTS data for %s found, walking up domain", host.get()));
   1.526 +  uint32_t offset = 0;
   1.527 +  for (offset = host.FindChar('.', offset) + 1;
   1.528 +       offset > 0;
   1.529 +       offset = host.FindChar('.', offset) + 1) {
   1.530 +
   1.531 +    subdomain = host.get() + offset;
   1.532 +
   1.533 +    // If we get an empty string, don't continue.
   1.534 +    if (strlen(subdomain) < 1) {
   1.535 +      break;
   1.536 +    }
   1.537 +
   1.538 +    if (isPrivate) {
   1.539 +      pbEntry = mPrivateModeHostTable.GetEntry(subdomain);
   1.540 +    }
   1.541 +
   1.542 +    // normalize all URIs with https://
   1.543 +    rv = NS_NewURI(getter_AddRefs(domainWalkURI),
   1.544 +                   NS_LITERAL_CSTRING("https://") + Substring(host, offset));
   1.545 +    NS_ENSURE_SUCCESS(rv, rv);
   1.546 +
   1.547 +    rv = GetPrincipalForURI(domainWalkURI, getter_AddRefs(domainWalkPrincipal));
   1.548 +    NS_ENSURE_SUCCESS(rv, rv);
   1.549 +
   1.550 +    rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal,
   1.551 +                                                    STS_PERMISSION,
   1.552 +                                                    &permMgrPermission);
   1.553 +    NS_ENSURE_SUCCESS(rv, rv);
   1.554 +
   1.555 +    // Do the same thing as with the exact host, except now we're looking at
   1.556 +    // ancestor domains of the original host. So, we have to look at the
   1.557 +    // include subdomains permissions (although we still have to check for the
   1.558 +    // STS_PERMISSION first to check that this is an sts host and not a
   1.559 +    // knockout entry - and again, if it is a knockout entry, we stop looking
   1.560 +    // for data on it and skip to the next higher up ancestor domain).
   1.561 +    if (pbEntry && pbEntry->mStsPermission != STS_UNSET) {
   1.562 +      SSSLOG(("Found private browsing table entry for %s", subdomain));
   1.563 +      if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) {
   1.564 +        *aResult = pbEntry->mIncludeSubdomains;
   1.565 +        break;
   1.566 +      }
   1.567 +    }
   1.568 +    else if (permMgrPermission != STS_UNSET) {
   1.569 +      SSSLOG(("Found permission manager entry for %s", subdomain));
   1.570 +      if (permMgrPermission == STS_SET) {
   1.571 +        uint32_t subdomainPermission;
   1.572 +        rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal,
   1.573 +                                                        STS_SUBDOMAIN_PERMISSION,
   1.574 +                                                        &subdomainPermission);
   1.575 +        NS_ENSURE_SUCCESS(rv, rv);
   1.576 +        *aResult = (subdomainPermission == STS_SET);
   1.577 +        break;
   1.578 +      }
   1.579 +    }
   1.580 +    // This is an ancestor, so if we get a match, we have to check if the
   1.581 +    // preloaded entry includes subdomains.
   1.582 +    else if ((preload = GetPreloadListEntry(subdomain)) != nullptr) {
   1.583 +      if (preload->mIncludeSubdomains) {
   1.584 +        SSSLOG(("%s is a preloaded STS host", subdomain));
   1.585 +        *aResult = true;
   1.586 +        break;
   1.587 +      }
   1.588 +    }
   1.589 +
   1.590 +    SSSLOG(("no HSTS data for %s found, walking up domain", subdomain));
   1.591 +  }
   1.592 +
   1.593 +  // Use whatever we ended up with, which defaults to false.
   1.594 +  return NS_OK;
   1.595 +}
   1.596 +
   1.597 +
   1.598 +// Verify the trustworthiness of the security info (are there any cert errors?)
   1.599 +NS_IMETHODIMP
   1.600 +nsSiteSecurityService::ShouldIgnoreHeaders(nsISupports* aSecurityInfo,
   1.601 +                                           bool* aResult)
   1.602 +{
   1.603 +  nsresult rv;
   1.604 +  bool tlsIsBroken = false;
   1.605 +  nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(aSecurityInfo);
   1.606 +  NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
   1.607 +
   1.608 +  nsCOMPtr<nsISSLStatus> sslstat;
   1.609 +  rv = sslprov->GetSSLStatus(getter_AddRefs(sslstat));
   1.610 +  NS_ENSURE_SUCCESS(rv, rv);
   1.611 +  NS_ENSURE_TRUE(sslstat, NS_ERROR_FAILURE);
   1.612 +
   1.613 +  bool trustcheck;
   1.614 +  rv = sslstat->GetIsDomainMismatch(&trustcheck);
   1.615 +  NS_ENSURE_SUCCESS(rv, rv);
   1.616 +  tlsIsBroken = tlsIsBroken || trustcheck;
   1.617 +
   1.618 +  rv = sslstat->GetIsNotValidAtThisTime(&trustcheck);
   1.619 +  NS_ENSURE_SUCCESS(rv, rv);
   1.620 +  tlsIsBroken = tlsIsBroken || trustcheck;
   1.621 +
   1.622 +  rv = sslstat->GetIsUntrusted(&trustcheck);
   1.623 +  NS_ENSURE_SUCCESS(rv, rv);
   1.624 +  tlsIsBroken = tlsIsBroken || trustcheck;
   1.625 +
   1.626 +  *aResult = tlsIsBroken;
   1.627 +  return NS_OK;
   1.628 +}
   1.629 +
   1.630 +//------------------------------------------------------------
   1.631 +// nsSiteSecurityService::nsIObserver
   1.632 +//------------------------------------------------------------
   1.633 +
   1.634 +NS_IMETHODIMP
   1.635 +nsSiteSecurityService::Observe(nsISupports *subject,
   1.636 +                               const char *topic,
   1.637 +                               const char16_t *data)
   1.638 +{
   1.639 +  if (strcmp(topic, "last-pb-context-exited") == 0) {
   1.640 +    mPrivateModeHostTable.Clear();
   1.641 +  }
   1.642 +  else if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
   1.643 +    mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
   1.644 +  }
   1.645 +
   1.646 +  return NS_OK;
   1.647 +}
   1.648 +
   1.649 +//------------------------------------------------------------
   1.650 +// Functions to overlay the permission manager calls in case
   1.651 +// we're in private browsing mode.
   1.652 +//------------------------------------------------------------
   1.653 +nsresult
   1.654 +nsSiteSecurityService::AddPermission(nsIURI     *aURI,
   1.655 +                                     const char *aType,
   1.656 +                                     uint32_t   aPermission,
   1.657 +                                     uint32_t   aExpireType,
   1.658 +                                     int64_t    aExpireTime,
   1.659 +                                     bool       aIsPrivate)
   1.660 +{
   1.661 +    // Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let
   1.662 +    // those be stored persistently.
   1.663 +    if (!aIsPrivate || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
   1.664 +      // Not in private mode, or manually-set permission
   1.665 +      nsCOMPtr<nsIPrincipal> principal;
   1.666 +      nsresult rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
   1.667 +      NS_ENSURE_SUCCESS(rv, rv);
   1.668 +
   1.669 +      return mPermMgr->AddFromPrincipal(principal, aType, aPermission,
   1.670 +                                        aExpireType, aExpireTime);
   1.671 +    }
   1.672 +
   1.673 +    nsAutoCString host;
   1.674 +    nsresult rv = GetHost(aURI, host);
   1.675 +    NS_ENSURE_SUCCESS(rv, rv);
   1.676 +    SSSLOG(("AddPermission for entry for %s", host.get()));
   1.677 +
   1.678 +    // Update in mPrivateModeHostTable only, so any changes will be rolled
   1.679 +    // back when exiting private mode.
   1.680 +
   1.681 +    // Note: EXPIRE_NEVER permissions should trump anything that shows up in
   1.682 +    // the HTTP header, so if there's an EXPIRE_NEVER permission already
   1.683 +    // don't store anything new.
   1.684 +    // Currently there's no way to get the type of expiry out of the
   1.685 +    // permission manager, but that's okay since there's nothing that stores
   1.686 +    // EXPIRE_NEVER permissions.
   1.687 +
   1.688 +    // PutEntry returns an existing entry if there already is one, or it
   1.689 +    // creates a new one if there isn't.
   1.690 +    nsSSSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get());
   1.691 +    if (!entry) {
   1.692 +      return NS_ERROR_OUT_OF_MEMORY;
   1.693 +    }
   1.694 +    SSSLOG(("Created private mode entry for %s", host.get()));
   1.695 +
   1.696 +    // AddPermission() will be called twice if the STS header encountered has
   1.697 +    // includeSubdomains (first for the main permission and second for the
   1.698 +    // subdomains permission). If AddPermission() gets called a second time
   1.699 +    // with the STS_SUBDOMAIN_PERMISSION, we just have to flip that bit in
   1.700 +    // the nsSSSHostEntry.
   1.701 +    if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
   1.702 +      entry->mIncludeSubdomains = true;
   1.703 +    }
   1.704 +    else if (strcmp(aType, STS_PERMISSION) == 0) {
   1.705 +      entry->mStsPermission = aPermission;
   1.706 +    }
   1.707 +
   1.708 +    // Also refresh the expiration time.
   1.709 +    entry->SetExpireTime(aExpireTime);
   1.710 +    return NS_OK;
   1.711 +}
   1.712 +
   1.713 +nsresult
   1.714 +nsSiteSecurityService::RemovePermission(const nsCString  &aHost,
   1.715 +                                        const char       *aType,
   1.716 +                                        bool aIsPrivate)
   1.717 +{
   1.718 +    // Build up a principal for use with the permission manager.
   1.719 +    // normalize all URIs with https://
   1.720 +    nsCOMPtr<nsIURI> uri;
   1.721 +    nsresult rv = NS_NewURI(getter_AddRefs(uri),
   1.722 +                            NS_LITERAL_CSTRING("https://") + aHost);
   1.723 +    NS_ENSURE_SUCCESS(rv, rv);
   1.724 +
   1.725 +    nsCOMPtr<nsIPrincipal> principal;
   1.726 +    rv = GetPrincipalForURI(uri, getter_AddRefs(principal));
   1.727 +    NS_ENSURE_SUCCESS(rv, rv);
   1.728 +
   1.729 +    if (!aIsPrivate) {
   1.730 +      // Not in private mode: remove permissions persistently.
   1.731 +      // This means setting the permission to STS_KNOCKOUT in case
   1.732 +      // this host is on the preload list (so we can override it).
   1.733 +      return mPermMgr->AddFromPrincipal(principal, aType,
   1.734 +                                        STS_KNOCKOUT,
   1.735 +                                        nsIPermissionManager::EXPIRE_NEVER, 0);
   1.736 +    }
   1.737 +
   1.738 +    // Make changes in mPrivateModeHostTable only, so any changes will be
   1.739 +    // rolled back when exiting private mode.
   1.740 +    nsSSSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get());
   1.741 +
   1.742 +    if (!entry) {
   1.743 +      entry = mPrivateModeHostTable.PutEntry(aHost.get());
   1.744 +      if (!entry) {
   1.745 +        return NS_ERROR_OUT_OF_MEMORY;
   1.746 +      }
   1.747 +      SSSLOG(("Created private mode deleted mask for %s", aHost.get()));
   1.748 +    }
   1.749 +
   1.750 +    if (strcmp(aType, STS_PERMISSION) == 0) {
   1.751 +      entry->mStsPermission = STS_KNOCKOUT;
   1.752 +    }
   1.753 +    else if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
   1.754 +      entry->mIncludeSubdomains = false;
   1.755 +    }
   1.756 +
   1.757 +    return NS_OK;
   1.758 +}

mercurial