| Index: nss_pkcs12/p12d.c
|
| ===================================================================
|
| --- nss_pkcs12/p12d.c (revision 0)
|
| +++ nss_pkcs12/p12d.c (revision 0)
|
| @@ -0,0 +1,3548 @@
|
| +/* 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 "nssrenam.h"
|
| +#include "p12t.h"
|
| +#include "p12.h"
|
| +#include "plarena.h"
|
| +#include "secitem.h"
|
| +#include "secoid.h"
|
| +#include "seccomon.h"
|
| +#include "secport.h"
|
| +#include "cert.h"
|
| +#include "secpkcs7.h"
|
| +#include "secasn1.h"
|
| +#include "secerr.h"
|
| +#include "pk11func.h"
|
| +#include "p12plcy.h"
|
| +#include "p12local.h"
|
| +#include "secder.h"
|
| +#include "secport.h"
|
| +
|
| +#include "certdb.h"
|
| +
|
| +#include "prcpucfg.h"
|
| +
|
| +/* This belongs in secport.h */
|
| +#define PORT_ArenaGrowArray(poolp, oldptr, type, oldnum, newnum) \
|
| + (type *)PORT_ArenaGrow((poolp), (oldptr), \
|
| + (oldnum) * sizeof(type), (newnum) * sizeof(type))
|
| +
|
| +
|
| +typedef struct sec_PKCS12SafeContentsContextStr sec_PKCS12SafeContentsContext;
|
| +
|
| +/* Opaque structure for decoding SafeContents. These are used
|
| + * for each authenticated safe as well as any nested safe contents.
|
| + */
|
| +struct sec_PKCS12SafeContentsContextStr {
|
| + /* the parent decoder context */
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| +
|
| + /* memory arena to allocate space from */
|
| + PLArenaPool *arena;
|
| +
|
| + /* decoder context and destination for decoding safe contents */
|
| + SEC_ASN1DecoderContext *safeContentsA1Dcx;
|
| + sec_PKCS12SafeContents safeContents;
|
| +
|
| + /* information for decoding safe bags within the safe contents.
|
| + * these variables are updated for each safe bag decoded.
|
| + */
|
| + SEC_ASN1DecoderContext *currentSafeBagA1Dcx;
|
| + sec_PKCS12SafeBag *currentSafeBag;
|
| + PRBool skipCurrentSafeBag;
|
| +
|
| + /* if the safe contents is nested, the parent is pointed to here. */
|
| + sec_PKCS12SafeContentsContext *nestedSafeContentsCtx;
|
| +};
|
| +
|
| +/* opaque decoder context structure. information for decoding a pkcs 12
|
| + * PDU are stored here as well as decoding pointers for intermediary
|
| + * structures which are part of the PKCS 12 PDU. Upon a successful
|
| + * decode, the safe bags containing certificates and keys encountered.
|
| + */
|
| +struct SEC_PKCS12DecoderContextStr {
|
| + PLArenaPool *arena;
|
| + PK11SlotInfo *slot;
|
| + void *wincx;
|
| + PRBool error;
|
| + int errorValue;
|
| +
|
| + /* password */
|
| + SECItem *pwitem;
|
| +
|
| + /* used for decoding the PFX structure */
|
| + SEC_ASN1DecoderContext *pfxA1Dcx;
|
| + sec_PKCS12PFXItem pfx;
|
| +
|
| + /* safe bags found during decoding */
|
| + sec_PKCS12SafeBag **safeBags;
|
| + unsigned int safeBagCount;
|
| +
|
| + /* state variables for decoding authenticated safes. */
|
| + SEC_PKCS7DecoderContext *currentASafeP7Dcx;
|
| + SEC_ASN1DecoderContext *aSafeA1Dcx;
|
| + SEC_PKCS7DecoderContext *aSafeP7Dcx;
|
| + SEC_PKCS7ContentInfo *aSafeCinfo;
|
| + sec_PKCS12AuthenticatedSafe authSafe;
|
| + sec_PKCS12SafeContents safeContents;
|
| +
|
| + /* safe contents info */
|
| + unsigned int safeContentsCnt;
|
| + sec_PKCS12SafeContentsContext **safeContentsList;
|
| +
|
| + /* HMAC info */
|
| + sec_PKCS12MacData macData;
|
| +
|
| + /* routines for reading back the data to be hmac'd */
|
| + /* They are called as follows.
|
| + *
|
| + * Stage 1: decode the aSafes cinfo into a buffer in dArg,
|
| + * which p12d.c sometimes refers to as the "temp file".
|
| + * This occurs during SEC_PKCS12DecoderUpdate calls.
|
| + *
|
| + * dOpen(dArg, PR_FALSE)
|
| + * dWrite(dArg, buf, len)
|
| + * ...
|
| + * dWrite(dArg, buf, len)
|
| + * dClose(dArg, PR_FALSE)
|
| + *
|
| + * Stage 2: verify MAC
|
| + * This occurs SEC_PKCS12DecoderVerify.
|
| + *
|
| + * dOpen(dArg, PR_TRUE)
|
| + * dRead(dArg, buf, IN_BUF_LEN)
|
| + * ...
|
| + * dRead(dArg, buf, IN_BUF_LEN)
|
| + * dClose(dArg, PR_TRUE)
|
| + */
|
| + digestOpenFn dOpen;
|
| + digestCloseFn dClose;
|
| + digestIOFn dRead, dWrite;
|
| + void *dArg;
|
| + PRBool dIsOpen; /* is the temp file created? */
|
| +
|
| + /* helper functions */
|
| + SECKEYGetPasswordKey pwfn;
|
| + void *pwfnarg;
|
| + PRBool swapUnicodeBytes;
|
| +
|
| + /* import information */
|
| + PRBool bagsVerified;
|
| +
|
| + /* buffer management for the default callbacks implementation */
|
| + void *buffer; /* storage area */
|
| + PRInt32 filesize; /* actual data size */
|
| + PRInt32 allocated; /* total buffer size allocated */
|
| + PRInt32 currentpos; /* position counter */
|
| + SECPKCS12TargetTokenCAs tokenCAs;
|
| + sec_PKCS12SafeBag **keyList;/* used by ...IterateNext() */
|
| + unsigned int iteration;
|
| + SEC_PKCS12DecoderItem decitem;
|
| +};
|
| +
|
| +/* forward declarations of functions that are used when decoding
|
| + * safeContents bags which are nested and when decoding the
|
| + * authenticatedSafes.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
|
| + *safeContentsCtx);
|
| +static SECStatus
|
| +sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
|
| + *safeContentsCtx);
|
| +
|
| +
|
| +/* make sure that the PFX version being decoded is a version
|
| + * which we support.
|
| + */
|
| +static PRBool
|
| +sec_pkcs12_proper_version(sec_PKCS12PFXItem *pfx)
|
| +{
|
| + /* if no version, assume it is not supported */
|
| + if(pfx->version.len == 0) {
|
| + return PR_FALSE;
|
| + }
|
| +
|
| + if(DER_GetInteger(&pfx->version) > SEC_PKCS12_VERSION) {
|
| + return PR_FALSE;
|
| + }
|
| +
|
| + return PR_TRUE;
|
| +}
|
| +
|
| +/* retrieve the key for decrypting the safe contents */
|
| +static PK11SymKey *
|
| +sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid)
|
| +{
|
| + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *) arg;
|
| + PK11SlotInfo *slot;
|
| + PK11SymKey *bulkKey;
|
| +
|
| + if(!p12dcx) {
|
| + return NULL;
|
| + }
|
| +
|
| + /* if no slot specified, use the internal key slot */
|
| + if(p12dcx->slot) {
|
| + slot = PK11_ReferenceSlot(p12dcx->slot);
|
| + } else {
|
| + slot = PK11_GetInternalKeySlot();
|
| + }
|
| +
|
| + bulkKey = PK11_PBEKeyGen(slot, algid, p12dcx->pwitem,
|
| + PR_FALSE, p12dcx->wincx);
|
| + /* some tokens can't generate PBE keys on their own, generate the
|
| + * key in the internal slot, and let the Import code deal with it,
|
| + * (if the slot can't generate PBEs, then we need to use the internal
|
| + * slot anyway to unwrap). */
|
| + if (!bulkKey && !PK11_IsInternal(slot)) {
|
| + PK11_FreeSlot(slot);
|
| + slot = PK11_GetInternalKeySlot();
|
| + bulkKey = PK11_PBEKeyGen(slot, algid, p12dcx->pwitem,
|
| + PR_FALSE, p12dcx->wincx);
|
| + }
|
| + PK11_FreeSlot(slot);
|
| +
|
| + /* set the password data on the key */
|
| + if (bulkKey) {
|
| + PK11_SetSymKeyUserData(bulkKey,p12dcx->pwitem, NULL);
|
| + }
|
| +
|
| +
|
| + return bulkKey;
|
| +}
|
| +
|
| +/* XXX this needs to be modified to handle enveloped data. most
|
| + * likely, it should mirror the routines for SMIME in that regard.
|
| + */
|
| +static PRBool
|
| +sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid,
|
| + PK11SymKey *bulkkey)
|
| +{
|
| + PRBool decryptionAllowed = SEC_PKCS12DecryptionAllowed(algid);
|
| +
|
| + if(!decryptionAllowed) {
|
| + return PR_FALSE;
|
| + }
|
| +
|
| + return PR_TRUE;
|
| +}
|
| +
|
| +/* when we encounter a new safe bag during the decoding, we need
|
| + * to allocate space for the bag to be decoded to and set the
|
| + * state variables appropriately. all of the safe bags are allocated
|
| + * in a buffer in the outer SEC_PKCS12DecoderContext, however,
|
| + * a pointer to the safeBag is also used in the sec_PKCS12SafeContentsContext
|
| + * for the current bag.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_decoder_init_new_safe_bag(sec_PKCS12SafeContentsContext
|
| + *safeContentsCtx)
|
| +{
|
| + void *mark = NULL;
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| +
|
| + /* make sure that the structures are defined, and there has
|
| + * not been an error in the decoding
|
| + */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx
|
| + || safeContentsCtx->p12dcx->error) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + p12dcx = safeContentsCtx->p12dcx;
|
| + mark = PORT_ArenaMark(p12dcx->arena);
|
| +
|
| + /* allocate a new safe bag, if bags already exist, grow the
|
| + * list of bags, otherwise allocate a new list. the list is
|
| + * NULL terminated.
|
| + */
|
| + p12dcx->safeBags = (!p12dcx->safeBagCount)
|
| + ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
|
| + : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
|
| + sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
|
| + p12dcx->safeBagCount + 2);
|
| +
|
| + if(!p12dcx->safeBags) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + /* append the bag to the end of the list and update the reference
|
| + * in the safeContentsCtx.
|
| + */
|
| + p12dcx->safeBags[p12dcx->safeBagCount] =
|
| + safeContentsCtx->currentSafeBag =
|
| + PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
|
| + if(!safeContentsCtx->currentSafeBag) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + p12dcx->safeBags[++p12dcx->safeBagCount] = NULL;
|
| +
|
| + safeContentsCtx->currentSafeBag->slot = safeContentsCtx->p12dcx->slot;
|
| + safeContentsCtx->currentSafeBag->pwitem = safeContentsCtx->p12dcx->pwitem;
|
| + safeContentsCtx->currentSafeBag->swapUnicodeBytes =
|
| + safeContentsCtx->p12dcx->swapUnicodeBytes;
|
| + safeContentsCtx->currentSafeBag->arena = safeContentsCtx->p12dcx->arena;
|
| + safeContentsCtx->currentSafeBag->tokenCAs =
|
| + safeContentsCtx->p12dcx->tokenCAs;
|
| +
|
| + PORT_ArenaUnmark(p12dcx->arena, mark);
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| +
|
| + /* if an error occurred, release the memory and set the error flag
|
| + * the only possible errors triggered by this function are memory
|
| + * related.
|
| + */
|
| + if(mark) {
|
| + PORT_ArenaRelease(p12dcx->arena, mark);
|
| + }
|
| +
|
| + p12dcx->error = PR_TRUE;
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* A wrapper for updating the ASN1 context in which a safeBag is
|
| + * being decoded. This function is called as a callback from
|
| + * secasn1d when decoding SafeContents structures.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data,
|
| + unsigned long len, int depth,
|
| + SEC_ASN1EncodingPart data_kind)
|
| +{
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx =
|
| + (sec_PKCS12SafeContentsContext *)arg;
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + SECStatus rv;
|
| +
|
| + /* make sure that we are not skipping the current safeBag,
|
| + * and that there are no errors. If so, just return rather
|
| + * than continuing to process.
|
| + */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx
|
| + || safeContentsCtx->p12dcx->error
|
| + || safeContentsCtx->skipCurrentSafeBag) {
|
| + return;
|
| + }
|
| + p12dcx = safeContentsCtx->p12dcx;
|
| +
|
| + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->currentSafeBagA1Dcx, data, len);
|
| + if(rv != SECSuccess) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + /* set the error, and finish the decoder context. because there
|
| + * is not a way of returning an error message, it may be worth
|
| + * while to do a check higher up and finish any decoding contexts
|
| + * that are still open.
|
| + */
|
| + p12dcx->error = PR_TRUE;
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
|
| + safeContentsCtx->currentSafeBagA1Dcx = NULL;
|
| + return;
|
| +}
|
| +
|
| +/* notify function for decoding safeBags. This function is
|
| + * used to filter safeBag types which are not supported,
|
| + * initiate the decoding of nested safe contents, and decode
|
| + * safeBags in general. this function is set when the decoder
|
| + * context for the safeBag is first created.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_safe_bag_notify(void *arg, PRBool before,
|
| + void *dest, int real_depth)
|
| +{
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx =
|
| + (sec_PKCS12SafeContentsContext *)arg;
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + sec_PKCS12SafeBag *bag;
|
| + PRBool after;
|
| +
|
| + /* if an error is encountered, return */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx ||
|
| + safeContentsCtx->p12dcx->error) {
|
| + return;
|
| + }
|
| + p12dcx = safeContentsCtx->p12dcx;
|
| +
|
| + /* to make things more readable */
|
| + if(before)
|
| + after = PR_FALSE;
|
| + else
|
| + after = PR_TRUE;
|
| +
|
| + /* have we determined the safeBagType yet? */
|
| + bag = safeContentsCtx->currentSafeBag;
|
| + if(bag->bagTypeTag == NULL) {
|
| + if(after && (dest == &(bag->safeBagType))) {
|
| + bag->bagTypeTag = SECOID_FindOID(&(bag->safeBagType));
|
| + if(bag->bagTypeTag == NULL) {
|
| + p12dcx->error = PR_TRUE;
|
| + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
|
| + }
|
| + }
|
| + return;
|
| + }
|
| +
|
| + /* process the safeBag depending on it's type. those
|
| + * which we do not support, are ignored. we start a decoding
|
| + * context for a nested safeContents.
|
| + */
|
| + switch(bag->bagTypeTag->offset) {
|
| + case SEC_OID_PKCS12_V1_KEY_BAG_ID:
|
| + case SEC_OID_PKCS12_V1_CERT_BAG_ID:
|
| + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
|
| + break;
|
| + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
|
| + /* if we are just starting to decode the safeContents, initialize
|
| + * a new safeContentsCtx to process it.
|
| + */
|
| + if(before && (dest == &(bag->safeBagContent))) {
|
| + sec_pkcs12_decoder_begin_nested_safe_contents(safeContentsCtx);
|
| + } else if(after && (dest == &(bag->safeBagContent))) {
|
| + /* clean up the nested decoding */
|
| + sec_pkcs12_decoder_finish_nested_safe_contents(safeContentsCtx);
|
| + }
|
| + break;
|
| + case SEC_OID_PKCS12_V1_CRL_BAG_ID:
|
| + case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
|
| + default:
|
| + /* skip any safe bag types we don't understand or handle */
|
| + safeContentsCtx->skipCurrentSafeBag = PR_TRUE;
|
| + break;
|
| + }
|
| +
|
| + return;
|
| +}
|
| +
|
| +/* notify function for decoding safe contents. each entry in the
|
| + * safe contents is a safeBag which needs to be allocated and
|
| + * the decoding context initialized at the beginning and then
|
| + * the context needs to be closed and finished at the end.
|
| + *
|
| + * this function is set when the safeContents decode context is
|
| + * initialized.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_safe_contents_notify(void *arg, PRBool before,
|
| + void *dest, int real_depth)
|
| +{
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx =
|
| + (sec_PKCS12SafeContentsContext*)arg;
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + SECStatus rv;
|
| +
|
| + /* if there is an error we don't want to continue processing,
|
| + * just return and keep going.
|
| + */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx
|
| + || safeContentsCtx->p12dcx->error) {
|
| + return;
|
| + }
|
| + p12dcx = safeContentsCtx->p12dcx;
|
| +
|
| + /* if we are done with the current safeBag, then we need to
|
| + * finish the context and set the state variables appropriately.
|
| + */
|
| + if(!before) {
|
| + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
|
| + safeContentsCtx->currentSafeBagA1Dcx = NULL;
|
| + safeContentsCtx->skipCurrentSafeBag = PR_FALSE;
|
| + } else {
|
| + /* we are starting a new safe bag. we need to allocate space
|
| + * for the bag and initialize the decoding context.
|
| + */
|
| + rv = sec_pkcs12_decoder_init_new_safe_bag(safeContentsCtx);
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* set up the decoder context */
|
| + safeContentsCtx->currentSafeBagA1Dcx =
|
| + SEC_ASN1DecoderStart(p12dcx->arena,
|
| + safeContentsCtx->currentSafeBag,
|
| + sec_PKCS12SafeBagTemplate);
|
| + if(!safeContentsCtx->currentSafeBagA1Dcx) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + /* set the notify and filter procs so that the safe bag
|
| + * data gets sent to the proper location when decoding.
|
| + */
|
| + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->currentSafeBagA1Dcx,
|
| + sec_pkcs12_decoder_safe_bag_notify,
|
| + safeContentsCtx);
|
| + SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsA1Dcx,
|
| + sec_pkcs12_decoder_safe_bag_update,
|
| + safeContentsCtx, PR_TRUE);
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + /* in the event of an error, we want to close the decoding
|
| + * context and clear the filter and notify procedures.
|
| + */
|
| + p12dcx->error = PR_TRUE;
|
| +
|
| + if(safeContentsCtx->currentSafeBagA1Dcx) {
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
|
| + safeContentsCtx->currentSafeBagA1Dcx = NULL;
|
| + }
|
| +
|
| + SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->safeContentsA1Dcx);
|
| + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
|
| +
|
| + return;
|
| +}
|
| +
|
| +/* initialize the safeContents for decoding. this routine
|
| + * is used for authenticatedSafes as well as nested safeContents.
|
| + */
|
| +static sec_PKCS12SafeContentsContext *
|
| +sec_pkcs12_decoder_safe_contents_init_decode(SEC_PKCS12DecoderContext *p12dcx,
|
| + PRBool nestedSafe)
|
| +{
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx = NULL;
|
| + const SEC_ASN1Template *theTemplate;
|
| +
|
| + if(!p12dcx || p12dcx->error) {
|
| + return NULL;
|
| + }
|
| +
|
| + /* allocate a new safeContents list or grow the existing list and
|
| + * append the new safeContents onto the end.
|
| + */
|
| + p12dcx->safeContentsList = (!p12dcx->safeContentsCnt)
|
| + ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeContentsContext *, 2)
|
| + : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeContentsList,
|
| + sec_PKCS12SafeContentsContext *,
|
| + 1 + p12dcx->safeContentsCnt,
|
| + 2 + p12dcx->safeContentsCnt);
|
| +
|
| + if(!p12dcx->safeContentsList) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + p12dcx->safeContentsList[p12dcx->safeContentsCnt] = safeContentsCtx =
|
| + PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeContentsContext);
|
| + if(!p12dcx->safeContentsList[p12dcx->safeContentsCnt]) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + p12dcx->safeContentsList[++p12dcx->safeContentsCnt] = NULL;
|
| +
|
| + /* set up the state variables */
|
| + safeContentsCtx->p12dcx = p12dcx;
|
| + safeContentsCtx->arena = p12dcx->arena;
|
| +
|
| + /* begin the decoding -- the template is based on whether we are
|
| + * decoding a nested safeContents or not.
|
| + */
|
| + if(nestedSafe == PR_TRUE) {
|
| + theTemplate = sec_PKCS12NestedSafeContentsDecodeTemplate;
|
| + } else {
|
| + theTemplate = sec_PKCS12SafeContentsDecodeTemplate;
|
| + }
|
| +
|
| + /* start the decoder context */
|
| + safeContentsCtx->safeContentsA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
|
| + &safeContentsCtx->safeContents,
|
| + theTemplate);
|
| +
|
| + if(!safeContentsCtx->safeContentsA1Dcx) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + /* set the safeContents notify procedure to look for
|
| + * and start the decode of safeBags.
|
| + */
|
| + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsA1Dcx,
|
| + sec_pkcs12_decoder_safe_contents_notify,
|
| + safeContentsCtx);
|
| +
|
| + return safeContentsCtx;
|
| +
|
| +loser:
|
| + /* in the case of an error, we want to finish the decoder
|
| + * context and set the error flag.
|
| + */
|
| + if(safeContentsCtx && safeContentsCtx->safeContentsA1Dcx) {
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
|
| + safeContentsCtx->safeContentsA1Dcx = NULL;
|
| + }
|
| +
|
| + p12dcx->error = PR_TRUE;
|
| +
|
| + return NULL;
|
| +}
|
| +
|
| +/* wrapper for updating safeContents. this is set as the filter of
|
| + * safeBag when there is a nested safeContents.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_nested_safe_contents_update(void *arg, const char *buf,
|
| + unsigned long len, int depth,
|
| + SEC_ASN1EncodingPart data_kind)
|
| +{
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx =
|
| + (sec_PKCS12SafeContentsContext *)arg;
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + SECStatus rv;
|
| +
|
| + /* check for an error */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx
|
| + || safeContentsCtx->p12dcx->error
|
| + || !safeContentsCtx->safeContentsA1Dcx) {
|
| + return;
|
| + }
|
| +
|
| + /* no need to update if no data sent in */
|
| + if(!len || !buf) {
|
| + return;
|
| + }
|
| +
|
| + /* update the decoding context */
|
| + p12dcx = safeContentsCtx->p12dcx;
|
| + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len);
|
| + if(rv != SECSuccess) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + /* handle any errors. If a decoding context is open, close it. */
|
| + p12dcx->error = PR_TRUE;
|
| + if(safeContentsCtx->safeContentsA1Dcx) {
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
|
| + safeContentsCtx->safeContentsA1Dcx = NULL;
|
| + }
|
| +}
|
| +
|
| +/* whenever a new safeContentsSafeBag is encountered, we need
|
| + * to init a safeContentsContext.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
|
| + *safeContentsCtx)
|
| +{
|
| + /* check for an error */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx ||
|
| + safeContentsCtx->p12dcx->error) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + safeContentsCtx->nestedSafeContentsCtx =
|
| + sec_pkcs12_decoder_safe_contents_init_decode(safeContentsCtx->p12dcx,
|
| + PR_TRUE);
|
| + if(!safeContentsCtx->nestedSafeContentsCtx) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* set up new filter proc */
|
| + SEC_ASN1DecoderSetNotifyProc(
|
| + safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx,
|
| + sec_pkcs12_decoder_safe_contents_notify,
|
| + safeContentsCtx->nestedSafeContentsCtx);
|
| +
|
| + SEC_ASN1DecoderSetFilterProc(safeContentsCtx->currentSafeBagA1Dcx,
|
| + sec_pkcs12_decoder_nested_safe_contents_update,
|
| + safeContentsCtx->nestedSafeContentsCtx,
|
| + PR_TRUE);
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* when the safeContents is done decoding, we need to reset the
|
| + * proper filter and notify procs and close the decoding context
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
|
| + *safeContentsCtx)
|
| +{
|
| + /* check for error */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx ||
|
| + safeContentsCtx->p12dcx->error) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* clean up */
|
| + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->currentSafeBagA1Dcx);
|
| + SEC_ASN1DecoderClearNotifyProc(
|
| + safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx);
|
| + SEC_ASN1DecoderFinish(
|
| + safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx);
|
| + safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx = NULL;
|
| + safeContentsCtx->nestedSafeContentsCtx = NULL;
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* wrapper for updating safeContents. This is used when decoding
|
| + * the nested safeContents and any authenticatedSafes.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_safe_contents_callback(void *arg, const char *buf,
|
| + unsigned long len)
|
| +{
|
| + SECStatus rv;
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx =
|
| + (sec_PKCS12SafeContentsContext *)arg;
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| +
|
| + /* check for error */
|
| + if(!safeContentsCtx || !safeContentsCtx->p12dcx
|
| + || safeContentsCtx->p12dcx->error
|
| + || !safeContentsCtx->safeContentsA1Dcx) {
|
| + return;
|
| + }
|
| + p12dcx = safeContentsCtx->p12dcx;
|
| +
|
| + /* update the decoder */
|
| + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len);
|
| + if(rv != SECSuccess) {
|
| + /* if we fail while trying to decode a 'safe', it's probably because
|
| + * we didn't have the correct password. */
|
| + PORT_SetError(SEC_ERROR_BAD_PASSWORD);
|
| + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
|
| + SEC_PKCS7DecoderAbort(p12dcx->currentASafeP7Dcx,SEC_ERROR_BAD_PASSWORD);
|
| + goto loser;
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + /* set the error and finish the context */
|
| + p12dcx->error = PR_TRUE;
|
| + if(safeContentsCtx->safeContentsA1Dcx) {
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
|
| + safeContentsCtx->safeContentsA1Dcx = NULL;
|
| + }
|
| +
|
| + return;
|
| +}
|
| +
|
| +/* this is a wrapper for the ASN1 decoder to call SEC_PKCS7DecoderUpdate
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_wrap_p7_update(void *arg, const char *data,
|
| + unsigned long len, int depth,
|
| + SEC_ASN1EncodingPart data_kind)
|
| +{
|
| + SEC_PKCS7DecoderContext *p7dcx = (SEC_PKCS7DecoderContext *)arg;
|
| +
|
| + SEC_PKCS7DecoderUpdate(p7dcx, data, len);
|
| +}
|
| +
|
| +/* notify function for decoding aSafes. at the beginning,
|
| + * of an authenticatedSafe, we start a decode of a safeContents.
|
| + * at the end, we clean up the safeContents decoder context and
|
| + * reset state variables
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_asafes_notify(void *arg, PRBool before, void *dest,
|
| + int real_depth)
|
| +{
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx;
|
| +
|
| + /* make sure no error occurred. */
|
| + p12dcx = (SEC_PKCS12DecoderContext *)arg;
|
| + if(!p12dcx || p12dcx->error) {
|
| + return;
|
| + }
|
| +
|
| + if(before) {
|
| +
|
| + /* init a new safeContentsContext */
|
| + safeContentsCtx = sec_pkcs12_decoder_safe_contents_init_decode(p12dcx,
|
| + PR_FALSE);
|
| + if(!safeContentsCtx) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* initiate the PKCS7ContentInfo decode */
|
| + p12dcx->currentASafeP7Dcx = SEC_PKCS7DecoderStart(
|
| + sec_pkcs12_decoder_safe_contents_callback,
|
| + safeContentsCtx,
|
| + p12dcx->pwfn, p12dcx->pwfnarg,
|
| + sec_pkcs12_decoder_get_decrypt_key, p12dcx,
|
| + sec_pkcs12_decoder_decryption_allowed);
|
| + if(!p12dcx->currentASafeP7Dcx) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + SEC_ASN1DecoderSetFilterProc(p12dcx->aSafeA1Dcx,
|
| + sec_pkcs12_decoder_wrap_p7_update,
|
| + p12dcx->currentASafeP7Dcx, PR_TRUE);
|
| + }
|
| +
|
| + if(!before) {
|
| + /* if one is being decoded, finish the decode */
|
| + if(p12dcx->currentASafeP7Dcx != NULL) {
|
| + SEC_PKCS7ContentInfo * cinfo;
|
| + unsigned int cnt = p12dcx->safeContentsCnt - 1;
|
| + safeContentsCtx = p12dcx->safeContentsList[cnt];
|
| + if (safeContentsCtx->safeContentsA1Dcx) {
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
|
| + safeContentsCtx->safeContentsA1Dcx = NULL;
|
| + }
|
| + cinfo = SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
|
| + p12dcx->currentASafeP7Dcx = NULL;
|
| + if(!cinfo) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + SEC_PKCS7DestroyContentInfo(cinfo); /* don't leak it */
|
| + }
|
| + }
|
| +
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + /* set the error flag */
|
| + p12dcx->error = PR_TRUE;
|
| + return;
|
| +}
|
| +
|
| +/* wrapper for updating asafes decoding context. this function
|
| + * writes data being decoded to disk, so that a mac can be computed
|
| + * later.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_asafes_callback(void *arg, const char *buf,
|
| + unsigned long len)
|
| +{
|
| + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
|
| + SECStatus rv;
|
| +
|
| + if(!p12dcx || p12dcx->error) {
|
| + return;
|
| + }
|
| +
|
| + /* update the context */
|
| + rv = SEC_ASN1DecoderUpdate(p12dcx->aSafeA1Dcx, buf, len);
|
| + if(rv != SECSuccess) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + p12dcx->error = PR_TRUE;
|
| + goto loser;
|
| + }
|
| +
|
| + /* if we are writing to a file, write out the new information */
|
| + if(p12dcx->dWrite) {
|
| + unsigned long writeLen = (*p12dcx->dWrite)(p12dcx->dArg,
|
| + (unsigned char *)buf, len);
|
| + if(writeLen != len) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + /* set the error flag */
|
| + p12dcx->error = PR_TRUE;
|
| + SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
|
| + p12dcx->aSafeA1Dcx = NULL;
|
| +
|
| + return;
|
| +}
|
| +
|
| +/* start the decode of an authenticatedSafe contentInfo.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_decode_start_asafes_cinfo(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + if(!p12dcx || p12dcx->error) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* start the decode context */
|
| + p12dcx->aSafeA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
|
| + &p12dcx->authSafe,
|
| + sec_PKCS12AuthenticatedSafeTemplate);
|
| + if(!p12dcx->aSafeA1Dcx) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + /* set the notify function */
|
| + SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeA1Dcx,
|
| + sec_pkcs12_decoder_asafes_notify, p12dcx);
|
| +
|
| + /* begin the authSafe decoder context */
|
| + p12dcx->aSafeP7Dcx = SEC_PKCS7DecoderStart(
|
| + sec_pkcs12_decoder_asafes_callback, p12dcx,
|
| + p12dcx->pwfn, p12dcx->pwfnarg, NULL, NULL, NULL);
|
| + if(!p12dcx->aSafeP7Dcx) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + /* open the temp file for writing, if the digest functions were set */
|
| + if(p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE)
|
| + != SECSuccess) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + /* dOpen(dArg, PR_FALSE) creates the temp file */
|
| + p12dcx->dIsOpen = PR_TRUE;
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + p12dcx->error = PR_TRUE;
|
| +
|
| + if(p12dcx->aSafeA1Dcx) {
|
| + SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
|
| + p12dcx->aSafeA1Dcx = NULL;
|
| + }
|
| +
|
| + if(p12dcx->aSafeP7Dcx) {
|
| + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
|
| + p12dcx->aSafeP7Dcx = NULL;
|
| + }
|
| +
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* wrapper for updating the safeContents. this function is used as
|
| + * a filter for the pfx when decoding the authenticated safes
|
| + */
|
| +static void
|
| +sec_pkcs12_decode_asafes_cinfo_update(void *arg, const char *buf,
|
| + unsigned long len, int depth,
|
| + SEC_ASN1EncodingPart data_kind)
|
| +{
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + SECStatus rv;
|
| +
|
| + p12dcx = (SEC_PKCS12DecoderContext*)arg;
|
| + if(!p12dcx || p12dcx->error) {
|
| + return;
|
| + }
|
| +
|
| + /* update the safeContents decoder */
|
| + rv = SEC_PKCS7DecoderUpdate(p12dcx->aSafeP7Dcx, buf, len);
|
| + if(rv != SECSuccess) {
|
| + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
|
| + goto loser;
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| +
|
| + /* did we find an error? if so, close the context and set the
|
| + * error flag.
|
| + */
|
| + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
|
| + p12dcx->aSafeP7Dcx = NULL;
|
| + p12dcx->error = PR_TRUE;
|
| +}
|
| +
|
| +/* notify procedure used while decoding the pfx. When we encounter
|
| + * the authSafes, we want to trigger the decoding of authSafes as well
|
| + * as when we encounter the macData, trigger the decoding of it. we do
|
| + * this because we we are streaming the decoder and not decoding in place.
|
| + * the pfx which is the destination, only has the version decoded into it.
|
| + */
|
| +static void
|
| +sec_pkcs12_decoder_pfx_notify_proc(void *arg, PRBool before, void *dest,
|
| + int real_depth)
|
| +{
|
| + SECStatus rv;
|
| + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext*)arg;
|
| +
|
| + /* if an error occurs, clear the notifyProc and the filterProc
|
| + * and continue.
|
| + */
|
| + if(p12dcx->error) {
|
| + SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxA1Dcx);
|
| + SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx);
|
| + return;
|
| + }
|
| +
|
| + if(before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
|
| +
|
| + /* we want to make sure this is a version we support */
|
| + if(!sec_pkcs12_proper_version(&p12dcx->pfx)) {
|
| + p12dcx->errorValue = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
|
| + goto loser;
|
| + }
|
| +
|
| + /* start the decode of the aSafes cinfo... */
|
| + rv = sec_pkcs12_decode_start_asafes_cinfo(p12dcx);
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* set the filter proc to update the authenticated safes. */
|
| + SEC_ASN1DecoderSetFilterProc(p12dcx->pfxA1Dcx,
|
| + sec_pkcs12_decode_asafes_cinfo_update,
|
| + p12dcx, PR_TRUE);
|
| + }
|
| +
|
| + if(!before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
|
| +
|
| + /* we are done decoding the authenticatedSafes, so we need to
|
| + * finish the decoderContext and clear the filter proc
|
| + * and close the hmac callback, if present
|
| + */
|
| + p12dcx->aSafeCinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
|
| + p12dcx->aSafeP7Dcx = NULL;
|
| + if(!p12dcx->aSafeCinfo) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| + SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx);
|
| + if(p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE)
|
| + != SECSuccess)) {
|
| + p12dcx->errorValue = PORT_GetError();
|
| + goto loser;
|
| + }
|
| +
|
| + }
|
| +
|
| + return;
|
| +
|
| +loser:
|
| + p12dcx->error = PR_TRUE;
|
| +}
|
| +
|
| +/* default implementations of the open/close/read/write functions for
|
| + SEC_PKCS12DecoderStart
|
| +*/
|
| +
|
| +#define DEFAULT_TEMP_SIZE 4096
|
| +
|
| +static SECStatus
|
| +p12u_DigestOpen(void *arg, PRBool readData)
|
| +{
|
| + SEC_PKCS12DecoderContext* p12cxt = arg;
|
| +
|
| + p12cxt->currentpos = 0;
|
| +
|
| + if (PR_FALSE == readData) {
|
| + /* allocate an initial buffer */
|
| + p12cxt->filesize = 0;
|
| + p12cxt->allocated = DEFAULT_TEMP_SIZE;
|
| + p12cxt->buffer = PORT_Alloc(DEFAULT_TEMP_SIZE);
|
| + PR_ASSERT(p12cxt->buffer);
|
| + }
|
| + else
|
| + {
|
| + PR_ASSERT(p12cxt->buffer);
|
| + if (!p12cxt->buffer) {
|
| + return SECFailure; /* no data to read */
|
| + }
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +p12u_DigestClose(void *arg, PRBool removeFile)
|
| +{
|
| + SEC_PKCS12DecoderContext* p12cxt = arg;
|
| +
|
| + PR_ASSERT(p12cxt);
|
| + if (!p12cxt) {
|
| + return SECFailure;
|
| + }
|
| + p12cxt->currentpos = 0;
|
| +
|
| + if (PR_TRUE == removeFile) {
|
| + PR_ASSERT(p12cxt->buffer);
|
| + if (!p12cxt->buffer) {
|
| + return SECFailure;
|
| + }
|
| + if (p12cxt->buffer) {
|
| + PORT_Free(p12cxt->buffer);
|
| + p12cxt->buffer = NULL;
|
| + p12cxt->allocated = 0;
|
| + p12cxt->filesize = 0;
|
| + }
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static int
|
| +p12u_DigestRead(void *arg, unsigned char *buf, unsigned long len)
|
| +{
|
| + int toread = len;
|
| + SEC_PKCS12DecoderContext* p12cxt = arg;
|
| +
|
| + if(!buf || len == 0 || !p12cxt->buffer) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return -1;
|
| + }
|
| +
|
| + if ((p12cxt->filesize - p12cxt->currentpos) < (long)len) {
|
| + /* trying to read past the end of the buffer */
|
| + toread = p12cxt->filesize - p12cxt->currentpos;
|
| + }
|
| + memcpy(buf, (char*)p12cxt->buffer + p12cxt->currentpos, toread);
|
| + p12cxt->currentpos += toread;
|
| + return toread;
|
| +}
|
| +
|
| +static int
|
| +p12u_DigestWrite(void *arg, unsigned char *buf, unsigned long len)
|
| +{
|
| + SEC_PKCS12DecoderContext* p12cxt = arg;
|
| +
|
| + if(!buf || len == 0) {
|
| + return -1;
|
| + }
|
| +
|
| + if (p12cxt->currentpos+(long)len > p12cxt->filesize) {
|
| + p12cxt->filesize = p12cxt->currentpos + len;
|
| + }
|
| + else {
|
| + p12cxt->filesize += len;
|
| + }
|
| + if (p12cxt->filesize > p12cxt->allocated) {
|
| + void* newbuffer;
|
| + size_t newsize = p12cxt->filesize + DEFAULT_TEMP_SIZE;
|
| + newbuffer = PORT_Realloc(p12cxt->buffer, newsize);
|
| + if (NULL == newbuffer) {
|
| + return -1; /* can't extend the buffer */
|
| + }
|
| + p12cxt->buffer = newbuffer;
|
| + p12cxt->allocated = newsize;
|
| + }
|
| + PR_ASSERT(p12cxt->buffer);
|
| + memcpy((char*)p12cxt->buffer + p12cxt->currentpos, buf, len);
|
| + p12cxt->currentpos += len;
|
| + return len;
|
| +}
|
| +
|
| +/* SEC_PKCS12DecoderStart
|
| + * Creates a decoder context for decoding a PKCS 12 PDU objct.
|
| + * This function sets up the initial decoding context for the
|
| + * PFX and sets the needed state variables.
|
| + *
|
| + * pwitem - the password for the hMac and any encoded safes.
|
| + * this should be changed to take a callback which retrieves
|
| + * the password. it may be possible for different safes to
|
| + * have different passwords. also, the password is already
|
| + * in unicode. it should probably be converted down below via
|
| + * a unicode conversion callback.
|
| + * slot - the slot to import the dataa into should multiple slots
|
| + * be supported based on key type and cert type?
|
| + * dOpen, dClose, dRead, dWrite - digest routines for writing data
|
| + * to a file so it could be read back and the hmac recomputed
|
| + * and verified. doesn't seem to be a way for both encoding
|
| + * and decoding to be single pass, thus the need for these
|
| + * routines.
|
| + * dArg - the argument for dOpen, etc.
|
| + *
|
| + * if NULL == dOpen == dClose == dRead == dWrite == dArg, then default
|
| + * implementations using a memory buffer are used
|
| + *
|
| + * This function returns the decoder context, if it was successful.
|
| + * Otherwise, null is returned.
|
| + */
|
| +SEC_PKCS12DecoderContext *
|
| +SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx,
|
| + digestOpenFn dOpen, digestCloseFn dClose,
|
| + digestIOFn dRead, digestIOFn dWrite, void *dArg)
|
| +{
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| + PLArenaPool *arena;
|
| +
|
| + arena = PORT_NewArena(2048); /* different size? */
|
| + if(!arena) {
|
| + return NULL; /* error is already set */
|
| + }
|
| +
|
| + /* allocate the decoder context and set the state variables */
|
| + p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext);
|
| + if(!p12dcx) {
|
| + goto loser; /* error is already set */
|
| + }
|
| +
|
| + if (!dOpen && !dClose && !dRead && !dWrite && !dArg) {
|
| + /* use default implementations */
|
| + dOpen = p12u_DigestOpen;
|
| + dClose = p12u_DigestClose;
|
| + dRead = p12u_DigestRead;
|
| + dWrite = p12u_DigestWrite;
|
| + dArg = (void*)p12dcx;
|
| + }
|
| +
|
| + p12dcx->arena = arena;
|
| + p12dcx->pwitem = pwitem;
|
| + p12dcx->slot = (slot ? PK11_ReferenceSlot(slot)
|
| + : PK11_GetInternalKeySlot());
|
| + p12dcx->wincx = wincx;
|
| + p12dcx->tokenCAs = SECPKCS12TargetTokenNoCAs;
|
| +#ifdef IS_LITTLE_ENDIAN
|
| + p12dcx->swapUnicodeBytes = PR_TRUE;
|
| +#else
|
| + p12dcx->swapUnicodeBytes = PR_FALSE;
|
| +#endif
|
| + p12dcx->errorValue = 0;
|
| + p12dcx->error = PR_FALSE;
|
| +
|
| + /* start the decoding of the PFX and set the notify proc
|
| + * for the PFX item.
|
| + */
|
| + p12dcx->pfxA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx,
|
| + sec_PKCS12PFXItemTemplate);
|
| + if(!p12dcx->pfxA1Dcx) {
|
| + PK11_FreeSlot(p12dcx->slot);
|
| + goto loser;
|
| + }
|
| +
|
| + SEC_ASN1DecoderSetNotifyProc(p12dcx->pfxA1Dcx,
|
| + sec_pkcs12_decoder_pfx_notify_proc,
|
| + p12dcx);
|
| +
|
| + /* set up digest functions */
|
| + p12dcx->dOpen = dOpen;
|
| + p12dcx->dWrite = dWrite;
|
| + p12dcx->dClose = dClose;
|
| + p12dcx->dRead = dRead;
|
| + p12dcx->dArg = dArg;
|
| + p12dcx->dIsOpen = PR_FALSE;
|
| +
|
| + p12dcx->keyList = NULL;
|
| + p12dcx->decitem.type = 0;
|
| + p12dcx->decitem.der = NULL;
|
| + p12dcx->decitem.hasKey = PR_FALSE;
|
| + p12dcx->decitem.friendlyName = NULL;
|
| + p12dcx->iteration = 0;
|
| +
|
| + return p12dcx;
|
| +
|
| +loser:
|
| + PORT_FreeArena(arena, PR_TRUE);
|
| + return NULL;
|
| +}
|
| +
|
| +SECStatus
|
| +SEC_PKCS12DecoderSetTargetTokenCAs(SEC_PKCS12DecoderContext *p12dcx,
|
| + SECPKCS12TargetTokenCAs tokenCAs)
|
| +{
|
| + if (!p12dcx || p12dcx->error) {
|
| + return SECFailure;
|
| + }
|
| + p12dcx->tokenCAs = tokenCAs;
|
| + return SECSuccess;
|
| +}
|
| +
|
| +
|
| +/* SEC_PKCS12DecoderUpdate
|
| + * Streaming update sending more data to the decoder. If
|
| + * an error occurs, SECFailure is returned.
|
| + *
|
| + * p12dcx - the decoder context
|
| + * data, len - the data buffer and length of data to send to
|
| + * the update functions.
|
| + */
|
| +SECStatus
|
| +SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx,
|
| + unsigned char *data, unsigned long len)
|
| +{
|
| + SECStatus rv;
|
| +
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* update the PFX decoder context */
|
| + rv = SEC_ASN1DecoderUpdate(p12dcx->pfxA1Dcx, (const char *)data, len);
|
| + if(rv != SECSuccess) {
|
| + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
|
| + goto loser;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| +
|
| + p12dcx->error = PR_TRUE;
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* This should be a nice sized buffer for reading in data (potentially large
|
| +** amounts) to be MACed. It should be MUCH larger than HASH_LENGTH_MAX.
|
| +*/
|
| +#define IN_BUF_LEN 1024
|
| +#ifdef DEBUG
|
| +static const char bufferEnd[] = { "BufferEnd" } ;
|
| +#endif
|
| +#define FUDGE 128 /* must be as large as bufferEnd or more. */
|
| +
|
| +/* verify the hmac by reading the data from the temporary file
|
| + * using the routines specified when the decodingContext was
|
| + * created and return SECSuccess if the hmac matches.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_decoder_verify_mac(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + PK11Context * pk11cx = NULL;
|
| + PK11SymKey * symKey = NULL;
|
| + SECItem * params = NULL;
|
| + unsigned char * buf;
|
| + SECStatus rv = SECFailure;
|
| + SECStatus lrv;
|
| + unsigned int bufLen;
|
| + int iteration;
|
| + int bytesRead;
|
| + SECOidTag algtag;
|
| + SECItem hmacRes;
|
| + SECItem ignore = {0};
|
| + CK_MECHANISM_TYPE integrityMech;
|
| +
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| + buf = (unsigned char *)PORT_Alloc(IN_BUF_LEN + FUDGE);
|
| + if (!buf)
|
| + return SECFailure; /* error code has been set. */
|
| +
|
| +#ifdef DEBUG
|
| + memcpy(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd);
|
| +#endif
|
| +
|
| + /* generate hmac key */
|
| + if(p12dcx->macData.iter.data) {
|
| + iteration = (int)DER_GetInteger(&p12dcx->macData.iter);
|
| + } else {
|
| + iteration = 1;
|
| + }
|
| +
|
| + params = PK11_CreatePBEParams(&p12dcx->macData.macSalt, p12dcx->pwitem,
|
| + iteration);
|
| +
|
| + algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm);
|
| + switch (algtag) {
|
| + case SEC_OID_SHA1:
|
| + integrityMech = CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN; break;
|
| + case SEC_OID_MD5:
|
| + integrityMech = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break;
|
| + case SEC_OID_MD2:
|
| + integrityMech = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break;
|
| + default:
|
| + goto loser;
|
| + }
|
| +
|
| + symKey = PK11_KeyGen(NULL, integrityMech, params, 20, NULL);
|
| + PK11_DestroyPBEParams(params);
|
| + params = NULL;
|
| + if (!symKey) goto loser;
|
| + /* init hmac */
|
| + pk11cx = PK11_CreateContextBySymKey(sec_pkcs12_algtag_to_mech(algtag),
|
| + CKA_SIGN, symKey, &ignore);
|
| + if(!pk11cx) {
|
| + goto loser;
|
| + }
|
| + lrv = PK11_DigestBegin(pk11cx);
|
| + if (lrv == SECFailure ) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* try to open the data for readback */
|
| + if(p12dcx->dOpen && ((*p12dcx->dOpen)(p12dcx->dArg, PR_TRUE)
|
| + != SECSuccess)) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* read the data back IN_BUF_LEN bytes at a time and recompute
|
| + * the hmac. if fewer bytes are read than are requested, it is
|
| + * assumed that the end of file has been reached. if bytesRead
|
| + * is returned as -1, then an error occurred reading from the
|
| + * file.
|
| + */
|
| + do {
|
| + bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN);
|
| + if (bytesRead < 0) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_READ);
|
| + goto loser;
|
| + }
|
| + PORT_Assert(bytesRead <= IN_BUF_LEN);
|
| + PORT_Assert(!memcmp(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd));
|
| +
|
| + if (bytesRead > IN_BUF_LEN) {
|
| + /* dRead callback overflowed buffer. */
|
| + PORT_SetError(SEC_ERROR_INPUT_LEN);
|
| + goto loser;
|
| + }
|
| +
|
| + if (bytesRead) {
|
| + lrv = PK11_DigestOp(pk11cx, buf, bytesRead);
|
| + if (lrv == SECFailure) {
|
| + goto loser;
|
| + }
|
| + }
|
| + } while (bytesRead == IN_BUF_LEN);
|
| +
|
| + /* finish the hmac context */
|
| + lrv = PK11_DigestFinal(pk11cx, buf, &bufLen, IN_BUF_LEN);
|
| + if (lrv == SECFailure ) {
|
| + goto loser;
|
| + }
|
| +
|
| + hmacRes.data = buf;
|
| + hmacRes.len = bufLen;
|
| +
|
| + /* is the hmac computed the same as the hmac which was decoded? */
|
| + rv = SECSuccess;
|
| + if(SECITEM_CompareItem(&hmacRes, &p12dcx->macData.safeMac.digest)
|
| + != SECEqual) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
|
| + rv = SECFailure;
|
| + }
|
| +
|
| +loser:
|
| + /* close the file and remove it */
|
| + if(p12dcx->dClose) {
|
| + (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
|
| + p12dcx->dIsOpen = PR_FALSE;
|
| + }
|
| +
|
| + if(pk11cx) {
|
| + PK11_DestroyContext(pk11cx, PR_TRUE);
|
| + }
|
| + if (params) {
|
| + PK11_DestroyPBEParams(params);
|
| + }
|
| + if (symKey) {
|
| + PK11_FreeSymKey(symKey);
|
| + }
|
| + PORT_ZFree(buf, IN_BUF_LEN + FUDGE);
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +/* SEC_PKCS12DecoderVerify
|
| + * Verify the macData or the signature of the decoded PKCS 12 PDU.
|
| + * If the signature or the macData do not match, SECFailure is
|
| + * returned.
|
| + *
|
| + * p12dcx - the decoder context
|
| + */
|
| +SECStatus
|
| +SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + SECStatus rv = SECSuccess;
|
| +
|
| + /* make sure that no errors have occurred... */
|
| + if(!p12dcx) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| + if(p12dcx->error) {
|
| + /* error code is already set! PORT_SetError(p12dcx->errorValue); */
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
|
| + p12dcx->pfxA1Dcx = NULL;
|
| + if(rv != SECSuccess) {
|
| + return rv;
|
| + }
|
| +
|
| + /* check the signature or the mac depending on the type of
|
| + * integrity used.
|
| + */
|
| + if(p12dcx->pfx.encodedMacData.len) {
|
| + rv = SEC_ASN1DecodeItem(p12dcx->arena, &p12dcx->macData,
|
| + sec_PKCS12MacDataTemplate,
|
| + &p12dcx->pfx.encodedMacData);
|
| + if(rv == SECSuccess) {
|
| + return sec_pkcs12_decoder_verify_mac(p12dcx);
|
| + }
|
| + return rv;
|
| + }
|
| + if (SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner,
|
| + PR_FALSE)) {
|
| + return SECSuccess;
|
| + }
|
| + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* SEC_PKCS12DecoderFinish
|
| + * Free any open ASN1 or PKCS7 decoder contexts and then
|
| + * free the arena pool which everything should be allocated
|
| + * from. This function should be called upon completion of
|
| + * decoding and installing of a pfx pdu. This should be
|
| + * called even if an error occurs.
|
| + *
|
| + * p12dcx - the decoder context
|
| + */
|
| +void
|
| +SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + unsigned int i;
|
| +
|
| + if(!p12dcx) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + if(p12dcx->pfxA1Dcx) {
|
| + SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
|
| + p12dcx->pfxA1Dcx = NULL;
|
| + }
|
| +
|
| + if(p12dcx->aSafeA1Dcx) {
|
| + SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
|
| + p12dcx->aSafeA1Dcx = NULL;
|
| + }
|
| +
|
| + /* cleanup any old ASN1 decoder contexts */
|
| + for (i = 0; i < p12dcx->safeContentsCnt; ++i) {
|
| + sec_PKCS12SafeContentsContext *safeContentsCtx, *nested;
|
| + safeContentsCtx = p12dcx->safeContentsList[i];
|
| + if (safeContentsCtx) {
|
| + nested = safeContentsCtx->nestedSafeContentsCtx;
|
| + while (nested) {
|
| + if (nested->safeContentsA1Dcx) {
|
| + SEC_ASN1DecoderFinish(nested->safeContentsA1Dcx);
|
| + nested->safeContentsA1Dcx = NULL;
|
| + }
|
| + nested = nested->nestedSafeContentsCtx;
|
| + }
|
| + if (safeContentsCtx->safeContentsA1Dcx) {
|
| + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
|
| + safeContentsCtx->safeContentsA1Dcx = NULL;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (p12dcx->currentASafeP7Dcx &&
|
| + p12dcx->currentASafeP7Dcx != p12dcx->aSafeP7Dcx) {
|
| + SEC_PKCS7ContentInfo * cinfo;
|
| + cinfo = SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
|
| + if (cinfo) {
|
| + SEC_PKCS7DestroyContentInfo(cinfo); /* don't leak it */
|
| + }
|
| + }
|
| + p12dcx->currentASafeP7Dcx = NULL;
|
| +
|
| + if(p12dcx->aSafeP7Dcx) {
|
| + SEC_PKCS7ContentInfo * cinfo;
|
| + cinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
|
| + if (cinfo) {
|
| + SEC_PKCS7DestroyContentInfo(cinfo);
|
| + }
|
| + p12dcx->aSafeP7Dcx = NULL;
|
| + }
|
| +
|
| + if(p12dcx->aSafeCinfo) {
|
| + SEC_PKCS7DestroyContentInfo(p12dcx->aSafeCinfo);
|
| + p12dcx->aSafeCinfo = NULL;
|
| + }
|
| +
|
| + if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) {
|
| + SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE);
|
| + }
|
| + if (p12dcx->decitem.friendlyName != NULL) {
|
| + SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE);
|
| + }
|
| +
|
| + if(p12dcx->slot) {
|
| + PK11_FreeSlot(p12dcx->slot);
|
| + p12dcx->slot = NULL;
|
| + }
|
| +
|
| + if(p12dcx->dIsOpen && p12dcx->dClose) {
|
| + (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
|
| + p12dcx->dIsOpen = PR_FALSE;
|
| + }
|
| +
|
| + if(p12dcx->arena) {
|
| + PORT_FreeArena(p12dcx->arena, PR_TRUE);
|
| + }
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_decoder_set_attribute_value(sec_PKCS12SafeBag *bag,
|
| + SECOidTag attributeType,
|
| + SECItem *attrValue)
|
| +{
|
| + int i = 0;
|
| + SECOidData *oid;
|
| +
|
| + if(!bag || !attrValue) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + oid = SECOID_FindOIDByTag(attributeType);
|
| + if(!oid) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!bag->attribs) {
|
| + bag->attribs =
|
| + PORT_ArenaZNewArray(bag->arena, sec_PKCS12Attribute *, 2);
|
| + } else {
|
| + while(bag->attribs[i])
|
| + i++;
|
| + bag->attribs = PORT_ArenaGrowArray(bag->arena, bag->attribs,
|
| + sec_PKCS12Attribute *, i + 1, i + 2);
|
| + }
|
| +
|
| + if(!bag->attribs) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + bag->attribs[i] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
|
| + if(!bag->attribs) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + bag->attribs[i]->attrValue = PORT_ArenaZNewArray(bag->arena, SECItem *, 2);
|
| + if(!bag->attribs[i]->attrValue) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + bag->attribs[i+1] = NULL;
|
| + bag->attribs[i]->attrValue[0] = attrValue;
|
| + bag->attribs[i]->attrValue[1] = NULL;
|
| +
|
| + return SECITEM_CopyItem(bag->arena, &bag->attribs[i]->attrType, &oid->oid);
|
| +}
|
| +
|
| +static SECItem *
|
| +sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag,
|
| + SECOidTag attributeType)
|
| +{
|
| + int i;
|
| +
|
| + if(!bag->attribs) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + for (i = 0; bag->attribs[i] != NULL; i++) {
|
| + if (SECOID_FindOIDTag(&bag->attribs[i]->attrType) == attributeType) {
|
| + return bag->attribs[i]->attrValue[0];
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +/* For now, this function will merely remove any ":"
|
| + * in the nickname which the PK11 functions may have
|
| + * placed there. This will keep dual certs from appearing
|
| + * twice under "Your" certificates when imported onto smart
|
| + * cards. Once with the name "Slot:Cert" and another with
|
| + * the nickname "Slot:Slot:Cert"
|
| + */
|
| +static void
|
| +sec_pkcs12_sanitize_nickname(PK11SlotInfo *slot, SECItem *nick)
|
| +{
|
| + char *nickname;
|
| + char *delimit;
|
| + int delimitlen;
|
| +
|
| + nickname = (char*)nick->data;
|
| + if ((delimit = PORT_Strchr(nickname, ':')) != NULL) {
|
| + char *slotName;
|
| + int slotNameLen;
|
| +
|
| + slotNameLen = delimit-nickname;
|
| + slotName = PORT_NewArray(char, (slotNameLen+1));
|
| + PORT_Assert(slotName);
|
| + if (slotName == NULL) {
|
| + /* What else can we do?*/
|
| + return;
|
| + }
|
| + PORT_Memcpy(slotName, nickname, slotNameLen);
|
| + slotName[slotNameLen] = '\0';
|
| + if (PORT_Strcmp(PK11_GetTokenName(slot), slotName) == 0) {
|
| + delimitlen = PORT_Strlen(delimit+1);
|
| + PORT_Memmove(nickname, delimit+1, delimitlen+1);
|
| + nick->len = delimitlen;
|
| + }
|
| + PORT_Free(slotName);
|
| + }
|
| +
|
| +}
|
| +
|
| +static SECItem *
|
| +sec_pkcs12_get_nickname(sec_PKCS12SafeBag *bag)
|
| +{
|
| + SECItem *src, *dest;
|
| +
|
| + if(!bag) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + src = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
|
| +
|
| + /* The return value src is 16-bit Unicode characters, in big-endian format.
|
| + * Check if it is NULL or empty name.
|
| + */
|
| + if(!src || !src->data || src->len < 2 || (!src->data[0] && !src->data[1])) {
|
| + return NULL;
|
| + }
|
| +
|
| + dest = (SECItem*)PORT_ZAlloc(sizeof(SECItem));
|
| + if(!dest) {
|
| + goto loser;
|
| + }
|
| + if(!sec_pkcs12_convert_item_to_unicode(NULL, dest, src, PR_FALSE,
|
| + PR_FALSE, PR_FALSE)) {
|
| + goto loser;
|
| + }
|
| +
|
| + sec_pkcs12_sanitize_nickname(bag->slot, dest);
|
| +
|
| + return dest;
|
| +
|
| +loser:
|
| + if(dest) {
|
| + SECITEM_ZfreeItem(dest, PR_TRUE);
|
| + }
|
| +
|
| + bag->problem = PR_TRUE;
|
| + bag->error = PORT_GetError();
|
| + return NULL;
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_set_nickname(sec_PKCS12SafeBag *bag, SECItem *name)
|
| +{
|
| + sec_PKCS12Attribute *attr = NULL;
|
| + SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_FRIENDLY_NAME);
|
| +
|
| + if(!bag || !bag->arena || !name) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!bag->attribs) {
|
| + if(!oid) {
|
| + goto loser;
|
| + }
|
| +
|
| + bag->attribs =
|
| + PORT_ArenaZNewArray(bag->arena, sec_PKCS12Attribute *, 2);
|
| + if(!bag->attribs) {
|
| + goto loser;
|
| + }
|
| + bag->attribs[0] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
|
| + if(!bag->attribs[0]) {
|
| + goto loser;
|
| + }
|
| + bag->attribs[1] = NULL;
|
| +
|
| + attr = bag->attribs[0];
|
| + if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid)
|
| + != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + } else {
|
| + int i;
|
| + for (i = 0; bag->attribs[i]; i++) {
|
| + if(SECOID_FindOIDTag(&bag->attribs[i]->attrType)
|
| + == SEC_OID_PKCS9_FRIENDLY_NAME) {
|
| + attr = bag->attribs[i];
|
| + break;
|
| + }
|
| + }
|
| + if(!attr) {
|
| + if(!oid) {
|
| + goto loser;
|
| + }
|
| + bag->attribs = PORT_ArenaGrowArray(bag->arena, bag->attribs,
|
| + sec_PKCS12Attribute *, i+1, i+2);
|
| + if(!bag->attribs) {
|
| + goto loser;
|
| + }
|
| + bag->attribs[i] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
|
| + if(!bag->attribs[i]) {
|
| + goto loser;
|
| + }
|
| + bag->attribs[i+1] = NULL;
|
| + attr = bag->attribs[i];
|
| + if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid)
|
| + != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + }
|
| + }
|
| +
|
| + PORT_Assert(attr);
|
| + if(!attr->attrValue) {
|
| + attr->attrValue = PORT_ArenaZNewArray(bag->arena, SECItem *, 2);
|
| + if(!attr->attrValue) {
|
| + goto loser;
|
| + }
|
| + attr->attrValue[0] = PORT_ArenaZNew(bag->arena, SECItem);
|
| + if(!attr->attrValue[0]) {
|
| + goto loser;
|
| + }
|
| + attr->attrValue[1] = NULL;
|
| + }
|
| +
|
| + name->len = PORT_Strlen((char *)name->data);
|
| + if(!sec_pkcs12_convert_item_to_unicode(bag->arena, attr->attrValue[0],
|
| + name, PR_FALSE, PR_FALSE, PR_TRUE)) {
|
| + goto loser;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + bag->problem = PR_TRUE;
|
| + bag->error = PORT_GetError();
|
| + return SECFailure;
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key)
|
| +{
|
| + int i = 0;
|
| + SECKEYPrivateKeyInfo *pki = NULL;
|
| +
|
| + if(!key) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* if the bag does *not* contain an unencrypted PrivateKeyInfo
|
| + * then we cannot convert the attributes. We are propagating
|
| + * attributes within the PrivateKeyInfo to the SafeBag level.
|
| + */
|
| + if(SECOID_FindOIDTag(&(key->safeBagType)) !=
|
| + SEC_OID_PKCS12_V1_KEY_BAG_ID) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + pki = key->safeBagContent.pkcs8KeyBag;
|
| +
|
| + if(!pki || !pki->attributes) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + while(pki->attributes[i]) {
|
| + SECOidTag tag = SECOID_FindOIDTag(&pki->attributes[i]->attrType);
|
| +
|
| + if (tag == SEC_OID_PKCS9_LOCAL_KEY_ID ||
|
| + tag == SEC_OID_PKCS9_FRIENDLY_NAME) {
|
| + SECItem *attrValue = sec_pkcs12_get_attribute_value(key, tag);
|
| + if(!attrValue) {
|
| + if(sec_pkcs12_decoder_set_attribute_value(key, tag,
|
| + pki->attributes[i]->attrValue[0])
|
| + != SECSuccess) {
|
| + key->problem = PR_TRUE;
|
| + key->error = PORT_GetError();
|
| + return SECFailure;
|
| + }
|
| + }
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* retrieve the nickname for the certificate bag. first look
|
| + * in the cert bag, otherwise get it from the key.
|
| + */
|
| +static SECItem *
|
| +sec_pkcs12_get_nickname_for_cert(sec_PKCS12SafeBag *cert,
|
| + sec_PKCS12SafeBag *key)
|
| +{
|
| + SECItem *nickname;
|
| +
|
| + if(!cert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + nickname = sec_pkcs12_get_nickname(cert);
|
| + if(nickname) {
|
| + return nickname;
|
| + }
|
| +
|
| + if(key) {
|
| + nickname = sec_pkcs12_get_nickname(key);
|
| +
|
| + if(nickname && sec_pkcs12_set_nickname(cert, nickname)
|
| + != SECSuccess) {
|
| + SECITEM_ZfreeItem(nickname, PR_TRUE);
|
| + return NULL;
|
| + }
|
| + }
|
| +
|
| + return nickname;
|
| +}
|
| +
|
| +/* set the nickname for the certificate */
|
| +static SECStatus
|
| +sec_pkcs12_set_nickname_for_cert(sec_PKCS12SafeBag *cert,
|
| + sec_PKCS12SafeBag *key,
|
| + SECItem *nickname)
|
| +{
|
| + if(!nickname || !cert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(key) {
|
| + if(sec_pkcs12_set_nickname(key, nickname) != SECSuccess) {
|
| + cert->problem = PR_TRUE;
|
| + cert->error = key->error;
|
| + return SECFailure;
|
| + }
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* retrieve the DER cert from the cert bag */
|
| +static SECItem *
|
| +sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert)
|
| +{
|
| + if(!cert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + if(SECOID_FindOIDTag(&cert->safeBagType) != SEC_OID_PKCS12_V1_CERT_BAG_ID) {
|
| + return NULL;
|
| + }
|
| +
|
| + /* only support X509 certs not SDSI */
|
| + if(SECOID_FindOIDTag(&cert->safeBagContent.certBag->bagID)
|
| + != SEC_OID_PKCS9_X509_CERT) {
|
| + return NULL;
|
| + }
|
| +
|
| + return SECITEM_DupItem(&(cert->safeBagContent.certBag->value.x509Cert));
|
| +}
|
| +
|
| +struct certNickInfo {
|
| + PLArenaPool *arena;
|
| + unsigned int nNicks;
|
| + SECItem **nickList;
|
| + unsigned int error;
|
| +};
|
| +
|
| +/* callback for traversing certificates to gather the nicknames
|
| + * used in a particular traversal. for instance, when using
|
| + * CERT_TraversePermCertsForSubject, gather the nicknames and
|
| + * store them in the certNickInfo for a particular DN.
|
| + *
|
| + * this handles the case where multiple nicknames are allowed
|
| + * for the same dn, which is not currently allowed, but may be
|
| + * in the future.
|
| + */
|
| +static SECStatus
|
| +gatherNicknames(CERTCertificate *cert, void *arg)
|
| +{
|
| + struct certNickInfo *nickArg = (struct certNickInfo *)arg;
|
| + SECItem tempNick;
|
| + unsigned int i;
|
| +
|
| + if(!cert || !nickArg || nickArg->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!cert->nickname) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + tempNick.data = (unsigned char *)cert->nickname;
|
| + tempNick.len = PORT_Strlen(cert->nickname) + 1;
|
| +
|
| + /* do we already have the nickname in the list? */
|
| + if(nickArg->nNicks > 0) {
|
| +
|
| + /* nicknames have been encountered, but there is no list -- bad */
|
| + if(!nickArg->nickList) {
|
| + nickArg->error = SEC_ERROR_INVALID_ARGS;
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + for(i = 0; i < nickArg->nNicks; i++) {
|
| + if(SECITEM_CompareItem(nickArg->nickList[i], &tempNick)
|
| + == SECEqual) {
|
| + return SECSuccess;
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* add the nickname to the list */
|
| + nickArg->nickList = (nickArg->nNicks == 0)
|
| + ? PORT_ArenaZNewArray(nickArg->arena, SECItem *, 2)
|
| + : PORT_ArenaGrowArray(nickArg->arena, nickArg->nickList, SECItem *,
|
| + nickArg->nNicks + 1, nickArg->nNicks + 2);
|
| +
|
| + if(!nickArg->nickList) {
|
| + nickArg->error = SEC_ERROR_NO_MEMORY;
|
| + return SECFailure;
|
| + }
|
| +
|
| + nickArg->nickList[nickArg->nNicks] =
|
| + PORT_ArenaZNew(nickArg->arena, SECItem);
|
| + if(!nickArg->nickList[nickArg->nNicks]) {
|
| + nickArg->error = PORT_GetError();
|
| + return SECFailure;
|
| + }
|
| +
|
| +
|
| + if(SECITEM_CopyItem(nickArg->arena, nickArg->nickList[nickArg->nNicks],
|
| + &tempNick) != SECSuccess) {
|
| + nickArg->error = PORT_GetError();
|
| + return SECFailure;
|
| + }
|
| +
|
| + nickArg->nNicks++;
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* traverses the certs in the data base or in the token for the
|
| + * DN to see if any certs currently have a nickname set.
|
| + * If so, return it.
|
| + */
|
| +static SECItem *
|
| +sec_pkcs12_get_existing_nick_for_dn(sec_PKCS12SafeBag *cert)
|
| +{
|
| + struct certNickInfo *nickArg = NULL;
|
| + SECItem *derCert, *returnDn = NULL;
|
| + PLArenaPool *arena = NULL;
|
| + CERTCertificate *tempCert;
|
| +
|
| + if(!cert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + derCert = sec_pkcs12_get_der_cert(cert);
|
| + if(!derCert) {
|
| + return NULL;
|
| + }
|
| +
|
| + tempCert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
|
| + if(!tempCert) {
|
| + returnDn = NULL;
|
| + goto loser;
|
| + }
|
| +
|
| + arena = PORT_NewArena(1024);
|
| + if(!arena) {
|
| + returnDn = NULL;
|
| + goto loser;
|
| + }
|
| + nickArg = PORT_ArenaZNew(arena, struct certNickInfo);
|
| + if(!nickArg) {
|
| + returnDn = NULL;
|
| + goto loser;
|
| + }
|
| + nickArg->error = 0;
|
| + nickArg->nNicks = 0;
|
| + nickArg->nickList = NULL;
|
| + nickArg->arena = arena;
|
| +
|
| + /* if the token is local, first traverse the cert database
|
| + * then traverse the token.
|
| + */
|
| + if(PK11_TraverseCertsForSubjectInSlot(tempCert, cert->slot, gatherNicknames,
|
| + (void *)nickArg) != SECSuccess) {
|
| + returnDn = NULL;
|
| + goto loser;
|
| + }
|
| +
|
| + if(nickArg->error) {
|
| + /* XXX do we want to set the error? */
|
| + returnDn = NULL;
|
| + goto loser;
|
| + }
|
| +
|
| + if(nickArg->nNicks == 0) {
|
| + returnDn = NULL;
|
| + goto loser;
|
| + }
|
| +
|
| + /* set it to the first name, for now. handle multiple names? */
|
| + returnDn = SECITEM_DupItem(nickArg->nickList[0]);
|
| +
|
| +loser:
|
| + if(arena) {
|
| + PORT_FreeArena(arena, PR_TRUE);
|
| + }
|
| +
|
| + if(tempCert) {
|
| + CERT_DestroyCertificate(tempCert);
|
| + }
|
| +
|
| + if(derCert) {
|
| + SECITEM_FreeItem(derCert, PR_TRUE);
|
| + }
|
| +
|
| + return (returnDn);
|
| +}
|
| +
|
| +/* counts certificates found for a given traversal function */
|
| +static SECStatus
|
| +countCertificate(CERTCertificate *cert, void *arg)
|
| +{
|
| + unsigned int *nCerts = (unsigned int *)arg;
|
| +
|
| + if(!cert || !arg) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + (*nCerts)++;
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static PRBool
|
| +sec_pkcs12_certs_for_nickname_exist(SECItem *nickname, PK11SlotInfo *slot)
|
| +{
|
| + unsigned int nCerts = 0;
|
| +
|
| + if(!nickname || !slot) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return PR_TRUE;
|
| + }
|
| +
|
| + /* we want to check the local database first if we are importing to it */
|
| + PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate,
|
| + (void *)&nCerts);
|
| + return (PRBool)(nCerts != 0);
|
| +}
|
| +
|
| +/* validate cert nickname such that there is a one-to-one relation
|
| + * between nicknames and dn's. we want to enforce the case that the
|
| + * nickname is non-NULL and that there is only one nickname per DN.
|
| + *
|
| + * if there is a problem with a nickname or the nickname is not present,
|
| + * the user will be prompted for it.
|
| + */
|
| +static void
|
| +sec_pkcs12_validate_cert_nickname(sec_PKCS12SafeBag *cert,
|
| + sec_PKCS12SafeBag *key,
|
| + SEC_PKCS12NicknameCollisionCallback nicknameCb,
|
| + CERTCertificate *leafCert)
|
| +{
|
| + SECItem *certNickname, *existingDNNick;
|
| + PRBool setNickname = PR_FALSE, cancel = PR_FALSE;
|
| + SECItem *newNickname = NULL;
|
| +
|
| + if(!cert || !cert->hasKey) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + if(!nicknameCb) {
|
| + cert->problem = PR_TRUE;
|
| + cert->error = SEC_ERROR_INVALID_ARGS;
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + if(cert->hasKey && !key) {
|
| + cert->problem = PR_TRUE;
|
| + cert->error = SEC_ERROR_INVALID_ARGS;
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + certNickname = sec_pkcs12_get_nickname_for_cert(cert, key);
|
| + existingDNNick = sec_pkcs12_get_existing_nick_for_dn(cert);
|
| +
|
| + /* nickname is already used w/ this dn, so it is safe to return */
|
| + if(certNickname && existingDNNick &&
|
| + SECITEM_CompareItem(certNickname, existingDNNick) == SECEqual) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* nickname not set in pkcs 12 bags, but a nick is already used for
|
| + * this dn. set the nicks in the p12 bags and finish.
|
| + */
|
| + if(existingDNNick) {
|
| + sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick);
|
| + goto loser;
|
| + }
|
| +
|
| + /* at this point, we have a certificate for which the DN is not located
|
| + * on the token. the nickname specified may or may not be NULL. if it
|
| + * is not null, we need to make sure that there are no other certificates
|
| + * with this nickname in the token for it to be valid. this imposes a
|
| + * one to one relationship between DN and nickname.
|
| + *
|
| + * if the nickname is null, we need the user to enter a nickname for
|
| + * the certificate.
|
| + *
|
| + * once we have a nickname, we make sure that the nickname is unique
|
| + * for the DN. if it is not, the user is reprompted to enter a new
|
| + * nickname.
|
| + *
|
| + * in order to exit this loop, the nickname entered is either unique
|
| + * or the user hits cancel and the certificate is not imported.
|
| + */
|
| + setNickname = PR_FALSE;
|
| + while(1) {
|
| + /* we will use the nickname so long as no other certs have the
|
| + * same nickname. and the nickname is not NULL.
|
| + */
|
| + if (certNickname && certNickname->data &&
|
| + !sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) {
|
| + if (setNickname) {
|
| + sec_pkcs12_set_nickname_for_cert(cert, key, certNickname);
|
| + }
|
| + break;
|
| + }
|
| +
|
| + setNickname = PR_FALSE;
|
| + newNickname = (*nicknameCb)(certNickname, &cancel, leafCert);
|
| + if(cancel) {
|
| + cert->problem = PR_TRUE;
|
| + cert->error = SEC_ERROR_USER_CANCELLED;
|
| + break;
|
| + }
|
| +
|
| + if(!newNickname) {
|
| + cert->problem = PR_TRUE;
|
| + cert->error = PORT_GetError();
|
| + break;
|
| + }
|
| +
|
| + /* at this point we have a new nickname, if we have an existing
|
| + * certNickname, we need to free it and assign the new nickname
|
| + * to it to avoid a memory leak. happy?
|
| + */
|
| + if(certNickname) {
|
| + SECITEM_ZfreeItem(certNickname, PR_TRUE);
|
| + certNickname = NULL;
|
| + }
|
| +
|
| + certNickname = newNickname;
|
| + setNickname = PR_TRUE;
|
| + /* go back and recheck the new nickname */
|
| + }
|
| +
|
| +loser:
|
| + if(certNickname) {
|
| + SECITEM_ZfreeItem(certNickname, PR_TRUE);
|
| + }
|
| +
|
| + if(existingDNNick) {
|
| + SECITEM_ZfreeItem(existingDNNick, PR_TRUE);
|
| + }
|
| +}
|
| +
|
| +static void
|
| +sec_pkcs12_validate_cert(sec_PKCS12SafeBag *cert,
|
| + sec_PKCS12SafeBag *key,
|
| + SEC_PKCS12NicknameCollisionCallback nicknameCb)
|
| +{
|
| + CERTCertificate *leafCert;
|
| +
|
| + if(!cert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + cert->validated = PR_TRUE;
|
| +
|
| + if(!nicknameCb) {
|
| + cert->noInstall = PR_TRUE;
|
| + cert->problem = PR_TRUE;
|
| + cert->error = SEC_ERROR_INVALID_ARGS;
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + if(!cert->safeBagContent.certBag) {
|
| + cert->noInstall = PR_TRUE;
|
| + cert->problem = PR_TRUE;
|
| + cert->error = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
|
| + return;
|
| + }
|
| +
|
| + cert->noInstall = PR_FALSE;
|
| + cert->unused = PR_FALSE;
|
| + cert->problem = PR_FALSE;
|
| + cert->error = 0;
|
| +
|
| + leafCert = CERT_DecodeDERCertificate(
|
| + &cert->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL);
|
| + if(!leafCert) {
|
| + cert->noInstall = PR_TRUE;
|
| + cert->problem = PR_TRUE;
|
| + cert->error = PORT_GetError();
|
| + return;
|
| + }
|
| +
|
| + sec_pkcs12_validate_cert_nickname(cert, key, nicknameCb, leafCert);
|
| +
|
| + CERT_DestroyCertificate(leafCert);
|
| +}
|
| +
|
| +static void
|
| +sec_pkcs12_validate_key_by_cert(sec_PKCS12SafeBag *cert, sec_PKCS12SafeBag *key,
|
| + void *wincx)
|
| +{
|
| + CERTCertificate *leafCert;
|
| + SECKEYPrivateKey *privk;
|
| +
|
| + if(!key) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return;
|
| + }
|
| +
|
| + key->validated = PR_TRUE;
|
| +
|
| + if(!cert) {
|
| + key->problem = PR_TRUE;
|
| + key->noInstall = PR_TRUE;
|
| + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
|
| + return;
|
| + }
|
| +
|
| + leafCert = CERT_DecodeDERCertificate(
|
| + &(cert->safeBagContent.certBag->value.x509Cert), PR_FALSE, NULL);
|
| + if(!leafCert) {
|
| + key->problem = PR_TRUE;
|
| + key->noInstall = PR_TRUE;
|
| + key->error = PORT_GetError();
|
| + return;
|
| + }
|
| +
|
| + privk = PK11_FindPrivateKeyFromCert(key->slot, leafCert, wincx);
|
| + if(!privk) {
|
| + privk = PK11_FindKeyByDERCert(key->slot, leafCert, wincx);
|
| + }
|
| +
|
| + if(privk) {
|
| + SECKEY_DestroyPrivateKey(privk);
|
| + key->noInstall = PR_TRUE;
|
| + }
|
| +
|
| + CERT_DestroyCertificate(leafCert);
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_add_cert(sec_PKCS12SafeBag *cert, PRBool keyExists, void *wincx)
|
| +{
|
| + SECItem *derCert, *nickName;
|
| + char *nickData = NULL;
|
| + PRBool isIntermediateCA;
|
| + SECStatus rv;
|
| +
|
| + if(!cert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(cert->problem || cert->noInstall || cert->installed) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + derCert = &cert->safeBagContent.certBag->value.x509Cert;
|
| +
|
| + PORT_Assert(!cert->problem && !cert->noInstall);
|
| +
|
| + nickName = sec_pkcs12_get_nickname(cert);
|
| + if(nickName) {
|
| + nickData = (char *)nickName->data;
|
| + }
|
| +
|
| + isIntermediateCA = CERT_IsCADERCert(derCert, NULL) &&
|
| + !CERT_IsRootDERCert(derCert);
|
| +
|
| + if(keyExists) {
|
| + CERTCertificate *newCert;
|
| +
|
| + newCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
|
| + derCert, NULL, PR_FALSE, PR_FALSE);
|
| + if(!newCert) {
|
| + if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE);
|
| + cert->error = PORT_GetError();
|
| + cert->problem = PR_TRUE;
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = PK11_ImportCertForKeyToSlot(cert->slot, newCert, nickData,
|
| + PR_TRUE, wincx);
|
| + CERT_DestroyCertificate(newCert);
|
| + } else if ((cert->tokenCAs == SECPKCS12TargetTokenNoCAs) ||
|
| + ((cert->tokenCAs == SECPKCS12TargetTokenIntermediateCAs) &&
|
| + !isIntermediateCA)) {
|
| + SECItem *certList[2];
|
| + certList[0] = derCert;
|
| + certList[1] = NULL;
|
| +
|
| + rv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageUserCertImport,
|
| + 1, certList, NULL, PR_TRUE, PR_FALSE, nickData);
|
| + } else {
|
| + rv = PK11_ImportDERCert(cert->slot, derCert, CK_INVALID_HANDLE,
|
| + nickData, PR_FALSE);
|
| + }
|
| + if (rv) {
|
| + cert->problem = 1;
|
| + cert->error = PORT_GetError();
|
| + }
|
| + cert->installed = PR_TRUE;
|
| + if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE);
|
| + return rv;
|
| +}
|
| +
|
| +static SECItem *
|
| +sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey, KeyType *type);
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECKEYPublicKey *pubKey,
|
| + unsigned int keyUsage,
|
| + SECItem *nickName, void *wincx)
|
| +{
|
| + SECStatus rv;
|
| + SECItem *publicValue = NULL;
|
| + KeyType keyType;
|
| +
|
| + /* We should always have values for "key" and "pubKey"
|
| + so they can be dereferenced later. */
|
| + if(!key || !pubKey) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(key->problem || key->noInstall) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + /* get the value and type from the public key */
|
| + publicValue = sec_pkcs12_get_public_value_and_type(pubKey, &keyType);
|
| + if (!publicValue) {
|
| + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
|
| + key->problem = PR_TRUE;
|
| + return SECFailure;
|
| + }
|
| +
|
| + switch(SECOID_FindOIDTag(&key->safeBagType))
|
| + {
|
| + case SEC_OID_PKCS12_V1_KEY_BAG_ID:
|
| + rv = PK11_ImportPrivateKeyInfo(key->slot,
|
| + key->safeBagContent.pkcs8KeyBag,
|
| + nickName, publicValue, PR_TRUE, PR_TRUE,
|
| + keyUsage, wincx);
|
| + break;
|
| + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
|
| + rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot,
|
| + key->safeBagContent.pkcs8ShroudedKeyBag,
|
| + key->pwitem, nickName, publicValue,
|
| + PR_TRUE, PR_TRUE, keyType, keyUsage,
|
| + wincx);
|
| + break;
|
| + default:
|
| + key->error = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
|
| + key->problem = PR_TRUE;
|
| + if(nickName) {
|
| + SECITEM_ZfreeItem(nickName, PR_TRUE);
|
| + }
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(rv != SECSuccess) {
|
| + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
|
| + key->problem = PR_TRUE;
|
| + } else {
|
| + /* try to import the public key. Failure to do so is not fatal,
|
| + * not all tokens can store the public key */
|
| + if (pubKey) {
|
| + PK11_ImportPublicKey(key->slot, pubKey, PR_TRUE);
|
| + }
|
| + key->installed = PR_TRUE;
|
| + }
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +/*
|
| + * The correctness of the code in this file ABSOLUTELY REQUIRES
|
| + * that ALL BAGs share a single common arena.
|
| + *
|
| + * This function allocates the bag list from the arena of whatever bag
|
| + * happens to be passed to it. Each time a new bag is handed to it,
|
| + * it grows (resizes) the arena of the bag that was handed to it.
|
| + * If the bags have different arenas, it will grow the wrong arena.
|
| + *
|
| + * Worse, if the bags had separate arenas, then while destroying the bags
|
| + * in a bag list, when the bag whose arena contained the bag list was
|
| + * destroyed, the baglist itself would be destroyed, making it difficult
|
| + * or impossible to continue to destroy the bags in the destroyed list.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_add_item_to_bag_list(sec_PKCS12SafeBag ***bagList,
|
| + sec_PKCS12SafeBag *bag)
|
| +{
|
| + sec_PKCS12SafeBag **newBagList = NULL;
|
| + int i = 0;
|
| +
|
| + if(!bagList || !bag) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!(*bagList)) {
|
| + newBagList = PORT_ArenaZNewArray(bag->arena, sec_PKCS12SafeBag *, 2);
|
| + } else {
|
| + while((*bagList)[i])
|
| + i++;
|
| + newBagList = PORT_ArenaGrowArray(bag->arena, *bagList,
|
| + sec_PKCS12SafeBag *, i + 1, i + 2);
|
| + }
|
| +
|
| + if(!newBagList) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return SECFailure;
|
| + }
|
| +
|
| + newBagList[i] = bag;
|
| + newBagList[i+1] = NULL;
|
| + *bagList = newBagList;
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static sec_PKCS12SafeBag **
|
| +sec_pkcs12_find_certs_for_key(sec_PKCS12SafeBag **safeBags,
|
| + sec_PKCS12SafeBag *key )
|
| +{
|
| + sec_PKCS12SafeBag **certList = NULL;
|
| + SECItem *keyId;
|
| + int i;
|
| +
|
| + if(!safeBags || !safeBags[0]) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID);
|
| + if(!keyId) {
|
| + return NULL;
|
| + }
|
| +
|
| + for (i = 0; safeBags[i]; i++) {
|
| + if(SECOID_FindOIDTag(&(safeBags[i]->safeBagType))
|
| + == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
|
| + SECItem *certKeyId = sec_pkcs12_get_attribute_value(safeBags[i],
|
| + SEC_OID_PKCS9_LOCAL_KEY_ID);
|
| +
|
| + if(certKeyId && (SECITEM_CompareItem(certKeyId, keyId)
|
| + == SECEqual)) {
|
| + if(sec_pkcs12_add_item_to_bag_list(&certList, safeBags[i])
|
| + != SECSuccess) {
|
| + /* This would leak the partial list of safeBags,
|
| + * but that list is allocated from the arena of
|
| + * one of the safebags, and will be destroyed when
|
| + * that arena is destroyed. So this is not a real leak.
|
| + */
|
| + return NULL;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + return certList;
|
| +}
|
| +
|
| +CERTCertList *
|
| +SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + CERTCertList *certList = NULL;
|
| + sec_PKCS12SafeBag **safeBags;
|
| + int i;
|
| +
|
| + if (!p12dcx || !p12dcx->safeBags || !p12dcx->safeBags[0]) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + safeBags = p12dcx->safeBags;
|
| + certList = CERT_NewCertList();
|
| +
|
| + if (certList == NULL) {
|
| + return NULL;
|
| + }
|
| +
|
| + for (i = 0; safeBags[i]; i++) {
|
| + if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType))
|
| + == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
|
| + SECItem *derCert = sec_pkcs12_get_der_cert(safeBags[i]) ;
|
| + CERTCertificate *tempCert = NULL;
|
| +
|
| + if (derCert == NULL)
|
| + continue;
|
| + tempCert=CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
|
| + derCert, NULL,
|
| + PR_FALSE, PR_TRUE);
|
| +
|
| + if (tempCert) {
|
| + CERT_AddCertToListTail(certList,tempCert);
|
| + }
|
| + SECITEM_FreeItem(derCert,PR_TRUE);
|
| + }
|
| + /* fixed an infinite loop here, by ensuring that i gets incremented
|
| + * if derCert is NULL above.
|
| + */
|
| + }
|
| +
|
| + return certList;
|
| +}
|
| +static sec_PKCS12SafeBag **
|
| +sec_pkcs12_get_key_bags(sec_PKCS12SafeBag **safeBags)
|
| +{
|
| + int i;
|
| + sec_PKCS12SafeBag **keyList = NULL;
|
| + SECOidTag bagType;
|
| +
|
| + if(!safeBags || !safeBags[0]) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + for (i = 0; safeBags[i]; i++) {
|
| + bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType));
|
| + switch(bagType) {
|
| + case SEC_OID_PKCS12_V1_KEY_BAG_ID:
|
| + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
|
| + if(sec_pkcs12_add_item_to_bag_list(&keyList, safeBags[i])
|
| + != SECSuccess) {
|
| + /* This would leak, except that keyList is allocated
|
| + * from the arena shared by all the safeBags.
|
| + */
|
| + return NULL;
|
| + }
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| + }
|
| +
|
| + return keyList;
|
| +}
|
| +
|
| +/* This function takes two passes over the bags, validating them
|
| + * The two passes are intended to mirror exactly the two passes in
|
| + * sec_pkcs12_install_bags. But they don't. :(
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags,
|
| + SEC_PKCS12NicknameCollisionCallback nicknameCb,
|
| + void *wincx)
|
| +{
|
| + sec_PKCS12SafeBag **keyList;
|
| + int i;
|
| +
|
| + if(!safeBags || !nicknameCb) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!safeBags[0]) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + /* First pass. Find all the key bags.
|
| + * Find the matching cert(s) for each key.
|
| + */
|
| + keyList = sec_pkcs12_get_key_bags(safeBags);
|
| + if(keyList) {
|
| + for (i = 0; keyList[i]; ++i) {
|
| + sec_PKCS12SafeBag *key = keyList[i];
|
| + sec_PKCS12SafeBag **certList =
|
| + sec_pkcs12_find_certs_for_key(safeBags, key);
|
| +
|
| + if(certList) {
|
| + int j;
|
| +
|
| + if(SECOID_FindOIDTag(&(key->safeBagType)) ==
|
| + SEC_OID_PKCS12_V1_KEY_BAG_ID) {
|
| + /* if it is an unencrypted private key then make sure
|
| + * the attributes are propageted to the appropriate
|
| + * level
|
| + */
|
| + if(sec_pkcs12_get_key_info(key) != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| + }
|
| +
|
| + sec_pkcs12_validate_key_by_cert(certList[0], key, wincx);
|
| + for (j = 0; certList[j]; ++j) {
|
| + sec_PKCS12SafeBag *cert = certList[j];
|
| + cert->hasKey = PR_TRUE;
|
| + if(key->problem) {
|
| + cert->problem = PR_TRUE;
|
| + cert->error = key->error;
|
| + continue;
|
| + }
|
| + sec_pkcs12_validate_cert(cert, key, nicknameCb);
|
| + if(cert->problem) {
|
| + key->problem = cert->problem;
|
| + key->error = cert->error;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* Now take a second pass over the safebags and mark for installation any
|
| + * certs that were neither installed nor disqualified by the first pass.
|
| + */
|
| + for (i = 0; safeBags[i]; ++i) {
|
| + sec_PKCS12SafeBag *bag = safeBags[i];
|
| +
|
| + if(!bag->validated) {
|
| + SECOidTag bagType = SECOID_FindOIDTag(&bag->safeBagType);
|
| +
|
| + switch(bagType) {
|
| + case SEC_OID_PKCS12_V1_CERT_BAG_ID:
|
| + sec_pkcs12_validate_cert(bag, NULL, nicknameCb);
|
| + break;
|
| + case SEC_OID_PKCS12_V1_KEY_BAG_ID:
|
| + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
|
| + bag->noInstall = PR_TRUE;
|
| + bag->problem = PR_TRUE;
|
| + bag->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
|
| + break;
|
| + default:
|
| + bag->noInstall = PR_TRUE;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +SECStatus
|
| +SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx,
|
| + SEC_PKCS12NicknameCollisionCallback nicknameCb)
|
| +{
|
| + SECStatus rv;
|
| + int i, noInstallCnt, probCnt, bagCnt, errorVal = 0;
|
| + if(!p12dcx || p12dcx->error || !p12dcx->safeBags) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = sec_pkcs12_validate_bags(p12dcx->safeBags, nicknameCb, p12dcx->wincx);
|
| + if(rv == SECSuccess) {
|
| + p12dcx->bagsVerified = PR_TRUE;
|
| + }
|
| +
|
| + noInstallCnt = probCnt = bagCnt = 0;
|
| + i = 0;
|
| + while(p12dcx->safeBags[i]) {
|
| + bagCnt++;
|
| + if(p12dcx->safeBags[i]->noInstall)
|
| + noInstallCnt++;
|
| + if(p12dcx->safeBags[i]->problem) {
|
| + probCnt++;
|
| + errorVal = p12dcx->safeBags[i]->error;
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + /* formerly was erroneous code here that assumed that if all bags
|
| + * failed to import, then the problem was duplicated data;
|
| + * that is, it assume that the problem must be that the file had
|
| + * previously been successfully imported. But importing a
|
| + * previously imported file causes NO ERRORS at all, and this
|
| + * false assumption caused real errors to be hidden behind false
|
| + * errors about duplicated data.
|
| + */
|
| +
|
| + if(probCnt) {
|
| + PORT_SetError(errorVal);
|
| + return SECFailure;
|
| + }
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +
|
| +static SECKEYPublicKey *
|
| +sec_pkcs12_get_public_key_and_usage(sec_PKCS12SafeBag *certBag,
|
| + unsigned int *usage)
|
| +{
|
| + SECKEYPublicKey *pubKey = NULL;
|
| + CERTCertificate *cert = NULL;
|
| +
|
| + if(!certBag || !usage) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + *usage = 0;
|
| +
|
| + cert = CERT_DecodeDERCertificate(
|
| + &certBag->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL);
|
| + if(!cert) {
|
| + return NULL;
|
| + }
|
| +
|
| + *usage = cert->keyUsage;
|
| + pubKey = CERT_ExtractPublicKey(cert);
|
| + CERT_DestroyCertificate(cert);
|
| + return pubKey;
|
| +}
|
| +
|
| +static SECItem *
|
| +sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey,
|
| + KeyType *type)
|
| +{
|
| + SECItem *pubValue = NULL;
|
| +
|
| + if(!type || !pubKey) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + *type = pubKey->keyType;
|
| + switch(pubKey->keyType) {
|
| + case dsaKey:
|
| + pubValue = &pubKey->u.dsa.publicValue;
|
| + break;
|
| + case dhKey:
|
| + pubValue = &pubKey->u.dh.publicValue;
|
| + break;
|
| + case rsaKey:
|
| + pubValue = &pubKey->u.rsa.modulus;
|
| + break;
|
| + case ecKey:
|
| + pubValue = &pubKey->u.ec.publicValue;
|
| + break;
|
| + default:
|
| + pubValue = NULL;
|
| + }
|
| +
|
| + return pubValue;
|
| +}
|
| +
|
| +/* This function takes two passes over the bags, installing them in the
|
| + * desired slot. The two passes are intended to mirror exactly the
|
| + * two passes in sec_pkcs12_validate_bags.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, void *wincx)
|
| +{
|
| + sec_PKCS12SafeBag **keyList;
|
| + int i;
|
| + int failedKeys = 0;
|
| +
|
| + if(!safeBags) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!safeBags[0]) {
|
| + return SECSuccess;
|
| + }
|
| +
|
| + /* First pass. Find all the key bags.
|
| + * Try to install them, and any certs associated with them.
|
| + */
|
| + keyList = sec_pkcs12_get_key_bags(safeBags);
|
| + if(keyList) {
|
| + for (i = 0; keyList[i]; i++) {
|
| + SECStatus rv;
|
| + SECKEYPublicKey *pubKey = NULL;
|
| + SECItem *nickName = NULL;
|
| + sec_PKCS12SafeBag *key = keyList[i];
|
| + sec_PKCS12SafeBag **certList;
|
| + unsigned int keyUsage;
|
| +
|
| + if(key->problem) {
|
| + ++failedKeys;
|
| + continue;
|
| + }
|
| +
|
| + certList = sec_pkcs12_find_certs_for_key(safeBags, key);
|
| + if(certList && certList[0]) {
|
| + pubKey = sec_pkcs12_get_public_key_and_usage(certList[0],
|
| + &keyUsage);
|
| + /* use the cert's nickname, if it has one, else use the
|
| + * key's nickname, else fail.
|
| + */
|
| + nickName = sec_pkcs12_get_nickname_for_cert(certList[0], key);
|
| + } else {
|
| + nickName = sec_pkcs12_get_nickname(key);
|
| + }
|
| + if (!nickName) {
|
| + key->error = SEC_ERROR_BAD_NICKNAME;
|
| + key->problem = PR_TRUE;
|
| + rv = SECFailure;
|
| + } else if (!pubKey) {
|
| + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
|
| + key->problem = PR_TRUE;
|
| + rv = SECFailure;
|
| + } else {
|
| + rv = sec_pkcs12_add_key(key, pubKey, keyUsage, nickName, wincx);
|
| + }
|
| + if (pubKey) {
|
| + SECKEY_DestroyPublicKey(pubKey);
|
| + pubKey = NULL;
|
| + }
|
| + if (nickName) {
|
| + SECITEM_FreeItem(nickName, PR_TRUE);
|
| + nickName = NULL;
|
| + }
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(key->error);
|
| + ++failedKeys;
|
| + }
|
| +
|
| + if(certList) {
|
| + int j;
|
| +
|
| + for (j = 0; certList[j]; j++) {
|
| + sec_PKCS12SafeBag *cert = certList[j];
|
| + SECStatus certRv;
|
| +
|
| + if (!cert)
|
| + continue;
|
| + if(rv != SECSuccess) {
|
| + cert->problem = key->problem;
|
| + cert->error = key->error;
|
| + cert->noInstall = PR_TRUE;
|
| + continue;
|
| + }
|
| +
|
| + certRv = sec_pkcs12_add_cert(cert, cert->hasKey, wincx);
|
| + if(certRv != SECSuccess) {
|
| + key->problem = cert->problem;
|
| + key->error = cert->error;
|
| + PORT_SetError(cert->error);
|
| + return SECFailure;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (failedKeys)
|
| + return SECFailure;
|
| +
|
| + /* Now take a second pass over the safebags and install any certs
|
| + * that were neither installed nor disqualified by the first pass.
|
| + */
|
| + for (i = 0; safeBags[i]; i++) {
|
| + sec_PKCS12SafeBag *bag = safeBags[i];
|
| +
|
| + if (!bag->installed && !bag->problem && !bag->noInstall) {
|
| + SECStatus rv;
|
| + SECOidTag bagType = SECOID_FindOIDTag(&(bag->safeBagType));
|
| +
|
| + switch(bagType) {
|
| + case SEC_OID_PKCS12_V1_CERT_BAG_ID:
|
| + rv = sec_pkcs12_add_cert(bag, bag->hasKey, wincx);
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(bag->error);
|
| + return SECFailure;
|
| + }
|
| + break;
|
| + case SEC_OID_PKCS12_V1_KEY_BAG_ID:
|
| + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
|
| + default:
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +SECStatus
|
| +SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(!p12dcx->bagsVerified) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx);
|
| +}
|
| +
|
| +PRBool
|
| +sec_pkcs12_bagHasKey(SEC_PKCS12DecoderContext *p12dcx, sec_PKCS12SafeBag *bag)
|
| +{
|
| + int i;
|
| + SECItem *keyId;
|
| + SECItem *certKeyId;
|
| +
|
| + certKeyId = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_LOCAL_KEY_ID);
|
| + if (certKeyId == NULL) {
|
| + return PR_FALSE;
|
| + }
|
| +
|
| + for (i=0; p12dcx->keyList && p12dcx->keyList[i]; i++) {
|
| + keyId = sec_pkcs12_get_attribute_value(p12dcx->keyList[i],
|
| + SEC_OID_PKCS9_LOCAL_KEY_ID);
|
| + if(!keyId) {
|
| + continue;
|
| + }
|
| + if(SECITEM_CompareItem(certKeyId, keyId) == SECEqual) {
|
| + return PR_TRUE;
|
| + }
|
| + }
|
| + return PR_FALSE;
|
| +}
|
| +
|
| +SECItem *
|
| +sec_pkcs12_get_friendlyName(sec_PKCS12SafeBag *bag)
|
| +{
|
| + SECItem *friendlyName;
|
| + SECItem *tempnm;
|
| +
|
| + tempnm = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
|
| + friendlyName = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
|
| + if (friendlyName) {
|
| + if (!sec_pkcs12_convert_item_to_unicode(NULL, friendlyName,
|
| + tempnm, PR_TRUE, PR_FALSE, PR_FALSE)) {
|
| + SECITEM_FreeItem(friendlyName, PR_TRUE);
|
| + friendlyName = NULL;
|
| + }
|
| + }
|
| + return friendlyName;
|
| +}
|
| +
|
| +/* Following two functions provide access to selected portions of the safe bags.
|
| + * Iteration is implemented per decoder context and may be accessed after
|
| + * SEC_PKCS12DecoderVerify() returns success.
|
| + * When ...DecoderIterateNext() returns SUCCESS a decoder item has been returned
|
| + * where item.type is always set; item.friendlyName is set if it is non-null;
|
| + * item.der, item.hasKey are set only for SEC_OID_PKCS12_V1_CERT_BAG_ID items.
|
| + * ...DecoderIterateNext() returns FAILURE when the list is exhausted or when
|
| + * arguments are invalid; PORT_GetError() is 0 at end-of-list.
|
| + * Caller has read-only access to decoder items. Any SECItems generated are
|
| + * owned by the decoder context and are freed by ...DecoderFinish().
|
| + */
|
| +SECStatus
|
| +SEC_PKCS12DecoderIterateInit(SEC_PKCS12DecoderContext *p12dcx)
|
| +{
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + p12dcx->iteration = 0;
|
| + return SECSuccess;
|
| +}
|
| +
|
| +SECStatus
|
| +SEC_PKCS12DecoderIterateNext(SEC_PKCS12DecoderContext *p12dcx,
|
| + const SEC_PKCS12DecoderItem **ipp)
|
| +{
|
| + sec_PKCS12SafeBag *bag;
|
| +
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) {
|
| + SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE);
|
| + }
|
| + if (p12dcx->decitem.shroudAlg != NULL) {
|
| + SECOID_DestroyAlgorithmID(p12dcx->decitem.shroudAlg, PR_TRUE);
|
| + }
|
| + if (p12dcx->decitem.friendlyName != NULL) {
|
| + SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE);
|
| + }
|
| + p12dcx->decitem.type = 0;
|
| + p12dcx->decitem.der = NULL;
|
| + p12dcx->decitem.shroudAlg = NULL;
|
| + p12dcx->decitem.friendlyName = NULL;
|
| + p12dcx->decitem.hasKey = PR_FALSE;
|
| + *ipp = NULL;
|
| + if (p12dcx->keyList == NULL) {
|
| + p12dcx->keyList = sec_pkcs12_get_key_bags(p12dcx->safeBags);
|
| + }
|
| +
|
| +
|
| + for (; p12dcx->iteration < p12dcx->safeBagCount; p12dcx->iteration++) {
|
| + bag = p12dcx->safeBags[p12dcx->iteration];
|
| + if(bag == NULL || bag->problem) {
|
| + continue;
|
| + }
|
| + p12dcx->decitem.type = SECOID_FindOIDTag(&(bag->safeBagType));
|
| + switch(p12dcx->decitem.type) {
|
| + case SEC_OID_PKCS12_V1_CERT_BAG_ID:
|
| + p12dcx->decitem.der = sec_pkcs12_get_der_cert(bag);
|
| + p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
|
| + p12dcx->decitem.hasKey = sec_pkcs12_bagHasKey(p12dcx, bag);
|
| + break;
|
| + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
|
| + p12dcx->decitem.shroudAlg = PORT_ZNew(SECAlgorithmID);
|
| + if (p12dcx->decitem.shroudAlg) {
|
| + SECOID_CopyAlgorithmID(NULL, p12dcx->decitem.shroudAlg,
|
| + &bag->safeBagContent.pkcs8ShroudedKeyBag->algorithm);
|
| + }
|
| + case SEC_OID_PKCS12_V1_KEY_BAG_ID:
|
| + p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
|
| + break;
|
| + default:
|
| + /* return these even though we don't expect them */
|
| + break;
|
| + case SEC_OID_UNKNOWN:
|
| + /* ignore these */
|
| + continue;
|
| + }
|
| + *ipp = &p12dcx->decitem;
|
| + p12dcx->iteration++;
|
| + break; /* end for() */
|
| + }
|
| +
|
| + PORT_SetError(0); /* end-of-list is SECFailure with no PORT error */
|
| + return ((p12dcx->decitem.type == 0) ? SECFailure : SECSuccess);
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx,
|
| + sec_PKCS12SafeBag *bag)
|
| +{
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + p12dcx->safeBags = !p12dcx->safeBagCount
|
| + ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
|
| + : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
|
| + sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
|
| + p12dcx->safeBagCount + 2);
|
| +
|
| + if(!p12dcx->safeBags) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return SECFailure;
|
| + }
|
| +
|
| + p12dcx->safeBags[p12dcx->safeBagCount] = bag;
|
| + p12dcx->safeBags[p12dcx->safeBagCount+1] = NULL;
|
| + p12dcx->safeBagCount++;
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static sec_PKCS12SafeBag *
|
| +sec_pkcs12_decoder_convert_old_key(SEC_PKCS12DecoderContext *p12dcx,
|
| + void *key, PRBool isEspvk)
|
| +{
|
| + sec_PKCS12SafeBag *keyBag;
|
| + SECOidData *oid;
|
| + SECOidTag keyTag;
|
| + SECItem *keyID, *nickName, *newNickName;
|
| +
|
| + if(!p12dcx || p12dcx->error || !key) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + newNickName = PORT_ArenaZNew(p12dcx->arena, SECItem);
|
| + keyBag = PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
|
| + if(!keyBag || !newNickName) {
|
| + return NULL;
|
| + }
|
| +
|
| + keyBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
|
| + keyBag->slot = p12dcx->slot;
|
| + keyBag->arena = p12dcx->arena;
|
| + keyBag->pwitem = p12dcx->pwitem;
|
| + keyBag->tokenCAs = p12dcx->tokenCAs;
|
| + keyBag->oldBagType = PR_TRUE;
|
| +
|
| + keyTag = (isEspvk) ? SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID :
|
| + SEC_OID_PKCS12_V1_KEY_BAG_ID;
|
| + oid = SECOID_FindOIDByTag(keyTag);
|
| + if(!oid) {
|
| + return NULL;
|
| + }
|
| +
|
| + if(SECITEM_CopyItem(p12dcx->arena, &keyBag->safeBagType, &oid->oid)
|
| + != SECSuccess) {
|
| + return NULL;
|
| + }
|
| +
|
| + if(isEspvk) {
|
| + SEC_PKCS12ESPVKItem *espvk = (SEC_PKCS12ESPVKItem *)key;
|
| + keyBag->safeBagContent.pkcs8ShroudedKeyBag =
|
| + espvk->espvkCipherText.pkcs8KeyShroud;
|
| + nickName = &(espvk->espvkData.uniNickName);
|
| + if(!espvk->espvkData.assocCerts || !espvk->espvkData.assocCerts[0]) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
|
| + return NULL;
|
| + }
|
| + keyID = &espvk->espvkData.assocCerts[0]->digest;
|
| + } else {
|
| + SEC_PKCS12PrivateKey *pk = (SEC_PKCS12PrivateKey *)key;
|
| + keyBag->safeBagContent.pkcs8KeyBag = &pk->pkcs8data;
|
| + nickName= &(pk->pvkData.uniNickName);
|
| + if(!pk->pvkData.assocCerts || !pk->pvkData.assocCerts[0]) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
|
| + return NULL;
|
| + }
|
| + keyID = &pk->pvkData.assocCerts[0]->digest;
|
| + }
|
| +
|
| + if(nickName->len) {
|
| + if(nickName->len >= 2) {
|
| + if(nickName->data[0] && nickName->data[1]) {
|
| + if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName,
|
| + nickName, PR_FALSE, PR_FALSE, PR_TRUE)) {
|
| + return NULL;
|
| + }
|
| + nickName = newNickName;
|
| + } else if(nickName->data[0] && !nickName->data[1]) {
|
| + unsigned int j = 0;
|
| + unsigned char t;
|
| + for(j = 0; j < nickName->len; j+=2) {
|
| + t = nickName->data[j+1];
|
| + nickName->data[j+1] = nickName->data[j];
|
| + nickName->data[j] = t;
|
| + }
|
| + }
|
| + } else {
|
| + if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName,
|
| + nickName, PR_FALSE, PR_FALSE, PR_TRUE)) {
|
| + return NULL;
|
| + }
|
| + nickName = newNickName;
|
| + }
|
| + }
|
| +
|
| + if(sec_pkcs12_decoder_set_attribute_value(keyBag,
|
| + SEC_OID_PKCS9_FRIENDLY_NAME,
|
| + nickName) != SECSuccess) {
|
| + return NULL;
|
| + }
|
| +
|
| + if(sec_pkcs12_decoder_set_attribute_value(keyBag,SEC_OID_PKCS9_LOCAL_KEY_ID,
|
| + keyID) != SECSuccess) {
|
| + return NULL;
|
| + }
|
| +
|
| + return keyBag;
|
| +}
|
| +
|
| +static sec_PKCS12SafeBag *
|
| +sec_pkcs12_decoder_create_cert(SEC_PKCS12DecoderContext *p12dcx,
|
| + SECItem *derCert)
|
| +{
|
| + sec_PKCS12SafeBag *certBag;
|
| + SECOidData *oid;
|
| + SGNDigestInfo *digest;
|
| + SECItem *keyId;
|
| + SECStatus rv;
|
| +
|
| + if(!p12dcx || p12dcx->error || !derCert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + keyId = PORT_ArenaZNew(p12dcx->arena, SECItem);
|
| + if(!keyId) {
|
| + return NULL;
|
| + }
|
| +
|
| + digest = sec_pkcs12_compute_thumbprint(derCert);
|
| + if(!digest) {
|
| + return NULL;
|
| + }
|
| +
|
| + rv = SECITEM_CopyItem(p12dcx->arena, keyId, &digest->digest);
|
| + SGN_DestroyDigestInfo(digest);
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| +
|
| + oid = SECOID_FindOIDByTag(SEC_OID_PKCS12_V1_CERT_BAG_ID);
|
| + certBag = PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
|
| + if(!certBag || !oid || (SECITEM_CopyItem(p12dcx->arena,
|
| + &certBag->safeBagType, &oid->oid) != SECSuccess)) {
|
| + return NULL;
|
| + }
|
| +
|
| + certBag->slot = p12dcx->slot;
|
| + certBag->pwitem = p12dcx->pwitem;
|
| + certBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
|
| + certBag->arena = p12dcx->arena;
|
| + certBag->tokenCAs = p12dcx->tokenCAs;
|
| +
|
| + oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_X509_CERT);
|
| + certBag->safeBagContent.certBag =
|
| + PORT_ArenaZNew(p12dcx->arena, sec_PKCS12CertBag);
|
| + if(!certBag->safeBagContent.certBag || !oid ||
|
| + (SECITEM_CopyItem(p12dcx->arena,
|
| + &certBag->safeBagContent.certBag->bagID,
|
| + &oid->oid) != SECSuccess)) {
|
| + return NULL;
|
| + }
|
| +
|
| + if(SECITEM_CopyItem(p12dcx->arena,
|
| + &(certBag->safeBagContent.certBag->value.x509Cert),
|
| + derCert) != SECSuccess) {
|
| + return NULL;
|
| + }
|
| +
|
| + if(sec_pkcs12_decoder_set_attribute_value(certBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
|
| + keyId) != SECSuccess) {
|
| + return NULL;
|
| + }
|
| +
|
| + return certBag;
|
| +}
|
| +
|
| +static sec_PKCS12SafeBag **
|
| +sec_pkcs12_decoder_convert_old_cert(SEC_PKCS12DecoderContext *p12dcx,
|
| + SEC_PKCS12CertAndCRL *oldCert)
|
| +{
|
| + sec_PKCS12SafeBag **certList;
|
| + SECItem **derCertList;
|
| + int i, j;
|
| +
|
| + if(!p12dcx || p12dcx->error || !oldCert) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + derCertList = SEC_PKCS7GetCertificateList(&oldCert->value.x509->certOrCRL);
|
| + if(!derCertList) {
|
| + return NULL;
|
| + }
|
| +
|
| + i = 0;
|
| + while(derCertList[i]) i++;
|
| +
|
| + certList = PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, (i + 1));
|
| + if(!certList) {
|
| + return NULL;
|
| + }
|
| +
|
| + for(j = 0; j < i; j++) {
|
| + certList[j] = sec_pkcs12_decoder_create_cert(p12dcx, derCertList[j]);
|
| + if(!certList[j]) {
|
| + return NULL;
|
| + }
|
| + }
|
| +
|
| + return certList;
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_decoder_convert_old_key_and_certs(SEC_PKCS12DecoderContext *p12dcx,
|
| + void *oldKey, PRBool isEspvk,
|
| + SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12Baggage *baggage)
|
| +{
|
| + sec_PKCS12SafeBag *key, **certList;
|
| + SEC_PKCS12CertAndCRL *oldCert;
|
| + SEC_PKCS12PVKSupportingData *pvkData;
|
| + int i;
|
| + SECItem *keyName;
|
| +
|
| + if(!p12dcx || !oldKey) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(isEspvk) {
|
| + pvkData = &((SEC_PKCS12ESPVKItem *)(oldKey))->espvkData;
|
| + } else {
|
| + pvkData = &((SEC_PKCS12PrivateKey *)(oldKey))->pvkData;
|
| + }
|
| +
|
| + if(!pvkData->assocCerts || !pvkData->assocCerts[0]) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
|
| + return SECFailure;
|
| + }
|
| +
|
| + oldCert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
|
| + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, NULL,
|
| + pvkData->assocCerts[0]);
|
| + if(!oldCert) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
|
| + return SECFailure;
|
| + }
|
| +
|
| + key = sec_pkcs12_decoder_convert_old_key(p12dcx,oldKey, isEspvk);
|
| + certList = sec_pkcs12_decoder_convert_old_cert(p12dcx, oldCert);
|
| + if(!key || !certList) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, key) != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + keyName = sec_pkcs12_get_nickname(key);
|
| + if(!keyName) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + i = 0;
|
| + while(certList[i]) {
|
| + if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, certList[i])
|
| + != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + certList = sec_pkcs12_find_certs_for_key(p12dcx->safeBags, key);
|
| + if(!certList) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + i = 0;
|
| + while(certList[i] != 0) {
|
| + if(sec_pkcs12_set_nickname(certList[i], keyName) != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +sec_pkcs12_decoder_convert_old_safe_to_bags(SEC_PKCS12DecoderContext *p12dcx,
|
| + SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12Baggage *baggage)
|
| +{
|
| + SECStatus rv;
|
| +
|
| + if(!p12dcx || p12dcx->error) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if(safe && safe->contents) {
|
| + int i = 0;
|
| + while(safe->contents[i] != NULL) {
|
| + if(SECOID_FindOIDTag(&safe->contents[i]->safeBagType)
|
| + == SEC_OID_PKCS12_KEY_BAG_ID) {
|
| + int j = 0;
|
| + SEC_PKCS12PrivateKeyBag *privBag =
|
| + safe->contents[i]->safeContent.keyBag;
|
| +
|
| + while(privBag->privateKeys[j] != NULL) {
|
| + SEC_PKCS12PrivateKey *pk = privBag->privateKeys[j];
|
| + rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx,pk,
|
| + PR_FALSE, safe, baggage);
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + j++;
|
| + }
|
| + }
|
| + i++;
|
| + }
|
| + }
|
| +
|
| + if(baggage && baggage->bags) {
|
| + int i = 0;
|
| + while(baggage->bags[i] != NULL) {
|
| + SEC_PKCS12BaggageItem *bag = baggage->bags[i];
|
| + int j = 0;
|
| +
|
| + if(!bag->espvks) {
|
| + i++;
|
| + continue;
|
| + }
|
| +
|
| + while(bag->espvks[j] != NULL) {
|
| + SEC_PKCS12ESPVKItem *espvk = bag->espvks[j];
|
| + rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx, espvk,
|
| + PR_TRUE, safe, baggage);
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + j++;
|
| + }
|
| + i++;
|
| + }
|
| + }
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + return SECFailure;
|
| +}
|
| +
|
| +SEC_PKCS12DecoderContext *
|
| +sec_PKCS12ConvertOldSafeToNew(PLArenaPool *arena, PK11SlotInfo *slot,
|
| + PRBool swapUnicode, SECItem *pwitem,
|
| + void *wincx, SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12Baggage *baggage)
|
| +{
|
| + SEC_PKCS12DecoderContext *p12dcx;
|
| +
|
| + if(!arena || !slot || !pwitem) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + if(!safe && !baggage) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + return NULL;
|
| + }
|
| +
|
| + p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext);
|
| + if(!p12dcx) {
|
| + return NULL;
|
| + }
|
| +
|
| + p12dcx->arena = arena;
|
| + p12dcx->slot = PK11_ReferenceSlot(slot);
|
| + p12dcx->wincx = wincx;
|
| + p12dcx->error = PR_FALSE;
|
| + p12dcx->swapUnicodeBytes = swapUnicode;
|
| + p12dcx->pwitem = pwitem;
|
| + p12dcx->tokenCAs = SECPKCS12TargetTokenNoCAs;
|
| +
|
| + if(sec_pkcs12_decoder_convert_old_safe_to_bags(p12dcx, safe, baggage)
|
| + != SECSuccess) {
|
| + p12dcx->error = PR_TRUE;
|
| + return NULL;
|
| + }
|
| +
|
| + return p12dcx;
|
| +}
|
|
|