--- a/security/manager/pki/resources/content/certManager.js
+++ b/security/manager/pki/resources/content/certManager.js
@@ -4,25 +4,31 @@
/* import-globals-from pippki.js */
"use strict";
const nsIFilePicker = Ci.nsIFilePicker;
const nsFilePicker = "@mozilla.org/filepicker;1";
const nsIX509CertDB = Ci.nsIX509CertDB;
const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
const nsIX509Cert = Ci.nsIX509Cert;
+const nsStringBundle = "@mozilla.org/intl/stringbundle;1";
+const nsIStringBundleService = Ci.nsIStringBundleService;
const nsICertTree = Ci.nsICertTree;
const nsCertTree = "@mozilla.org/security/nsCertTree;1";
const gCertFileTypes = "*.p7b; *.crt; *.cert; *.cer; *.pem; *.der";
var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
var key;
+var certdialogs = Cc[nsCertificateDialogs].getService(nsICertificateDialogs);
+var strbundle = Cc[nsStringBundle].getService(nsIStringBundleService)
+ .createBundle("chrome://pipnss/locale/pipnss.properties");
+
/**
* List of certs currently selected in the active tab.
* @type nsIX509Cert[]
*/
var selected_certs = [];
var selected_tree_items = [];
var selected_index = [];
var certdb;
@@ -200,16 +206,48 @@ function nothingOrContainerSelected(cert
return true;
}
}
}
return false;
}
+function promptError(aErrorCode) {
+ if (aErrorCode != Ci.nsIX509CertDB.Success) {
+ let msgName = "PKCS12UnknownErr";
+ switch (aErrorCode) {
+ case Ci.nsIX509CertDB.ERROR_PKCS12_NOSMARTCARD_EXPORT:
+ msgName = "PKCS12InfoNoSmartcardBackup";
+ break;
+ case Ci.nsIX509CertDB.ERROR_PKCS12_RESTORE_FAILED:
+ msgName = "PKCS12UnknownErrRestore";
+ break;
+ case Ci.nsIX509CertDB.ERROR_PKCS12_BACKUP_FAILED:
+ msgName = "PKCS12UnknownErrBackup";
+ break;
+ case Ci.nsIX509CertDB.ERROR_PKCS12_CERT_COLLISION:
+ case Ci.nsIX509CertDB.ERROR_PKCS12_DUPLICATE_DATA:
+ msgName = "PKCS12DupData";
+ break;
+ case Ci.nsIX509CertDB.ERROR_BAD_PASSWORD:
+ msgName = "PK11BadPassword";
+ break;
+ case Ci.nsIX509CertDB.ERROR_DECODE_ERROR:
+ msgName = "PKCS12DecodeErr";
+ break;
+ default:
+ break;
+ }
+ let message = strbundle.GetStringFromName(msgName);
+ let prompter = Services.ww.getNewPrompter(window);
+ prompter.alert(null, message);
+ }
+}
+
/**
* Enables or disables buttons corresponding to a cert tree depending on what
* is selected in the cert tree.
*
* @param {nsICertTree} certTree
* @param {Array} idList A list of string identifiers for button elements to
* enable or disable.
*/
@@ -271,17 +309,22 @@ function backupCerts() {
bundle.getString("chooseP12BackupFileDialog"),
nsIFilePicker.modeSave);
fp.appendFilter(bundle.getString("file_browse_PKCS12_spec"),
"*.p12");
fp.appendFilters(nsIFilePicker.filterAll);
fp.defaultExtension = "p12";
fp.open(rv => {
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
- certdb.exportPKCS12File(fp.file, selected_certs.length, selected_certs);
+ let password = {};
+ if (certdialogs.setPKCS12FilePassword(window, password)) {
+ let errorCode = certdb.exportPKCS12File(fp.file, selected_certs.length,
+ selected_certs, password.value);
+ promptError(errorCode);
+ }
}
});
}
function backupAllCerts() {
// Select all rows, then call doBackup()
userTreeView.selection.selectAll();
backupCerts();
@@ -337,17 +380,28 @@ function restoreCerts() {
let interfaceRequestor = {
getInterface() {
return prompter;
}
};
certdb.importUserCertificate(dataArray, dataArray.length, interfaceRequestor);
} else {
// Otherwise, assume it's a PKCS12 file and import it that way.
- certdb.importPKCS12File(fp.file);
+ let password = {};
+ let errorCode = Ci.nsIX509CertDB.ERROR_BAD_PASSWORD;
+ while (errorCode == Ci.nsIX509CertDB.ERROR_BAD_PASSWORD &&
+ certdialogs.getPKCS12FilePassword(window, password)) {
+ errorCode = certdb.importPKCS12File(fp.file, password.value);
+ if (errorCode == Ci.nsIX509CertDB.ERROR_BAD_PASSWORD &&
+ password.value.length == 0) {
+ // It didn't like empty string password, try no password.
+ errorCode = certdb.importPKCS12File(fp.file, null);
+ }
+ promptError(errorCode);
+ }
}
var certcache = certdb.getCerts();
userTreeView.loadCertsFromCache(certcache, nsIX509Cert.USER_CERT);
userTreeView.selection.clearSelection();
caTreeView.loadCertsFromCache(certcache, nsIX509Cert.CA_CERT);
caTreeView.selection.clearSelection();
enableBackupAllButton();
--- a/security/manager/ssl/nsIX509CertDB.idl
+++ b/security/manager/ssl/nsIX509CertDB.idl
@@ -165,36 +165,52 @@ interface nsIX509CertDB : nsISupports {
* to be imported.
* @param aType Describes the type of certificate that is going to
* be imported. See type constants in nsIX509Cert.
*/
[must_use]
void importCertsFromFile(in nsIFile aFile,
in unsigned long aType);
+ const uint32_t Success = 0;
+ const uint32_t ERROR_UNKNOWN = 1;
+ const uint32_t ERROR_PKCS12_NOSMARTCARD_EXPORT = 2;
+ const uint32_t ERROR_PKCS12_RESTORE_FAILED = 3;
+ const uint32_t ERROR_PKCS12_BACKUP_FAILED = 4;
+ const uint32_t ERROR_PKCS12_CERT_COLLISION = 5;
+ const uint32_t ERROR_BAD_PASSWORD = 6;
+ const uint32_t ERROR_DECODE_ERROR = 7;
+ const uint32_t ERROR_PKCS12_DUPLICATE_DATA = 8;
+
/**
* Import a PKCS#12 file containing cert(s) and key(s) into the database.
*
* @param aFile Identifies a file that contains the data to be imported.
+ * @param password The password used to protect the file.
+ * @return Success or the specific error code on failure. The return
+ * values are defined in this file.
*/
[must_use]
- void importPKCS12File(in nsIFile aFile);
+ uint32_t importPKCS12File(in nsIFile aFile, in AString aPassword);
/**
* Export a set of certs and keys from the database to a PKCS#12 file.
*
* @param aFile Identifies a file that will be filled with the data to be
* exported.
* @param count The number of certificates to be exported.
* @param aCerts The array of all certificates to be exported.
+ * @param password The password used to protect the file.
+ * @return Success or the specific error code on failure
*/
[must_use]
- void exportPKCS12File(in nsIFile aFile,
- in unsigned long count,
- [array, size_is(count)] in nsIX509Cert aCerts);
+ uint32_t exportPKCS12File(in nsIFile aFile,
+ in unsigned long count,
+ [array, size_is(count)] in nsIX509Cert aCerts,
+ in AString aPassword);
/*
* Decode a raw data presentation and instantiate an object in memory.
*
* @param base64 The raw representation of a certificate,
* encoded as Base 64.
* @return The new certificate object.
*/
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -834,57 +834,59 @@ nsNSSCertificateDB::ImportCertsFromFile(
MOZ_ASSERT(false, "Unsupported type should have been filtered out");
break;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
-nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile)
+nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
+ uint32_t* aError)
{
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_SAME_THREAD;
}
nsresult rv = BlockUntilLoadableRootsLoaded();
if (NS_FAILED(rv)) {
return rv;
}
NS_ENSURE_ARG(aFile);
nsPKCS12Blob blob;
- rv = blob.ImportFromFile(aFile);
-
+ rv = blob.ImportFromFile(aFile, aPassword, *aError);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (NS_SUCCEEDED(rv) && observerService) {
observerService->NotifyObservers(nullptr, "psm:user-certificate-added", nullptr);
}
return rv;
}
NS_IMETHODIMP
-nsNSSCertificateDB::ExportPKCS12File(nsIFile* aFile, uint32_t count,
- nsIX509Cert** certs)
+nsNSSCertificateDB::ExportPKCS12File(nsIFile* aFile, uint32_t aCount,
+ nsIX509Cert** aCerts,
+ const nsAString& aPassword,
+ uint32_t* aError)
{
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_SAME_THREAD;
}
nsresult rv = BlockUntilLoadableRootsLoaded();
if (NS_FAILED(rv)) {
return rv;
}
NS_ENSURE_ARG(aFile);
- if (count == 0) {
+ if (aCount == 0) {
return NS_OK;
}
nsPKCS12Blob blob;
- return blob.ExportToFile(aFile, certs, count);
+ return blob.ExportToFile(aFile, aCerts, aCount, aPassword, *aError);
}
NS_IMETHODIMP
nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
/*out*/ nsIX509Cert** _retval)
{
if (!_retval) {
return NS_ERROR_INVALID_POINTER;
--- a/security/manager/ssl/nsPKCS12Blob.cpp
+++ b/security/manager/ssl/nsPKCS12Blob.cpp
@@ -8,16 +8,17 @@
#include "mozilla/Assertions.h"
#include "mozilla/Casting.h"
#include "mozilla/Unused.h"
#include "nsICertificateDialogs.h"
#include "nsIFile.h"
#include "nsIInputStream.h"
#include "nsIPrompt.h"
#include "nsIWindowWatcher.h"
+#include "nsIX509CertDB.h"
#include "nsNSSCertHelper.h"
#include "nsNSSCertificate.h"
#include "nsNSSHelper.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "nsThreadUtils.h"
#include "p12plcy.h"
#include "pkix/pkixtypes.h"
@@ -35,115 +36,65 @@ extern LazyLogModule gPIPNSSLog;
nsPKCS12Blob::nsPKCS12Blob()
: mUIContext(new PipUIContext())
{
}
// Given a file handle, read a PKCS#12 blob from that file, decode it, and
// import the results into the internal database.
nsresult
-nsPKCS12Blob::ImportFromFile(nsIFile* file)
-{
- nsresult rv;
- RetryReason wantRetry;
- do {
- rv = ImportFromFileHelper(file, ImportMode::StandardPrompt, wantRetry);
-
- if (NS_SUCCEEDED(rv) && wantRetry == RetryReason::AutoRetryEmptyPassword) {
- rv = ImportFromFileHelper(file, ImportMode::TryZeroLengthSecitem,
- wantRetry);
- }
- } while (NS_SUCCEEDED(rv) && (wantRetry != RetryReason::DoNotRetry));
-
- return rv;
-}
-
-void
-nsPKCS12Blob::handleImportError(PRErrorCode nssError, RetryReason& retryReason,
- uint32_t passwordLengthInBytes)
+nsPKCS12Blob::ImportFromFile(nsIFile* aFile, const nsAString& aPassword, uint32_t& aError)
{
- if (nssError == SEC_ERROR_BAD_PASSWORD) {
- // If the password is 2 bytes, it only consists of the wide character null
- // terminator. In this case we want to retry with a zero-length password.
- if (passwordLengthInBytes == 2) {
- retryReason = nsPKCS12Blob::RetryReason::AutoRetryEmptyPassword;
- } else {
- retryReason = RetryReason::BadPassword;
- handleError(PIP_PKCS12_NSS_ERROR, nssError);
- }
- } else {
- handleError(PIP_PKCS12_NSS_ERROR, nssError);
- }
-}
-
-// Returns a failing nsresult if some XPCOM operation failed, and NS_OK
-// otherwise. Returns by reference whether or not we want to retry the operation
-// immediately.
-nsresult
-nsPKCS12Blob::ImportFromFileHelper(nsIFile* file, ImportMode aImportMode,
- RetryReason& aWantRetry)
-{
- aWantRetry = RetryReason::DoNotRetry;
+ uint32_t passwordBufferLength;
+ UniquePtr<uint8_t[]> passwordBuffer;
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
if (!slot) {
return NS_ERROR_FAILURE;
}
- uint32_t passwordBufferLength;
- UniquePtr<uint8_t[]> passwordBuffer;
- if (aImportMode == ImportMode::TryZeroLengthSecitem) {
- passwordBufferLength = 0;
- passwordBuffer = nullptr;
- } else {
- // get file password (unicode)
- nsresult rv = getPKCS12FilePassword(passwordBufferLength, passwordBuffer);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!passwordBuffer) {
- return NS_OK;
- }
- }
+ passwordBuffer = stringToBigEndianBytes(aPassword, passwordBufferLength);
// initialize the decoder
SECItem unicodePw = { siBuffer, passwordBuffer.get(), passwordBufferLength };
UniqueSEC_PKCS12DecoderContext dcx(
SEC_PKCS12DecoderStart(&unicodePw, slot.get(), nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr));
if (!dcx) {
return NS_ERROR_FAILURE;
}
- // read input file and feed it to the decoder
+ // read input aFile and feed it to the decoder
PRErrorCode nssError;
- nsresult rv = inputToDecoder(dcx, file, nssError);
+ nsresult rv = inputToDecoder(dcx, aFile, nssError);
if (NS_FAILED(rv)) {
return rv;
}
if (nssError != 0) {
- handleImportError(nssError, aWantRetry, unicodePw.len);
+ aError = handlePRErrorCode(nssError);
return NS_OK;
}
// verify the blob
SECStatus srv = SEC_PKCS12DecoderVerify(dcx.get());
if (srv != SECSuccess) {
- handleImportError(PR_GetError(), aWantRetry, unicodePw.len);
+ aError = handlePRErrorCode(PR_GetError());
return NS_OK;
}
// validate bags
srv = SEC_PKCS12DecoderValidateBags(dcx.get(), nicknameCollision);
if (srv != SECSuccess) {
- handleImportError(PR_GetError(), aWantRetry, unicodePw.len);
+ aError = handlePRErrorCode(PR_GetError());
return NS_OK;
}
// import cert and key
srv = SEC_PKCS12DecoderImportBags(dcx.get());
if (srv != SECSuccess) {
- handleImportError(PR_GetError(), aWantRetry, unicodePw.len);
+ aError = handlePRErrorCode(PR_GetError());
+ return NS_OK;
}
+ aError = nsIX509CertDB::Success;
return NS_OK;
}
static bool
isExtractable(UniqueSECKEYPrivateKey& privKey)
{
ScopedAutoSECItem value;
SECStatus rv = PK11_ReadRawAttribute(
@@ -157,186 +108,135 @@ isExtractable(UniqueSECKEYPrivateKey& pr
isExtractable = !!(*(CK_BBOOL*)value.data);
}
return isExtractable;
}
// Having already loaded the certs, form them into a blob (loading the keys
// also), encode the blob, and stuff it into the file.
nsresult
-nsPKCS12Blob::ExportToFile(nsIFile* file, nsIX509Cert** certs, int numCerts)
+nsPKCS12Blob::ExportToFile(nsIFile* aFile, nsIX509Cert** aCerts, int aNumCerts,
+ const nsAString& aPassword, uint32_t& aError)
{
- bool informedUserNoSmartcardBackup = false;
// get file password (unicode)
uint32_t passwordBufferLength;
UniquePtr<uint8_t[]> passwordBuffer;
- nsresult rv = newPKCS12FilePassword(passwordBufferLength, passwordBuffer);
- if (NS_FAILED(rv)) {
- return rv;
- }
+ passwordBuffer = stringToBigEndianBytes(aPassword, passwordBufferLength);
+ aError = nsIX509CertDB::Success;
if (!passwordBuffer) {
return NS_OK;
}
UniqueSEC_PKCS12ExportContext ecx(
SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr, nullptr));
if (!ecx) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
+ return NS_OK;
}
// add password integrity
SECItem unicodePw = { siBuffer, passwordBuffer.get(), passwordBufferLength };
SECStatus srv = SEC_PKCS12AddPasswordIntegrity(ecx.get(), &unicodePw,
SEC_OID_SHA1);
if (srv != SECSuccess) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
+ return NS_OK;
}
- for (int i = 0; i < numCerts; i++) {
- UniqueCERTCertificate nssCert(certs[i]->GetCert());
+ for (int i = 0; i < aNumCerts; i++) {
+ UniqueCERTCertificate nssCert(aCerts[i]->GetCert());
if (!nssCert) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
+ return NS_OK;
}
// We can probably only successfully export certs that are on the internal
// token. Most, if not all, smart card vendors won't let you extract the
// private key (in any way shape or form) from the card. So let's punt if
// the cert is not in the internal db.
if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
// We aren't the internal token, see if the key is extractable.
UniqueSECKEYPrivateKey privKey(
PK11_FindKeyByDERCert(nssCert->slot, nssCert.get(), mUIContext));
if (privKey && !isExtractable(privKey)) {
- if (!informedUserNoSmartcardBackup) {
- informedUserNoSmartcardBackup = true;
- handleError(PIP_PKCS12_NOSMARTCARD_EXPORT, PR_GetError());
- }
+ // This is informative. If a serious error occurs later it will
+ // override it later and return.
+ aError = nsIX509CertDB::ERROR_PKCS12_NOSMARTCARD_EXPORT;
continue;
}
}
// certSafe and keySafe are owned by ecx.
SEC_PKCS12SafeInfo* certSafe;
SEC_PKCS12SafeInfo* keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx.get());
if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
certSafe = keySafe;
} else {
certSafe = SEC_PKCS12CreatePasswordPrivSafe(
ecx.get(), &unicodePw,
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
}
if (!certSafe || !keySafe) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
+ return NS_OK;
}
// add the cert and key to the blob
srv = SEC_PKCS12AddCertAndKey(
ecx.get(),
certSafe,
nullptr,
nssCert.get(),
CERT_GetDefaultCertDB(),
keySafe,
nullptr,
true,
&unicodePw,
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
if (srv != SECSuccess) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
+ return NS_OK;
}
}
UniquePRFileDesc prFile;
PRFileDesc* rawPRFile;
- rv = file->OpenNSPRFileDesc(
- PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0664, &rawPRFile);
+ nsresult rv = aFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ 0664, &rawPRFile);
if (NS_FAILED(rv) || !rawPRFile) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
+ return NS_OK;
}
prFile.reset(rawPRFile);
// encode and write
srv = SEC_PKCS12Encode(ecx.get(), writeExportFile, prFile.get());
if (srv != SECSuccess) {
- handleError(PIP_PKCS12_BACKUP_FAILED, PR_GetError());
- return NS_ERROR_FAILURE;
+ aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
}
return NS_OK;
}
// For the NSS PKCS#12 library, must convert PRUnichars (shorts) to a buffer of
// octets. Must handle byte order correctly.
UniquePtr<uint8_t[]>
-nsPKCS12Blob::stringToBigEndianBytes(const nsString& uni, uint32_t& bytesLength)
+nsPKCS12Blob::stringToBigEndianBytes(const nsAString& uni, uint32_t& bytesLength)
{
+ if (uni.IsVoid()) {
+ bytesLength = 0;
+ return nullptr;
+ }
+
uint32_t wideLength = uni.Length() + 1; // +1 for the null terminator.
bytesLength = wideLength * 2;
auto buffer = MakeUnique<uint8_t[]>(bytesLength);
// We have to use a cast here because on Windows, uni.get() returns
// char16ptr_t instead of char16_t*.
mozilla::NativeEndian::copyAndSwapToBigEndian(
- buffer.get(), static_cast<const char16_t*>(uni.get()), wideLength);
+ buffer.get(), static_cast<const char16_t*>(uni.BeginReading()), wideLength);
return buffer;
}
-// Launch a dialog requesting the user for a new PKCS#12 file passowrd.
-// Handle user canceled by returning null password (caller must catch).
-nsresult
-nsPKCS12Blob::newPKCS12FilePassword(uint32_t& passwordBufferLength,
- UniquePtr<uint8_t[]>& passwordBuffer)
-{
- nsAutoString password;
- nsCOMPtr<nsICertificateDialogs> certDialogs;
- nsresult rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
- NS_GET_IID(nsICertificateDialogs),
- NS_CERTIFICATEDIALOGS_CONTRACTID);
- if (NS_FAILED(rv)) {
- return rv;
- }
- bool pressedOK = false;
- rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!pressedOK) {
- return NS_OK;
- }
- passwordBuffer = stringToBigEndianBytes(password, passwordBufferLength);
- return NS_OK;
-}
-
-// Launch a dialog requesting the user for the password to a PKCS#12 file.
-// Handle user canceled by returning null password (caller must catch).
-nsresult
-nsPKCS12Blob::getPKCS12FilePassword(uint32_t& passwordBufferLength,
- UniquePtr<uint8_t[]>& passwordBuffer)
-{
- nsCOMPtr<nsICertificateDialogs> certDialogs;
- nsresult rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
- NS_GET_IID(nsICertificateDialogs),
- NS_CERTIFICATEDIALOGS_CONTRACTID);
- if (NS_FAILED(rv)) {
- return rv;
- }
- nsAutoString password;
- bool pressedOK = false;
- rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!pressedOK) {
- return NS_OK;
- }
- passwordBuffer = stringToBigEndianBytes(password, passwordBufferLength);
- return NS_OK;
-}
-
// Given a decoder, read bytes from file and input them to the decoder.
nsresult
nsPKCS12Blob::inputToDecoder(UniqueSEC_PKCS12DecoderContext& dcx, nsIFile* file,
PRErrorCode& nssError)
{
nssError = 0;
nsCOMPtr<nsIInputStream> fileStream;
@@ -429,74 +329,35 @@ nsPKCS12Blob::nicknameCollision(SECItem*
void
nsPKCS12Blob::writeExportFile(void* arg, const char* buf, unsigned long len)
{
PRFileDesc* file = static_cast<PRFileDesc*>(arg);
MOZ_RELEASE_ASSERT(file);
PR_Write(file, buf, len);
}
-void
-nsPKCS12Blob::handleError(int myerr, PRErrorCode prerr)
+// Translate PRErrorCode to nsIX509CertDB error
+uint32_t
+nsPKCS12Blob::handlePRErrorCode(PRErrorCode aPrerr)
{
- MOZ_ASSERT(NS_IsMainThread());
- if (!NS_IsMainThread()) {
- return;
- }
-
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PKCS12: NSS/NSPR error(%d)", prerr));
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PKCS12: I called(%d)", myerr));
-
- const char* msgID = nullptr;
-
- switch (myerr) {
- case PIP_PKCS12_NOSMARTCARD_EXPORT:
- msgID = "PKCS12InfoNoSmartcardBackup";
- break;
- case PIP_PKCS12_RESTORE_FAILED:
- msgID = "PKCS12UnknownErrRestore";
+ MOZ_ASSERT(aPrerr != 0);
+ uint32_t error = nsIX509CertDB::ERROR_UNKNOWN;
+ switch (aPrerr) {
+ case SEC_ERROR_PKCS12_CERT_COLLISION:
+ error = nsIX509CertDB::ERROR_PKCS12_DUPLICATE_DATA;
break;
- case PIP_PKCS12_BACKUP_FAILED:
- msgID = "PKCS12UnknownErrBackup";
+ // INVALID_ARGS is returned on bad password when importing cert
+ // exported from firefox or generated by openssl
+ case SEC_ERROR_INVALID_ARGS:
+ case SEC_ERROR_BAD_PASSWORD:
+ error = nsIX509CertDB::ERROR_BAD_PASSWORD;
break;
- case PIP_PKCS12_NSS_ERROR:
- switch (prerr) {
- case 0:
- break;
- case SEC_ERROR_PKCS12_CERT_COLLISION:
- msgID = "PKCS12DupData";
- break;
- case SEC_ERROR_BAD_PASSWORD:
- msgID = "PK11BadPassword";
- break;
-
- case SEC_ERROR_BAD_DER:
- case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
- case SEC_ERROR_PKCS12_INVALID_MAC:
- msgID = "PKCS12DecodeErr";
- break;
-
- case SEC_ERROR_PKCS12_DUPLICATE_DATA:
- msgID = "PKCS12DupData";
- break;
- }
+ case SEC_ERROR_BAD_DER:
+ case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
+ case SEC_ERROR_PKCS12_INVALID_MAC:
+ error = nsIX509CertDB::ERROR_DECODE_ERROR;
+ break;
+ case SEC_ERROR_PKCS12_DUPLICATE_DATA:
+ error = nsIX509CertDB::ERROR_PKCS12_DUPLICATE_DATA;
break;
}
-
- if (!msgID) {
- msgID = "PKCS12UnknownErr";
- }
-
- nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
- if (!wwatch) {
- return;
- }
- nsCOMPtr<nsIPrompt> prompter;
- if (NS_FAILED(wwatch->GetNewPrompter(nullptr, getter_AddRefs(prompter)))) {
- return;
- }
- nsAutoString message;
- if (NS_FAILED(GetPIPNSSBundleString(msgID, message))) {
- return;
- }
-
- Unused << prompter->Alert(nullptr, message.get());
+ return error;
}
--- a/security/manager/ssl/nsPKCS12Blob.h
+++ b/security/manager/ssl/nsPKCS12Blob.h
@@ -19,63 +19,32 @@ class nsIX509Cert;
// Class for importing/exporting PKCS#12 blobs
class nsPKCS12Blob
{
public:
nsPKCS12Blob();
~nsPKCS12Blob() {}
// PKCS#12 Import
- nsresult ImportFromFile(nsIFile* file);
+ nsresult ImportFromFile(nsIFile* file, const nsAString& password,
+ uint32_t& error);
// PKCS#12 Export
- nsresult ExportToFile(nsIFile* file, nsIX509Cert** certs, int numCerts);
+ nsresult ExportToFile(nsIFile* file, nsIX509Cert** certs, int numCerts,
+ const nsAString& password, uint32_t& error);
private:
nsCOMPtr<nsIInterfaceRequestor> mUIContext;
// local helper functions
- nsresult getPKCS12FilePassword(uint32_t& passwordBufferLength,
- mozilla::UniquePtr<uint8_t[]>& passwordBuffer);
- nsresult newPKCS12FilePassword(uint32_t& passwordBufferLength,
- mozilla::UniquePtr<uint8_t[]>& passwordBuffer);
nsresult inputToDecoder(mozilla::UniqueSEC_PKCS12DecoderContext& dcx,
nsIFile* file,
PRErrorCode& nssError);
- mozilla::UniquePtr<uint8_t[]> stringToBigEndianBytes(const nsString& uni,
+ mozilla::UniquePtr<uint8_t[]> stringToBigEndianBytes(const nsAString& uni,
uint32_t& bytesLength);
- void handleError(int myerr, PRErrorCode prerr);
-
- // RetryReason and ImportMode are used when importing a PKCS12 file.
- // There are two reasons that cause us to retry:
- // - When the password entered by the user is incorrect.
- // The user will be prompted to try again.
- // - When the user entered a zero length password.
- // An empty password should be represented as an empty string (a SECItem
- // that contains a single terminating null UTF16 character), but some
- // applications use a zero length SECItem. We try both variations, zero
- // length item and empty string, without giving a user prompt when trying
- // the different empty password flavors.
- enum class RetryReason
- {
- DoNotRetry,
- BadPassword,
- AutoRetryEmptyPassword,
- };
- enum class ImportMode
- {
- StandardPrompt,
- TryZeroLengthSecitem
- };
-
- void handleImportError(PRErrorCode nssError, RetryReason& retryReason,
- uint32_t passwordLengthInBytes);
-
- nsresult ImportFromFileHelper(nsIFile* file,
- ImportMode aImportMode,
- RetryReason& aWantRetry);
+ uint32_t handlePRErrorCode(PRErrorCode prerr);
static SECItem* nicknameCollision(SECItem* oldNick, PRBool* cancel,
void* wincx);
static void writeExportFile(void* arg, const char* buf, unsigned long len);
};
#endif // nsPKCS12Blob_h
--- a/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js
+++ b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js
@@ -11,105 +11,48 @@ do_get_profile();
const gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
const CERT_COMMON_NAME = "test_cert_from_windows";
const TEST_CERT_PASSWORD = "黒い";
const TEST_OUTPUT_PASSWORD = "other password";
-let gPasswordToUse = TEST_CERT_PASSWORD;
-
-// Mock implementation of nsICertificateDialogs.
-const gCertificateDialogs = {
- confirmDownloadCACert: () => {
- // We don't test anything that calls this method.
- ok(false, "confirmDownloadCACert() should not have been called");
- },
- setPKCS12FilePassword: (ctx, password) => {
- password.value = gPasswordToUse;
- return true;
- },
- getPKCS12FilePassword: (ctx, password) => {
- password.value = gPasswordToUse;
- return true;
- },
- viewCert: (ctx, cert) => {
- // This shouldn't be called for import methods.
- ok(false, "viewCert() should not have been called");
- },
-
- QueryInterface: ChromeUtils.generateQI([Ci.nsICertificateDialogs])
-};
-
-var gPrompt = {
- clickOk: true,
-
- QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
-
- // This intentionally does not use arrow function syntax to avoid an issue
- // where in the context of the arrow function, |this != gPrompt| due to
- // how objects get wrapped when going across xpcom boundaries.
- alert(title, text) {
- ok(false, "Not expecting alert to be called.");
- },
-
- promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
- ok(false, "Not expecting a password prompt.");
- return false;
- },
-};
-
-const gPromptFactory = {
- QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptFactory]),
- getPrompt: (aWindow, aIID) => gPrompt,
-};
-
function findCertByCommonName(commonName) {
let certEnumerator = gCertDB.getCerts().getEnumerator();
while (certEnumerator.hasMoreElements()) {
let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
if (cert.commonName == commonName) {
return cert;
}
}
return null;
}
function run_test() {
- let certificateDialogsCID =
- MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1",
- gCertificateDialogs);
- let promptFactoryCID =
- MockRegistrar.register("@mozilla.org/prompter;1", gPromptFactory);
-
- registerCleanupFunction(() => {
- MockRegistrar.unregister(certificateDialogsCID);
- MockRegistrar.unregister(promptFactoryCID);
- });
-
// Import the certificate and key so we have something to export.
let cert = findCertByCommonName(CERT_COMMON_NAME);
equal(cert, null, "cert should not be found before import");
let certFile = do_get_file(PKCS12_FILE);
ok(certFile, `${PKCS12_FILE} should exist`);
- gPasswordToUse = TEST_CERT_PASSWORD;
- gCertDB.importPKCS12File(certFile);
+ let errorCode = gCertDB.importPKCS12File(certFile, TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should be imported");
cert = findCertByCommonName(CERT_COMMON_NAME);
notEqual(cert, null, "cert should be found now");
// Export the certificate and key.
let output = do_get_tempdir();
output.append("output.p12");
ok(!output.exists(), "output shouldn't exist before exporting PKCS12 file");
- gPasswordToUse = TEST_OUTPUT_PASSWORD;
- gCertDB.exportPKCS12File(output, 1, [cert]);
+ errorCode = gCertDB.exportPKCS12File(output, 1, [cert], TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should be exported");
ok(output.exists(), "output should exist after exporting PKCS12 file");
// We should be able to import the exported blob again using the new password.
- gCertDB.importPKCS12File(output);
+ errorCode = gCertDB.importPKCS12File(output, TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should be imported");
output.remove(false /* not a directory; recursive doesn't apply */);
// Ideally there would be some way to confirm that this actually did anything.
// Unfortunately, since deleting a certificate currently doesn't actually do
// anything until the platform is restarted, we can't confirm that we
// successfully re-imported the certificate.
}
--- a/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js
+++ b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js
@@ -10,55 +10,28 @@ do_get_profile();
const gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
const CERT_COMMON_NAME = "test_cert_from_windows";
const TEST_CERT_PASSWORD = "黒い";
-// Mock implementation of nsICertificateDialogs.
-const gCertificateDialogs = {
- confirmDownloadCACert: () => {
- // We don't test anything that calls this method.
- ok(false, "confirmDownloadCACert() should not have been called");
- },
- setPKCS12FilePassword: (ctx, password) => {
- password.value = TEST_CERT_PASSWORD;
- return true;
- },
- getPKCS12FilePassword: (ctx, password) => {
- password.value = TEST_CERT_PASSWORD;
- return true;
- },
- viewCert: (ctx, cert) => {
- // This shouldn't be called for import methods.
- ok(false, "viewCert() should not have been called");
- },
-
- QueryInterface: ChromeUtils.generateQI([Ci.nsICertificateDialogs])
-};
-
var gPrompt = {
password: "password",
clickOk: true,
- expectingAlert: false,
- expectedAlertRegexp: null,
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
// This intentionally does not use arrow function syntax to avoid an issue
// where in the context of the arrow function, |this != gPrompt| due to
// how objects get wrapped when going across xpcom boundaries.
alert(title, text) {
info(`alert('${text}')`);
- ok(this.expectingAlert,
- "alert() should only be called if we're expecting it");
- ok(this.expectedAlertRegexp.test(text),
- "alert text should match expected message");
+ ok(false, "not expecting alert() to be called");
},
promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
equal(text,
"Please enter your master password.",
"password prompt text should be as expected");
equal(checkMsg, null, "checkMsg should be null");
password.value = this.password;
@@ -78,61 +51,59 @@ function findCertByCommonName(commonName
if (cert.commonName == commonName) {
return cert;
}
}
return null;
}
function run_test() {
- let certificateDialogsCID =
- MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1",
- gCertificateDialogs);
let promptFactoryCID =
MockRegistrar.register("@mozilla.org/prompter;1", gPromptFactory);
registerCleanupFunction(() => {
- MockRegistrar.unregister(certificateDialogsCID);
MockRegistrar.unregister(promptFactoryCID);
});
// Set a master password.
let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
.getService(Ci.nsIPK11TokenDB);
let token = tokenDB.getInternalKeyToken();
token.initPassword("password");
token.logoutSimple();
// Import the certificate and key so we have something to export.
let cert = findCertByCommonName(CERT_COMMON_NAME);
equal(cert, null, "cert should not be found before import");
let certFile = do_get_file(PKCS12_FILE);
ok(certFile, `${PKCS12_FILE} should exist`);
- gCertDB.importPKCS12File(certFile);
+ let errorCode = gCertDB.importPKCS12File(certFile, TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should import");
cert = findCertByCommonName(CERT_COMMON_NAME);
notEqual(cert, null, "cert should be found now");
// Log out so we're prompted for the password.
token.logoutSimple();
// Export the certificate and key (and don't cancel the password request
// dialog).
let output = do_get_tempdir();
output.append("output.p12");
ok(!output.exists(), "output shouldn't exist before exporting PKCS12 file");
- gCertDB.exportPKCS12File(output, 1, [cert]);
+ errorCode = gCertDB.exportPKCS12File(output, 1, [cert], TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should export");
ok(output.exists(), "output should exist after exporting PKCS12 file");
output.remove(false /* not a directory; recursive doesn't apply */);
// Log out again so we're prompted for the password.
token.logoutSimple();
// Attempt to export the certificate and key, but this time cancel the
// password request dialog. The export operation should also be canceled.
gPrompt.clickOk = false;
let output2 = do_get_tempdir();
output2.append("output2.p12");
ok(!output2.exists(), "output2 shouldn't exist before exporting PKCS12 file");
- gPrompt.expectingAlert = true;
- gPrompt.expectedAlertRegexp = /Failed to create the PKCS #12 backup file for unknown reasons\./;
- throws(() => gCertDB.exportPKCS12File(output, 1, [cert]), /NS_ERROR_FAILURE/);
+ errorCode = gCertDB.exportPKCS12File(output, 1, [cert], TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.ERROR_PKCS12_BACKUP_FAILED, "cert should not export");
+
ok(!output2.exists(), "output2 shouldn't exist after failing to export");
}
new file mode 100644
index 0000000000000000000000000000000000000000..879d424b85fe096f066bcd76c01f4f4571b8bbca
GIT binary patch
literal 2068
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
new file mode 100644
index 0000000000000000000000000000000000000000..7dcd6681212314f9cef827d6d85fbf9069082db9
GIT binary patch
literal 2068
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
--- a/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
@@ -6,97 +6,77 @@
// Tests import PKCS12 file by nsIX509CertDB.
do_get_profile();
const gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
+const PKCS12_FILE_EMPTY_PASS =
+ "test_certDB_import/cert_from_windows_emptypass.pfx";
+const PKCS12_FILE_NO_PASS = "test_certDB_import/cert_from_windows_nopass.pfx";
const CERT_COMMON_NAME = "test_cert_from_windows";
const TEST_CERT_PASSWORD = "黒い";
// Has getPKCS12FilePassword been called since we last reset this?
let gGetPKCS12FilePasswordCalled = false;
let gCurrentTestcase = null;
let gTestcases = [
// Test that importing a PKCS12 file with the wrong password fails.
{
name: "import using incorrect password",
filename: PKCS12_FILE,
- // cancel after the first failed password prompt
- multiplePasswordPromptsMeans: "cancel",
- expectingAlert: true,
- expectedAlertRegexp: /^The password entered was incorrect\.$/,
passwordToUse: "this is the wrong password",
successExpected: false,
+ errorCode: Ci.nsIX509CertDB.ERROR_BAD_PASSWORD,
+ checkCertExist: true,
},
// Test that importing something that isn't a PKCS12 file fails.
{
name: "import non-PKCS12 file",
filename: "test_certDB_import_pkcs12.js",
- // cancel after the first failed password prompt
- multiplePasswordPromptsMeans: "cancel",
- expectingAlert: true,
- expectedAlertRegexp: /^Failed to decode the file\./,
passwordToUse: TEST_CERT_PASSWORD,
successExpected: false,
+ errorCode: Ci.nsIX509CertDB.ERROR_DECODE_ERROR,
+ checkCertExist: true,
},
// Test that importing a PKCS12 file with the correct password succeeds.
// This needs to be last because currently there isn't a way to delete the
// imported certificate (and thus reset the test state) that doesn't depend on
// the garbage collector running.
{
name: "import PKCS12 file",
filename: PKCS12_FILE,
- // If we see more than one password prompt, this is a test failure.
- multiplePasswordPromptsMeans: "test failure",
- expectingAlert: false,
- expectedAlertRegexp: null,
passwordToUse: TEST_CERT_PASSWORD,
successExpected: true,
+ errorCode: Ci.nsIX509CertDB.Success,
+ checkCertExist: true,
+ },
+ // Same cert file protected with empty string password
+ {
+ name: "import PKCS12 file empty password",
+ filename: PKCS12_FILE_EMPTY_PASS,
+ passwordToUse: "",
+ successExpected: true,
+ errorCode: Ci.nsIX509CertDB.Success,
+ checkCertExist: false,
+ },
+ // Same cert file protected with no password
+ {
+ name: "import PKCS12 file no password",
+ filename: PKCS12_FILE_NO_PASS,
+ passwordToUse: null,
+ successExpected: true,
+ errorCode: Ci.nsIX509CertDB.Success,
+ checkCertExist: false,
},
];
-// Mock implementation of nsICertificateDialogs.
-const gCertificateDialogs = {
- QueryInterface: ChromeUtils.generateQI([Ci.nsICertificateDialogs]),
-
- getPKCS12FilePassword: (ctx, password) => {
- if (gGetPKCS12FilePasswordCalled) {
- if (gCurrentTestcase.multiplePasswordPromptsMeans == "cancel") {
- return false;
- }
- ok(false, "getPKCS12FilePassword should be called only once");
- }
-
- password.value = gCurrentTestcase.passwordToUse;
- info("getPKCS12FilePassword() called");
- gGetPKCS12FilePasswordCalled = true;
- return true;
- },
-};
-
-const gPrompt = {
- QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
- alert: (title, text) => {
- info(`alert('${text}')`);
- ok(gCurrentTestcase.expectingAlert,
- "alert() should only be called if we're expecting it");
- ok(gCurrentTestcase.expectedAlertRegexp.test(text),
- "alert text should match expected message");
- },
-};
-
-const gPromptFactory = {
- QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptFactory]),
- getPrompt: (aWindow, aIID) => gPrompt,
-};
-
function doesCertExist(commonName) {
let allCerts = gCertDB.getCerts();
let enumerator = allCerts.getEnumerator();
while (enumerator.hasMoreElements()) {
let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
if (cert.isBuiltInRoot) {
continue;
}
@@ -105,40 +85,29 @@ function doesCertExist(commonName) {
}
}
return false;
}
function runOneTestcase(testcase) {
info(`running ${testcase.name}`);
- ok(!doesCertExist(CERT_COMMON_NAME),
- "cert should not be in the database before import");
+ if (testcase.checkCertExist) {
+ ok(!doesCertExist(CERT_COMMON_NAME),
+ "cert should not be in the database before import");
+ }
// Import and check for failure.
let certFile = do_get_file(testcase.filename);
ok(certFile, `${testcase.filename} should exist`);
gGetPKCS12FilePasswordCalled = false;
gCurrentTestcase = testcase;
- gCertDB.importPKCS12File(certFile);
- ok(gGetPKCS12FilePasswordCalled,
- "getPKCS12FilePassword should have been called");
-
+ let errorCode = gCertDB.importPKCS12File(certFile, testcase.passwordToUse);
+ equal(errorCode, testcase.errorCode, `verifying error code`);
equal(doesCertExist(CERT_COMMON_NAME), testcase.successExpected,
`cert should${testcase.successExpected ? "" : " not"} be found now`);
}
function run_test() {
- let certificateDialogsCID =
- MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1",
- gCertificateDialogs);
- let promptFactoryCID =
- MockRegistrar.register("@mozilla.org/prompter;1", gPromptFactory);
-
- registerCleanupFunction(() => {
- MockRegistrar.unregister(certificateDialogsCID);
- MockRegistrar.unregister(promptFactoryCID);
- });
-
for (let testcase of gTestcases) {
runOneTestcase(testcase);
}
}