Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / netwerk / base / TLSServerSocket.cpp
blob131ea50573b34a4c507b849629a7b1447e0c83a6
1 /* vim:set ts=2 sw=2 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "TLSServerSocket.h"
8 #include "mozilla/net/DNS.h"
9 #include "nsComponentManagerUtils.h"
10 #include "nsDependentSubstring.h"
11 #include "nsIServerSocket.h"
12 #include "nsIX509Cert.h"
13 #include "nsIX509CertDB.h"
14 #include "nsNetCID.h"
15 #include "nsProxyRelease.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsSocketTransport2.h"
18 #include "nsThreadUtils.h"
19 #include "ScopedNSSTypes.h"
20 #include "ssl.h"
22 namespace mozilla {
23 namespace net {
25 //-----------------------------------------------------------------------------
26 // TLSServerSocket
27 //-----------------------------------------------------------------------------
29 NS_IMPL_ISUPPORTS_INHERITED(TLSServerSocket, nsServerSocket, nsITLSServerSocket)
31 nsresult TLSServerSocket::SetSocketDefaults() {
32 // Set TLS options on the listening socket
33 mFD = SSL_ImportFD(nullptr, mFD);
34 if (NS_WARN_IF(!mFD)) {
35 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
38 SSL_OptionSet(mFD, SSL_SECURITY, true);
39 SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_CLIENT, false);
40 SSL_OptionSet(mFD, SSL_HANDSHAKE_AS_SERVER, true);
41 SSL_OptionSet(mFD, SSL_NO_CACHE, true);
43 // We don't currently notify the server API consumer of renegotiation events
44 // (to revalidate peer certs, etc.), so disable it for now.
45 SSL_OptionSet(mFD, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER);
47 SetSessionTickets(true);
48 SetRequestClientCertificate(REQUEST_NEVER);
50 return NS_OK;
53 void TLSServerSocket::CreateClientTransport(PRFileDesc* aClientFD,
54 const NetAddr& aClientAddr) {
55 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
56 nsresult rv;
58 RefPtr<nsSocketTransport> trans = new nsSocketTransport;
59 if (NS_WARN_IF(!trans)) {
60 mCondition = NS_ERROR_OUT_OF_MEMORY;
61 return;
64 RefPtr<TLSServerConnectionInfo> info = new TLSServerConnectionInfo();
65 info->mServerSocket = this;
66 info->mTransport = trans;
67 nsCOMPtr<nsIInterfaceRequestor> infoInterfaceRequestor(info);
68 rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr,
69 infoInterfaceRequestor);
70 if (NS_WARN_IF(NS_FAILED(rv))) {
71 mCondition = rv;
72 return;
75 // Override the default peer certificate validation, so that server consumers
76 // can make their own choice after the handshake completes.
77 SSL_AuthCertificateHook(aClientFD, AuthCertificateHook, nullptr);
78 // Once the TLS handshake has completed, the server consumer is notified and
79 // has access to various TLS state details.
80 // It's safe to pass info here because the socket transport holds it as
81 // |mSecInfo| which keeps it alive for the lifetime of the socket.
82 SSL_HandshakeCallback(aClientFD, TLSServerConnectionInfo::HandshakeCallback,
83 info);
85 // Notify the consumer of the new client so it can manage the streams.
86 // Security details aren't known yet. The security observer will be notified
87 // later when they are ready.
88 nsCOMPtr<nsIServerSocket> serverSocket =
89 do_QueryInterface(NS_ISUPPORTS_CAST(nsITLSServerSocket*, this));
90 mListener->OnSocketAccepted(serverSocket, trans);
93 nsresult TLSServerSocket::OnSocketListen() {
94 if (NS_WARN_IF(!mServerCert)) {
95 return NS_ERROR_NOT_INITIALIZED;
98 UniqueCERTCertificate cert(mServerCert->GetCert());
99 if (NS_WARN_IF(!cert)) {
100 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
103 UniqueSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert.get(), nullptr));
104 if (NS_WARN_IF(!key)) {
105 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
108 SSLKEAType certKEA = NSS_FindCertKEAType(cert.get());
110 nsresult rv =
111 MapSECStatus(SSL_ConfigSecureServer(mFD, cert.get(), key.get(), certKEA));
112 if (NS_WARN_IF(NS_FAILED(rv))) {
113 return rv;
116 return NS_OK;
119 // static
120 SECStatus TLSServerSocket::AuthCertificateHook(void* arg, PRFileDesc* fd,
121 PRBool checksig,
122 PRBool isServer) {
123 // Allow any client cert here, server consumer code can decide whether it's
124 // okay after being notified of the new client socket.
125 return SECSuccess;
128 //-----------------------------------------------------------------------------
129 // TLSServerSocket::nsITLSServerSocket
130 //-----------------------------------------------------------------------------
132 NS_IMETHODIMP
133 TLSServerSocket::GetServerCert(nsIX509Cert** aCert) {
134 if (NS_WARN_IF(!aCert)) {
135 return NS_ERROR_INVALID_POINTER;
137 *aCert = do_AddRef(mServerCert).take();
138 return NS_OK;
141 NS_IMETHODIMP
142 TLSServerSocket::SetServerCert(nsIX509Cert* aCert) {
143 // If AsyncListen was already called (and set mListener), it's too late to set
144 // this.
145 if (NS_WARN_IF(mListener)) {
146 return NS_ERROR_IN_PROGRESS;
148 mServerCert = aCert;
149 return NS_OK;
152 NS_IMETHODIMP
153 TLSServerSocket::SetSessionTickets(bool aEnabled) {
154 // If AsyncListen was already called (and set mListener), it's too late to set
155 // this.
156 if (NS_WARN_IF(mListener)) {
157 return NS_ERROR_IN_PROGRESS;
159 SSL_OptionSet(mFD, SSL_ENABLE_SESSION_TICKETS, aEnabled);
160 return NS_OK;
163 NS_IMETHODIMP
164 TLSServerSocket::SetRequestClientCertificate(uint32_t aMode) {
165 // If AsyncListen was already called (and set mListener), it's too late to set
166 // this.
167 if (NS_WARN_IF(mListener)) {
168 return NS_ERROR_IN_PROGRESS;
170 SSL_OptionSet(mFD, SSL_REQUEST_CERTIFICATE, aMode != REQUEST_NEVER);
172 switch (aMode) {
173 case REQUEST_ALWAYS:
174 SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NO_ERROR);
175 break;
176 case REQUIRE_FIRST_HANDSHAKE:
177 SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_FIRST_HANDSHAKE);
178 break;
179 case REQUIRE_ALWAYS:
180 SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_ALWAYS);
181 break;
182 default:
183 SSL_OptionSet(mFD, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
185 return NS_OK;
188 NS_IMETHODIMP
189 TLSServerSocket::SetVersionRange(uint16_t aMinVersion, uint16_t aMaxVersion) {
190 // If AsyncListen was already called (and set mListener), it's too late to set
191 // this.
192 if (NS_WARN_IF(mListener)) {
193 return NS_ERROR_IN_PROGRESS;
196 SSLVersionRange range = {aMinVersion, aMaxVersion};
197 if (SSL_VersionRangeSet(mFD, &range) != SECSuccess) {
198 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
201 return NS_OK;
204 //-----------------------------------------------------------------------------
205 // TLSServerConnectionInfo
206 //-----------------------------------------------------------------------------
208 namespace {
210 class TLSServerSecurityObserverProxy final
211 : public nsITLSServerSecurityObserver {
212 ~TLSServerSecurityObserverProxy() = default;
214 public:
215 explicit TLSServerSecurityObserverProxy(
216 nsITLSServerSecurityObserver* aListener)
217 : mListener(new nsMainThreadPtrHolder<nsITLSServerSecurityObserver>(
218 "TLSServerSecurityObserverProxy::mListener", aListener)) {}
220 NS_DECL_THREADSAFE_ISUPPORTS
221 NS_DECL_NSITLSSERVERSECURITYOBSERVER
223 class OnHandshakeDoneRunnable : public Runnable {
224 public:
225 OnHandshakeDoneRunnable(
226 const nsMainThreadPtrHandle<nsITLSServerSecurityObserver>& aListener,
227 nsITLSServerSocket* aServer, nsITLSClientStatus* aStatus)
228 : Runnable(
229 "net::TLSServerSecurityObserverProxy::OnHandshakeDoneRunnable"),
230 mListener(aListener),
231 mServer(aServer),
232 mStatus(aStatus) {}
234 NS_DECL_NSIRUNNABLE
236 private:
237 nsMainThreadPtrHandle<nsITLSServerSecurityObserver> mListener;
238 nsCOMPtr<nsITLSServerSocket> mServer;
239 nsCOMPtr<nsITLSClientStatus> mStatus;
242 private:
243 nsMainThreadPtrHandle<nsITLSServerSecurityObserver> mListener;
246 NS_IMPL_ISUPPORTS(TLSServerSecurityObserverProxy, nsITLSServerSecurityObserver)
248 NS_IMETHODIMP
249 TLSServerSecurityObserverProxy::OnHandshakeDone(nsITLSServerSocket* aServer,
250 nsITLSClientStatus* aStatus) {
251 RefPtr<OnHandshakeDoneRunnable> r =
252 new OnHandshakeDoneRunnable(mListener, aServer, aStatus);
253 return NS_DispatchToMainThread(r);
256 NS_IMETHODIMP
257 TLSServerSecurityObserverProxy::OnHandshakeDoneRunnable::Run() {
258 mListener->OnHandshakeDone(mServer, mStatus);
259 return NS_OK;
262 } // namespace
264 NS_IMPL_ISUPPORTS(TLSServerConnectionInfo, nsITLSServerConnectionInfo,
265 nsITLSClientStatus, nsIInterfaceRequestor)
267 TLSServerConnectionInfo::~TLSServerConnectionInfo() {
268 RefPtr<nsITLSServerSecurityObserver> observer;
270 MutexAutoLock lock(mLock);
271 observer = ToRefPtr(std::move(mSecurityObserver));
274 if (observer) {
275 NS_ReleaseOnMainThread("TLSServerConnectionInfo::mSecurityObserver",
276 observer.forget());
280 NS_IMETHODIMP
281 TLSServerConnectionInfo::SetSecurityObserver(
282 nsITLSServerSecurityObserver* aObserver) {
284 MutexAutoLock lock(mLock);
285 if (!aObserver) {
286 mSecurityObserver = nullptr;
287 return NS_OK;
290 mSecurityObserver = new TLSServerSecurityObserverProxy(aObserver);
291 // Call `OnHandshakeDone` if TLS handshake is already completed.
292 if (mTlsVersionUsed != TLS_VERSION_UNKNOWN) {
293 nsCOMPtr<nsITLSServerSocket> serverSocket;
294 GetServerSocket(getter_AddRefs(serverSocket));
295 mSecurityObserver->OnHandshakeDone(serverSocket, this);
296 mSecurityObserver = nullptr;
299 return NS_OK;
302 NS_IMETHODIMP
303 TLSServerConnectionInfo::GetServerSocket(nsITLSServerSocket** aSocket) {
304 if (NS_WARN_IF(!aSocket)) {
305 return NS_ERROR_INVALID_POINTER;
307 *aSocket = do_AddRef(mServerSocket).take();
308 return NS_OK;
311 NS_IMETHODIMP
312 TLSServerConnectionInfo::GetStatus(nsITLSClientStatus** aStatus) {
313 if (NS_WARN_IF(!aStatus)) {
314 return NS_ERROR_INVALID_POINTER;
316 *aStatus = do_AddRef(this).take();
317 return NS_OK;
320 NS_IMETHODIMP
321 TLSServerConnectionInfo::GetPeerCert(nsIX509Cert** aCert) {
322 if (NS_WARN_IF(!aCert)) {
323 return NS_ERROR_INVALID_POINTER;
325 *aCert = do_AddRef(mPeerCert).take();
326 return NS_OK;
329 NS_IMETHODIMP
330 TLSServerConnectionInfo::GetTlsVersionUsed(int16_t* aTlsVersionUsed) {
331 if (NS_WARN_IF(!aTlsVersionUsed)) {
332 return NS_ERROR_INVALID_POINTER;
334 *aTlsVersionUsed = mTlsVersionUsed;
335 return NS_OK;
338 NS_IMETHODIMP
339 TLSServerConnectionInfo::GetCipherName(nsACString& aCipherName) {
340 aCipherName.Assign(mCipherName);
341 return NS_OK;
344 NS_IMETHODIMP
345 TLSServerConnectionInfo::GetKeyLength(uint32_t* aKeyLength) {
346 if (NS_WARN_IF(!aKeyLength)) {
347 return NS_ERROR_INVALID_POINTER;
349 *aKeyLength = mKeyLength;
350 return NS_OK;
353 NS_IMETHODIMP
354 TLSServerConnectionInfo::GetMacLength(uint32_t* aMacLength) {
355 if (NS_WARN_IF(!aMacLength)) {
356 return NS_ERROR_INVALID_POINTER;
358 *aMacLength = mMacLength;
359 return NS_OK;
362 NS_IMETHODIMP
363 TLSServerConnectionInfo::GetInterface(const nsIID& aIID, void** aResult) {
364 NS_ENSURE_ARG_POINTER(aResult);
365 *aResult = nullptr;
367 if (aIID.Equals(NS_GET_IID(nsITLSServerConnectionInfo))) {
368 *aResult = static_cast<nsITLSServerConnectionInfo*>(this);
369 NS_ADDREF_THIS();
370 return NS_OK;
373 return NS_NOINTERFACE;
376 // static
377 void TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD, void* aArg) {
378 RefPtr<TLSServerConnectionInfo> info =
379 static_cast<TLSServerConnectionInfo*>(aArg);
380 nsISocketTransport* transport = info->mTransport;
381 // No longer needed outside this function, so clear the weak ref
382 info->mTransport = nullptr;
383 nsresult rv = info->HandshakeCallback(aFD);
384 if (NS_WARN_IF(NS_FAILED(rv))) {
385 transport->Close(rv);
389 nsresult TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD) {
390 nsresult rv;
392 UniqueCERTCertificate clientCert(SSL_PeerCertificate(aFD));
393 if (clientCert) {
394 nsCOMPtr<nsIX509CertDB> certDB =
395 do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
396 if (NS_FAILED(rv)) {
397 return rv;
400 nsCOMPtr<nsIX509Cert> clientCertPSM;
401 nsTArray<uint8_t> clientCertBytes;
402 clientCertBytes.AppendElements(clientCert->derCert.data,
403 clientCert->derCert.len);
404 rv = certDB->ConstructX509(clientCertBytes, getter_AddRefs(clientCertPSM));
405 if (NS_FAILED(rv)) {
406 return rv;
409 mPeerCert = clientCertPSM;
412 SSLChannelInfo channelInfo;
413 rv = MapSECStatus(SSL_GetChannelInfo(aFD, &channelInfo, sizeof(channelInfo)));
414 if (NS_FAILED(rv)) {
415 return rv;
418 SSLCipherSuiteInfo cipherInfo;
419 rv = MapSECStatus(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
420 sizeof(cipherInfo)));
421 if (NS_FAILED(rv)) {
422 return rv;
424 mCipherName.Assign(cipherInfo.cipherSuiteName);
425 mKeyLength = cipherInfo.effectiveKeyBits;
426 mMacLength = cipherInfo.macBits;
428 // Notify consumer code that handshake is complete
429 nsCOMPtr<nsITLSServerSecurityObserver> observer;
431 MutexAutoLock lock(mLock);
432 mTlsVersionUsed = channelInfo.protocolVersion;
433 if (!mSecurityObserver) {
434 return NS_OK;
436 mSecurityObserver.swap(observer);
438 nsCOMPtr<nsITLSServerSocket> serverSocket;
439 GetServerSocket(getter_AddRefs(serverSocket));
440 observer->OnHandshakeDone(serverSocket, this);
442 return NS_OK;
445 } // namespace net
446 } // namespace mozilla