--- a/security/manager/ssl/ScopedNSSTypes.h
+++ b/security/manager/ssl/ScopedNSSTypes.h
@@ -321,19 +321,25 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLAT
// Emulates MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE, but for UniquePtrs.
#define MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(name, Type, Deleter) \
struct name##DeletePolicy \
{ \
void operator()(Type* aValue) { Deleter(aValue); } \
}; \
typedef UniquePtr<Type, name##DeletePolicy> name;
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificate,
+ CERTCertificate,
+ CERT_DestroyCertificate)
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificatePolicies,
CERTCertificatePolicies,
CERT_DestroyCertificatePoliciesExtension)
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertList,
+ CERTCertList,
+ CERT_DestroyCertList)
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertNicknames,
CERTCertNicknames,
CERT_FreeNicknames)
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTOidSequence,
CERTOidSequence,
CERT_DestroyOidSequence)
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTUserNotice,
CERTUserNotice,
@@ -361,14 +367,15 @@ MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(Un
SECItem,
internal::SECITEM_FreeItem_true)
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey,
SECKEYPublicKey,
SECKEY_DestroyPublicKey)
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule,
SECMODModule,
SECMOD_DestroyModule)
+
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext,
VFYContext,
internal::VFY_DestroyContext_true)
} // namespace mozilla
#endif // mozilla_ScopedNSSTypes_h
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -2,47 +2,47 @@
* 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/. */
// XXX: This must be done prior to including cert.h (directly or indirectly).
// CERT_AddTempCertToPerm is exposed as __CERT_AddTempCertToPerm, but it is
// only exported so PSM can use it for this specific purpose.
#define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm
-#include "nsNSSComponent.h"
#include "nsNSSCertificateDB.h"
#include "CertVerifier.h"
#include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h"
-#include "pkix/pkixtypes.h"
-#include "pkix/Time.h"
-#include "nsNSSComponent.h"
+#include "SharedSSLState.h"
#include "mozilla/Base64.h"
+#include "mozilla/unused.h"
+#include "nsArrayUtils.h"
#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "nsComponentManagerUtils.h"
+#include "nsICertificateDialogs.h"
+#include "nsIFile.h"
+#include "nsIMutableArray.h"
+#include "nsIObserverService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsIPrompt.h"
+#include "nsNSSCertHelper.h"
+#include "nsNSSCertTrust.h"
#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h"
#include "nsNSSHelper.h"
-#include "nsNSSCertHelper.h"
-#include "nsCRT.h"
-#include "nsICertificateDialogs.h"
-#include "nsNSSCertTrust.h"
-#include "nsIFile.h"
+#include "nsNSSShutDown.h"
+#include "nsPK11TokenDB.h"
#include "nsPKCS12Blob.h"
-#include "nsPK11TokenDB.h"
#include "nsReadableUtils.h"
-#include "nsIMutableArray.h"
-#include "nsArrayUtils.h"
-#include "nsNSSShutDown.h"
-#include "nsIPrefService.h"
-#include "nsIPrefBranch.h"
-#include "nsComponentManagerUtils.h"
-#include "nsIPrompt.h"
#include "nsThreadUtils.h"
-#include "nsIObserverService.h"
-#include "SharedSSLState.h"
+#include "pkix/Time.h"
+#include "pkix/pkixtypes.h"
#include "nspr.h"
#include "certdb.h"
#include "secerr.h"
#include "nssb64.h"
#include "secasn1.h"
#include "secder.h"
#include "ssl.h"
@@ -220,31 +220,31 @@ collect_certs(void *arg, SECItem **certs
cert++;
certs++;
}
return (SECSuccess);
}
CERTDERCerts*
-nsNSSCertificateDB::getCertsFromPackage(PLArenaPool *arena, uint8_t *data,
- uint32_t length,
- const nsNSSShutDownPreventionLock &/*proofOfLock*/)
+nsNSSCertificateDB::getCertsFromPackage(const UniquePLArenaPool& arena,
+ uint8_t* data, uint32_t length,
+ const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
- CERTDERCerts *collectArgs =
- (CERTDERCerts *)PORT_ArenaZAlloc(arena, sizeof(CERTDERCerts));
- if (!collectArgs)
+ CERTDERCerts* collectArgs =
+ (CERTDERCerts*)PORT_ArenaZAlloc(arena.get(), sizeof(CERTDERCerts));
+ if (!collectArgs) {
return nullptr;
+ }
- collectArgs->arena = arena;
- SECStatus sec_rv = CERT_DecodeCertPackage(reinterpret_cast<char *>(data),
- length, collect_certs,
- (void *)collectArgs);
- if (sec_rv != SECSuccess)
+ collectArgs->arena = arena.get();
+ if (CERT_DecodeCertPackage(char_ptr_cast(data), length, collect_certs,
+ collectArgs) != SECSuccess) {
return nullptr;
+ }
return collectArgs;
}
nsresult
nsNSSCertificateDB::handleCACertDownload(nsIArray *x509Certs,
nsIInterfaceRequestor *ctx,
const nsNSSShutDownPreventionLock &proofOfLock)
@@ -385,17 +385,17 @@ nsNSSCertificateDB::handleCACertDownload
trust.GetTrust());
if (srv != SECSuccess)
return NS_ERROR_FAILURE;
// Import additional delivered certificates that can be verified.
// build a CertList for filtering
- ScopedCERTCertList certList(CERT_NewCertList());
+ UniqueCERTCertList certList(CERT_NewCertList());
if (!certList) {
return NS_ERROR_FAILURE;
}
// get all remaining certs into temp store
for (uint32_t i=0; i<numCerts; i++) {
if (i == selCertIndex) {
@@ -412,85 +412,150 @@ nsNSSCertificateDB::handleCACertDownload
free(der.data);
der.data = nullptr;
der.len = 0;
if (!tmpCert2) {
NS_ERROR("Couldn't create temp cert from DER blob");
continue; // Let's try to import the rest of 'em
}
-
+
CERT_AddCertToListTail(certList.get(), tmpCert2);
}
- return ImportValidCACertsInList(certList.get(), ctx, proofOfLock);
+ return ImportValidCACertsInList(certList, ctx, proofOfLock);
}
-NS_IMETHODIMP
-nsNSSCertificateDB::ImportCertificates(uint8_t * data, uint32_t length,
- uint32_t type,
- nsIInterfaceRequestor *ctx)
-
+NS_IMETHODIMP
+nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
+ uint32_t type,
+ nsIInterfaceRequestor* ctx)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
- nsresult nsrv;
-
- PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if (!arena)
- return NS_ERROR_OUT_OF_MEMORY;
-
- CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
- if (!certCollection) {
- PORT_FreeArena(arena, false);
+ // We currently only handle CA certificates.
+ if (type != nsIX509Cert::CA_CERT) {
return NS_ERROR_FAILURE;
}
- nsCOMPtr<nsIMutableArray> array =
- do_CreateInstance(NS_ARRAY_CONTRACTID, &nsrv);
- if (NS_FAILED(nsrv)) {
- PORT_FreeArena(arena, false);
- return nsrv;
+
+ UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ CERTDERCerts* certCollection = getCertsFromPackage(arena, data, length,
+ locker);
+ if (!certCollection) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
}
// Now let's create some certs to work with
- nsCOMPtr<nsIX509Cert> x509Cert;
- nsNSSCertificate *nssCert;
- SECItem *currItem;
- for (int i=0; i<certCollection->numcerts; i++) {
- currItem = &certCollection->rawCerts[i];
- nssCert = nsNSSCertificate::ConstructFromDER((char*)currItem->data, currItem->len);
- if (!nssCert)
- return NS_ERROR_FAILURE;
- x509Cert = do_QueryInterface((nsIX509Cert*)nssCert);
- array->AppendElement(x509Cert, false);
+ for (int i = 0; i < certCollection->numcerts; i++) {
+ SECItem* currItem = &certCollection->rawCerts[i];
+ nsCOMPtr<nsIX509Cert> cert =
+ nsNSSCertificate::ConstructFromDER(reinterpret_cast<char*>(currItem->data),
+ currItem->len);
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = array->AppendElement(cert, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
}
- switch (type) {
- case nsIX509Cert::CA_CERT:
- nsrv = handleCACertDownload(array, ctx, locker);
- break;
- default:
- // We only deal with import CA certs in this method currently.
- nsrv = NS_ERROR_FAILURE;
- break;
- }
- PORT_FreeArena(arena, false);
- return nsrv;
+
+ return handleCACertDownload(array, ctx, locker);
}
-static
-SECStatus
-ImportCertsIntoPermanentStorage(
- const ScopedCERTCertList& certChain,
- const SECCertUsage usage, const PRBool caOnly)
+/**
+ * Filters an array of certs by usage and imports them into temporary storage.
+ *
+ * @param numcerts
+ * Size of the |certs| array.
+ * @param certs
+ * Pointer to array of certs to import.
+ * @param usage
+ * Usage the certs should be filtered on.
+ * @param caOnly
+ * Whether to import only CA certs.
+ * @param filteredCerts
+ * List of certs that weren't filtered out and were successfully imported.
+ */
+static nsresult
+ImportCertsIntoTempStorage(int numcerts, SECItem* certs,
+ const SECCertUsage usage, const bool caOnly,
+ const nsNSSShutDownPreventionLock& /*proofOfLock*/,
+ /*out*/ const UniqueCERTCertList& filteredCerts)
{
- CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
+ NS_ENSURE_ARG_MIN(numcerts, 1);
+ NS_ENSURE_ARG_POINTER(certs);
+ NS_ENSURE_ARG_POINTER(filteredCerts.get());
+
+ // CERT_ImportCerts() expects an array of *pointers* to SECItems, so we have
+ // to convert |certs| to such a format first.
+ SECItem** ptrArray =
+ static_cast<SECItem**>(PORT_Alloc(sizeof(SECItem*) * numcerts));
+ if (!ptrArray) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (int i = 0; i < numcerts; i++) {
+ ptrArray[i] = &certs[i];
+ }
+
+ CERTCertificate** importedCerts = nullptr;
+ SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage,
+ numcerts, ptrArray, &importedCerts, false,
+ caOnly, nullptr);
+ PORT_Free(ptrArray);
+ ptrArray = nullptr;
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ for (int i = 0; i < numcerts; i++) {
+ if (!importedCerts[i]) {
+ continue;
+ }
+
+ UniqueCERTCertificate cert(CERT_DupCertificate(importedCerts[i]));
+ if (!cert) {
+ continue;
+ }
+
+ if (CERT_AddCertToListTail(filteredCerts.get(), cert.get()) == SECSuccess) {
+ Unused << cert.release();
+ }
+ }
+
+ CERT_DestroyCertArray(importedCerts, numcerts);
+
+ // CERT_ImportCerts() ignores its |usage| parameter, so we have to manually
+ // filter out unwanted certs.
+ if (CERT_FilterCertListByUsage(filteredCerts.get(), usage, caOnly)
+ != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static SECStatus
+ImportCertsIntoPermanentStorage(const ScopedCERTCertList& certChain,
+ const SECCertUsage usage, const bool caOnly)
+{
int chainLen = 0;
for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
!CERT_LIST_END(chainNode, certChain);
chainNode = CERT_LIST_NEXT(chainNode)) {
chainLen++;
}
SECItem **rawArray;
@@ -500,207 +565,125 @@ ImportCertsIntoPermanentStorage(
}
int i = 0;
for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
!CERT_LIST_END(chainNode, certChain);
chainNode = CERT_LIST_NEXT(chainNode), i++) {
rawArray[i] = &chainNode->cert->derCert;
}
- SECStatus srv = CERT_ImportCerts(certdb, usage, chainLen, rawArray,
- nullptr, true, caOnly, nullptr);
+ SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage, chainLen,
+ rawArray, nullptr, true, caOnly, nullptr);
PORT_Free(rawArray);
return srv;
-}
-
+}
NS_IMETHODIMP
-nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length,
- nsIInterfaceRequestor *ctx)
-
+nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
+ nsIInterfaceRequestor* ctx)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
- SECStatus srv = SECFailure;
- nsresult nsrv = NS_OK;
- CERTCertDBHandle *certdb;
- CERTCertificate **certArray = nullptr;
- ScopedCERTCertList certList;
- CERTCertListNode *node;
- SECItem **rawArray;
- int numcerts;
- int i;
-
- PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if (!arena)
+ UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
return NS_ERROR_OUT_OF_MEMORY;
+ }
CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
if (!certCollection) {
- PORT_FreeArena(arena, false);
+ return NS_ERROR_FAILURE;
+ }
+
+ UniqueCERTCertList filteredCerts(CERT_NewCertList());
+ if (!filteredCerts) {
return NS_ERROR_FAILURE;
}
+ nsresult rv = ImportCertsIntoTempStorage(certCollection->numcerts,
+ certCollection->rawCerts,
+ certUsageEmailRecipient,
+ false, locker, filteredCerts);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
- NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
-
- certdb = CERT_GetDefaultCertDB();
-
- numcerts = certCollection->numcerts;
-
- rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts);
- if ( !rawArray ) {
- nsrv = NS_ERROR_FAILURE;
- goto loser;
- }
-
- for (i=0; i < numcerts; i++) {
- rawArray[i] = &certCollection->rawCerts[i];
+ if (!certVerifier) {
+ return NS_ERROR_UNEXPECTED;
}
- srv = CERT_ImportCerts(certdb, certUsageEmailRecipient, numcerts, rawArray,
- &certArray, false, false, nullptr);
-
- PORT_Free(rawArray);
- rawArray = nullptr;
-
- if (srv != SECSuccess) {
- nsrv = NS_ERROR_FAILURE;
- goto loser;
- }
-
- // build a CertList for filtering
- certList = CERT_NewCertList();
- if (!certList) {
- nsrv = NS_ERROR_FAILURE;
- goto loser;
- }
- for (i=0; i < numcerts; i++) {
- CERTCertificate *cert = certArray[i];
- if (cert)
- cert = CERT_DupCertificate(cert);
- if (cert)
- CERT_AddCertToListTail(certList.get(), cert);
- }
-
- /* go down the remaining list of certs and verify that they have
- * valid chains, then import them.
- */
-
- for (node = CERT_LIST_HEAD(certList);
- !CERT_LIST_END(node,certList);
+ // Iterate through the filtered cert list and import verified certs into
+ // permanent storage.
+ // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004.
+ for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get());
+ !CERT_LIST_END(node, filteredCerts.get());
node = CERT_LIST_NEXT(node)) {
-
if (!node->cert) {
continue;
}
ScopedCERTCertList certChain;
-
- SECStatus rv = certVerifier->VerifyCert(node->cert,
- certificateUsageEmailRecipient,
- mozilla::pkix::Now(), ctx,
- nullptr, certChain);
-
- if (rv != SECSuccess) {
+ SECStatus srv = certVerifier->VerifyCert(node->cert,
+ certificateUsageEmailRecipient,
+ mozilla::pkix::Now(), ctx,
+ nullptr, certChain);
+ if (srv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
continue;
}
- rv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient,
- false);
- if (rv != SECSuccess) {
- goto loser;
- }
+ srv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient,
+ false);
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
CERT_SaveSMimeProfile(node->cert, nullptr, nullptr);
-
}
-loser:
- if (certArray) {
- CERT_DestroyCertArray(certArray, numcerts);
- }
- if (arena)
- PORT_FreeArena(arena, true);
- return nsrv;
+ return NS_OK;
}
nsresult
-nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx, const nsNSSShutDownPreventionLock &proofOfLock)
+nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem* caCerts,
+ nsIInterfaceRequestor* ctx,
+ const nsNSSShutDownPreventionLock& proofOfLock)
{
- ScopedCERTCertList certList;
- SECItem **rawArray;
-
- // build a CertList for filtering
- certList = CERT_NewCertList();
- if (!certList) {
- return NS_ERROR_FAILURE;
- }
-
- // get all certs into temp store
- SECStatus srv = SECFailure;
- CERTCertificate **certArray = nullptr;
-
- rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numCACerts);
- if ( !rawArray ) {
+ UniqueCERTCertList filteredCerts(CERT_NewCertList());
+ if (!filteredCerts) {
return NS_ERROR_FAILURE;
}
- for (int i=0; i < numCACerts; i++) {
- rawArray[i] = &CACerts[i];
+ nsresult rv = ImportCertsIntoTempStorage(numCACerts, caCerts, certUsageAnyCA,
+ true, proofOfLock, filteredCerts);
+ if (NS_FAILED(rv)) {
+ return rv;
}
- srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageAnyCA, numCACerts, rawArray,
- &certArray, false, true, nullptr);
-
- PORT_Free(rawArray);
- rawArray = nullptr;
-
- if (srv != SECSuccess) {
- return NS_ERROR_FAILURE;
- }
-
- for (int i2=0; i2 < numCACerts; i2++) {
- CERTCertificate *cacert = certArray[i2];
- if (cacert)
- cacert = CERT_DupCertificate(cacert);
- if (cacert)
- CERT_AddCertToListTail(certList, cacert);
- }
-
- CERT_DestroyCertArray(certArray, numCACerts);
-
- return ImportValidCACertsInList(certList, ctx, proofOfLock);
+ return ImportValidCACertsInList(filteredCerts, ctx, proofOfLock);
}
nsresult
-nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx,
- const nsNSSShutDownPreventionLock &proofOfLock)
+nsNSSCertificateDB::ImportValidCACertsInList(const UniqueCERTCertList& filteredCerts,
+ nsIInterfaceRequestor* ctx,
+ const nsNSSShutDownPreventionLock& proofOfLock)
{
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
- if (!certVerifier)
+ if (!certVerifier) {
return NS_ERROR_UNEXPECTED;
-
- /* filter out the certs we don't want */
- SECStatus srv = CERT_FilterCertListByUsage(certList, certUsageAnyCA, true);
- if (srv != SECSuccess) {
- return NS_ERROR_FAILURE;
}
- /* go down the remaining list of certs and verify that they have
- * valid chains, if yes, then import.
- */
- CERTCertListNode *node;
-
- for (node = CERT_LIST_HEAD(certList);
- !CERT_LIST_END(node,certList);
+ // Iterate through the filtered cert list and import verified certs into
+ // permanent storage.
+ // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004.
+ for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get());
+ !CERT_LIST_END(node, filteredCerts.get());
node = CERT_LIST_NEXT(node)) {
ScopedCERTCertList certChain;
SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageVerifyCA,
mozilla::pkix::Now(), ctx,
nullptr, certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
@@ -708,17 +691,17 @@ nsNSSCertificateDB::ImportValidCACertsIn
continue;
}
rv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
if (rv != SECSuccess) {
return NS_ERROR_FAILURE;
}
}
-
+
return NS_OK;
}
void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor *ctx,
const char *stringID,
nsIX509Cert *certToShow,
const nsNSSShutDownPreventionLock &/*proofOfLock*/)
{
@@ -747,101 +730,82 @@ void nsNSSCertificateDB::DisplayCertific
if (!prompt) {
return;
}
prompt->Alert(nullptr, tmpMessage.get());
}
}
-
-NS_IMETHODIMP
-nsNSSCertificateDB::ImportUserCertificate(uint8_t *data, uint32_t length, nsIInterfaceRequestor *ctx)
+NS_IMETHODIMP
+nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
+ nsIInterfaceRequestor* ctx)
{
if (!NS_IsMainThread()) {
NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread");
return NS_ERROR_NOT_SAME_THREAD;
}
-
+
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
- ScopedPK11SlotInfo slot;
- nsAutoCString nickname;
- nsresult rv = NS_ERROR_FAILURE;
- int numCACerts;
- SECItem *CACerts;
- CERTDERCerts * collectArgs;
- PLArenaPool *arena;
- ScopedCERTCertificate cert;
-
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
- goto loser;
+ return NS_ERROR_OUT_OF_MEMORY;
}
- collectArgs = getCertsFromPackage(arena, data, length, locker);
+ CERTDERCerts* collectArgs = getCertsFromPackage(arena, data, length, locker);
if (!collectArgs) {
- goto loser;
+ return NS_ERROR_FAILURE;
}
- cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,
- nullptr, false, true);
+ UniqueCERTCertificate cert(
+ CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,
+ nullptr, false, true));
if (!cert) {
- goto loser;
+ return NS_ERROR_FAILURE;
}
- slot = PK11_KeyForCertExists(cert.get(), nullptr, ctx);
+ UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
if (!slot) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker);
- goto loser;
+ return NS_ERROR_FAILURE;
}
slot = nullptr;
/* pick a nickname for the cert */
+ nsAutoCString nickname;
if (cert->nickname) {
- /* sigh, we need a call to look up other certs with this subject and
- * identify nicknames from them. We can no longer walk down internal
- * database structures rjr */
- nickname = cert->nickname;
- }
- else {
+ nickname = cert->nickname;
+ } else {
get_default_nickname(cert.get(), ctx, nickname, locker);
}
/* user wants to import the cert */
- {
- char *cast_const_away = const_cast<char*>(nickname.get());
- slot = PK11_ImportCertForKey(cert.get(), cast_const_away, ctx);
- }
+ slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
if (!slot) {
- goto loser;
+ return NS_ERROR_FAILURE;
}
slot = nullptr;
{
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker);
}
- rv = NS_OK;
- numCACerts = collectArgs->numcerts - 1;
+ int numCACerts = collectArgs->numcerts - 1;
if (numCACerts) {
- CACerts = collectArgs->rawCerts+1;
- rv = ImportValidCACerts(numCACerts, CACerts, ctx, locker);
+ SECItem* caCerts = collectArgs->rawCerts + 1;
+ return ImportValidCACerts(numCACerts, caCerts, ctx, locker);
}
-
-loser:
- if (arena) {
- PORT_FreeArena(arena, false);
- }
- return rv;
+
+ return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert)
{
NS_ENSURE_ARG_POINTER(aCert);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
--- a/security/manager/ssl/nsNSSCertificateDB.h
+++ b/security/manager/ssl/nsNSSCertificateDB.h
@@ -6,16 +6,17 @@
#define nsNSSCertificateDB_h
#include "certt.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsIX509CertDB.h"
#include "nsNSSShutDown.h"
+#include "ScopedNSSTypes.h"
class nsCString;
class nsIArray;
class nsNSSCertificateDB final : public nsIX509CertDB
, public nsNSSShutDownObject
{
@@ -35,26 +36,27 @@ public:
const nsNSSShutDownPreventionLock &proofOfLock);
protected:
virtual ~nsNSSCertificateDB();
private:
static nsresult
- ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx,
- const nsNSSShutDownPreventionLock &proofOfLock);
+ ImportValidCACertsInList(const mozilla::UniqueCERTCertList& filteredCerts,
+ nsIInterfaceRequestor* ctx,
+ const nsNSSShutDownPreventionLock& proofOfLock);
static void DisplayCertificateAlert(nsIInterfaceRequestor *ctx,
const char *stringID, nsIX509Cert *certToShow,
const nsNSSShutDownPreventionLock &proofOfLock);
- CERTDERCerts *getCertsFromPackage(PLArenaPool *arena, uint8_t *data,
- uint32_t length,
- const nsNSSShutDownPreventionLock &proofOfLock);
+ CERTDERCerts* getCertsFromPackage(const mozilla::UniquePLArenaPool& arena,
+ uint8_t* data, uint32_t length,
+ const nsNSSShutDownPreventionLock& proofOfLock);
nsresult handleCACertDownload(nsIArray *x509Certs,
nsIInterfaceRequestor *ctx,
const nsNSSShutDownPreventionLock &proofOfLock);
// We don't own any NSS objects here, so no need to clean up
virtual void virtualDestroyNSSReference() override { };
};
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -700,8 +700,16 @@ function attempt_adding_cert_override(aH
// subsequent connection succeeding (i.e. the same error code is encountered).
// The idea here is that for HSTS hosts or hosts with key pins, no error is
// overridable, even if an entry is added to the override service.
function add_prevented_cert_override_test(aHost, aExpectedBits, aExpectedError) {
add_connection_test(aHost, aExpectedError, null,
attempt_adding_cert_override.bind(this, aHost, aExpectedBits));
add_connection_test(aHost, aExpectedError);
}
+
+function loginToDBWithDefaultPassword() {
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB);
+ let token = tokenDB.getInternalKeyToken();
+ token.initPassword("");
+ token.login(/*force*/ false);
+}
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -14,16 +14,17 @@ TEST_DIRS += [
'ocsp_certs',
'test_cert_eku',
'test_cert_embedded_null',
'test_cert_keyUsage',
'test_cert_sha1',
'test_cert_signatures',
'test_cert_trust',
'test_cert_version',
+ 'test_certDB_import',
'test_ev_certs',
'test_getchain',
'test_intermediate_basic_usage_constraints',
'test_keysize',
'test_keysize_ev',
'test_name_constraints',
'test_ocsp_fetch_method',
'test_ocsp_url',
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import.js
@@ -0,0 +1,118 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the various nsIX509CertDB import methods.
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+
+const CA_CERT_COMMON_NAME = "importedCA";
+const TEST_EMAIL_ADDRESS = "test@example.com";
+
+let gCACertImportDialogCount = 0;
+
+// Mock implementation of nsICertificateDialogs.
+const gCertificateDialogs = {
+ confirmDownloadCACert: (ctx, cert, trust) => {
+ gCACertImportDialogCount++;
+ equal(cert.commonName, CA_CERT_COMMON_NAME,
+ "CA cert to import should have the correct CN");
+ trust.value = Ci.nsIX509CertDB.TRUSTED_EMAIL;
+ return true;
+ },
+ setPKCS12FilePassword: (ctx, password) => {
+ // This is only relevant to exporting.
+ ok(false, "setPKCS12FilePassword() should not have been called");
+ },
+ getPKCS12FilePassword: (ctx, password) => {
+ // We don't test anything that calls this method yet.
+ ok(false, "getPKCS12FilePassword() should not have been called");
+ },
+ viewCert: (ctx, cert) => {
+ // This shouldn't be called for import methods.
+ ok(false, "viewCert() should not have been called");
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICertificateDialogs])
+};
+
+// Implements nsIInterfaceRequestor. Mostly serves to mock nsIPrompt.
+const gInterfaceRequestor = {
+ alert: (title, text) => {
+ // We don't test anything that calls this method yet.
+ ok(false, `alert() should not have been called: ${text}`);
+ },
+
+ getInterface: iid => {
+ if (iid.equals(Ci.nsIPrompt)) {
+ return this;
+ }
+
+ throw new Error(Cr.NS_ERROR_NO_INTERFACE);
+ }
+};
+
+function getCertAsByteArray(certPath) {
+ let certFile = do_get_file(certPath, false);
+ let certBytes = readFile(certFile);
+
+ let byteArray = [];
+ for (let i = 0; i < certBytes.length; i++) {
+ byteArray.push(certBytes.charCodeAt(i));
+ }
+
+ return byteArray;
+}
+
+function testImportCACert() {
+ // Sanity check the CA cert is missing.
+ throws(() => gCertDB.findCertByNickname(CA_CERT_COMMON_NAME),
+ /NS_ERROR_FAILURE/,
+ "CA cert should not be in the database before import");
+
+ // Import and check for success.
+ let caArray = getCertAsByteArray("test_certDB_import/importedCA.pem");
+ gCertDB.importCertificates(caArray, caArray.length, Ci.nsIX509Cert.CA_CERT,
+ gInterfaceRequestor);
+ equal(gCACertImportDialogCount, 1,
+ "Confirmation dialog for the CA cert should only be shown once");
+
+ let caCert = gCertDB.findCertByNickname(CA_CERT_COMMON_NAME);
+ notEqual(caCert, null, "CA cert should now be found in the database");
+ ok(gCertDB.isCertTrusted(caCert, Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL),
+ "CA cert should be trusted for e-mail");
+}
+
+function run_test() {
+ // We have to set a password and login before we attempt to import anything.
+ // In particular, the SQL NSS DB requires the user to be authenticated to set
+ // certificate trust settings, which we do when we import CA certs.
+ loginToDBWithDefaultPassword();
+
+ let certificateDialogsCID =
+ MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1",
+ gCertificateDialogs);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(certificateDialogsCID);
+ });
+
+ // Sanity check the e-mail cert is missing.
+ throws(() => gCertDB.findCertByEmailAddress(TEST_EMAIL_ADDRESS),
+ /NS_ERROR_FAILURE/,
+ "E-mail cert should not be in the database before import");
+
+ // Import the CA cert so that the e-mail import succeeds.
+ testImportCACert();
+
+ // Import the e-mail cert and check for success.
+ let emailArray = getCertAsByteArray("test_certDB_import/emailEE.pem");
+ gCertDB.importEmailCertificate(emailArray, emailArray.length,
+ gInterfaceRequestor);
+ notEqual(gCertDB.findCertByEmailAddress(TEST_EMAIL_ADDRESS), null,
+ "E-mail cert should now be found in the database");
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaygAwIBAgIUP71Va37c54kapFAS+PSgTI+oHhQwCwYJKoZIhvcNAQEL
+MBUxEzARBgNVBAMMCmltcG9ydGVkQ0EwIhgPMjAxNDExMjcwMDAwMDBaGA8yMDE3
+MDIwNDAwMDAwMFowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAATALBgkqhkiG9w0BAQsDggEBAEJPROEDLfrf3huYWfh6ejaAV2DWQjXK7Bj5
+zvprH1yx2j9CnlLf0HRknTRSsfgb4JWOc3gKtiBLk2WjI6etp4JiRdn+fadA5LXV
+a0KfJiztdsbjzFADvRUy43z2aX16mcT0qy+FXMyE+50AHV79y2YlXDYsQHuteJDy
+7S3lVU/weE/tSNC9DatdcSP8vCecgzTiDu20qbYMbBXq+2zFRCk+/3Y58k3ps66H
+z5IvQC8ddDwUbn+bD1ULeR0WIap9Zt+nap5GiCZVpekdWfSNX/k9QZjlt5aC671L
+Wq/5qSnOS1Y2onCnZ/ZP68UBsU9fSAX7jbiryCtPdMJRSnGFzEk=
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem.certspec
@@ -0,0 +1,2 @@
+issuer:importedCA
+subject:/emailAddress=test@example.com
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyDCCAbKgAwIBAgIURx1jxEZp5WQaQO8kyUnTTK/e/E8wCwYJKoZIhvcNAQEL
+MBUxEzARBgNVBAMMCmltcG9ydGVkQ0EwIhgPMjAxNDExMjcwMDAwMDBaGA8yMDE3
+MDIwNDAwMDAwMFowFTETMBEGA1UEAwwKaW1wb3J0ZWRDQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMQMA4wDAYD
+VR0TBAUwAwEB/zALBgkqhkiG9w0BAQsDggEBACVHxcZWyN5G1GosA3lWVbsKCSmS
+JJg8z7fPFoHfQncSyuMQwI9D01a2XYjNjHB1QUz9N4MlV3E4ieTWSwtSeRcHxV/g
+1tFCEbzKZLa3rCdyozyj+0ReLVhkXr+XBb731WAOvcybLuzAS6dJvoXq7TSZckbq
+hLhXDRrWlZVGzPAn6oaaSAn3dcD8BTDwzyTZ8HaDzyUnwottzl8Ik29G6o3cqkAQ
+SDscNpP+DgZXZJA6HmpT6jCad4E/Y103ExipG70bhT0D2khT/0f06YuVbwsZqAc5
+mSm90y3NLjTORW1DQ3A8M86auq7O+oLMh6lLbtR/x6NMxMDHzmLWnqXOFh4=
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem.certspec
@@ -0,0 +1,3 @@
+issuer:importedCA
+subject:importedCA
+extension:basicConstraints:cA,
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See Bug 1256495.
+#test_certificates = (
+# 'emailEE.pem',
+# 'importedCA.pem',
+#)
+#
+#for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
--- a/security/manager/ssl/tests/unit/test_sdr.js
+++ b/security/manager/ssl/tests/unit/test_sdr.js
@@ -24,21 +24,17 @@ const gTokenPasswordDialogs = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsITokenPasswordDialogs])
};
function run_test() {
// We have to set a password and login before we attempt to encrypt anything.
// In particular, failing to do so will cause the Encrypt() implementation to
// pop up a dialog asking for a password to be set. This won't work in the
// xpcshell environment and will lead to an assertion.
- let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
- .getService(Ci.nsIPK11TokenDB);
- let token = tokenDB.getInternalKeyToken();
- token.initPassword("");
- token.login(/*force*/ false);
+ loginToDBWithDefaultPassword();
let sdr = Cc["@mozilla.org/security/sdr;1"]
.getService(Ci.nsISecretDecoderRing);
// Test valid inputs for encryptString() and decryptString().
let inputs = [
"",
"foo",
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -6,16 +6,17 @@ support-files =
ocsp_common/**
test_cert_eku/**
test_cert_embedded_null/**
test_cert_keyUsage/**
test_cert_sha1/**
test_cert_signatures/**
test_cert_trust/**
test_cert_version/**
+ test_certDB_import/**
test_certviewer_invalid_oids/**
test_ev_certs/**
test_getchain/**
test_intermediate_basic_usage_constraints/**
test_keysize/**
test_keysize_ev/**
test_name_constraints/**
test_ocsp_fetch_method/**
@@ -40,16 +41,17 @@ run-sequentially = hardcoded ports
[test_cert_overrides.js]
run-sequentially = hardcoded ports
[test_cert_override_bits_mismatches.js]
run-sequentially = hardcoded ports
[test_cert_sha1.js]
[test_cert_signatures.js]
[test_cert_trust.js]
[test_cert_version.js]
+[test_certDB_import.js]
[test_certviewer_invalid_oids.js]
skip-if = toolkit == 'android' || buildapp == 'b2g'
[test_constructX509FromBase64.js]
[test_datasignatureverifier.js]
[test_ev_certs.js]
run-sequentially = hardcoded ports
[test_getchain.js]
[test_hash_algorithms.js]