tor-browser

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

nsCertOverrideService.cpp (23102B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 "nsCertOverrideService.h"
      8 
      9 #include "NSSCertDBTrustDomain.h"
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/ScopeExit.h"
     12 #include "mozilla/TaskQueue.h"
     13 #include "mozilla/glean/SecurityManagerSslMetrics.h"
     14 #include "mozilla/TextUtils.h"
     15 #include "mozilla/Tokenizer.h"
     16 #include "mozilla/dom/ToJSValue.h"
     17 #include "nsAppDirectoryServiceDefs.h"
     18 #include "nsCRT.h"
     19 #include "nsILineInputStream.h"
     20 #ifdef ENABLE_WEBDRIVER
     21 #  include "nsIMarionette.h"
     22 #endif
     23 #include "nsIObserver.h"
     24 #include "nsIObserverService.h"
     25 #include "nsIOutputStream.h"
     26 #ifdef ENABLE_WEBDRIVER
     27 #  include "nsIRemoteAgent.h"
     28 #endif
     29 #include "nsISafeOutputStream.h"
     30 #include "nsIX509Cert.h"
     31 #include "nsNSSCertificate.h"
     32 #include "nsNSSComponent.h"
     33 #include "nsNetUtil.h"
     34 #include "nsStreamUtils.h"
     35 #include "nsThreadUtils.h"
     36 
     37 using namespace mozilla;
     38 using namespace mozilla::psm;
     39 
     40 #define CERT_OVERRIDE_FILE_NAME "cert_override.txt"
     41 
     42 class WriterRunnable : public Runnable {
     43 public:
     44  WriterRunnable(nsCertOverrideService* aService, nsCString& aData,
     45                 nsCOMPtr<nsIFile> aFile)
     46      : Runnable("nsCertOverrideService::WriterRunnable"),
     47        mCertOverrideService(aService),
     48        mData(aData),
     49        mFile(std::move(aFile)) {}
     50 
     51  NS_IMETHOD
     52  Run() override {
     53    mCertOverrideService->AssertOnTaskQueue();
     54    nsresult rv;
     55 
     56    auto removeShutdownBlockerOnExit =
     57        MakeScopeExit([certOverrideService = mCertOverrideService]() {
     58          NS_DispatchToMainThread(NS_NewRunnableFunction(
     59              "nsCertOverrideService::RemoveShutdownBlocker",
     60              [certOverrideService] {
     61                certOverrideService->RemoveShutdownBlocker();
     62              }));
     63        });
     64 
     65    nsCOMPtr<nsIOutputStream> outputStream;
     66    rv = NS_NewSafeLocalFileOutputStream(
     67        getter_AddRefs(outputStream), mFile,
     68        PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
     69    NS_ENSURE_SUCCESS(rv, rv);
     70 
     71    const char* ptr = mData.get();
     72    uint32_t remaining = mData.Length();
     73    uint32_t written = 0;
     74    while (remaining > 0) {
     75      rv = outputStream->Write(ptr, remaining, &written);
     76      NS_ENSURE_SUCCESS(rv, rv);
     77      remaining -= written;
     78      ptr += written;
     79    }
     80 
     81    nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outputStream);
     82    MOZ_ASSERT(safeStream);
     83    rv = safeStream->Finish();
     84    NS_ENSURE_SUCCESS(rv, rv);
     85 
     86    return NS_OK;
     87  }
     88 
     89 private:
     90  const RefPtr<nsCertOverrideService> mCertOverrideService;
     91  nsCString mData;
     92  const nsCOMPtr<nsIFile> mFile;
     93 };
     94 
     95 NS_IMPL_ISUPPORTS(nsCertOverride, nsICertOverride)
     96 
     97 NS_IMETHODIMP
     98 nsCertOverride::GetAsciiHost(/*out*/ nsACString& aAsciiHost) {
     99  aAsciiHost = mAsciiHost;
    100  return NS_OK;
    101 }
    102 
    103 NS_IMETHODIMP
    104 nsCertOverride::GetFingerprint(/*out*/ nsACString& aFingerprint) {
    105  aFingerprint = mFingerprint;
    106  return NS_OK;
    107 }
    108 
    109 NS_IMETHODIMP
    110 nsCertOverride::GetPort(/*out*/ int32_t* aPort) {
    111  *aPort = mPort;
    112  return NS_OK;
    113 }
    114 
    115 NS_IMETHODIMP
    116 nsCertOverride::GetHostPort(/*out*/ nsACString& aHostPort) {
    117  nsCertOverrideService::GetHostWithPort(mAsciiHost, mPort, aHostPort);
    118  return NS_OK;
    119 }
    120 
    121 NS_IMETHODIMP
    122 nsCertOverride::GetOriginAttributes(
    123    JSContext* aCtx, /*out*/ JS::MutableHandle<JS::Value> aValue) {
    124  if (ToJSValue(aCtx, mOriginAttributes, aValue)) {
    125    return NS_OK;
    126  }
    127  return NS_ERROR_FAILURE;
    128 }
    129 
    130 NS_IMPL_ISUPPORTS(nsCertOverrideService, nsICertOverrideService, nsIObserver,
    131                  nsISupportsWeakReference, nsIAsyncShutdownBlocker)
    132 
    133 nsCertOverrideService::nsCertOverrideService()
    134    : mMutex("nsCertOverrideService.mutex"),
    135      mDisableAllSecurityCheck(false),
    136      mPendingWriteCount(0) {
    137  nsCOMPtr<nsIEventTarget> target =
    138      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
    139  MOZ_ASSERT(target);
    140 
    141  mWriterTaskQueue = TaskQueue::Create(target.forget(), "CertOverrideService");
    142 }
    143 
    144 nsCertOverrideService::~nsCertOverrideService() = default;
    145 
    146 static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier() {
    147  MOZ_ASSERT(NS_IsMainThread());
    148  nsCOMPtr<nsIAsyncShutdownService> svc =
    149      mozilla::services::GetAsyncShutdownService();
    150  MOZ_RELEASE_ASSERT(svc);
    151 
    152  nsCOMPtr<nsIAsyncShutdownClient> barrier;
    153  nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
    154 
    155  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
    156  MOZ_RELEASE_ASSERT(barrier);
    157  return barrier;
    158 }
    159 
    160 nsresult nsCertOverrideService::Init() {
    161  if (!NS_IsMainThread()) {
    162    MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread");
    163    return NS_ERROR_NOT_SAME_THREAD;
    164  }
    165 
    166  nsCOMPtr<nsIObserverService> observerService =
    167      mozilla::services::GetObserverService();
    168 
    169  // If we cannot add ourselves as a profile change observer, then we will not
    170  // attempt to read/write any settings file. Otherwise, we would end up
    171  // reading/writing the wrong settings file after a profile change.
    172  if (observerService) {
    173    observerService->AddObserver(this, "last-pb-context-exited", false);
    174    observerService->AddObserver(this, "profile-do-change", true);
    175    // simulate a profile change so we read the current profile's settings file
    176    Observe(nullptr, "profile-do-change", nullptr);
    177  }
    178 
    179  return NS_OK;
    180 }
    181 
    182 NS_IMETHODIMP
    183 nsCertOverrideService::Observe(nsISupports*, const char* aTopic,
    184                               const char16_t* aData) {
    185  if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
    186    // The profile has already changed.
    187    // Now read from the new profile location.
    188    // we also need to update the cached file location
    189 
    190    MutexAutoLock lock(mMutex);
    191 
    192    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    193                                         getter_AddRefs(mSettingsFile));
    194    if (NS_SUCCEEDED(rv)) {
    195      mSettingsFile->AppendNative(nsLiteralCString(CERT_OVERRIDE_FILE_NAME));
    196    } else {
    197      mSettingsFile = nullptr;
    198    }
    199    Read(lock);
    200    CountPermanentOverrideTelemetry(lock);
    201  } else if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) {
    202    ClearValidityOverride("all:temporary-certificates"_ns, 0,
    203                          OriginAttributes());
    204  }
    205 
    206  return NS_OK;
    207 }
    208 
    209 void nsCertOverrideService::RemoveAllTemporaryOverrides() {
    210  MutexAutoLock lock(mMutex);
    211  bool removedAny = false;
    212  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
    213    nsCertOverrideEntry* entry = iter.Get();
    214    if (entry->mSettings->mIsTemporary) {
    215      iter.Remove();
    216      removedAny = true;
    217    }
    218  }
    219  if (removedAny) {
    220    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    221    if (os) {
    222      os->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
    223    }
    224  }
    225  // no need to write, as temporaries are never written to disk
    226 }
    227 
    228 static const char sSHA256OIDString[] = "OID.2.16.840.1.101.3.4.2.1";
    229 nsresult nsCertOverrideService::Read(const MutexAutoLock& aProofOfLock) {
    230  mMutex.AssertCurrentThreadOwns();
    231  // If we don't have a profile, then we won't try to read any settings file.
    232  if (!mSettingsFile) return NS_OK;
    233 
    234  nsresult rv;
    235  nsCOMPtr<nsIInputStream> fileInputStream;
    236  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
    237                                  mSettingsFile);
    238  if (NS_FAILED(rv)) {
    239    return rv;
    240  }
    241 
    242  nsCOMPtr<nsILineInputStream> lineInputStream =
    243      do_QueryInterface(fileInputStream, &rv);
    244  if (NS_FAILED(rv)) {
    245    return rv;
    246  }
    247 
    248  nsAutoCString buffer;
    249  bool isMore = true;
    250 
    251  // Each line is of the form:
    252  // host:port:originAttributes \t sSHA256OIDString \t fingerprint \t
    253  // There may be some "bits" identifiers and "dbKey" after the `fingerprint`
    254  // field in 'fingerprint \t \t dbKey' format, but these are now ignored.
    255  // Lines that don't match this form are silently dropped.
    256 
    257  while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
    258    if (buffer.IsEmpty() || buffer.First() == '#') {
    259      continue;
    260    }
    261 
    262    Tokenizer parser(buffer);
    263    nsDependentCSubstring host;
    264    if (parser.CheckChar('[')) {  // this is a IPv6 address
    265      if (!parser.ReadUntil(Tokenizer::Token::Char(']'), host) ||
    266          host.Length() == 0 || !parser.CheckChar(':')) {
    267        continue;
    268      }
    269    } else if (!parser.ReadUntil(Tokenizer::Token::Char(':'), host) ||
    270               host.Length() == 0) {
    271      continue;
    272    }
    273    int32_t port = -1;
    274    if (!parser.ReadInteger(&port)) {
    275      continue;
    276    }
    277    OriginAttributes attributes;
    278    if (parser.CheckChar(':')) {
    279      nsDependentCSubstring attributesString;
    280      if (!parser.ReadUntil(Tokenizer::Token::Whitespace(), attributesString) ||
    281          !attributes.PopulateFromSuffix(attributesString)) {
    282        continue;
    283      }
    284    } else if (!parser.CheckWhite()) {
    285      continue;
    286    }
    287    nsDependentCSubstring algorithm;
    288    if (!parser.ReadUntil(Tokenizer::Token::Whitespace(), algorithm) ||
    289        algorithm != sSHA256OIDString) {
    290      continue;
    291    }
    292    nsDependentCSubstring fingerprint;
    293    if (!parser.ReadUntil(Tokenizer::Token::Whitespace(), fingerprint) ||
    294        fingerprint.Length() == 0) {
    295      continue;
    296    }
    297 
    298    AddEntryToList(host, port, attributes,
    299                   false,  // not temporary
    300                   fingerprint, aProofOfLock);
    301  }
    302 
    303  return NS_OK;
    304 }
    305 
    306 nsresult nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock) {
    307  mMutex.AssertCurrentThreadOwns();
    308  MOZ_ASSERT(NS_IsMainThread());
    309  if (!NS_IsMainThread()) {
    310    return NS_ERROR_NOT_SAME_THREAD;
    311  }
    312 
    313  // If we don't have any profile, then we won't try to write any file
    314  if (!mSettingsFile) {
    315    return NS_OK;
    316  }
    317 
    318  nsCString output;
    319 
    320  static const char kHeader[] =
    321      "# PSM Certificate Override Settings file" NS_LINEBREAK
    322      "# This is a generated file!  Do not edit." NS_LINEBREAK;
    323 
    324  /* see ::Read for file format */
    325 
    326  output.Append(kHeader);
    327 
    328  static const char kTab[] = "\t";
    329  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
    330    nsCertOverrideEntry* entry = iter.Get();
    331 
    332    RefPtr<nsCertOverride> settings = entry->mSettings;
    333    if (settings->mIsTemporary) {
    334      continue;
    335    }
    336 
    337    output.Append(entry->mKeyString);
    338    output.Append(kTab);
    339    output.Append(sSHA256OIDString);
    340    output.Append(kTab);
    341    output.Append(settings->mFingerprint);
    342    output.Append(kTab);
    343    // the "bits" string used to go here, but it no longer exists
    344    // the "\t dbKey" string used to go here, but it no longer exists
    345    output.Append(NS_LINEBREAK);
    346  }
    347 
    348  // Make a clone of the file to pass to the WriterRunnable.
    349  nsCOMPtr<nsIFile> file;
    350  nsresult rv;
    351  rv = mSettingsFile->Clone(getter_AddRefs(file));
    352  NS_ENSURE_SUCCESS(rv, rv);
    353 
    354  nsCOMPtr<nsIRunnable> runnable = new WriterRunnable(this, output, file);
    355  rv = mWriterTaskQueue->Dispatch(runnable.forget());
    356  if (NS_FAILED(rv)) {
    357    return rv;
    358  }
    359  mPendingWriteCount++;
    360 
    361  if (mPendingWriteCount == 1) {
    362    rv = GetShutdownBarrier()->AddBlocker(
    363        this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
    364        u"nsCertOverrideService writing data"_ns);
    365    NS_ENSURE_SUCCESS(rv, rv);
    366  }
    367 
    368  return NS_OK;
    369 }
    370 
    371 nsresult GetCertSha256Fingerprint(nsIX509Cert* aCert, nsCString& aResult) {
    372  nsAutoString fpStrUTF16;
    373  nsresult rv = aCert->GetSha256Fingerprint(fpStrUTF16);
    374  if (NS_FAILED(rv)) {
    375    return rv;
    376  }
    377  aResult.Assign(NS_ConvertUTF16toUTF8(fpStrUTF16));
    378  return NS_OK;
    379 }
    380 
    381 NS_IMETHODIMP
    382 nsCertOverrideService::RememberValidityOverride(
    383    const nsACString& aHostName, int32_t aPort,
    384    const OriginAttributes& aOriginAttributes, nsIX509Cert* aCert,
    385    bool aTemporary) {
    386  if (aHostName.IsEmpty() || !IsAscii(aHostName) || !aCert) {
    387    return NS_ERROR_INVALID_ARG;
    388  }
    389  if (aPort < -1) {
    390    return NS_ERROR_INVALID_ARG;
    391  }
    392  if (!NS_IsMainThread()) {
    393    return NS_ERROR_NOT_SAME_THREAD;
    394  }
    395 
    396  nsAutoCString fpStr;
    397  nsresult rv = GetCertSha256Fingerprint(aCert, fpStr);
    398  if (NS_FAILED(rv)) {
    399    return rv;
    400  }
    401 
    402  {
    403    MutexAutoLock lock(mMutex);
    404    AddEntryToList(aHostName, aPort, aOriginAttributes, aTemporary, fpStr,
    405                   lock);
    406    if (!aTemporary) {
    407      Write(lock);
    408    }
    409  }
    410 
    411  return NS_OK;
    412 }
    413 
    414 NS_IMETHODIMP
    415 nsCertOverrideService::RememberValidityOverrideScriptable(
    416    const nsACString& aHostName, int32_t aPort,
    417    JS::Handle<JS::Value> aOriginAttributes, nsIX509Cert* aCert,
    418    bool aTemporary, JSContext* aCx) {
    419  OriginAttributes attrs;
    420  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
    421    return NS_ERROR_INVALID_ARG;
    422  }
    423 
    424  return RememberValidityOverride(aHostName, aPort, attrs, aCert, aTemporary);
    425 }
    426 
    427 NS_IMETHODIMP
    428 nsCertOverrideService::HasMatchingOverride(
    429    const nsACString& aHostName, int32_t aPort,
    430    const OriginAttributes& aOriginAttributes, nsIX509Cert* aCert,
    431    bool* aIsTemporary, bool* aRetval) {
    432  bool disableAllSecurityCheck = false;
    433  {
    434    MutexAutoLock lock(mMutex);
    435    if (mUserContextIdsWithSecurityChecksOverride.has(
    436            aOriginAttributes.mUserContextId)) {
    437      auto p = mUserContextIdsWithSecurityChecksOverride.lookup(
    438          aOriginAttributes.mUserContextId);
    439      disableAllSecurityCheck = p->value();
    440    } else {
    441      disableAllSecurityCheck = mDisableAllSecurityCheck;
    442    }
    443  }
    444  if (disableAllSecurityCheck) {
    445    *aIsTemporary = false;
    446    *aRetval = true;
    447    return NS_OK;
    448  }
    449 
    450  if (aHostName.IsEmpty() || !IsAscii(aHostName)) {
    451    return NS_ERROR_INVALID_ARG;
    452  }
    453  if (aPort < -1) return NS_ERROR_INVALID_ARG;
    454 
    455  NS_ENSURE_ARG_POINTER(aCert);
    456  NS_ENSURE_ARG_POINTER(aIsTemporary);
    457  NS_ENSURE_ARG_POINTER(aRetval);
    458  *aRetval = false;
    459 
    460  RefPtr<nsCertOverride> settings(
    461      GetOverrideFor(aHostName, aPort, aOriginAttributes));
    462  // If there is no corresponding override and the given OriginAttributes isn't
    463  // the default, try to look up an override using the default OriginAttributes.
    464  if (!settings && aOriginAttributes != OriginAttributes()) {
    465    settings = GetOverrideFor(aHostName, aPort, OriginAttributes());
    466  }
    467  if (!settings) {
    468    return NS_OK;
    469  }
    470 
    471  *aIsTemporary = settings->mIsTemporary;
    472 
    473  nsAutoCString fpStr;
    474  nsresult rv = GetCertSha256Fingerprint(aCert, fpStr);
    475  if (NS_FAILED(rv)) {
    476    return rv;
    477  }
    478 
    479  *aRetval = settings->mFingerprint.Equals(fpStr);
    480  return NS_OK;
    481 }
    482 
    483 already_AddRefed<nsCertOverride> nsCertOverrideService::GetOverrideFor(
    484    const nsACString& aHostName, int32_t aPort,
    485    const OriginAttributes& aOriginAttributes) {
    486  nsAutoCString keyString;
    487  GetKeyString(aHostName, aPort, aOriginAttributes, keyString);
    488  MutexAutoLock lock(mMutex);
    489  nsCertOverrideEntry* entry = mSettingsTable.GetEntry(keyString.get());
    490  if (!entry) {
    491    return nullptr;
    492  }
    493  return do_AddRef(entry->mSettings);
    494 }
    495 
    496 NS_IMETHODIMP
    497 nsCertOverrideService::HasMatchingOverrideScriptable(
    498    const nsACString& aHostName, int32_t aPort,
    499    JS::Handle<JS::Value> aOriginAttributes, nsIX509Cert* aCert,
    500    bool* aIsTemporary, JSContext* aCx, bool* aRetval) {
    501  OriginAttributes attrs;
    502  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
    503    return NS_ERROR_INVALID_ARG;
    504  }
    505 
    506  return HasMatchingOverride(aHostName, aPort, attrs, aCert, aIsTemporary,
    507                             aRetval);
    508 }
    509 
    510 nsresult nsCertOverrideService::AddEntryToList(
    511    const nsACString& aHostName, int32_t aPort,
    512    const OriginAttributes& aOriginAttributes, const bool aIsTemporary,
    513    const nsACString& fingerprint, const MutexAutoLock& aProofOfLock) {
    514  mMutex.AssertCurrentThreadOwns();
    515  nsAutoCString keyString;
    516  GetKeyString(aHostName, aPort, aOriginAttributes, keyString);
    517 
    518  nsCertOverrideEntry* entry = mSettingsTable.PutEntry(keyString.get());
    519 
    520  if (!entry) {
    521    NS_ERROR("can't insert a null entry!");
    522    return NS_ERROR_OUT_OF_MEMORY;
    523  }
    524 
    525  entry->mKeyString = keyString;
    526 
    527  RefPtr<nsCertOverride> settings(new nsCertOverride());
    528 
    529  settings->mAsciiHost = aHostName;
    530  settings->mPort = aPort;
    531  settings->mOriginAttributes = aOriginAttributes;
    532  settings->mIsTemporary = aIsTemporary;
    533  settings->mFingerprint = fingerprint;
    534  entry->mSettings = settings;
    535 
    536  return NS_OK;
    537 }
    538 
    539 NS_IMETHODIMP
    540 nsCertOverrideService::ClearValidityOverride(
    541    const nsACString& aHostName, int32_t aPort,
    542    const OriginAttributes& aOriginAttributes) {
    543  if (aHostName.IsEmpty() || !IsAscii(aHostName)) {
    544    return NS_ERROR_INVALID_ARG;
    545  }
    546  if (!NS_IsMainThread()) {
    547    return NS_ERROR_NOT_SAME_THREAD;
    548  }
    549 
    550  if (aPort == 0 && aHostName.EqualsLiteral("all:temporary-certificates")) {
    551    RemoveAllTemporaryOverrides();
    552    return NS_OK;
    553  }
    554  nsAutoCString keyString;
    555  GetKeyString(aHostName, aPort, aOriginAttributes, keyString);
    556  {
    557    MutexAutoLock lock(mMutex);
    558    mSettingsTable.RemoveEntry(keyString.get());
    559    Write(lock);
    560  }
    561 
    562  nsCOMPtr<nsINSSComponent> nss(do_GetService(PSM_COMPONENT_CONTRACTID));
    563  if (nss) {
    564    nss->ClearSSLExternalAndInternalSessionCache();
    565  } else {
    566    return NS_ERROR_NOT_AVAILABLE;
    567  }
    568 
    569  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    570  if (os) {
    571    os->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
    572  }
    573 
    574  return NS_OK;
    575 }
    576 NS_IMETHODIMP
    577 nsCertOverrideService::ClearValidityOverrideScriptable(
    578    const nsACString& aHostName, int32_t aPort,
    579    JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx) {
    580  OriginAttributes attrs;
    581  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
    582    return NS_ERROR_INVALID_ARG;
    583  }
    584 
    585  return ClearValidityOverride(aHostName, aPort, attrs);
    586 }
    587 
    588 NS_IMETHODIMP
    589 nsCertOverrideService::ClearAllOverrides() {
    590  if (!NS_IsMainThread()) {
    591    return NS_ERROR_NOT_SAME_THREAD;
    592  }
    593 
    594  {
    595    MutexAutoLock lock(mMutex);
    596    mSettingsTable.Clear();
    597    Write(lock);
    598  }
    599 
    600  nsCOMPtr<nsINSSComponent> nss(do_GetService(PSM_COMPONENT_CONTRACTID));
    601  if (nss) {
    602    nss->ClearSSLExternalAndInternalSessionCache();
    603  } else {
    604    return NS_ERROR_NOT_AVAILABLE;
    605  }
    606 
    607  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    608  if (os) {
    609    os->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
    610  }
    611 
    612  return NS_OK;
    613 }
    614 
    615 void nsCertOverrideService::CountPermanentOverrideTelemetry(
    616    const MutexAutoLock& aProofOfLock) {
    617  mMutex.AssertCurrentThreadOwns();
    618  uint32_t overrideCount = 0;
    619  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
    620    if (!iter.Get()->mSettings->mIsTemporary) {
    621      overrideCount++;
    622    }
    623  }
    624  glean::ssl::permanent_cert_error_overrides.AccumulateSingleSample(
    625      overrideCount);
    626 }
    627 
    628 static bool IsDebugger() {
    629 #ifdef ENABLE_WEBDRIVER
    630  nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
    631  if (marionette) {
    632    bool marionetteRunning = false;
    633    marionette->GetRunning(&marionetteRunning);
    634    if (marionetteRunning) {
    635      return true;
    636    }
    637  }
    638 
    639  nsCOMPtr<nsIRemoteAgent> agent = do_GetService(NS_REMOTEAGENT_CONTRACTID);
    640  if (agent) {
    641    bool remoteAgentRunning = false;
    642    agent->GetRunning(&remoteAgentRunning);
    643    if (remoteAgentRunning) {
    644      return true;
    645    }
    646  }
    647 #endif
    648 
    649  return false;
    650 }
    651 
    652 NS_IMETHODIMP
    653 nsCertOverrideService::
    654    SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(bool aDisable) {
    655  if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
    656    return NS_ERROR_NOT_AVAILABLE;
    657  }
    658 
    659  {
    660    MutexAutoLock lock(mMutex);
    661    mDisableAllSecurityCheck = aDisable;
    662  }
    663 
    664  nsCOMPtr<nsINSSComponent> nss(do_GetService(PSM_COMPONENT_CONTRACTID));
    665  if (nss) {
    666    nss->ClearSSLExternalAndInternalSessionCache();
    667  } else {
    668    return NS_ERROR_NOT_AVAILABLE;
    669  }
    670 
    671  return NS_OK;
    672 }
    673 
    674 NS_IMETHODIMP
    675 nsCertOverrideService::
    676    SetDisableAllSecurityChecksAndLetAttackersInterceptMyDataForUserContext(
    677        uint32_t aUserContextId, bool aDisable) {
    678  if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
    679    return NS_ERROR_NOT_AVAILABLE;
    680  }
    681 
    682  {
    683    MutexAutoLock lock(mMutex);
    684    (void)mUserContextIdsWithSecurityChecksOverride.put(aUserContextId,
    685                                                        aDisable);
    686  }
    687 
    688  return NS_OK;
    689 }
    690 
    691 NS_IMETHODIMP
    692 nsCertOverrideService::
    693    ResetDisableAllSecurityChecksAndLetAttackersInterceptMyDataForUserContext(
    694        uint32_t aUserContextId) {
    695  if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
    696    return NS_ERROR_NOT_AVAILABLE;
    697  }
    698 
    699  {
    700    MutexAutoLock lock(mMutex);
    701    mUserContextIdsWithSecurityChecksOverride.remove(aUserContextId);
    702  }
    703 
    704  return NS_OK;
    705 }
    706 
    707 NS_IMETHODIMP
    708 nsCertOverrideService::GetSecurityCheckDisabled(bool* aDisabled) {
    709  MutexAutoLock lock(mMutex);
    710  *aDisabled = mDisableAllSecurityCheck;
    711  return NS_OK;
    712 }
    713 
    714 NS_IMETHODIMP
    715 nsCertOverrideService::GetOverrides(
    716    /*out*/ nsTArray<RefPtr<nsICertOverride>>& retval) {
    717  MutexAutoLock lock(mMutex);
    718  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
    719    const RefPtr<nsICertOverride> settings = iter.Get()->mSettings;
    720 
    721    retval.AppendElement(settings);
    722  }
    723  return NS_OK;
    724 }
    725 
    726 void nsCertOverrideService::GetHostWithPort(const nsACString& aHostName,
    727                                            int32_t aPort,
    728                                            nsACString& aRetval) {
    729  nsAutoCString hostPort;
    730  if (aHostName.Contains(':')) {
    731    // if aHostName is an IPv6 address, add brackets to match the internal
    732    // representation, which always stores IPv6 addresses with brackets
    733    hostPort.Append('[');
    734    hostPort.Append(aHostName);
    735    hostPort.Append(']');
    736  } else {
    737    hostPort.Append(aHostName);
    738  }
    739  if (aPort == -1) {
    740    aPort = 443;
    741  }
    742  if (!hostPort.IsEmpty()) {
    743    hostPort.Append(':');
    744    hostPort.AppendInt(aPort);
    745  }
    746  aRetval.Assign(hostPort);
    747 }
    748 
    749 void nsCertOverrideService::GetKeyString(
    750    const nsACString& aHostName, int32_t aPort,
    751    const OriginAttributes& aOriginAttributes, nsACString& aRetval) {
    752  nsAutoCString keyString;
    753  GetHostWithPort(aHostName, aPort, keyString);
    754  keyString.Append(':');
    755  OriginAttributes strippedAttributes(aOriginAttributes);
    756  strippedAttributes.StripAttributes(
    757      ~OriginAttributes::STRIP_PRIVATE_BROWSING_ID);
    758  nsAutoCString attributeSuffix;
    759  strippedAttributes.CreateSuffix(attributeSuffix);
    760  keyString.Append(attributeSuffix);
    761  aRetval.Assign(keyString);
    762 }
    763 
    764 // nsIAsyncShutdownBlocker implementation
    765 NS_IMETHODIMP
    766 nsCertOverrideService::GetName(nsAString& aName) {
    767  aName = u"nsCertOverrideService: shutdown"_ns;
    768  return NS_OK;
    769 }
    770 
    771 NS_IMETHODIMP
    772 nsCertOverrideService::GetState(nsIPropertyBag** aState) {
    773  if (!aState) {
    774    return NS_ERROR_INVALID_ARG;
    775  }
    776  *aState = nullptr;
    777  return NS_OK;
    778 }
    779 
    780 NS_IMETHODIMP
    781 nsCertOverrideService::BlockShutdown(nsIAsyncShutdownClient*) { return NS_OK; }
    782 
    783 void nsCertOverrideService::RemoveShutdownBlocker() {
    784  MOZ_ASSERT(NS_IsMainThread());
    785  MOZ_ASSERT(mPendingWriteCount > 0);
    786  mPendingWriteCount--;
    787  if (mPendingWriteCount == 0) {
    788    nsresult rv = GetShutdownBarrier()->RemoveBlocker(this);
    789    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
    790  }
    791 }