diff options
Diffstat (limited to '')
-rw-r--r-- | security/manager/ssl/NSSSocketControl.h | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/security/manager/ssl/NSSSocketControl.h b/security/manager/ssl/NSSSocketControl.h new file mode 100644 index 0000000000..f607340b76 --- /dev/null +++ b/security/manager/ssl/NSSSocketControl.h @@ -0,0 +1,366 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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 NSSSocketControl_h +#define NSSSocketControl_h + +#include "CommonSocketControl.h" +#include "SharedSSLState.h" +#include "TLSClientAuthCertSelection.h" +#include "nsThreadUtils.h" + +extern mozilla::LazyLogModule gPIPNSSLog; + +class SelectClientAuthCertificate; + +class NSSSocketControl final : public CommonSocketControl { + public: + NSSSocketControl(const nsCString& aHostName, int32_t aPort, + mozilla::psm::SharedSSLState& aState, uint32_t providerFlags, + uint32_t providerTlsFlags); + + NS_INLINE_DECL_REFCOUNTING_INHERITED(NSSSocketControl, CommonSocketControl); + + void SetForSTARTTLS(bool aForSTARTTLS); + bool GetForSTARTTLS(); + + nsresult GetFileDescPtr(PRFileDesc** aFilePtr); + nsresult SetFileDescPtr(PRFileDesc* aFilePtr); + + bool IsHandshakePending() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mHandshakePending; + } + void SetHandshakeNotPending() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mHandshakePending = false; + } + + void SetTLSVersionRange(SSLVersionRange range) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mTLSVersionRange = range; + } + SSLVersionRange GetTLSVersionRange() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mTLSVersionRange; + }; + + // From nsITLSSocketControl. + NS_IMETHOD ProxyStartSSL(void) override; + NS_IMETHOD StartTLS(void) override; + NS_IMETHOD SetNPNList(nsTArray<nsCString>& aNPNList) override; + NS_IMETHOD GetAlpnEarlySelection(nsACString& _retval) override; + NS_IMETHOD GetEarlyDataAccepted(bool* aEarlyDataAccepted) override; + NS_IMETHOD DriveHandshake(void) override; + NS_IMETHOD GetKEAUsed(int16_t* aKEAUsed) override; + NS_IMETHOD GetKEAKeyBits(uint32_t* aKEAKeyBits) override; + NS_IMETHOD GetSSLVersionOffered(int16_t* aSSLVersionOffered) override; + NS_IMETHOD GetMACAlgorithmUsed(int16_t* aMACAlgorithmUsed) override; + bool GetDenyClientCert() override; + void SetDenyClientCert(bool aDenyClientCert) override; + NS_IMETHOD GetEsniTxt(nsACString& aEsniTxt) override; + NS_IMETHOD SetEsniTxt(const nsACString& aEsniTxt) override; + NS_IMETHOD GetEchConfig(nsACString& aEchConfig) override; + NS_IMETHOD SetEchConfig(const nsACString& aEchConfig) override; + NS_IMETHOD GetPeerId(nsACString& aResult) override; + NS_IMETHOD GetRetryEchConfig(nsACString& aEchConfig) override; + NS_IMETHOD DisableEarlyData(void) override; + NS_IMETHOD SetHandshakeCallbackListener( + nsITlsHandshakeCallbackListener* callback) override; + NS_IMETHOD Claim() override; + NS_IMETHOD SetBrowserId(uint64_t browserId) override; + NS_IMETHOD GetBrowserId(uint64_t* browserId) override; + + PRStatus CloseSocketAndDestroy(); + + void SetNegotiatedNPN(const char* value, uint32_t length); + void SetEarlyDataAccepted(bool aAccepted); + + void SetHandshakeCompleted(); + bool IsHandshakeCompleted() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mHandshakeCompleted; + } + void NoteTimeUntilReady(); + + void SetFalseStartCallbackCalled() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mFalseStartCallbackCalled = true; + } + void SetFalseStarted() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mFalseStarted = true; + } + + // Note that this is only valid *during* a handshake; at the end of the + // handshake, it gets reset back to false. + void SetFullHandshake() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mIsFullHandshake = true; + } + bool IsFullHandshake() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mIsFullHandshake; + } + + void UpdateEchExtensionStatus(EchExtensionStatus aEchExtensionStatus) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mEchExtensionStatus = std::max(aEchExtensionStatus, mEchExtensionStatus); + } + EchExtensionStatus GetEchExtensionStatus() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mEchExtensionStatus; + } + + void WillSendXyberShare() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mSentXyberShare = true; + } + + bool SentXyberShare() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mSentXyberShare; + } + + void SetHasTls13HandshakeSecrets() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mHasTls13HandshakeSecrets = true; + } + + bool HasTls13HandshakeSecrets() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mHasTls13HandshakeSecrets; + } + + bool GetJoined() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mJoined; + } + + uint32_t GetProviderTlsFlags() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mProviderTlsFlags; + } + + mozilla::psm::SharedSSLState& SharedState(); + + enum CertVerificationState { + BeforeCertVerification, + WaitingForCertVerification, + AfterCertVerification + }; + + void SetCertVerificationWaiting(); + + // Use errorCode == 0 to indicate success; + void SetCertVerificationResult(PRErrorCode errorCode) override; + + void ClientAuthCertificateSelected( + nsTArray<uint8_t>& certBytes, + nsTArray<nsTArray<uint8_t>>& certChainBytes); + + bool IsWaitingForCertVerification() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mCertVerificationState == WaitingForCertVerification; + } + void AddPlaintextBytesRead(uint64_t val) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mPlaintextBytesRead += val; + } + + bool IsPreliminaryHandshakeDone() const { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mPreliminaryHandshakeDone; + } + void SetPreliminaryHandshakeDone() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mPreliminaryHandshakeDone = true; + } + + void SetKEAUsed(int16_t kea) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mKEAUsed = kea; + } + + void SetKEAKeyBits(uint32_t keaBits) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mKEAKeyBits = keaBits; + } + + void SetMACAlgorithmUsed(int16_t mac) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mMACAlgorithmUsed = mac; + } + + void SetShortWritePending(int32_t amount, unsigned char data) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mIsShortWritePending = true; + mShortWriteOriginalAmount = amount; + mShortWritePendingByte = data; + } + + bool IsShortWritePending() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return mIsShortWritePending; + } + + unsigned char const* GetShortWritePendingByteRef() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + return &mShortWritePendingByte; + } + + int32_t ResetShortWritePending() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mIsShortWritePending = false; + return mShortWriteOriginalAmount; + } + +#ifdef DEBUG + // These helpers assert that the caller does try to send the same data + // as it was previously when we hit the short-write. This is a measure + // to make sure we communicate correctly to the consumer. + void RememberShortWrittenBuffer(const unsigned char* data) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + mShortWriteBufferCheck = + mozilla::MakeUnique<char[]>(mShortWriteOriginalAmount); + memcpy(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount); + } + void CheckShortWrittenBuffer(const unsigned char* data, int32_t amount) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + if (!mShortWriteBufferCheck) return; + MOZ_ASSERT(amount >= mShortWriteOriginalAmount, + "unexpected amount length after short write"); + MOZ_ASSERT( + !memcmp(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount), + "unexpected buffer content after short write"); + mShortWriteBufferCheck = nullptr; + } +#endif + + void SetSharedOwningReference(mozilla::psm::SharedSSLState* ref); + + nsresult SetResumptionTokenFromExternalCache(PRFileDesc* fd); + + void SetPreliminaryHandshakeInfo(const SSLChannelInfo& channelInfo, + const SSLCipherSuiteInfo& cipherInfo); + + void SetPendingSelectClientAuthCertificate( + nsCOMPtr<nsIRunnable>&& selectClientAuthCertificate) { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + MOZ_LOG( + gPIPNSSLog, mozilla::LogLevel::Debug, + ("[%p] setting pending select client auth certificate", (void*)mFd)); + // If the connection corresponding to this socket hasn't been claimed, it + // is a speculative connection. The connection will block until the "choose + // a client auth certificate" dialog has been shown. The dialog will only + // be shown when this connection gets claimed. However, necko will never + // claim the connection as long as it is blocking. Thus, this connection + // can't proceed, so it's best to cancel it. Necko will create a new, + // non-speculative connection instead. + if (!mClaimed) { + SetCanceled(PR_CONNECT_RESET_ERROR); + } else { + mPendingSelectClientAuthCertificate = + std::move(selectClientAuthCertificate); + } + } + + void MaybeDispatchSelectClientAuthCertificate() { + COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); + if (!IsWaitingForCertVerification() && mClaimed && + mPendingSelectClientAuthCertificate) { + MOZ_LOG(gPIPNSSLog, mozilla::LogLevel::Debug, + ("[%p] dispatching pending select client auth certificate", + (void*)mFd)); + mozilla::Unused << NS_DispatchToMainThread( + mPendingSelectClientAuthCertificate); + mPendingSelectClientAuthCertificate = nullptr; + } + } + + private: + ~NSSSocketControl() = default; + + PRFileDesc* mFd; + + CertVerificationState mCertVerificationState; + + mozilla::psm::SharedSSLState& mSharedState; + bool mForSTARTTLS; + SSLVersionRange mTLSVersionRange; + bool mHandshakePending; + bool mPreliminaryHandshakeDone; // after false start items are complete + + nsresult ActivateSSL(); + + nsCString mEsniTxt; + nsCString mEchConfig; + bool mEarlyDataAccepted; + bool mDenyClientCert; + bool mFalseStartCallbackCalled; + bool mFalseStarted; + bool mIsFullHandshake; + bool mNotedTimeUntilReady; + EchExtensionStatus mEchExtensionStatus; // Currently only used for telemetry. + bool mSentXyberShare; + bool mHasTls13HandshakeSecrets; + + // True when SSL layer has indicated an "SSL short write", i.e. need + // to call on send one or more times to push all pending data to write. + bool mIsShortWritePending; + + // These are only valid if mIsShortWritePending is true. + // + // Value of the last byte pending from the SSL short write that needs + // to be passed to subsequent calls to send to perform the flush. + unsigned char mShortWritePendingByte; + + // Original amount of data the upper layer has requested to write to + // return after the successful flush. + int32_t mShortWriteOriginalAmount; + +#ifdef DEBUG + mozilla::UniquePtr<char[]> mShortWriteBufferCheck; +#endif + + // mKEA* are used in false start and http/2 detetermination + // Values are from nsITLSSocketControl + int16_t mKEAUsed; + uint32_t mKEAKeyBits; + int16_t mMACAlgorithmUsed; + + uint32_t mProviderTlsFlags; + mozilla::TimeStamp mSocketCreationTimestamp; + uint64_t mPlaintextBytesRead; + + bool mClaimed; + nsCOMPtr<nsIRunnable> mPendingSelectClientAuthCertificate; + + // Regarding the client certificate message in the TLS handshake, RFC 5246 + // (TLS 1.2) says: + // If the certificate_authorities list in the certificate request + // message was non-empty, one of the certificates in the certificate + // chain SHOULD be issued by one of the listed CAs. + // (RFC 8446 (TLS 1.3) has a similar provision) + // These certificates may be known to gecko but not NSS (e.g. enterprise + // intermediates). In order to make these certificates discoverable to NSS + // so it can include them in the message, we cache them here as temporary + // certificates. + mozilla::UniqueCERTCertList mClientCertChain; + + // if non-null this is a reference to the mSharedState (which is + // not an owning reference). If this is used, the info has a private + // state that does not share things like intolerance lists with the + // rest of the session. This is normally used when you have per + // socket tls flags overriding session wide defaults. + RefPtr<mozilla::psm::SharedSSLState> mOwningSharedRef; + + nsCOMPtr<nsITlsHandshakeCallbackListener> mTlsHandshakeCallback; + + uint64_t mBrowserId; +}; + +#endif // NSSSocketControl_h |