Bug 495115 - Implement Strict-Transport-Security to allow sites to specify HTTPS-only connections, r=kaie+honzab+bjarne, a=betaN+

This commit is contained in:
Sid Stamm 2010-06-03 14:03:17 -07:00
parent e36a2a1d5a
commit a781965b6d
24 changed files with 1530 additions and 31 deletions

View File

@ -122,6 +122,13 @@
toggle('technicalContent');
toggle('expertContent');
}
// if this is a Strict-Transport-Security host and the cert
// is bad, don't allow overrides (STS Spec section 7.3).
if (getCSSClass() == "badStsCert") {
var ec = document.getElementById('expertContent');
document.getElementById('errorLongContent').removeChild(ec);
}
var tech = document.getElementById("technicalContentText");
if (tech)

View File

@ -161,6 +161,7 @@
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIWebBrowserChrome2.h"
#include "nsITabChild.h"
#include "nsIStrictTransportSecurityService.h"
// Editor-related
#include "nsIEditingSession.h"
@ -3781,13 +3782,27 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
if (!messageStr.IsEmpty()) {
if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
error.AssignLiteral("nssBadCert");
// if this is a Strict-Transport-Security host and the cert
// is bad, don't allow overrides (STS Spec section 7.3).
nsCOMPtr<nsIStrictTransportSecurityService> stss =
do_GetService(NS_STSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isStsHost = PR_FALSE;
rv = stss->IsStsURI(aURI, &isStsHost);
NS_ENSURE_SUCCESS(rv, rv);
if (isStsHost)
cssClass.AssignLiteral("badStsCert");
PRBool expert = PR_FALSE;
mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert",
&expert);
if (expert) {
cssClass.AssignLiteral("expertBadCert");
}
// See if an alternate cert error page is registered
nsXPIDLCString alternateErrorPage;
mPrefs->GetCharPref("security.alternate_certificate_error_page",

View File

@ -137,6 +137,7 @@ XPIDLSRCS = \
nsINetUtil.idl \
nsIProxiedChannel.idl \
nsIRandomGenerator.idl \
nsIStrictTransportSecurityService.idl \
nsIURIWithPrincipal.idl \
nsIURIClassifier.idl \
nsIRedirectResultListener.idl \

View File

@ -0,0 +1,105 @@
/* ***** 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 "nsISupports.idl"
interface nsIURI;
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:
* http://tools.ietf.org/html/draft-hodges-strict-transport-sec
* and allows a host to specify that future requests on port 80 should be
* upgraded to HTTPS.
*
* @param aSourceURI the URI of the resource with the HTTP header.
* @param aHeader the HTTP response header specifying STS data.
* @return NS_OK if it succeeds
* NS_ERROR_FAILURE if it can't be parsed
* NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
* if there are unrecognized tokens in the header.
*/
void processStsHeader(in nsIURI aSourceURI,
in string aHeader);
/**
* 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.
*/
void removeStsState(in nsIURI aURI);
/**
* Checks if the given security info is for an STS host with a broken
* transport layer (certificate errors like invalid CN).
*/
PRBool shouldIgnoreStsHeader(in nsISupports aSecurityInfo);
/**
* Checks whether or not the given hostname has STS state set.
* The host is an STS host if either it has the STS permission, or one of
* its super-domains has an STS "includeSubdomains" permission set.
*
* @param aHost the hostname (punycode) to query for STS state.
*/
PRBool isStsHost(in string aHost);
/**
* Checks whether or not the URI's hostname has STS state set.
* The URI is an STS URI if either the host has the STS permission, or one
* of its super-domains has an STS "includeSubdomains" permission set.
* NOTE: this function makes decisions based only on the scheme and
* host contained in the URI, and disregards other portions of the URI
* such as path and port.
*
* @param aURI the URI to query for STS state.
*/
PRBool isStsURI(in nsIURI aURI);
};
%{C++
#define NS_STSSERVICE_CONTRACTID "@mozilla.org/stsservice;1"
#define NS_STSSERVICE_CLASSNAME "stsservice"
#define STS_PERMISSION "sts/use"
#define STS_SUBDOMAIN_PERMISSION "sts/subd"
%}

View File

@ -62,6 +62,7 @@
#include "nsPrintfCString.h"
#include "nsNetUtil.h"
#include "prprf.h"
#include "prnetdb.h"
#include "nsEscape.h"
#include "nsInt64.h"
#include "nsStreamUtils.h"
@ -83,7 +84,6 @@ class AutoRedirectVetoNotifier
public:
AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel) {}
~AutoRedirectVetoNotifier() {ReportRedirectResult(false);}
void DontReport() {mChannel = nsnull;}
void RedirectSucceeded() {ReportRedirectResult(true);}
private:
@ -97,6 +97,8 @@ AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
if (!mChannel)
return;
mChannel->mRedirectChannel = nsnull;
nsCOMPtr<nsIRedirectResultListener> vetoHook;
NS_QueryNotificationCallbacks(mChannel,
NS_GET_IID(nsIRedirectResultListener),
@ -194,6 +196,29 @@ nsHttpChannel::Connect(PRBool firstTime)
LOG(("nsHttpChannel::Connect [this=%p]\n", this));
// Even if we're in private browsing mode, we still enforce existing STS
// data (it is read-only).
// if the connection is not using SSL and either the exact host matches or
// a superdomain wants to force HTTPS, do it.
PRBool usingSSL = PR_FALSE;
rv = mURI->SchemeIs("https", &usingSSL);
NS_ENSURE_SUCCESS(rv,rv);
if (!usingSSL) {
// enforce Strict-Transport-Security
nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
NS_ENSURE_TRUE(stss, NS_ERROR_OUT_OF_MEMORY);
PRBool isStsHost = PR_FALSE;
rv = stss->IsStsURI(mURI, &isStsHost);
NS_ENSURE_SUCCESS(rv, rv);
if (isStsHost) {
LOG(("nsHttpChannel::Connect() STS permissions found\n"));
return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
}
}
// ensure that we are using a valid hostname
if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
return NS_ERROR_UNKNOWN_HOST;
@ -884,6 +909,94 @@ nsHttpChannel::ShouldSSLProxyResponseContinue(PRUint32 httpStatus)
return PR_FALSE;
}
/**
* Decide whether or not to remember Strict-Transport-Security, and whether
* or not to enforce channel integrity.
*
* @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;
nsCAutoString asciiHost;
rv = mURI->GetAsciiHost(asciiHost);
NS_ENSURE_SUCCESS(rv, rv);
// If the channel is not a hostname, but rather an IP, STS doesn't do
// anything.
PRNetAddr hostAddr;
if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr))
return NS_OK;
nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
NS_ENSURE_TRUE(stss, NS_ERROR_OUT_OF_MEMORY);
// Check the trustworthiness of the channel (are there any cert errors?)
// If there are certificate errors, we still load the data, we just ignore
// any STS headers that are present.
NS_ENSURE_TRUE(mSecurityInfo, NS_ERROR_FAILURE);
PRBool tlsIsBroken = PR_FALSE;
rv = stss->ShouldIgnoreStsHeader(mSecurityInfo, &tlsIsBroken);
NS_ENSURE_SUCCESS(rv, rv);
// If this was already an STS host, the connection should have been aborted
// by the bad cert handler in the case of cert errors. If it didn't abort the connection,
// there's probably something funny going on.
// If this wasn't an STS host, errors are allowed, but no more STS processing
// will happen during the session.
PRBool wasAlreadySTSHost;
rv = stss->IsStsURI(mURI, &wasAlreadySTSHost);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!(wasAlreadySTSHost && tlsIsBroken),
"connection should have been aborted by nss-bad-cert-handler");
// Any STS header is ignored if the channel is not trusted due to
// certificate errors (STS Spec 7.1) -- there is nothing else to do, and
// the load may progress.
if (tlsIsBroken) {
LOG(("STS: Transport layer is not trustworthy, ignoring "
"STS headers and continuing load\n"));
return NS_OK;
}
// If there's a STS header, process it (STS Spec 7.1). At this point in
// processing, the channel is trusted, so the header should not be ignored.
const nsHttpAtom atom = nsHttp::ResolveAtom("Strict-Transport-Security");
nsCAutoString stsHeader;
rv = mResponseHead->GetHeader(atom, stsHeader);
if (rv == NS_ERROR_NOT_AVAILABLE) {
LOG(("STS: No STS header, continuing load.\n"));
return NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
rv = stss->ProcessStsHeader(mURI, stsHeader.get());
if (NS_FAILED(rv)) {
LOG(("STS: Failed to parse STS header, continuing load.\n"));
return NS_OK;
}
return NS_OK;
}
nsresult
nsHttpChannel::ProcessResponse()
{
@ -897,6 +1010,10 @@ nsHttpChannel::ProcessResponse()
!ShouldSSLProxyResponseContinue(httpStatus))
return ProcessFailedSSLConnect(httpStatus);
// If STS data is present, process it here.
rv = ProcessSTSHeader();
NS_ENSURE_SUCCESS(rv, rv);
// notify "http-on-examine-response" observers
gHttpHandler->OnExamineResponse(this);
@ -1233,6 +1350,143 @@ nsHttpChannel::ContinueHandleAsyncReplaceWithProxy(nsresult status)
return NS_OK;
}
void
nsHttpChannel::HandleAsyncRedirectChannelToHttps()
{
NS_PRECONDITION(!mPendingAsyncCallOnResume, "How did that happen?");
if (mSuspendCount) {
LOG(("Waiting until resume to do async redirect to https [this=%p]\n", this));
mPendingAsyncCallOnResume = &nsHttpChannel::HandleAsyncRedirectChannelToHttps;
return;
}
nsresult rv = AsyncRedirectChannelToHttps();
if (NS_FAILED(rv))
ContinueAsyncRedirectChannelToHttps(rv);
}
nsresult
nsHttpChannel::AsyncRedirectChannelToHttps()
{
nsresult rv = NS_OK;
LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsIURI> upgradedURI;
rv = mURI->Clone(getter_AddRefs(upgradedURI));
NS_ENSURE_SUCCESS(rv,rv);
upgradedURI->SetScheme(NS_LITERAL_CSTRING("https"));
PRInt32 oldPort = -1;
rv = mURI->GetPort(&oldPort);
if (NS_FAILED(rv)) return rv;
// Keep any nonstandard ports so only the scheme is changed.
// For example:
// http://foo.com:80 -> https://foo.com:443
// http://foo.com:81 -> https://foo.com:81
if (oldPort == 80 || oldPort == -1)
upgradedURI->SetPort(-1);
else
upgradedURI->SetPort(oldPort);
nsCOMPtr<nsIIOService> ioService;
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
NS_ENSURE_SUCCESS(rv, rv);
rv = ioService->NewChannelFromURI(upgradedURI, getter_AddRefs(newChannel));
NS_ENSURE_SUCCESS(rv, rv);
rv = SetupReplacementChannel(upgradedURI, newChannel, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
// Inform consumers about this fake redirect
mRedirectChannel = newChannel;
PRUint32 flags = nsIChannelEventSink::REDIRECT_PERMANENT;
PushRedirectAsyncFunc(
&nsHttpChannel::ContinueAsyncRedirectChannelToHttps);
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
if (NS_SUCCEEDED(rv))
rv = WaitForRedirectCallback();
if (NS_FAILED(rv)) {
AutoRedirectVetoNotifier notifier(this);
PopRedirectAsyncFunc(
&nsHttpChannel::ContinueAsyncRedirectChannelToHttps);
}
return rv;
}
nsresult
nsHttpChannel::ContinueAsyncRedirectChannelToHttps(nsresult rv)
{
AutoRedirectVetoNotifier notifier(this);
if (NS_FAILED(rv)) {
// Fill the failure status here, the update to https had been vetoed
// but from the security reasons we have to discard the whole channel
// load.
mStatus = rv;
}
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
if (NS_FAILED(rv)) {
// We have to manually notify the listener because there is not any pump
// that would call our OnStart/StopRequest after resume from waiting for
// the redirect callback.
DoNotifyListener();
return rv;
}
// Make sure to do this _after_ calling OnChannelRedirect
mRedirectChannel->SetOriginalURI(mOriginalURI);
// And now, notify observers the deprecated way
nsCOMPtr<nsIHttpEventSink> httpEventSink;
GetCallback(httpEventSink);
if (httpEventSink) {
// NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
// versions.
rv = httpEventSink->OnRedirect(this, mRedirectChannel);
if (NS_FAILED(rv)) {
mStatus = rv;
DoNotifyListener();
return rv;
}
}
// open new channel
rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
if (NS_FAILED(rv)) {
mStatus = rv;
DoNotifyListener();
return rv;
}
mStatus = NS_BINDING_REDIRECTED;
notifier.RedirectSucceeded();
// disconnect from the old listeners...
mListener = nsnull;
mListenerContext = nsnull;
// ...and the old callbacks
mCallbacks = nsnull;
mProgressSink = nsnull;
return rv;
}
nsresult
nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
{
@ -1261,7 +1515,6 @@ nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
if (NS_FAILED(rv)) {
AutoRedirectVetoNotifier notifier(this);
PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
mRedirectChannel = nsnull;
}
return rv;
@ -1282,7 +1535,6 @@ nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv)
// open new channel
rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
mRedirectChannel = nsnull;
if (NS_FAILED(rv))
return rv;
@ -1629,7 +1881,6 @@ nsHttpChannel::ProcessFallback(PRBool *waitingForRedirectCallback)
if (NS_FAILED(rv)) {
AutoRedirectVetoNotifier notifier(this);
PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
mRedirectChannel = nsnull;
return rv;
}
@ -1653,7 +1904,6 @@ nsHttpChannel::ContinueProcessFallback(nsresult rv)
mRedirectChannel->SetOriginalURI(mOriginalURI);
rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
mRedirectChannel = nsnull;
if (NS_FAILED(rv))
return rv;
@ -3067,7 +3317,6 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
if (NS_FAILED(rv)) {
AutoRedirectVetoNotifier notifier(this);
PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
mRedirectChannel = nsnull;
}
return rv;
@ -3102,7 +3351,6 @@ nsHttpChannel::ContinueProcessRedirection(nsresult rv)
// begin loading the new channel
rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
mRedirectChannel = nsnull;
if (NS_FAILED(rv))
return rv;

View File

@ -67,6 +67,7 @@
#include "nsIAsyncVerifyRedirectCallback.h"
class nsAHttpConnection;
class AutoRedirectVetoNotifier;
using namespace mozilla::net;
@ -257,6 +258,18 @@ private:
nsresult DoAuthRetry(nsAHttpConnection *);
PRBool MustValidateBasedOnQueryUrl();
void HandleAsyncRedirectChannelToHttps();
nsresult AsyncRedirectChannelToHttps();
nsresult ContinueAsyncRedirectChannelToHttps(nsresult rv);
/**
* A function that takes care of reading STS headers and enforcing STS
* load rules. After a secure channel is erected, STS requires the channel
* to be trusted or any STS header data on the channel is ignored.
* This is called from ProcessResponse.
*/
nsresult ProcessSTSHeader();
private:
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsICancelable> mProxyRequest;
@ -303,6 +316,7 @@ private:
// cache entry.
nsCString mFallbackKey;
friend class AutoRedirectVetoNotifier;
nsCOMPtr<nsIURI> mRedirectURI;
nsCOMPtr<nsIChannel> mRedirectChannel;
PRUint32 mRedirectType;

