author | Sid Stamm <sstamm@mozilla.com> |
Wed, 06 Oct 2010 10:07:39 -0700 | |
changeset 61189 | 57db6ae6d83266e95f8d462520e0bc7d7868a9fc |
parent 61188 | 2221636653f7704f5a53c36aa7195b4adfaa3374 |
child 61190 | 07e249383f3f61d1908d369c27aba94afc55c2be |
push id | unknown |
push user | unknown |
push date | unknown |
reviewers | ehsan, dveditz, blocking-betaN |
bugs | 557598 |
milestone | 2.0b10pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/netwerk/base/public/nsIStrictTransportSecurityService.idl +++ b/netwerk/base/public/nsIStrictTransportSecurityService.idl @@ -33,16 +33,17 @@ * 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 "nsISupports.idl" interface nsIURI; +interface nsIObserver; interface nsIHttpChannel; [scriptable, uuid(16955eee-6c48-4152-9309-c42a465138a1)] interface nsIStrictTransportSecurityService : nsISupports { /** * Parses a given HTTP header and records the results internally. * The format of the STS header is defined by the STS specification:
--- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -905,23 +905,16 @@ nsHttpChannel::ShouldSSLProxyResponseCon * * @return NS_ERROR_FAILURE if there's security information missing even though * it's an HTTPS connection. */ nsresult nsHttpChannel::ProcessSTSHeader() { nsresult rv; - - // We need to check private browsing mode here since some permissions are - // allowed to be tweaked when private browsing mode is enabled, but STS is - // not allowed to operate at all in PBM. - if (gHttpHandler->InPrivateBrowsingMode()) - return NS_OK; - PRBool isHttps = PR_FALSE; rv = mURI->SchemeIs("https", &isHttps); NS_ENSURE_SUCCESS(rv, rv); // If this channel is not loading securely, STS doesn't do anything. // The upgrade to HTTPS takes place earlier in the channel load process. if (!isHttps) return NS_OK;
--- a/security/manager/boot/src/nsStrictTransportSecurityService.cpp +++ b/security/manager/boot/src/nsStrictTransportSecurityService.cpp @@ -35,16 +35,17 @@ * * ***** END LICENSE BLOCK ***** */ #include "plstr.h" #include "prlog.h" #include "prprf.h" #include "nsCRTGlue.h" #include "nsIPermissionManager.h" +#include "nsIPrivateBrowsingService.h" #include "nsISSLStatus.h" #include "nsISSLStatusProvider.h" #include "nsStrictTransportSecurityService.h" #include "nsIURI.h" #include "nsInt64.h" #include "nsNetUtil.h" #include "nsThreadUtils.h" #include "nsStringGlue.h" @@ -56,35 +57,72 @@ PRLogModuleInfo *gSTSLog = PR_NewLogModu #define STSLOG(args) PR_LOG(gSTSLog, 4, args) #define STS_PARSER_FAIL_IF(test,args) \ if (test) { \ STSLOG(args); \ return NS_ERROR_FAILURE; \ } + +//////////////////////////////////////////////////////////////////////////////// + +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) +{ +} + +//////////////////////////////////////////////////////////////////////////////// + + nsStrictTransportSecurityService::nsStrictTransportSecurityService() + : mInPrivateMode(PR_FALSE) { } nsStrictTransportSecurityService::~nsStrictTransportSecurityService() { } -NS_IMPL_THREADSAFE_ISUPPORTS1(nsStrictTransportSecurityService, +NS_IMPL_THREADSAFE_ISUPPORTS2(nsStrictTransportSecurityService, + nsIObserver, nsIStrictTransportSecurityService) nsresult nsStrictTransportSecurityService::Init() { nsresult rv; 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); + + 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; + return NS_OK; } nsresult nsStrictTransportSecurityService::GetHost(nsIURI *aURI, nsACString &aResult) { nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI); if (!innerURI) return NS_ERROR_FAILURE; @@ -107,54 +145,62 @@ nsStrictTransportSecurityService::SetSts 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 - mPermMgr->Add(aSourceURI, STS_PERMISSION, + STSLOG(("STS: maxage permission SET, adding permission\n")); + nsresult rv = AddPermission(aSourceURI, + STS_PERMISSION, (PRUint32) nsIPermissionManager::ALLOW_ACTION, (PRUint32) nsIPermissionManager::EXPIRE_TIME, expiretime); - STSLOG(("STS: set maxage permission\n")); + NS_ENSURE_SUCCESS(rv, rv); if (includeSubdomains) { // record entry for this host with include subdomains in the permissions manager - mPermMgr->Add(aSourceURI, STS_SUBDOMAIN_PERMISSION, - (PRUint32) nsIPermissionManager::ALLOW_ACTION, - (PRUint32) nsIPermissionManager::EXPIRE_TIME, - expiretime); - STSLOG(("STS: set subdomains permission\n")); + 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); } else { // !includeSubdomains nsCAutoString hostname; - nsresult rv = GetHost(aSourceURI, hostname); + rv = GetHost(aSourceURI, hostname); NS_ENSURE_SUCCESS(rv, rv); - mPermMgr->Remove(hostname, STS_SUBDOMAIN_PERMISSION); + STSLOG(("STS: subdomains permission UNSET, removing any existing ones\n")); + rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION); + NS_ENSURE_SUCCESS(rv, rv); } 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); - mPermMgr->Remove(hostname, STS_PERMISSION); + rv = RemovePermission(hostname, STS_PERMISSION); + NS_ENSURE_SUCCESS(rv, rv); STSLOG(("STS: deleted maxage permission\n")); - mPermMgr->Remove(hostname, STS_SUBDOMAIN_PERMISSION); + rv = RemovePermission(hostname, STS_SUBDOMAIN_PERMISSION); + NS_ENSURE_SUCCESS(rv, rv); STSLOG(("STS: deleted subdomains permission\n")); return NS_OK; } NS_IMETHODIMP nsStrictTransportSecurityService::ProcessStsHeader(nsIURI* aSourceURI, const char* aHeader) @@ -290,21 +336,22 @@ nsStrictTransportSecurityService::IsStsU { // 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. - rv = mPermMgr->TestExactPermission(aURI, STS_PERMISSION, &permExact); + rv = TestPermission(aURI, STS_PERMISSION, &permExact, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); + // If any super-domain has the includeSubdomains permission, this is an // STS host. - rv = mPermMgr->TestPermission(aURI, STS_SUBDOMAIN_PERMISSION, &permGeneral); + rv = TestPermission(aURI, STS_SUBDOMAIN_PERMISSION, &permGeneral, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); *aResult = ((permExact == nsIPermissionManager::ALLOW_ACTION) || (permGeneral == nsIPermissionManager::ALLOW_ACTION)); return NS_OK; } @@ -336,8 +383,244 @@ nsStrictTransportSecurityService::Should rv = sslstat->GetIsUntrusted(&trustcheck); NS_ENSURE_SUCCESS(rv, rv); tlsIsBroken = tlsIsBroken || trustcheck; *aResult = tlsIsBroken; return NS_OK; } + +//------------------------------------------------------------ +// 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, + PRBool testExact) +{ + // 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; +}
--- a/security/manager/boot/src/nsStrictTransportSecurityService.h +++ b/security/manager/boot/src/nsStrictTransportSecurityService.h @@ -38,36 +38,128 @@ /** * This wraps nsSimpleURI so that all calls to it are done on the main thread. */ #ifndef __nsStrictTransportSecurityService_h__ #define __nsStrictTransportSecurityService_h__ #include "nsIStrictTransportSecurityService.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" #include "nsIPermissionManager.h" #include "nsCOMPtr.h" #include "nsIURI.h" #include "nsString.h" +#include "nsTHashtable.h" // {16955eee-6c48-4152-9309-c42a465138a1} #define NS_STRICT_TRANSPORT_SECURITY_CID \ {0x16955eee, 0x6c48, 0x4152, \ {0x93, 0x09, 0xc4, 0x2a, 0x46, 0x51, 0x38, 0xa1} } +//////////////////////////////////////////////////////////////////////////////// +// nsSTSHostEntry - similar to the nsHostEntry class in +// nsPermissionManager.cpp, but specific to private-mode caching of STS +// permissions. +// +// Each nsSTSHostEntry contains: +// - Expiry time +// - Deleted flag (boolean, default PR_FALSE) +// - Subdomains flag (boolean, default PR_FALSE) +// +// The existence of the nsSTSHostEntry implies STS state is set for the given +// host -- unless the deleted flag is set, in which case not only is the STS +// state not set for the host, but any permission actually present in the +// permission manager should be ignored. +// +// Note: Only one expiry time is stored since the subdomains and STS +// permissions are both encountered at the same time in the HTTP header; if the +// includeSubdomains directive isn't present in the header, it means to delete +// the permission, so the subdomains flag in the nsSTSHostEntry means both that +// the permission doesn't exist and any permission in the real permission +// manager should be ignored since newer information about it has been +// encountered in private browsing mode. +// +// Note: If there's a permission set by the user (EXPIRE_NEVER), STS is not set +// for the host (including the subdomains permission) when the header is +// encountered. Furthermore, any user-set permissions are stored persistently +// and can't be shadowed. + +class nsSTSHostEntry : public PLDHashEntryHdr +{ + public: + explicit nsSTSHostEntry(const char* aHost); + explicit nsSTSHostEntry(const nsSTSHostEntry& toCopy); + + nsCString mHost; + PRInt64 mExpireTime; + PRPackedBool mDeleted; + PRPackedBool mIncludeSubdomains; + + // Hash methods + typedef const char* KeyType; + typedef const char* KeyTypePointer; + + KeyType GetKey() const + { + return mHost.get(); + } + + PRBool KeyEquals(KeyTypePointer aKey) const + { + return !strcmp(mHost.get(), aKey); + } + + static KeyTypePointer KeyToPointer(KeyType aKey) + { + return aKey; + } + + static PLDHashNumber HashKey(KeyTypePointer aKey) + { + return PL_DHashStringKey(nsnull, aKey); + } + + // force the hashtable to use the copy constructor. + enum { ALLOW_MEMMOVE = PR_FALSE }; +}; +//////////////////////////////////////////////////////////////////////////////// + class nsStrictTransportSecurityService : public nsIStrictTransportSecurityService + , public nsIObserver { public: NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER NS_DECL_NSISTRICTTRANSPORTSECURITYSERVICE nsStrictTransportSecurityService(); nsresult Init(); virtual ~nsStrictTransportSecurityService(); private: nsresult GetHost(nsIURI *aURI, nsACString &aResult); nsresult SetStsState(nsIURI* aSourceURI, PRInt64 maxage, PRBool includeSubdomains); nsresult ProcessStsHeaderMutating(nsIURI* aSourceURI, char* aHeader); + + // private-mode-preserving permission manager overlay functions + nsresult AddPermission(nsIURI *aURI, + const char *aType, + PRUint32 aPermission, + PRUint32 aExpireType, + PRInt64 aExpireTime); + nsresult RemovePermission(const nsCString &aHost, + const char *aType); + nsresult TestPermission(nsIURI *aURI, + const char *aType, + PRUint32 *aPermission, + PRBool testExact); + + // cached services nsCOMPtr<nsIPermissionManager> mPermMgr; + nsCOMPtr<nsIObserverService> mObserverService; + + PRBool mInPrivateMode; + nsTHashtable<nsSTSHostEntry> mPrivateModeHostTable; }; #endif // __nsStrictTransportSecurityService_h__
--- a/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in @@ -44,14 +44,17 @@ relativesrcdir = security/ssl/stricttran include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES = \ plain_bootstrap.html \ plain_bootstrap.html^headers^ \ subdom_bootstrap.html \ subdom_bootstrap.html^headers^ \ + nosts_bootstrap.html \ + nosts_bootstrap.html^headers^ \ verify.sjs \ test_stricttransportsecurity.html \ + test_sts_privatebrowsing.html \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html @@ -0,0 +1,57 @@ +<!-- ***** 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 LGPL or the GPL. 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 ***** --> + +<!DOCTYPE HTML> +<html> + <head> + <title>STS test iframe</title> + <script> + var self = window; + window.addEventListener("load", function() { + self.parent.postMessage("BOOTSTRAP plain", "http://mochi.test:8888"); + }, false); + </script> + </head> + <body> + <!-- This frame should be loaded over HTTPS to set the STS header. --> + This frame was loaded using + <script> + document.write(document.location.protocol); + </script> + and set the STS header to force this site and allow subdomain upgrading. + </body> +</html>
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^ @@ -0,0 +1,1 @@ +Cache-Control: no-cache
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing.html @@ -0,0 +1,269 @@ +<!-- ***** 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 LGPL or the GPL. 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 ***** --> + +<!DOCTYPE HTML> +<html> +<head> + <title>opens additional content that should be converted to https</title> + <script type="text/javascript" src="/MochiKit/packed.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <script class="testbody" type="text/javascript"> + SimpleTest.waitForExplicitFinish(); + + const STSPATH = "/tests/security/ssl/stricttransportsecurity"; + + const NUM_TEST_FRAMES = 4; + var testframes = { + 'samedom': + {'url': "http://example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'SECURE', + 'subdom': 'SECURE', + 'nosts': 'INSECURE'}}, + 'subdom': + {'url': "http://test1.example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'INSECURE', + 'subdom': 'SECURE', + 'nosts': 'INSECURE'}}, + 'otherdom': + {'url': "http://example.org" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'INSECURE', + 'subdom': 'INSECURE', + 'nosts': 'INSECURE'}}, + 'alreadysecure': + {'url': "https://test2.example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'SECURE', + 'subdom': 'SECURE', + 'nosts': 'SECURE'}}, + }; + + // This is how many sub-tests (testframes) in each round. + // When the round begins, this will be initialized. + var testsleftinround = 0; + var currentround = ""; + + var _PBSvc = null; + var _PrefSvc = null; + + function _getPBService() { + if (_PBSvc) + return _PBSvc; + + // not all apps will have the private browsing service. + try { + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + _PBSvc = Components.classes["@mozilla.org/privatebrowsing;1"] + .getService(Components.interfaces.nsIPrivateBrowsingService); + return _PBSvc; + } catch (e) {} + return null; + } + function _getPrefService() { + if (_PrefSvc) + return _PrefSvc; + + // not all apps will have the private browsing service. + try { + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + _PrefSvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService) + .QueryInterface(Components.interfaces.nsIPrefBranch2); + return _PrefSvc; + } catch (e) {} + return null; + } + + function startRound(round) { + currentround = round; + testsleftinround = NUM_TEST_FRAMES; + dump("TESTS LEFT IN ROUND: " + testsleftinround + "\n"); + var frame = document.createElement("iframe"); + frame.setAttribute('id', 'ifr_bootstrap'); + frame.setAttribute('src', "https://example.com" + STSPATH + + "/" + round + "_bootstrap.html"); + document.body.appendChild(frame); + } + + function loadVerifyFrames(round) { + for (var test in testframes) { + var frame = document.createElement("iframe"); + frame.setAttribute('id', 'ifr_' + test); + frame.setAttribute('src', testframes[test].url + '?id=' + test); + document.body.appendChild(frame); + } + } + + /* Messages received are in this format: + * (BOOTSTRAP|SECURE|INSECURE) testid + * For example: "BOOTSTRAP subdom" + * or: "INSECURE otherdom" + */ + function onMessageReceived(event) { + + // otherwise, it's a test result + var result = event.data.split(/\s+/); + if (result.length != 2) { + SimpleTest.ok(false, event.data); + return; + } + + if (result[0] === "BOOTSTRAP") { + loadVerifyFrames(currentround); + return; + } + + // check if the result (SECURE/INSECURE) is expected for this round/test + // combo + dump_STSState(); + dump( "*** in ROUND " + currentround + + ", test " + result[1] + + " is " + result[0] + "\n"); + SimpleTest.is(result[0], testframes[result[1]].expected[currentround], + "in ROUND " + currentround + + ", test " + result[1]); + testsleftinround--; + + // if this round is complete... + if (testsleftinround < 1) { + dump("DONE WITH ROUND " + currentround + "\n"); + // remove all the iframes in the document + document.body.removeChild(document.getElementById('ifr_bootstrap')); + for (var test in testframes) + document.body.removeChild(document.getElementById('ifr_' + test)); + currentround = ""; + + // And advance to the next test. + // Defer this so it doesn't muck with the stack too much. + SimpleTest.executeSoon(nextTest); + } + } + + function test_sts_before_private_mode() { + dump_STSState(); + dump("*** not in private browsing mode\n"); + startRound('plain'); + } + + function test_sts_in_private_mode() { + dump_STSState(); + dump("*** Entering private browsing mode\n"); + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + _getPrefService().setBoolPref("browser.privatebrowsing.keep_current_session", + true); + _getPBService().privateBrowsingEnabled = true; + dump("*** ... done\n"); + dump_STSState(); + startRound('subdom'); + } + + function test_sts_after_exiting_private_mode() { + dump_STSState(); + dump("*** Exiting private browsing mode\n"); + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + _getPBService().privateBrowsingEnabled = false; + _getPrefService().clearUserPref("browser.privatebrowsing.keep_current_session"); + dump("*** ... done\n"); + dump_STSState(); + startRound('nosts'); + } + + function clean_up_sts_state() { + // erase all signs that this test ran. + dump("*** Cleaning up STS data.\n"); + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + const Cc = Components.classes; + const Ci = Components.interfaces; + 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); + dump_STSState(); + SimpleTest.executeSoon(nextTest); + } + +function dump_STSState() { + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + var stss = Components.classes["@mozilla.org/stsservice;1"] + .getService(Components.interfaces.nsIStrictTransportSecurityService); + dump("*** State of example.com: " + stss.isStsHost("example.com") + "\n"); +} + + // these are executed in the order presented. + // 0. test that STS works before entering private browsing mode. + // (load sts-bootstrapped "plain" tests) + // ... clear any STS data ... + // 1. test that STS works in private browsing mode + // (load sts-bootstrapped "subdomain" tests) + // 2. test that after exiting private browsing, STS data is forgotten + // (verified with non-sts-bootstrapped pages) + var tests = []; + { // skip these tests if there's no private mode support + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + if ("@mozilla.org/privatebrowsing;1" in Components.classes) { + tests = [ + test_sts_before_private_mode, + clean_up_sts_state, + test_sts_in_private_mode, + test_sts_after_exiting_private_mode, + clean_up_sts_state, + ]; + } + } + + function nextTest() { + if (tests.length) + SimpleTest.executeSoon(tests.shift()); + else + SimpleTest.executeSoon(SimpleTest.finish); + } + + // listen for calls back from the sts-setting iframe and then + // the verification frames. + window.addEventListener("message", onMessageReceived, false); + window.addEventListener('load', nextTest, false); + + </script> +</head> + +<body> + This test will load some iframes and do some tests. + +</body> +</html>