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