View File

@ -130,6 +130,16 @@ private:
*/
nsresult ContinueOnAuthAvailable(const nsCSubstring& creds);
nsresult DoRedirectChannelToHttps();
/**
* A function that takes care of reading STS headers and enforcing STS
* load rules. After a secure channel is erected, STS requires the channel
* to be trusted or any STS header data on the channel is ignored.
* This is called from ProcessResponse.
*/
nsresult ProcessSTSHeader();
private:
nsIHttpAuthenticableChannel *mAuthChannel; // weak ref

View File

@ -251,6 +251,12 @@ nsHttpHandler::Init()
NeckoChild::InitNeckoChild();
#endif // MOZ_IPC
// figure out if we're starting in private browsing mode
nsCOMPtr<nsIPrivateBrowsingService> pbs =
do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
if (pbs)
pbs->GetPrivateBrowsingEnabled(&mInPrivateBrowsingMode);
InitUserAgentComponents();
// monitor some preference changes
@ -313,6 +319,7 @@ nsHttpHandler::Init()
mObserverService->AddObserver(this, "profile-change-net-restore", PR_TRUE);
mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
mObserverService->AddObserver(this, "net:clear-active-logins", PR_TRUE);
mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE);
}
StartPruneDeadConnectionsTimer();
@ -500,6 +507,14 @@ nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
return NS_OK;
}
nsIStrictTransportSecurityService*
nsHttpHandler::GetSTSService()
{
if (!mSTSService)
mSTSService = do_GetService(NS_STSSERVICE_CONTRACTID);
return mSTSService;
}
nsICookieService *
nsHttpHandler::GetCookieService()
{
@ -1771,7 +1786,13 @@ nsHttpHandler::Observe(nsISupports *subject,
else if (strcmp(topic, "net:clear-active-logins") == 0) {
mAuthCache.ClearAll();
}
else if (strcmp(topic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(data))
mInPrivateBrowsingMode = PR_TRUE;
else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(data))
mInPrivateBrowsingMode = PR_FALSE;
}
return NS_OK;
}

