From 3bb822a9ab3e450099b31ac358e7117ce1c23db7 Mon Sep 17 00:00:00 2001 From: "kaie%netscape.com" Date: Thu, 11 Apr 2002 05:07:09 +0000 Subject: [PATCH] b=102633 Allow the user to enable additional ciphers, dynamically obtaining a list of ciphers from NSS. Disable support for two ciphers that are no longer in use. r=javi/nelsonb/cotter sr=alecf --- security/manager/ssl/public/nsICipherInfo.idl | 75 ++++ security/manager/ssl/src/nsCipherInfo.cpp | 416 ++++++++++++++++++ security/manager/ssl/src/nsCipherInfo.h | 112 +++++ 3 files changed, 603 insertions(+) create mode 100644 security/manager/ssl/public/nsICipherInfo.idl create mode 100644 security/manager/ssl/src/nsCipherInfo.cpp create mode 100644 security/manager/ssl/src/nsCipherInfo.h diff --git a/security/manager/ssl/public/nsICipherInfo.idl b/security/manager/ssl/public/nsICipherInfo.idl new file mode 100644 index 000000000000..f92a6f1215de --- /dev/null +++ b/security/manager/ssl/public/nsICipherInfo.idl @@ -0,0 +1,75 @@ +/* ***** 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 Communicator. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 "nsISupports.idl" +#include "nsISimpleEnumerator.idl" + +[scriptable, uuid(766d47cb-6d8c-4e71-b6b7-336917629a69)] +interface nsICipherInfoService : nsISupports +{ + nsISimpleEnumerator listCiphers(); +}; + +[scriptable, uuid(028e2b2a-1f0b-43a4-a1a7-365d2d7f35d0)] +interface nsICipherInfo : nsISupports +{ + readonly attribute string longName; + + readonly attribute PRBool isSSL2; + readonly attribute PRBool isFIPS; + readonly attribute PRBool isExportable; + readonly attribute PRBool nonStandard; + readonly attribute string symCipherName; + readonly attribute string authAlgorithmName; + readonly attribute string keaTypeName; + readonly attribute string macAlgorithmName; + readonly attribute PRInt32 effectiveKeyBits; + + readonly attribute string prefString; +}; + +%{C++ + +#define NS_CIPHERINFOSERVICE_CID { /* ec693a6f-0832-49dd-877c-89f6552df5de */ \ + 0xec693a6f, \ + 0x0832, \ + 0x49dd, \ + {0x87, 0x7c, 0x89, 0xf6, 0x55, 0x2d, 0xf5, 0xde} \ + } + +#define NS_CIPHERINFOSERVICE_CONTRACTID "@mozilla.org/security/cipherinfo;1" + +%} diff --git a/security/manager/ssl/src/nsCipherInfo.cpp b/security/manager/ssl/src/nsCipherInfo.cpp new file mode 100644 index 000000000000..72a1485e33e0 --- /dev/null +++ b/security/manager/ssl/src/nsCipherInfo.cpp @@ -0,0 +1,416 @@ +/* ***** 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 Communicator. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 "nsCipherInfo.h" +#include "nsReadableUtils.h" +#include "nsEnumeratorUtils.h" +#include "nsCRT.h" +#include "ssl.h" +#include "sslproto.h" + +nsCiphers* nsCiphers::singleton = nsnull; + +void nsCiphers::InitSingleton() +{ + NS_ASSERTION(!singleton, "trying to instantiate nsCiphers::singleton twice"); + + singleton = new nsCiphers(); +} + +void nsCiphers::DestroySingleton() +{ + delete singleton; + singleton = nsnull; +} + + +struct struct_historical_cipher_pref_strings +{ + PRUint16 cipher_id; + const char *pref_string; +} + const historical_cipher_pref_strings[] = +{ + { SSL_EN_RC4_128_WITH_MD5, "security.ssl2.rc4_128" }, + { SSL_EN_RC2_128_CBC_WITH_MD5, "security.ssl2.rc2_128" }, + { SSL_EN_DES_192_EDE3_CBC_WITH_MD5, "security.ssl2.des_ede3_192" }, + { SSL_EN_DES_64_CBC_WITH_MD5, "security.ssl2.des_64" }, + { SSL_EN_RC4_128_EXPORT40_WITH_MD5, "security.ssl2.rc4_40" }, + { SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, "security.ssl2.rc2_40" }, + { SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, "security.ssl3.fortezza_fortezza_sha" }, + { SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, "security.ssl3.fortezza_rc4_sha" }, + { SSL_RSA_WITH_RC4_128_MD5, "security.ssl3.rsa_rc4_128_md5" }, + { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, "security.ssl3.rsa_fips_des_ede3_sha" }, + { SSL_RSA_WITH_3DES_EDE_CBC_SHA, "security.ssl3.rsa_des_ede3_sha" }, + { SSL_RSA_FIPS_WITH_DES_CBC_SHA, "security.ssl3.rsa_fips_des_sha" }, + { SSL_RSA_WITH_DES_CBC_SHA, "security.ssl3.rsa_des_sha" }, + { TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, "security.ssl3.rsa_1024_rc4_56_sha" }, + { TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, "security.ssl3.rsa_1024_des_cbc_sha" }, + { SSL_RSA_EXPORT_WITH_RC4_40_MD5, "security.ssl3.rsa_rc4_40_md5" }, + { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "security.ssl3.rsa_rc2_40_md5" }, + { SSL_FORTEZZA_DMS_WITH_NULL_SHA, "security.ssl3.fortezza_null_sha" }, + { SSL_RSA_WITH_NULL_MD5, "security.ssl3.rsa_null_md5" } +}; + +const PRUint16 number_of_historical_cipher_pref_strings = + sizeof(historical_cipher_pref_strings) + / sizeof(struct_historical_cipher_pref_strings); + +PRBool isCipherWithHistoricaPrefString(const PRUint16 cipher_id, PRUint16 &out_index_into_array) +{ + for (PRUint16 i = 0; i < number_of_historical_cipher_pref_strings; ++i) + { + if (cipher_id == historical_cipher_pref_strings[i].cipher_id) + { + out_index_into_array = i; + return PR_TRUE; + } + } + + return PR_FALSE; +} + +nsCiphers::nsCiphers() +{ + // count number of wanted ciphers + + mCiphers = new CipherData[SSL_NumImplementedCiphers]; + + if (!mCiphers) + return; + + for (PRUint16 i = 0; i < SSL_NumImplementedCiphers; ++i) + { + CipherData &data = mCiphers[i]; + + data.id = SSL_ImplementedCiphers[i]; + + switch (data.id) + { + case SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA: + case SSL_RSA_FIPS_WITH_DES_CBC_SHA: + // filter out no longer supported ciphers + data.isWanted = PR_FALSE; + break; + + case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA: + case SSL_FORTEZZA_DMS_WITH_RC4_128_SHA: + case SSL_FORTEZZA_DMS_WITH_NULL_SHA: + // filter out fortezza ciphers until we implement proper UI handling + data.isWanted = PR_FALSE; + break; + + default: + data.isWanted = PR_TRUE; + break; + } + + if (!data.isWanted) + continue; + + // In past versions, there was a hardcoded mapping from cipher IDs + // to preference strings. + // In order to be backwards compatible with regards to preferences, + // we need to continue using those strings. + // However, we are now using the available ciphers from NSS dynamically, + // therefore we are using automatic preference string creation for + // any other ciphers. + + + data.isGood = ( + (SECSuccess == SSL_GetCipherSuiteInfo(data.id, &data.info, sizeof(data.info))) + && + (sizeof(data.info) == data.info.length)); + + if (!data.isGood) + { + NS_ASSERTION(0, "unable to get info for implemented cipher"); + continue; + } + + PRUint16 array_index = 0; + if (isCipherWithHistoricaPrefString(data.id, array_index)) + { + data.prefString = historical_cipher_pref_strings[array_index].pref_string; + data.isHeapString = PR_FALSE; + } + else + { + nsCAutoString pref; + pref.Append("security."); + pref.Append( SSL_IS_SSL2_CIPHER(data.info.cipherSuite) ? "ssl2." : "ssl3." ); + pref.Append(data.info.cipherSuiteName); + ToLowerCase(pref); + data.prefString = ToNewCString(pref); + data.isHeapString = PR_TRUE; + } + } +} + +nsCiphers::~nsCiphers() +{ + delete [] mCiphers; +} + +void nsCiphers::SetAllCiphersFromPrefs(nsIPref *ipref) +{ + PRBool enabled; + for (PRUint16 iCipher = 0; iCipher < SSL_NumImplementedCiphers; ++iCipher) + { + if (!singleton->mCiphers[iCipher].isWanted || !singleton->mCiphers[iCipher].isGood) + continue; + + CipherData &cd = singleton->mCiphers[iCipher]; + + ipref->GetBoolPref(cd.prefString, &enabled); + SSL_CipherPrefSetDefault(cd.id, enabled); + } +} + +void nsCiphers::SetCipherFromPref(nsIPref *ipref, const char *prefname) +{ + PRBool enabled; + for (PRUint16 iCipher = 0; iCipher < SSL_NumImplementedCiphers; ++iCipher) + { + if (!singleton->mCiphers[iCipher].isWanted || !singleton->mCiphers[iCipher].isGood) + continue; + + CipherData &cd = singleton->mCiphers[iCipher]; + + // find cipher ID + if (!nsCRT::strcmp(prefname, cd.prefString)) + { + ipref->GetBoolPref(cd.prefString, &enabled); + SSL_CipherPrefSetDefault(cd.id, enabled); + break; + } + } +} + +PRBool nsCiphers::IsImplementedCipherWanted(PRUint16 implemented_cipher_index) +{ + NS_ASSERTION(implemented_cipher_index < SSL_NumImplementedCiphers, + "internal error"); + + return + singleton->mCiphers[implemented_cipher_index].isWanted + && + singleton->mCiphers[implemented_cipher_index].isGood; +} + +NS_IMPL_ISUPPORTS1(nsCipherInfoService, nsICipherInfoService) + +nsCipherInfoService::nsCipherInfoService() +{ + NS_INIT_ISUPPORTS(); +} + +nsCipherInfoService::~nsCipherInfoService() +{ +} + +NS_IMETHODIMP nsCipherInfoService::ListCiphers(nsISimpleEnumerator **_retval) +{ + nsresult rv = NS_OK; + + if (!mArray) + { + rv = NS_NewISupportsArray(getter_AddRefs(mArray)); + if (NS_FAILED(rv)) + return rv; + + for (PRUint16 i = 0; i < SSL_NumImplementedCiphers; ++i) + { + if (!nsCiphers::IsImplementedCipherWanted(i)) + continue; + + nsCipherInfo *nsCI = nsnull; + NS_NEWXPCOM(nsCI, nsCipherInfo); + nsCI->setCipherByImplementedCipherIndex(i); + mArray->AppendElement(NS_STATIC_CAST(nsICipherInfo*, nsCI)); + } + } + + return NS_NewArrayEnumerator(_retval, mArray); +} + + +NS_IMPL_ISUPPORTS1(nsCipherInfo, nsICipherInfo) + +nsCipherInfo::nsCipherInfo() +:mIsInitialized(PR_FALSE) +{ + NS_INIT_ISUPPORTS(); +} + +nsCipherInfo::~nsCipherInfo() +{ +} + +void nsCipherInfo::setCipherByImplementedCipherIndex(PRUint16 i) +{ + NS_ASSERTION(i < SSL_NumImplementedCiphers, "internal error"); + + mIsInitialized = PR_TRUE; + mCipherIndex = i; +} + +NS_IMETHODIMP nsCipherInfo::GetLongName(char * *aLongName) +{ + NS_ENSURE_ARG_POINTER(aLongName); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aLongName = ToNewCString(nsDependentCString(nsCiphers::singleton->mCiphers[mCipherIndex].info.cipherSuiteName)); + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetIsSSL2(PRBool *aIsSSL2) +{ + NS_ENSURE_ARG_POINTER(aIsSSL2); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aIsSSL2 = SSL_IS_SSL2_CIPHER(nsCiphers::singleton->mCiphers[mCipherIndex].info.cipherSuite); + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetIsFIPS(PRBool *aIsFIPS) +{ + NS_ENSURE_ARG_POINTER(aIsFIPS); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aIsFIPS = nsCiphers::singleton->mCiphers[mCipherIndex].info.isFIPS; + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetIsExportable(PRBool *aIsExportable) +{ + NS_ENSURE_ARG_POINTER(aIsExportable); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aIsExportable = nsCiphers::singleton->mCiphers[mCipherIndex].info.isExportable; + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetNonStandard(PRBool *aNonStandard) +{ + NS_ENSURE_ARG_POINTER(aNonStandard); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aNonStandard = nsCiphers::singleton->mCiphers[mCipherIndex].info.nonStandard; + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetSymCipherName(char * *aSymCipherName) +{ + NS_ENSURE_ARG_POINTER(aSymCipherName); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aSymCipherName = ToNewCString(nsDependentCString(nsCiphers::singleton->mCiphers[mCipherIndex].info.symCipherName)); + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetAuthAlgorithmName(char * *aAuthAlgorithmName) +{ + NS_ENSURE_ARG_POINTER(aAuthAlgorithmName); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aAuthAlgorithmName = ToNewCString(nsDependentCString(nsCiphers::singleton->mCiphers[mCipherIndex].info.authAlgorithmName)); + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetKeaTypeName(char * *aKeaTypeName) +{ + NS_ENSURE_ARG_POINTER(aKeaTypeName); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aKeaTypeName = ToNewCString(nsDependentCString(nsCiphers::singleton->mCiphers[mCipherIndex].info.keaTypeName)); + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetMacAlgorithmName(char * *aMacAlgorithmName) +{ + NS_ENSURE_ARG_POINTER(aMacAlgorithmName); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aMacAlgorithmName = ToNewCString(nsDependentCString(nsCiphers::singleton->mCiphers[mCipherIndex].info.macAlgorithmName)); + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetEffectiveKeyBits(PRInt32 *aEffectiveKeyBits) +{ + NS_ENSURE_ARG_POINTER(aEffectiveKeyBits); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + *aEffectiveKeyBits = nsCiphers::singleton->mCiphers[mCipherIndex].info.effectiveKeyBits; + return NS_OK; +} + +NS_IMETHODIMP nsCipherInfo::GetPrefString(char * *aPrefString) +{ + NS_ENSURE_ARG_POINTER(aPrefString); + + if (!mIsInitialized || !nsCiphers::singleton->mCiphers[mCipherIndex].isGood) + return NS_ERROR_NOT_INITIALIZED; + + if (!nsCiphers::singleton->mCiphers[mCipherIndex].isWanted) + { + *aPrefString = nsnull; + return NS_OK; + } + + *aPrefString = ToNewCString(nsDependentCString(nsCiphers::singleton->mCiphers[mCipherIndex].prefString)); + return NS_OK; +} diff --git a/security/manager/ssl/src/nsCipherInfo.h b/security/manager/ssl/src/nsCipherInfo.h new file mode 100644 index 000000000000..4f424738145c --- /dev/null +++ b/security/manager/ssl/src/nsCipherInfo.h @@ -0,0 +1,112 @@ +/* ***** 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 Communicator. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 "nsISupportsArray.h" +#include "nsICipherInfo.h" +#include "nsIPref.h" +#include "nsString.h" +#include "sslt.h" + +class nsCipherInfo; + +class nsCiphers +{ +public: + nsCiphers(); + ~nsCiphers(); + + static void InitSingleton(); + static void DestroySingleton(); + + static void SetAllCiphersFromPrefs(nsIPref *ipref); + static void SetCipherFromPref(nsIPref *ipref, const char *prefname); + + static PRBool IsImplementedCipherWanted(PRUint16 implemented_cipher_index); + +private: + static nsCiphers *singleton; + + struct CipherData { + CipherData() + :id(0), prefString(nsnull), isHeapString(PR_FALSE), isWanted(PR_FALSE), isGood(PR_FALSE) {} + + ~CipherData() { + if (isHeapString) { + delete [] prefString; + } + } + + PRUint16 id; + const char *prefString; + PRPackedBool isHeapString; + PRPackedBool isWanted; + PRPackedBool isGood; + SSLCipherSuiteInfo info; + }; + + struct CipherData *mCiphers; + + friend class nsCipherInfo; +}; + +class nsCipherInfoService : public nsICipherInfoService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICIPHERINFOSERVICE + + nsCipherInfoService(); + virtual ~nsCipherInfoService(); + +private: + nsCOMPtr mArray; +}; + +class nsCipherInfo : public nsICipherInfo +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICIPHERINFO + + nsCipherInfo(); + virtual ~nsCipherInfo(); + + void setCipherByImplementedCipherIndex(PRUint16 i); + +private: + PRBool mIsInitialized; + PRUint16 mCipherIndex; +};