tor-browser

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

SecretDecoderRing.cpp (11112B)


      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 "SecretDecoderRing.h"
      8 
      9 #include "ScopedNSSTypes.h"
     10 #include "mozilla/Base64.h"
     11 #include "mozilla/Casting.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/Services.h"
     14 #include "mozilla/StaticPrefs_security.h"
     15 #include "mozilla/ErrorResult.h"
     16 #include "mozilla/dom/Promise.h"
     17 #include "nsCOMPtr.h"
     18 #include "nsIInterfaceRequestor.h"
     19 #include "nsIInterfaceRequestorUtils.h"
     20 #include "nsIObserverService.h"
     21 #include "nsITokenPasswordDialogs.h"
     22 #include "nsNSSComponent.h"
     23 #include "nsNSSHelper.h"
     24 #include "nsNetCID.h"
     25 #include "nsPK11TokenDB.h"
     26 #include "pk11func.h"
     27 #include "pk11sdr.h"
     28 
     29 static mozilla::LazyLogModule gSDRLog("sdrlog");
     30 
     31 using namespace mozilla;
     32 using dom::Promise;
     33 
     34 NS_IMPL_ISUPPORTS(SecretDecoderRing, nsISecretDecoderRing)
     35 
     36 void BackgroundSdrEncryptStrings(const nsTArray<nsCString>& plaintexts,
     37                                 RefPtr<Promise>& aPromise) {
     38  nsCOMPtr<nsISecretDecoderRing> sdrService =
     39      do_GetService(NS_SECRETDECODERRING_CONTRACTID);
     40  nsTArray<nsString> cipherTexts(plaintexts.Length());
     41 
     42  nsresult rv = NS_ERROR_FAILURE;
     43  for (const auto& plaintext : plaintexts) {
     44    nsCString cipherText;
     45    rv = sdrService->EncryptString(plaintext, cipherText);
     46 
     47    if (NS_WARN_IF(NS_FAILED(rv))) {
     48      break;
     49    }
     50 
     51    cipherTexts.AppendElement(NS_ConvertASCIItoUTF16(cipherText));
     52  }
     53 
     54  nsCOMPtr<nsIRunnable> runnable(
     55      NS_NewRunnableFunction("BackgroundSdrEncryptStringsResolve",
     56                             [rv, aPromise = std::move(aPromise),
     57                              cipherTexts = std::move(cipherTexts)]() {
     58                               if (NS_FAILED(rv)) {
     59                                 aPromise->MaybeReject(rv);
     60                               } else {
     61                                 aPromise->MaybeResolve(cipherTexts);
     62                               }
     63                             }));
     64  NS_DispatchToMainThread(runnable.forget());
     65 }
     66 
     67 void BackgroundSdrDecryptStrings(const nsTArray<nsCString>& encryptedStrings,
     68                                 RefPtr<Promise>& aPromise) {
     69  nsCOMPtr<nsISecretDecoderRing> sdrService =
     70      do_GetService(NS_SECRETDECODERRING_CONTRACTID);
     71  nsTArray<nsString> plainTexts(encryptedStrings.Length());
     72 
     73  nsresult rv = NS_ERROR_FAILURE;
     74  for (const auto& encryptedString : encryptedStrings) {
     75    nsCString plainText;
     76    rv = sdrService->DecryptString(encryptedString, plainText);
     77 
     78    if (NS_FAILED(rv)) {
     79      if (rv == NS_ERROR_NOT_AVAILABLE) {
     80        // Master Password entry was canceled. Don't keep prompting again.
     81        break;
     82      }
     83 
     84      // NS_ERROR_ILLEGAL_VALUE or NS_ERROR_FAILURE could be due to bad data for
     85      // a single string but we still want to decrypt the others.
     86      // Callers of `decryptMany` in crypto-SDR.js assume there will be an
     87      // equal number of usernames and passwords so use an empty string to keep
     88      // this assumption true.
     89      MOZ_LOG(gSDRLog, LogLevel::Warning,
     90              ("Couldn't decrypt string: %s", encryptedString.get()));
     91      plainTexts.AppendElement(nullptr);
     92      rv = NS_OK;
     93      continue;
     94    }
     95 
     96    plainTexts.AppendElement(NS_ConvertUTF8toUTF16(plainText));
     97  }
     98 
     99  nsCOMPtr<nsIRunnable> runnable(
    100      NS_NewRunnableFunction("BackgroundSdrDecryptStringsResolve",
    101                             [rv, aPromise = std::move(aPromise),
    102                              plainTexts = std::move(plainTexts)]() {
    103                               if (NS_FAILED(rv)) {
    104                                 aPromise->MaybeReject(rv);
    105                               } else {
    106                                 aPromise->MaybeResolve(plainTexts);
    107                               }
    108                             }));
    109  NS_DispatchToMainThread(runnable.forget());
    110 }
    111 
    112 nsresult SecretDecoderRing::Encrypt(CK_MECHANISM_TYPE type,
    113                                    const nsACString& data,
    114                                    /*out*/ nsACString& result) {
    115  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    116  if (!slot) {
    117    return NS_ERROR_NOT_AVAILABLE;
    118  }
    119 
    120  /* Make sure token is initialized. */
    121  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
    122  nsresult rv = setPassword(slot.get(), ctx);
    123  if (NS_FAILED(rv)) {
    124    return rv;
    125  }
    126 
    127  /* Force authentication */
    128  if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
    129    return NS_ERROR_FAILURE;
    130  }
    131 
    132  /* Use default key id */
    133  SECItem keyid;
    134  keyid.data = nullptr;
    135  keyid.len = 0;
    136  SECItem request;
    137  request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
    138  request.len = data.Length();
    139  ScopedAutoSECItem reply;
    140  if (PK11SDR_EncryptWithMechanism(slot.get(), &keyid, type, &request, &reply,
    141                                   ctx) != SECSuccess) {
    142    return NS_ERROR_FAILURE;
    143  }
    144 
    145  result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
    146  return NS_OK;
    147 }
    148 
    149 nsresult SecretDecoderRing::Decrypt(const nsACString& data,
    150                                    /*out*/ nsACString& result) {
    151  /* Find token with SDR key */
    152  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    153  if (!slot) {
    154    return NS_ERROR_NOT_AVAILABLE;
    155  }
    156 
    157  /* Force authentication */
    158  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
    159  if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
    160    return NS_ERROR_NOT_AVAILABLE;
    161  }
    162 
    163  SECItem request;
    164  request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
    165  request.len = data.Length();
    166  ScopedAutoSECItem reply;
    167  if (PK11SDR_Decrypt(&request, &reply, ctx) != SECSuccess) {
    168    return NS_ERROR_FAILURE;
    169  }
    170 
    171  result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
    172  return NS_OK;
    173 }
    174 
    175 NS_IMETHODIMP
    176 SecretDecoderRing::EncryptString(const nsACString& text,
    177                                 /*out*/ nsACString& encryptedBase64Text) {
    178  CK_MECHANISM_TYPE type;
    179  nsCString prefix;
    180  switch (StaticPrefs::security_sdr_mechanism()) {
    181    case 0:
    182      type = CKM_DES3_CBC;
    183      break;
    184    case 1:
    185    default:
    186      type = CKM_AES_CBC;
    187      break;
    188  }
    189  nsAutoCString encryptedText;
    190  nsresult rv = Encrypt(type, text, encryptedText);
    191  if (NS_FAILED(rv)) {
    192    return rv;
    193  }
    194 
    195  rv = Base64Encode(encryptedText, encryptedBase64Text);
    196  if (NS_FAILED(rv)) {
    197    return rv;
    198  }
    199 
    200  return NS_OK;
    201 }
    202 
    203 NS_IMETHODIMP
    204 SecretDecoderRing::AsyncEncryptStrings(const nsTArray<nsCString>& plaintexts,
    205                                       JSContext* aCx, Promise** aPromise) {
    206  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    207  NS_ENSURE_ARG(!plaintexts.IsEmpty());
    208  NS_ENSURE_ARG_POINTER(aCx);
    209  NS_ENSURE_ARG_POINTER(aPromise);
    210 
    211  nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
    212  if (NS_WARN_IF(!globalObject)) {
    213    return NS_ERROR_UNEXPECTED;
    214  }
    215 
    216  ErrorResult result;
    217  RefPtr<Promise> promise = Promise::Create(globalObject, result);
    218  if (NS_WARN_IF(result.Failed())) {
    219    return result.StealNSResult();
    220  }
    221 
    222  // plaintexts are already expected to be UTF-8.
    223  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    224      "BackgroundSdrEncryptStrings",
    225      [promise, plaintexts = plaintexts.Clone()]() mutable {
    226        BackgroundSdrEncryptStrings(plaintexts, promise);
    227      }));
    228 
    229  nsCOMPtr<nsIEventTarget> target(
    230      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
    231  if (!target) {
    232    return NS_ERROR_FAILURE;
    233  }
    234  nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
    235  if (NS_WARN_IF(NS_FAILED(rv))) {
    236    return rv;
    237  }
    238 
    239  promise.forget(aPromise);
    240  return NS_OK;
    241 }
    242 
    243 NS_IMETHODIMP
    244 SecretDecoderRing::DecryptString(const nsACString& encryptedBase64Text,
    245                                 /*out*/ nsACString& decryptedText) {
    246  nsAutoCString encryptedText;
    247  nsresult rv = Base64Decode(encryptedBase64Text, encryptedText);
    248  if (NS_FAILED(rv)) {
    249    return rv;
    250  }
    251 
    252  rv = Decrypt(encryptedText, decryptedText);
    253  if (NS_FAILED(rv)) {
    254    return rv;
    255  }
    256 
    257  return NS_OK;
    258 }
    259 
    260 NS_IMETHODIMP
    261 SecretDecoderRing::AsyncDecryptStrings(
    262    const nsTArray<nsCString>& encryptedStrings, JSContext* aCx,
    263    Promise** aPromise) {
    264  MOZ_RELEASE_ASSERT(NS_IsMainThread());
    265  NS_ENSURE_ARG(!encryptedStrings.IsEmpty());
    266  NS_ENSURE_ARG_POINTER(aCx);
    267  NS_ENSURE_ARG_POINTER(aPromise);
    268 
    269  nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
    270  if (NS_WARN_IF(!globalObject)) {
    271    return NS_ERROR_UNEXPECTED;
    272  }
    273 
    274  ErrorResult result;
    275  RefPtr<Promise> promise = Promise::Create(globalObject, result);
    276  if (NS_WARN_IF(result.Failed())) {
    277    return result.StealNSResult();
    278  }
    279 
    280  // encryptedStrings are expected to be base64.
    281  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    282      "BackgroundSdrDecryptStrings",
    283      [promise, encryptedStrings = encryptedStrings.Clone()]() mutable {
    284        BackgroundSdrDecryptStrings(encryptedStrings, promise);
    285      }));
    286 
    287  nsCOMPtr<nsIEventTarget> target(
    288      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
    289  if (!target) {
    290    return NS_ERROR_FAILURE;
    291  }
    292  nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
    293  if (NS_WARN_IF(NS_FAILED(rv))) {
    294    return rv;
    295  }
    296 
    297  promise.forget(aPromise);
    298  return NS_OK;
    299 }
    300 
    301 NS_IMETHODIMP
    302 SecretDecoderRing::ChangePassword() {
    303  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    304  if (!slot) {
    305    return NS_ERROR_NOT_AVAILABLE;
    306  }
    307 
    308  // nsPK11Token::nsPK11Token takes its own reference to slot, so we pass a
    309  // non-owning pointer here.
    310  nsCOMPtr<nsIPK11Token> token = new nsPK11Token(slot.get());
    311 
    312  nsCOMPtr<nsITokenPasswordDialogs> dialogs;
    313  nsresult rv = getNSSDialogs(getter_AddRefs(dialogs),
    314                              NS_GET_IID(nsITokenPasswordDialogs),
    315                              NS_TOKENPASSWORDSDIALOG_CONTRACTID);
    316  if (NS_FAILED(rv)) {
    317    return rv;
    318  }
    319 
    320  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
    321  bool canceled;  // Ignored
    322  return dialogs->SetPassword(ctx, token, &canceled);
    323 }
    324 
    325 NS_IMETHODIMP
    326 SecretDecoderRing::Logout() {
    327  PK11_LogoutAll();
    328  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
    329  if (!nssComponent) {
    330    return NS_ERROR_NOT_AVAILABLE;
    331  }
    332  return nssComponent->ClearSSLExternalAndInternalSessionCache();
    333 }
    334 
    335 NS_IMETHODIMP
    336 SecretDecoderRing::LogoutAndTeardown() {
    337  PK11_LogoutAll();
    338  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
    339  if (!nssComponent) {
    340    return NS_ERROR_NOT_AVAILABLE;
    341  }
    342 
    343  // LogoutAuthenticatedPK11 also clears the SSL caches.
    344  nsresult rv = nssComponent->LogoutAuthenticatedPK11();
    345  if (NS_FAILED(rv)) {
    346    return rv;
    347  }
    348 
    349  // After we just logged out, we need to prune dead connections to make
    350  // sure that all connections that should be stopped, are stopped. See
    351  // bug 517584.
    352  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    353  if (os) {
    354    os->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr);
    355  }
    356 
    357  return NS_OK;
    358 }