mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
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:
parent
e36a2a1d5a
commit
a781965b6d
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -137,6 +137,7 @@ XPIDLSRCS = \
|
||||
nsINetUtil.idl \
|
||||
nsIProxiedChannel.idl \
|
||||
nsIRandomGenerator.idl \
|
||||
nsIStrictTransportSecurityService.idl \
|
||||
nsIURIWithPrincipal.idl \
|
||||
nsIURIClassifier.idl \
|
||||
nsIRedirectResultListener.idl \
|
||||
|
105
netwerk/base/public/nsIStrictTransportSecurityService.idl
Normal file
105
netwerk/base/public/nsIStrictTransportSecurityService.idl
Normal 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"
|
||||
%}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
196
netwerk/test/TestSTSParser.cpp
Normal file
196
netwerk/test/TestSTSParser.cpp
Normal 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);
|
||||
}
|
@ -58,6 +58,7 @@ CPPSRCS = \
|
||||
nsSecureBrowserUIImpl.cpp \
|
||||
nsBOOTModule.cpp \
|
||||
nsSecurityWarningDialogs.cpp \
|
||||
nsStrictTransportSecurityService.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
343
security/manager/boot/src/nsStrictTransportSecurityService.cpp
Normal file
343
security/manager/boot/src/nsStrictTransportSecurityService.cpp
Normal 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;
|
||||
}
|
73
security/manager/boot/src/nsStrictTransportSecurityService.h
Normal file
73
security/manager/boot/src/nsStrictTransportSecurityService.h
Normal 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__
|
@ -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.
|
||||
|
@ -45,6 +45,7 @@ MODULE = pipnss
|
||||
DIRS = \
|
||||
bugs \
|
||||
mixedcontent \
|
||||
stricttransportsecurity \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -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)
|
@ -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>
|
@ -0,0 +1,2 @@
|
||||
Cache-Control: no-cache
|
||||
Strict-Transport-Security: max-age=60
|
@ -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>
|
@ -0,0 +1,2 @@
|
||||
Cache-Control: no-cache
|
||||
Strict-Transport-Security: max-age=60; includeSubDomains
|
@ -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>
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user