/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Opaque structure for decoding SafeContents. These are used *foreachauthenticatedsafeaswellasanynestedsafecontents.
*/ struct sec_PKCS12SafeContentsContextStr { /* the parent decoder context */
SEC_PKCS12DecoderContext *p12dcx;
/* memory arena to allocate space from */
PLArenaPool *arena;
/* decoder context and destination for decoding safe contents */
SEC_ASN1DecoderContext *safeContentsA1Dcx;
sec_PKCS12SafeContents safeContents;
/* information for decoding safe bags within the safe contents. *thesevariablesareupdatedforeachsafebagdecoded.
*/
SEC_ASN1DecoderContext *currentSafeBagA1Dcx;
sec_PKCS12SafeBag *currentSafeBag;
PRBool skipCurrentSafeBag;
/* if the safe contents is nested, the parent is pointed to here. */
sec_PKCS12SafeContentsContext *nestedSafeContentsCtx;
};
/* opaque decoder context structure. information for decoding a pkcs 12 *PDUarestoredhereaswellasdecodingpointersforintermediary *structureswhicharepartofthePKCS12PDU.Uponasuccessful *decode,thesafebagscontainingcertificatesandkeysencountered.
*/ struct SEC_PKCS12DecoderContextStr {
PLArenaPool *arena;
PK11SlotInfo *slot; void *wincx;
PRBool error; int errorValue;
/* password */
SECItem *pwitem;
/* used for decoding the PFX structure */
SEC_ASN1DecoderContext *pfxA1Dcx;
sec_PKCS12PFXItem pfx;
/* safe bags found during decoding */
sec_PKCS12SafeBag **safeBags; unsignedint safeBagCount;
/* state variables for decoding authenticated safes. */
SEC_PKCS7DecoderContext *currentASafeP7Dcx;
SEC_ASN1DecoderContext *aSafeA1Dcx;
SEC_PKCS7DecoderContext *aSafeP7Dcx;
SEC_PKCS7ContentInfo *aSafeCinfo;
sec_PKCS12AuthenticatedSafe authSafe;
sec_PKCS12SafeContents safeContents;
/* safe contents info */ unsignedint safeContentsCnt;
sec_PKCS12SafeContentsContext **safeContentsList;
/* HMAC info */
sec_PKCS12MacData macData;
/* routines for reading back the data to be hmac'd */ /* They are called as follows. * *Stage1:decodetheaSafescinfointoabufferindArg, *whichp12d.csometimesreferstoasthe"tempfile". *ThisoccursduringSEC_PKCS12DecoderUpdatecalls. * *dOpen(dArg,PR_FALSE) *dWrite(dArg,buf,len) *... *dWrite(dArg,buf,len) *dClose(dArg,PR_FALSE) * *Stage2:verifyMAC *ThisoccursSEC_PKCS12DecoderVerify. * *dOpen(dArg,PR_TRUE) *dRead(dArg,buf,IN_BUF_LEN) *... *dRead(dArg,buf,IN_BUF_LEN) *dClose(dArg,PR_TRUE)
*/
digestOpenFn dOpen;
digestCloseFn dClose;
digestIOFn dRead, dWrite; void *dArg;
PRBool dIsOpen; /* is the temp file created? */
/* buffer management for the default callbacks implementation */ void *buffer; /* storage area */
PRInt32 filesize; /* actual data size */
PRInt32 allocated; /* total buffer size allocated */
PRInt32 currentpos; /* position counter */
SECPKCS12TargetTokenCAs tokenCAs;
sec_PKCS12SafeBag **keyList; /* used by ...IterateNext() */ unsignedint iteration;
SEC_PKCS12DecoderItem decitem;
};
/* forward declarations of functions that are used when decoding *safeContentsbagswhicharenestedandwhendecodingthe *authenticatedSafes.
*/ static SECStatus
sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
*safeContentsCtx); static SECStatus
sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
*safeContentsCtx);
/* make sure that the PFX version being decoded is a version *whichwesupport.
*/ static PRBool
sec_pkcs12_proper_version(sec_PKCS12PFXItem *pfx)
{ /* if no version, assume it is not supported */ if (pfx->version.len == 0) { return PR_FALSE;
}
if (DER_GetInteger(&pfx->version) > SEC_PKCS12_VERSION) { return PR_FALSE;
}
/* if no slot specified, use the internal key slot */ if (p12dcx->slot) {
slot = PK11_ReferenceSlot(p12dcx->slot);
} else {
slot = PK11_GetInternalKeySlot();
}
algorithm = SECOID_GetAlgorithmTag(algid);
if (p12dcx->forceUnicode) { if (SECITEM_CopyItem(NULL, &pwitem, p12dcx->pwitem) != SECSuccess) {
PK11_FreeSlot(slot); return NULL;
}
} else { if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm, p12dcx->pwitem)) {
PK11_FreeSlot(slot); return NULL;
}
}
bulkKey = PK11_PBEKeyGen(slot, algid, &pwitem, PR_FALSE, p12dcx->wincx); /* some tokens can't generate PBE keys on their own, generate the *keyintheinternalslot,andlettheImportcodedealwithit, *(iftheslotcan'tgeneratePBEs,thenweneedtousetheinternal
* slot anyway to unwrap). */ if (!bulkKey && !PK11_IsInternal(slot)) {
PK11_FreeSlot(slot);
slot = PK11_GetInternalKeySlot();
bulkKey = PK11_PBEKeyGen(slot, algid, &pwitem, PR_FALSE, p12dcx->wincx);
}
PK11_FreeSlot(slot);
/* set the password data on the key */ if (bulkKey) {
PK11_SetSymKeyUserData(bulkKey, p12dcx->pwitem, NULL);
}
if (pwitem.data) {
SECITEM_ZfreeItem(&pwitem, PR_FALSE);
}
return bulkKey;
}
/* XXX this needs to be modified to handle enveloped data. most *likely,itshouldmirrortheroutinesforSMIMEinthatregard.
*/ static PRBool
sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid,
PK11SymKey *bulkkey)
{
PRBool decryptionAllowed = SEC_PKCS12DecryptionAllowed(algid);
if (!decryptionAllowed) {
PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); return PR_FALSE;
}
return PR_TRUE;
}
/* when we encounter a new safe bag during the decoding, we need *toallocatespaceforthebagtobedecodedtoandsetthe *statevariablesappropriately.allofthesafebagsareallocated *inabufferintheouterSEC_PKCS12DecoderContext,however, *apointertothesafeBagisalsousedinthesec_PKCS12SafeContentsContext *forthecurrentbag.
*/ static SECStatus
sec_pkcs12_decoder_init_new_safe_bag(sec_PKCS12SafeContentsContext
*safeContentsCtx)
{ void *mark = NULL;
SEC_PKCS12DecoderContext *p12dcx;
/* make sure that the structures are defined, and there has *notbeenanerrorinthedecoding
*/ if (!safeContentsCtx || !safeContentsCtx->p12dcx || safeContentsCtx->p12dcx->error) { return SECFailure;
}
p12dcx = safeContentsCtx->p12dcx;
mark = PORT_ArenaMark(p12dcx->arena);
/* allocate a new safe bag, if bags already exist, grow the *listofbags,otherwiseallocateanewlist.thelistis *NULLterminated.
*/
p12dcx->safeBags = (!p12dcx->safeBagCount)
? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
: PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
p12dcx->safeBagCount + 2);
if (!p12dcx->safeBags) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
/* append the bag to the end of the list and update the reference *inthesafeContentsCtx.
*/
p12dcx->safeBags[p12dcx->safeBagCount] =
safeContentsCtx->currentSafeBag =
PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag); if (!safeContentsCtx->currentSafeBag) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
p12dcx->safeBags[++p12dcx->safeBagCount] = NULL;
/* if an error occurred, release the memory and set the error flag *theonlypossibleerrorstriggeredbythisfunctionarememory *related.
*/ if (mark) {
PORT_ArenaRelease(p12dcx->arena, mark);
}
p12dcx->error = PR_TRUE; return SECFailure;
}
/* A wrapper for updating the ASN1 context in which a safeBag is *beingdecoded.Thisfunctioniscalledasacallbackfrom *secasn1dwhendecodingSafeContentsstructures.
*/ staticvoid
sec_pkcs12_decoder_safe_bag_update(void *arg, constchar *data, unsignedlong len, int depth,
SEC_ASN1EncodingPart data_kind)
{
sec_PKCS12SafeContentsContext *safeContentsCtx =
(sec_PKCS12SafeContentsContext *)arg;
SEC_PKCS12DecoderContext *p12dcx;
SECStatus rv;
/* make sure that there are no errors and we are not skipping the current safeBag */ if (p12dcx->error || safeContentsCtx->skipCurrentSafeBag) { goto loser;
}
/* The update may have set safeContentsCtx->skipCurrentSafeBag, and we *maynotgetanotheropportunitytocleanupthedecodercontext.
*/ if (safeContentsCtx->skipCurrentSafeBag) { goto loser;
}
return;
loser: /* Finish the decoder context. Because there *isnotawayofreturninganerrormessage,itmaybeworth *whiletodoacheckhigherupandfinishanydecodingcontexts *thatarestillopen.
*/
SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
safeContentsCtx->currentSafeBagA1Dcx = NULL; return;
}
/* notify function for decoding safeBags. This function is *usedtofiltersafeBagtypeswhicharenotsupported, *initiatethedecodingofnestedsafecontents,anddecode *safeBagsingeneral.thisfunctionissetwhenthedecoder *contextforthesafeBagisfirstcreated.
*/ staticvoid
sec_pkcs12_decoder_safe_bag_notify(void *arg, PRBool before, void *dest, int real_depth)
{
sec_PKCS12SafeContentsContext *safeContentsCtx =
(sec_PKCS12SafeContentsContext *)arg;
SEC_PKCS12DecoderContext *p12dcx;
sec_PKCS12SafeBag *bag;
PRBool after;
/* if an error is encountered, return */ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
safeContentsCtx->p12dcx->error) { return;
}
p12dcx = safeContentsCtx->p12dcx;
/* to make things more readable */ if (before)
after = PR_FALSE; else
after = PR_TRUE;
/* have we determined the safeBagType yet? */
bag = safeContentsCtx->currentSafeBag; if (bag->bagTypeTag == NULL) { if (after && (dest == &(bag->safeBagType))) {
bag->bagTypeTag = SECOID_FindOID(&(bag->safeBagType)); if (bag->bagTypeTag == NULL) {
p12dcx->error = PR_TRUE;
p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
}
} return;
}
/* process the safeBag depending on it's type. those *whichwedonotsupport,areignored.westartadecoding *contextforanestedsafeContents.
*/ switch (bag->bagTypeTag->offset) { case SEC_OID_PKCS12_V1_KEY_BAG_ID: case SEC_OID_PKCS12_V1_CERT_BAG_ID: case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: break; case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: /* if we are just starting to decode the safeContents, initialize *anewsafeContentsCtxtoprocessit.
*/ if (before && (dest == &(bag->safeBagContent))) {
sec_pkcs12_decoder_begin_nested_safe_contents(safeContentsCtx);
} elseif (after && (dest == &(bag->safeBagContent))) { /* clean up the nested decoding */
sec_pkcs12_decoder_finish_nested_safe_contents(safeContentsCtx);
} break; case SEC_OID_PKCS12_V1_CRL_BAG_ID: case SEC_OID_PKCS12_V1_SECRET_BAG_ID: default: /* skip any safe bag types we don't understand or handle */
safeContentsCtx->skipCurrentSafeBag = PR_TRUE; break;
}
return;
}
/* notify function for decoding safe contents. each entry in the *safecontentsisasafeBagwhichneedstobeallocatedand *thedecodingcontextinitializedatthebeginningandthen *thecontextneedstobeclosedandfinishedattheend. * *thisfunctionissetwhenthesafeContentsdecodecontextis *initialized.
*/ staticvoid
sec_pkcs12_decoder_safe_contents_notify(void *arg, PRBool before, void *dest, int real_depth)
{
sec_PKCS12SafeContentsContext *safeContentsCtx =
(sec_PKCS12SafeContentsContext *)arg;
SEC_PKCS12DecoderContext *p12dcx;
SECStatus rv;
/* if there is an error we don't want to continue processing, *justreturnandkeepgoing.
*/ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
safeContentsCtx->p12dcx->error) { return;
}
p12dcx = safeContentsCtx->p12dcx;
/* if we are done with the current safeBag, then we need to *finishthecontextandsetthestatevariablesappropriately.
*/ if (!before) {
SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
safeContentsCtx->currentSafeBagA1Dcx = NULL;
safeContentsCtx->skipCurrentSafeBag = PR_FALSE;
} else { /* we are starting a new safe bag. we need to allocate space *forthebagandinitializethedecodingcontext.
*/
rv = sec_pkcs12_decoder_init_new_safe_bag(safeContentsCtx); if (rv != SECSuccess) { goto loser;
}
/* set up the decoder context */
safeContentsCtx->currentSafeBagA1Dcx =
SEC_ASN1DecoderStart(p12dcx->arena,
safeContentsCtx->currentSafeBag,
sec_PKCS12SafeBagTemplate); if (!safeContentsCtx->currentSafeBagA1Dcx) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
/* set the notify and filter procs so that the safe bag *datagetssenttotheproperlocationwhendecoding.
*/
SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->currentSafeBagA1Dcx,
sec_pkcs12_decoder_safe_bag_notify,
safeContentsCtx);
SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsA1Dcx,
sec_pkcs12_decoder_safe_bag_update,
safeContentsCtx, PR_TRUE);
}
return;
loser: /* in the event of an error, we want to close the decoding *contextandclearthefilterandnotifyprocedures.
*/
p12dcx->error = PR_TRUE;
if (safeContentsCtx->currentSafeBagA1Dcx) {
SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
safeContentsCtx->currentSafeBagA1Dcx = NULL;
}
/* initialize the safeContents for decoding. this routine *isusedforauthenticatedSafesaswellasnestedsafeContents.
*/ static sec_PKCS12SafeContentsContext *
sec_pkcs12_decoder_safe_contents_init_decode(SEC_PKCS12DecoderContext *p12dcx,
PRBool nestedSafe)
{
sec_PKCS12SafeContentsContext *safeContentsCtx = NULL; const SEC_ASN1Template *theTemplate;
if (!p12dcx || p12dcx->error) { return NULL;
}
/* allocate a new safeContents list or grow the existing list and *appendthenewsafeContentsontotheend.
*/
p12dcx->safeContentsList = (!p12dcx->safeContentsCnt)
? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeContentsContext *, 2)
: PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeContentsList,
sec_PKCS12SafeContentsContext *, 1 + p12dcx->safeContentsCnt, 2 + p12dcx->safeContentsCnt);
if (!p12dcx->safeContentsList) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
/* set up the state variables */
safeContentsCtx->p12dcx = p12dcx;
safeContentsCtx->arena = p12dcx->arena;
/* begin the decoding -- the template is based on whether we are *decodinganestedsafeContentsornot.
*/ if (nestedSafe == PR_TRUE) {
theTemplate = sec_PKCS12NestedSafeContentsDecodeTemplate;
} else {
theTemplate = sec_PKCS12SafeContentsDecodeTemplate;
}
if (!safeContentsCtx->safeContentsA1Dcx) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
/* set the safeContents notify procedure to look for *andstartthedecodeofsafeBags.
*/
SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsA1Dcx,
sec_pkcs12_decoder_safe_contents_notify,
safeContentsCtx);
return safeContentsCtx;
loser: /* in the case of an error, we want to finish the decoder *contextandsettheerrorflag.
*/ if (safeContentsCtx && safeContentsCtx->safeContentsA1Dcx) {
SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
safeContentsCtx->safeContentsA1Dcx = NULL;
}
p12dcx->error = PR_TRUE;
return NULL;
}
/* wrapper for updating safeContents. this is set as the filter of *safeBagwhenthereisanestedsafeContents.
*/ staticvoid
sec_pkcs12_decoder_nested_safe_contents_update(void *arg, constchar *buf, unsignedlong len, int depth,
SEC_ASN1EncodingPart data_kind)
{
sec_PKCS12SafeContentsContext *safeContentsCtx =
(sec_PKCS12SafeContentsContext *)arg;
SEC_PKCS12DecoderContext *p12dcx;
SECStatus rv;
/* check for an error */ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
safeContentsCtx->p12dcx->error || !safeContentsCtx->safeContentsA1Dcx) { return;
}
/* no need to update if no data sent in */ if (!len || !buf) { return;
}
loser: /* handle any errors. If a decoding context is open, close it. */
p12dcx->error = PR_TRUE; if (safeContentsCtx->safeContentsA1Dcx) {
SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
safeContentsCtx->safeContentsA1Dcx = NULL;
}
}
/* whenever a new safeContentsSafeBag is encountered, we need *toinitasafeContentsContext.
*/ static SECStatus
sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
*safeContentsCtx)
{ /* check for an error */ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
safeContentsCtx->p12dcx->error) { return SECFailure;
}
safeContentsCtx->nestedSafeContentsCtx =
sec_pkcs12_decoder_safe_contents_init_decode(safeContentsCtx->p12dcx,
PR_TRUE); if (!safeContentsCtx->nestedSafeContentsCtx) { return SECFailure;
}
/* set up new filter proc */
SEC_ASN1DecoderSetNotifyProc(
safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx,
sec_pkcs12_decoder_safe_contents_notify,
safeContentsCtx->nestedSafeContentsCtx);
/* when the safeContents is done decoding, we need to reset the *properfilterandnotifyprocsandclosethedecodingcontext
*/ static SECStatus
sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
*safeContentsCtx)
{ /* check for error */ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
safeContentsCtx->p12dcx->error) { return SECFailure;
}
/* wrapper for updating safeContents. This is used when decoding *thenestedsafeContentsandanyauthenticatedSafes.
*/ staticvoid
sec_pkcs12_decoder_safe_contents_callback(void *arg, constchar *buf, unsignedlong len)
{
SECStatus rv;
sec_PKCS12SafeContentsContext *safeContentsCtx =
(sec_PKCS12SafeContentsContext *)arg;
SEC_PKCS12DecoderContext *p12dcx;
/* check for error */ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
safeContentsCtx->p12dcx->error || !safeContentsCtx->safeContentsA1Dcx) { return;
}
p12dcx = safeContentsCtx->p12dcx;
/* update the decoder */
rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len); if (rv != SECSuccess) { /* if we fail while trying to decode a 'safe', it's probably because
* we didn't have the correct password. */
PORT_SetError(SEC_ERROR_BAD_PASSWORD);
p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
SEC_PKCS7DecoderAbort(p12dcx->currentASafeP7Dcx, SEC_ERROR_BAD_PASSWORD); goto loser;
}
return;
loser: /* set the error and finish the context */
p12dcx->error = PR_TRUE; if (safeContentsCtx->safeContentsA1Dcx) {
SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
safeContentsCtx->safeContentsA1Dcx = NULL;
}
return;
}
/* this is a wrapper for the ASN1 decoder to call SEC_PKCS7DecoderUpdate
*/ staticvoid
sec_pkcs12_decoder_wrap_p7_update(void *arg, constchar *data, unsignedlong len, int depth,
SEC_ASN1EncodingPart data_kind)
{
SEC_PKCS7DecoderContext *p7dcx = (SEC_PKCS7DecoderContext *)arg;
SEC_PKCS7DecoderUpdate(p7dcx, data, len);
}
/* notify function for decoding aSafes. at the beginning, *ofanauthenticatedSafe,westartadecodeofasafeContents. *attheend,wecleanupthesafeContentsdecodercontextand *resetstatevariables
*/ staticvoid
sec_pkcs12_decoder_asafes_notify(void *arg, PRBool before, void *dest, int real_depth)
{
SEC_PKCS12DecoderContext *p12dcx;
sec_PKCS12SafeContentsContext *safeContentsCtx;
/* make sure no error occurred. */
p12dcx = (SEC_PKCS12DecoderContext *)arg; if (!p12dcx || p12dcx->error) { return;
}
if (before) {
/* init a new safeContentsContext */
safeContentsCtx = sec_pkcs12_decoder_safe_contents_init_decode(p12dcx,
PR_FALSE); if (!safeContentsCtx) { goto loser;
}
/* if we are writing to a file, write out the new information */ if (p12dcx->dWrite) { unsignedlong writeLen = (*p12dcx->dWrite)(p12dcx->dArg,
(unsignedchar *)buf, len); if (writeLen != len) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
}
return;
loser: /* set the error flag */
p12dcx->error = PR_TRUE;
SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
p12dcx->aSafeA1Dcx = NULL;
return;
}
/* start the decode of an authenticatedSafe contentInfo.
*/ static SECStatus
sec_pkcs12_decode_start_asafes_cinfo(SEC_PKCS12DecoderContext *p12dcx)
{ if (!p12dcx || p12dcx->error) { return SECFailure;
}
/* start the decode context */
p12dcx->aSafeA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
&p12dcx->authSafe,
sec_PKCS12AuthenticatedSafeTemplate); if (!p12dcx->aSafeA1Dcx) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
/* set the notify function */
SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeA1Dcx,
sec_pkcs12_decoder_asafes_notify, p12dcx);
/* begin the authSafe decoder context */
p12dcx->aSafeP7Dcx = SEC_PKCS7DecoderStart(
sec_pkcs12_decoder_asafes_callback, p12dcx,
p12dcx->pwfn, p12dcx->pwfnarg, NULL, NULL, NULL); if (!p12dcx->aSafeP7Dcx) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
/* open the temp file for writing, if the digest functions were set */ if (p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE) != SECSuccess) {
p12dcx->errorValue = PORT_GetError(); goto loser;
} /* dOpen(dArg, PR_FALSE) creates the temp file */
p12dcx->dIsOpen = PR_TRUE;
return SECSuccess;
loser:
p12dcx->error = PR_TRUE;
if (p12dcx->aSafeA1Dcx) {
SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
p12dcx->aSafeA1Dcx = NULL;
}
if (p12dcx->aSafeP7Dcx) {
SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
p12dcx->aSafeP7Dcx = NULL;
}
return SECFailure;
}
/* wrapper for updating the safeContents. this function is used as *afilterforthepfxwhendecodingtheauthenticatedsafes
*/ staticvoid
sec_pkcs12_decode_asafes_cinfo_update(void *arg, constchar *buf, unsignedlong len, int depth,
SEC_ASN1EncodingPart data_kind)
{
SEC_PKCS12DecoderContext *p12dcx;
SECStatus rv;
/* did we find an error? if so, close the context and set the *errorflag.
*/
SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
p12dcx->aSafeP7Dcx = NULL;
p12dcx->error = PR_TRUE;
}
/* notify procedure used while decoding the pfx. When we encounter *theauthSafes,wewanttotriggerthedecodingofauthSafesaswell *aswhenweencounterthemacData,triggerthedecodingofit.wedo *thisbecausewewearestreamingthedecoderandnotdecodinginplace. *thepfxwhichisthedestination,onlyhastheversiondecodedintoit.
*/ staticvoid
sec_pkcs12_decoder_pfx_notify_proc(void *arg, PRBool before, void *dest, int real_depth)
{
SECStatus rv;
SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
/* if an error occurs, clear the notifyProc and the filterProc *andcontinue.
*/ if (p12dcx->error) {
SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxA1Dcx);
SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx); return;
}
if (before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
/* we want to make sure this is a version we support */ if (!sec_pkcs12_proper_version(&p12dcx->pfx)) {
p12dcx->errorValue = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION; goto loser;
}
/* start the decode of the aSafes cinfo... */
rv = sec_pkcs12_decode_start_asafes_cinfo(p12dcx); if (rv != SECSuccess) { goto loser;
}
/* set the filter proc to update the authenticated safes. */
SEC_ASN1DecoderSetFilterProc(p12dcx->pfxA1Dcx,
sec_pkcs12_decode_asafes_cinfo_update,
p12dcx, PR_TRUE);
}
if (!before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
/* we are done decoding the authenticatedSafes, so we need to *finishthedecoderContextandclearthefilterproc *andclosethehmaccallback,ifpresent
*/
p12dcx->aSafeCinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
p12dcx->aSafeP7Dcx = NULL; if (!p12dcx->aSafeCinfo) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx); if (p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE) != SECSuccess)) {
p12dcx->errorValue = PORT_GetError(); goto loser;
}
}
return;
loser:
p12dcx->error = PR_TRUE;
}
/* default implementations of the open/close/read/write functions for SEC_PKCS12DecoderStart
*/
if (!buf || len == 0 || !p12cxt->buffer) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return -1;
}
/* Clamp `len` to the bytes left in the buffer. toread is positive here,
* so the comparison stays unsigned and `len` cannot wrap. */
toread = p12cxt->filesize - p12cxt->currentpos; if (toread <= 0) { return0;
} if (len < (unsignedlong)toread) {
toread = (int)len;
}
/* The buffer position counters are signed PRInt32. Reject any write *whoselengthwouldnotfitsothat`len`cannotoverfloworwrapthem
* on LLP64 platforms where unsigned long is 32-bit (Win64). */ if (len > (unsignedlong)(PR_INT32_MAX - p12cxt->currentpos)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return -1;
}
arena = PORT_NewArena(2048); /* different size? */ if (!arena) { return NULL; /* error is already set */
}
/* allocate the decoder context and set the state variables */
p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext); if (!p12dcx) { goto loser; /* error is already set */
}
/* start the decoding of the PFX and set the notify proc *forthePFXitem.
*/
p12dcx->pfxA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx,
sec_PKCS12PFXItemTemplate); if (!p12dcx->pfxA1Dcx) {
PK11_FreeSlot(p12dcx->slot); goto loser;
}
/* This should be a nice sized buffer for reading in data (potentially large **amounts)tobeMACed.ItshouldbeMUCHlargerthanHASH_LENGTH_MAX.
*/ #define IN_BUF_LEN 1024 #ifdef DEBUG staticconstchar bufferEnd[] = { "BufferEnd" }; #endif #define FUDGE 128/* must be as large as bufferEnd or more. */
/* try to open the data for readback */ if (p12dcx->dOpen && ((*p12dcx->dOpen)(p12dcx->dArg, PR_TRUE) != SECSuccess)) { goto loser;
}
/* read the data back IN_BUF_LEN bytes at a time and recompute *thehmac.iffewerbytesarereadthanarerequested,itis *assumedthattheendoffilehasbeenreached.ifbytesRead *isreturnedas-1,thenanerroroccurredreadingfromthe *file.
*/ do {
bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN); if (bytesRead < 0) {
PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_READ); goto loser;
}
PORT_Assert(bytesRead <= IN_BUF_LEN);
PORT_Assert(!memcmp(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd));
if (bytesRead) {
lrv = PK11_DigestOp(pk11cx, buf, bytesRead); if (lrv == SECFailure) { goto loser;
}
}
} while (bytesRead == IN_BUF_LEN);
/* finish the hmac context */
lrv = PK11_DigestFinal(pk11cx, buf, &bufLen, IN_BUF_LEN); if (lrv == SECFailure) { goto loser;
}
hmacRes.data = buf;
hmacRes.len = bufLen;
/* is the hmac computed the same as the hmac which was decoded? */
rv = SECSuccess; if (SECITEM_CompareItem(&hmacRes, &p12dcx->macData.safeMac.digest) != SECEqual) {
PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
rv = SECFailure;
}
loser: /* close the file and remove it */ if (p12dcx->dClose) {
(*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
p12dcx->dIsOpen = PR_FALSE;
}
if (pk11cx) {
PK11_DestroyContext(pk11cx, PR_TRUE);
} if (symKey) {
PK11_FreeSymKey(symKey);
}
PORT_ZFree(buf, IN_BUF_LEN + FUDGE);
/* make sure that no errors have occurred... */ if (!p12dcx) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
} if (p12dcx->error) { /* error code is already set! PORT_SetError(p12dcx->errorValue); */ return SECFailure;
}
rv = SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
p12dcx->pfxA1Dcx = NULL; if (rv != SECSuccess) { return rv;
} #ifdef UNSAFE_FUZZER_MODE return sec_pkcs12_decoder_verify_fuzzer(p12dcx); #else/* UNSAFE_FUZZER_MODE */ /* check the signature or the mac depending on the type of *integrityused.
*/ if (p12dcx->pfx.encodedMacData.len) {
rv = SEC_ASN1DecodeItem(p12dcx->arena, &p12dcx->macData,
sec_PKCS12MacDataTemplate,
&p12dcx->pfx.encodedMacData); if (rv == SECSuccess) { return sec_pkcs12_decoder_verify_mac(p12dcx);
} return rv;
} if (SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner,
PR_FALSE)) { return SECSuccess;
}
PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); return SECFailure; #endif/* UNSAFE_FUZZER_MODE */
}
static SECItem *
sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag,
SECOidTag attributeType)
{ int i;
if (!bag->attribs) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL;
}
for (i = 0; bag->attribs[i] != NULL; i++) { if (SECOID_FindOIDTag(&bag->attribs[i]->attrType) == attributeType) { return bag->attribs[i]->attrValue[0];
}
} return NULL;
}
/* For now, this function will merely remove any ":" *inthenicknamewhichthePK11functionsmayhave *placedthere.Thiswillkeepdualcertsfromappearing *twiceunder"Your"certificateswhenimportedontosmart *cards.Oncewiththename"Slot:Cert"andanotherwith *thenickname"Slot:Slot:Cert"
*/ staticvoid
sec_pkcs12_sanitize_nickname(PK11SlotInfo *slot, SECItem *nick)
{ char *nickname; char *delimit; int delimitlen;
nickname = (char *)nick->data; if ((delimit = PORT_Strchr(nickname, ':')) != NULL) { char *slotName; int slotNameLen;
slotNameLen = delimit - nickname;
slotName = PORT_NewArray(char, (slotNameLen + 1));
PORT_Assert(slotName); if (slotName == NULL) { /* What else can we do?*/ return;
}
PORT_Memcpy(slotName, nickname, slotNameLen);
slotName[slotNameLen] = '\0'; if (PORT_Strcmp(PK11_GetTokenName(slot), slotName) == 0) {
delimitlen = PORT_Strlen(delimit + 1); if (delimitlen == 0) { /* Nickname was exactly "TokenName:" with nothing after the *prefix.StrippingitwouldyieldanemptySECItem,which
* is not a useful nickname; leave the original in place. */
PORT_Free(slotName); return;
}
PORT_Memmove(nickname, delimit + 1, delimitlen + 1);
nick->len = delimitlen;
}
PORT_Free(slotName);
}
}
static SECStatus
sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key)
{ int i = 0;
SECKEYPrivateKeyInfo *pki = NULL;
if (!key) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
/* if the bag does *not* contain an unencrypted PrivateKeyInfo *thenwecannotconverttheattributes.Wearepropagating *attributeswithinthePrivateKeyInfototheSafeBaglevel.
*/ if (SECOID_FindOIDTag(&(key->safeBagType)) !=
SEC_OID_PKCS12_V1_KEY_BAG_ID) { return SECSuccess;
}
pki = key->safeBagContent.pkcs8KeyBag;
if (!pki || !pki->attributes) { return SECSuccess;
}
while (pki->attributes[i]) {
SECOidTag tag = SECOID_FindOIDTag(&pki->attributes[i]->attrType);
if (tag == SEC_OID_PKCS9_LOCAL_KEY_ID ||
tag == SEC_OID_PKCS9_FRIENDLY_NAME) {
SECItem *attrValue = sec_pkcs12_get_attribute_value(key, tag); if (!attrValue) { if (sec_pkcs12_decoder_set_attribute_value(key, tag,
pki->attributes[i]->attrValue[0]) != SECSuccess) {
key->problem = PR_TRUE;
key->error = PORT_GetError(); return SECFailure;
}
}
}
i++;
}
return SECSuccess;
}
/* retrieve the nickname for the certificate bag. first look *inthecertbag,otherwisegetitfromthekey.
*/ static SECItem *
sec_pkcs12_get_nickname_for_cert(sec_PKCS12SafeBag *cert,
sec_PKCS12SafeBag *key)
{
SECItem *nickname;
if (!cert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL;
}
nickname = sec_pkcs12_get_nickname(cert); if (nickname) { return nickname;
}
if (key) {
nickname = sec_pkcs12_get_nickname(key);
/* set the nickname for the certificate */ static SECStatus
sec_pkcs12_set_nickname_for_cert(sec_PKCS12SafeBag *cert,
sec_PKCS12SafeBag *key,
SECItem *nickname)
{ if (!nickname || !cert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) { return SECFailure;
}
if (key) { if (sec_pkcs12_set_nickname(key, nickname) != SECSuccess) {
cert->problem = PR_TRUE;
cert->error = key->error; return SECFailure;
}
}
return SECSuccess;
}
/* retrieve the DER cert from the cert bag */ static SECItem *
sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert)
{ if (!cert || !cert->safeBagContent.certBag) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL;
}
if (SECOID_FindOIDTag(&cert->safeBagType) != SEC_OID_PKCS12_V1_CERT_BAG_ID) { return NULL;
}
/* only support X509 certs not SDSI */ if (SECOID_FindOIDTag(&cert->safeBagContent.certBag->bagID) != SEC_OID_PKCS9_X509_CERT) { return NULL;
}
/* do we already have the nickname in the list? */ if (nickArg->nNicks > 0) {
/* nicknames have been encountered, but there is no list -- bad */ if (!nickArg->nickList) {
nickArg->error = SEC_ERROR_INVALID_ARGS;
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
for (i = 0; i < nickArg->nNicks; i++) { if (SECITEM_CompareItem(nickArg->nickList[i], &tempNick) == SECEqual) { return SECSuccess;
}
}
}
/* add the nickname to the list */
nickArg->nickList = (nickArg->nNicks == 0)
? PORT_ArenaZNewArray(nickArg->arena, SECItem *, 2)
: PORT_ArenaGrowArray(nickArg->arena, nickArg->nickList, SECItem *,
nickArg->nNicks + 1, nickArg->nNicks + 2);
if (!nickArg->nickList) {
nickArg->error = SEC_ERROR_NO_MEMORY; return SECFailure;
}
/* traverses the certs in the data base or in the token for the *DNtoseeifanycertscurrentlyhaveanicknameset. *Ifso,returnit.
*/ static SECItem *
sec_pkcs12_get_existing_nick_for_dn(sec_PKCS12SafeBag *cert)
{ struct certNickInfo *nickArg = NULL;
SECItem *derCert, *returnDn = NULL;
PLArenaPool *arena = NULL;
CERTCertificate *tempCert;
if (!cert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL;
}
derCert = sec_pkcs12_get_der_cert(cert); if (!derCert) { return NULL;
}
/* if the token is local, first traverse the cert database *thentraversethetoken.
*/ if (PK11_TraverseCertsForSubjectInSlot(tempCert, cert->slot, gatherNicknames,
(void *)nickArg) != SECSuccess) {
returnDn = NULL; goto loser;
}
if (nickArg->error) { /* XXX do we want to set the error? */
returnDn = NULL; goto loser;
}
/* set it to the first name, for now. handle multiple names? */
returnDn = SECITEM_DupItem(nickArg->nickList[0]);
loser: if (arena) {
PORT_FreeArena(arena, PR_TRUE);
}
if (tempCert) {
CERT_DestroyCertificate(tempCert);
}
if (derCert) {
SECITEM_FreeItem(derCert, PR_TRUE);
}
return (returnDn);
}
/* counts certificates found for a given traversal function */ static SECStatus
countCertificate(CERTCertificate *cert, void *arg)
{ unsignedint *nCerts = (unsignedint *)arg;
if (!cert || !arg) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (!nickname || !slot) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return PR_TRUE;
}
/* we want to check the local database first if we are importing to it */
PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate,
(void *)&nCerts); return (PRBool)(nCerts != 0);
}
/* validate cert nickname such that there is a one-to-one relation *betweennicknamesanddn's.wewanttoenforcethecasethatthe *nicknameisnon-NULLandthatthereisonlyonenicknameperDN. * *ifthereisaproblemwithanicknameorthenicknameisnotpresent, *theuserwillbepromptedforit.
*/ staticvoid
sec_pkcs12_validate_cert_nickname(sec_PKCS12SafeBag *cert,
sec_PKCS12SafeBag *key,
SEC_PKCS12NicknameCollisionCallback nicknameCb,
CERTCertificate *leafCert)
{
SECItem *certNickname, *existingDNNick;
PRBool setNickname = PR_FALSE, cancel = PR_FALSE;
SECItem *newNickname = NULL;
if (!cert || !cert->hasKey) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return;
}
/* nickname is already used w/ this dn, so it is safe to return */ if (certNickname && existingDNNick &&
SECITEM_CompareItem(certNickname, existingDNNick) == SECEqual) { goto loser;
}
/* nickname not set in pkcs 12 bags, but a nick is already used for *thisdn.setthenicksinthep12bagsandfinish.
*/ if (existingDNNick) {
sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick); goto loser;
}
/* at this point, we have a certificate for which the DN is not located *onthetoken.thenicknamespecifiedmayormaynotbeNULL.ifit *isnotnull,weneedtomakesurethattherearenoothercertificates *withthisnicknameinthetokenforittobevalid.thisimposesa *onetoonerelationshipbetweenDNandnickname. * *ifthenicknameisnull,weneedtheusertoenteranicknamefor *thecertificate. * *oncewehaveanickname,wemakesurethatthenicknameisunique *fortheDN.ifitisnot,theuserisrepromptedtoenteranew *nickname. * *inordertoexitthisloop,thenicknameenterediseitherunique *ortheuserhitscancelandthecertificateisnotimported.
*/
setNickname = PR_FALSE; while (1) { /* we will use the nickname so long as no other certs have the *samenickname.andthenicknameisnotNULL.
*/ if (certNickname && certNickname->data &&
!sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) { if (setNickname) {
sec_pkcs12_set_nickname_for_cert(cert, key, certNickname);
} break;
}
/* at this point we have a new nickname, if we have an existing *certNickname,weneedtofreeitandassignthenewnickname *toittoavoidamemoryleak.happy?
*/ if (certNickname) {
SECITEM_ZfreeItem(certNickname, PR_TRUE);
certNickname = NULL;
}
certNickname = newNickname;
setNickname = PR_TRUE; /* go back and recheck the new nickname */
}
loser: if (certNickname) {
SECITEM_ZfreeItem(certNickname, PR_TRUE);
}
if (existingDNNick) {
SECITEM_ZfreeItem(existingDNNick, PR_TRUE);
}
}
/* We should always have values for "key" and "pubKey"
so they can be dereferenced later. */ if (!key || !pubKey) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (key->problem || key->noInstall) { return SECSuccess;
}
/* get the value and type from the public key */
publicValue = sec_pkcs12_get_public_value_and_type(pubKey, &keyType); if (!publicValue) {
key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
key->problem = PR_TRUE; return SECFailure;
}
if (rv != SECSuccess) {
key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
key->problem = PR_TRUE;
} else { /* try to import the public key. Failure to do so is not fatal,
* not all tokens can store the public key */ if (pubKey) {
PK11_ImportPublicKey(key->slot, pubKey, PR_TRUE);
}
key->installed = PR_TRUE;
}
if (!safeBags || !safeBags[0]) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL;
}
keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID); if (!keyId) { return NULL;
}
for (i = 0; safeBags[i]; i++) { if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
SECItem *certKeyId = sec_pkcs12_get_attribute_value(safeBags[i],
SEC_OID_PKCS9_LOCAL_KEY_ID);
if (certKeyId && (SECITEM_CompareItem(certKeyId, keyId) == SECEqual)) { if (sec_pkcs12_add_item_to_bag_list(&certList, safeBags[i]) != SECSuccess) { /* This would leak the partial list of safeBags, *butthatlistisallocatedfromthearenaof *oneofthesafebags,andwillbedestroyedwhen *thatarenaisdestroyed.Sothisisnotarealleak.
*/ return NULL;
}
}
}
}
if (tempCert) {
CERT_AddCertToListTail(certList, tempCert);
}
SECITEM_FreeItem(derCert, PR_TRUE);
} /* fixed an infinite loop here, by ensuring that i gets incremented *ifderCertisNULLabove.
*/
}
if (!safeBags || !safeBags[0]) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL;
}
for (i = 0; safeBags[i]; i++) {
bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType)); switch (bagType) { case SEC_OID_PKCS12_V1_KEY_BAG_ID: case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: if (sec_pkcs12_add_item_to_bag_list(&keyList, safeBags[i]) != SECSuccess) { /* This would leak, except that keyList is allocated *fromthearenasharedbyallthesafeBags.
*/ return NULL;
} break; default: break;
}
}
return keyList;
}
/* This function takes two passes over the bags, validating them *Thetwopassesareintendedtomirrorexactlythetwopassesin *sec_pkcs12_install_bags.Buttheydon't.:(
*/ static SECStatus
sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags,
SEC_PKCS12NicknameCollisionCallback nicknameCb, void *wincx)
{
sec_PKCS12SafeBag **keyList; int i;
if (!safeBags || !nicknameCb) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (!safeBags[0]) { return SECSuccess;
}
/* First pass. Find all the key bags. *Findthematchingcert(s)foreachkey.
*/
keyList = sec_pkcs12_get_key_bags(safeBags); if (keyList) { for (i = 0; keyList[i]; ++i) {
sec_PKCS12SafeBag *key = keyList[i];
sec_PKCS12SafeBag **certList =
sec_pkcs12_find_certs_for_key(safeBags, key);
if (certList) { int j;
if (SECOID_FindOIDTag(&(key->safeBagType)) ==
SEC_OID_PKCS12_V1_KEY_BAG_ID) { /* if it is an unencrypted private key then make sure *theattributesarepropagetedtotheappropriate *level
*/ if (sec_pkcs12_get_key_info(key) != SECSuccess) { return SECFailure;
}
}
/* Now take a second pass over the safebags and mark for installation any *certsthatwereneitherinstallednordisqualifiedbythefirstpass.
*/ for (i = 0; safeBags[i]; ++i) {
sec_PKCS12SafeBag *bag = safeBags[i];
if (!bag->validated) {
SECOidTag bagType = SECOID_FindOIDTag(&bag->safeBagType);
switch (bagType) { case SEC_OID_PKCS12_V1_CERT_BAG_ID:
sec_pkcs12_validate_cert(bag, NULL, nicknameCb); break; case SEC_OID_PKCS12_V1_KEY_BAG_ID: case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
bag->noInstall = PR_TRUE;
bag->problem = PR_TRUE;
bag->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; break; default:
bag->noInstall = PR_TRUE;
}
}
}
return SECSuccess;
}
SECStatus
SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx,
SEC_PKCS12NicknameCollisionCallback nicknameCb)
{
SECStatus rv; int i, probCnt, errorVal = 0; if (!p12dcx || p12dcx->error || !p12dcx->safeBags) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
/* This function takes two passes over the bags, installing them in the *desiredslot.Thetwopassesareintendedtomirrorexactlythe *twopassesinsec_pkcs12_validate_bags.
*/ static SECStatus
sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, PRBool forceUnicode, void *wincx)
{
sec_PKCS12SafeBag **keyList; int i; int failedKeys = 0;
if (!safeBags) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (!safeBags[0]) { return SECSuccess;
}
/* First pass. Find all the key bags. *Trytoinstallthem,andanycertsassociatedwiththem.
*/
keyList = sec_pkcs12_get_key_bags(safeBags); if (keyList) { for (i = 0; keyList[i]; i++) {
SECStatus rv;
SECKEYPublicKey *pubKey = NULL;
SECItem *nickName = NULL;
sec_PKCS12SafeBag *key = keyList[i];
sec_PKCS12SafeBag **certList; unsignedint keyUsage;
if (key->problem) {
++failedKeys; continue;
}
certList = sec_pkcs12_find_certs_for_key(safeBags, key); if (certList && certList[0]) {
pubKey = sec_pkcs12_get_public_key_and_usage(certList[0],
&keyUsage); /* use the cert's nickname, if it has one, else use the *key'snickname,elsefail.
*/
nickName = sec_pkcs12_get_nickname_for_cert(certList[0], key);
} else {
nickName = sec_pkcs12_get_nickname(key);
} if (!nickName) {
key->error = SEC_ERROR_BAD_NICKNAME;
key->problem = PR_TRUE;
rv = SECFailure;
} elseif (!pubKey) {
key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
key->problem = PR_TRUE;
rv = SECFailure;
} else {
rv = sec_pkcs12_add_key(key, pubKey, keyUsage, nickName,
forceUnicode, wincx);
} if (pubKey) {
SECKEY_DestroyPublicKey(pubKey);
pubKey = NULL;
} if (nickName) {
SECITEM_FreeItem(nickName, PR_TRUE);
nickName = NULL;
} if (rv != SECSuccess) {
PORT_SetError(key->error);
++failedKeys;
}
/* Now take a second pass over the safebags and install any certs *thatwereneitherinstallednordisqualifiedbythefirstpass.
*/ for (i = 0; safeBags[i]; i++) {
sec_PKCS12SafeBag *bag = safeBags[i];
if (!p12dcx || p12dcx->error) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (!p12dcx->bagsVerified) { return SECFailure;
}
/* We need to check the option here as well as in *SEC_PKCS12DecoderStart,becausedifferentPBE'scouldbeused
* for PKCS #7 and PKCS #8 */
rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode); if (rv != SECSuccess) { return SECFailure;
}
for (; p12dcx->iteration < p12dcx->safeBagCount; p12dcx->iteration++) {
bag = p12dcx->safeBags[p12dcx->iteration]; if (bag == NULL || bag->problem) { continue;
}
p12dcx->decitem.type = SECOID_FindOIDTag(&(bag->safeBagType)); switch (p12dcx->decitem.type) { case SEC_OID_PKCS12_V1_CERT_BAG_ID:
p12dcx->decitem.der = sec_pkcs12_get_der_cert(bag);
p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
p12dcx->decitem.hasKey = sec_pkcs12_bagHasKey(p12dcx, bag); /* if we don't understand the cert, or it's not parsable, skip it */ /* as per the comment above, friendlyName may be null legitimately */ if (!p12dcx->decitem.der) {
p12dcx->decitem.type = 0; /* clear out the type we are ignoring */ continue;
} break; case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
p12dcx->decitem.shroudAlg = PORT_ZNew(SECAlgorithmID); if (p12dcx->decitem.shroudAlg) {
SECOID_CopyAlgorithmID(NULL, p12dcx->decitem.shroudAlg,
&bag->safeBagContent.pkcs8ShroudedKeyBag->algorithm);
} /* fall through */ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag); break; default: /* return these even though we don't expect them */ break; case SEC_OID_UNKNOWN: /* ignore these */
p12dcx->decitem.type = 0; /* clear out the type we are ignoring */ continue;
}
*ipp = &p12dcx->decitem;
p12dcx->iteration++; break; /* end for() */
}
PORT_SetError(0); /* end-of-list is SECFailure with no PORT error */ return ((p12dcx->decitem.type == 0) ? SECFailure : SECSuccess);
}
if (!p12dcx || p12dcx->error) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
if (safe && safe->contents) { int i = 0; while (safe->contents[i] != NULL) { if (SECOID_FindOIDTag(&safe->contents[i]->safeBagType) == SEC_OID_PKCS12_KEY_BAG_ID) { int j = 0;
SEC_PKCS12PrivateKeyBag *privBag =
safe->contents[i]->safeContent.keyBag;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.126Bemerkung:
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.