Bug 722979 - Add privacy status argument to relevant nsIStrictTransportSecurityService methods. r=bsmith sr=biesi

This commit is contained in:
Josh Matthews 2012-06-30 07:34:17 -07:00
parent 7e9fe96633
commit 79a2c3192a
25 changed files with 258 additions and 179 deletions

View File

@ -498,12 +498,15 @@
var connector =
Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect);
var searchURI = engine.getSubmission("dummy").uri;
connector.speculativeConnect(searchURI, null);
let callbacks = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
connector.speculativeConnect(searchURI, callbacks);
if (engine.supportsResponseType(SUGGEST_TYPE)) {
var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE).uri;
if (suggestURI.prePath != searchURI.prePath)
connector.speculativeConnect(suggestURI, null);
connector.speculativeConnect(suggestURI, callbacks);
}
]]></handler>
</handlers>

View File

@ -1233,7 +1233,13 @@ function test41()
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var thehost = ios.newURI("http://example.com", null, null);
var stss = Cc["@mozilla.org/stsservice;1"].getService(Ci.nsIStrictTransportSecurityService);
stss.removeStsState(thehost);
var loadContext = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
var flags = 0;
if (loadContext.usePrivateBrowsing)
flags |= Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
stss.removeStsState(thehost, flags);
doTest(42);
}
}

View File

@ -114,6 +114,7 @@
#include "nsIObserver.h"
#include "nsINestedURI.h"
#include "nsITransportSecurityInfo.h"
#include "nsISSLSocketControl.h"
#include "nsINSSErrorsService.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheChannel.h"
@ -4174,9 +4175,14 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
nsCOMPtr<nsIStrictTransportSecurityService> stss =
do_GetService(NS_STSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t flags = 0;
nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(tsi);
if (socketControl) {
socketControl->GetProviderFlags(&flags);
}
bool isStsHost = false;
rv = stss->IsStsURI(aURI, &isStsHost);
rv = stss->IsStsURI(aURI, flags, &isStsHost);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t bucketId;

View File

@ -150,6 +150,16 @@ interface nsISocketTransport : nsITransport
*/
const unsigned long DISABLE_IPV6 = (1 << 2);
/**
* If set, indicates that the connection was initiated from a source
* defined as being private in the sense of Private Browsing. Generally,
* there should be no state shared between connections that are private
* and those that are not; it is OK for multiple private connections
* to share state with each other, and it is OK for multiple non-private
* connections to share state with each other.
*/
const unsigned long NO_PERMANENT_STORAGE = (1 << 3);
/**
* Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or
* IPTOS_CLASS_CSx (or IPTOS_DSCP_EF, but currently no supported

View File

@ -8,7 +8,7 @@ interface nsIURI;
interface nsIObserver;
interface nsIHttpChannel;
[scriptable, uuid(aee925d1-2bc9-469e-9582-b27b1d6b5192)]
[scriptable, uuid(c6138514-f212-4747-98c2-7abfce3be293)]
interface nsIStrictTransportSecurityService : nsISupports
{
/**
@ -20,6 +20,8 @@ interface nsIStrictTransportSecurityService : nsISupports
*
* @param aSourceURI the URI of the resource with the HTTP header.
* @param aHeader the HTTP response header specifying STS data.
* @param aFlags options for this request as defined in nsISocketProvider:
* NO_PERMANENT_STORAGE
* @param aMaxAge the parsed max-age directive of the header.
* @param aIncludeSubdomains the parsed includeSubdomains directive.
* @return NS_OK if it succeeds
@ -29,6 +31,7 @@ interface nsIStrictTransportSecurityService : nsISupports
*/
void processStsHeader(in nsIURI aSourceURI,
in string aHeader,
in uint32_t aFlags,
[optional] out unsigned long long aMaxAge,
[optional] out boolean aIncludeSubdomains);
@ -36,8 +39,12 @@ interface nsIStrictTransportSecurityService : nsISupports
* Removes the STS state of a host, including the includeSubdomains state
* that would affect subdomains. This essentially removes STS state for
* the domain tree rooted at this host.
* @param aURI the URI of the target host
* @param aFlags options for this request as defined in nsISocketProvider:
* NO_PERMANENT_STORAGE
*/
void removeStsState(in nsIURI aURI);
void removeStsState(in nsIURI aURI,
in uint32_t aFlags);
/**
* Checks if the given security info is for an STS host with a broken
@ -51,8 +58,10 @@ interface nsIStrictTransportSecurityService : nsISupports
* its super-domains has an STS "includeSubdomains" permission set.
*
* @param aHost the hostname (punycode) to query for STS state.
* @param aFlags options for this request as defined in nsISocketProvider:
* NO_PERMANENT_STORAGE
*/
boolean isStsHost(in string aHost);
boolean isStsHost(in string aHost, in uint32_t aFlags);
/**
* Checks whether or not the URI's hostname has STS state set.
@ -63,8 +72,10 @@ interface nsIStrictTransportSecurityService : nsISupports
* such as path and port.
*
* @param aURI the URI to query for STS state.
* @param aFlags options for this request as defined in nsISocketProvider:
* NO_PERMANENT_STORAGE
*/
boolean isStsURI(in nsIURI aURI);
boolean isStsURI(in nsIURI aURI, in uint32_t aFlags);
};

View File

