mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 242448 - Add a basic scriptable TLS server. r=honzab
This commit is contained in:
parent
b051c1fc40
commit
497d1586b4
@ -108,6 +108,7 @@ XPIDL_SOURCES += [
|
||||
'nsIThreadRetargetableRequest.idl',
|
||||
'nsIThreadRetargetableStreamListener.idl',
|
||||
'nsITimedChannel.idl',
|
||||
'nsITLSServerSocket.idl',
|
||||
'nsITraceableChannel.idl',
|
||||
'nsITransport.idl',
|
||||
'nsIUDPSocket.idl',
|
||||
|
179
netwerk/base/public/nsITLSServerSocket.idl
Normal file
179
netwerk/base/public/nsITLSServerSocket.idl
Normal file
@ -0,0 +1,179 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIServerSocket.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
interface nsITLSServerSecurityObserver;
|
||||
interface nsISocketTransport;
|
||||
|
||||
[scriptable, uuid(2e025b6c-96ba-4781-85fb-d1cf1a653207)]
|
||||
interface nsITLSServerSocket : nsIServerSocket
|
||||
{
|
||||
/**
|
||||
* serverCert
|
||||
*
|
||||
* The server's certificate that is presented to the client during the TLS
|
||||
* handshake. This is required to be set before calling |asyncListen|.
|
||||
*/
|
||||
attribute nsIX509Cert serverCert;
|
||||
|
||||
/**
|
||||
* setSessionCache
|
||||
*
|
||||
* Whether the server should use a session cache. Defaults to true. This
|
||||
* should be set before calling |asyncListen| if you wish to change the
|
||||
* default.
|
||||
*/
|
||||
void setSessionCache(in boolean aSessionCache);
|
||||
|
||||
/**
|
||||
* setSessionTickets
|
||||
*
|
||||
* Whether the server should support session tickets. Defaults to true. This
|
||||
* should be set before calling |asyncListen| if you wish to change the
|
||||
* default.
|
||||
*/
|
||||
void setSessionTickets(in boolean aSessionTickets);
|
||||
|
||||
/**
|
||||
* Values for setRequestClientCertificate
|
||||
*/
|
||||
// Never request
|
||||
const unsigned long REQUEST_NEVER = 0;
|
||||
// Request (but do not require) during the first handshake only
|
||||
const unsigned long REQUEST_FIRST_HANDSHAKE = 1;
|
||||
// Request (but do not require) during each handshake
|
||||
const unsigned long REQUEST_ALWAYS = 2;
|
||||
// Require during the first handshake only
|
||||
const unsigned long REQUIRE_FIRST_HANDSHAKE = 3;
|
||||
// Require during each handshake
|
||||
const unsigned long REQUIRE_ALWAYS = 4;
|
||||
|
||||
/**
|
||||
* setRequestClientCertificate
|
||||
*
|
||||
* Whether the server should request and/or require a client auth certificate
|
||||
* from the client. Defaults to REQUEST_NEVER. See the possible options
|
||||
* above. This should be set before calling |asyncListen| if you wish to
|
||||
* change the default.
|
||||
*/
|
||||
void setRequestClientCertificate(in unsigned long aRequestClientCert);
|
||||
};
|
||||
|
||||
/**
|
||||
* Security summary for a given TLS client connection being handled by a
|
||||
* |nsITLSServerSocket| server.
|
||||
*
|
||||
* This is accessible through the security info object on the transport, which
|
||||
* will be an instance of |nsITLSServerConnectionInfo| (see below).
|
||||
*
|
||||
* The values of these attributes are available once the |onHandshakeDone|
|
||||
* method of the security observer has been called (see
|
||||
* |nsITLSServerSecurityObserver| below).
|
||||
*/
|
||||
[scriptable, uuid(19668ea4-e5ad-4182-9698-7e890d48f327)]
|
||||
interface nsITLSClientStatus : nsISupports
|
||||
{
|
||||
/**
|
||||
* peerCert
|
||||
*
|
||||
* The client's certificate, if one was requested via |requestCertificate|
|
||||
* above and supplied by the client.
|
||||
*/
|
||||
readonly attribute nsIX509Cert peerCert;
|
||||
|
||||
/**
|
||||
* Values for tlsVersionUsed, as defined by TLS
|
||||
*/
|
||||
const short SSL_VERSION_3 = 0x0300;
|
||||
const short TLS_VERSION_1 = 0x0301;
|
||||
const short TLS_VERSION_1_1 = 0x0302;
|
||||
const short TLS_VERSION_1_2 = 0x0303;
|
||||
const short TLS_VERSION_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* tlsVersionUsed
|
||||
*
|
||||
* The version of TLS used by the connection. See values above.
|
||||
*/
|
||||
readonly attribute short tlsVersionUsed;
|
||||
|
||||
/**
|
||||
* cipherName
|
||||
*
|
||||
* Name of the cipher suite used, such as
|
||||
* "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256".
|
||||
* See security/nss/lib/ssl/sslinfo.c for the possible values.
|
||||
*/
|
||||
readonly attribute ACString cipherName;
|
||||
|
||||
/**
|
||||
* keyLength
|
||||
*
|
||||
* The "effective" key size of the symmetric key in bits.
|
||||
*/
|
||||
readonly attribute unsigned long keyLength;
|
||||
|
||||
/**
|
||||
* macLength
|
||||
*
|
||||
* The size of the MAC in bits.
|
||||
*/
|
||||
readonly attribute unsigned long macLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection info for a given TLS client connection being handled by a
|
||||
* |nsITLSServerSocket| server. This object is thread-safe.
|
||||
*
|
||||
* This is exposed as the security info object on the transport, so it can be
|
||||
* accessed via |transport.securityInfo|.
|
||||
*
|
||||
* This interface is available by the time the |onSocketAttached| is called,
|
||||
* which is the first time the TLS server consumer is notified of a new client.
|
||||
*/
|
||||
[scriptable, uuid(8a93f5d5-eddd-4c62-a4bd-bfd297653184)]
|
||||
interface nsITLSServerConnectionInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* setSecurityObserver
|
||||
*
|
||||
* Set the security observer to be notified when the TLS handshake has
|
||||
* completed.
|
||||
*/
|
||||
void setSecurityObserver(in nsITLSServerSecurityObserver observer);
|
||||
|
||||
/**
|
||||
* serverSocket
|
||||
*
|
||||
* The nsITLSServerSocket instance that accepted this client connection.
|
||||
*/
|
||||
readonly attribute nsITLSServerSocket serverSocket;
|
||||
|
||||
/**
|
||||
* status
|
||||
*
|
||||
* Security summary for this TLS client connection. Note that the values of
|
||||
* this interface are not available until the TLS handshake has completed.
|
||||
* See |nsITLSClientStatus| above for more details.
|
||||
*/
|
||||
readonly attribute nsITLSClientStatus status;
|
||||
};
|
||||
|
||||
[scriptable, uuid(1f62e1ae-e546-4a38-8917-d428472ed736)]
|
||||
interface nsITLSServerSecurityObserver : nsISupports
|
||||
{
|
||||
/**
|
||||
* onHandsakeDone
|
||||
*
|
||||
* This method is called once the TLS handshake is completed. This takes
|
||||
* place after |onSocketAccepted| has been called, which typically opens the
|
||||
* streams to keep things moving along. It's important to be aware that the
|
||||
* handshake has not completed at the point that |onSocketAccepted| is called,
|
||||
* so no security verification can be done until this method is called.
|
||||
*/
|
||||
void onHandshakeDone(in nsITLSServerSocket aServer,
|
||||
in nsITLSClientStatus aStatus);
|
||||
};
|
476
netwerk/base/src/TLSServerSocket.cpp
Normal file
476
netwerk/base/src/TLSServerSocket.cpp
Normal file
@ -0,0 +1,476 @@
|
||||
/* vim:set ts=2 sw=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TLSServerSocket.h"
|
||||
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIServerSocket.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsSocketTransport2.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "ssl.h"
|
||||
|
||||
extern PRThread *gSocketThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TLSServerSocket
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
TLSServerSocket::TLSServerSocket()
|
||||
: mServerCert(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TLSServerSocket::~TLSServerSocket()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(TLSServerSocket,
|
||||
nsServerSocket,
|
||||
nsITLSServerSocket)
|
||||
|
||||
nsresult
|
||||
TLSServerSocket::SetSocketDefaults()
|
||||
{
|
||||
// Set TLS options on the listening socket
|
||||
mFD = SSL_ImportFD(nullptr, mFD);
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
SSL_OptionSet(mFD, SSL_SECURITY, true);
|
||||
SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_CLIENT, false);
|
||||
SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_SERVER, true);
|
||||
|
||||
// We don't currently notify the server API consumer of renegotiation events
|
||||
// (to revalidate peer certs, etc.), so disable it for now.
|
||||
SSL_OptionSet(mFD, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER);
|
||||
|
||||
SetSessionCache(true);
|
||||
SetSessionTickets(true);
|
||||
SetRequestClientCertificate(REQUEST_NEVER);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TLSServerSocket::CreateClientTransport(PRFileDesc* aClientFD,
|
||||
const NetAddr& aClientAddr)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsresult rv;
|
||||
|
||||
nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
|
||||
if (NS_WARN_IF(!trans)) {
|
||||
mCondition = NS_ERROR_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<TLSServerConnectionInfo> info = new TLSServerConnectionInfo();
|
||||
info->mServerSocket = this;
|
||||
info->mTransport = trans;
|
||||
nsCOMPtr<nsISupports> infoSupports =
|
||||
NS_ISUPPORTS_CAST(nsITLSServerConnectionInfo*, info);
|
||||
rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr, infoSupports);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mCondition = rv;
|
||||
return;
|
||||
}
|
||||
|
||||
// Override the default peer certificate validation, so that server consumers
|
||||
// can make their own choice after the handshake completes.
|
||||
SSL_AuthCertificateHook(aClientFD, AuthCertificateHook, nullptr);
|
||||
// Once the TLS handshake has completed, the server consumer is notified and
|
||||
// has access to various TLS state details.
|
||||
// It's safe to pass info here because the socket transport holds it as
|
||||
// |mSecInfo| which keeps it alive for the lifetime of the socket.
|
||||
SSL_HandshakeCallback(aClientFD, TLSServerConnectionInfo::HandshakeCallback,
|
||||
info);
|
||||
|
||||
// Notify the consumer of the new client so it can manage the streams.
|
||||
// Security details aren't known yet. The security observer will be notified
|
||||
// later when they are ready.
|
||||
nsCOMPtr<nsIServerSocket> serverSocket =
|
||||
do_QueryInterface(NS_ISUPPORTS_CAST(nsITLSServerSocket*, this));
|
||||
mListener->OnSocketAccepted(serverSocket, trans);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TLSServerSocket::OnSocketListen()
|
||||
{
|
||||
if (NS_WARN_IF(!mServerCert)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
ScopedCERTCertificate cert(mServerCert->GetCert());
|
||||
if (NS_WARN_IF(!cert)) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
ScopedSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert, nullptr));
|
||||
if (NS_WARN_IF(!key)) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
SSLKEAType certKEA = NSS_FindCertKEAType(cert);
|
||||
|
||||
nsresult rv = MapSECStatus(SSL_ConfigSecureServer(mFD, cert, key, certKEA));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
SECStatus
|
||||
TLSServerSocket::AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checksig,
|
||||
PRBool isServer)
|
||||
{
|
||||
// Allow any client cert here, server consumer code can decide whether it's
|
||||
// okay after being notified of the new client socket.
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TLSServerSocket::nsITLSServerSocket
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSocket::GetServerCert(nsIX509Cert** aCert)
|
||||
{
|
||||
if (NS_WARN_IF(!aCert)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aCert = mServerCert;
|
||||
NS_IF_ADDREF(*aCert);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSocket::SetServerCert(nsIX509Cert* aCert)
|
||||
{
|
||||
// If AsyncListen was already called (and set mListener), it's too late to set
|
||||
// this.
|
||||
if (NS_WARN_IF(mListener)) {
|
||||
return NS_ERROR_IN_PROGRESS;
|
||||
}
|
||||
mServerCert = aCert;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSocket::SetSessionCache(bool aEnabled)
|
||||
{
|
||||
// If AsyncListen was already called (and set mListener), it's too late to set
|
||||
// this.
|
||||
if (NS_WARN_IF(mListener)) {
|
||||
return NS_ERROR_IN_PROGRESS;
|
||||
}
|
||||
SSL_OptionSet(mFD, SSL_NO_CACHE, !aEnabled);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSocket::SetSessionTickets(bool aEnabled)
|
||||
{
|
||||
// If AsyncListen was already called (and set mListener), it's too late to set
|
||||
// this.
|
||||
if (NS_WARN_IF(mListener)) {
|
||||
return NS_ERROR_IN_PROGRESS;
|
||||
}
|
||||
SSL_OptionSet(mFD, SSL_ENABLE_SESSION_TICKETS, aEnabled);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSocket::SetRequestClientCertificate(uint32_t aMode)
|
||||
{
|
||||
// If AsyncListen was already called (and set mListener), it's too late to set
|
||||
// this.
|
||||
if (NS_WARN_IF(mListener)) {
|
||||
return NS_ERROR_IN_PROGRESS;
|
||||
}
|
||||
SSL_OptionSet(mFD, SSL_REQUEST_CERTIFICATE, aMode != REQUEST_NEVER);
|
||||
|
||||
switch (aMode) {
|
||||
case REQUEST_ALWAYS:
|
||||
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NO_ERROR);
|
||||
break;
|
||||
case REQUIRE_FIRST_HANDSHAKE:
|
||||
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_FIRST_HANDSHAKE);
|
||||
break;
|
||||
case REQUIRE_ALWAYS:
|
||||
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_ALWAYS);
|
||||
break;
|
||||
default:
|
||||
SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TLSServerConnectionInfo
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
class TLSServerSecurityObserverProxy MOZ_FINAL : public nsITLSServerSecurityObserver
|
||||
{
|
||||
~TLSServerSecurityObserverProxy() {}
|
||||
|
||||
public:
|
||||
explicit TLSServerSecurityObserverProxy(nsITLSServerSecurityObserver* aListener)
|
||||
: mListener(new nsMainThreadPtrHolder<nsITLSServerSecurityObserver>(aListener))
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITLSSERVERSECURITYOBSERVER
|
||||
|
||||
class OnHandshakeDoneRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
OnHandshakeDoneRunnable(const nsMainThreadPtrHandle<nsITLSServerSecurityObserver>& aListener,
|
||||
nsITLSServerSocket* aServer,
|
||||
nsITLSClientStatus* aStatus)
|
||||
: mListener(aListener)
|
||||
, mServer(aServer)
|
||||
, mStatus(aStatus)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsITLSServerSecurityObserver> mListener;
|
||||
nsCOMPtr<nsITLSServerSocket> mServer;
|
||||
nsCOMPtr<nsITLSClientStatus> mStatus;
|
||||
};
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsITLSServerSecurityObserver> mListener;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(TLSServerSecurityObserverProxy,
|
||||
nsITLSServerSecurityObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSecurityObserverProxy::OnHandshakeDone(nsITLSServerSocket* aServer,
|
||||
nsITLSClientStatus* aStatus)
|
||||
{
|
||||
nsRefPtr<OnHandshakeDoneRunnable> r =
|
||||
new OnHandshakeDoneRunnable(mListener, aServer, aStatus);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerSecurityObserverProxy::OnHandshakeDoneRunnable::Run()
|
||||
{
|
||||
mListener->OnHandshakeDone(mServer, mStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(TLSServerConnectionInfo,
|
||||
nsITLSServerConnectionInfo,
|
||||
nsITLSClientStatus)
|
||||
|
||||
TLSServerConnectionInfo::TLSServerConnectionInfo()
|
||||
: mServerSocket(nullptr)
|
||||
, mTransport(nullptr)
|
||||
, mPeerCert(nullptr)
|
||||
, mTlsVersionUsed(TLS_VERSION_UNKNOWN)
|
||||
, mKeyLength(0)
|
||||
, mMacLength(0)
|
||||
, mLock("TLSServerConnectionInfo.mLock")
|
||||
, mSecurityObserver(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TLSServerConnectionInfo::~TLSServerConnectionInfo()
|
||||
{
|
||||
if (!mSecurityObserver) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsITLSServerSecurityObserver* observer;
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mSecurityObserver.forget(&observer);
|
||||
}
|
||||
|
||||
if (observer) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
NS_ProxyRelease(mainThread, observer);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::SetSecurityObserver(nsITLSServerSecurityObserver* aObserver)
|
||||
{
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mSecurityObserver = new TLSServerSecurityObserverProxy(aObserver);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetServerSocket(nsITLSServerSocket** aSocket)
|
||||
{
|
||||
if (NS_WARN_IF(!aSocket)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aSocket = mServerSocket;
|
||||
NS_IF_ADDREF(*aSocket);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetStatus(nsITLSClientStatus** aStatus)
|
||||
{
|
||||
if (NS_WARN_IF(!aStatus)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aStatus = this;
|
||||
NS_IF_ADDREF(*aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetPeerCert(nsIX509Cert** aCert)
|
||||
{
|
||||
if (NS_WARN_IF(!aCert)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aCert = mPeerCert;
|
||||
NS_IF_ADDREF(*aCert);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetTlsVersionUsed(int16_t* aTlsVersionUsed)
|
||||
{
|
||||
if (NS_WARN_IF(!aTlsVersionUsed)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aTlsVersionUsed = mTlsVersionUsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetCipherName(nsACString& aCipherName)
|
||||
{
|
||||
aCipherName.Assign(mCipherName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetKeyLength(uint32_t* aKeyLength)
|
||||
{
|
||||
if (NS_WARN_IF(!aKeyLength)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aKeyLength = mKeyLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TLSServerConnectionInfo::GetMacLength(uint32_t* aMacLength)
|
||||
{
|
||||
if (NS_WARN_IF(!aMacLength)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*aMacLength = mMacLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD, void* aArg)
|
||||
{
|
||||
nsRefPtr<TLSServerConnectionInfo> info =
|
||||
static_cast<TLSServerConnectionInfo*>(aArg);
|
||||
nsISocketTransport* transport = info->mTransport;
|
||||
// No longer needed outside this function, so clear the weak ref
|
||||
info->mTransport = nullptr;
|
||||
nsresult rv = info->HandshakeCallback(aFD);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
transport->Close(rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
ScopedCERTCertificate clientCert(SSL_PeerCertificate(aFD));
|
||||
if (clientCert) {
|
||||
nsCOMPtr<nsIX509CertDB> certDB =
|
||||
do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIX509Cert> clientCertPSM;
|
||||
rv = certDB->ConstructX509(reinterpret_cast<char*>(clientCert->derCert.data),
|
||||
clientCert->derCert.len,
|
||||
getter_AddRefs(clientCertPSM));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPeerCert = clientCertPSM;
|
||||
}
|
||||
|
||||
SSLChannelInfo channelInfo;
|
||||
rv = MapSECStatus(SSL_GetChannelInfo(aFD, &channelInfo, sizeof(channelInfo)));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mTlsVersionUsed = channelInfo.protocolVersion;
|
||||
|
||||
SSLCipherSuiteInfo cipherInfo;
|
||||
rv = MapSECStatus(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
|
||||
sizeof(cipherInfo)));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mCipherName.Assign(cipherInfo.cipherSuiteName);
|
||||
mKeyLength = cipherInfo.effectiveKeyBits;
|
||||
mMacLength = cipherInfo.macBits;
|
||||
|
||||
if (!mSecurityObserver) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Notify consumer code that handshake is complete
|
||||
nsCOMPtr<nsITLSServerSecurityObserver> observer;
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mSecurityObserver.swap(observer);
|
||||
}
|
||||
nsCOMPtr<nsITLSServerSocket> serverSocket;
|
||||
GetServerSocket(getter_AddRefs(serverSocket));
|
||||
observer->OnHandshakeDone(serverSocket, this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
81
netwerk/base/src/TLSServerSocket.h
Normal file
81
netwerk/base/src/TLSServerSocket.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* vim:set ts=2 sw=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_net_TLSServerSocket_h
|
||||
#define mozilla_net_TLSServerSocket_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsITLSServerSocket.h"
|
||||
#include "nsServerSocket.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "seccomon.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class TLSServerSocket : public nsServerSocket
|
||||
, public nsITLSServerSocket
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_FORWARD_NSISERVERSOCKET(nsServerSocket::)
|
||||
NS_DECL_NSITLSSERVERSOCKET
|
||||
|
||||
// Override methods from nsServerSocket
|
||||
virtual void CreateClientTransport(PRFileDesc* clientFD,
|
||||
const NetAddr& clientAddr) MOZ_OVERRIDE;
|
||||
virtual nsresult SetSocketDefaults() MOZ_OVERRIDE;
|
||||
virtual nsresult OnSocketListen() MOZ_OVERRIDE;
|
||||
|
||||
TLSServerSocket();
|
||||
|
||||
private:
|
||||
virtual ~TLSServerSocket();
|
||||
|
||||
static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
|
||||
PRBool checksig, PRBool isServer);
|
||||
|
||||
nsCOMPtr<nsIX509Cert> mServerCert;
|
||||
};
|
||||
|
||||
class TLSServerConnectionInfo : public nsITLSServerConnectionInfo
|
||||
, public nsITLSClientStatus
|
||||
{
|
||||
friend class TLSServerSocket;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITLSSERVERCONNECTIONINFO
|
||||
NS_DECL_NSITLSCLIENTSTATUS
|
||||
|
||||
TLSServerConnectionInfo();
|
||||
|
||||
private:
|
||||
virtual ~TLSServerConnectionInfo();
|
||||
|
||||
static void HandshakeCallback(PRFileDesc* aFD, void* aArg);
|
||||
nsresult HandshakeCallback(PRFileDesc* aFD);
|
||||
|
||||
nsRefPtr<TLSServerSocket> mServerSocket;
|
||||
// Weak ref to the transport, to avoid cycles since the transport holds a
|
||||
// reference to the TLSServerConnectionInfo object. This is not handed out to
|
||||
// anyone, and is only used in HandshakeCallback to close the transport in
|
||||
// case of an error. After this, it's set to nullptr.
|
||||
nsISocketTransport* mTransport;
|
||||
nsCOMPtr<nsIX509Cert> mPeerCert;
|
||||
int16_t mTlsVersionUsed;
|
||||
nsCString mCipherName;
|
||||
uint32_t mKeyLength;
|
||||
uint32_t mMacLength;
|
||||
// lock protects access to mSecurityObserver
|
||||
mozilla::Mutex mLock;
|
||||
nsCOMPtr<nsITLSServerSecurityObserver> mSecurityObserver;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_TLSServerSocket_h
|
@ -77,6 +77,7 @@ UNIFIED_SOURCES += [
|
||||
'RedirectChannelRegistrar.cpp',
|
||||
'StreamingProtocolService.cpp',
|
||||
'Tickler.cpp',
|
||||
'TLSServerSocket.cpp',
|
||||
]
|
||||
|
||||
# These files cannot be built in unified mode because they force NSPR logging.
|
||||
|
@ -45,8 +45,8 @@ PostEvent(nsServerSocket *s, nsServerSocketFunc func)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsServerSocket::nsServerSocket()
|
||||
: mLock("nsServerSocket.mLock")
|
||||
, mFD(nullptr)
|
||||
: mFD(nullptr)
|
||||
, mLock("nsServerSocket.mLock")
|
||||
, mAttached(false)
|
||||
, mKeepWhenOffline(false)
|
||||
{
|
||||
@ -154,6 +154,25 @@ nsServerSocket::TryAttach()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsServerSocket::CreateClientTransport(PRFileDesc* aClientFD,
|
||||
const NetAddr& aClientAddr)
|
||||
{
|
||||
nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
|
||||
if (NS_WARN_IF(!trans)) {
|
||||
mCondition = NS_ERROR_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mCondition = rv;
|
||||
return;
|
||||
}
|
||||
|
||||
mListener->OnSocketAccepted(this, trans);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsServerSocket::nsASocketHandler
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -185,25 +204,14 @@ nsServerSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
|
||||
|
||||
clientFD = PR_Accept(mFD, &prClientAddr, PR_INTERVAL_NO_WAIT);
|
||||
PRNetAddrToNetAddr(&prClientAddr, &clientAddr);
|
||||
if (!clientFD)
|
||||
{
|
||||
if (!clientFD) {
|
||||
NS_WARNING("PR_Accept failed");
|
||||
mCondition = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
|
||||
if (!trans)
|
||||
mCondition = NS_ERROR_OUT_OF_MEMORY;
|
||||
else
|
||||
{
|
||||
nsresult rv = trans->InitWithConnectedSocket(clientFD, &clientAddr);
|
||||
if (NS_FAILED(rv))
|
||||
mCondition = rv;
|
||||
else
|
||||
mListener->OnSocketAccepted(this, trans);
|
||||
}
|
||||
}
|
||||
|
||||
// Accept succeeded, create socket transport and notify consumer
|
||||
CreateClientTransport(clientFD, clientAddr);
|
||||
}
|
||||
|
||||
void
|
||||
@ -328,6 +336,7 @@ NS_IMETHODIMP
|
||||
nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog)
|
||||
{
|
||||
NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
|
||||
nsresult rv;
|
||||
|
||||
//
|
||||
// configure listening socket...
|
||||
@ -373,12 +382,18 @@ nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Set any additional socket defaults needed by child classes
|
||||
rv = SetSocketDefaults();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// wait until AsyncListen is called before polling the socket for
|
||||
// client connections.
|
||||
return NS_OK;
|
||||
|
||||
fail:
|
||||
nsresult rv = ErrorAccordingToNSPR(PR_GetError());
|
||||
rv = ErrorAccordingToNSPR(PR_GetError());
|
||||
Close();
|
||||
return rv;
|
||||
}
|
||||
@ -509,6 +524,13 @@ nsServerSocket::AsyncListen(nsIServerSocketListener *aListener)
|
||||
mListener = new ServerSocketListenerProxy(aListener);
|
||||
mListenerTarget = NS_GetCurrentThread();
|
||||
}
|
||||
|
||||
// Child classes may need to do additional setup just before listening begins
|
||||
nsresult rv = OnSocketListen();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return PostEvent(this, &nsServerSocket::OnMsgAttach);
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,18 @@
|
||||
#ifndef nsServerSocket_h__
|
||||
#define nsServerSocket_h__
|
||||
|
||||
#include "prio.h"
|
||||
#include "nsASocketHandler.h"
|
||||
#include "nsIServerSocket.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsIEventTarget;
|
||||
namespace mozilla { namespace net {
|
||||
union NetAddr;
|
||||
}} // namespace mozilla::net
|
||||
|
||||
class nsServerSocket : public nsASocketHandler
|
||||
, public nsIServerSocket
|
||||
{
|
||||
@ -29,20 +35,26 @@ public:
|
||||
virtual uint64_t ByteCountReceived() { return 0; }
|
||||
nsServerSocket();
|
||||
|
||||
private:
|
||||
virtual ~nsServerSocket();
|
||||
virtual void CreateClientTransport(PRFileDesc* clientFD,
|
||||
const mozilla::net::NetAddr& clientAddr);
|
||||
virtual nsresult SetSocketDefaults() { return NS_OK; }
|
||||
virtual nsresult OnSocketListen() { return NS_OK; }
|
||||
|
||||
protected:
|
||||
virtual ~nsServerSocket();
|
||||
PRFileDesc* mFD;
|
||||
nsCOMPtr<nsIServerSocketListener> mListener;
|
||||
|
||||
private:
|
||||
void OnMsgClose();
|
||||
void OnMsgAttach();
|
||||
|
||||
|
||||
// try attaching our socket (mFD) to the STS's poll list.
|
||||
nsresult TryAttach();
|
||||
|
||||
// lock protects access to mListener; so it is not cleared while being used.
|
||||
mozilla::Mutex mLock;
|
||||
PRFileDesc *mFD;
|
||||
PRNetAddr mAddr;
|
||||
nsCOMPtr<nsIServerSocketListener> mListener;
|
||||
nsCOMPtr<nsIEventTarget> mListenerTarget;
|
||||
bool mAttached;
|
||||
bool mKeepWhenOffline;
|
||||
|
@ -942,6 +942,15 @@ nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr)
|
||||
return PostEvent(MSG_RETRY_INIT_SOCKET);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransport::InitWithConnectedSocket(PRFileDesc* aFD,
|
||||
const NetAddr* aAddr,
|
||||
nsISupports* aSecInfo)
|
||||
{
|
||||
mSecInfo = aSecInfo;
|
||||
return InitWithConnectedSocket(aFD, aAddr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransport::PostEvent(uint32_t type, nsresult status, nsISupports *param)
|
||||
{
|
||||
|
@ -131,6 +131,12 @@ public:
|
||||
nsresult InitWithConnectedSocket(PRFileDesc *socketFD,
|
||||
const mozilla::net::NetAddr *addr);
|
||||
|
||||
// this method instructs the socket transport to use an already connected
|
||||
// socket with the given address, and additionally supplies security info.
|
||||
nsresult InitWithConnectedSocket(PRFileDesc* aSocketFD,
|
||||
const mozilla::net::NetAddr* aAddr,
|
||||
nsISupports* aSecInfo);
|
||||
|
||||
// This method instructs the socket transport to open a socket
|
||||
// connected to the given Unix domain address. We can only create
|
||||
// unlayered, simple, stream sockets.
|
||||
|
@ -335,6 +335,17 @@
|
||||
{0xab, 0x1d, 0x5e, 0x68, 0xa9, 0xf4, 0x5f, 0x08} \
|
||||
}
|
||||
|
||||
// component implementing nsITLSServerSocket
|
||||
#define NS_TLSSERVERSOCKET_CONTRACTID \
|
||||
"@mozilla.org/network/tls-server-socket;1"
|
||||
#define NS_TLSSERVERSOCKET_CID \
|
||||
{ /* 1813cbb4-c98e-4622-8c7d-839167f3f272 */ \
|
||||
0x1813cbb4, \
|
||||
0xc98e, \
|
||||
0x4622, \
|
||||
{0x8c, 0x7d, 0x83, 0x91, 0x67, 0xf3, 0xf2, 0x72} \
|
||||
}
|
||||
|
||||
// component implementing nsIUDPSocket
|
||||
#define NS_UDPSOCKET_CONTRACTID \
|
||||
"@mozilla.org/network/udp-socket;1"
|
||||
|
@ -75,6 +75,10 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSocketTransportService, Init)
|
||||
#include "nsServerSocket.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsServerSocket)
|
||||
|
||||
#include "TLSServerSocket.h"
|
||||
typedef mozilla::net::TLSServerSocket TLSServerSocket;
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TLSServerSocket)
|
||||
|
||||
#include "nsUDPSocket.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUDPSocket)
|
||||
|
||||
@ -665,6 +669,7 @@ NS_DEFINE_NAMED_CID(NS_IOSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_STREAMTRANSPORTSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SOCKETTRANSPORTSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SERVERSOCKET_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_TLSSERVERSOCKET_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_UDPSOCKET_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SOCKETPROVIDERSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DNSSERVICE_CID);
|
||||
@ -804,6 +809,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_STREAMTRANSPORTSERVICE_CID, false, nullptr, nsStreamTransportServiceConstructor },
|
||||
{ &kNS_SOCKETTRANSPORTSERVICE_CID, false, nullptr, nsSocketTransportServiceConstructor },
|
||||
{ &kNS_SERVERSOCKET_CID, false, nullptr, nsServerSocketConstructor },
|
||||
{ &kNS_TLSSERVERSOCKET_CID, false, nullptr, TLSServerSocketConstructor },
|
||||
{ &kNS_UDPSOCKET_CID, false, nullptr, nsUDPSocketConstructor },
|
||||
{ &kNS_SOCKETPROVIDERSERVICE_CID, false, nullptr, nsSocketProviderService::Create },
|
||||
{ &kNS_DNSSERVICE_CID, false, nullptr, nsIDNSServiceConstructor },
|
||||
@ -950,6 +956,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
||||
{ NS_STREAMTRANSPORTSERVICE_CONTRACTID, &kNS_STREAMTRANSPORTSERVICE_CID },
|
||||
{ NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &kNS_SOCKETTRANSPORTSERVICE_CID },
|
||||
{ NS_SERVERSOCKET_CONTRACTID, &kNS_SERVERSOCKET_CID },
|
||||
{ NS_TLSSERVERSOCKET_CONTRACTID, &kNS_TLSSERVERSOCKET_CID },
|
||||
{ NS_UDPSOCKET_CONTRACTID, &kNS_UDPSOCKET_CID },
|
||||
{ NS_SOCKETPROVIDERSERVICE_CONTRACTID, &kNS_SOCKETPROVIDERSERVICE_CID },
|
||||
{ NS_DNSSERVICE_CONTRACTID, &kNS_DNSSERVICE_CID },
|
||||
|
167
netwerk/test/unit/test_tls_server.js
Normal file
167
netwerk/test/unit/test_tls_server.js
Normal file
@ -0,0 +1,167 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Need profile dir to store the key / cert
|
||||
do_get_profile();
|
||||
// Ensure PSM is initialized
|
||||
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
|
||||
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
const { Promise: promise } =
|
||||
Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const certService = Cc["@mozilla.org/security/local-cert-service;1"]
|
||||
.getService(Ci.nsILocalCertService);
|
||||
const certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
|
||||
.getService(Ci.nsICertOverrideService);
|
||||
const socketTransportService =
|
||||
Cc["@mozilla.org/network/socket-transport-service;1"]
|
||||
.getService(Ci.nsISocketTransportService);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function getCert() {
|
||||
let deferred = promise.defer();
|
||||
certService.getOrCreateCert("tls-test", {
|
||||
handleCert: function(c, rv) {
|
||||
if (rv) {
|
||||
deferred.reject(rv);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(c);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function startServer(cert) {
|
||||
let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"]
|
||||
.createInstance(Ci.nsITLSServerSocket);
|
||||
tlsServer.init(-1, true, -1);
|
||||
tlsServer.serverCert = cert;
|
||||
|
||||
let input, output;
|
||||
|
||||
let listener = {
|
||||
onSocketAccepted: function(socket, transport) {
|
||||
do_print("Accept TLS client connection");
|
||||
let connectionInfo = transport.securityInfo
|
||||
.QueryInterface(Ci.nsITLSServerConnectionInfo);
|
||||
connectionInfo.setSecurityObserver(listener);
|
||||
input = transport.openInputStream(0, 0, 0);
|
||||
output = transport.openOutputStream(0, 0, 0);
|
||||
},
|
||||
onHandshakeDone: function(socket, status) {
|
||||
do_print("TLS handshake done");
|
||||
ok(!!status.peerCert, "Has peer cert");
|
||||
ok(status.peerCert.equals(cert), "Peer cert matches expected cert");
|
||||
|
||||
equal(status.tlsVersionUsed, Ci.nsITLSClientStatus.TLS_VERSION_1_2,
|
||||
"Using TLS 1.2");
|
||||
equal(status.cipherName, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"Using expected cipher");
|
||||
equal(status.keyLength, 128, "Using 128-bit key");
|
||||
equal(status.macLength, 128, "Using 128-bit MAC");
|
||||
|
||||
input.asyncWait({
|
||||
onInputStreamReady: function(input) {
|
||||
NetUtil.asyncCopy(input, output);
|
||||
}
|
||||
}, 0, 0, Services.tm.currentThread);
|
||||
},
|
||||
onStopListening: function() {}
|
||||
};
|
||||
|
||||
tlsServer.setSessionCache(false);
|
||||
tlsServer.setSessionTickets(false);
|
||||
tlsServer.setRequestClientCertificate(Ci.nsITLSServerSocket.REQUIRE_ALWAYS);
|
||||
|
||||
tlsServer.asyncListen(listener);
|
||||
|
||||
return tlsServer.port;
|
||||
}
|
||||
|
||||
function storeCertOverride(port, cert) {
|
||||
let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH;
|
||||
certOverrideService.rememberValidityOverride("127.0.0.1", port, cert,
|
||||
overrideBits, true);
|
||||
}
|
||||
|
||||
function startClient(port, cert) {
|
||||
let transport =
|
||||
socketTransportService.createTransport(["ssl"], 1, "127.0.0.1", port, null);
|
||||
let input;
|
||||
let output;
|
||||
|
||||
let inputDeferred = promise.defer();
|
||||
let outputDeferred = promise.defer();
|
||||
|
||||
let handler = {
|
||||
|
||||
onTransportStatus: function(transport, status) {
|
||||
if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
|
||||
output.asyncWait(handler, 0, 0, Services.tm.currentThread);
|
||||
}
|
||||
},
|
||||
|
||||
onInputStreamReady: function(input) {
|
||||
try {
|
||||
let data = NetUtil.readInputStreamToString(input, input.available());
|
||||
equal(data, "HELLO", "Echoed data received");
|
||||
input.close();
|
||||
output.close();
|
||||
inputDeferred.resolve();
|
||||
} catch (e) {
|
||||
let SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
|
||||
let SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
|
||||
let errorCode = -1 * (e.result & 0xFFFF);
|
||||
if (errorCode == SEC_ERROR_UNKNOWN_ISSUER) {
|
||||
do_print("Client doesn't like server cert");
|
||||
}
|
||||
inputDeferred.reject(e);
|
||||
}
|
||||
},
|
||||
|
||||
onOutputStreamReady: function(output) {
|
||||
try {
|
||||
// Set the cert we want to avoid any cert UI prompts
|
||||
let clientSecInfo = transport.securityInfo;
|
||||
let tlsControl = clientSecInfo.QueryInterface(Ci.nsISSLSocketControl);
|
||||
tlsControl.clientCert = cert;
|
||||
|
||||
output.write("HELLO", 5);
|
||||
do_print("Output to server written");
|
||||
outputDeferred.resolve();
|
||||
input = transport.openInputStream(0, 0, 0);
|
||||
input.asyncWait(handler, 0, 0, Services.tm.currentThread);
|
||||
} catch (e) {
|
||||
let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
|
||||
let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17;
|
||||
let errorCode = -1 * (e.result & 0xFFFF);
|
||||
if (errorCode == SSL_ERROR_BAD_CERT_ALERT) {
|
||||
do_print("Server doesn't like client cert");
|
||||
}
|
||||
outputDeferred.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
transport.setEventSink(handler, Services.tm.currentThread);
|
||||
output = transport.openOutputStream(0, 0, 0);
|
||||
|
||||
return promise.all([inputDeferred.promise, outputDeferred.promise]);
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
let cert = yield getCert();
|
||||
ok(!!cert, "Got self-signed cert");
|
||||
let port = startServer(cert);
|
||||
storeCertOverride(port, cert);
|
||||
yield startClient(port, cert);
|
||||
});
|
@ -299,3 +299,6 @@ run-if = os == "win"
|
||||
[test_redirect_history.js]
|
||||
[test_reply_without_content_type.js]
|
||||
[test_websocket_offline.js]
|
||||
[test_tls_server.js]
|
||||
# The local cert service used by this test is not currently shipped on Android
|
||||
skip-if = os == "android"
|
||||
|
Loading…
Reference in New Issue
Block a user