/* -*- 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 "SecretDecoderRing.h" #include "ScopedNSSTypes.h" #include "mozilla/Base64.h" #include "mozilla/Casting.h" #include "mozilla/Services.h" #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIObserverService.h" #include "nsIServiceManager.h" #include "nsITokenPasswordDialogs.h" #include "nsMemory.h" #include "nsNSSComponent.h" #include "nsNSSHelper.h" #include "nsString.h" #include "nsThreadUtils.h" #include "pk11func.h" #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt #include "plstr.h" #include "ssl.h" // For SSL_ClearSessionCache #include "stdlib.h" using namespace mozilla; // NOTE: Should these be the thread-safe versions? NS_IMPL_ISUPPORTS(SecretDecoderRing, nsISecretDecoderRing) SecretDecoderRing::SecretDecoderRing() { } SecretDecoderRing::~SecretDecoderRing() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return; } shutdown(calledFromObject); } nsresult SecretDecoderRing::Encrypt(unsigned char* data, uint32_t dataLen, /*out*/ unsigned char** result, /*out*/ uint32_t* resultLen) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr ctx = new PipUIContext(); UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return NS_ERROR_NOT_AVAILABLE; } /* Make sure token is initialized. */ nsresult rv = setPassword(slot.get(), ctx, locker); if (NS_FAILED(rv)) { return rv; } /* Force authentication */ if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) { return NS_ERROR_FAILURE; } /* Use default key id */ SECItem keyid; keyid.data = nullptr; keyid.len = 0; SECItem request; request.data = data; request.len = dataLen; SECItem reply; reply.data = nullptr; reply.len = 0; if (PK11SDR_Encrypt(&keyid, &request, &reply, ctx) != SECSuccess) { return NS_ERROR_FAILURE; } *result = reply.data; *resultLen = reply.len; return NS_OK; } nsresult SecretDecoderRing::Decrypt(unsigned char* data, uint32_t dataLen, /*out*/ unsigned char** result, /*out*/ uint32_t* resultLen) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr ctx = new PipUIContext(); *result = nullptr; *resultLen = 0; /* Find token with SDR key */ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return NS_ERROR_NOT_AVAILABLE; } /* Force authentication */ if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) { return NS_ERROR_NOT_AVAILABLE; } SECItem request; request.data = data; request.len = dataLen; SECItem reply; reply.data = nullptr; reply.len = 0; if (PK11SDR_Decrypt(&request, &reply, ctx) != SECSuccess) { return NS_ERROR_FAILURE; } *result = reply.data; *resultLen = reply.len; return NS_OK; } NS_IMETHODIMP SecretDecoderRing::EncryptString(const char* text, char** _retval) { nsresult rv = NS_OK; unsigned char *encrypted = 0; uint32_t eLen = 0; if (!text || !_retval) { rv = NS_ERROR_INVALID_POINTER; goto loser; } rv = Encrypt((unsigned char *)text, strlen(text), &encrypted, &eLen); if (rv != NS_OK) { goto loser; } rv = Base64Encode(BitwiseCast(encrypted), eLen, _retval); loser: if (encrypted) PORT_Free(encrypted); return rv; } NS_IMETHODIMP SecretDecoderRing::DecryptString(const char* crypt, char** _retval) { nsresult rv = NS_OK; char *r = 0; unsigned char *decoded = 0; uint32_t decodedLen = 0; unsigned char *decrypted = 0; uint32_t decryptedLen = 0; if (!crypt || !_retval) { rv = NS_ERROR_INVALID_POINTER; goto loser; } rv = Base64Decode(crypt, strlen(crypt), BitwiseCast(&decoded), &decodedLen); if (NS_FAILED(rv)) goto loser; rv = Decrypt(decoded, decodedLen, &decrypted, &decryptedLen); if (rv != NS_OK) goto loser; // Convert to NUL-terminated string r = (char *)moz_xmalloc(decryptedLen+1); if (!r) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } memcpy(r, decrypted, decryptedLen); r[decryptedLen] = 0; *_retval = r; r = 0; loser: if (decrypted) PORT_Free(decrypted); if (decoded) free(decoded); return rv; } NS_IMETHODIMP SecretDecoderRing::ChangePassword() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return NS_ERROR_NOT_AVAILABLE; } NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot.get())); nsCOMPtr dialogs; nsresult rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsITokenPasswordDialogs), NS_TOKENPASSWORDSDIALOG_CONTRACTID); if (NS_FAILED(rv)) { return rv; } nsCOMPtr ctx = new PipUIContext(); bool canceled; // Ignored return dialogs->SetPassword(ctx, tokenName.get(), &canceled); } NS_IMETHODIMP SecretDecoderRing::Logout() { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } PK11_LogoutAll(); SSL_ClearSessionCache(); } return NS_OK; } NS_IMETHODIMP SecretDecoderRing::LogoutAndTeardown() { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } PK11_LogoutAll(); SSL_ClearSessionCache(); } rv = nssComponent->LogoutAuthenticatedPK11(); // After we just logged out, we need to prune dead connections to make // sure that all connections that should be stopped, are stopped. See // bug 517584. nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr); return rv; }