2010-06-03 21:03:17 +00:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Strict-Transport-Security.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Foundation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Sid Stamm <sid@mozilla.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "plstr.h"
|
|
|
|
#include "prlog.h"
|
|
|
|
#include "prprf.h"
|
|
|
|
#include "nsCRTGlue.h"
|
|
|
|
#include "nsIPermissionManager.h"
|
2010-10-06 17:07:39 +00:00
|
|
|
#include "nsIPrivateBrowsingService.h"
|
2010-06-03 21:03:17 +00:00
|
|
|
#include "nsISSLStatus.h"
|
|
|
|
#include "nsISSLStatusProvider.h"
|
|
|
|
#include "nsStrictTransportSecurityService.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsStringGlue.h"
|
|
|
|
|
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
PRLogModuleInfo *gSTSLog = PR_NewLogModule("nsSTSService");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define STSLOG(args) PR_LOG(gSTSLog, 4, args)
|
|
|
|
|
|
|
|
#define STS_PARSER_FAIL_IF(test,args) \
|
|
|
|
if (test) { \
|
|
|
|
STSLOG(args); \
|
|
|
|
return NS_ERROR_FAILURE; \
|
|
|
|
}
|
|
|
|
|
2010-10-06 17:07:39 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
nsSTSHostEntry::nsSTSHostEntry(const char* aHost)
|
|
|
|
: mHost(aHost)
|
|
|
|
, mExpireTime(0)
|
|
|
|
, mDeleted(PR_FALSE)
|
|
|
|
, mIncludeSubdomains(PR_FALSE)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsSTSHostEntry::nsSTSHostEntry(const nsSTSHostEntry& toCopy)
|
|
|
|
: mHost(toCopy.mHost)
|
|
|
|
, mExpireTime(toCopy.mExpireTime)
|
|
|
|
, mDeleted(toCopy.mDeleted)
|
|
|
|
, mIncludeSubdomains(toCopy.mIncludeSubdomains)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2010-06-03 21:03:17 +00:00
|
|
|
nsStrictTransportSecurityService::nsStrictTransportSecurityService()
|
2010-10-06 17:07:39 +00:00
|
|
|
: mInPrivateMode(PR_FALSE)
|
2010-06-03 21:03:17 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsStrictTransportSecurityService::~nsStrictTransportSecurityService()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-10-06 17:07:39 +00:00
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS2(nsStrictTransportSecurityService,
|
|
|
|
nsIObserver,
|
2010-06-03 21:03:17 +00:00
|
|
|
nsIStrictTransportSecurityService)
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::Init()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2010-10-06 17:07:39 +00:00
|
|
|
// figure out if we're starting in private browsing mode
|
|
|
|
nsCOMPtr<nsIPrivateBrowsingService> pbs =
|
|
|
|
do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
|
|
|
|
if (pbs)
|
|
|
|
pbs->GetPrivateBrowsingEnabled(&mInPrivateMode);
|
|
|
|
|
|
|
|
mObserverService = mozilla::services::GetObserverService();
|
|
|
|
if (mObserverService)
|
|
|
|
mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_FALSE);
|
|
|
|
|
|
|
|
if (mInPrivateMode && !mPrivateModeHostTable.Init())
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2010-06-03 21:03:17 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::GetHost(nsIURI *aURI, nsACString &aResult)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
|
|
|
|
if (!innerURI) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsresult rv = innerURI->GetAsciiHost(aResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(rv) || aResult.IsEmpty())
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::SetStsState(nsIURI* aSourceURI,
|
|
|
|
PRInt64 maxage,
|
2011-09-29 06:19:26 +00:00
|
|
|
bool includeSubdomains)
|
2010-06-03 21:03:17 +00:00
|
|
|
{
|
|
|
|
// If max-age is zero, that's an indication to immediately remove the
|
|
|
|
// permissions, so here's a shortcut.
|
|
|
|
if (!maxage)
|
|
|
|
return RemoveStsState(aSourceURI);
|
|
|
|
|
|
|
|
// Expire time is millis from now. Since STS max-age is in seconds, and
|
|
|
|
// PR_Now() is in micros, must equalize the units at milliseconds.
|
|
|
|
PRInt64 expiretime = (PR_Now() / 1000) + (maxage * 1000);
|
|
|
|
|
|
|
|
// record entry for this host with max-age in the permissions manager
|
2010-10-06 17:07:39 +00:00
|
|
|
STSLOG(("STS: maxage permission SET, adding permission\n"));
|
|
|
|
nsresult rv = AddPermission(aSourceURI,
|
|
|
|
STS_PERMISSION,
|
2010-06-03 21:03:17 +00:00
|
|
|
(PRUint32) nsIPermissionManager::ALLOW_ACTION,
|
|
|
|
(PRUint32) nsIPermissionManager::EXPIRE_TIME,
|
|
|
|
expiretime);
|
2010-10-06 17:07:39 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2010-06-03 21:03:17 +00:00
|
|
|
|
|
|
|
if (includeSubdomains) {
|
|
|
|
// record entry for this host with include subdomains in the permissions manager
|
2010-10-06 17:07:39 +00:00
|
|
|
STSLOG(("STS: subdomains permission SET, adding permission\n"));
|
|
|
|
rv = AddPermission(aSourceURI,
|
|
|
|
STS_SUBDOMAIN_PERMISSION,
|
|
|
|
(PRUint32) nsIPermissionManager::ALLOW_ACTION,
|
|
|
|
(PRUint32) nsIPermissionManager::EXPIRE_TIME,
|
|
|
|
expiretime);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2010-06-03 21:03:17 +00:00
|
|
|
} else { // !includeSubdomains
|
|
|
|
nsCAutoString hostname;
|
2010-10-06 17:07:39 +00:00
|
|
|
rv = GetHost(aSourceURI, hostname);
|
2010-06-03 21:03:17 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2010-10-06 17:07:39 +00:00
|
|
|
STSLOG(("STS: subdomains permission UNSET, removing any existing ones\n"));
|
|
|
|
rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2010-06-03 21:03:17 +00:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsStrictTransportSecurityService::RemoveStsState(nsIURI* aURI)
|
|
|
|
{
|
|
|
|
// Should be called on the main thread (or via proxy) since the permission
|
|
|
|
// manager is used and it's not threadsafe.
|
|
|
|
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
|
|
|
|
|
|
|
|
nsCAutoString hostname;
|
|
|
|
nsresult rv = GetHost(aURI, hostname);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2010-10-06 17:07:39 +00:00
|
|
|
rv = RemovePermission(hostname, STS_PERMISSION);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2010-06-03 21:03:17 +00:00
|
|
|
STSLOG(("STS: deleted maxage permission\n"));
|
|
|
|
|
2010-10-06 17:07:39 +00:00
|
|
|
rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2010-06-03 21:03:17 +00:00
|
|
|
STSLOG(("STS: deleted subdomains permission\n"));
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsStrictTransportSecurityService::ProcessStsHeader(nsIURI* aSourceURI,
|
|
|
|
const char* aHeader)
|
|
|
|
{
|
|
|
|
// Should be called on the main thread (or via proxy) since the permission
|
|
|
|
// manager is used and it's not threadsafe.
|
|
|
|
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
|
|
|
|
|
|
|
|
char * header = NS_strdup(aHeader);
|
|
|
|
if (!header) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsresult rv = ProcessStsHeaderMutating(aSourceURI, header);
|
|
|
|
NS_Free(header);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::ProcessStsHeaderMutating(nsIURI* aSourceURI,
|
|
|
|
char* aHeader)
|
|
|
|
{
|
|
|
|
STSLOG(("STS: ProcessStrictTransportHeader(%s)\n", aHeader));
|
|
|
|
|
|
|
|
// "Strict-Transport-Security" ":" OWS
|
|
|
|
// STS-d *( OWS ";" OWS STS-d OWS)
|
|
|
|
//
|
|
|
|
// ; STS directive
|
|
|
|
// STS-d = maxAge / includeSubDomains
|
|
|
|
//
|
|
|
|
// maxAge = "max-age" "=" delta-seconds v-ext
|
|
|
|
//
|
|
|
|
// includeSubDomains = [ "includeSubDomains" ]
|
|
|
|
|
|
|
|
const char* directive;
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool foundMaxAge = false;
|
|
|
|
bool foundUnrecognizedTokens = false;
|
|
|
|
bool includeSubdomains = false;
|
2010-06-03 21:03:17 +00:00
|
|
|
PRInt64 maxAge = 0;
|
|
|
|
|
|
|
|
NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
|
|
|
|
NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
|
|
|
|
|
|
|
|
while ((directive = NS_strtok(";", &aHeader))) {
|
|
|
|
//skip leading whitespace
|
|
|
|
directive = NS_strspnp(" \t", directive);
|
|
|
|
STS_PARSER_FAIL_IF(!(*directive), ("error removing initial whitespace\n."));
|
|
|
|
|
|
|
|
if (!PL_strncasecmp(directive, max_age_var.get(), max_age_var.Length())) {
|
|
|
|
// skip directive name
|
|
|
|
directive += max_age_var.Length();
|
|
|
|
// skip leading whitespace
|
|
|
|
directive = NS_strspnp(" \t", directive);
|
|
|
|
STS_PARSER_FAIL_IF(*directive != '=',
|
|
|
|
("No equal sign found in max-age directive\n"));
|
|
|
|
|
|
|
|
// skip over the equal sign
|
|
|
|
STS_PARSER_FAIL_IF(*(++directive) == '\0',
|
|
|
|
("No delta-seconds present\n"));
|
|
|
|
|
|
|
|
// obtain the delta-seconds value
|
|
|
|
STS_PARSER_FAIL_IF(PR_sscanf(directive, "%lld", &maxAge) != 1,
|
|
|
|
("Could not convert delta-seconds\n"));
|
|
|
|
STSLOG(("STS: ProcessStrictTransportHeader() STS found maxage %lld\n", maxAge));
|
|
|
|
foundMaxAge = PR_TRUE;
|
|
|
|
|
|
|
|
// skip max-age value and trailing whitespace
|
|
|
|
directive = NS_strspnp("0123456789 \t", directive);
|
|
|
|
|
|
|
|
// log unknown tokens, but don't fail (for forwards compatibility)
|
|
|
|
if (*directive != '\0') {
|
|
|
|
foundUnrecognizedTokens = PR_TRUE;
|
|
|
|
STSLOG(("Extra stuff in max-age after delta-seconds: %s \n", directive));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!PL_strncasecmp(directive, include_subd_var.get(), include_subd_var.Length())) {
|
|
|
|
directive += include_subd_var.Length();
|
|
|
|
|
|
|
|
// only record "includesubdomains" if it is a token by itself... for
|
|
|
|
// example, don't set includeSubdomains = PR_TRUE if the directive is
|
|
|
|
// "includesubdomainsFooBar".
|
|
|
|
if (*directive == '\0' || *directive =='\t' || *directive == ' ') {
|
|
|
|
includeSubdomains = PR_TRUE;
|
|
|
|
STSLOG(("STS: ProcessStrictTransportHeader: obtained subdomains status\n"));
|
|
|
|
|
|
|
|
// skip trailing whitespace
|
|
|
|
directive = NS_strspnp(" \t", directive);
|
|
|
|
|
|
|
|
if (*directive != '\0') {
|
|
|
|
foundUnrecognizedTokens = PR_TRUE;
|
|
|
|
STSLOG(("Extra stuff after includesubdomains: %s\n", directive));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
foundUnrecognizedTokens = PR_TRUE;
|
|
|
|
STSLOG(("Unrecognized directive in header: %s\n", directive));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// log unknown directives, but don't fail (for backwards compatibility)
|
|
|
|
foundUnrecognizedTokens = PR_TRUE;
|
|
|
|
STSLOG(("Unrecognized directive in header: %s\n", directive));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// after processing all the directives, make sure we came across max-age
|
|
|
|
// somewhere.
|
|
|
|
STS_PARSER_FAIL_IF(!foundMaxAge,
|
|
|
|
("Parse ERROR: couldn't locate max-age token\n"));
|
|
|
|
|
|
|
|
// record the successfully parsed header data.
|
|
|
|
SetStsState(aSourceURI, maxAge, includeSubdomains);
|
|
|
|
|
|
|
|
return foundUnrecognizedTokens ?
|
|
|
|
NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA :
|
|
|
|
NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2011-09-29 06:19:26 +00:00
|
|
|
nsStrictTransportSecurityService::IsStsHost(const char* aHost, bool* aResult)
|
2010-06-03 21:03:17 +00:00
|
|
|
{
|
|
|
|
// Should be called on the main thread (or via proxy) since the permission
|
|
|
|
// manager is used and it's not threadsafe.
|
|
|
|
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
nsDependentCString hostString(aHost);
|
|
|
|
nsresult rv = NS_NewURI(getter_AddRefs(uri),
|
|
|
|
NS_LITERAL_CSTRING("https://") + hostString);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return IsStsURI(uri, aResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2011-09-29 06:19:26 +00:00
|
|
|
nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult)
|
2010-06-03 21:03:17 +00:00
|
|
|
{
|
|
|
|
// Should be called on the main thread (or via proxy) since the permission
|
|
|
|
// manager is used and it's not threadsafe.
|
|
|
|
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
PRUint32 permExact, permGeneral;
|
|
|
|
// If this domain has the forcehttps permission, this is an STS host.
|
2010-10-06 17:07:39 +00:00
|
|
|
rv = TestPermission(aURI, STS_PERMISSION, &permExact, PR_TRUE);
|
2010-06-03 21:03:17 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2010-10-06 17:07:39 +00:00
|
|
|
|
2010-06-03 21:03:17 +00:00
|
|
|
// If any super-domain has the includeSubdomains permission, this is an
|
|
|
|
// STS host.
|
2010-10-06 17:07:39 +00:00
|
|
|
rv = TestPermission(aURI, STS_SUBDOMAIN_PERMISSION, &permGeneral, PR_FALSE);
|
2010-06-03 21:03:17 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
*aResult = ((permExact == nsIPermissionManager::ALLOW_ACTION) ||
|
|
|
|
(permGeneral == nsIPermissionManager::ALLOW_ACTION));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verify the trustworthiness of the security info (are there any cert errors?)
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsStrictTransportSecurityService::ShouldIgnoreStsHeader(nsISupports* aSecurityInfo,
|
2011-09-29 06:19:26 +00:00
|
|
|
bool* aResult)
|
2010-06-03 21:03:17 +00:00
|
|
|
{
|
|
|
|
nsresult rv;
|
2011-09-29 06:19:26 +00:00
|
|
|
bool tlsIsBroken = false;
|
2010-06-03 21:03:17 +00:00
|
|
|
nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(aSecurityInfo);
|
|
|
|
NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> isupstat;
|
|
|
|
rv = sslprov->GetSSLStatus(getter_AddRefs(isupstat));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsISSLStatus> sslstat = do_QueryInterface(isupstat);
|
|
|
|
NS_ENSURE_TRUE(sslstat, NS_ERROR_FAILURE);
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool trustcheck;
|
2010-06-03 21:03:17 +00:00
|
|
|
rv = sslstat->GetIsDomainMismatch(&trustcheck);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
tlsIsBroken = tlsIsBroken || trustcheck;
|
|
|
|
|
|
|
|
rv = sslstat->GetIsNotValidAtThisTime(&trustcheck);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
tlsIsBroken = tlsIsBroken || trustcheck;
|
|
|
|
|
|
|
|
rv = sslstat->GetIsUntrusted(&trustcheck);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
tlsIsBroken = tlsIsBroken || trustcheck;
|
|
|
|
|
|
|
|
*aResult = tlsIsBroken;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2010-10-06 17:07:39 +00:00
|
|
|
|
|
|
|
//------------------------------------------------------------
|
|
|
|
// nsStrictTransportSecurityService::nsIObserver
|
|
|
|
//------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsStrictTransportSecurityService::Observe(nsISupports *subject,
|
|
|
|
const char *topic,
|
|
|
|
const PRUnichar *data)
|
|
|
|
{
|
|
|
|
if (strcmp(topic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
|
|
|
|
if(NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(data)) {
|
|
|
|
// Indication to start recording stuff locally and not writing changes
|
|
|
|
// out to the permission manager.
|
|
|
|
|
|
|
|
if (!mPrivateModeHostTable.IsInitialized()
|
|
|
|
&& !mPrivateModeHostTable.Init()) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
mInPrivateMode = PR_TRUE;
|
|
|
|
}
|
|
|
|
else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(data)) {
|
|
|
|
mPrivateModeHostTable.Clear();
|
|
|
|
mInPrivateMode = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------
|
|
|
|
// Functions to overlay the permission manager calls in case
|
|
|
|
// we're in private browsing mode.
|
|
|
|
//------------------------------------------------------------
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::AddPermission(nsIURI *aURI,
|
|
|
|
const char *aType,
|
|
|
|
PRUint32 aPermission,
|
|
|
|
PRUint32 aExpireType,
|
|
|
|
PRInt64 aExpireTime)
|
|
|
|
{
|
|
|
|
// Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let
|
|
|
|
// those be stored persistently.
|
|
|
|
if (!mInPrivateMode || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
|
|
|
|
// Not in private mode, or manually-set permission
|
|
|
|
return mPermMgr->Add(aURI, aType, aPermission, aExpireType, aExpireTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCAutoString host;
|
|
|
|
nsresult rv = GetHost(aURI, host);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
STSLOG(("AddPermission for entry for for %s", host.get()));
|
|
|
|
|
|
|
|
// Update in mPrivateModeHostTable only, so any changes will be rolled
|
|
|
|
// back when exiting private mode.
|
|
|
|
|
|
|
|
// Note: EXPIRE_NEVER permissions should trump anything that shows up in
|
|
|
|
// the HTTP header, so if there's an EXPIRE_NEVER permission already
|
|
|
|
// don't store anything new.
|
|
|
|
// Currently there's no way to get the type of expiry out of the
|
|
|
|
// permission manager, but that's okay since there's nothing that stores
|
|
|
|
// EXPIRE_NEVER permissions.
|
|
|
|
|
|
|
|
// PutEntry returns an existing entry if there already is one, or it
|
|
|
|
// creates a new one if there isn't.
|
|
|
|
nsSTSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get());
|
|
|
|
STSLOG(("Created private mode entry for for %s", host.get()));
|
|
|
|
|
|
|
|
// AddPermission() will be called twice if the STS header encountered has
|
|
|
|
// includeSubdomains (first for the main permission and second for the
|
|
|
|
// subdomains permission). If AddPermission() gets called a second time
|
|
|
|
// with the STS_SUBDOMAIN_PERMISSION, we just have to flip that bit in
|
|
|
|
// the nsSTSHostEntry.
|
|
|
|
if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) {
|
|
|
|
entry->mIncludeSubdomains = PR_TRUE;
|
|
|
|
}
|
|
|
|
// for the case where PutEntry() returned an existing host entry, make
|
|
|
|
// sure it's not set as deleted (which might have happened in the past).
|
|
|
|
entry->mDeleted = PR_FALSE;
|
|
|
|
|
|
|
|
// Also refresh the expiration time.
|
|
|
|
entry->mExpireTime = aExpireTime;
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::RemovePermission(const nsCString &aHost,
|
|
|
|
const char *aType)
|
|
|
|
{
|
|
|
|
if (!mInPrivateMode) {
|
|
|
|
// Not in private mode: remove permissions persistently.
|
|
|
|
return mPermMgr->Remove(aHost, aType);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make changes in mPrivateModeHostTable only, so any changes will be
|
|
|
|
// rolled back when exiting private mode.
|
|
|
|
nsSTSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get());
|
|
|
|
|
|
|
|
// Build up an nsIURI for use with the permission manager.
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
nsresult rv = NS_NewURI(getter_AddRefs(uri),
|
|
|
|
NS_LITERAL_CSTRING("http://") + aHost);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// Check to see if there's STS data stored for this host in the
|
|
|
|
// permission manager (probably set outside private mode).
|
|
|
|
PRUint32 permmgrValue;
|
|
|
|
rv = mPermMgr->TestExactPermission(uri, aType, &permmgrValue);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// If there is STS data in the permission manager, store a "deleted" mask
|
|
|
|
// for the permission in mPrivateModeHostTable (either update
|
|
|
|
// mPrivateModeHostTable to have the deleted mask, or add one).
|
|
|
|
// This is because we don't want removals that happen in private mode to
|
|
|
|
// be reflected when private mode is exited -- but while in private mode
|
|
|
|
// we still want the effect of the removal.
|
|
|
|
if (permmgrValue != nsIPermissionManager::UNKNOWN_ACTION) {
|
|
|
|
// if there's no entry in mPrivateModeHostTable, we have to make one.
|
|
|
|
if (!entry) {
|
|
|
|
entry = mPrivateModeHostTable.PutEntry(aHost.get());
|
|
|
|
STSLOG(("Created private mode deleted mask for for %s", aHost.get()));
|
|
|
|
}
|
|
|
|
entry->mDeleted = PR_TRUE;
|
|
|
|
entry->mIncludeSubdomains = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, permission doesn't exist in the real permission manager, so
|
|
|
|
// there's nothing to "pretend" to delete. I'ts ok to delete any copy in
|
|
|
|
// mPrivateModeHostTable.
|
|
|
|
if (entry) mPrivateModeHostTable.RawRemoveEntry(entry);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsStrictTransportSecurityService::TestPermission(nsIURI *aURI,
|
|
|
|
const char *aType,
|
|
|
|
PRUint32 *aPermission,
|
2011-09-29 06:19:26 +00:00
|
|
|
bool testExact)
|
2010-10-06 17:07:39 +00:00
|
|
|
{
|
|
|
|
// set default for if we can't find any STS information
|
|
|
|
*aPermission = nsIPermissionManager::UNKNOWN_ACTION;
|
|
|
|
|
|
|
|
if (!mInPrivateMode) {
|
|
|
|
// if not in private mode, just delegate to the permission manager.
|
|
|
|
if (testExact)
|
|
|
|
return mPermMgr->TestExactPermission(aURI, aType, aPermission);
|
|
|
|
else
|
|
|
|
return mPermMgr->TestPermission(aURI, aType, aPermission);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCAutoString host;
|
|
|
|
nsresult rv = GetHost(aURI, host);
|
|
|
|
if (NS_FAILED(rv)) return NS_OK;
|
|
|
|
|
|
|
|
nsSTSHostEntry *entry;
|
|
|
|
PRUint32 actualExactPermission;
|
|
|
|
PRUint32 offset = 0;
|
|
|
|
PRInt64 now = PR_Now() / 1000;
|
|
|
|
|
|
|
|
// Used for testing permissions as we walk up the domain tree.
|
|
|
|
nsCOMPtr<nsIURI> domainWalkURI;
|
|
|
|
|
|
|
|
// In parallel, loop over private mode cache and also the real permission
|
|
|
|
// manager--ignoring any masked as "deleted" in the local cache. We have
|
|
|
|
// to do this here since the most specific permission in *either* the
|
|
|
|
// permission manager or mPrivateModeHostTable should be used.
|
|
|
|
do {
|
|
|
|
entry = mPrivateModeHostTable.GetEntry(host.get() + offset);
|
|
|
|
STSLOG(("Checking PM Table entry and permmgr for %s", host.get()+offset));
|
|
|
|
|
|
|
|
// flag as deleted any entries encountered that have expired. We only
|
|
|
|
// flag the nsSTSHostEntry because there could be some data in the
|
|
|
|
// permission manager that -- if not in private mode -- would have been
|
|
|
|
// overwritten by newly encountered STS data.
|
|
|
|
if (entry && (now > entry->mExpireTime)) {
|
|
|
|
STSLOG(("Deleting expired PM Table entry for %s", host.get()+offset));
|
|
|
|
entry->mDeleted = PR_TRUE;
|
|
|
|
entry->mIncludeSubdomains = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = NS_NewURI(getter_AddRefs(domainWalkURI),
|
|
|
|
NS_LITERAL_CSTRING("http://") + Substring(host, offset));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
rv = mPermMgr->TestExactPermission(domainWalkURI,
|
|
|
|
aType,
|
|
|
|
&actualExactPermission);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// There are three cases as we walk up the hostname testing
|
|
|
|
// permissions:
|
|
|
|
// 1. There's no entry in mPrivateModeHostTable for this host; rely
|
|
|
|
// on data in the permission manager
|
|
|
|
if (!entry) {
|
|
|
|
if (actualExactPermission != nsIPermissionManager::UNKNOWN_ACTION) {
|
|
|
|
// no cached data but a permission in the permission manager so use
|
|
|
|
// it and stop looking.
|
|
|
|
*aPermission = actualExactPermission;
|
|
|
|
STSLOG(("no PM Table entry for %s, using permmgr", host.get()+offset));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 2. There's a "deleted" mask in mPrivateModeHostTable for this host
|
|
|
|
// or we're looking for includeSubdomain information and it's not set:
|
|
|
|
// any data in the permission manager must be ignored, since the
|
|
|
|
// permission would have been deleted if not in private mode.
|
|
|
|
else if (entry->mDeleted || (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0
|
|
|
|
&& !entry->mIncludeSubdomains)) {
|
|
|
|
STSLOG(("no entry at all for %s, walking up", host.get()+offset));
|
|
|
|
// keep looking
|
|
|
|
}
|
|
|
|
// 3. There's a non-deleted entry in mPrivateModeHostTable for this
|
|
|
|
// host, so it should be used.
|
|
|
|
else {
|
|
|
|
// All STS permissions' values are ALLOW_ACTION or they are not
|
|
|
|
// known (as in, not set or turned off).
|
|
|
|
*aPermission = nsIPermissionManager::ALLOW_ACTION;
|
|
|
|
STSLOG(("PM Table entry for %s: forcing", host.get()+offset));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't continue walking up the host segments if the test was for an
|
|
|
|
// exact match only.
|
|
|
|
if (testExact) break;
|
|
|
|
|
|
|
|
STSLOG(("no PM Table entry or permmgr data for %s, walking up domain",
|
|
|
|
host.get()+offset));
|
|
|
|
// walk up the host segments
|
|
|
|
offset = host.FindChar('.', offset) + 1;
|
|
|
|
} while (offset > 0);
|
|
|
|
|
|
|
|
// Use whatever we ended up with, which defaults to UNKNOWN_ACTION.
|
|
|
|
return NS_OK;
|
|
|
|
}
|