diff options
Diffstat (limited to 'security/manager/ssl/nsNSSIOLayer.h')
-rw-r--r-- | security/manager/ssl/nsNSSIOLayer.h | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/security/manager/ssl/nsNSSIOLayer.h b/security/manager/ssl/nsNSSIOLayer.h new file mode 100644 index 0000000000..beb0a67340 --- /dev/null +++ b/security/manager/ssl/nsNSSIOLayer.h @@ -0,0 +1,359 @@ +/* -*- 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 nsNSSIOLayer_h +#define nsNSSIOLayer_h + +#include "CommonSocketControl.h" +#include "mozilla/Assertions.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "nsCOMPtr.h" +#include "nsDataHashtable.h" +#include "nsIProxyInfo.h" +#include "nsISSLSocketControl.h" +#include "nsNSSCertificate.h" +#include "nsTHashtable.h" +#include "sslt.h" + +namespace mozilla { +class OriginAttributes; +namespace psm { +class SharedSSLState; +} // namespace psm +} // namespace mozilla + +using mozilla::OriginAttributes; + +class nsIObserver; + +class nsNSSSocketInfo final : public CommonSocketControl { + public: + nsNSSSocketInfo(mozilla::psm::SharedSSLState& aState, uint32_t providerFlags, + uint32_t providerTlsFlags); + + NS_DECL_ISUPPORTS_INHERITED + + void SetForSTARTTLS(bool aForSTARTTLS); + bool GetForSTARTTLS(); + + nsresult GetFileDescPtr(PRFileDesc** aFilePtr); + nsresult SetFileDescPtr(PRFileDesc* aFilePtr); + + bool IsHandshakePending() const { return mHandshakePending; } + void SetHandshakeNotPending() { mHandshakePending = false; } + + void SetTLSVersionRange(SSLVersionRange range) { mTLSVersionRange = range; } + SSLVersionRange GetTLSVersionRange() const { return mTLSVersionRange; }; + + // From nsISSLSocketControl. + 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; + using nsISSLSocketControl::GetKEAUsed; + NS_IMETHOD GetKEAUsed(int16_t* aKEAUsed) override; + NS_IMETHOD GetKEAKeyBits(uint32_t* aKEAKeyBits) override; + NS_IMETHOD GetProviderTlsFlags(uint32_t* aProviderTlsFlags) 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 GetClientCert(nsIX509Cert** aClientCert) override; + NS_IMETHOD SetClientCert(nsIX509Cert* aClientCert) 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; + + PRStatus CloseSocketAndDestroy(); + + void SetNegotiatedNPN(const char* value, uint32_t length); + void SetEarlyDataAccepted(bool aAccepted); + + void SetHandshakeCompleted(); + bool IsHandshakeCompleted() const { return mHandshakeCompleted; } + void NoteTimeUntilReady(); + + void SetFalseStartCallbackCalled() { mFalseStartCallbackCalled = true; } + void SetFalseStarted() { 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() { mIsFullHandshake = true; } + bool IsFullHandshake() const { return mIsFullHandshake; } + + bool GetJoined() { return mJoined; } + void SetSentClientCert() { mSentClientCert = true; } + + uint32_t GetProviderTlsFlags() const { return mProviderTlsFlags; } + + mozilla::psm::SharedSSLState& SharedState(); + + // XXX: These are only used on for diagnostic purposes + enum CertVerificationState { + before_cert_verification, + waiting_for_cert_verification, + after_cert_verification + }; + void SetCertVerificationWaiting(); + // Use errorCode == 0 to indicate success; + void SetCertVerificationResult(PRErrorCode errorCode) override; + + // for logging only + PRBool IsWaitingForCertVerification() const { + return mCertVerificationState == waiting_for_cert_verification; + } + void AddPlaintextBytesRead(uint64_t val) { mPlaintextBytesRead += val; } + + bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; } + void SetPreliminaryHandshakeDone() { mPreliminaryHandshakeDone = true; } + + void SetKEAUsed(uint16_t kea) { mKEAUsed = kea; } + + void SetKEAKeyBits(uint32_t keaBits) { mKEAKeyBits = keaBits; } + + void SetMACAlgorithmUsed(int16_t mac) { mMACAlgorithmUsed = mac; } + + void SetShortWritePending(int32_t amount, unsigned char data) { + mIsShortWritePending = true; + mShortWriteOriginalAmount = amount; + mShortWritePendingByte = data; + } + + bool IsShortWritePending() { return mIsShortWritePending; } + + unsigned char const* GetShortWritePendingByteRef() { + return &mShortWritePendingByte; + } + + int32_t ResetShortWritePending() { + 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) { + mShortWriteBufferCheck = + mozilla::MakeUnique<char[]>(mShortWriteOriginalAmount); + memcpy(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount); + } + void CheckShortWrittenBuffer(const unsigned char* data, int32_t amount) { + 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(); + + void SetClientCertChain(mozilla::UniqueCERTCertList&& clientCertChain) { + mClientCertChain = std::move(clientCertChain); + } + + protected: + virtual ~nsNSSSocketInfo(); + + private: + 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; + nsCString mPeerId; + bool mEarlyDataAccepted; + bool mDenyClientCert; + bool mFalseStartCallbackCalled; + bool mFalseStarted; + bool mIsFullHandshake; + bool mNotedTimeUntilReady; + + // 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 nsISSLSocketControl + int16_t mKEAUsed; + uint32_t mKEAKeyBits; + int16_t mMACAlgorithmUsed; + + uint32_t mProviderTlsFlags; + mozilla::TimeStamp mSocketCreationTimestamp; + uint64_t mPlaintextBytesRead; + + nsCOMPtr<nsIX509Cert> mClientCert; + // 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; +}; + +// This class is used to store the needed information for invoking the client +// cert selection UI. +class ClientAuthInfo final { + public: + explicit ClientAuthInfo(const nsACString& hostName, + const OriginAttributes& originAttributes, + int32_t port, uint32_t providerFlags, + uint32_t providerTlsFlags, nsIX509Cert* clientCert); + ~ClientAuthInfo() = default; + ClientAuthInfo(ClientAuthInfo&& aOther) noexcept; + + const nsACString& HostName() const; + const OriginAttributes& OriginAttributesRef() const; + int32_t Port() const; + already_AddRefed<nsIX509Cert> GetClientCert() const; + uint32_t ProviderFlags() const; + uint32_t ProviderTlsFlags() const; + + private: + ClientAuthInfo(const ClientAuthInfo&) = delete; + void operator=(const ClientAuthInfo&) = delete; + + nsCString mHostName; + OriginAttributes mOriginAttributes; + int32_t mPort; + uint32_t mProviderFlags; + uint32_t mProviderTlsFlags; + nsCOMPtr<nsIX509Cert> mClientCert; +}; + +class nsSSLIOLayerHelpers { + public: + explicit nsSSLIOLayerHelpers(uint32_t aTlsFlags = 0); + ~nsSSLIOLayerHelpers(); + + nsresult Init(); + void Cleanup(); + + static bool nsSSLIOLayerInitialized; + static PRDescIdentity nsSSLIOLayerIdentity; + static PRDescIdentity nsSSLPlaintextLayerIdentity; + static PRIOMethods nsSSLIOLayerMethods; + static PRIOMethods nsSSLPlaintextLayerMethods; + + bool mTreatUnsafeNegotiationAsBroken; + + void setTreatUnsafeNegotiationAsBroken(bool broken); + bool treatUnsafeNegotiationAsBroken(); + + private: + struct IntoleranceEntry { + uint16_t tolerant; + uint16_t intolerant; + PRErrorCode intoleranceReason; + + void AssertInvariant() const { + MOZ_ASSERT(intolerant == 0 || tolerant < intolerant); + } + }; + nsDataHashtable<nsCStringHashKey, IntoleranceEntry> mTLSIntoleranceInfo; + // Sites that require insecure fallback to TLS 1.0, set by the pref + // security.tls.insecure_fallback_hosts, which is a comma-delimited + // list of domain names. + nsTHashtable<nsCStringHashKey> mInsecureFallbackSites; + + public: + void rememberTolerantAtVersion(const nsACString& hostname, int16_t port, + uint16_t tolerant); + bool fallbackLimitReached(const nsACString& hostname, uint16_t intolerant); + bool rememberIntolerantAtVersion(const nsACString& hostname, int16_t port, + uint16_t intolerant, uint16_t minVersion, + PRErrorCode intoleranceReason); + void forgetIntolerance(const nsACString& hostname, int16_t port); + void adjustForTLSIntolerance(const nsACString& hostname, int16_t port, + /*in/out*/ SSLVersionRange& range); + PRErrorCode getIntoleranceReason(const nsACString& hostname, int16_t port); + + void clearStoredData(); + void loadVersionFallbackLimit(); + void setInsecureFallbackSites(const nsCString& str); + void initInsecureFallbackSites(); + bool isPublic() const; + void removeInsecureFallbackSite(const nsACString& hostname, uint16_t port); + bool isInsecureFallbackSite(const nsACString& hostname); + + uint16_t mVersionFallbackLimit; + + private: + mozilla::Mutex mutex; + nsCOMPtr<nsIObserver> mPrefObserver; + uint32_t mTlsFlags; +}; + +nsresult nsSSLIOLayerNewSocket(int32_t family, const char* host, int32_t port, + nsIProxyInfo* proxy, + const OriginAttributes& originAttributes, + PRFileDesc** fd, nsISupports** securityInfo, + bool forSTARTTLS, uint32_t flags, + uint32_t tlsFlags); + +nsresult nsSSLIOLayerAddToSocket(int32_t family, const char* host, int32_t port, + nsIProxyInfo* proxy, + const OriginAttributes& originAttributes, + PRFileDesc* fd, nsISupports** securityInfo, + bool forSTARTTLS, uint32_t flags, + uint32_t tlsFlags); + +SECStatus DoGetClientAuthData(ClientAuthInfo&& info, + const mozilla::UniqueCERTCertificate& serverCert, + nsTArray<nsTArray<uint8_t>>&& collectedCANames, + mozilla::UniqueCERTCertificate& outCert, + mozilla::UniqueSECKEYPrivateKey& outKey, + mozilla::UniqueCERTCertList& outBuiltChain); + +#endif // nsNSSIOLayer_h |