/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Terry Hayes * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "stdlib.h" #include "plstr.h" #include "plbase64.h" #include "nsMemory.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nsThreadUtils.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIServiceManager.h" #include "nsIWindowWatcher.h" #include "nsIPrompt.h" #include "nsProxiedService.h" #include "nsITokenPasswordDialogs.h" #include "nsISecretDecoderRing.h" #include "nsSDR.h" #include "nsNSSComponent.h" #include "nsNSSShutDown.h" #include "pk11func.h" #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt #include "ssl.h" // For SSL_ClearSessionCache #include "nsNSSCleaner.h" NSSCleanupAutoPtrClass(PK11SlotInfo, PK11_FreeSlot) // // Implementation of an nsIInterfaceRequestor for use // as context for NSS calls // class nsSDRContext : public nsIInterfaceRequestor { public: NS_DECL_ISUPPORTS NS_DECL_NSIINTERFACEREQUESTOR nsSDRContext(); virtual ~nsSDRContext(); }; NS_IMPL_ISUPPORTS1(nsSDRContext, nsIInterfaceRequestor) nsSDRContext::nsSDRContext() { } nsSDRContext::~nsSDRContext() { } /* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */ NS_IMETHODIMP nsSDRContext::GetInterface(const nsIID & uuid, void * *result) { if (uuid.Equals(NS_GET_IID(nsIPrompt))) { nsCOMPtr prompter; nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); if (wwatch) { wwatch->GetNewPrompter(0, getter_AddRefs(prompter)); if (prompter) { nsCOMPtr proxyPrompt; NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIPrompt), prompter, NS_PROXY_SYNC, getter_AddRefs(proxyPrompt)); if (!proxyPrompt) return NS_ERROR_FAILURE; *result = proxyPrompt; NS_ADDREF((nsIPrompt*)*result); } } } else { return NS_ERROR_NO_INTERFACE; } return NS_OK; } // Standard ISupports implementation // NOTE: Should these be the thread-safe versions? NS_IMPL_ISUPPORTS2(nsSecretDecoderRing, nsISecretDecoderRing, nsISecretDecoderRingConfig) // nsSecretDecoderRing constructor nsSecretDecoderRing::nsSecretDecoderRing() { // initialize superclass } // nsSecretDecoderRing destructor nsSecretDecoderRing::~nsSecretDecoderRing() { } /* [noscript] long encrypt (in buffer data, in long dataLen, out buffer result); */ NS_IMETHODIMP nsSecretDecoderRing:: Encrypt(unsigned char * data, PRInt32 dataLen, unsigned char * *result, PRInt32 *_retval) { nsNSSShutDownPreventionLock locker; nsresult rv = NS_OK; PK11SlotInfo *slot = 0; PK11SlotInfoCleaner tmpSlotCleaner(slot); SECItem keyid; SECItem request; SECItem reply; SECStatus s; nsCOMPtr ctx = new nsSDRContext(); slot = PK11_GetInternalKeySlot(); if (!slot) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } /* Make sure token is initialized. */ rv = setPassword(slot, ctx); if (NS_FAILED(rv)) goto loser; s = PK11_Authenticate(slot, PR_TRUE, ctx); if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; } /* Use default key id */ keyid.data = 0; keyid.len = 0; request.data = data; request.len = dataLen; reply.data = 0; reply.len = 0; s= PK11SDR_Encrypt(&keyid, &request, &reply, ctx); if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; } *result = reply.data; *_retval = reply.len; loser: return rv; } /* [noscript] long decrypt (in buffer data, in long dataLen, out buffer result); */ NS_IMETHODIMP nsSecretDecoderRing:: Decrypt(unsigned char * data, PRInt32 dataLen, unsigned char * *result, PRInt32 *_retval) { nsNSSShutDownPreventionLock locker; nsresult rv = NS_OK; PK11SlotInfo *slot = 0; PK11SlotInfoCleaner tmpSlotCleaner(slot); SECStatus s; SECItem request; SECItem reply; nsCOMPtr ctx = new nsSDRContext(); *result = 0; *_retval = 0; /* Find token with SDR key */ slot = PK11_GetInternalKeySlot(); if (!slot) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } /* Force authentication */ if (PK11_Authenticate(slot, PR_TRUE, ctx) != SECSuccess) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } request.data = data; request.len = dataLen; reply.data = 0; reply.len = 0; s = PK11SDR_Decrypt(&request, &reply, ctx); if (s != SECSuccess) { rv = NS_ERROR_FAILURE; goto loser; } *result = reply.data; *_retval = reply.len; loser: return rv; } /* string encryptString (in string text); */ NS_IMETHODIMP nsSecretDecoderRing:: EncryptString(const char *text, char **_retval) { nsNSSShutDownPreventionLock locker; nsresult rv = NS_OK; unsigned char *encrypted = 0; PRInt32 eLen; if (text == nsnull || _retval == nsnull) { rv = NS_ERROR_INVALID_POINTER; goto loser; } rv = Encrypt((unsigned char *)text, PL_strlen(text), &encrypted, &eLen); if (rv != NS_OK) { goto loser; } rv = encode(encrypted, eLen, _retval); loser: if (encrypted) nsMemory::Free(encrypted); return rv; } /* string decryptString (in string crypt); */ NS_IMETHODIMP nsSecretDecoderRing:: DecryptString(const char *crypt, char **_retval) { nsNSSShutDownPreventionLock locker; nsresult rv = NS_OK; char *r = 0; unsigned char *decoded = 0; PRInt32 decodedLen; unsigned char *decrypted = 0; PRInt32 decryptedLen; if (crypt == nsnull || _retval == nsnull) { rv = NS_ERROR_INVALID_POINTER; goto loser; } rv = decode(crypt, &decoded, &decodedLen); if (rv != NS_OK) goto loser; rv = Decrypt(decoded, decodedLen, &decrypted, &decryptedLen); if (rv != NS_OK) goto loser; // Convert to NUL-terminated string r = (char *)nsMemory::Alloc(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) nsMemory::Free(decrypted); if (decoded) nsMemory::Free(decoded); return rv; } /* void changePassword(); */ NS_IMETHODIMP nsSecretDecoderRing:: ChangePassword() { nsNSSShutDownPreventionLock locker; nsresult rv; PK11SlotInfo *slot; slot = PK11_GetInternalKeySlot(); if (!slot) return NS_ERROR_NOT_AVAILABLE; /* Convert UTF8 token name to UCS2 */ NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot)); PK11_FreeSlot(slot); /* Get the set password dialog handler imlementation */ nsCOMPtr dialogs; rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsITokenPasswordDialogs), NS_TOKENPASSWORDSDIALOG_CONTRACTID); if (NS_FAILED(rv)) return rv; nsCOMPtr ctx = new nsSDRContext(); PRBool canceled; { nsPSMUITracker tracker; if (tracker.isUIForbidden()) { rv = NS_ERROR_NOT_AVAILABLE; } else { rv = dialogs->SetPassword(ctx, tokenName.get(), &canceled); } } /* canceled is ignored */ return rv; } static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); NS_IMETHODIMP nsSecretDecoderRing:: Logout() { nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; { nsNSSShutDownPreventionLock locker; PK11_LogoutAll(); SSL_ClearSessionCache(); } return NS_OK; } NS_IMETHODIMP nsSecretDecoderRing:: LogoutAndTeardown() { nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; { nsNSSShutDownPreventionLock locker; PK11_LogoutAll(); SSL_ClearSessionCache(); } return nssComponent->LogoutAuthenticatedPK11(); } /* void setWindow(in nsISupports w); */ NS_IMETHODIMP nsSecretDecoderRing:: SetWindow(nsISupports *w) { return NS_OK; } // Support routines nsresult nsSecretDecoderRing:: encode(const unsigned char *data, PRInt32 dataLen, char **_retval) { nsresult rv = NS_OK; *_retval = PL_Base64Encode((const char *)data, dataLen, NULL); if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } loser: return rv; } nsresult nsSecretDecoderRing:: decode(const char *data, unsigned char **result, PRInt32 * _retval) { nsresult rv = NS_OK; PRUint32 len = PL_strlen(data); int adjust = 0; /* Compute length adjustment */ if (data[len-1] == '=') { adjust++; if (data[len-2] == '=') adjust++; } *result = (unsigned char *)PL_Base64Decode(data, len, NULL); if (!*result) { rv = NS_ERROR_ILLEGAL_VALUE; goto loser; } *_retval = (len*3)/4 - adjust; loser: return rv; }