tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CommonSocketControl.cpp (16128B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 *
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CommonSocketControl.h"
      8 
      9 #include "PublicKeyPinningService.h"
     10 #include "SharedCertVerifier.h"
     11 #include "mozilla/ErrorResult.h"
     12 #include "mozilla/StaticPrefs_network.h"
     13 #include "mozilla/dom/Promise.h"
     14 #include "nsICertOverrideService.h"
     15 #include "nsISocketProvider.h"
     16 #include "nsITlsHandshakeListener.h"
     17 #include "nsNSSCertificate.h"
     18 #include "nsNSSComponent.h"
     19 #include "nsNSSHelper.h"
     20 #include "secerr.h"
     21 #include "ssl.h"
     22 #include "sslt.h"
     23 
     24 using namespace mozilla;
     25 
     26 extern LazyLogModule gPIPNSSLog;
     27 
     28 NS_IMPL_ISUPPORTS(CommonSocketControl, nsITLSSocketControl)
     29 
     30 CommonSocketControl::CommonSocketControl(const nsCString& aHostName,
     31                                         int32_t aPort, uint32_t aProviderFlags)
     32    : mHostName(aHostName),
     33      mPort(aPort),
     34      mCanceled(false),
     35      mHandshakeCompleted(false),
     36      mJoined(false),
     37      mSentClientCert(false),
     38      mFailedVerification(false),
     39      mSSLVersionUsed(nsITLSSocketControl::SSL_VERSION_UNKNOWN),
     40      mProviderFlags(aProviderFlags),
     41      mSecurityState(0),
     42      mErrorCode(0),
     43      mServerCert(nullptr),
     44      mCertificateTransparencyStatus(0),
     45      mMadeOCSPRequests(false),
     46      mUsedPrivateDNS(aProviderFlags & nsISocketProvider::USED_PRIVATE_DNS),
     47      mNPNCompleted(false),
     48      mResumed(false),
     49      mIsBuiltCertChainRootBuiltInRoot(false) {
     50 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
     51  mOwningThread = PR_GetCurrentThread();
     52 #endif
     53 }
     54 
     55 void CommonSocketControl::SetStatusErrorBits(
     56    nsITransportSecurityInfo::OverridableErrorCategory
     57        overridableErrorCategory) {
     58  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
     59  mOverridableErrorCategory = Some(overridableErrorCategory);
     60 }
     61 
     62 static void CreateCertChain(nsTArray<RefPtr<nsIX509Cert>>& aOutput,
     63                            nsTArray<nsTArray<uint8_t>>&& aCertList) {
     64  nsTArray<nsTArray<uint8_t>> certList = std::move(aCertList);
     65  aOutput.Clear();
     66  for (auto& certBytes : certList) {
     67    RefPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certBytes));
     68    aOutput.AppendElement(cert);
     69  }
     70 }
     71 
     72 void CommonSocketControl::SetServerCert(
     73    const nsCOMPtr<nsIX509Cert>& aServerCert,
     74    mozilla::psm::EVStatus aEVStatus) {
     75  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
     76  mServerCert = aServerCert;
     77  mIsEV = Some(aEVStatus == mozilla::psm::EVStatus::EV);
     78 }
     79 
     80 void CommonSocketControl::SetSucceededCertChain(
     81    nsTArray<nsTArray<uint8_t>>&& aCertList) {
     82  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
     83  return CreateCertChain(mSucceededCertChain, std::move(aCertList));
     84 }
     85 
     86 void CommonSocketControl::SetHandshakeCertificates(
     87    nsTArray<nsTArray<uint8_t>>&& aCertList) {
     88  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
     89  return CreateCertChain(mHandshakeCertificates, std::move(aCertList));
     90 }
     91 
     92 void CommonSocketControl::SetCanceled(PRErrorCode errorCode) {
     93  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
     94  MOZ_ASSERT(errorCode != 0);
     95  if (errorCode == 0) {
     96    errorCode = SEC_ERROR_LIBRARY_FAILURE;
     97  }
     98 
     99  mErrorCode = errorCode;
    100  mCanceled = true;
    101 }
    102 
    103 // NB: GetErrorCode may be called before an error code is set (if ever). In that
    104 // case, this returns 0, which is treated as a successful value.
    105 int32_t CommonSocketControl::GetErrorCode() {
    106  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    107  // We're in an inconsistent state if we think we've been canceled but no error
    108  // code was set or we haven't been canceled but an error code was set.
    109  MOZ_ASSERT(
    110      !((mCanceled && mErrorCode == 0) || (!mCanceled && mErrorCode != 0)));
    111  if ((mCanceled && mErrorCode == 0) || (!mCanceled && mErrorCode != 0)) {
    112    mCanceled = true;
    113    mErrorCode = SEC_ERROR_LIBRARY_FAILURE;
    114  }
    115 
    116  return mErrorCode;
    117 }
    118 
    119 NS_IMETHODIMP
    120 CommonSocketControl::ProxyStartSSL(void) { return NS_ERROR_NOT_IMPLEMENTED; }
    121 
    122 NS_IMETHODIMP
    123 CommonSocketControl::StartTLS(void) { return NS_ERROR_NOT_IMPLEMENTED; }
    124 
    125 NS_IMETHODIMP
    126 CommonSocketControl::AsyncStartTLS(JSContext* aCx,
    127                                   mozilla::dom::Promise** aPromise) {
    128  return NS_ERROR_NOT_IMPLEMENTED;
    129 }
    130 
    131 NS_IMETHODIMP
    132 CommonSocketControl::SetNPNList(nsTArray<nsCString>& aNPNList) {
    133  return NS_ERROR_NOT_IMPLEMENTED;
    134 }
    135 
    136 NS_IMETHODIMP
    137 CommonSocketControl::GetAlpnEarlySelection(nsACString& _retval) {
    138  return NS_ERROR_NOT_IMPLEMENTED;
    139 }
    140 
    141 NS_IMETHODIMP
    142 CommonSocketControl::GetEarlyDataAccepted(bool* aEarlyDataAccepted) {
    143  return NS_ERROR_NOT_IMPLEMENTED;
    144 }
    145 
    146 NS_IMETHODIMP
    147 CommonSocketControl::DriveHandshake(void) { return NS_ERROR_NOT_IMPLEMENTED; }
    148 
    149 NS_IMETHODIMP
    150 CommonSocketControl::JoinConnection(const nsACString& npnProtocol,
    151                                    const nsACString& hostname, int32_t port,
    152                                    bool* _retval) {
    153  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    154  nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval);
    155  if (NS_SUCCEEDED(rv) && *_retval) {
    156    // All tests pass - this is joinable
    157    mJoined = true;
    158  }
    159  return rv;
    160 }
    161 
    162 NS_IMETHODIMP
    163 CommonSocketControl::TestJoinConnection(const nsACString& npnProtocol,
    164                                        const nsACString& hostname,
    165                                        int32_t port, bool* _retval) {
    166  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    167  *_retval = false;
    168 
    169  // Different ports may not be joined together
    170  if (port != GetPort()) return NS_OK;
    171 
    172  // Make sure NPN has been completed and matches requested npnProtocol
    173  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol)) {
    174    return NS_OK;
    175  }
    176 
    177  IsAcceptableForHost(hostname, _retval);  // sets _retval
    178  return NS_OK;
    179 }
    180 
    181 NS_IMETHODIMP
    182 CommonSocketControl::IsAcceptableForHost(const nsACString& hostname,
    183                                         bool* _retval) {
    184  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    185  NS_ENSURE_ARG(_retval);
    186 
    187  *_retval = false;
    188 
    189  // If this is the same hostname then the certicate status does not
    190  // need to be considered. They are joinable.
    191  if (hostname.Equals(GetHostName())) {
    192    *_retval = true;
    193    return NS_OK;
    194  }
    195 
    196  // Before checking the server certificate we need to make sure the
    197  // handshake has completed.
    198  if (!mHandshakeCompleted || !HasServerCert()) {
    199    return NS_OK;
    200  }
    201 
    202  // Security checks can only be skipped when running xpcshell tests.
    203  if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
    204    nsCOMPtr<nsICertOverrideService> overrideService =
    205        do_GetService(NS_CERTOVERRIDE_CONTRACTID);
    206    if (overrideService) {
    207      bool securityCheckDisabled = false;
    208      overrideService->GetSecurityCheckDisabled(&securityCheckDisabled);
    209      if (securityCheckDisabled) {
    210        *_retval = true;
    211        return NS_OK;
    212      }
    213    }
    214  }
    215 
    216  // If the cert has error bits (e.g. it is untrusted) then do not join.
    217  if (mOverridableErrorCategory.isSome()) {
    218    return NS_OK;
    219  }
    220 
    221  // If the connection is using client certificates then do not join
    222  // because the user decides on whether to send client certs to hosts on a
    223  // per-domain basis.
    224  if (mSentClientCert) return NS_OK;
    225 
    226  // Ensure that the server certificate covers the hostname that would
    227  // like to join this connection
    228 
    229  nsCOMPtr<nsIX509Cert> cert(GetServerCert());
    230  if (!cert) {
    231    return NS_OK;
    232  }
    233  nsTArray<uint8_t> certDER;
    234  if (NS_FAILED(cert->GetRawDER(certDER))) {
    235    return NS_OK;
    236  }
    237 
    238  // An empty mSucceededCertChain means the server certificate verification
    239  // failed before, so don't join in this case.
    240  if (mSucceededCertChain.IsEmpty()) {
    241    return NS_OK;
    242  }
    243 
    244  // See where CheckCertHostname() is called in
    245  // CertVerifier::VerifySSLServerCert. We are doing the same hostname-specific
    246  // checks here. If any hostname-specific checks are added to
    247  // CertVerifier::VerifySSLServerCert we need to add them here too.
    248  pkix::Input serverCertInput;
    249  mozilla::pkix::Result rv =
    250      serverCertInput.Init(certDER.Elements(), certDER.Length());
    251  if (rv != pkix::Success) {
    252    return NS_OK;
    253  }
    254 
    255  pkix::Input hostnameInput;
    256  rv = hostnameInput.Init(
    257      BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
    258      hostname.Length());
    259  if (rv != pkix::Success) {
    260    return NS_OK;
    261  }
    262 
    263  rv = CheckCertHostname(serverCertInput, hostnameInput);
    264  if (rv != pkix::Success) {
    265    return NS_OK;
    266  }
    267 
    268  nsTArray<nsTArray<uint8_t>> rawDerCertList;
    269  nsTArray<Span<const uint8_t>> derCertSpanList;
    270  for (const auto& cert : mSucceededCertChain) {
    271    rawDerCertList.EmplaceBack();
    272    nsresult nsrv = cert->GetRawDER(rawDerCertList.LastElement());
    273    if (NS_FAILED(nsrv)) {
    274      return nsrv;
    275    }
    276    derCertSpanList.EmplaceBack(rawDerCertList.LastElement());
    277  }
    278  bool chainHasValidPins;
    279  nsresult nsrv = mozilla::psm::PublicKeyPinningService::ChainHasValidPins(
    280      derCertSpanList, PromiseFlatCString(hostname).BeginReading(), pkix::Now(),
    281      mIsBuiltCertChainRootBuiltInRoot, chainHasValidPins, nullptr);
    282  if (NS_FAILED(nsrv)) {
    283    return NS_OK;
    284  }
    285 
    286  if (!chainHasValidPins) {
    287    return NS_OK;
    288  }
    289 
    290  // All tests pass
    291  *_retval = true;
    292  return NS_OK;
    293 }
    294 
    295 void CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache() {
    296  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    297  if (!mSessionCacheInfo) {
    298    MOZ_LOG(
    299        gPIPNSSLog, LogLevel::Debug,
    300        ("CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache cannot "
    301         "find cached info."));
    302    return;
    303  }
    304 
    305  mozilla::net::SessionCacheInfo& info = *mSessionCacheInfo;
    306  nsCOMPtr<nsIX509Cert> cert(
    307      new nsNSSCertificate(std::move(info.mServerCertBytes)));
    308  SetServerCert(cert, info.mEVStatus);
    309  if (info.mOverridableErrorCategory !=
    310      nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET) {
    311    SetStatusErrorBits(info.mOverridableErrorCategory);
    312  }
    313  SetCertificateTransparencyStatus(info.mCertificateTransparencyStatus);
    314  if (info.mSucceededCertChainBytes) {
    315    SetSucceededCertChain(std::move(*info.mSucceededCertChainBytes));
    316  }
    317 
    318  if (info.mIsBuiltCertChainRootBuiltInRoot) {
    319    SetIsBuiltCertChainRootBuiltInRoot(*info.mIsBuiltCertChainRootBuiltInRoot);
    320  }
    321 
    322  if (info.mHandshakeCertificatesBytes) {
    323    SetHandshakeCertificates(std::move(*info.mHandshakeCertificatesBytes));
    324  }
    325 }
    326 
    327 NS_IMETHODIMP
    328 CommonSocketControl::GetKEAUsed(int16_t* aKEAUsed) {
    329  return NS_ERROR_NOT_IMPLEMENTED;
    330 }
    331 
    332 NS_IMETHODIMP
    333 CommonSocketControl::GetKEAKeyBits(uint32_t* aKEAKeyBits) {
    334  return NS_ERROR_NOT_IMPLEMENTED;
    335 }
    336 
    337 NS_IMETHODIMP
    338 CommonSocketControl::GetProviderFlags(uint32_t* aProviderFlags) {
    339  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    340  *aProviderFlags = mProviderFlags;
    341  return NS_OK;
    342 }
    343 
    344 NS_IMETHODIMP
    345 CommonSocketControl::GetSSLVersionUsed(int16_t* aSSLVersionUsed) {
    346  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    347  *aSSLVersionUsed = mSSLVersionUsed;
    348  return NS_OK;
    349 }
    350 
    351 NS_IMETHODIMP
    352 CommonSocketControl::GetSSLVersionOffered(int16_t* aSSLVersionOffered) {
    353  return NS_ERROR_NOT_IMPLEMENTED;
    354 }
    355 
    356 NS_IMETHODIMP
    357 CommonSocketControl::GetMACAlgorithmUsed(int16_t* aMACAlgorithmUsed) {
    358  return NS_ERROR_NOT_IMPLEMENTED;
    359 }
    360 
    361 bool CommonSocketControl::GetDenyClientCert() { return true; }
    362 
    363 void CommonSocketControl::SetDenyClientCert(bool aDenyClientCert) {}
    364 
    365 NS_IMETHODIMP
    366 CommonSocketControl::GetClientCertSent(bool* arg) {
    367  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    368  *arg = mSentClientCert;
    369  return NS_OK;
    370 }
    371 
    372 NS_IMETHODIMP
    373 CommonSocketControl::GetFailedVerification(bool* arg) {
    374  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    375  *arg = mFailedVerification;
    376  return NS_OK;
    377 }
    378 
    379 NS_IMETHODIMP
    380 CommonSocketControl::GetEsniTxt(nsACString& aEsniTxt) {
    381  return NS_ERROR_NOT_IMPLEMENTED;
    382 }
    383 
    384 NS_IMETHODIMP
    385 CommonSocketControl::SetEsniTxt(const nsACString& aEsniTxt) {
    386  return NS_ERROR_NOT_IMPLEMENTED;
    387 }
    388 
    389 NS_IMETHODIMP
    390 CommonSocketControl::GetEchConfig(nsACString& aEchConfig) {
    391  return NS_ERROR_NOT_IMPLEMENTED;
    392 }
    393 
    394 NS_IMETHODIMP
    395 CommonSocketControl::SetEchConfig(const nsACString& aEchConfig) {
    396  return NS_ERROR_NOT_IMPLEMENTED;
    397 }
    398 
    399 NS_IMETHODIMP
    400 CommonSocketControl::GetRetryEchConfig(nsACString& aEchConfig) {
    401  return NS_ERROR_NOT_IMPLEMENTED;
    402 }
    403 
    404 NS_IMETHODIMP
    405 CommonSocketControl::SetHandshakeCallbackListener(
    406    nsITlsHandshakeCallbackListener* callback) {
    407  return NS_ERROR_NOT_IMPLEMENTED;
    408 }
    409 
    410 NS_IMETHODIMP
    411 CommonSocketControl::DisableEarlyData(void) { return NS_ERROR_NOT_IMPLEMENTED; }
    412 
    413 NS_IMETHODIMP
    414 CommonSocketControl::GetPeerId(nsACString& aResult) {
    415  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    416  if (!mPeerId.IsEmpty()) {
    417    aResult.Assign(mPeerId);
    418    return NS_OK;
    419  }
    420 
    421  if (mProviderFlags &
    422      nsISocketProvider::ANONYMOUS_CONNECT) {  // See bug 466080
    423    mPeerId.AppendLiteral("anon:");
    424  }
    425  if (mProviderFlags & nsISocketProvider::NO_PERMANENT_STORAGE) {
    426    mPeerId.AppendLiteral("private:");
    427  }
    428  if (mProviderFlags & nsISocketProvider::BE_CONSERVATIVE) {
    429    mPeerId.AppendLiteral("beConservative:");
    430  }
    431 
    432  mPeerId.Append(mHostName);
    433  mPeerId.Append(':');
    434  mPeerId.AppendInt(GetPort());
    435  nsAutoCString suffix;
    436  mOriginAttributes.CreateSuffix(suffix);
    437  mPeerId.Append(suffix);
    438 
    439  aResult.Assign(mPeerId);
    440  return NS_OK;
    441 }
    442 
    443 NS_IMETHODIMP
    444 CommonSocketControl::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
    445  COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
    446  // Make sure peerId is set.
    447  nsAutoCString unused;
    448  nsresult rv = GetPeerId(unused);
    449  if (NS_FAILED(rv)) {
    450    return rv;
    451  }
    452  nsCOMPtr<nsITransportSecurityInfo> securityInfo(
    453      new psm::TransportSecurityInfo(
    454          mSecurityState, mErrorCode, mHandshakeCertificates.Clone(),
    455          mServerCert, mSucceededCertChain.Clone(), mCipherSuite, mKeaGroupName,
    456          mSignatureSchemeName, mProtocolVersion,
    457          mCertificateTransparencyStatus, mIsAcceptedEch,
    458          mIsDelegatedCredential, mOverridableErrorCategory, mMadeOCSPRequests,
    459          mUsedPrivateDNS, mIsEV, mNPNCompleted, mNegotiatedNPN, mResumed,
    460          mIsBuiltCertChainRootBuiltInRoot, mPeerId));
    461  securityInfo.forget(aSecurityInfo);
    462  return NS_OK;
    463 }
    464 
    465 NS_IMETHODIMP
    466 CommonSocketControl::AsyncGetSecurityInfo(JSContext* aCx,
    467                                          mozilla::dom::Promise** aPromise) {
    468  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    469  NS_ENSURE_ARG_POINTER(aCx);
    470  NS_ENSURE_ARG_POINTER(aPromise);
    471 
    472  nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
    473  if (!globalObject) {
    474    return NS_ERROR_UNEXPECTED;
    475  }
    476 
    477  ErrorResult result;
    478  RefPtr<mozilla::dom::Promise> promise =
    479      mozilla::dom::Promise::Create(globalObject, result);
    480  if (result.Failed()) {
    481    return result.StealNSResult();
    482  }
    483  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    484      "CommonSocketControl::AsyncGetSecurityInfo",
    485      [promise, self = RefPtr{this}]() mutable {
    486        nsCOMPtr<nsITransportSecurityInfo> securityInfo;
    487        nsresult rv = self->GetSecurityInfo(getter_AddRefs(securityInfo));
    488        nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    489            "CommonSocketControl::AsyncGetSecurityInfoResolve",
    490            [rv, promise = std::move(promise),
    491             securityInfo = std::move(securityInfo)]() {
    492              if (NS_FAILED(rv)) {
    493                promise->MaybeReject(rv);
    494              } else {
    495                promise->MaybeResolve(securityInfo);
    496              }
    497            }));
    498        NS_DispatchToMainThread(runnable.forget());
    499      }));
    500  nsCOMPtr<nsIEventTarget> target(
    501      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
    502  if (!target) {
    503    return NS_ERROR_FAILURE;
    504  }
    505  nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
    506  if (NS_FAILED(rv)) {
    507    return rv;
    508  }
    509 
    510  promise.forget(aPromise);
    511  return NS_OK;
    512 }
    513 
    514 NS_IMETHODIMP CommonSocketControl::Claim() { return NS_ERROR_NOT_IMPLEMENTED; }
    515 
    516 NS_IMETHODIMP CommonSocketControl::SetBrowserId(uint64_t) {
    517  return NS_ERROR_NOT_IMPLEMENTED;
    518 }
    519 
    520 NS_IMETHODIMP CommonSocketControl::GetBrowserId(uint64_t*) {
    521  return NS_ERROR_NOT_IMPLEMENTED;
    522 }