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 }