1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542 |
- /* -*- 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/. */
- #include "nsNSSIOLayer.h"
- #include <algorithm>
- #include "NSSCertDBTrustDomain.h"
- #include "NSSErrorsService.h"
- #include "PSMRunnable.h"
- #include "SSLServerCertVerification.h"
- #include "ScopedNSSTypes.h"
- #include "SharedSSLState.h"
- #include "keyhi.h"
- #include "mozilla/Casting.h"
- #include "mozilla/DebugOnly.h"
- #include "mozilla/Logging.h"
- #include "mozilla/Move.h"
- #include "mozilla/Preferences.h"
- #include "nsArray.h"
- #include "nsArrayUtils.h"
- #include "nsCharSeparatedTokenizer.h"
- #include "nsClientAuthRemember.h"
- #include "nsContentUtils.h"
- #include "nsIClientAuthDialogs.h"
- #include "nsIConsoleService.h"
- #include "nsIPrefService.h"
- #include "nsISocketProvider.h"
- #include "nsIWebProgressListener.h"
- #include "nsNSSCertHelper.h"
- #include "nsNSSComponent.h"
- #include "nsPrintfCString.h"
- #include "nsServiceManagerUtils.h"
- #include "pkix/pkixtypes.h"
- #include "prmem.h"
- #include "prnetdb.h"
- #include "secder.h"
- #include "secerr.h"
- #include "ssl.h"
- #include "sslerr.h"
- #include "sslproto.h"
- using namespace mozilla;
- using namespace mozilla::psm;
- //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
- //reports when doing SSL read/write
- //#define DUMP_BUFFER //Enable this define along with
- //DEBUG_SSL_VERBOSE to dump SSL
- //read/write buffer to a log.
- //Uses PR_LOG except on Mac where
- //we always write out to our own
- //file.
- namespace {
- #define MAX_ALPN_LENGTH 255
- void
- getSiteKey(const nsACString& hostName, uint16_t port,
- /*out*/ nsCSubstring& key)
- {
- key = hostName;
- key.AppendASCII(":");
- key.AppendInt(port);
- }
- // Historically, we have required that the server negotiate ALPN or NPN in
- // order to false start, as a compatibility hack to work around
- // implementations that just stop responding during false start. However, now
- // false start is resricted to modern crypto (TLS 1.2 and AEAD cipher suites)
- // so it is less likely that requring NPN or ALPN is still necessary.
- static const bool FALSE_START_REQUIRE_NPN_DEFAULT = false;
- } // unnamed namespace
- extern LazyLogModule gPIPNSSLog;
- nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
- : mFd(nullptr),
- mCertVerificationState(before_cert_verification),
- mSharedState(aState),
- mForSTARTTLS(false),
- mHandshakePending(true),
- mRememberClientAuthCertificate(false),
- mPreliminaryHandshakeDone(false),
- mNPNCompleted(false),
- mEarlyDataAccepted(false),
- mFalseStartCallbackCalled(false),
- mFalseStarted(false),
- mIsFullHandshake(false),
- mHandshakeCompleted(false),
- mJoined(false),
- mSentClientCert(false),
- mNotedTimeUntilReady(false),
- mFailedVerification(false),
- mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
- mKEAKeyBits(0),
- mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
- mMACAlgorithmUsed(nsISSLSocketControl::SSL_MAC_UNKNOWN),
- mBypassAuthentication(false),
- mProviderFlags(providerFlags),
- mSocketCreationTimestamp(TimeStamp::Now()),
- mPlaintextBytesRead(0),
- mClientCert(nullptr)
- {
- mTLSVersionRange.min = 0;
- mTLSVersionRange.max = 0;
- }
- nsNSSSocketInfo::~nsNSSSocketInfo()
- {
- }
- NS_IMPL_ISUPPORTS_INHERITED(nsNSSSocketInfo, TransportSecurityInfo,
- nsISSLSocketControl,
- nsIClientAuthUserDecision)
- NS_IMETHODIMP
- nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags)
- {
- *aProviderFlags = mProviderFlags;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetKEAUsed(int16_t* aKea)
- {
- *aKea = mKEAUsed;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetKEAKeyBits(uint32_t* aKeyBits)
- {
- *aKeyBits = mKEAKeyBits;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed)
- {
- *aSSLVersionUsed = mSSLVersionUsed;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetSSLVersionOffered(int16_t* aSSLVersionOffered)
- {
- *aSSLVersionOffered = mTLSVersionRange.max;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetMACAlgorithmUsed(int16_t* aMac)
- {
- *aMac = mMACAlgorithmUsed;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetClientCert(nsIX509Cert** aClientCert)
- {
- NS_ENSURE_ARG_POINTER(aClientCert);
- *aClientCert = mClientCert;
- NS_IF_ADDREF(*aClientCert);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::SetClientCert(nsIX509Cert* aClientCert)
- {
- mClientCert = aClientCert;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetBypassAuthentication(bool* arg)
- {
- *arg = mBypassAuthentication;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetFailedVerification(bool* arg)
- {
- *arg = mFailedVerification;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember)
- {
- NS_ENSURE_ARG_POINTER(aRemember);
- *aRemember = mRememberClientAuthCertificate;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::SetRememberClientAuthCertificate(bool aRemember)
- {
- mRememberClientAuthCertificate = aRemember;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks)
- {
- *aCallbacks = mCallbacks;
- NS_IF_ADDREF(*aCallbacks);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
- {
- if (!aCallbacks) {
- mCallbacks = nullptr;
- return NS_OK;
- }
- mCallbacks = aCallbacks;
- return NS_OK;
- }
- void
- nsNSSSocketInfo::NoteTimeUntilReady()
- {
- if (mNotedTimeUntilReady)
- return;
- mNotedTimeUntilReady = true;
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] nsNSSSocketInfo::NoteTimeUntilReady\n", mFd));
- }
- void
- nsNSSSocketInfo::SetHandshakeCompleted()
- {
- // Remove the plain text layer as it is not needed anymore.
- // The plain text layer is not always present - so its not a fatal error
- // if it cannot be removed
- PRFileDesc* poppedPlaintext =
- PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
- if (poppedPlaintext) {
- PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
- poppedPlaintext->dtor(poppedPlaintext);
- }
- mHandshakeCompleted = true;
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*) mFd));
- mIsFullHandshake = false; // reset for next handshake on this connection
- }
- void
- nsNSSSocketInfo::SetNegotiatedNPN(const char* value, uint32_t length)
- {
- if (!value) {
- mNegotiatedNPN.Truncate();
- } else {
- mNegotiatedNPN.Assign(value, length);
- }
- mNPNCompleted = true;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN)
- {
- if (!mNPNCompleted)
- return NS_ERROR_NOT_CONNECTED;
- aNegotiatedNPN = mNegotiatedNPN;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetAlpnEarlySelection(nsACString& aAlpnSelected)
- {
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown() || isPK11LoggedOut()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- SSLNextProtoState alpnState;
- unsigned char chosenAlpn[MAX_ALPN_LENGTH];
- unsigned int chosenAlpnLen;
- SECStatus rv = SSL_GetNextProto(mFd, &alpnState, chosenAlpn, &chosenAlpnLen,
- AssertedCast<unsigned int>(ArrayLength(chosenAlpn)));
- if (rv != SECSuccess || alpnState != SSL_NEXT_PROTO_EARLY_VALUE ||
- chosenAlpnLen == 0) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- aAlpnSelected.Assign(BitwiseCast<char*, unsigned char*>(chosenAlpn),
- chosenAlpnLen);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::GetEarlyDataAccepted(bool* aAccepted)
- {
- *aAccepted = mEarlyDataAccepted;
- return NS_OK;
- }
- void
- nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted)
- {
- mEarlyDataAccepted = aAccepted;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::DriveHandshake()
- {
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown() || isPK11LoggedOut()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- if (!mFd) {
- return NS_ERROR_FAILURE;
- }
- PRErrorCode errorCode = GetErrorCode();
- if (errorCode) {
- return GetXPCOMFromNSSError(errorCode);
- }
- SECStatus rv = SSL_ForceHandshake(mFd);
- if (rv != SECSuccess) {
- errorCode = PR_GetError();
- if (errorCode == PR_WOULD_BLOCK_ERROR) {
- return NS_BASE_STREAM_WOULD_BLOCK;
- }
- SetCanceled(errorCode, PlainErrorMessage);
- return GetXPCOMFromNSSError(errorCode);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
- {
- NS_ENSURE_ARG(_retval);
- *_retval = false;
- // If this is the same hostname then the certicate status does not
- // need to be considered. They are joinable.
- if (hostname.Equals(GetHostName())) {
- *_retval = true;
- return NS_OK;
- }
- // Before checking the server certificate we need to make sure the
- // handshake has completed.
- if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->HasServerCert()) {
- return NS_OK;
- }
- // If the cert has error bits (e.g. it is untrusted) then do not join.
- // The value of mHaveCertErrorBits is only reliable because we know that
- // the handshake completed.
- if (SSLStatus()->mHaveCertErrorBits)
- return NS_OK;
- // If the connection is using client certificates then do not join
- // because the user decides on whether to send client certs to hosts on a
- // per-domain basis.
- if (mSentClientCert)
- return NS_OK;
- // Ensure that the server certificate covers the hostname that would
- // like to join this connection
- UniqueCERTCertificate nssCert;
- nsCOMPtr<nsIX509Cert> cert;
- if (NS_FAILED(SSLStatus()->GetServerCert(getter_AddRefs(cert)))) {
- return NS_OK;
- }
- if (cert) {
- nssCert.reset(cert->GetCert());
- }
- if (!nssCert) {
- return NS_OK;
- }
- // Attempt to verify the joinee's certificate using the joining hostname.
- // This ensures that any hostname-specific verification logic (e.g. key
- // pinning) is satisfied by the joinee's certificate chain.
- // This verification only uses local information; since we're on the network
- // thread, we would be blocking on ourselves if we attempted any network i/o.
- // TODO(bug 1056935): The certificate chain built by this verification may be
- // different than the certificate chain originally built during the joined
- // connection's TLS handshake. Consequently, we may report a wrong and/or
- // misleading certificate chain for HTTP transactions coalesced onto this
- // connection. This may become problematic in the future. For example,
- // if/when we begin relying on intermediate certificates being stored in the
- // securityInfo of a cached HTTPS response, that cached certificate chain may
- // actually be the wrong chain. We should consider having JoinConnection
- // return the certificate chain built here, so that the calling Necko code
- // can associate the correct certificate chain with the HTTP transactions it
- // is trying to join onto this connection.
- RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
- if (!certVerifier) {
- return NS_OK;
- }
- nsAutoCString hostnameFlat(PromiseFlatCString(hostname));
- CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY;
- UniqueCERTCertList unusedBuiltChain;
- mozilla::pkix::Result result =
- certVerifier->VerifySSLServerCert(nssCert,
- nullptr, // stapledOCSPResponse
- nullptr, // sctsFromTLSExtension
- mozilla::pkix::Now(),
- nullptr, // pinarg
- hostnameFlat.get(),
- unusedBuiltChain,
- false, // save intermediates
- flags);
- if (result != mozilla::pkix::Success) {
- return NS_OK;
- }
- // All tests pass
- *_retval = true;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
- const nsACString& hostname,
- int32_t port,
- bool* _retval)
- {
- *_retval = false;
- // Different ports may not be joined together
- if (port != GetPort())
- return NS_OK;
- // Make sure NPN has been completed and matches requested npnProtocol
- if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
- return NS_OK;
- if (mBypassAuthentication) {
- // An unauthenticated connection does not know whether or not it
- // is acceptable for a particular hostname
- return NS_OK;
- }
- IsAcceptableForHost(hostname, _retval);
- if (*_retval) {
- // All tests pass - this is joinable
- mJoined = true;
- }
- return NS_OK;
- }
- bool
- nsNSSSocketInfo::GetForSTARTTLS()
- {
- return mForSTARTTLS;
- }
- void
- nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS)
- {
- mForSTARTTLS = aForSTARTTLS;
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::ProxyStartSSL()
- {
- return ActivateSSL();
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::StartTLS()
- {
- return ActivateSSL();
- }
- NS_IMETHODIMP
- nsNSSSocketInfo::SetNPNList(nsTArray<nsCString>& protocolArray)
- {
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown())
- return NS_ERROR_NOT_AVAILABLE;
- if (!mFd)
- return NS_ERROR_FAILURE;
- // the npn list is a concatenated list of 8 bit byte strings.
- nsCString npnList;
- for (uint32_t index = 0; index < protocolArray.Length(); ++index) {
- if (protocolArray[index].IsEmpty() ||
- protocolArray[index].Length() > 255)
- return NS_ERROR_ILLEGAL_VALUE;
- npnList.Append(protocolArray[index].Length());
- npnList.Append(protocolArray[index]);
- }
- if (SSL_SetNextProtoNego(
- mFd,
- BitwiseCast<const unsigned char*, const char*>(npnList.get()),
- npnList.Length()) != SECSuccess)
- return NS_ERROR_FAILURE;
- return NS_OK;
- }
- nsresult
- nsNSSSocketInfo::ActivateSSL()
- {
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown())
- return NS_ERROR_NOT_AVAILABLE;
- if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, true))
- return NS_ERROR_FAILURE;
- if (SECSuccess != SSL_ResetHandshake(mFd, false))
- return NS_ERROR_FAILURE;
- mHandshakePending = true;
- return NS_OK;
- }
- nsresult
- nsNSSSocketInfo::GetFileDescPtr(PRFileDesc** aFilePtr)
- {
- *aFilePtr = mFd;
- return NS_OK;
- }
- nsresult
- nsNSSSocketInfo::SetFileDescPtr(PRFileDesc* aFilePtr)
- {
- mFd = aFilePtr;
- return NS_OK;
- }
- void
- nsNSSSocketInfo::SetCertVerificationWaiting()
- {
- // mCertVerificationState may be before_cert_verification for the first
- // handshake on the connection, or after_cert_verification for subsequent
- // renegotiation handshakes.
- NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
- "Invalid state transition to waiting_for_cert_verification");
- mCertVerificationState = waiting_for_cert_verification;
- }
- // Be careful that SetCertVerificationResult does NOT get called while we are
- // processing a SSL callback function, because SSL_AuthCertificateComplete will
- // attempt to acquire locks that are already held by libssl when it calls
- // callbacks.
- void
- nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
- SSLErrorMessageType errorMessageType)
- {
- NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
- "Invalid state transition to cert_verification_finished");
- if (mFd) {
- SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode);
- // Only replace errorCode if there was originally no error
- if (rv != SECSuccess && errorCode == 0) {
- errorCode = PR_GetError();
- errorMessageType = PlainErrorMessage;
- if (errorCode == 0) {
- NS_ERROR("SSL_AuthCertificateComplete didn't set error code");
- errorCode = PR_INVALID_STATE_ERROR;
- }
- }
- }
- if (errorCode) {
- mFailedVerification = true;
- SetCanceled(errorCode, errorMessageType);
- }
- mCertVerificationState = after_cert_verification;
- }
- SharedSSLState&
- nsNSSSocketInfo::SharedState()
- {
- return mSharedState;
- }
- void nsSSLIOLayerHelpers::Cleanup()
- {
- MutexAutoLock lock(mutex);
- mTLSIntoleranceInfo.Clear();
- mInsecureFallbackSites.Clear();
- }
- static void
- nsHandleSSLError(nsNSSSocketInfo* socketInfo,
- ::mozilla::psm::SSLErrorMessageType errtype,
- PRErrorCode err)
- {
- if (!NS_IsMainThread()) {
- NS_ERROR("nsHandleSSLError called off the main thread");
- return;
- }
- // SetCanceled is only called by the main thread or the socket transport
- // thread. Whenever this function is called on the main thread, the SSL
- // thread is blocked on it. So, no mutex is necessary for
- // SetCanceled()/GetError*().
- if (socketInfo->GetErrorCode()) {
- // If the socket has been flagged as canceled,
- // the code who did was responsible for setting the error code.
- return;
- }
- // We must cancel first, which sets the error code.
- socketInfo->SetCanceled(err, PlainErrorMessage);
- nsXPIDLString errorString;
- socketInfo->GetErrorLogMessage(err, errtype, errorString);
- if (!errorString.IsEmpty()) {
- nsContentUtils::LogSimpleConsoleError(errorString, "SSL");
- }
- }
- namespace {
- enum Operation { reading, writing, not_reading_or_writing };
- int32_t checkHandshake(int32_t bytesTransfered, bool wasReading,
- PRFileDesc* ssl_layer_fd,
- nsNSSSocketInfo* socketInfo);
- nsNSSSocketInfo*
- getSocketInfoIfRunning(PRFileDesc* fd, Operation op,
- const nsNSSShutDownPreventionLock& /*proofOfLock*/)
- {
- if (!fd || !fd->lower || !fd->secret ||
- fd->identity != nsSSLIOLayerHelpers::nsSSLIOLayerIdentity) {
- NS_ERROR("bad file descriptor passed to getSocketInfoIfRunning");
- PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
- return nullptr;
- }
- nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
- if (socketInfo->isAlreadyShutDown() || socketInfo->isPK11LoggedOut()) {
- PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
- return nullptr;
- }
- if (socketInfo->GetErrorCode()) {
- PRErrorCode err = socketInfo->GetErrorCode();
- PR_SetError(err, 0);
- if (op == reading || op == writing) {
- // We must do TLS intolerance checks for reads and writes, for timeouts
- // in particular.
- (void) checkHandshake(-1, op == reading, fd, socketInfo);
- }
- // If we get here, it is probably because cert verification failed and this
- // is the first I/O attempt since that failure.
- return nullptr;
- }
- return socketInfo;
- }
- } // namespace
- static PRStatus
- nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
- PRIntervalTime timeout)
- {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] connecting SSL socket\n",
- (void*) fd));
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
- return PR_FAILURE;
- PRStatus status = fd->lower->methods->connect(fd->lower, addr, timeout);
- if (status != PR_SUCCESS) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("[%p] Lower layer connect error: %d\n",
- (void*) fd, PR_GetError()));
- return status;
- }
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Connect\n", (void*) fd));
- return status;
- }
- void
- nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString& hostName,
- int16_t port, uint16_t tolerant)
- {
- nsCString key;
- getSiteKey(hostName, port, key);
- MutexAutoLock lock(mutex);
- IntoleranceEntry entry;
- if (mTLSIntoleranceInfo.Get(key, &entry)) {
- entry.AssertInvariant();
- entry.tolerant = std::max(entry.tolerant, tolerant);
- if (entry.intolerant != 0 && entry.intolerant <= entry.tolerant) {
- entry.intolerant = entry.tolerant + 1;
- entry.intoleranceReason = 0; // lose the reason
- }
- if (entry.strongCipherStatus == StrongCipherStatusUnknown) {
- entry.strongCipherStatus = StrongCiphersWorked;
- }
- } else {
- entry.tolerant = tolerant;
- entry.intolerant = 0;
- entry.intoleranceReason = 0;
- entry.strongCipherStatus = StrongCiphersWorked;
- }
- entry.AssertInvariant();
- mTLSIntoleranceInfo.Put(key, entry);
- }
- void
- nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName,
- int16_t port)
- {
- nsCString key;
- getSiteKey(hostName, port, key);
- MutexAutoLock lock(mutex);
- IntoleranceEntry entry;
- if (mTLSIntoleranceInfo.Get(key, &entry)) {
- entry.AssertInvariant();
- entry.intolerant = 0;
- entry.intoleranceReason = 0;
- if (entry.strongCipherStatus != StrongCiphersWorked) {
- entry.strongCipherStatus = StrongCipherStatusUnknown;
- }
- entry.AssertInvariant();
- mTLSIntoleranceInfo.Put(key, entry);
- }
- }
- bool
- nsSSLIOLayerHelpers::fallbackLimitReached(const nsACString& hostName,
- uint16_t intolerant)
- {
- if (isInsecureFallbackSite(hostName)) {
- return intolerant <= SSL_LIBRARY_VERSION_TLS_1_0;
- }
- return intolerant <= mVersionFallbackLimit;
- }
- // returns true if we should retry the handshake
- bool
- nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
- int16_t port,
- uint16_t minVersion,
- uint16_t intolerant,
- PRErrorCode intoleranceReason)
- {
- if (intolerant <= minVersion || fallbackLimitReached(hostName, intolerant)) {
- // We can't fall back any further. Assume that intolerance isn't the issue.
- forgetIntolerance(hostName, port);
- return false;
- }
- nsCString key;
- getSiteKey(hostName, port, key);
- MutexAutoLock lock(mutex);
- IntoleranceEntry entry;
- if (mTLSIntoleranceInfo.Get(key, &entry)) {
- entry.AssertInvariant();
- if (intolerant <= entry.tolerant) {
- // We already know the server is tolerant at an equal or higher version.
- return false;
- }
- if ((entry.intolerant != 0 && intolerant >= entry.intolerant)) {
- // We already know that the server is intolerant at a lower version.
- return true;
- }
- } else {
- entry.tolerant = 0;
- entry.strongCipherStatus = StrongCipherStatusUnknown;
- }
- entry.intolerant = intolerant;
- entry.intoleranceReason = intoleranceReason;
- entry.AssertInvariant();
- mTLSIntoleranceInfo.Put(key, entry);
- return true;
- }
- // returns true if we should retry the handshake
- bool
- nsSSLIOLayerHelpers::rememberStrongCiphersFailed(const nsACString& hostName,
- int16_t port,
- PRErrorCode intoleranceReason)
- {
- nsCString key;
- getSiteKey(hostName, port, key);
- MutexAutoLock lock(mutex);
- IntoleranceEntry entry;
- if (mTLSIntoleranceInfo.Get(key, &entry)) {
- entry.AssertInvariant();
- if (entry.strongCipherStatus != StrongCipherStatusUnknown) {
- // We already know if the server supports a strong cipher.
- return false;
- }
- } else {
- entry.tolerant = 0;
- entry.intolerant = 0;
- entry.intoleranceReason = intoleranceReason;
- }
- entry.strongCipherStatus = StrongCiphersFailed;
- entry.AssertInvariant();
- mTLSIntoleranceInfo.Put(key, entry);
- return true;
- }
- void
- nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString& hostName,
- int16_t port,
- /*in/out*/ SSLVersionRange& range,
- /*out*/ StrongCipherStatus& strongCipherStatus)
- {
- IntoleranceEntry entry;
- {
- nsCString key;
- getSiteKey(hostName, port, key);
- MutexAutoLock lock(mutex);
- if (!mTLSIntoleranceInfo.Get(key, &entry)) {
- return;
- }
- }
- entry.AssertInvariant();
- if (entry.intolerant != 0) {
- // We've tried connecting at a higher range but failed, so try at the
- // version we haven't tried yet, unless we have reached the minimum.
- if (range.min < entry.intolerant) {
- range.max = entry.intolerant - 1;
- }
- }
- strongCipherStatus = entry.strongCipherStatus;
- }
- PRErrorCode
- nsSSLIOLayerHelpers::getIntoleranceReason(const nsACString& hostName,
- int16_t port)
- {
- IntoleranceEntry entry;
- {
- nsCString key;
- getSiteKey(hostName, port, key);
- MutexAutoLock lock(mutex);
- if (!mTLSIntoleranceInfo.Get(key, &entry)) {
- return 0;
- }
- }
- entry.AssertInvariant();
- return entry.intoleranceReason;
- }
- bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
- PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
- PRDescIdentity nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity;
- PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
- PRIOMethods nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods;
- static PRStatus
- nsSSLIOLayerClose(PRFileDesc* fd)
- {
- nsNSSShutDownPreventionLock locker;
- if (!fd)
- return PR_FAILURE;
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Shutting down socket\n",
- (void*) fd));
- nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
- NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
- return socketInfo->CloseSocketAndDestroy(locker);
- }
- PRStatus
- nsNSSSocketInfo::CloseSocketAndDestroy(
- const nsNSSShutDownPreventionLock& /*proofOfLock*/)
- {
- PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER);
- NS_ASSERTION(popped &&
- popped->identity == nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
- "SSL Layer not on top of stack");
- // The plain text layer is not always present - so its not a fatal error
- // if it cannot be removed
- PRFileDesc* poppedPlaintext =
- PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
- if (poppedPlaintext) {
- PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
- poppedPlaintext->dtor(poppedPlaintext);
- }
- PRStatus status = mFd->methods->close(mFd);
- // the nsNSSSocketInfo instance can out-live the connection, so we need some
- // indication that the connection has been closed. mFd == nullptr is that
- // indication. This is needed, for example, when the connection is closed
- // before we have finished validating the server's certificate.
- mFd = nullptr;
- if (status != PR_SUCCESS) return status;
- popped->identity = PR_INVALID_IO_LAYER;
- NS_RELEASE_THIS();
- popped->dtor(popped);
- return PR_SUCCESS;
- }
- #if defined(DEBUG_SSL_VERBOSE) && defined(DUMP_BUFFER)
- // Dumps a (potentially binary) buffer using SSM_DEBUG. (We could have used
- // the version in ssltrace.c, but that's specifically tailored to SSLTRACE.)
- #define DUMPBUF_LINESIZE 24
- static void
- nsDumpBuffer(unsigned char* buf, int len)
- {
- char hexbuf[DUMPBUF_LINESIZE*3+1];
- char chrbuf[DUMPBUF_LINESIZE+1];
- static const char* hex = "0123456789abcdef";
- int i = 0;
- int l = 0;
- char ch;
- char* c;
- char* h;
- if (len == 0)
- return;
- hexbuf[DUMPBUF_LINESIZE*3] = '\0';
- chrbuf[DUMPBUF_LINESIZE] = '\0';
- (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
- (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
- h = hexbuf;
- c = chrbuf;
- while (i < len) {
- ch = buf[i];
- if (l == DUMPBUF_LINESIZE) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("%s%s\n", hexbuf, chrbuf));
- (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
- (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
- h = hexbuf;
- c = chrbuf;
- l = 0;
- }
- // Convert a character to hex.
- *h++ = hex[(ch >> 4) & 0xf];
- *h++ = hex[ch & 0xf];
- h++;
- // Put the character (if it's printable) into the character buffer.
- if ((ch >= 0x20) && (ch <= 0x7e)) {
- *c++ = ch;
- } else {
- *c++ = '.';
- }
- i++; l++;
- }
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("%s%s\n", hexbuf, chrbuf));
- }
- #define DEBUG_DUMP_BUFFER(buf,len) nsDumpBuffer(buf,len)
- #else
- #define DEBUG_DUMP_BUFFER(buf,len)
- #endif
- class SSLErrorRunnable : public SyncRunnableBase
- {
- public:
- SSLErrorRunnable(nsNSSSocketInfo* infoObject,
- ::mozilla::psm::SSLErrorMessageType errtype,
- PRErrorCode errorCode)
- : mInfoObject(infoObject)
- , mErrType(errtype)
- , mErrorCode(errorCode)
- {
- }
- virtual void RunOnTargetThread()
- {
- nsHandleSSLError(mInfoObject, mErrType, mErrorCode);
- }
- RefPtr<nsNSSSocketInfo> mInfoObject;
- ::mozilla::psm::SSLErrorMessageType mErrType;
- const PRErrorCode mErrorCode;
- };
- namespace {
- bool
- retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
- {
- // This function is supposed to decide which error codes should
- // be used to conclude server is TLS intolerant.
- // Note this only happens during the initial SSL handshake.
- SSLVersionRange range = socketInfo->GetTLSVersionRange();
- nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers();
- if (err == SSL_ERROR_UNSUPPORTED_VERSION &&
- range.min == SSL_LIBRARY_VERSION_TLS_1_0) {
- socketInfo->SetSecurityState(nsIWebProgressListener::STATE_IS_INSECURE |
- nsIWebProgressListener::STATE_USES_SSL_3);
- }
- // NSS will return SSL_ERROR_RX_MALFORMED_SERVER_HELLO if anti-downgrade
- // detected the downgrade.
- if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT ||
- err == SSL_ERROR_RX_MALFORMED_SERVER_HELLO) {
- // This is a clear signal that we've fallen back too many versions. Treat
- // this as a hard failure, but forget any intolerance so that later attempts
- // don't use this version (i.e., range.max) and trigger the error again.
- helpers.forgetIntolerance(socketInfo->GetHostName(),
- socketInfo->GetPort());
- return false;
- }
- // Disallow PR_CONNECT_RESET_ERROR if fallback limit reached.
- bool fallbackLimitReached =
- helpers.fallbackLimitReached(socketInfo->GetHostName(), range.max);
- if (err == PR_CONNECT_RESET_ERROR && fallbackLimitReached) {
- return false;
- }
- if ((err == SSL_ERROR_NO_CYPHER_OVERLAP || err == PR_END_OF_FILE_ERROR ||
- err == PR_CONNECT_RESET_ERROR) &&
- nsNSSComponent::AreAnyWeakCiphersEnabled()) {
- if (helpers.isInsecureFallbackSite(socketInfo->GetHostName()) ||
- helpers.mUnrestrictedRC4Fallback) {
- if (helpers.rememberStrongCiphersFailed(socketInfo->GetHostName(),
- socketInfo->GetPort(), err)) {
- return true;
- }
- }
- }
- // When not using a proxy we'll see a connection reset error.
- // When using a proxy, we'll see an end of file error.
- // Don't allow STARTTLS connections to fall back on connection resets or
- // EOF.
- if ((err == PR_CONNECT_RESET_ERROR || err == PR_END_OF_FILE_ERROR)
- && socketInfo->GetForSTARTTLS()) {
- return false;
- }
- if (!helpers.rememberIntolerantAtVersion(socketInfo->GetHostName(),
- socketInfo->GetPort(),
- range.min, range.max, err)) {
- return false;
- }
- return true;
- }
- // Ensure that we haven't added too many errors to fit.
- static_assert((SSL_ERROR_END_OF_LIST - SSL_ERROR_BASE) <= 256,
- "too many SSL errors");
- static_assert((SEC_ERROR_END_OF_LIST - SEC_ERROR_BASE) <= 256,
- "too many SEC errors");
- static_assert((PR_MAX_ERROR - PR_NSPR_ERROR_BASE) <= 128,
- "too many NSPR errors");
- static_assert((mozilla::pkix::ERROR_BASE - mozilla::pkix::END_OF_LIST) < 31,
- "too many moz::pkix errors");
- int32_t
- checkHandshake(int32_t bytesTransfered, bool wasReading,
- PRFileDesc* ssl_layer_fd, nsNSSSocketInfo* socketInfo)
- {
- const PRErrorCode originalError = PR_GetError();
- PRErrorCode err = originalError;
- // This is where we work around all of those SSL servers that don't
- // conform to the SSL spec and shutdown a connection when we request
- // SSL v3.1 (aka TLS). The spec says the client says what version
- // of the protocol we're willing to perform, in our case SSL v3.1
- // In its response, the server says which version it wants to perform.
- // Many servers out there only know how to do v3.0. Next, we're supposed
- // to send back the version of the protocol we requested (ie v3.1). At
- // this point many servers's implementations are broken and they shut
- // down the connection when they don't see the version they sent back.
- // This is supposed to prevent a man in the middle from forcing one
- // side to dumb down to a lower level of the protocol. Unfortunately,
- // there are enough broken servers out there that such a gross work-around
- // is necessary. :(
- // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown.
- // Simply retry.
- // This depends on the fact that Cert UI will not be shown again,
- // should the user override the bad cert.
- bool handleHandshakeResultNow = socketInfo->IsHandshakePending();
- bool wantRetry = false;
- if (0 > bytesTransfered) {
- if (handleHandshakeResultNow) {
- if (PR_WOULD_BLOCK_ERROR == err) {
- PR_SetError(err, 0);
- return bytesTransfered;
- }
- wantRetry = retryDueToTLSIntolerance(err, socketInfo);
- }
- // This is the common place where we trigger non-cert-errors on a SSL
- // socket. This might be reached at any time of the connection.
- //
- // The socketInfo->GetErrorCode() check is here to ensure we don't try to
- // do the synchronous dispatch to the main thread unnecessarily after we've
- // already handled a certificate error. (SSLErrorRunnable calls
- // nsHandleSSLError, which has logic to avoid replacing the error message,
- // so without the !socketInfo->GetErrorCode(), it would just be an
- // expensive no-op.)
- if (!wantRetry && mozilla::psm::IsNSSErrorCode(err) &&
- !socketInfo->GetErrorCode()) {
- RefPtr<SyncRunnableBase> runnable(new SSLErrorRunnable(socketInfo,
- PlainErrorMessage,
- err));
- (void) runnable->DispatchToMainThreadAndWait();
- }
- } else if (wasReading && 0 == bytesTransfered) {
- // zero bytes on reading, socket closed
- if (handleHandshakeResultNow) {
- wantRetry = retryDueToTLSIntolerance(PR_END_OF_FILE_ERROR, socketInfo);
- }
- }
- if (wantRetry) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] checkHandshake: will retry with lower max TLS version\n",
- ssl_layer_fd));
- // We want to cause the network layer to retry the connection.
- err = PR_CONNECT_RESET_ERROR;
- if (wasReading)
- bytesTransfered = -1;
- }
- // TLS intolerant servers only cause the first transfer to fail, so let's
- // set the HandshakePending attribute to false so that we don't try the logic
- // above again in a subsequent transfer.
- if (handleHandshakeResultNow) {
- socketInfo->SetHandshakeNotPending();
- }
- if (bytesTransfered < 0) {
- // Remember that we encountered an error so that getSocketInfoIfRunning
- // will correctly cause us to fail if another part of Gecko
- // (erroneously) calls an I/O function (PR_Send/PR_Recv/etc.) again on
- // this socket. Note that we use the original error because if we use
- // PR_CONNECT_RESET_ERROR, we'll repeated try to reconnect.
- if (originalError != PR_WOULD_BLOCK_ERROR && !socketInfo->GetErrorCode()) {
- socketInfo->SetCanceled(originalError, PlainErrorMessage);
- }
- PR_SetError(err, 0);
- }
- return bytesTransfered;
- }
- } // namespace
- static int16_t
- nsSSLIOLayerPoll(PRFileDesc* fd, int16_t in_flags, int16_t* out_flags)
- {
- nsNSSShutDownPreventionLock locker;
- if (!out_flags) {
- NS_WARNING("nsSSLIOLayerPoll called with null out_flags");
- return 0;
- }
- *out_flags = 0;
- nsNSSSocketInfo* socketInfo =
- getSocketInfoIfRunning(fd, not_reading_or_writing, locker);
- if (!socketInfo) {
- // If we get here, it is probably because certificate validation failed
- // and this is the first I/O operation after the failure.
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] polling SSL socket right after certificate verification failed "
- "or NSS shutdown or SDR logout %d\n",
- fd, (int) in_flags));
- NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
- "caller did not poll for EXCEPT (canceled)");
- // Since this poll method cannot return errors, we want the caller to call
- // PR_Send/PR_Recv right away to get the error, so we tell that we are
- // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning).
- *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619
- return in_flags;
- }
- MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
- (socketInfo->IsWaitingForCertVerification()
- ? "[%p] polling SSL socket during certificate verification using lower %d\n"
- : "[%p] poll SSL socket using lower %d\n",
- fd, (int) in_flags));
- // We want the handshake to continue during certificate validation, so we
- // don't need to do anything special here. libssl automatically blocks when
- // it reaches any point that would be unsafe to send/receive something before
- // cert validation is complete.
- int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
- MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
- ("[%p] poll SSL socket returned %d\n", (void*) fd, (int) result));
- return result;
- }
- nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
- : mTreatUnsafeNegotiationAsBroken(false)
- , mTLSIntoleranceInfo()
- , mFalseStartRequireNPN(false)
- , mUnrestrictedRC4Fallback(false)
- , mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0)
- , mutex("nsSSLIOLayerHelpers.mutex")
- {
- }
- static int
- _PSM_InvalidInt(void)
- {
- MOZ_ASSERT_UNREACHABLE("I/O method is invalid");
- PR_SetError(PR_INVALID_METHOD_ERROR, 0);
- return -1;
- }
- static int64_t
- _PSM_InvalidInt64(void)
- {
- MOZ_ASSERT_UNREACHABLE("I/O method is invalid");
- PR_SetError(PR_INVALID_METHOD_ERROR, 0);
- return -1;
- }
- static PRStatus
- _PSM_InvalidStatus(void)
- {
- MOZ_ASSERT_UNREACHABLE("I/O method is invalid");
- PR_SetError(PR_INVALID_METHOD_ERROR, 0);
- return PR_FAILURE;
- }
- static PRFileDesc*
- _PSM_InvalidDesc(void)
- {
- MOZ_ASSERT_UNREACHABLE("I/O method is invalid");
- PR_SetError(PR_INVALID_METHOD_ERROR, 0);
- return nullptr;
- }
- static PRStatus
- PSMGetsockname(PRFileDesc* fd, PRNetAddr* addr)
- {
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
- return PR_FAILURE;
- return fd->lower->methods->getsockname(fd->lower, addr);
- }
- static PRStatus
- PSMGetpeername(PRFileDesc* fd, PRNetAddr* addr)
- {
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
- return PR_FAILURE;
- return fd->lower->methods->getpeername(fd->lower, addr);
- }
- static PRStatus
- PSMGetsocketoption(PRFileDesc* fd, PRSocketOptionData* data)
- {
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
- return PR_FAILURE;
- return fd->lower->methods->getsocketoption(fd, data);
- }
- static PRStatus
- PSMSetsocketoption(PRFileDesc* fd, const PRSocketOptionData* data)
- {
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
- return PR_FAILURE;
- return fd->lower->methods->setsocketoption(fd, data);
- }
- static int32_t
- PSMRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
- PRIntervalTime timeout)
- {
- nsNSSShutDownPreventionLock locker;
- nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, reading, locker);
- if (!socketInfo)
- return -1;
- if (flags != PR_MSG_PEEK && flags != 0) {
- PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
- return -1;
- }
- int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
- timeout);
- MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
- ("[%p] read %d bytes\n", (void*) fd, bytesRead));
- #ifdef DEBUG_SSL_VERBOSE
- DEBUG_DUMP_BUFFER((unsigned char*) buf, bytesRead);
- #endif
- return checkHandshake(bytesRead, true, fd, socketInfo);
- }
- static int32_t
- PSMSend(PRFileDesc* fd, const void* buf, int32_t amount, int flags,
- PRIntervalTime timeout)
- {
- nsNSSShutDownPreventionLock locker;
- nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, writing, locker);
- if (!socketInfo)
- return -1;
- if (flags != 0) {
- PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
- return -1;
- }
- #ifdef DEBUG_SSL_VERBOSE
- DEBUG_DUMP_BUFFER((unsigned char*) buf, amount);
- #endif
- int32_t bytesWritten = fd->lower->methods->send(fd->lower, buf, amount,
- flags, timeout);
- MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
- ("[%p] wrote %d bytes\n", fd, bytesWritten));
- return checkHandshake(bytesWritten, false, fd, socketInfo);
- }
- static PRStatus
- PSMBind(PRFileDesc* fd, const PRNetAddr *addr)
- {
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
- return PR_FAILURE;
- return fd->lower->methods->bind(fd->lower, addr);
- }
- static int32_t
- nsSSLIOLayerRead(PRFileDesc* fd, void* buf, int32_t amount)
- {
- return PSMRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
- }
- static int32_t
- nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, int32_t amount)
- {
- return PSMSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
- }
- static PRStatus
- PSMConnectcontinue(PRFileDesc* fd, int16_t out_flags)
- {
- nsNSSShutDownPreventionLock locker;
- if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker)) {
- return PR_FAILURE;
- }
- return fd->lower->methods->connectcontinue(fd, out_flags);
- }
- static int
- PSMAvailable(void)
- {
- // This is called through PR_Available(), but is not implemented in PSM
- PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
- return -1;
- }
- static int64_t
- PSMAvailable64(void)
- {
- // This is called through PR_Available(), but is not implemented in PSM
- PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
- return -1;
- }
- namespace {
- class PrefObserver : public nsIObserver {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIOBSERVER
- explicit PrefObserver(nsSSLIOLayerHelpers* aOwner) : mOwner(aOwner) {}
- protected:
- virtual ~PrefObserver() {}
- private:
- nsSSLIOLayerHelpers* mOwner;
- };
- } // unnamed namespace
- NS_IMPL_ISUPPORTS(PrefObserver, nsIObserver)
- NS_IMETHODIMP
- PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
- const char16_t* someData)
- {
- if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
- NS_ConvertUTF16toUTF8 prefName(someData);
- if (prefName.EqualsLiteral("security.ssl.treat_unsafe_negotiation_as_broken")) {
- bool enabled;
- Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
- mOwner->setTreatUnsafeNegotiationAsBroken(enabled);
- } else if (prefName.EqualsLiteral("security.ssl.false_start.require-npn")) {
- mOwner->mFalseStartRequireNPN =
- Preferences::GetBool("security.ssl.false_start.require-npn",
- FALSE_START_REQUIRE_NPN_DEFAULT);
- } else if (prefName.EqualsLiteral("security.tls.version.fallback-limit")) {
- mOwner->loadVersionFallbackLimit();
- } else if (prefName.EqualsLiteral("security.tls.insecure_fallback_hosts")) {
- // Changes to the whitelist on the public side will update the pref.
- // Don't propagate the changes to the private side.
- if (mOwner->isPublic()) {
- mOwner->initInsecureFallbackSites();
- }
- } else if (prefName.EqualsLiteral("security.tls.unrestricted_rc4_fallback")) {
- mOwner->mUnrestrictedRC4Fallback =
- Preferences::GetBool("security.tls.unrestricted_rc4_fallback", false);
- }
- }
- return NS_OK;
- }
- static int32_t
- PlaintextRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
- PRIntervalTime timeout)
- {
- // The shutdownlocker is not needed here because it will already be
- // held higher in the stack
- nsNSSSocketInfo* socketInfo = nullptr;
- int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
- timeout);
- if (fd->identity == nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity)
- socketInfo = (nsNSSSocketInfo*) fd->secret;
- if ((bytesRead > 0) && socketInfo)
- socketInfo->AddPlaintextBytesRead(bytesRead);
- return bytesRead;
- }
- nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
- {
- // mPrefObserver will only be set if this->Init was called. The GTest tests
- // do not call Init.
- if (mPrefObserver) {
- Preferences::RemoveObserver(mPrefObserver,
- "security.ssl.treat_unsafe_negotiation_as_broken");
- Preferences::RemoveObserver(mPrefObserver,
- "security.ssl.false_start.require-npn");
- Preferences::RemoveObserver(mPrefObserver,
- "security.tls.version.fallback-limit");
- Preferences::RemoveObserver(mPrefObserver,
- "security.tls.insecure_fallback_hosts");
- Preferences::RemoveObserver(mPrefObserver,
- "security.tls.unrestricted_rc4_fallback");
- }
- }
- nsresult
- nsSSLIOLayerHelpers::Init()
- {
- if (!nsSSLIOLayerInitialized) {
- nsSSLIOLayerInitialized = true;
- nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
- nsSSLIOLayerMethods = *PR_GetDefaultIOMethods();
- nsSSLIOLayerMethods.available = (PRAvailableFN) PSMAvailable;
- nsSSLIOLayerMethods.available64 = (PRAvailable64FN) PSMAvailable64;
- nsSSLIOLayerMethods.fsync = (PRFsyncFN) _PSM_InvalidStatus;
- nsSSLIOLayerMethods.seek = (PRSeekFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.seek64 = (PRSeek64FN) _PSM_InvalidInt64;
- nsSSLIOLayerMethods.fileInfo = (PRFileInfoFN) _PSM_InvalidStatus;
- nsSSLIOLayerMethods.fileInfo64 = (PRFileInfo64FN) _PSM_InvalidStatus;
- nsSSLIOLayerMethods.writev = (PRWritevFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.accept = (PRAcceptFN) _PSM_InvalidDesc;
- nsSSLIOLayerMethods.listen = (PRListenFN) _PSM_InvalidStatus;
- nsSSLIOLayerMethods.shutdown = (PRShutdownFN) _PSM_InvalidStatus;
- nsSSLIOLayerMethods.recvfrom = (PRRecvfromFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.sendto = (PRSendtoFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.acceptread = (PRAcceptreadFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.transmitfile = (PRTransmitfileFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.sendfile = (PRSendfileFN) _PSM_InvalidInt;
- nsSSLIOLayerMethods.getsockname = PSMGetsockname;
- nsSSLIOLayerMethods.getpeername = PSMGetpeername;
- nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption;
- nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption;
- nsSSLIOLayerMethods.recv = PSMRecv;
- nsSSLIOLayerMethods.send = PSMSend;
- nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue;
- nsSSLIOLayerMethods.bind = PSMBind;
- nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect;
- nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
- nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
- nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
- nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
- nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer");
- nsSSLPlaintextLayerMethods = *PR_GetDefaultIOMethods();
- nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
- }
- bool enabled = false;
- Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
- setTreatUnsafeNegotiationAsBroken(enabled);
- mFalseStartRequireNPN =
- Preferences::GetBool("security.ssl.false_start.require-npn",
- FALSE_START_REQUIRE_NPN_DEFAULT);
- loadVersionFallbackLimit();
- initInsecureFallbackSites();
- mUnrestrictedRC4Fallback =
- Preferences::GetBool("security.tls.unrestricted_rc4_fallback", false);
- mPrefObserver = new PrefObserver(this);
- Preferences::AddStrongObserver(mPrefObserver,
- "security.ssl.treat_unsafe_negotiation_as_broken");
- Preferences::AddStrongObserver(mPrefObserver,
- "security.ssl.false_start.require-npn");
- Preferences::AddStrongObserver(mPrefObserver,
- "security.tls.version.fallback-limit");
- Preferences::AddStrongObserver(mPrefObserver,
- "security.tls.insecure_fallback_hosts");
- Preferences::AddStrongObserver(mPrefObserver,
- "security.tls.unrestricted_rc4_fallback");
- return NS_OK;
- }
- void
- nsSSLIOLayerHelpers::loadVersionFallbackLimit()
- {
- // see nsNSSComponent::setEnabledTLSVersions for pref handling rules
- uint32_t limit = Preferences::GetUint("security.tls.version.fallback-limit",
- 3); // 3 = TLS 1.2
- SSLVersionRange defaults = { SSL_LIBRARY_VERSION_TLS_1_2,
- SSL_LIBRARY_VERSION_TLS_1_2 };
- SSLVersionRange filledInRange;
- nsNSSComponent::FillTLSVersionRange(filledInRange, limit, limit, defaults);
- if (filledInRange.max < SSL_LIBRARY_VERSION_TLS_1_2) {
- filledInRange.max = SSL_LIBRARY_VERSION_TLS_1_2;
- }
- mVersionFallbackLimit = filledInRange.max;
- }
- void
- nsSSLIOLayerHelpers::clearStoredData()
- {
- MutexAutoLock lock(mutex);
- mInsecureFallbackSites.Clear();
- mTLSIntoleranceInfo.Clear();
- }
- void
- nsSSLIOLayerHelpers::setInsecureFallbackSites(const nsCString& str)
- {
- MutexAutoLock lock(mutex);
- mInsecureFallbackSites.Clear();
- if (str.IsEmpty()) {
- return;
- }
- nsCCharSeparatedTokenizer toker(str, ',');
- while (toker.hasMoreTokens()) {
- const nsCSubstring& host = toker.nextToken();
- if (!host.IsEmpty()) {
- mInsecureFallbackSites.PutEntry(host);
- }
- }
- }
- void
- nsSSLIOLayerHelpers::initInsecureFallbackSites()
- {
- MOZ_ASSERT(NS_IsMainThread());
- nsCString insecureFallbackHosts;
- Preferences::GetCString("security.tls.insecure_fallback_hosts",
- &insecureFallbackHosts);
- setInsecureFallbackSites(insecureFallbackHosts);
- }
- bool
- nsSSLIOLayerHelpers::isPublic() const
- {
- return this == &PublicSSLState()->IOLayerHelpers();
- }
- void
- nsSSLIOLayerHelpers::addInsecureFallbackSite(const nsCString& hostname,
- bool temporary)
- {
- MOZ_ASSERT(NS_IsMainThread());
- {
- MutexAutoLock lock(mutex);
- if (mInsecureFallbackSites.Contains(hostname)) {
- return;
- }
- mInsecureFallbackSites.PutEntry(hostname);
- }
- if (!isPublic() || temporary) {
- return;
- }
- nsCString value;
- Preferences::GetCString("security.tls.insecure_fallback_hosts", &value);
- if (!value.IsEmpty()) {
- value.Append(',');
- }
- value.Append(hostname);
- Preferences::SetCString("security.tls.insecure_fallback_hosts", value);
- }
- class FallbackPrefRemover final : public Runnable
- {
- public:
- explicit FallbackPrefRemover(const nsACString& aHost)
- : mHost(aHost)
- {}
- NS_IMETHOD Run() override;
- private:
- nsCString mHost;
- };
- NS_IMETHODIMP
- FallbackPrefRemover::Run()
- {
- MOZ_ASSERT(NS_IsMainThread());
- nsCString oldValue;
- Preferences::GetCString("security.tls.insecure_fallback_hosts", &oldValue);
- nsCCharSeparatedTokenizer toker(oldValue, ',');
- nsCString newValue;
- while (toker.hasMoreTokens()) {
- const nsCSubstring& host = toker.nextToken();
- if (host.Equals(mHost)) {
- continue;
- }
- if (!newValue.IsEmpty()) {
- newValue.Append(',');
- }
- newValue.Append(host);
- }
- Preferences::SetCString("security.tls.insecure_fallback_hosts", newValue);
- return NS_OK;
- }
- void
- nsSSLIOLayerHelpers::removeInsecureFallbackSite(const nsACString& hostname,
- uint16_t port)
- {
- forgetIntolerance(hostname, port);
- {
- MutexAutoLock lock(mutex);
- if (!mInsecureFallbackSites.Contains(hostname)) {
- return;
- }
- mInsecureFallbackSites.RemoveEntry(hostname);
- }
- if (!isPublic()) {
- return;
- }
- RefPtr<Runnable> runnable = new FallbackPrefRemover(hostname);
- if (NS_IsMainThread()) {
- runnable->Run();
- } else {
- NS_DispatchToMainThread(runnable);
- }
- }
- bool
- nsSSLIOLayerHelpers::isInsecureFallbackSite(const nsACString& hostname)
- {
- MutexAutoLock lock(mutex);
- return mInsecureFallbackSites.Contains(hostname);
- }
- void
- nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
- {
- MutexAutoLock lock(mutex);
- mTreatUnsafeNegotiationAsBroken = broken;
- }
- bool
- nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()
- {
- MutexAutoLock lock(mutex);
- return mTreatUnsafeNegotiationAsBroken;
- }
- nsresult
- nsSSLIOLayerNewSocket(int32_t family,
- const char* host,
- int32_t port,
- nsIProxyInfo *proxy,
- const NeckoOriginAttributes& originAttributes,
- PRFileDesc** fd,
- nsISupports** info,
- bool forSTARTTLS,
- uint32_t flags)
- {
- PRFileDesc* sock = PR_OpenTCPSocket(family);
- if (!sock) return NS_ERROR_OUT_OF_MEMORY;
- nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxy,
- originAttributes, sock, info,
- forSTARTTLS, flags);
- if (NS_FAILED(rv)) {
- PR_Close(sock);
- return rv;
- }
- *fd = sock;
- return NS_OK;
- }
- // Creates CA names strings from (CERTDistNames* caNames)
- //
- // - arena: arena to allocate strings on
- // - caNameStrings: filled with CA names strings on return
- // - caNames: CERTDistNames to extract strings from
- // - return: SECSuccess if successful; error code otherwise
- //
- // Note: copied in its entirety from Nova code
- static SECStatus
- nsConvertCANamesToStrings(const UniquePLArenaPool& arena, char** caNameStrings,
- CERTDistNames* caNames)
- {
- MOZ_ASSERT(arena.get());
- MOZ_ASSERT(caNameStrings);
- MOZ_ASSERT(caNames);
- if (!arena.get() || !caNameStrings || !caNames) {
- PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
- return SECFailure;
- }
- SECItem* dername;
- int n;
- char* namestring;
- for (n = 0; n < caNames->nnames; n++) {
- dername = &caNames->names[n];
- namestring = CERT_DerNameToAscii(dername);
- if (!namestring) {
- // XXX - keep going until we fail to convert the name
- caNameStrings[n] = const_cast<char*>("");
- } else {
- caNameStrings[n] = PORT_ArenaStrdup(arena.get(), namestring);
- PR_Free(namestring);
- if (!caNameStrings[n]) {
- return SECFailure;
- }
- }
- }
- return SECSuccess;
- }
- // Possible behaviors for choosing a cert for client auth.
- enum class UserCertChoice {
- // Ask the user to choose a cert.
- Ask = 0,
- // Automatically choose a cert.
- Auto = 1,
- };
- // Returns the most appropriate user cert choice based on the value of the
- // security.default_personal_cert preference.
- UserCertChoice
- nsGetUserCertChoice()
- {
- nsAutoCString value;
- nsresult rv = Preferences::GetCString("security.default_personal_cert", &value);
- if (NS_FAILED(rv)) {
- return UserCertChoice::Ask;
- }
- // There are three cases for what the preference could be set to:
- // 1. "Select Automatically" -> Auto.
- // 2. "Ask Every Time" -> Ask.
- // 3. Something else -> Ask. This might be a nickname from a migrated cert,
- // but we no longer support this case.
- return value.EqualsLiteral("Select Automatically") ? UserCertChoice::Auto
- : UserCertChoice::Ask;
- }
- static bool
- hasExplicitKeyUsageNonRepudiation(CERTCertificate* cert)
- {
- // There is no extension, v1 or v2 certificate
- if (!cert->extensions)
- return false;
- SECStatus srv;
- SECItem keyUsageItem;
- keyUsageItem.data = nullptr;
- srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem);
- if (srv == SECFailure)
- return false;
- unsigned char keyUsage = keyUsageItem.data[0];
- PORT_Free (keyUsageItem.data);
- return !!(keyUsage & KU_NON_REPUDIATION);
- }
- class ClientAuthDataRunnable : public SyncRunnableBase
- {
- public:
- ClientAuthDataRunnable(CERTDistNames* caNames,
- CERTCertificate** pRetCert,
- SECKEYPrivateKey** pRetKey,
- nsNSSSocketInfo* info,
- const UniqueCERTCertificate& serverCert)
- : mRV(SECFailure)
- , mErrorCodeToReport(SEC_ERROR_NO_MEMORY)
- , mPRetCert(pRetCert)
- , mPRetKey(pRetKey)
- , mCANames(caNames)
- , mSocketInfo(info)
- , mServerCert(serverCert.get())
- {
- }
- SECStatus mRV; // out
- PRErrorCode mErrorCodeToReport; // out
- CERTCertificate** const mPRetCert; // in/out
- SECKEYPrivateKey** const mPRetKey; // in/out
- protected:
- virtual void RunOnTargetThread();
- private:
- CERTDistNames* const mCANames; // in
- nsNSSSocketInfo* const mSocketInfo; // in
- CERTCertificate* const mServerCert; // in
- };
- // This callback function is used to pull client certificate
- // information upon server request
- //
- // - arg: SSL data connection
- // - socket: SSL socket we're dealing with
- // - caNames: list of CA names
- // - pRetCert: returns a pointer to a pointer to a valid certificate if
- // successful; otherwise nullptr
- // - pRetKey: returns a pointer to a pointer to the corresponding key if
- // successful; otherwise nullptr
- SECStatus
- nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
- CERTDistNames* caNames, CERTCertificate** pRetCert,
- SECKEYPrivateKey** pRetKey)
- {
- nsNSSShutDownPreventionLock locker;
- if (!socket || !caNames || !pRetCert || !pRetKey) {
- PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
- return SECFailure;
- }
- RefPtr<nsNSSSocketInfo> info(
- BitwiseCast<nsNSSSocketInfo*, PRFilePrivate*>(socket->higher->secret));
- UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket));
- if (!serverCert) {
- NS_NOTREACHED("Missing server certificate should have been detected during "
- "server cert authentication.");
- PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
- return SECFailure;
- }
- if (info->GetJoined()) {
- // We refuse to send a client certificate when there are multiple hostnames
- // joined on this connection, because we only show the user one hostname
- // (mHostName) in the client certificate UI.
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] Not returning client cert due to previous join\n", socket));
- *pRetCert = nullptr;
- *pRetKey = nullptr;
- return SECSuccess;
- }
- // XXX: This should be done asynchronously; see bug 696976
- RefPtr<ClientAuthDataRunnable> runnable(
- new ClientAuthDataRunnable(caNames, pRetCert, pRetKey, info, serverCert));
- nsresult rv = runnable->DispatchToMainThreadAndWait();
- if (NS_FAILED(rv)) {
- PR_SetError(SEC_ERROR_NO_MEMORY, 0);
- return SECFailure;
- }
- if (runnable->mRV != SECSuccess) {
- PR_SetError(runnable->mErrorCodeToReport, 0);
- } else if (*runnable->mPRetCert || *runnable->mPRetKey) {
- // Make joinConnection prohibit joining after we've sent a client cert
- info->SetSentClientCert();
- }
- return runnable->mRV;
- }
- void
- ClientAuthDataRunnable::RunOnTargetThread()
- {
- // We check the value of a pref in this runnable, so this runnable should only
- // be run on the main thread.
- MOZ_ASSERT(NS_IsMainThread());
- UniquePLArenaPool arena;
- char** caNameStrings;
- UniqueCERTCertificate cert;
- UniqueSECKEYPrivateKey privKey;
- void* wincx = mSocketInfo;
- nsresult rv;
- nsCOMPtr<nsIX509Cert> socketClientCert;
- mSocketInfo->GetClientCert(getter_AddRefs(socketClientCert));
- // If a client cert preference was set on the socket info, use that and skip
- // the client cert UI and/or search of the user's past cert decisions.
- if (socketClientCert) {
- cert.reset(socketClientCert->GetCert());
- if (!cert) {
- goto loser;
- }
- // Get the private key
- privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx));
- if (!privKey) {
- goto loser;
- }
- *mPRetCert = cert.release();
- *mPRetKey = privKey.release();
- mRV = SECSuccess;
- return;
- }
- // create caNameStrings
- arena.reset(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
- if (!arena) {
- goto loser;
- }
- caNameStrings = static_cast<char**>(
- PORT_ArenaAlloc(arena.get(), sizeof(char*) * mCANames->nnames));
- if (!caNameStrings) {
- goto loser;
- }
- mRV = nsConvertCANamesToStrings(arena, caNameStrings, mCANames);
- if (mRV != SECSuccess) {
- goto loser;
- }
- // find valid user cert and key pair
- if (nsGetUserCertChoice() == UserCertChoice::Auto) {
- // automatically find the right cert
- // find all user certs that are valid and for SSL
- UniqueCERTCertList certList(
- CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient,
- false, true, wincx));
- if (!certList) {
- goto loser;
- }
- // filter the list to those issued by CAs supported by the server
- mRV = CERT_FilterCertListByCANames(certList.get(), mCANames->nnames,
- caNameStrings, certUsageSSLClient);
- if (mRV != SECSuccess) {
- goto loser;
- }
- // make sure the list is not empty
- if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
- goto loser;
- }
- UniqueCERTCertificate lowPrioNonrepCert;
- // loop through the list until we find a cert with a key
- for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
- !CERT_LIST_END(node, certList);
- node = CERT_LIST_NEXT(node)) {
- // if the certificate has restriction and we do not satisfy it we do not
- // use it
- privKey.reset(PK11_FindKeyByAnyCert(node->cert, wincx));
- if (privKey) {
- if (hasExplicitKeyUsageNonRepudiation(node->cert)) {
- privKey = nullptr;
- // Not a preferred cert
- if (!lowPrioNonrepCert) { // did not yet find a low prio cert
- lowPrioNonrepCert.reset(CERT_DupCertificate(node->cert));
- }
- } else {
- // this is a good cert to present
- cert.reset(CERT_DupCertificate(node->cert));
- break;
- }
- }
- if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) {
- // problem with password: bail
- goto loser;
- }
- }
- if (!cert && lowPrioNonrepCert) {
- cert = Move(lowPrioNonrepCert);
- privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx));
- }
- if (!cert) {
- goto loser;
- }
- } else { // Not Auto => ask
- // Get the SSL Certificate
- nsXPIDLCString hostname;
- mSocketInfo->GetHostName(getter_Copies(hostname));
- RefPtr<nsClientAuthRememberService> cars =
- mSocketInfo->SharedState().GetClientAuthRememberService();
- bool hasRemembered = false;
- nsCString rememberedDBKey;
- if (cars) {
- bool found;
- rv = cars->HasRememberedDecision(hostname,
- mSocketInfo->GetOriginAttributes(),
- mServerCert, rememberedDBKey, &found);
- if (NS_SUCCEEDED(rv) && found) {
- hasRemembered = true;
- }
- }
- if (hasRemembered && !rememberedDBKey.IsEmpty()) {
- nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
- if (certdb) {
- nsCOMPtr<nsIX509Cert> foundCert;
- rv = certdb->FindCertByDBKey(rememberedDBKey.get(),
- getter_AddRefs(foundCert));
- if (NS_SUCCEEDED(rv) && foundCert) {
- nsNSSCertificate* objCert =
- BitwiseCast<nsNSSCertificate*, nsIX509Cert*>(foundCert.get());
- if (objCert) {
- cert.reset(objCert->GetCert());
- }
- }
- if (!cert) {
- hasRemembered = false;
- }
- }
- }
- if (!hasRemembered) {
- // user selects a cert to present
- nsCOMPtr<nsIClientAuthDialogs> dialogs;
- // find all user certs that are for SSL
- // note that we are allowing expired certs in this list
- UniqueCERTCertList certList(
- CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient,
- false, false, wincx));
- if (!certList) {
- goto loser;
- }
- if (mCANames->nnames != 0) {
- // filter the list to those issued by CAs supported by the server
- mRV = CERT_FilterCertListByCANames(certList.get(),
- mCANames->nnames,
- caNameStrings,
- certUsageSSLClient);
- if (mRV != SECSuccess) {
- goto loser;
- }
- }
- if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
- // list is empty - no matching certs
- goto loser;
- }
- int32_t port;
- mSocketInfo->GetPort(&port);
- UniquePORTString corg(CERT_GetOrgName(&mServerCert->subject));
- nsAutoCString org(corg.get());
- UniquePORTString cissuer(CERT_GetOrgName(&mServerCert->issuer));
- nsAutoCString issuer(cissuer.get());
- nsCOMPtr<nsIMutableArray> certArray = nsArrayBase::Create();
- if (!certArray) {
- goto loser;
- }
- for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
- !CERT_LIST_END(node, certList);
- node = CERT_LIST_NEXT(node)) {
- nsCOMPtr<nsIX509Cert> tempCert = nsNSSCertificate::Create(node->cert);
- if (!tempCert) {
- goto loser;
- }
- rv = certArray->AppendElement(tempCert, false);
- if (NS_FAILED(rv)) {
- goto loser;
- }
- }
- // Throw up the client auth dialog and get back the index of the selected cert
- rv = getNSSDialogs(getter_AddRefs(dialogs),
- NS_GET_IID(nsIClientAuthDialogs),
- NS_CLIENTAUTHDIALOGS_CONTRACTID);
- if (NS_FAILED(rv)) {
- goto loser;
- }
- uint32_t selectedIndex = 0;
- bool certChosen = false;
- rv = dialogs->ChooseCertificate(mSocketInfo, hostname, port, org, issuer,
- certArray, &selectedIndex, &certChosen);
- if (NS_FAILED(rv)) {
- goto loser;
- }
- // even if the user has canceled, we want to remember that, to avoid repeating prompts
- bool wantRemember = false;
- mSocketInfo->GetRememberClientAuthCertificate(&wantRemember);
- if (certChosen) {
- nsCOMPtr<nsIX509Cert> selectedCert = do_QueryElementAt(certArray,
- selectedIndex);
- if (!selectedCert) {
- goto loser;
- }
- cert.reset(selectedCert->GetCert());
- }
- if (cars && wantRemember) {
- cars->RememberDecision(hostname, mSocketInfo->GetOriginAttributes(),
- mServerCert, certChosen ? cert.get() : nullptr);
- }
- }
- if (!cert) {
- goto loser;
- }
- // go get the private key
- privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx));
- if (!privKey) {
- goto loser;
- }
- }
- goto done;
- loser:
- if (mRV == SECSuccess) {
- mRV = SECFailure;
- }
- done:
- int error = PR_GetError();
- *mPRetCert = cert.release();
- *mPRetKey = privKey.release();
- if (mRV == SECFailure) {
- mErrorCodeToReport = error;
- }
- }
- static PRFileDesc*
- nsSSLIOLayerImportFD(PRFileDesc* fd,
- nsNSSSocketInfo* infoObject,
- const char* host)
- {
- nsNSSShutDownPreventionLock locker;
- PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
- if (!sslSock) {
- NS_ASSERTION(false, "NSS: Error importing socket");
- return nullptr;
- }
- SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*) infoObject);
- SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
- SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
- // Disable this hook if we connect anonymously. See bug 466080.
- uint32_t flags = 0;
- infoObject->GetProviderFlags(&flags);
- if (flags & nsISocketProvider::ANONYMOUS_CONNECT) {
- SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject);
- } else {
- SSL_GetClientAuthDataHook(sslSock,
- (SSLGetClientAuthData) nsNSS_SSLGetClientAuthData,
- infoObject);
- }
- if (flags & nsISocketProvider::MITM_OK) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] nsSSLIOLayerImportFD: bypass authentication flag\n", fd));
- infoObject->SetBypassAuthentication(true);
- }
- if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook,
- infoObject)) {
- NS_NOTREACHED("failed to configure AuthCertificateHook");
- goto loser;
- }
- if (SECSuccess != SSL_SetURL(sslSock, host)) {
- NS_NOTREACHED("SSL_SetURL failed");
- goto loser;
- }
- return sslSock;
- loser:
- if (sslSock) {
- PR_Close(sslSock);
- }
- return nullptr;
- }
- // Please change getSignatureName in nsNSSCallbacks.cpp when changing the list
- // here.
- static const SSLSignatureScheme sEnabledSignatureSchemes[] = {
- ssl_sig_ecdsa_secp256r1_sha256,
- ssl_sig_ecdsa_secp384r1_sha384,
- ssl_sig_ecdsa_secp521r1_sha512,
- ssl_sig_rsa_pss_sha256,
- ssl_sig_rsa_pss_sha384,
- ssl_sig_rsa_pss_sha512,
- ssl_sig_rsa_pkcs1_sha256,
- ssl_sig_rsa_pkcs1_sha384,
- ssl_sig_rsa_pkcs1_sha512,
- ssl_sig_ecdsa_sha1,
- ssl_sig_rsa_pkcs1_sha1,
- };
- static nsresult
- nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
- bool haveProxy, const char* host, int32_t port,
- nsNSSSocketInfo* infoObject)
- {
- nsNSSShutDownPreventionLock locker;
- if (forSTARTTLS || haveProxy) {
- if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, false)) {
- return NS_ERROR_FAILURE;
- }
- }
- SSLVersionRange range;
- if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
- return NS_ERROR_FAILURE;
- }
- if ((infoObject->GetProviderFlags() & nsISocketProvider::BE_CONSERVATIVE) &&
- (range.max > SSL_LIBRARY_VERSION_TLS_1_2)) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] nsSSLIOLayerSetOptions: range.max limited to 1.2 due to BE_CONSERVATIVE flag\n",
- fd));
- range.max = SSL_LIBRARY_VERSION_TLS_1_2;
- }
- uint16_t maxEnabledVersion = range.max;
- StrongCipherStatus strongCiphersStatus = StrongCipherStatusUnknown;
- infoObject->SharedState().IOLayerHelpers()
- .adjustForTLSIntolerance(infoObject->GetHostName(), infoObject->GetPort(),
- range, strongCiphersStatus);
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] nsSSLIOLayerSetOptions: using TLS version range (0x%04x,0x%04x)%s\n",
- fd, static_cast<unsigned int>(range.min),
- static_cast<unsigned int>(range.max),
- strongCiphersStatus == StrongCiphersFailed ? " with weak ciphers" : ""));
- if (SSL_VersionRangeSet(fd, &range) != SECSuccess) {
- return NS_ERROR_FAILURE;
- }
- infoObject->SetTLSVersionRange(range);
- if (strongCiphersStatus == StrongCiphersFailed) {
- nsNSSComponent::UseWeakCiphersOnSocket(fd);
- }
- // when adjustForTLSIntolerance tweaks the maximum version downward,
- // we tell the server using this SCSV so they can detect a downgrade attack
- if (range.max < maxEnabledVersion) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd));
- // Some servers will choke if we send the fallback SCSV with TLS 1.2.
- if (range.max < SSL_LIBRARY_VERSION_TLS_1_2) {
- if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) {
- return NS_ERROR_FAILURE;
- }
- }
- // tell NSS the max enabled version to make anti-downgrade effective
- if (SECSuccess != SSL_SetDowngradeCheckVersion(fd, maxEnabledVersion)) {
- return NS_ERROR_FAILURE;
- }
- }
- // Include a modest set of named groups.
- // Please change getKeaGroupName in nsNSSCallbacks.cpp when changing the list
- // here.
- const SSLNamedGroup namedGroups[] = {
- ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1,
- ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072
- };
- if (SECSuccess != SSL_NamedGroupConfig(fd, namedGroups,
- mozilla::ArrayLength(namedGroups))) {
- return NS_ERROR_FAILURE;
- }
- // This ensures that we send key shares for X25519 and P-256 in TLS 1.3, so
- // that servers are less likely to use HelloRetryRequest.
- if (SECSuccess != SSL_SendAdditionalKeyShares(fd, 1)) {
- return NS_ERROR_FAILURE;
- }
- if (SECSuccess != SSL_SignatureSchemePrefSet(fd, sEnabledSignatureSchemes,
- mozilla::ArrayLength(sEnabledSignatureSchemes))) {
- return NS_ERROR_FAILURE;
- }
- bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
- if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) {
- return NS_ERROR_FAILURE;
- }
- bool sctsEnabled = infoObject->SharedState().IsSignedCertTimestampsEnabled();
- if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
- sctsEnabled)) {
- return NS_ERROR_FAILURE;
- }
- if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
- return NS_ERROR_FAILURE;
- }
- // Set the Peer ID so that SSL proxy connections work properly and to
- // separate anonymous and/or private browsing connections.
- uint32_t flags = infoObject->GetProviderFlags();
- nsAutoCString peerId;
- if (flags & nsISocketProvider::ANONYMOUS_CONNECT) { // See bug 466080
- peerId.AppendLiteral("anon:");
- }
- if (flags & nsISocketProvider::NO_PERMANENT_STORAGE) {
- peerId.AppendLiteral("private:");
- }
- if (flags & nsISocketProvider::MITM_OK) {
- peerId.AppendLiteral("bypassAuth:");
- }
- if (flags & nsISocketProvider::BE_CONSERVATIVE) {
- peerId.AppendLiteral("beConservative:");
- }
- peerId.Append(host);
- peerId.Append(':');
- peerId.AppendInt(port);
- nsAutoCString suffix;
- infoObject->GetOriginAttributes().CreateSuffix(suffix);
- peerId.Append(suffix);
- if (SECSuccess != SSL_SetSockPeerID(fd, peerId.get())) {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- nsresult
- nsSSLIOLayerAddToSocket(int32_t family,
- const char* host,
- int32_t port,
- nsIProxyInfo* proxy,
- const NeckoOriginAttributes& originAttributes,
- PRFileDesc* fd,
- nsISupports** info,
- bool forSTARTTLS,
- uint32_t providerFlags)
- {
- nsNSSShutDownPreventionLock locker;
- PRFileDesc* layer = nullptr;
- PRFileDesc* plaintextLayer = nullptr;
- nsresult rv;
- PRStatus stat;
- SharedSSLState* sharedState =
- providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE ? PrivateSSLState() : PublicSSLState();
- nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags);
- if (!infoObject) return NS_ERROR_FAILURE;
- NS_ADDREF(infoObject);
- infoObject->SetForSTARTTLS(forSTARTTLS);
- infoObject->SetHostName(host);
- infoObject->SetPort(port);
- infoObject->SetOriginAttributes(originAttributes);
- bool haveProxy = false;
- if (proxy) {
- nsCString proxyHost;
- proxy->GetHost(proxyHost);
- haveProxy = !proxyHost.IsEmpty();
- }
- // A plaintext observer shim is inserted so we can observe some protocol
- // details without modifying nss
- plaintextLayer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity,
- &nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods);
- if (plaintextLayer) {
- plaintextLayer->secret = (PRFilePrivate*) infoObject;
- stat = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer);
- if (stat == PR_FAILURE) {
- plaintextLayer->dtor(plaintextLayer);
- plaintextLayer = nullptr;
- }
- }
- PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
- if (!sslSock) {
- NS_ASSERTION(false, "NSS: Error importing socket");
- goto loser;
- }
- infoObject->SetFileDescPtr(sslSock);
- rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port,
- infoObject);
- if (NS_FAILED(rv))
- goto loser;
- // Now, layer ourselves on top of the SSL socket...
- layer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
- &nsSSLIOLayerHelpers::nsSSLIOLayerMethods);
- if (!layer)
- goto loser;
- layer->secret = (PRFilePrivate*) infoObject;
- stat = PR_PushIOLayer(sslSock, PR_GetLayersIdentity(sslSock), layer);
- if (stat == PR_FAILURE) {
- goto loser;
- }
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Socket set up\n", (void*) sslSock));
- infoObject->QueryInterface(NS_GET_IID(nsISupports), (void**) (info));
- // We are going use a clear connection first //
- if (forSTARTTLS || haveProxy) {
- infoObject->SetHandshakeNotPending();
- }
- infoObject->SharedState().NoteSocketCreated();
- return NS_OK;
- loser:
- NS_IF_RELEASE(infoObject);
- if (layer) {
- layer->dtor(layer);
- }
- if (plaintextLayer) {
- PR_PopIOLayer(fd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
- plaintextLayer->dtor(plaintextLayer);
- }
- return NS_ERROR_FAILURE;
- }
|