summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pkcs12/p12exp.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/pkcs12/p12exp.c')
-rw-r--r--security/nss/lib/pkcs12/p12exp.c1374
1 files changed, 1374 insertions, 0 deletions
diff --git a/security/nss/lib/pkcs12/p12exp.c b/security/nss/lib/pkcs12/p12exp.c
new file mode 100644
index 0000000000..b9cb076566
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12exp.c
@@ -0,0 +1,1374 @@
+/* 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/. */
+
+#include "plarena.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "seccomon.h"
+#include "secport.h"
+#include "cert.h"
+#include "pkcs12.h"
+#include "p12local.h"
+#include "secpkcs7.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "p12plcy.h"
+
+/* release the memory taken up by the list of nicknames */
+static void
+sec_pkcs12_destroy_nickname_list(SECItem **nicknames)
+{
+ int i = 0;
+
+ if (nicknames == NULL) {
+ return;
+ }
+
+ while (nicknames[i] != NULL) {
+ SECITEM_FreeItem(nicknames[i], PR_FALSE);
+ i++;
+ }
+
+ PORT_Free(nicknames);
+}
+
+/* release the memory taken up by the list of certificates */
+static void
+sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs)
+{
+ int i = 0;
+
+ if (ref_certs == NULL) {
+ return;
+ }
+
+ while (ref_certs[i] != NULL) {
+ CERT_DestroyCertificate(ref_certs[i]);
+ i++;
+ }
+}
+
+static void
+sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag)
+{
+ int j = 0;
+ j = 0;
+ while (certBag->certAndCRLs[j] != NULL) {
+ SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID);
+ if (certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) {
+ SEC_PKCS12X509CertCRL *x509;
+ x509 = certBag->certAndCRLs[j]->value.x509;
+ SEC_PKCS7DestroyContentInfo(&x509->certOrCRL);
+ }
+ j++;
+ }
+}
+
+/* destroy all content infos since they were not allocated in common
+ * pool
+ */
+static void
+sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ int i, j;
+
+ if ((safe != NULL) && (safe->contents != NULL)) {
+ i = 0;
+ while (safe->contents[i] != NULL) {
+ SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
+ if (bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
+ SEC_PKCS12CertAndCRLBag *certBag;
+ certBag = safe->contents[i]->safeContent.certAndCRLBag;
+ sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
+ }
+ i++;
+ }
+ }
+
+ if ((baggage != NULL) && (baggage->bags != NULL)) {
+ i = 0;
+ while (baggage->bags[i] != NULL) {
+ if (baggage->bags[i]->unencSecrets != NULL) {
+ j = 0;
+ while (baggage->bags[i]->unencSecrets[j] != NULL) {
+ SECOidTag bagType;
+ bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType);
+ if (bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
+ SEC_PKCS12CertAndCRLBag *certBag;
+ certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag;
+ sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
+ }
+ j++;
+ }
+ }
+ i++;
+ }
+ }
+}
+
+/* convert the nickname list from a NULL termincated Char list
+ * to a NULL terminated SECItem list
+ */
+static SECItem **
+sec_pkcs12_convert_nickname_list(char **nicknames)
+{
+ SECItem **nicks;
+ int i, j;
+ PRBool error = PR_FALSE;
+
+ if (nicknames == NULL) {
+ return NULL;
+ }
+
+ i = j = 0;
+ while (nicknames[i] != NULL) {
+ i++;
+ }
+
+ /* allocate the space and copy the data */
+ nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1));
+ if (nicks != NULL) {
+ for (j = 0; ((j < i) && (error == PR_FALSE)); j++) {
+ nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (nicks[j] != NULL) {
+ nicks[j]->data =
+ (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j]) + 1);
+ if (nicks[j]->data != NULL) {
+ nicks[j]->len = PORT_Strlen(nicknames[j]);
+ PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len);
+ nicks[j]->data[nicks[j]->len] = 0;
+ } else {
+ error = PR_TRUE;
+ }
+ } else {
+ error = PR_TRUE;
+ }
+ }
+ }
+
+ if (error == PR_TRUE) {
+ for (i = 0; i < j; i++) {
+ SECITEM_FreeItem(nicks[i], PR_TRUE);
+ }
+ PORT_Free(nicks);
+ nicks = NULL;
+ }
+
+ return nicks;
+}
+
+/* package the certificate add_cert into PKCS12 structures,
+ * retrieve the certificate chain for the cert and return
+ * the packaged contents.
+ * poolp -- common memory pool;
+ * add_cert -- certificate to package up
+ * nickname for the certificate
+ * a return of NULL indicates an error
+ */
+static SEC_PKCS12CertAndCRL *
+sec_pkcs12_get_cert(PLArenaPool *poolp,
+ CERTCertificate *add_cert,
+ SECItem *nickname)
+{
+ SEC_PKCS12CertAndCRL *cert;
+ SEC_PKCS7ContentInfo *cinfo;
+ SGNDigestInfo *t_di;
+ void *mark;
+ SECStatus rv;
+
+ if ((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) {
+ return NULL;
+ }
+ mark = PORT_ArenaMark(poolp);
+
+ cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG);
+ if (cert != NULL) {
+
+ /* copy the nickname */
+ rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ cert = NULL;
+ } else {
+
+ /* package the certificate and cert chain into a NULL signer
+ * PKCS 7 SignedData content Info and prepare it for encoding
+ * since we cannot use DER_ANY_TEMPLATE
+ */
+ cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL);
+ rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL);
+
+ /* thumbprint the certificate */
+ if ((cinfo != NULL) && (rv == SECSuccess)) {
+ PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo));
+ t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert);
+ if (t_di != NULL) {
+ /* test */
+ rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint,
+ t_di);
+ if (rv != SECSuccess) {
+ cert = NULL;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ SGN_DestroyDigestInfo(t_di);
+ } else
+ cert = NULL;
+ }
+ }
+ }
+
+ if (cert == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ } else {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return cert;
+}
+
+/* package the private key associated with the certificate and
+ * return the appropriate PKCS 12 structure
+ * poolp common memory pool
+ * nickname key nickname
+ * cert -- cert to look up
+ * wincx -- window handle
+ * an error is indicated by a return of NULL
+ */
+static SEC_PKCS12PrivateKey *
+sec_pkcs12_get_private_key(PLArenaPool *poolp,
+ SECItem *nickname,
+ CERTCertificate *cert,
+ void *wincx)
+{
+ SECKEYPrivateKeyInfo *pki;
+ SEC_PKCS12PrivateKey *pk;
+ SECStatus rv;
+ void *mark;
+
+ if ((poolp == NULL) || (nickname == NULL)) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* retrieve key from the data base */
+ pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx);
+ if (pki == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ return NULL;
+ }
+
+ pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12PrivateKey));
+ if (pk != NULL) {
+ rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData);
+
+ if (rv == SECSuccess) {
+ /* copy the key into poolp memory space */
+ rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ pk = NULL;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+
+ /* destroy private key, zeroing out data */
+ SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
+ if (pk == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ } else {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return pk;
+}
+
+/* get a shrouded key item associated with a certificate
+ * return the appropriate PKCS 12 structure
+ * poolp common memory pool
+ * nickname key nickname
+ * cert -- cert to look up
+ * wincx -- window handle
+ * an error is indicated by a return of NULL
+ */
+static SEC_PKCS12ESPVKItem *
+sec_pkcs12_get_shrouded_key(PLArenaPool *poolp,
+ SECItem *nickname,
+ CERTCertificate *cert,
+ SECOidTag algorithm,
+ SECItem *pwitem,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ SECKEYEncryptedPrivateKeyInfo *epki;
+ SEC_PKCS12ESPVKItem *pk;
+ void *mark;
+ SECStatus rv;
+ PK11SlotInfo *slot = NULL;
+ PRBool swapUnicodeBytes = PR_FALSE;
+
+#ifdef IS_LITTLE_ENDIAN
+ swapUnicodeBytes = PR_TRUE;
+#endif
+
+ if ((poolp == NULL) || (nickname == NULL))
+ return NULL;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* use internal key slot */
+ slot = PK11_GetInternalKeySlot();
+
+ /* retrieve encrypted prviate key */
+ epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem,
+ nickname, cert, 1, 0, NULL);
+ PK11_FreeSlot(slot);
+ if (epki == NULL) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+
+ /* create a private key and store the data into the poolp memory space */
+ pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING);
+ if (pk != NULL) {
+ rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData);
+ rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname);
+ pk->espvkCipherText.pkcs8KeyShroud =
+ (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ if ((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) {
+ rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp,
+ pk->espvkCipherText.pkcs8KeyShroud, epki);
+ if (rv == SECSuccess) {
+ rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname,
+ PR_TRUE, swapUnicodeBytes);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ pk = NULL;
+ }
+ }
+
+ SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
+ if (pk == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ } else {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return pk;
+}
+
+/* add a thumbprint to a private key associated certs list
+ * pvk is the area where the list is stored
+ * thumb is the thumbprint to copy
+ * a return of SECFailure indicates an error
+ */
+static SECStatus
+sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk,
+ SGNDigestInfo *thumb)
+{
+ SGNDigestInfo **thumb_list = NULL;
+ int nthumbs, size;
+ void *mark, *dummy;
+ SECStatus rv = SECFailure;
+
+ if ((pvk == NULL) || (thumb == NULL)) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(pvk->poolp);
+
+ thumb_list = pvk->assocCerts;
+ nthumbs = pvk->nThumbs;
+
+ /* allocate list space needed -- either growing or allocating
+ * list must be NULL terminated
+ */
+ size = sizeof(SGNDigestInfo *);
+ dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)),
+ (size * (nthumbs + 2)));
+ thumb_list = dummy;
+ if (dummy != NULL) {
+ thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp,
+ sizeof(SGNDigestInfo));
+ if (thumb_list[nthumbs] != NULL) {
+ SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb);
+ nthumbs += 1;
+ thumb_list[nthumbs] = 0;
+ } else {
+ dummy = NULL;
+ }
+ }
+
+ if (dummy == NULL) {
+ PORT_ArenaRelease(pvk->poolp, mark);
+ return SECFailure;
+ }
+
+ pvk->assocCerts = thumb_list;
+ pvk->nThumbs = nthumbs;
+
+ PORT_ArenaUnmark(pvk->poolp, mark);
+ return SECSuccess;
+}
+
+/* search the list of shrouded keys in the baggage for the desired
+ * name. return a pointer to the item. a return of NULL indicates
+ * that no match was present or that an error occurred.
+ */
+static SEC_PKCS12ESPVKItem *
+sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage,
+ SECItem *name)
+{
+ PRBool found = PR_FALSE;
+ SEC_PKCS12ESPVKItem *espvk = NULL;
+ int i, j;
+ SECComparison rv = SECEqual;
+ SECItem *t_name;
+ SEC_PKCS12BaggageItem *bag;
+
+ if ((luggage == NULL) || (name == NULL)) {
+ return NULL;
+ }
+
+ i = 0;
+ while ((found == PR_FALSE) && (i < luggage->luggage_size)) {
+ j = 0;
+ bag = luggage->bags[i];
+ while ((found == PR_FALSE) && (j < bag->nEspvks)) {
+ espvk = bag->espvks[j];
+ if (espvk->poolp == NULL) {
+ espvk->poolp = luggage->poolp;
+ }
+ t_name = SECITEM_DupItem(&espvk->espvkData.nickname);
+ if (t_name != NULL) {
+ rv = SECITEM_CompareItem(name, t_name);
+ if (rv == SECEqual) {
+ found = PR_TRUE;
+ }
+ SECITEM_FreeItem(t_name, PR_TRUE);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ j++;
+ }
+ i++;
+ }
+
+ if (found != PR_TRUE) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
+ return NULL;
+ }
+
+ return espvk;
+}
+
+/* locates a certificate and copies the thumbprint to the
+ * appropriate private key
+ */
+static SECStatus
+sec_pkcs12_propagate_thumbprints(SECItem **nicknames,
+ CERTCertificate **ref_certs,
+ SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ SEC_PKCS12CertAndCRL *cert;
+ SEC_PKCS12PrivateKey *key;
+ SEC_PKCS12ESPVKItem *espvk;
+ int i;
+ PRBool error = PR_FALSE;
+ SECStatus rv = SECFailure;
+
+ if ((nicknames == NULL) || (safe == NULL)) {
+ return SECFailure;
+ }
+
+ i = 0;
+ while ((nicknames[i] != NULL) && (error == PR_FALSE)) {
+ /* process all certs */
+ cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
+ SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID,
+ nicknames[i], NULL);
+ if (cert != NULL) {
+ /* locate key and copy thumbprint */
+ key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage,
+ SEC_OID_PKCS12_KEY_BAG_ID,
+ nicknames[i], NULL);
+ if (key != NULL) {
+ key->pvkData.poolp = key->poolp;
+ rv = sec_pkcs12_add_thumbprint(&key->pvkData,
+ &cert->value.x509->thumbprint);
+ if (rv == SECFailure)
+ error = PR_TRUE; /* XXX Set error? */
+ }
+
+ /* look in the baggage as well...*/
+ if ((baggage != NULL) && (error == PR_FALSE)) {
+ espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]);
+ if (espvk != NULL) {
+ espvk->espvkData.poolp = espvk->poolp;
+ rv = sec_pkcs12_add_thumbprint(&espvk->espvkData,
+ &cert->value.x509->thumbprint);
+ if (rv == SECFailure)
+ error = PR_TRUE; /* XXX Set error? */
+ }
+ }
+ }
+ i++;
+ }
+
+ if (error == PR_TRUE) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* append a safe bag to the end of the safe contents list */
+SECStatus
+sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12SafeBag *bag)
+{
+ int size;
+ void *mark = NULL, *dummy = NULL;
+
+ if ((bag == NULL) || (safe == NULL))
+ return SECFailure;
+
+ mark = PORT_ArenaMark(safe->poolp);
+
+ size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *));
+
+ if (safe->safe_size > 0) {
+ dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp,
+ safe->contents,
+ size,
+ (size + sizeof(SEC_PKCS12SafeBag *)));
+ safe->contents = dummy;
+ } else {
+ safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp,
+ (2 * sizeof(SEC_PKCS12SafeBag *)));
+ dummy = safe->contents;
+ }
+
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ safe->contents[safe->safe_size] = bag;
+ safe->safe_size++;
+ safe->contents[safe->safe_size] = NULL;
+
+ PORT_ArenaUnmark(safe->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(safe->poolp, mark);
+ return SECFailure;
+}
+
+/* append a certificate onto the end of a cert bag */
+static SECStatus
+sec_pkcs12_append_cert_to_bag(PLArenaPool *arena,
+ SEC_PKCS12SafeBag *safebag,
+ CERTCertificate *cert,
+ SECItem *nickname)
+{
+ int size;
+ void *dummy = NULL, *mark = NULL;
+ SEC_PKCS12CertAndCRL *p12cert;
+ SEC_PKCS12CertAndCRLBag *bag;
+
+ if ((arena == NULL) || (safebag == NULL) ||
+ (cert == NULL) || (nickname == NULL)) {
+ return SECFailure;
+ }
+
+ bag = safebag->safeContent.certAndCRLBag;
+ if (bag == NULL) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(arena);
+
+ p12cert = sec_pkcs12_get_cert(arena, cert, nickname);
+ if (p12cert == NULL) {
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+ }
+
+ size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *);
+ if (bag->bag_size > 0) {
+ dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp,
+ bag->certAndCRLs,
+ size,
+ size + sizeof(SEC_PKCS12CertAndCRL *));
+ bag->certAndCRLs = dummy;
+ } else {
+ bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp,
+ (2 * sizeof(SEC_PKCS12CertAndCRL *)));
+ dummy = bag->certAndCRLs;
+ }
+
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->certAndCRLs[bag->bag_size] = p12cert;
+ bag->bag_size++;
+ bag->certAndCRLs[bag->bag_size] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* append a key onto the end of a list of keys in a key bag */
+SECStatus
+sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag,
+ SEC_PKCS12PrivateKey *pk)
+{
+ void *mark, *dummy;
+ SEC_PKCS12PrivateKeyBag *bag;
+ int size;
+
+ if ((safebag == NULL) || (pk == NULL))
+ return SECFailure;
+
+ bag = safebag->safeContent.keyBag;
+ if (bag == NULL) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(bag->poolp);
+
+ size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *));
+
+ if (bag->bag_size > 0) {
+ dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp,
+ bag->privateKeys,
+ size,
+ size + sizeof(SEC_PKCS12PrivateKey *));
+ bag->privateKeys = dummy;
+ } else {
+ bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp,
+ (2 * sizeof(SEC_PKCS12PrivateKey *)));
+ dummy = bag->privateKeys;
+ }
+
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->privateKeys[bag->bag_size] = pk;
+ bag->bag_size++;
+ bag->privateKeys[bag->bag_size] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ /* XXX Free memory? */
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* append a safe bag to the baggage area */
+static SECStatus
+sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag,
+ SEC_PKCS12SafeBag *u_bag)
+{
+ int size;
+ void *mark = NULL, *dummy = NULL;
+
+ if ((bag == NULL) || (u_bag == NULL))
+ return SECFailure;
+
+ mark = PORT_ArenaMark(bag->poolp);
+
+ /* dump things into the first bag */
+ size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *);
+ dummy = PORT_ArenaGrow(bag->poolp,
+ bag->unencSecrets, size,
+ size + sizeof(SEC_PKCS12SafeBag *));
+ bag->unencSecrets = dummy;
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->unencSecrets[bag->nSecrets] = u_bag;
+ bag->nSecrets++;
+ bag->unencSecrets[bag->nSecrets] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* gather up all certificates and keys and package them up
+ * in the safe, baggage, or both.
+ * nicknames is the list of nicknames and corresponding certs in ref_certs
+ * ref_certs a null terminated list of certificates
+ * rSafe, rBaggage -- return areas for safe and baggage
+ * shroud_keys -- store keys externally
+ * pwitem -- password for computing integrity mac and encrypting contents
+ * wincx -- window handle
+ *
+ * if a failure occurs, an error is set and SECFailure returned.
+ */
+static SECStatus
+sec_pkcs12_package_certs_and_keys(SECItem **nicknames,
+ CERTCertificate **ref_certs,
+ PRBool unencryptedCerts,
+ SEC_PKCS12SafeContents **rSafe,
+ SEC_PKCS12Baggage **rBaggage,
+ PRBool shroud_keys,
+ SECOidTag shroud_alg,
+ SECItem *pwitem,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ PLArenaPool *permArena;
+ SEC_PKCS12SafeContents *safe = NULL;
+ SEC_PKCS12Baggage *baggage = NULL;
+
+ SECStatus rv = SECFailure;
+ PRBool problem = PR_FALSE;
+
+ SEC_PKCS12ESPVKItem *espvk = NULL;
+ SEC_PKCS12PrivateKey *pk = NULL;
+ CERTCertificate *add_cert = NULL;
+ SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL;
+ SEC_PKCS12BaggageItem *external_bag = NULL;
+ int ncerts = 0, nkeys = 0;
+ int i;
+
+ if ((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) {
+ return SECFailure;
+ }
+
+ *rBaggage = baggage;
+ *rSafe = safe;
+
+ permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (permArena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ /* allocate structures */
+ safe = sec_pkcs12_create_safe_contents(permArena);
+ if (safe == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ certbag = sec_pkcs12_create_safe_bag(permArena,
+ SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID);
+ if (certbag == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ if (shroud_keys != PR_TRUE) {
+ keybag = sec_pkcs12_create_safe_bag(permArena,
+ SEC_OID_PKCS12_KEY_BAG_ID);
+ if (keybag == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+ if ((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
+ baggage = sec_pkcs12_create_baggage(permArena);
+ if (baggage == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ external_bag = sec_pkcs12_create_external_bag(baggage);
+ }
+
+ /* package keys and certs */
+ i = 0;
+ while ((nicknames[i] != NULL) && (problem == PR_FALSE)) {
+ if (ref_certs[i] != NULL) {
+ /* append cert to bag o certs */
+ rv = sec_pkcs12_append_cert_to_bag(permArena, certbag,
+ ref_certs[i],
+ nicknames[i]);
+ if (rv == SECFailure) {
+ problem = PR_FALSE;
+ } else {
+ ncerts++;
+ }
+
+ if (rv == SECSuccess) {
+ /* package up them keys */
+ if (shroud_keys == PR_TRUE) {
+ espvk = sec_pkcs12_get_shrouded_key(permArena,
+ nicknames[i],
+ ref_certs[i],
+ shroud_alg,
+ pwitem, unicodeFn,
+ wincx);
+ if (espvk != NULL) {
+ rv = sec_pkcs12_append_shrouded_key(external_bag, espvk);
+ SECITEM_CopyItem(permArena, &espvk->derCert,
+ &ref_certs[i]->derCert);
+ } else {
+ rv = SECFailure;
+ }
+ } else {
+ pk = sec_pkcs12_get_private_key(permArena, nicknames[i],
+ ref_certs[i], wincx);
+ if (pk != NULL) {
+ rv = sec_pkcs12_append_key_to_bag(keybag, pk);
+ SECITEM_CopyItem(permArena, &espvk->derCert,
+ &ref_certs[i]->derCert);
+ } else {
+ rv = SECFailure;
+ }
+ }
+
+ if (rv == SECFailure) {
+ problem = PR_TRUE;
+ } else {
+ nkeys++;
+ }
+ }
+ } else {
+ /* handle only keys here ? */
+ problem = PR_TRUE;
+ }
+ i++;
+ }
+
+/* let success fall through */
+loser:
+ if (problem == PR_FALSE) {
+ /* if we have certs, we want to append the cert bag to the
+ * appropriate area
+ */
+ if (ncerts > 0) {
+ if (unencryptedCerts != PR_TRUE) {
+ rv = sec_pkcs12_append_safe_bag(safe, certbag);
+ } else {
+ rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag);
+ }
+ } else {
+ rv = SECSuccess;
+ }
+
+ /* append key bag, if they are stored in safe contents */
+ if ((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) {
+ rv = sec_pkcs12_append_safe_bag(safe, keybag);
+ }
+ } else {
+ rv = SECFailure;
+ }
+
+ /* if baggage not used, NULLify it */
+ if ((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
+ if (((unencryptedCerts == PR_TRUE) && (ncerts == 0)) &&
+ ((shroud_keys == PR_TRUE) && (nkeys == 0)))
+ baggage = NULL;
+ } else {
+ baggage = NULL;
+ }
+
+ if ((problem == PR_TRUE) || (rv == SECFailure)) {
+ PORT_FreeArena(permArena, PR_TRUE);
+ rv = SECFailure;
+ baggage = NULL;
+ safe = NULL;
+ }
+
+ *rBaggage = baggage;
+ *rSafe = safe;
+
+ return rv;
+}
+
+/* DER encode the safe contents and return a SECItem. if an error
+ * occurs, NULL is returned.
+ */
+static SECItem *
+sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe)
+{
+ SECItem *dsafe = NULL, *tsafe;
+ void *dummy = NULL;
+ PLArenaPool *arena;
+
+ if (safe == NULL) {
+ return NULL;
+ }
+
+ /* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE);
+ if(rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }*/
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem));
+ if (tsafe != NULL) {
+ dummy = SEC_ASN1EncodeItem(arena, tsafe, safe,
+ SEC_PKCS12SafeContentsTemplate);
+ if (dummy != NULL) {
+ dsafe = SECITEM_DupItem(tsafe);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+
+ PORT_FreeArena(arena, PR_TRUE);
+
+ return dsafe;
+}
+
+/* prepare the authenicated safe for encoding and encode it.
+ * baggage is copied to the appropriate area, safe is encoded and
+ * encrypted. the version and transport mode are set on the asafe.
+ * the whole ball of wax is then der encoded and packaged up into
+ * data content info
+ * safe -- container of certs and keys, is encrypted.
+ * baggage -- container of certs and keys, keys assumed to be encrypted by
+ * another method, certs are in the clear
+ * algorithm -- algorithm by which to encrypt safe
+ * pwitem -- password for encryption
+ * wincx - window handle
+ *
+ * return of NULL is an error condition.
+ */
+static SEC_PKCS7ContentInfo *
+sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage,
+ SECOidTag algorithm,
+ SECItem *pwitem,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ SECItem *src = NULL, *dest = NULL, *psalt = NULL;
+ PLArenaPool *poolp;
+ SEC_PKCS12AuthenticatedSafe *asafe;
+ SEC_PKCS7ContentInfo *safe_cinfo = NULL;
+ SEC_PKCS7ContentInfo *asafe_cinfo = NULL;
+ void *dummy;
+ SECStatus rv = SECSuccess;
+ PRBool swapUnicodeBytes = PR_FALSE;
+
+#ifdef IS_LITTLE_ENDIAN
+ swapUnicodeBytes = PR_TRUE;
+#endif
+
+ if (((safe != NULL) && (pwitem == NULL)) && (baggage == NULL))
+ return NULL;
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (poolp == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ /* prepare authenticated safe for encode */
+ asafe = sec_pkcs12_new_asafe(poolp);
+ if (asafe != NULL) {
+
+ /* set version */
+ dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version,
+ SEC_PKCS12_PFX_VERSION);
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* generate the privacy salt used to create virtual pwd */
+ psalt = sec_pkcs12_generate_salt();
+ if (psalt != NULL) {
+ rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt,
+ psalt);
+ if (rv == SECSuccess) {
+ asafe->privacySalt.len *= 8;
+ } else {
+ SECITEM_ZfreeItem(psalt, PR_TRUE);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ }
+
+ if ((psalt == NULL) || (rv == SECFailure)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* package up safe contents */
+ if (safe != NULL) {
+ safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx);
+ if ((safe_cinfo != NULL) && (safe->safe_size > 0)) {
+ /* encode the safe and encrypt the contents of the
+ * content info
+ */
+ src = sec_pkcs12_encode_safe_contents(safe);
+
+ if (src != NULL) {
+ rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len);
+ SECITEM_ZfreeItem(src, PR_TRUE);
+ if (rv == SECSuccess) {
+ SECItem *vpwd;
+ vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt,
+ unicodeFn, swapUnicodeBytes);
+ if (vpwd != NULL) {
+ rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo,
+ vpwd, wincx);
+ SECITEM_ZfreeItem(vpwd, PR_TRUE);
+ } else {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ rv = SECFailure;
+ }
+ } else if (safe->safe_size > 0) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ } else {
+ /* case where there is NULL content in the safe contents */
+ rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0);
+ if (rv != SECFailure) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo(safe_cinfo);
+ safe_cinfo = NULL;
+ goto loser;
+ }
+
+ asafe->safe = safe_cinfo;
+ /*
+ PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo));
+ */
+ }
+
+ /* copy the baggage to the authenticated safe baggage if present */
+ if (baggage != NULL) {
+ PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage));
+ }
+
+ /* encode authenticated safe and store it in a Data content info */
+ dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
+ if (dest != NULL) {
+ dummy = SEC_ASN1EncodeItem(poolp, dest, asafe,
+ SEC_PKCS12AuthenticatedSafeTemplate);
+ if (dummy != NULL) {
+ asafe_cinfo = SEC_PKCS7CreateData();
+ if (asafe_cinfo != NULL) {
+ rv = SEC_PKCS7SetContent(asafe_cinfo,
+ (char *)dest->data,
+ dest->len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ SEC_PKCS7DestroyContentInfo(asafe_cinfo);
+ asafe_cinfo = NULL;
+ }
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ }
+ }
+ }
+
+loser:
+ PORT_FreeArena(poolp, PR_TRUE);
+ if (safe_cinfo != NULL) {
+ SEC_PKCS7DestroyContentInfo(safe_cinfo);
+ }
+ if (psalt != NULL) {
+ SECITEM_ZfreeItem(psalt, PR_TRUE);
+ }
+
+ if (rv == SECFailure) {
+ return NULL;
+ }
+
+ return asafe_cinfo;
+}
+
+/* generates the PFX and computes the mac on the authenticated safe
+ * NULL implies an error
+ */
+static SEC_PKCS12PFXItem *
+sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo,
+ PRBool do_mac,
+ SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn)
+{
+ SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL;
+ SEC_PKCS12PFXItem *pfx;
+ SECStatus rv = SECFailure;
+ SGNDigestInfo *di;
+ SECItem *vpwd;
+ PRBool swapUnicodeBytes = PR_FALSE;
+
+#ifdef IS_LITTLE_ENDIAN
+ swapUnicodeBytes = PR_TRUE;
+#endif
+
+ if ((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) {
+ return NULL;
+ }
+
+ /* allocate new pfx structure */
+ pfx = sec_pkcs12_new_pfx();
+ if (pfx == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo));
+ if (do_mac == PR_TRUE) {
+
+ /* salt for computing mac */
+ salt = sec_pkcs12_generate_salt();
+ if (salt != NULL) {
+ rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt);
+ pfx->macData.macSalt.len *= 8;
+
+ vpwd = sec_pkcs12_create_virtual_password(pwitem, salt,
+ unicodeFn, swapUnicodeBytes);
+ if (vpwd == NULL) {
+ rv = SECFailure;
+ key = NULL;
+ } else {
+ key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1,
+ salt, vpwd);
+ SECITEM_ZfreeItem(vpwd, PR_TRUE);
+ }
+
+ if ((key != NULL) && (rv == SECSuccess)) {
+ dest = SEC_PKCS7GetContent(cinfo);
+ if (dest != NULL) {
+
+ /* compute mac on data -- for password integrity mode */
+ mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE);
+ if (mac != NULL) {
+ di = SGN_CreateDigestInfo(SEC_OID_SHA1,
+ mac->data, mac->len);
+ if (di != NULL) {
+ rv = SGN_CopyDigestInfo(pfx->poolp,
+ &pfx->macData.safeMac, di);
+ SGN_DestroyDigestInfo(di);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ }
+ } else {
+ rv = SECFailure;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ }
+
+ if (key != NULL) {
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ }
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ }
+ }
+
+ if (rv == SECFailure) {
+ SEC_PKCS12DestroyPFX(pfx);
+ pfx = NULL;
+ }
+
+ return pfx;
+}
+
+/* der encode the pfx */
+static SECItem *
+sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx)
+{
+ SECItem *dest;
+ void *dummy;
+
+ if (pfx == NULL) {
+ return NULL;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (dest == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate);
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+ dest = NULL;
+ }
+
+ return dest;
+}
+
+SECItem *
+SEC_PKCS12GetPFX(char **nicknames,
+ CERTCertificate **ref_certs,
+ PRBool shroud_keys,
+ SEC_PKCS5GetPBEPassword pbef,
+ void *pbearg,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ SECItem **nicks = NULL;
+ SEC_PKCS12PFXItem *pfx = NULL;
+ SEC_PKCS12Baggage *baggage = NULL;
+ SEC_PKCS12SafeContents *safe = NULL;
+ SEC_PKCS7ContentInfo *cinfo = NULL;
+ SECStatus rv = SECFailure;
+ SECItem *dest = NULL, *pwitem = NULL;
+ PRBool problem = PR_FALSE;
+ PRBool unencryptedCerts;
+ SECOidTag shroud_alg, safe_alg;
+
+ /* how should we encrypt certs ? */
+ unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed();
+ if (!unencryptedCerts) {
+ safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm();
+ if (safe_alg == SEC_OID_UNKNOWN) {
+ safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm();
+ }
+ if (safe_alg == SEC_OID_UNKNOWN) {
+ unencryptedCerts = PR_TRUE;
+ /* for export where no encryption is allowed, we still need
+ * to encrypt the NULL contents per the spec. encrypted info
+ * is known plaintext, so it shouldn't be a problem.
+ */
+ safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ }
+ } else {
+ /* for export where no encryption is allowed, we still need
+ * to encrypt the NULL contents per the spec. encrypted info
+ * is known plaintext, so it shouldn't be a problem.
+ */
+ safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ }
+
+ /* keys are always stored with triple DES */
+ shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
+
+ /* check for FIPS, if so, do not encrypt certs */
+ if (PK11_IsFIPS() && !unencryptedCerts) {
+ unencryptedCerts = PR_TRUE;
+ }
+
+ if ((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) {
+ problem = PR_TRUE;
+ goto loser;
+ }
+
+ /* get password */
+ pwitem = (*pbef)(pbearg);
+ if (pwitem == NULL) {
+ problem = PR_TRUE;
+ goto loser;
+ }
+ nicks = sec_pkcs12_convert_nickname_list(nicknames);
+
+ /* get safe and baggage */
+ rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts,
+ &safe, &baggage, shroud_keys,
+ shroud_alg, pwitem, unicodeFn, wincx);
+ if (rv == SECFailure) {
+ problem = PR_TRUE;
+ }
+
+ if ((safe != NULL) && (problem == PR_FALSE)) {
+ /* copy thumbprints */
+ rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage);
+
+ /* package everything up into AuthenticatedSafe */
+ cinfo = sec_pkcs12_get_auth_safe(safe, baggage,
+ safe_alg, pwitem, unicodeFn, wincx);
+
+ sec_pkcs12_destroy_cert_content_infos(safe, baggage);
+
+ /* get the pfx and mac it */
+ if (cinfo != NULL) {
+ pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn);
+ if (pfx != NULL) {
+ dest = sec_pkcs12_encode_pfx(pfx);
+ SEC_PKCS12DestroyPFX(pfx);
+ }
+ SEC_PKCS7DestroyContentInfo(cinfo);
+ }
+
+ if (safe != NULL) {
+ PORT_FreeArena(safe->poolp, PR_TRUE);
+ }
+ } else {
+ if (safe != NULL) {
+ PORT_FreeArena(safe->poolp, PR_TRUE);
+ }
+ }
+
+loser:
+ if (nicks != NULL) {
+ sec_pkcs12_destroy_nickname_list(nicks);
+ }
+
+ if (ref_certs != NULL) {
+ sec_pkcs12_destroy_certificate_list(ref_certs);
+ }
+
+ if (pwitem != NULL) {
+ SECITEM_ZfreeItem(pwitem, PR_TRUE);
+ }
+
+ if (problem == PR_TRUE) {
+ dest = NULL;
+ }
+
+ return dest;
+}