@ -977,6 +977,9 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &us
if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT)
proxyFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE)
proxyFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
nsCOMPtr<nsISupports> secinfo;
if (i == 0) {
// if this is the first type, we'll want the

View File

@ -370,7 +370,8 @@ nsHttpChannel::Connect()
NS_ENSURE_TRUE(stss, NS_ERROR_OUT_OF_MEMORY);
bool isStsHost = false;
rv = stss->IsStsURI(mURI, &isStsHost);
uint32_t flags = mPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
rv = stss->IsStsURI(mURI, flags, &isStsHost);
// if STS fails, there's no reason to cancel the load, but it's
// worrisome.
@ -1112,7 +1113,9 @@ nsHttpChannel::ProcessSTSHeader()
// If this wasn't an STS host, errors are allowed, but no more STS processing
// will happen during the session.
bool wasAlreadySTSHost;
rv = stss->IsStsURI(mURI, &wasAlreadySTSHost);
uint32_t flags =
NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
rv = stss->IsStsURI(mURI, flags, &wasAlreadySTSHost);
// Failure here means STS is broken. Don't prevent the load, but this
// shouldn't fail.
NS_ENSURE_SUCCESS(rv, NS_OK);
@ -1140,7 +1143,7 @@ nsHttpChannel::ProcessSTSHeader()
// All other failures are fatal.
NS_ENSURE_SUCCESS(rv, rv);
rv = stss->ProcessStsHeader(mURI, stsHeader.get(), NULL, NULL);
rv = stss->ProcessStsHeader(mURI, stsHeader.get(), flags, NULL, NULL);
if (NS_FAILED(rv)) {
LOG(("STS: Failed to parse STS header, continuing load.\n"));
return NS_OK;

View File

@ -2457,6 +2457,9 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
if (mEnt->mConnInfo->GetPrivate())
tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
// For backup connections, we disable IPv6. That's because some users have
// broken IPv6 connectivity (leading to very long timeouts), and disabling
// IPv6 on the backup connection gives them a much better user experience

View File

@ -1620,8 +1620,12 @@ nsHttpHandler::SpeculativeConnect(nsIURI *aURI,
if (!stss)
return NS_OK;
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(aCallbacks);
uint32_t flags = 0;
if (loadContext && loadContext->UsePrivateBrowsing())
flags |= nsISocketProvider::NO_PERMANENT_STORAGE;
nsCOMPtr<nsIURI> clone;
if (NS_SUCCEEDED(stss->IsStsURI(aURI, &isStsHost)) && isStsHost) {
if (NS_SUCCEEDED(stss->IsStsURI(aURI, flags, &isStsHost)) && isStsHost) {
if (NS_SUCCEEDED(aURI->Clone(getter_AddRefs(clone)))) {
clone->SetScheme(NS_LITERAL_CSTRING("https"));
aURI = clone.get();

View File

@ -50,5 +50,9 @@ interface nsISSLSocketControl : nsISupports {
in ACString hostname,
in long port);
/*
* The original flags from the socket provider.
*/
readonly attribute uint32_t providerFlags;
};

View File

@ -82,6 +82,15 @@ interface nsISocketProvider : nsISupports
*/
const long ANONYMOUS_CONNECT = 1 << 1;
/**
* If set, indicates that the connection was initiated from a source
* defined as being private in the sense of Private Browsing. Generally,
* there should be no state shared between connections that are private
* and those that are not; it is OK for multiple private connections
* to share state with each other, and it is OK for multiple non-private
* connections to share state with each other.
*/
const unsigned long NO_PERMANENT_STORAGE = 1 << 2;
};
%{C++

View File

@ -49,7 +49,7 @@ TestSuccess(const char* hdr, bool extraTokens,
uint64_t maxAge = 0;
bool includeSubdomains = false;
rv = stss->ProcessStsHeader(dummyUri, hdr, &maxAge, &includeSubdomains);
rv = stss->ProcessStsHeader(dummyUri, hdr, 0, &maxAge, &includeSubdomains);
EXPECT_SUCCESS(rv, "Failed to process valid header: %s", hdr);
REQUIRE_EQUAL(maxAge, expectedMaxAge, "Did not correctly parse maxAge");
@ -74,7 +74,7 @@ bool TestFailure(const char* hdr,
nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html");
EXPECT_SUCCESS(rv, "Failed to create URI");
rv = stss->ProcessStsHeader(dummyUri, hdr, NULL, NULL);
rv = stss->ProcessStsHeader(dummyUri, hdr, 0, NULL, NULL);
EXPECT_FAILURE(rv, "Parsed invalid header: %s", hdr);
passed(hdr);
return true;

View File

@ -7,7 +7,6 @@
#include "prprf.h"
#include "nsCRTGlue.h"
#include "nsIPermissionManager.h"
#include "nsIPrivateBrowsingService.h"
#include "nsISSLStatus.h"
#include "nsISSLStatusProvider.h"
#include "nsStrictTransportSecurityService.h"
@ -16,6 +15,7 @@
#include "nsThreadUtils.h"
#include "nsStringGlue.h"
#include "nsIScriptSecurityManager.h"
#include "nsISocketProvider.h"
#include "mozilla/Preferences.h"
// A note about the preload list:
@ -75,7 +75,7 @@ nsSTSHostEntry::nsSTSHostEntry(const nsSTSHostEntry& toCopy)
nsStrictTransportSecurityService::nsStrictTransportSecurityService()
: mInPrivateMode(false), mUsePreloadList(true)
: mUsePreloadList(true)
{
}
@ -95,19 +95,12 @@ nsStrictTransportSecurityService::Init()
mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// 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);
mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
mozilla::Preferences::AddStrongObserver(this, "network.stricttransportsecurity.preloadlist");
mObserverService = mozilla::services::GetObserverService();
if (mObserverService)
mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
mObserverService->AddObserver(this, "last-pb-context-exited", false);
if (mInPrivateMode)
mPrivateModeHostTable.Init();
return NS_OK;
@ -153,25 +146,30 @@ nsStrictTransportSecurityService::GetPrincipalForURI(nsIURI* aURI,
nsresult
nsStrictTransportSecurityService::SetStsState(nsIURI* aSourceURI,
int64_t maxage,
bool includeSubdomains)
bool includeSubdomains,
uint32_t flags)
{
// If max-age is zero, that's an indication to immediately remove the
// permissions, so here's a shortcut.
if (!maxage)
return RemoveStsState(aSourceURI);
if (!maxage) {
return RemoveStsState(aSourceURI, flags);
}
// 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.
int64_t expiretime = (PR_Now() / PR_USEC_PER_MSEC) +
(maxage * PR_MSEC_PER_SEC);
bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
// record entry for this host with max-age in the permissions manager
STSLOG(("STS: maxage permission SET, adding permission\n"));
nsresult rv = AddPermission(aSourceURI,
STS_PERMISSION,
(uint32_t) STS_SET,
(uint32_t) nsIPermissionManager::EXPIRE_TIME,
expiretime);
expiretime,
isPrivate);
NS_ENSURE_SUCCESS(rv, rv);
if (includeSubdomains) {
@ -181,7 +179,8 @@ nsStrictTransportSecurityService::SetStsState(nsIURI* aSourceURI,
STS_SUBDOMAIN_PERMISSION,
(uint32_t) STS_SET,
(uint32_t) nsIPermissionManager::EXPIRE_TIME,
expiretime);
expiretime,
isPrivate);
NS_ENSURE_SUCCESS(rv, rv);
} else { // !includeSubdomains
nsAutoCString hostname;
@ -189,14 +188,14 @@ nsStrictTransportSecurityService::SetStsState(nsIURI* aSourceURI,
NS_ENSURE_SUCCESS(rv, rv);
STSLOG(("STS: subdomains permission UNSET, removing any existing ones\n"));
rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
nsStrictTransportSecurityService::RemoveStsState(nsIURI* aURI)
nsStrictTransportSecurityService::RemoveStsState(nsIURI* aURI, uint32_t aFlags)
{
// Should be called on the main thread (or via proxy) since the permission
// manager is used and it's not threadsafe.
@ -206,11 +205,13 @@ nsStrictTransportSecurityService::RemoveStsState(nsIURI* aURI)
nsresult rv = GetHost(aURI, hostname);
NS_ENSURE_SUCCESS(rv, rv);
rv = RemovePermission(hostname, STS_PERMISSION);
bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
rv = RemovePermission(hostname, STS_PERMISSION, isPrivate);
NS_ENSURE_SUCCESS(rv, rv);
STSLOG(("STS: deleted maxage permission\n"));
rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION);
rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION, isPrivate);
NS_ENSURE_SUCCESS(rv, rv);
STSLOG(("STS: deleted subdomains permission\n"));
@ -220,6 +221,7 @@ nsStrictTransportSecurityService::RemoveStsState(nsIURI* aURI)
NS_IMETHODIMP
nsStrictTransportSecurityService::ProcessStsHeader(nsIURI* aSourceURI,
const char* aHeader,
uint32_t aFlags,
uint64_t *aMaxAge,
bool *aIncludeSubdomains)
{
@ -237,8 +239,8 @@ nsStrictTransportSecurityService::ProcessStsHeader(nsIURI* aSourceURI,
char * header = NS_strdup(aHeader);
if (!header) return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = ProcessStsHeaderMutating(aSourceURI, header, aMaxAge,
aIncludeSubdomains);
nsresult rv = ProcessStsHeaderMutating(aSourceURI, header, aFlags,
aMaxAge, aIncludeSubdomains);
NS_Free(header);
return rv;
}
@ -246,6 +248,7 @@ nsStrictTransportSecurityService::ProcessStsHeader(nsIURI* aSourceURI,
nsresult
nsStrictTransportSecurityService::ProcessStsHeaderMutating(nsIURI* aSourceURI,
char* aHeader,
uint32_t aFlags,
uint64_t *aMaxAge,
bool *aIncludeSubdomains)
{
@ -338,7 +341,7 @@ nsStrictTransportSecurityService::ProcessStsHeaderMutating(nsIURI* aSourceURI,
("Parse ERROR: couldn't locate max-age token\n"));
// record the successfully parsed header data.
SetStsState(aSourceURI, maxAge, includeSubdomains);
SetStsState(aSourceURI, maxAge, includeSubdomains, aFlags);
if (aMaxAge != nullptr) {
*aMaxAge = (uint64_t)maxAge;
@ -354,7 +357,7 @@ nsStrictTransportSecurityService::ProcessStsHeaderMutating(nsIURI* aSourceURI,
}
NS_IMETHODIMP
nsStrictTransportSecurityService::IsStsHost(const char* aHost, bool* aResult)
nsStrictTransportSecurityService::IsStsHost(const char* aHost, uint32_t aFlags, bool* aResult)
{
// Should be called on the main thread (or via proxy) since the permission
// manager is used and it's not threadsafe.
@ -365,7 +368,7 @@ nsStrictTransportSecurityService::IsStsHost(const char* aHost, bool* aResult)
nsresult rv = NS_NewURI(getter_AddRefs(uri),
NS_LITERAL_CSTRING("https://") + hostString);
NS_ENSURE_SUCCESS(rv, rv);
return IsStsURI(uri, aResult);
return IsStsURI(uri, aFlags, aResult);
}
int STSPreloadCompare(const void *key, const void *entry)
@ -401,7 +404,7 @@ nsStrictTransportSecurityService::GetPreloadListEntry(const char *aHost)
}
NS_IMETHODIMP
nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult)
nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, uint32_t aFlags, bool* aResult)
{
// Should be called on the main thread (or via proxy) since the permission
// manager is used and it's not threadsafe.
@ -417,7 +420,8 @@ nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult)
const nsSTSPreload *preload = nullptr;
nsSTSHostEntry *pbEntry = nullptr;
if (mInPrivateMode) {
bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
if (isPrivate) {
pbEntry = mPrivateModeHostTable.GetEntry(host.get());
}
@ -479,7 +483,7 @@ nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult)
break;
}
if (mInPrivateMode) {
if (isPrivate) {
pbEntry = mPrivateModeHostTable.GetEntry(subdomain);
}
@ -580,20 +584,8 @@ 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();
}
mInPrivateMode = true;
}
else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(data)) {
if (strcmp(topic, "last-pb-context-exited") == 0) {
mPrivateModeHostTable.Clear();
mInPrivateMode = false;
}
}
else if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
mUsePreloadList = mozilla::Preferences::GetBool("network.stricttransportsecurity.preloadlist", true);
@ -611,11 +603,12 @@ nsStrictTransportSecurityService::AddPermission(nsIURI *aURI,
const char *aType,
uint32_t aPermission,
uint32_t aExpireType,
int64_t aExpireTime)
int64_t aExpireTime,
bool aIsPrivate)
{
// Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let
// those be stored persistently.
if (!mInPrivateMode || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
if (!aIsPrivate || aExpireType == nsIPermissionManager::EXPIRE_NEVER) {
// Not in private mode, or manually-set permission
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipalForURI(aURI, getter_AddRefs(principal));
@ -667,7 +660,8 @@ nsStrictTransportSecurityService::AddPermission(nsIURI *aURI,
nsresult
nsStrictTransportSecurityService::RemovePermission(const nsCString &aHost,
const char *aType)
const char *aType,
bool aIsPrivate)
{
// Build up a principal for use with the permission manager.
// normalize all URIs with https://
@ -680,7 +674,7 @@ nsStrictTransportSecurityService::RemovePermission(const nsCString &aHost,
rv = GetPrincipalForURI(uri, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
if (!mInPrivateMode) {
if (!aIsPrivate) {
// Not in private mode: remove permissions persistently.
// This means setting the permission to STS_KNOCKOUT in case
// this host is on the preload list (so we can override it).

View File

@ -135,8 +135,8 @@ public:
private:
nsresult GetHost(nsIURI *aURI, nsACString &aResult);
nsresult GetPrincipalForURI(nsIURI *aURI, nsIPrincipal **aPrincipal);
nsresult SetStsState(nsIURI* aSourceURI, int64_t maxage, bool includeSubdomains);
nsresult ProcessStsHeaderMutating(nsIURI* aSourceURI, char* aHeader,
nsresult SetStsState(nsIURI* aSourceURI, int64_t maxage, bool includeSubdomains, uint32_t flags);
nsresult ProcessStsHeaderMutating(nsIURI* aSourceURI, char* aHeader, uint32_t flags,
uint64_t *aMaxAge, bool *aIncludeSubdomains);
const nsSTSPreload *GetPreloadListEntry(const char *aHost);
@ -145,15 +145,16 @@ private:
const char *aType,
uint32_t aPermission,
uint32_t aExpireType,
int64_t aExpireTime);
int64_t aExpireTime,
bool aIsPrivate);
nsresult RemovePermission(const nsCString &aHost,
const char *aType);
const char *aType,
bool aIsPrivate);
// cached services
nsCOMPtr<nsIPermissionManager> mPermMgr;
nsCOMPtr<nsIObserverService> mObserverService;
bool mInPrivateMode;
nsTHashtable<nsSTSHostEntry> mPrivateModeHostTable;
bool mUsePreloadList;
};

View File

@ -293,7 +293,14 @@ CertErrorRunnable::CheckCertOverrides()
nsCOMPtr<nsIStrictTransportSecurityService> stss
= do_GetService(NS_STSSERVICE_CONTRACTID, &nsrv);
if (NS_SUCCEEDED(nsrv)) {
nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
uint32_t flags = 0;
if (sslSocketControl) {
sslSocketControl->GetProviderFlags(&flags);
}
nsrv = stss->IsStsHost(mInfoObject->GetHostName(),
flags,
&strictTransportSecurityEnabled);
}
if (NS_FAILED(nsrv)) {

View File

@ -17,6 +17,7 @@
#include "nsIAssociatedContentSecurity.h"
#include "nsNSSShutDown.h"
#include "nsDataHashtable.h"
#include "nsISocketTransport.h"
namespace mozilla { namespace psm {

View File

@ -61,7 +61,7 @@ typedef enum {ASK, AUTO} SSM_UserCertChoice;
extern PRLogModuleInfo* gPIPNSSLog;
#endif
nsNSSSocketInfo::nsNSSSocketInfo()
nsNSSSocketInfo::nsNSSSocketInfo(uint32_t providerFlags)
: mFd(nullptr),
mCertVerificationState(before_cert_verification),
mForSTARTTLS(false),
@ -77,7 +77,8 @@ nsNSSSocketInfo::nsNSSSocketInfo()
mNPNCompleted(false),
mHandshakeCompleted(false),
mJoined(false),
mSentClientCert(false)
mSentClientCert(false),
mProviderFlags(providerFlags)
{
}
@ -85,6 +86,13 @@ NS_IMPL_ISUPPORTS_INHERITED2(nsNSSSocketInfo, TransportSecurityInfo,
nsISSLSocketControl,
nsIClientAuthUserDecision)
NS_IMETHODIMP
nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags)
{
*aProviderFlags = mProviderFlags;
return NS_OK;
}
nsresult
nsNSSSocketInfo::GetHandshakePending(bool *aHandshakePending)
{
@ -1312,14 +1320,14 @@ nsSSLIOLayerNewSocket(int32_t family,
PRFileDesc **fd,
nsISupports** info,
bool forSTARTTLS,
bool anonymousLoad)
uint32_t flags)
{
PRFileDesc* sock = PR_OpenTCPSocket(family);
if (!sock) return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxyHost, proxyPort,
sock, info, forSTARTTLS, anonymousLoad);
sock, info, forSTARTTLS, flags);
if (NS_FAILED(rv)) {
PR_Close(sock);
return rv;
@ -2309,8 +2317,7 @@ done:
static PRFileDesc*
nsSSLIOLayerImportFD(PRFileDesc *fd,
nsNSSSocketInfo *infoObject,
const char *host,
bool anonymousLoad)
const char *host)
{
nsNSSShutDownPreventionLock locker;
PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
@ -2322,7 +2329,9 @@ nsSSLIOLayerImportFD(PRFileDesc *fd,
SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
// Disable this hook if we connect anonymously. See bug 466080.
if (anonymousLoad) {
uint32_t flags = 0;
infoObject->GetProviderFlags(&flags);
if (flags & nsISocketTransport::ANONYMOUS_CONNECT) {
SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject);
} else {
SSL_GetClientAuthDataHook(sslSock,
@ -2428,14 +2437,14 @@ nsSSLIOLayerAddToSocket(int32_t family,
PRFileDesc* fd,
nsISupports** info,
bool forSTARTTLS,
bool anonymousLoad)
uint32_t providerFlags)
{
nsNSSShutDownPreventionLock locker;
PRFileDesc* layer = nullptr;
nsresult rv;
PRStatus stat;
nsNSSSocketInfo* infoObject = new nsNSSSocketInfo();
nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(providerFlags);
if (!infoObject) return NS_ERROR_FAILURE;
NS_ADDREF(infoObject);
@ -2443,7 +2452,8 @@ nsSSLIOLayerAddToSocket(int32_t family,
infoObject->SetHostName(host);
infoObject->SetPort(port);
PRFileDesc *sslSock = nsSSLIOLayerImportFD(fd, infoObject, host, anonymousLoad);
bool anonymousLoad = providerFlags & nsISocketProvider::ANONYMOUS_CONNECT;
PRFileDesc *sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
if (!sslSock) {
NS_ASSERTION(false, "NSS: Error importing socket");
goto loser;

View File

@ -13,13 +13,14 @@
#include "nsNSSCertificate.h"
#include "nsDataHashtable.h"
#include "nsTHashtable.h"
#include "nsISocketTransport.h"
class nsNSSSocketInfo : public mozilla::psm::TransportSecurityInfo,
public nsISSLSocketControl,
public nsIClientAuthUserDecision
{
public:
nsNSSSocketInfo();
nsNSSSocketInfo(uint32_t providerFlags);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSISSLSOCKETCONTROL
@ -100,6 +101,8 @@ private:
bool mHandshakeCompleted;
bool mJoined;
bool mSentClientCert;
uint32_t mProviderFlags;
};
class nsSSLIOLayerHelpers
@ -146,7 +149,7 @@ nsresult nsSSLIOLayerNewSocket(int32_t family,
PRFileDesc **fd,
nsISupports **securityInfo,
bool forSTARTTLS,
bool anonymousLoad);
uint32_t flags);
nsresult nsSSLIOLayerAddToSocket(int32_t family,
const char *host,
@ -156,7 +159,7 @@ nsresult nsSSLIOLayerAddToSocket(int32_t family,
PRFileDesc *fd,
nsISupports **securityInfo,
bool forSTARTTLS,
bool anonymousLoad);
uint32_t flags);
nsresult nsSSLIOLayerFreeTLSIntolerantSites();
nsresult displayUnknownCertErrorAlert(nsNSSSocketInfo *infoObject, int error);

View File

@ -36,7 +36,7 @@ nsSSLSocketProvider::NewSocket(int32_t family,
_result,
securityInfo,
false,
flags & ANONYMOUS_CONNECT);
flags);
return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
}
@ -59,7 +59,7 @@ nsSSLSocketProvider::AddToSocket(int32_t family,
aSocket,
securityInfo,
false,
flags & ANONYMOUS_CONNECT);
flags);
return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
}

View File

@ -36,7 +36,7 @@ nsTLSSocketProvider::NewSocket(int32_t family,
_result,
securityInfo,
true,
flags & ANONYMOUS_CONNECT);
flags);
return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
}
@ -60,7 +60,7 @@ nsTLSSocketProvider::AddToSocket(int32_t family,
aSocket,
securityInfo,
true,
flags & ANONYMOUS_CONNECT);
flags);
return (NS_FAILED(rv)) ? NS_ERROR_SOCKET_CREATE_FAILED : NS_OK;
}

View File

@ -54,7 +54,7 @@
var thehost = ios.newURI("http://example.com", null, null);
var stss = Cc["@mozilla.org/stsservice;1"].getService(Ci.nsIStrictTransportSecurityService);
stss.removeStsState(thehost);
stss.removeStsState(thehost, 0);
}
function loadVerifyFrames(round) {

View File

@ -171,7 +171,12 @@
var thehost = ios.newURI("http://example.com", null, null);
var stss = Cc["@mozilla.org/stsservice;1"]
.getService(Ci.nsIStrictTransportSecurityService);
stss.removeStsState(thehost);
var loadContext = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
var flags = loadContext.usePrivateBrowsing ?
Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0
stss.removeStsState(thehost, flags);
dump_STSState();
SimpleTest.executeSoon(nextTest);
}
@ -180,7 +185,12 @@ function dump_STSState() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var stss = Components.classes["@mozilla.org/stsservice;1"]
.getService(Components.interfaces.nsIStrictTransportSecurityService);
SimpleTest.info("State of example.com: " + stss.isStsHost("example.com"));
var loadContext = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsILoadContext);
var flags = loadContext.usePrivateBrowsing ?
Components.interfaces.nsISocketProvider.NO_PERMANENT_STORAGE : 0
SimpleTest.info("State of example.com: " + stss.isStsHost("example.com", flags));
}
// these are executed in the order presented.

View File

@ -3,73 +3,62 @@ var Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
var _PBSvc = null;
function get_PBSvc() {
if (_PBSvc)
return _PBSvc;
try {
_PBSvc = Cc["@mozilla.org/privatebrowsing;1"]
.getService(Ci.nsIPrivateBrowsingService);
return _PBSvc;
} catch (e) {}
return null;
}
var gSTSService = Cc["@mozilla.org/stsservice;1"]
.getService(Ci.nsIStrictTransportSecurityService);
function Observer() {}
Observer.prototype = {
observe: function(subject, topic, data) {
do_execute_soon(gNextTest);
}
};
var gObserver = new Observer();
var gNextTest = null;
function cleanup() {
Services.obs.removeObserver(gObserver, "private-browsing-transition-complete");
get_PBSvc().privateBrowsingEnabled = false;
leavePB();
// (we have to remove any state added to the sts service so as to not muck
// with other tests).
var uri = Services.io.newURI("http://localhost", null, null);
gSTSService.removeStsState(uri);
gSTSService.removeStsState(uri, privacyFlags());
}
function run_test() {
let pb = get_PBSvc();
if (pb) {
do_test_pending();
do_register_cleanup(cleanup);
gNextTest = test_part1;
Services.obs.addObserver(gObserver, "private-browsing-transition-complete", false);
Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
pb.privateBrowsingEnabled = true;
}
enterPB();
}
var gInPrivate = false;
function enterPB() {
gInPrivate = true;
do_execute_soon(gNextTest);
}
function leavePB() {
gInPrivate = false;
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
do_execute_soon(gNextTest);
}
function privacyFlags() {
return gInPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
}
function test_part1() {
var uri = Services.io.newURI("https://localhost/img.png", null, null);
gSTSService.processStsHeader(uri, "max-age=1000");
do_check_true(gSTSService.isStsHost("localhost"));
gSTSService.processStsHeader(uri, "max-age=1000", privacyFlags());
do_check_true(gSTSService.isStsHost("localhost", privacyFlags()));
gNextTest = test_part2;
get_PBSvc().privateBrowsingEnabled = false;
leavePB();
}
function test_part2() {
var uri = Services.io.newURI("https://localhost/img.png", null, null);
gSTSService.processStsHeader(uri, "max-age=1000");
do_check_true(gSTSService.isStsHost("localhost"));
gSTSService.processStsHeader(uri, "max-age=1000", privacyFlags());
do_check_true(gSTSService.isStsHost("localhost", privacyFlags()));
gNextTest = test_part3;
get_PBSvc().privateBrowsingEnabled = true;
enterPB();
}
function test_part3() {
var uri = Services.io.newURI("https://localhost/img.png", null, null);
gSTSService.processStsHeader(uri, "max-age=1000");
do_check_true(gSTSService.isStsHost("localhost"));
gSTSService.processStsHeader(uri, "max-age=1000", privacyFlags());
do_check_true(gSTSService.isStsHost("localhost", privacyFlags()));
do_test_finished();
}

View File

@ -46,7 +46,7 @@ function cleanup() {
for (var host of hosts) {
var uri = Services.io.newURI(host, null, null);
gSTSService.removeStsState(uri);
gSTSService.removeStsState(uri, 0);
}
}
@ -66,65 +66,65 @@ function run_test() {
function test_part1() {
// check that a host not in the list is not identified as an sts host
do_check_false(gSTSService.isStsHost("nonexistent.mozilla.com"));
do_check_false(gSTSService.isStsHost("nonexistent.mozilla.com", 0));
// check that an ancestor domain is not identified as an sts host
do_check_false(gSTSService.isStsHost("com"));
do_check_false(gSTSService.isStsHost("com", 0));
// Note: the following were taken from the STS preload list
// as of Sept. 2012. If the list changes, this test will need to be modified.
// check that the pref to toggle using the preload list works
Services.prefs.setBoolPref("network.stricttransportsecurity.preloadlist", false);
do_check_false(gSTSService.isStsHost("factor.cc"));
do_check_false(gSTSService.isStsHost("factor.cc", 0));
Services.prefs.setBoolPref("network.stricttransportsecurity.preloadlist", true);
do_check_true(gSTSService.isStsHost("factor.cc"));
do_check_true(gSTSService.isStsHost("factor.cc", 0));
// check that an entry at the beginning of the list is an sts host
do_check_true(gSTSService.isStsHost("arivo.com.br"));
do_check_true(gSTSService.isStsHost("arivo.com.br", 0));
// check that a subdomain is an sts host (includeSubdomains is set)
do_check_true(gSTSService.isStsHost("subdomain.arivo.com.br"));
do_check_true(gSTSService.isStsHost("subdomain.arivo.com.br", 0));
// check that another subdomain is an sts host (includeSubdomains is set)
do_check_true(gSTSService.isStsHost("a.b.c.subdomain.arivo.com.br"));
do_check_true(gSTSService.isStsHost("a.b.c.subdomain.arivo.com.br", 0));
// check that an entry in the middle of the list is an sts host
do_check_true(gSTSService.isStsHost("neg9.org"));
do_check_true(gSTSService.isStsHost("neg9.org", 0));
// check that a subdomain is not an sts host (includeSubdomains is not set)
do_check_false(gSTSService.isStsHost("subdomain.neg9.org"));
do_check_false(gSTSService.isStsHost("subdomain.neg9.org", 0));
// check that an entry at the end of the list is an sts host
do_check_true(gSTSService.isStsHost("www.noisebridge.net"));
do_check_true(gSTSService.isStsHost("www.noisebridge.net", 0));
// check that a subdomain is not an sts host (includeSubdomains is not set)
do_check_false(gSTSService.isStsHost("a.subdomain.www.noisebridge.net"));
do_check_false(gSTSService.isStsHost("a.subdomain.www.noisebridge.net", 0));
// check that a host with a dot on the end won't break anything
do_check_false(gSTSService.isStsHost("notsts.nonexistent.mozilla.com."));
do_check_false(gSTSService.isStsHost("notsts.nonexistent.mozilla.com.", 0));
// check that processing a header with max-age: 0 will remove a preloaded
// site from the list
var uri = Services.io.newURI("http://keyerror.com", null, null);
gSTSService.processStsHeader(uri, "max-age=0");
do_check_false(gSTSService.isStsHost("keyerror.com"));
do_check_false(gSTSService.isStsHost("subdomain.keyerror.com"));
gSTSService.processStsHeader(uri, "max-age=0", 0);
do_check_false(gSTSService.isStsHost("keyerror.com", 0));
do_check_false(gSTSService.isStsHost("subdomain.keyerror.com", 0));
// check that processing another header (with max-age non-zero) will
// re-enable a site's sts status
gSTSService.processStsHeader(uri, "max-age=1000");
do_check_true(gSTSService.isStsHost("keyerror.com"));
gSTSService.processStsHeader(uri, "max-age=1000", 0);
do_check_true(gSTSService.isStsHost("keyerror.com", 0));
// but this time include subdomains was not set, so test for that
do_check_false(gSTSService.isStsHost("subdomain.keyerror.com"));
do_check_false(gSTSService.isStsHost("subdomain.keyerror.com", 0));
// check that processing a header with max-age: 0 from a subdomain of a site
// will not remove that (ancestor) site from the list
var uri = Services.io.newURI("http://subdomain.intercom.io", null, null);
gSTSService.processStsHeader(uri, "max-age=0");
do_check_true(gSTSService.isStsHost("intercom.io"));
do_check_false(gSTSService.isStsHost("subdomain.intercom.io"));
gSTSService.processStsHeader(uri, "max-age=0", 0);
do_check_true(gSTSService.isStsHost("intercom.io", 0));
do_check_false(gSTSService.isStsHost("subdomain.intercom.io", 0));
var uri = Services.io.newURI("http://subdomain.pixi.me", null, null);
gSTSService.processStsHeader(uri, "max-age=0");
gSTSService.processStsHeader(uri, "max-age=0", 0);
// we received a header with "max-age=0", so we have "no information"
// regarding the sts state of subdomain.pixi.me specifically, but
// it is actually still an STS host, because of the preloaded pixi.me
@ -134,19 +134,19 @@ function test_part1() {
// |-- subdomain.pixi.me IS sts host
// | `-- another.subdomain.pixi.me IS sts host
// `-- sibling.pixi.me IS sts host
do_check_true(gSTSService.isStsHost("subdomain.pixi.me"));
do_check_true(gSTSService.isStsHost("sibling.pixi.me"));
do_check_true(gSTSService.isStsHost("another.subdomain.pixi.me"));
do_check_true(gSTSService.isStsHost("subdomain.pixi.me", 0));
do_check_true(gSTSService.isStsHost("sibling.pixi.me", 0));
do_check_true(gSTSService.isStsHost("another.subdomain.pixi.me", 0));
gSTSService.processStsHeader(uri, "max-age=1000");
gSTSService.processStsHeader(uri, "max-age=1000", 0);
// Here's what we have now:
// |-- pixi.me (in preload list, includes subdomains) IS sts host
// |-- subdomain.pixi.me (include subdomains is false) IS sts host
// | `-- another.subdomain.pixi.me IS NOT sts host
// `-- sibling.pixi.me IS sts host
do_check_true(gSTSService.isStsHost("subdomain.pixi.me"));
do_check_true(gSTSService.isStsHost("sibling.pixi.me"));
do_check_false(gSTSService.isStsHost("another.subdomain.pixi.me"));
do_check_true(gSTSService.isStsHost("subdomain.pixi.me", 0));
do_check_true(gSTSService.isStsHost("sibling.pixi.me", 0));
do_check_false(gSTSService.isStsHost("another.subdomain.pixi.me", 0));
// Test private browsing correctly interacts with removing preloaded sites.
// If we don't have the private browsing service, don't run those tests
@ -159,26 +159,28 @@ function test_part1() {
}
}
const IS_PRIVATE = Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
function test_private_browsing1() {
// sanity - crypto.cat is preloaded, includeSubdomains set
do_check_true(gSTSService.isStsHost("crypto.cat"));
do_check_true(gSTSService.isStsHost("a.b.c.subdomain.crypto.cat"));
do_check_true(gSTSService.isStsHost("crypto.cat", IS_PRIVATE));
do_check_true(gSTSService.isStsHost("a.b.c.subdomain.crypto.cat", IS_PRIVATE));
var uri = Services.io.newURI("http://crypto.cat", null, null);
gSTSService.processStsHeader(uri, "max-age=0");
do_check_false(gSTSService.isStsHost("crypto.cat"));
do_check_false(gSTSService.isStsHost("a.b.subdomain.crypto.cat"));
gSTSService.processStsHeader(uri, "max-age=0", IS_PRIVATE);
do_check_false(gSTSService.isStsHost("crypto.cat", IS_PRIVATE));
do_check_false(gSTSService.isStsHost("a.b.subdomain.crypto.cat", IS_PRIVATE));
// check adding it back in
gSTSService.processStsHeader(uri, "max-age=1000");
do_check_true(gSTSService.isStsHost("crypto.cat"));
gSTSService.processStsHeader(uri, "max-age=1000", IS_PRIVATE);
do_check_true(gSTSService.isStsHost("crypto.cat", IS_PRIVATE));
// but no includeSubdomains this time
do_check_false(gSTSService.isStsHost("b.subdomain.crypto.cat"));
do_check_false(gSTSService.isStsHost("b.subdomain.crypto.cat", IS_PRIVATE));
// do the hokey-pokey...
gSTSService.processStsHeader(uri, "max-age=0");
do_check_false(gSTSService.isStsHost("crypto.cat"));
do_check_false(gSTSService.isStsHost("subdomain.crypto.cat"));
gSTSService.processStsHeader(uri, "max-age=0", IS_PRIVATE);
do_check_false(gSTSService.isStsHost("crypto.cat", IS_PRIVATE));
do_check_false(gSTSService.isStsHost("subdomain.crypto.cat", IS_PRIVATE));
// TODO unfortunately we don't have a good way to know when an entry
// has expired in the permission manager, so we can't yet extend this test
@ -189,12 +191,12 @@ function test_private_browsing1() {
// a site on the preload list, and that header later expires. We need to
// then treat that host as no longer an sts host.)
// (sanity check first - this should be in the preload list)
do_check_true(gSTSService.isStsHost("logentries.com"));
do_check_true(gSTSService.isStsHost("logentries.com", IS_PRIVATE));
var uri = Services.io.newURI("http://logentries.com", null, null);
// according to the rfc, max-age can't be negative, but this is a great
// way to test an expired entry
gSTSService.processStsHeader(uri, "max-age=-1000");
do_check_false(gSTSService.isStsHost("logentries.com"));
gSTSService.processStsHeader(uri, "max-age=-1000", IS_PRIVATE);
do_check_false(gSTSService.isStsHost("logentries.com", IS_PRIVATE));
// if this test gets this far, it means there's a private browsing service
getPBSvc().privateBrowsingEnabled = false;
@ -202,13 +204,13 @@ function test_private_browsing1() {
function test_private_browsing2() {
// if this test gets this far, it means there's a private browsing service
do_check_true(gSTSService.isStsHost("crypto.cat"));
do_check_true(gSTSService.isStsHost("crypto.cat", 0));
// the crypto.cat entry has includeSubdomains set
do_check_true(gSTSService.isStsHost("subdomain.crypto.cat"));
do_check_true(gSTSService.isStsHost("subdomain.crypto.cat", 0));
// Now that we're out of private browsing mode, we need to make sure
// we've "forgotten" that we "forgot" this site's sts status.
do_check_true(gSTSService.isStsHost("logentries.com"));
do_check_true(gSTSService.isStsHost("logentries.com", 0));
run_next_test();
}