View File

@ -55,11 +55,13 @@
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIProxyObjectManager.h"
#include "nsIPrivateBrowsingService.h"
#include "nsIStreamConverterService.h"
#include "nsICacheSession.h"
#include "nsICookieService.h"
#include "nsIIDNService.h"
#include "nsITimer.h"
#include "nsIStrictTransportSecurityService.h"
class nsHttpConnectionInfo;
class nsHttpHeaderArray;
@ -167,6 +169,12 @@ public:
return mConnMgr->GetSocketThreadTarget(target);
}
// for anything that wants to know if we're in private browsing mode.
PRBool InPrivateBrowsingMode()
{
return mInPrivateBrowsingMode;
}
//
// The HTTP handler caches pointers to specific XPCOM services, and
// provides the following helper routines for accessing those services:
@ -174,6 +182,7 @@ public:
nsresult GetStreamConverterService(nsIStreamConverterService **);
nsresult GetIOService(nsIIOService** service);
nsICookieService * GetCookieService(); // not addrefed
nsIStrictTransportSecurityService * GetSTSService();
// Called by the channel before writing a request
void OnModifyRequest(nsIHttpChannel *chan)
@ -239,6 +248,7 @@ private:
nsCOMPtr<nsICookieService> mCookieService;
nsCOMPtr<nsIIDNService> mIDNConverter;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIStrictTransportSecurityService> mSTSService;
// the authentication credentials cache
nsHttpAuthCache mAuthCache;
@ -268,6 +278,9 @@ private:
PRUint8 mRedirectionLimit;
// cached value of whether or not the browser is in private browsing mode.
PRBool mInPrivateBrowsingMode;
// we'll warn the user if we load an URL containing a userpass field
// unless its length is less than this threshold. this warning is
// intended to protect the user against spoofing attempts that use

