michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "stdlib.h" michael@0: #include "plstr.h" michael@0: #include "plbase64.h" michael@0: michael@0: #include "mozilla/Services.h" michael@0: #include "nsMemory.h" michael@0: #include "nsString.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsITokenPasswordDialogs.h" michael@0: michael@0: #include "nsISecretDecoderRing.h" michael@0: #include "nsCRT.h" michael@0: #include "nsSDR.h" michael@0: #include "nsNSSComponent.h" michael@0: #include "nsNSSShutDown.h" michael@0: #include "ScopedNSSTypes.h" michael@0: michael@0: #include "pk11func.h" michael@0: #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt michael@0: michael@0: #include "ssl.h" // For SSL_ClearSessionCache michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // Standard ISupports implementation michael@0: // NOTE: Should these be the thread-safe versions? michael@0: NS_IMPL_ISUPPORTS(nsSecretDecoderRing, nsISecretDecoderRing, nsISecretDecoderRingConfig) michael@0: michael@0: // nsSecretDecoderRing constructor michael@0: nsSecretDecoderRing::nsSecretDecoderRing() michael@0: { michael@0: // initialize superclass michael@0: } michael@0: michael@0: // nsSecretDecoderRing destructor michael@0: nsSecretDecoderRing::~nsSecretDecoderRing() michael@0: { michael@0: } michael@0: michael@0: /* [noscript] long encrypt (in buffer data, in long dataLen, out buffer result); */ michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: Encrypt(unsigned char * data, int32_t dataLen, unsigned char * *result, int32_t *_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv = NS_OK; michael@0: ScopedPK11SlotInfo slot; michael@0: SECItem keyid; michael@0: SECItem request; michael@0: SECItem reply; michael@0: SECStatus s; michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: michael@0: slot = PK11_GetInternalKeySlot(); michael@0: if (!slot) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } michael@0: michael@0: /* Make sure token is initialized. */ michael@0: rv = setPassword(slot, ctx); michael@0: if (NS_FAILED(rv)) michael@0: goto loser; michael@0: michael@0: /* Force authentication */ michael@0: s = PK11_Authenticate(slot, true, ctx); michael@0: if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; } michael@0: michael@0: /* Use default key id */ michael@0: keyid.data = 0; michael@0: keyid.len = 0; michael@0: request.data = data; michael@0: request.len = dataLen; michael@0: reply.data = 0; michael@0: reply.len = 0; michael@0: s= PK11SDR_Encrypt(&keyid, &request, &reply, ctx); michael@0: if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; } michael@0: michael@0: *result = reply.data; michael@0: *_retval = reply.len; michael@0: michael@0: loser: michael@0: return rv; michael@0: } michael@0: michael@0: /* [noscript] long decrypt (in buffer data, in long dataLen, out buffer result); */ michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: Decrypt(unsigned char * data, int32_t dataLen, unsigned char * *result, int32_t *_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv = NS_OK; michael@0: ScopedPK11SlotInfo slot; michael@0: SECStatus s; michael@0: SECItem request; michael@0: SECItem reply; michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: michael@0: *result = 0; michael@0: *_retval = 0; michael@0: michael@0: /* Find token with SDR key */ michael@0: slot = PK11_GetInternalKeySlot(); michael@0: if (!slot) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } michael@0: michael@0: /* Force authentication */ michael@0: if (PK11_Authenticate(slot, true, ctx) != SECSuccess) michael@0: { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: goto loser; michael@0: } michael@0: michael@0: request.data = data; michael@0: request.len = dataLen; michael@0: reply.data = 0; michael@0: reply.len = 0; michael@0: s = PK11SDR_Decrypt(&request, &reply, ctx); michael@0: if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; } michael@0: michael@0: *result = reply.data; michael@0: *_retval = reply.len; michael@0: michael@0: loser: michael@0: return rv; michael@0: } michael@0: michael@0: /* string encryptString (in string text); */ michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: EncryptString(const char *text, char **_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv = NS_OK; michael@0: unsigned char *encrypted = 0; michael@0: int32_t eLen; michael@0: michael@0: if (!text || !_retval) { michael@0: rv = NS_ERROR_INVALID_POINTER; michael@0: goto loser; michael@0: } michael@0: michael@0: rv = Encrypt((unsigned char *)text, strlen(text), &encrypted, &eLen); michael@0: if (rv != NS_OK) { goto loser; } michael@0: michael@0: rv = encode(encrypted, eLen, _retval); michael@0: michael@0: loser: michael@0: if (encrypted) PORT_Free(encrypted); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* string decryptString (in string crypt); */ michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: DecryptString(const char *crypt, char **_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv = NS_OK; michael@0: char *r = 0; michael@0: unsigned char *decoded = 0; michael@0: int32_t decodedLen; michael@0: unsigned char *decrypted = 0; michael@0: int32_t decryptedLen; michael@0: michael@0: if (!crypt || !_retval) { michael@0: rv = NS_ERROR_INVALID_POINTER; michael@0: goto loser; michael@0: } michael@0: michael@0: rv = decode(crypt, &decoded, &decodedLen); michael@0: if (rv != NS_OK) goto loser; michael@0: michael@0: rv = Decrypt(decoded, decodedLen, &decrypted, &decryptedLen); michael@0: if (rv != NS_OK) goto loser; michael@0: michael@0: // Convert to NUL-terminated string michael@0: r = (char *)nsMemory::Alloc(decryptedLen+1); michael@0: if (!r) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } michael@0: michael@0: memcpy(r, decrypted, decryptedLen); michael@0: r[decryptedLen] = 0; michael@0: michael@0: *_retval = r; michael@0: r = 0; michael@0: michael@0: loser: michael@0: if (decrypted) PORT_Free(decrypted); michael@0: if (decoded) PR_DELETE(decoded); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* void changePassword(); */ michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: ChangePassword() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv; michael@0: ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); michael@0: if (!slot) return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: /* Convert UTF8 token name to UCS2 */ michael@0: NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot)); michael@0: michael@0: /* Get the set password dialog handler imlementation */ michael@0: nsCOMPtr dialogs; michael@0: michael@0: rv = getNSSDialogs(getter_AddRefs(dialogs), michael@0: NS_GET_IID(nsITokenPasswordDialogs), michael@0: NS_TOKENPASSWORDSDIALOG_CONTRACTID); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: bool canceled; michael@0: michael@0: { michael@0: nsPSMUITracker tracker; michael@0: if (tracker.isUIForbidden()) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: else { michael@0: rv = dialogs->SetPassword(ctx, tokenName.get(), &canceled); michael@0: } michael@0: } michael@0: michael@0: /* canceled is ignored */ michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: Logout() michael@0: { michael@0: static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PK11_LogoutAll(); michael@0: SSL_ClearSessionCache(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: LogoutAndTeardown() michael@0: { michael@0: static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: PK11_LogoutAll(); michael@0: SSL_ClearSessionCache(); michael@0: } michael@0: michael@0: rv = nssComponent->LogoutAuthenticatedPK11(); michael@0: michael@0: // After we just logged out, we need to prune dead connections to make michael@0: // sure that all connections that should be stopped, are stopped. See michael@0: // bug 517584. michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) michael@0: os->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* void setWindow(in nsISupports w); */ michael@0: NS_IMETHODIMP nsSecretDecoderRing:: michael@0: SetWindow(nsISupports *w) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Support routines michael@0: michael@0: nsresult nsSecretDecoderRing:: michael@0: encode(const unsigned char *data, int32_t dataLen, char **_retval) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: char *result = PL_Base64Encode((const char *)data, dataLen, nullptr); michael@0: if (!result) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } michael@0: michael@0: *_retval = NS_strdup(result); michael@0: PR_DELETE(result); michael@0: if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } michael@0: michael@0: loser: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsSecretDecoderRing:: michael@0: decode(const char *data, unsigned char **result, int32_t * _retval) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: uint32_t len = strlen(data); michael@0: int adjust = 0; michael@0: michael@0: /* Compute length adjustment */ michael@0: if (data[len-1] == '=') { michael@0: adjust++; michael@0: if (data[len-2] == '=') adjust++; michael@0: } michael@0: michael@0: *result = (unsigned char *)PL_Base64Decode(data, len, nullptr); michael@0: if (!*result) { rv = NS_ERROR_ILLEGAL_VALUE; goto loser; } michael@0: michael@0: *_retval = (len*3)/4 - adjust; michael@0: michael@0: loser: michael@0: return rv; michael@0: }