Blame security/manager/ssl/nsPKCS12Blob.cpp

Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "nsPKCS12Blob.h"
Packit f0b94e
Packit f0b94e
#include "ScopedNSSTypes.h"
Packit f0b94e
#include "mozilla/Assertions.h"
Packit f0b94e
#include "mozilla/Casting.h"
Packit f0b94e
#include "mozilla/Unused.h"
Packit f0b94e
#include "nsICertificateDialogs.h"
Packit f0b94e
#include "nsIFile.h"
Packit f0b94e
#include "nsIInputStream.h"
Packit f0b94e
#include "nsNSSCertHelper.h"
Packit f0b94e
#include "nsNSSCertificate.h"
Packit f0b94e
#include "nsNSSHelper.h"
Packit f0b94e
#include "nsNetUtil.h"
Packit f0b94e
#include "nsReadableUtils.h"
Packit f0b94e
#include "nsThreadUtils.h"
Packit f0b94e
#include "pkix/pkixtypes.h"
Packit f0b94e
#include "secerr.h"
Packit f0b94e
Packit f0b94e
using namespace mozilla;
Packit f0b94e
extern LazyLogModule gPIPNSSLog;
Packit f0b94e
Packit f0b94e
#define PIP_PKCS12_TMPFILENAME NS_LITERAL_CSTRING(".pip_p12tmp")
Packit f0b94e
#define PIP_PKCS12_BUFFER_SIZE 2048
Packit f0b94e
#define PIP_PKCS12_USER_CANCELED 3
Packit f0b94e
#define PIP_PKCS12_NOSMARTCARD_EXPORT 4
Packit f0b94e
#define PIP_PKCS12_RESTORE_FAILED 5
Packit f0b94e
#define PIP_PKCS12_BACKUP_FAILED 6
Packit f0b94e
#define PIP_PKCS12_NSS_ERROR 7
Packit f0b94e
Packit f0b94e
// constructor
Packit f0b94e
nsPKCS12Blob::nsPKCS12Blob() : mCertArray(nullptr), mTmpFile(nullptr) {
Packit f0b94e
  mUIContext = new PipUIContext();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// nsPKCS12Blob::ImportFromFile
Packit f0b94e
//
Packit f0b94e
// Given a file handle, read a PKCS#12 blob from that file, decode it, and
Packit f0b94e
// import the results into the internal database.
Packit f0b94e
nsresult nsPKCS12Blob::ImportFromFile(nsIFile *file) {
Packit f0b94e
  nsresult rv = NS_OK;
Packit f0b94e
Packit f0b94e
  RetryReason wantRetry;
Packit f0b94e
Packit f0b94e
  do {
Packit f0b94e
    rv = ImportFromFileHelper(file, im_standard_prompt, wantRetry);
Packit f0b94e
Packit f0b94e
    if (NS_SUCCEEDED(rv) && wantRetry == rr_auto_retry_empty_password_flavors) {
Packit f0b94e
      rv = ImportFromFileHelper(file, im_try_zero_length_secitem, wantRetry);
Packit f0b94e
    }
Packit f0b94e
  } while (NS_SUCCEEDED(rv) && (wantRetry != rr_do_not_retry));
Packit f0b94e
Packit f0b94e
  return rv;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult nsPKCS12Blob::ImportFromFileHelper(
Packit f0b94e
    nsIFile *file, nsPKCS12Blob::ImportMode aImportMode,
Packit f0b94e
    nsPKCS12Blob::RetryReason &aWantRetry) {
Packit f0b94e
  nsresult rv = NS_OK;
Packit f0b94e
  SECStatus srv = SECSuccess;
Packit f0b94e
  SEC_PKCS12DecoderContext *dcx = nullptr;
Packit f0b94e
  SECItem unicodePw = {siBuffer, nullptr, 0};
Packit f0b94e
Packit f0b94e
  aWantRetry = rr_do_not_retry;
Packit f0b94e
Packit f0b94e
  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
Packit f0b94e
  if (!slot) {
Packit f0b94e
    srv = SECFailure;
Packit f0b94e
    goto finish;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aImportMode == im_try_zero_length_secitem) {
Packit f0b94e
    unicodePw.len = 0;
Packit f0b94e
  } else {
Packit f0b94e
    // get file password (unicode)
Packit f0b94e
    rv = getPKCS12FilePassword(&unicodePw);
Packit f0b94e
    if (NS_FAILED(rv)) goto finish;
Packit f0b94e
    if (!unicodePw.data) {
Packit f0b94e
      handleError(PIP_PKCS12_USER_CANCELED);
Packit f0b94e
      return NS_OK;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // initialize the decoder
Packit f0b94e
  dcx = SEC_PKCS12DecoderStart(&unicodePw, slot.get(), nullptr, nullptr,
Packit f0b94e
                               nullptr, nullptr, nullptr, nullptr);
Packit f0b94e
  if (!dcx) {
Packit f0b94e
    srv = SECFailure;
Packit f0b94e
    goto finish;
Packit f0b94e
  }
Packit f0b94e
  // read input file and feed it to the decoder
Packit f0b94e
  rv = inputToDecoder(dcx, file);
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    if (NS_ERROR_ABORT == rv) {
Packit f0b94e
      // inputToDecoder indicated a NSS error
Packit f0b94e
      srv = SECFailure;
Packit f0b94e
    }
Packit f0b94e
    goto finish;
Packit f0b94e
  }
Packit f0b94e
  // verify the blob
Packit f0b94e
  srv = SEC_PKCS12DecoderVerify(dcx);
Packit f0b94e
  if (srv) goto finish;
Packit f0b94e
  // validate bags
Packit f0b94e
  srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision);
Packit f0b94e
  if (srv) goto finish;
Packit f0b94e
  // import cert and key
Packit f0b94e
  srv = SEC_PKCS12DecoderImportBags(dcx);
Packit f0b94e
  if (srv) goto finish;
Packit f0b94e
  // Later - check to see if this should become default email cert
Packit f0b94e
finish:
Packit f0b94e
  // If srv != SECSuccess, NSS probably set a specific error code.
Packit f0b94e
  // We should use that error code instead of inventing a new one
Packit f0b94e
  // for every error possible.
Packit f0b94e
  if (srv != SECSuccess) {
Packit f0b94e
    if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) {
Packit f0b94e
      if (unicodePw.len == sizeof(char16_t)) {
Packit f0b94e
        // no password chars available,
Packit f0b94e
        // unicodeToItem allocated space for the trailing zero character only.
Packit f0b94e
        aWantRetry = rr_auto_retry_empty_password_flavors;
Packit f0b94e
      } else {
Packit f0b94e
        aWantRetry = rr_bad_password;
Packit f0b94e
        handleError(PIP_PKCS12_NSS_ERROR);
Packit f0b94e
      }
Packit f0b94e
    } else {
Packit f0b94e
      handleError(PIP_PKCS12_NSS_ERROR);
Packit f0b94e
    }
Packit f0b94e
  } else if (NS_FAILED(rv)) {
Packit f0b94e
    handleError(PIP_PKCS12_RESTORE_FAILED);
Packit f0b94e
  }
Packit f0b94e
  // finish the decoder
Packit f0b94e
  if (dcx) SEC_PKCS12DecoderFinish(dcx);
Packit f0b94e
  SECITEM_ZfreeItem(&unicodePw, false);
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static bool isExtractable(SECKEYPrivateKey *privKey) {
Packit f0b94e
  ScopedAutoSECItem value;
Packit f0b94e
  SECStatus rv =
Packit f0b94e
      PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value);
Packit f0b94e
  if (rv != SECSuccess) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  bool isExtractable = false;
Packit f0b94e
  if ((value.len == 1) && value.data) {
Packit f0b94e
    isExtractable = !!(*(CK_BBOOL *)value.data);
Packit f0b94e
  }
Packit f0b94e
  return isExtractable;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// nsPKCS12Blob::ExportToFile
Packit f0b94e
//
Packit f0b94e
// Having already loaded the certs, form them into a blob (loading the keys
Packit f0b94e
// also), encode the blob, and stuff it into the file.
Packit f0b94e
nsresult nsPKCS12Blob::ExportToFile(nsIFile *file, nsIX509Cert **certs,
Packit f0b94e
                                    int numCerts) {
Packit f0b94e
  nsresult rv;
Packit f0b94e
  SECStatus srv = SECSuccess;
Packit f0b94e
  SEC_PKCS12ExportContext *ecx = nullptr;
Packit f0b94e
  SEC_PKCS12SafeInfo *certSafe = nullptr, *keySafe = nullptr;
Packit f0b94e
  SECItem unicodePw;
Packit f0b94e
  nsAutoString filePath;
Packit f0b94e
  int i;
Packit f0b94e
  nsCOMPtr<nsIFile> localFileRef;
Packit f0b94e
  // init slot
Packit f0b94e
Packit f0b94e
  bool InformedUserNoSmartcardBackup = false;
Packit f0b94e
  int numCertsExported = 0;
Packit f0b94e
Packit f0b94e
  // get file password (unicode)
Packit f0b94e
  unicodePw.data = nullptr;
Packit f0b94e
  rv = newPKCS12FilePassword(&unicodePw);
Packit f0b94e
  if (NS_FAILED(rv)) goto finish;
Packit f0b94e
  if (!unicodePw.data) {
Packit f0b94e
    handleError(PIP_PKCS12_USER_CANCELED);
Packit f0b94e
    return NS_OK;
Packit f0b94e
  }
Packit f0b94e
  // what about slotToUse in psm 1.x ???
Packit f0b94e
  // create export context
Packit f0b94e
  ecx = SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr /*slot*/,
Packit f0b94e
                                      nullptr);
Packit f0b94e
  if (!ecx) {
Packit f0b94e
    srv = SECFailure;
Packit f0b94e
    goto finish;
Packit f0b94e
  }
Packit f0b94e
  // add password integrity
Packit f0b94e
  srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
Packit f0b94e
  if (srv) goto finish;
Packit f0b94e
  for (i = 0; i < numCerts; i++) {
Packit f0b94e
    nsNSSCertificate *cert = (nsNSSCertificate *)certs[i];
Packit f0b94e
    // get it as a CERTCertificate XXX
Packit f0b94e
    UniqueCERTCertificate nssCert(cert->GetCert());
Packit f0b94e
    if (!nssCert) {
Packit f0b94e
      rv = NS_ERROR_FAILURE;
Packit f0b94e
      goto finish;
Packit f0b94e
    }
Packit f0b94e
    // We can only successfully export certs that are on
Packit f0b94e
    // internal token.  Most, if not all, smart card vendors
Packit f0b94e
    // won't let you extract the private key (in any way
Packit f0b94e
    // shape or form) from the card.  So let's punt if
Packit f0b94e
    // the cert is not in the internal db.
Packit f0b94e
    if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
Packit f0b94e
      // we aren't the internal token, see if the key is extractable.
Packit f0b94e
      SECKEYPrivateKey *privKey =
Packit f0b94e
          PK11_FindKeyByDERCert(nssCert->slot, nssCert.get(), this);
Packit f0b94e
Packit f0b94e
      if (privKey) {
Packit f0b94e
        bool privKeyIsExtractable = isExtractable(privKey);
Packit f0b94e
Packit f0b94e
        SECKEY_DestroyPrivateKey(privKey);
Packit f0b94e
Packit f0b94e
        if (!privKeyIsExtractable) {
Packit f0b94e
          if (!InformedUserNoSmartcardBackup) {
Packit f0b94e
            InformedUserNoSmartcardBackup = true;
Packit f0b94e
            handleError(PIP_PKCS12_NOSMARTCARD_EXPORT);
Packit f0b94e
          }
Packit f0b94e
          continue;
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // XXX this is why, to verify the slot is the same
Packit f0b94e
    // PK11_FindObjectForCert(nssCert, nullptr, slot);
Packit f0b94e
    // create the cert and key safes
Packit f0b94e
    keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
Packit f0b94e
    if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
Packit f0b94e
      certSafe = keySafe;
Packit f0b94e
    } else {
Packit f0b94e
      certSafe = SEC_PKCS12CreatePasswordPrivSafe(
Packit f0b94e
          ecx, &unicodePw, SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
Packit f0b94e
    }
Packit f0b94e
    if (!certSafe || !keySafe) {
Packit f0b94e
      rv = NS_ERROR_FAILURE;
Packit f0b94e
      goto finish;
Packit f0b94e
    }
Packit f0b94e
    // add the cert and key to the blob
Packit f0b94e
    srv = SEC_PKCS12AddCertAndKey(
Packit f0b94e
        ecx, certSafe, nullptr, nssCert.get(),
Packit f0b94e
        CERT_GetDefaultCertDB(),  // XXX
Packit f0b94e
        keySafe, nullptr, true, &unicodePw,
Packit f0b94e
        SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
Packit f0b94e
    if (srv) goto finish;
Packit f0b94e
    // cert was dup'ed, so release it
Packit f0b94e
    ++numCertsExported;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!numCertsExported) goto finish;
Packit f0b94e
Packit f0b94e
  // prepare the instance to write to an export file
Packit f0b94e
  this->mTmpFile = nullptr;
Packit f0b94e
  file->GetPath(filePath);
Packit f0b94e
  // Use the nsCOMPtr var localFileRef so that
Packit f0b94e
  // the reference to the nsIFile we create gets released as soon as
Packit f0b94e
  // we're out of scope, ie when this function exits.
Packit f0b94e
  if (filePath.RFind(".p12", true, -1, 4) < 0) {
Packit f0b94e
    // We're going to add the .p12 extension to the file name just like
Packit f0b94e
    // Communicator used to.  We create a new nsIFile and initialize
Packit f0b94e
    // it with the new patch.
Packit f0b94e
    filePath.AppendLiteral(".p12");
Packit f0b94e
    localFileRef = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv;;
Packit f0b94e
    if (NS_FAILED(rv)) goto finish;
Packit f0b94e
    localFileRef->InitWithPath(filePath);
Packit f0b94e
    file = localFileRef;
Packit f0b94e
  }
Packit f0b94e
  rv = file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0664,
Packit f0b94e
                              &mTmpFile);
Packit f0b94e
  if (NS_FAILED(rv) || !this->mTmpFile) goto finish;
Packit f0b94e
  // encode and write
Packit f0b94e
  srv = SEC_PKCS12Encode(ecx, write_export_file, this);
Packit f0b94e
  if (srv) goto finish;
Packit f0b94e
finish:
Packit f0b94e
  if (NS_FAILED(rv) || srv != SECSuccess) {
Packit f0b94e
    handleError(PIP_PKCS12_BACKUP_FAILED);
Packit f0b94e
  }
Packit f0b94e
  if (ecx) SEC_PKCS12DestroyExportContext(ecx);
Packit f0b94e
  if (this->mTmpFile) {
Packit f0b94e
    PR_Close(this->mTmpFile);
Packit f0b94e
    this->mTmpFile = nullptr;
Packit f0b94e
  }
Packit f0b94e
  SECITEM_ZfreeItem(&unicodePw, false);
Packit f0b94e
  return rv;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
///////////////////////////////////////////////////////////////////////
Packit f0b94e
//
Packit f0b94e
//  private members
Packit f0b94e
//
Packit f0b94e
///////////////////////////////////////////////////////////////////////
Packit f0b94e
Packit f0b94e
// unicodeToItem
Packit f0b94e
//
Packit f0b94e
// For the NSS PKCS#12 library, must convert PRUnichars (shorts) to
Packit f0b94e
// a buffer of octets.  Must handle byte order correctly.
Packit f0b94e
nsresult nsPKCS12Blob::unicodeToItem(const nsString &uni, SECItem *item) {
Packit f0b94e
  uint32_t len = uni.Length() + 1;  // +1 for the null terminator.
Packit f0b94e
  if (!SECITEM_AllocItem(nullptr, item, sizeof(char16_t) * len)) {
Packit f0b94e
    return NS_ERROR_OUT_OF_MEMORY;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // We have to use a cast here because on Windows, uni.get() returns
Packit f0b94e
  // char16ptr_t instead of char16_t*.
Packit f0b94e
  mozilla::NativeEndian::copyAndSwapToBigEndian(
Packit f0b94e
      item->data, static_cast<const char16_t *>(uni.get()), len);
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// newPKCS12FilePassword
Packit f0b94e
//
Packit f0b94e
// Launch a dialog requesting the user for a new PKCS#12 file passowrd.
Packit f0b94e
// Handle user canceled by returning null password (caller must catch).
Packit f0b94e
nsresult nsPKCS12Blob::newPKCS12FilePassword(SECItem *unicodePw) {
Packit f0b94e
  nsresult rv = NS_OK;
Packit f0b94e
  nsAutoString password;
Packit f0b94e
  nsCOMPtr<nsICertificateDialogs> certDialogs;
Packit f0b94e
  rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
Packit f0b94e
                       NS_GET_IID(nsICertificateDialogs),
Packit f0b94e
                       NS_CERTIFICATEDIALOGS_CONTRACTID);
Packit f0b94e
  if (NS_FAILED(rv)) return rv;
Packit f0b94e
  bool pressedOK;
Packit f0b94e
  rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
Packit f0b94e
  if (NS_FAILED(rv) || !pressedOK) return rv;
Packit f0b94e
  return unicodeToItem(password, unicodePw);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// getPKCS12FilePassword
Packit f0b94e
//
Packit f0b94e
// Launch a dialog requesting the user for the password to a PKCS#12 file.
Packit f0b94e
// Handle user canceled by returning null password (caller must catch).
Packit f0b94e
nsresult nsPKCS12Blob::getPKCS12FilePassword(SECItem *unicodePw) {
Packit f0b94e
  nsresult rv = NS_OK;
Packit f0b94e
  nsAutoString password;
Packit f0b94e
  nsCOMPtr<nsICertificateDialogs> certDialogs;
Packit f0b94e
  rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
Packit f0b94e
                       NS_GET_IID(nsICertificateDialogs),
Packit f0b94e
                       NS_CERTIFICATEDIALOGS_CONTRACTID);
Packit f0b94e
  if (NS_FAILED(rv)) return rv;
Packit f0b94e
  bool pressedOK;
Packit f0b94e
  rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
Packit f0b94e
  if (NS_FAILED(rv) || !pressedOK) return rv;
Packit f0b94e
  return unicodeToItem(password, unicodePw);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// inputToDecoder
Packit f0b94e
//
Packit f0b94e
// Given a decoder, read bytes from file and input them to the decoder.
Packit f0b94e
nsresult nsPKCS12Blob::inputToDecoder(SEC_PKCS12DecoderContext *dcx,
Packit f0b94e
                                      nsIFile *file) {
Packit f0b94e
  nsresult rv;
Packit f0b94e
  SECStatus srv;
Packit f0b94e
  uint32_t amount;
Packit f0b94e
  char buf[PIP_PKCS12_BUFFER_SIZE];
Packit f0b94e
Packit f0b94e
  nsCOMPtr<nsIInputStream> fileStream;
Packit f0b94e
  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file);
Packit f0b94e
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  while (true) {
Packit f0b94e
    rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount);
Packit f0b94e
    if (NS_FAILED(rv)) {
Packit f0b94e
      return rv;
Packit f0b94e
    }
Packit f0b94e
    // feed the file data into the decoder
Packit f0b94e
    srv = SEC_PKCS12DecoderUpdate(dcx, (unsigned char *)buf, amount);
Packit f0b94e
    if (srv) {
Packit f0b94e
      // don't allow the close call to overwrite our precious error code
Packit f0b94e
      int pr_err = PORT_GetError();
Packit f0b94e
      PORT_SetError(pr_err);
Packit f0b94e
      return NS_ERROR_ABORT;
Packit f0b94e
    }
Packit f0b94e
    if (amount < PIP_PKCS12_BUFFER_SIZE) break;
Packit f0b94e
  }
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// nickname_collision
Packit f0b94e
// what to do when the nickname collides with one already in the db.
Packit f0b94e
// TODO: not handled, throw a dialog allowing the nick to be changed?
Packit f0b94e
SECItem *nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel,
Packit f0b94e
                                          void *wincx) {
Packit f0b94e
  *cancel = false;
Packit f0b94e
  int count = 1;
Packit f0b94e
  nsCString nickname;
Packit f0b94e
  nsAutoString nickFromProp;
Packit f0b94e
  nsresult rv = GetPIPNSSBundleString("P12DefaultNickname", nickFromProp);
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return nullptr;
Packit f0b94e
  }
Packit f0b94e
  NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp);
Packit f0b94e
  // The user is trying to import a PKCS#12 file that doesn't have the
Packit f0b94e
  // attribute we use to set the nickname.  So in order to reduce the
Packit f0b94e
  // number of interactions we require with the user, we'll build a nickname
Packit f0b94e
  // for the user.  The nickname isn't prominently displayed in the UI,
Packit f0b94e
  // so it's OK if we generate one on our own here.
Packit f0b94e
  //   XXX If the NSS API were smarter and actually passed a pointer to
Packit f0b94e
  //       the CERTCertificate* we're importing we could actually just
Packit f0b94e
  //       call default_nickname (which is what the issuance code path
Packit f0b94e
  //       does) and come up with a reasonable nickname.  Alas, the NSS
Packit f0b94e
  //       API limits our ability to produce a useful nickname without
Packit f0b94e
  //       bugging the user.  :(
Packit f0b94e
  while (1) {
Packit f0b94e
    // If we've gotten this far, that means there isn't a certificate
Packit f0b94e
    // in the database that has the same subject name as the cert we're
Packit f0b94e
    // trying to import.  So we need to come up with a "nickname" to
Packit f0b94e
    // satisfy the NSS requirement or fail in trying to import.
Packit f0b94e
    // Basically we use a default nickname from a properties file and
Packit f0b94e
    // see if a certificate exists with that nickname.  If there isn't, then
Packit f0b94e
    // create update the count by one and append the string '#1' Or
Packit f0b94e
    // whatever the count currently is, and look for a cert with
Packit f0b94e
    // that nickname.  Keep updating the count until we find a nickname
Packit f0b94e
    // without a corresponding cert.
Packit f0b94e
    //  XXX If a user imports *many* certs without the 'friendly name'
Packit f0b94e
    //      attribute, then this may take a long time.  :(
Packit f0b94e
    nickname = nickFromPropC;
Packit f0b94e
    if (count > 1) {
Packit f0b94e
      nickname.AppendPrintf(" #%d", count);
Packit f0b94e
    }
Packit f0b94e
    UniqueCERTCertificate cert(
Packit f0b94e
        CERT_FindCertByNickname(CERT_GetDefaultCertDB(), nickname.get()));
Packit f0b94e
    if (!cert) {
Packit f0b94e
      break;
Packit f0b94e
    }
Packit f0b94e
    count++;
Packit f0b94e
  }
Packit f0b94e
  SECItem *newNick = new SECItem;
Packit f0b94e
  if (!newNick) return nullptr;
Packit f0b94e
Packit f0b94e
  newNick->type = siAsciiString;
Packit f0b94e
  newNick->data = (unsigned char *)strdup(nickname.get());
Packit f0b94e
  newNick->len = strlen((char *)newNick->data);
Packit f0b94e
  return newNick;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// write_export_file
Packit f0b94e
// write bytes to the exported PKCS#12 file
Packit f0b94e
void nsPKCS12Blob::write_export_file(void *arg, const char *buf,
Packit f0b94e
                                     unsigned long len) {
Packit f0b94e
  nsPKCS12Blob *cx = (nsPKCS12Blob *)arg;
Packit f0b94e
  PR_Write(cx->mTmpFile, buf, len);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// pip_ucs2_ascii_conversion_fn
Packit f0b94e
// required to be set by NSS (to do PKCS#12), but since we've already got
Packit f0b94e
// unicode make this a no-op.
Packit f0b94e
PRBool pip_ucs2_ascii_conversion_fn(PRBool toUnicode, unsigned char *inBuf,
Packit f0b94e
                                    unsigned int inBufLen,
Packit f0b94e
                                    unsigned char *outBuf,
Packit f0b94e
                                    unsigned int maxOutBufLen,
Packit f0b94e
                                    unsigned int *outBufLen, PRBool swapBytes) {
Packit f0b94e
  // do a no-op, since I've already got unicode.  Hah!
Packit f0b94e
  *outBufLen = inBufLen;
Packit f0b94e
  memcpy(outBuf, inBuf, inBufLen);
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsPKCS12Blob::handleError(int myerr) {
Packit f0b94e
  MOZ_ASSERT(NS_IsMainThread());
Packit f0b94e
  if (!NS_IsMainThread()) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  int prerr = PORT_GetError();
Packit f0b94e
  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PKCS12: NSS/NSPR error(%d)", prerr));
Packit f0b94e
  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PKCS12: I called(%d)", myerr));
Packit f0b94e
Packit f0b94e
  const char *msgID = nullptr;
Packit f0b94e
Packit f0b94e
  switch (myerr) {
Packit f0b94e
    case PIP_PKCS12_USER_CANCELED:
Packit f0b94e
      return; /* Just ignore it for now */
Packit f0b94e
    case PIP_PKCS12_NOSMARTCARD_EXPORT:
Packit f0b94e
      msgID = "PKCS12InfoNoSmartcardBackup";
Packit f0b94e
      break;
Packit f0b94e
    case PIP_PKCS12_RESTORE_FAILED:
Packit f0b94e
      msgID = "PKCS12UnknownErrRestore";
Packit f0b94e
      break;
Packit f0b94e
    case PIP_PKCS12_BACKUP_FAILED:
Packit f0b94e
      msgID = "PKCS12UnknownErrBackup";
Packit f0b94e
      break;
Packit f0b94e
    case PIP_PKCS12_NSS_ERROR:
Packit f0b94e
      switch (prerr) {
Packit f0b94e
        // The following errors have the potential to be "handled", by asking
Packit f0b94e
        // the user (via a dialog) whether s/he wishes to continue
Packit f0b94e
        case 0:
Packit f0b94e
          break;
Packit f0b94e
        case SEC_ERROR_PKCS12_CERT_COLLISION:
Packit f0b94e
          /* pop a dialog saying the cert is already in the database */
Packit f0b94e
          /* ask to keep going?  what happens if one collision but others ok? */
Packit f0b94e
          // The following errors cannot be "handled", notify the user (via an
Packit f0b94e
          // alert) that the operation failed.
Packit f0b94e
        case SEC_ERROR_BAD_PASSWORD:
Packit f0b94e
          msgID = "PK11BadPassword";
Packit f0b94e
          break;
Packit f0b94e
Packit f0b94e
        case SEC_ERROR_BAD_DER:
Packit f0b94e
        case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
Packit f0b94e
        case SEC_ERROR_PKCS12_INVALID_MAC:
Packit f0b94e
          msgID = "PKCS12DecodeErr";
Packit f0b94e
          break;
Packit f0b94e
Packit f0b94e
        case SEC_ERROR_PKCS12_DUPLICATE_DATA:
Packit f0b94e
          msgID = "PKCS12DupData";
Packit f0b94e
          break;
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!msgID) msgID = "PKCS12UnknownErr";
Packit f0b94e
Packit f0b94e
  nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
Packit f0b94e
  if (!wwatch) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
  nsCOMPtr<nsIPrompt> prompter;
Packit f0b94e
  if (NS_FAILED(wwatch->GetNewPrompter(nullptr, getter_AddRefs(prompter)))) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
  nsAutoString message;
Packit f0b94e
  if (NS_FAILED(GetPIPNSSBundleString(msgID, message))) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  Unused << prompter->Alert(nullptr, message.get());
Packit f0b94e
}