View File

@ -85,6 +85,10 @@ endif
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX))
CPP_UNIT_TESTS = \
TestSTSParser.cpp \
$(NULL)
include $(topsrcdir)/config/config.mk
LIBS = $(EXTRA_DSO_LIBS) \

View File

@ -0,0 +1,196 @@
/* ***** 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 ***** */
//#define MOZILLA_INTERNAL_API
#include "TestHarness.h"
#include <stdio.h>
#include "plstr.h"
#include "nsNetUtil.h"
#include "nsStringGlue.h"
#include "nsIStrictTransportSecurityService.h"
#include "nsIPermissionManager.h"
#define EXPECT_SUCCESS(rv,msg, ...) \
PR_BEGIN_MACRO \
if (NS_FAILED(rv)) { \
fail(msg,##__VA_ARGS__); \
return PR_FALSE; \
} \
PR_END_MACRO
#define EXPECT_FAILURE(rv,msg, ...) \
PR_BEGIN_MACRO \
if (NS_SUCCEEDED(rv)) { \
fail(msg,##__VA_ARGS__); \
return PR_FALSE; \
} \
PR_END_MACRO
#define REQUIRE_EQUAL(a,b,msg, ...) \
PR_BEGIN_MACRO \
if (a != b) { \
fail(msg, ##__VA_ARGS__); \
return PR_FALSE; \
} \
PR_END_MACRO
PRBool
TestSuccess(const char* hdr, PRBool extraTokens,
nsIStrictTransportSecurityService* stss,
nsIPermissionManager* pm)
{
nsCOMPtr<nsIURI> dummyUri;
nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html");
EXPECT_SUCCESS(rv, "Failed to create URI");
rv = stss->ProcessStsHeader(dummyUri, hdr);
EXPECT_SUCCESS(rv, "Failed to process valid header: %s", hdr);
if (extraTokens) {
REQUIRE_EQUAL(rv, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA,
"Extra tokens were expected when parsing, but were not encountered.");
} else {
REQUIRE_EQUAL(rv, NS_OK, "Unexpected tokens found during parsing.");
}
passed(hdr);
return PR_TRUE;
}
PRBool TestFailure(const char* hdr,
nsIStrictTransportSecurityService* stss,
nsIPermissionManager* pm)
{
nsCOMPtr<nsIURI> dummyUri;
nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html");
EXPECT_SUCCESS(rv, "Failed to create URI");
rv = stss->ProcessStsHeader(dummyUri, hdr);
EXPECT_FAILURE(rv, "Parsed invalid header: %s", hdr);
passed(hdr);
return PR_TRUE;
}
int
main(PRInt32 argc, char *argv[])
{
nsresult rv;
ScopedXPCOM xpcom("STS Parser Tests");
if (xpcom.failed())
return -1;
// grab handle to the service
nsCOMPtr<nsIStrictTransportSecurityService> stss;
stss = do_GetService("@mozilla.org/stsservice;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPermissionManager> pm;
pm = do_GetService("@mozilla.org/permissionmanager;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
int rv0, rv1;
nsTArray<PRBool> rvs(24);
// *** parsing tests
printf("*** Attempting to parse valid STS headers ...\n");
// SHOULD SUCCEED:
rvs.AppendElement(TestSuccess("max-age=100", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age =100", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess(" max-age=100", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age = 100 ", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age = 100 ", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("maX-aGe=100", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("MAX-age =100", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-AGE=100", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("Max-Age = 100 ", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("MAX-AGE = 100 ", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age=100;includeSubdomains", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age=100; includeSubdomains", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess(" max-age=100; includeSubdomains", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age = 100 ; includeSubdomains", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-age = 100 ; includeSubdomains", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("maX-aGe=100; includeSUBDOMAINS", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("MAX-age =100; includeSubDomains", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("max-AGE=100; iNcLuDeSuBdoMaInS", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("Max-Age = 100; includesubdomains ", PR_FALSE, stss, pm));
rvs.AppendElement(TestSuccess("INCLUDESUBDOMAINS;MaX-AgE = 100 ", PR_FALSE, stss, pm));
// these are weird tests, but are testing that some extended syntax is
// still allowed (but it is ignored)
rvs.AppendElement(TestSuccess("max-age=100randomstuffhere", PR_TRUE, stss, pm));
rvs.AppendElement(TestSuccess("max-age=100 includesubdomains", PR_TRUE, stss, pm));
rvs.AppendElement(TestSuccess("max-age=100 bar foo", PR_TRUE, stss, pm));
rvs.AppendElement(TestSuccess("max-age=100 ; includesubdomainsSomeStuff", PR_TRUE, stss, pm));
rv0 = rvs.Contains(PR_FALSE) ? 1 : 0;
if (rv0 == 0)
passed("Successfully Parsed STS headers with mixed case and LWS");
rvs.Clear();
// SHOULD FAIL:
printf("*** Attempting to parse invalid STS headers (should not parse)...\n");
// invalid max-ages
rvs.AppendElement(TestFailure("max-age ", stss, pm));
rvs.AppendElement(TestFailure("max-age=p", stss, pm));
rvs.AppendElement(TestFailure("max-age=*1p2", stss, pm));
rvs.AppendElement(TestFailure("max-age=.20032", stss, pm));
rvs.AppendElement(TestFailure("max-age=!20032", stss, pm));
rvs.AppendElement(TestFailure("max-age==20032", stss, pm));
// invalid headers
rvs.AppendElement(TestFailure("foobar", stss, pm));
rvs.AppendElement(TestFailure("maxage=100", stss, pm));
rvs.AppendElement(TestFailure("maxa-ge=100", stss, pm));
rvs.AppendElement(TestFailure("max-ag=100", stss, pm));
rvs.AppendElement(TestFailure("includesubdomains", stss, pm));
rvs.AppendElement(TestFailure(";", stss, pm));
rv1 = rvs.Contains(PR_FALSE) ? 1 : 0;
if (rv1 == 0)
passed("Avoided parsing invalid STS headers");
return (rv0 + rv1);
}

View File

@ -58,6 +58,7 @@ CPPSRCS = \
nsSecureBrowserUIImpl.cpp \
nsBOOTModule.cpp \
nsSecurityWarningDialogs.cpp \
nsStrictTransportSecurityService.cpp \
$(NULL)

View File

@ -41,19 +41,23 @@
#include "nsEntropyCollector.h"
#include "nsSecureBrowserUIImpl.h"
#include "nsSecurityWarningDialogs.h"
#include "nsStrictTransportSecurityService.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEntropyCollector)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecureBrowserUIImpl)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSecurityWarningDialogs, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStrictTransportSecurityService, Init)
NS_DEFINE_NAMED_CID(NS_ENTROPYCOLLECTOR_CID);
NS_DEFINE_NAMED_CID(NS_SECURITYWARNINGDIALOGS_CID);
NS_DEFINE_NAMED_CID(NS_SECURE_BROWSER_UI_CID);
NS_DEFINE_NAMED_CID(NS_STRICT_TRANSPORT_SECURITY_CID);
static const mozilla::Module::CIDEntry kBOOTCIDs[] = {
{ &kNS_ENTROPYCOLLECTOR_CID, false, NULL, nsEntropyCollectorConstructor },
{ &kNS_SECURITYWARNINGDIALOGS_CID, false, NULL, nsSecurityWarningDialogsConstructor },
{ &kNS_SECURE_BROWSER_UI_CID, false, NULL, nsSecureBrowserUIImplConstructor },
{ &kNS_STRICT_TRANSPORT_SECURITY_CID, false, NULL, nsStrictTransportSecurityServiceConstructor },
{ NULL }
};
@ -61,6 +65,7 @@ static const mozilla::Module::ContractIDEntry kBOOTContracts[] = {
{ NS_ENTROPYCOLLECTOR_CONTRACTID, &kNS_ENTROPYCOLLECTOR_CID },
{ NS_SECURITYWARNINGDIALOGS_CONTRACTID, &kNS_SECURITYWARNINGDIALOGS_CID },
{ NS_SECURE_BROWSER_UI_CONTRACTID, &kNS_SECURE_BROWSER_UI_CID },
{ NS_STSSERVICE_CONTRACTID, &kNS_STRICT_TRANSPORT_SECURITY_CID },
{ NULL }
};

View File

@ -0,0 +1,343 @@
/* ***** 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"
#include "nsISSLStatus.h"
#include "nsISSLStatusProvider.h"
#include "nsStrictTransportSecurityService.h"
#include "nsIURI.h"
#include "nsInt64.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; \
}
nsStrictTransportSecurityService::nsStrictTransportSecurityService()
{
}
nsStrictTransportSecurityService::~nsStrictTransportSecurityService()
{
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsStrictTransportSecurityService,
nsIStrictTransportSecurityService)
nsresult
nsStrictTransportSecurityService::Init()
{
nsresult rv;
mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
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,
PRBool includeSubdomains)
{
// 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
mPermMgr->Add(aSourceURI, STS_PERMISSION,
(PRUint32) nsIPermissionManager::ALLOW_ACTION,
(PRUint32) nsIPermissionManager::EXPIRE_TIME,
expiretime);
STSLOG(("STS: set maxage permission\n"));
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"));
} else { // !includeSubdomains
nsCAutoString hostname;
nsresult rv = GetHost(aSourceURI, hostname);
NS_ENSURE_SUCCESS(rv, rv);
mPermMgr->Remove(hostname, STS_SUBDOMAIN_PERMISSION);
}
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);
STSLOG(("STS: deleted maxage permission\n"));
mPermMgr->Remove(hostname, STS_SUBDOMAIN_PERMISSION);
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;
PRBool foundMaxAge = PR_FALSE;
PRBool foundUnrecognizedTokens = PR_FALSE;
PRBool includeSubdomains = PR_FALSE;
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
nsStrictTransportSecurityService::IsStsHost(const char* aHost, PRBool* aResult)
{
// 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
nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, PRBool* aResult)
{
// 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);
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);
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,
PRBool* aResult)
{
nsresult rv;
PRBool tlsIsBroken = PR_FALSE;
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);
PRBool trustcheck;
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;
}

View File

@ -0,0 +1,73 @@
/* ***** 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 ***** */
/**
* 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 "nsIPermissionManager.h"
#include "nsCOMPtr.h"
#include "nsIURI.h"
#include "nsString.h"
// {16955eee-6c48-4152-9309-c42a465138a1}
#define NS_STRICT_TRANSPORT_SECURITY_CID \
{0x16955eee, 0x6c48, 0x4152, \
{0x93, 0x09, 0xc4, 0x2a, 0x46, 0x51, 0x38, 0xa1} }
class nsStrictTransportSecurityService : public nsIStrictTransportSecurityService
{
public:
NS_DECL_ISUPPORTS
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);
nsCOMPtr<nsIPermissionManager> mPermMgr;
};
#endif // __nsStrictTransportSecurityService_h__

View File

@ -66,6 +66,7 @@
#include "nsIObjectOutputStream.h"
#include "nsRecentBadCerts.h"
#include "nsISSLCertErrorDialog.h"
#include "nsIStrictTransportSecurityService.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
@ -3485,32 +3486,59 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
remaining_display_errors = collected_errors;
nsCOMPtr<nsICertOverrideService> overrideService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
// it is fine to continue without the nsICertOverrideService
// Enforce Strict-Transport-Security for hosts that are "STS" hosts:
// connections must be dropped when there are any certificate errors
// (STS Spec section 7.3).
PRUint32 overrideBits = 0;
nsCOMPtr<nsIStrictTransportSecurityService> stss
= do_GetService(NS_STSSERVICE_CONTRACTID);
nsCOMPtr<nsIStrictTransportSecurityService> proxied_stss;
if (overrideService)
{
PRBool haveOverride;
PRBool isTemporaryOverride; // we don't care
nsrv = overrideService->HasMatchingOverride(hostString, port,
ix509,
&overrideBits,
&isTemporaryOverride,
&haveOverride);
if (NS_SUCCEEDED(nsrv) && haveOverride)
nsrv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIStrictTransportSecurityService),
stss, NS_PROXY_SYNC,
getter_AddRefs(proxied_stss));
NS_ENSURE_SUCCESS(nsrv, SECFailure);
// now grab the host name to pass to the STS Service
nsXPIDLCString hostName;
nsrv = infoObject->GetHostName(getter_Copies(hostName));
NS_ENSURE_SUCCESS(nsrv, SECFailure);
PRBool strictTransportSecurityEnabled;
nsrv = proxied_stss->IsStsHost(hostName, &strictTransportSecurityEnabled);
NS_ENSURE_SUCCESS(nsrv, SECFailure);
if (!strictTransportSecurityEnabled) {
nsCOMPtr<nsICertOverrideService> overrideService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
// it is fine to continue without the nsICertOverrideService
PRUint32 overrideBits = 0;
if (overrideService)
{
// remove the errors that are already overriden
remaining_display_errors -= overrideBits;
}
}
PRBool haveOverride;
PRBool isTemporaryOverride; // we don't care
if (!remaining_display_errors) {
// all errors are covered by override rules, so let's accept the cert
return SECSuccess;
nsrv = overrideService->HasMatchingOverride(hostString, port,
ix509,
&overrideBits,
&isTemporaryOverride,
&haveOverride);
if (NS_SUCCEEDED(nsrv) && haveOverride)
{
// remove the errors that are already overriden
remaining_display_errors -= overrideBits;
}
}
if (!remaining_display_errors) {
// all errors are covered by override rules, so let's accept the cert
return SECSuccess;
}
} else {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Strict-Transport-Security is violated: untrusted transport layer\n"));
}
// Ok, this is a full stop.

View File

@ -45,6 +45,7 @@ MODULE = pipnss
DIRS = \
bugs \
mixedcontent \
stricttransportsecurity \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -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 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 *****
DEPTH = ../../../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = security/ssl/stricttransportsecurity
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^ \
verify.sjs \
test_stricttransportsecurity.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)

View File

@ -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>

View File

@ -0,0 +1,2 @@
Cache-Control: no-cache
Strict-Transport-Security: max-age=60

View File

@ -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 subdom", "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>

View File

@ -0,0 +1,2 @@
Cache-Control: no-cache
Strict-Transport-Security: max-age=60; includeSubDomains

View File

@ -0,0 +1,159 @@
<!-- ***** 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";
// initialized manually here
var testsleft = {'plain': 4, 'subdom': 4};
var roundsLeft = 2;
var testframes = {
'samedom':
{'url': "http://example.com" + STSPATH + "/verify.sjs",
'expected': {'plain': 'SECURE', 'subdom': 'SECURE'}},
'subdom':
{'url': "http://test1.example.com" + STSPATH + "/verify.sjs",
'expected': {'plain': 'INSECURE', 'subdom': 'SECURE'}},
'otherdom':
{'url': "http://example.org" + STSPATH + "/verify.sjs",
'expected': {'plain': 'INSECURE', 'subdom': 'INSECURE'}},
'alreadysecure':
{'url': "https://test2.example.com" + STSPATH + "/verify.sjs",
'expected': {'plain': 'SECURE', 'subdom': 'SECURE'}},
};
function startRound(round) {
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 endRound(round) {
// 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));
// clean up the STS state
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);
}
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 plain"
* 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;
}
// figure out which round of tests we're in
var round = (roundsLeft == 2) ? 'plain' : 'subdom';
if (result[0] === "BOOTSTRAP") {
loadVerifyFrames(round);
return;
}
// check if the result (SECURE/INSECURE) is expected for this round/test combo
SimpleTest.is(result[0], testframes[result[1]].expected[round],
"in ROUND " + round + ", test " + result[1]);
testsleft[round]--;
// check if there are more tests to run.
if (testsleft[round] < 1) {
// if not, advance to next round
endRound(round);
roundsLeft--;
// defer this so it doesn't muck with the stack too much.
if (roundsLeft == 1)
setTimeout(function () {
startRound('subdom');
}, 0);
}
if (roundsLeft < 1) {
SimpleTest.finish();
}
}
// listen for calls back from the sts-setting iframe and then
// the verification frames.
window.addEventListener("message", onMessageReceived, false);
window.addEventListener('load', function() {startRound('plain');}, false);
</script>
</head>
<body>
This test will load some iframes and do some tests.
</body>
</html>

View File

@ -0,0 +1,80 @@
/* ***** 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 ***** */
// SJS file that serves un-cacheable responses for STS tests that postMessage
// to the parent saying whether or not they were loaded securely.
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/html", false);
if ('id' in query) {
var outstr = [
" <!DOCTYPE html>",
" <html> <head> <title>subframe for STS</title>",
" <script type='text/javascript'>",
" var self = window;",
" window.addEventListener('load', function() {",
" if (document.location.protocol === 'https:') {",
" self.parent.postMessage('SECURE " + query['id'] + "',",
" 'http://mochi.test:8888');",
" } else {",
" self.parent.postMessage('INSECURE " + query['id'] + "',",
" 'http://mochi.test:8888');",
" }",
" }, false);",
" </script>",
" </head>",
" <body>",
" STS state verification frame loaded via",
" <script>",
" document.write(document.location.protocol);",
" </script>",
" </body>",
" </html>"].join("\n");
response.write(outstr);
} else {
response.write("ERROR: no id provided");
}
}