security/nss/lib/pkcs12/p12d.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5
michael@0 6 #include "nssrenam.h"
michael@0 7 #include "p12t.h"
michael@0 8 #include "p12.h"
michael@0 9 #include "plarena.h"
michael@0 10 #include "secitem.h"
michael@0 11 #include "secoid.h"
michael@0 12 #include "seccomon.h"
michael@0 13 #include "secport.h"
michael@0 14 #include "cert.h"
michael@0 15 #include "secpkcs7.h"
michael@0 16 #include "secasn1.h"
michael@0 17 #include "secerr.h"
michael@0 18 #include "pk11func.h"
michael@0 19 #include "p12plcy.h"
michael@0 20 #include "p12local.h"
michael@0 21 #include "secder.h"
michael@0 22 #include "secport.h"
michael@0 23
michael@0 24 #include "certdb.h"
michael@0 25
michael@0 26 #include "prcpucfg.h"
michael@0 27
michael@0 28 /* This belongs in secport.h */
michael@0 29 #define PORT_ArenaGrowArray(poolp, oldptr, type, oldnum, newnum) \
michael@0 30 (type *)PORT_ArenaGrow((poolp), (oldptr), \
michael@0 31 (oldnum) * sizeof(type), (newnum) * sizeof(type))
michael@0 32
michael@0 33
michael@0 34 typedef struct sec_PKCS12SafeContentsContextStr sec_PKCS12SafeContentsContext;
michael@0 35
michael@0 36 /* Opaque structure for decoding SafeContents. These are used
michael@0 37 * for each authenticated safe as well as any nested safe contents.
michael@0 38 */
michael@0 39 struct sec_PKCS12SafeContentsContextStr {
michael@0 40 /* the parent decoder context */
michael@0 41 SEC_PKCS12DecoderContext *p12dcx;
michael@0 42
michael@0 43 /* memory arena to allocate space from */
michael@0 44 PLArenaPool *arena;
michael@0 45
michael@0 46 /* decoder context and destination for decoding safe contents */
michael@0 47 SEC_ASN1DecoderContext *safeContentsA1Dcx;
michael@0 48 sec_PKCS12SafeContents safeContents;
michael@0 49
michael@0 50 /* information for decoding safe bags within the safe contents.
michael@0 51 * these variables are updated for each safe bag decoded.
michael@0 52 */
michael@0 53 SEC_ASN1DecoderContext *currentSafeBagA1Dcx;
michael@0 54 sec_PKCS12SafeBag *currentSafeBag;
michael@0 55 PRBool skipCurrentSafeBag;
michael@0 56
michael@0 57 /* if the safe contents is nested, the parent is pointed to here. */
michael@0 58 sec_PKCS12SafeContentsContext *nestedSafeContentsCtx;
michael@0 59 };
michael@0 60
michael@0 61 /* opaque decoder context structure. information for decoding a pkcs 12
michael@0 62 * PDU are stored here as well as decoding pointers for intermediary
michael@0 63 * structures which are part of the PKCS 12 PDU. Upon a successful
michael@0 64 * decode, the safe bags containing certificates and keys encountered.
michael@0 65 */
michael@0 66 struct SEC_PKCS12DecoderContextStr {
michael@0 67 PLArenaPool *arena;
michael@0 68 PK11SlotInfo *slot;
michael@0 69 void *wincx;
michael@0 70 PRBool error;
michael@0 71 int errorValue;
michael@0 72
michael@0 73 /* password */
michael@0 74 SECItem *pwitem;
michael@0 75
michael@0 76 /* used for decoding the PFX structure */
michael@0 77 SEC_ASN1DecoderContext *pfxA1Dcx;
michael@0 78 sec_PKCS12PFXItem pfx;
michael@0 79
michael@0 80 /* safe bags found during decoding */
michael@0 81 sec_PKCS12SafeBag **safeBags;
michael@0 82 unsigned int safeBagCount;
michael@0 83
michael@0 84 /* state variables for decoding authenticated safes. */
michael@0 85 SEC_PKCS7DecoderContext *currentASafeP7Dcx;
michael@0 86 SEC_ASN1DecoderContext *aSafeA1Dcx;
michael@0 87 SEC_PKCS7DecoderContext *aSafeP7Dcx;
michael@0 88 SEC_PKCS7ContentInfo *aSafeCinfo;
michael@0 89 sec_PKCS12AuthenticatedSafe authSafe;
michael@0 90 sec_PKCS12SafeContents safeContents;
michael@0 91
michael@0 92 /* safe contents info */
michael@0 93 unsigned int safeContentsCnt;
michael@0 94 sec_PKCS12SafeContentsContext **safeContentsList;
michael@0 95
michael@0 96 /* HMAC info */
michael@0 97 sec_PKCS12MacData macData;
michael@0 98
michael@0 99 /* routines for reading back the data to be hmac'd */
michael@0 100 /* They are called as follows.
michael@0 101 *
michael@0 102 * Stage 1: decode the aSafes cinfo into a buffer in dArg,
michael@0 103 * which p12d.c sometimes refers to as the "temp file".
michael@0 104 * This occurs during SEC_PKCS12DecoderUpdate calls.
michael@0 105 *
michael@0 106 * dOpen(dArg, PR_FALSE)
michael@0 107 * dWrite(dArg, buf, len)
michael@0 108 * ...
michael@0 109 * dWrite(dArg, buf, len)
michael@0 110 * dClose(dArg, PR_FALSE)
michael@0 111 *
michael@0 112 * Stage 2: verify MAC
michael@0 113 * This occurs SEC_PKCS12DecoderVerify.
michael@0 114 *
michael@0 115 * dOpen(dArg, PR_TRUE)
michael@0 116 * dRead(dArg, buf, IN_BUF_LEN)
michael@0 117 * ...
michael@0 118 * dRead(dArg, buf, IN_BUF_LEN)
michael@0 119 * dClose(dArg, PR_TRUE)
michael@0 120 */
michael@0 121 digestOpenFn dOpen;
michael@0 122 digestCloseFn dClose;
michael@0 123 digestIOFn dRead, dWrite;
michael@0 124 void *dArg;
michael@0 125 PRBool dIsOpen; /* is the temp file created? */
michael@0 126
michael@0 127 /* helper functions */
michael@0 128 SECKEYGetPasswordKey pwfn;
michael@0 129 void *pwfnarg;
michael@0 130 PRBool swapUnicodeBytes;
michael@0 131
michael@0 132 /* import information */
michael@0 133 PRBool bagsVerified;
michael@0 134
michael@0 135 /* buffer management for the default callbacks implementation */
michael@0 136 void *buffer; /* storage area */
michael@0 137 PRInt32 filesize; /* actual data size */
michael@0 138 PRInt32 allocated; /* total buffer size allocated */
michael@0 139 PRInt32 currentpos; /* position counter */
michael@0 140 SECPKCS12TargetTokenCAs tokenCAs;
michael@0 141 sec_PKCS12SafeBag **keyList;/* used by ...IterateNext() */
michael@0 142 unsigned int iteration;
michael@0 143 SEC_PKCS12DecoderItem decitem;
michael@0 144 };
michael@0 145
michael@0 146 /* forward declarations of functions that are used when decoding
michael@0 147 * safeContents bags which are nested and when decoding the
michael@0 148 * authenticatedSafes.
michael@0 149 */
michael@0 150 static SECStatus
michael@0 151 sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
michael@0 152 *safeContentsCtx);
michael@0 153 static SECStatus
michael@0 154 sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
michael@0 155 *safeContentsCtx);
michael@0 156
michael@0 157
michael@0 158 /* make sure that the PFX version being decoded is a version
michael@0 159 * which we support.
michael@0 160 */
michael@0 161 static PRBool
michael@0 162 sec_pkcs12_proper_version(sec_PKCS12PFXItem *pfx)
michael@0 163 {
michael@0 164 /* if no version, assume it is not supported */
michael@0 165 if(pfx->version.len == 0) {
michael@0 166 return PR_FALSE;
michael@0 167 }
michael@0 168
michael@0 169 if(DER_GetInteger(&pfx->version) > SEC_PKCS12_VERSION) {
michael@0 170 return PR_FALSE;
michael@0 171 }
michael@0 172
michael@0 173 return PR_TRUE;
michael@0 174 }
michael@0 175
michael@0 176 /* retrieve the key for decrypting the safe contents */
michael@0 177 static PK11SymKey *
michael@0 178 sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid)
michael@0 179 {
michael@0 180 SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *) arg;
michael@0 181 PK11SlotInfo *slot;
michael@0 182 PK11SymKey *bulkKey;
michael@0 183
michael@0 184 if(!p12dcx) {
michael@0 185 return NULL;
michael@0 186 }
michael@0 187
michael@0 188 /* if no slot specified, use the internal key slot */
michael@0 189 if(p12dcx->slot) {
michael@0 190 slot = PK11_ReferenceSlot(p12dcx->slot);
michael@0 191 } else {
michael@0 192 slot = PK11_GetInternalKeySlot();
michael@0 193 }
michael@0 194
michael@0 195 bulkKey = PK11_PBEKeyGen(slot, algid, p12dcx->pwitem,
michael@0 196 PR_FALSE, p12dcx->wincx);
michael@0 197 /* some tokens can't generate PBE keys on their own, generate the
michael@0 198 * key in the internal slot, and let the Import code deal with it,
michael@0 199 * (if the slot can't generate PBEs, then we need to use the internal
michael@0 200 * slot anyway to unwrap). */
michael@0 201 if (!bulkKey && !PK11_IsInternal(slot)) {
michael@0 202 PK11_FreeSlot(slot);
michael@0 203 slot = PK11_GetInternalKeySlot();
michael@0 204 bulkKey = PK11_PBEKeyGen(slot, algid, p12dcx->pwitem,
michael@0 205 PR_FALSE, p12dcx->wincx);
michael@0 206 }
michael@0 207 PK11_FreeSlot(slot);
michael@0 208
michael@0 209 /* set the password data on the key */
michael@0 210 if (bulkKey) {
michael@0 211 PK11_SetSymKeyUserData(bulkKey,p12dcx->pwitem, NULL);
michael@0 212 }
michael@0 213
michael@0 214
michael@0 215 return bulkKey;
michael@0 216 }
michael@0 217
michael@0 218 /* XXX this needs to be modified to handle enveloped data. most
michael@0 219 * likely, it should mirror the routines for SMIME in that regard.
michael@0 220 */
michael@0 221 static PRBool
michael@0 222 sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid,
michael@0 223 PK11SymKey *bulkkey)
michael@0 224 {
michael@0 225 PRBool decryptionAllowed = SEC_PKCS12DecryptionAllowed(algid);
michael@0 226
michael@0 227 if(!decryptionAllowed) {
michael@0 228 return PR_FALSE;
michael@0 229 }
michael@0 230
michael@0 231 return PR_TRUE;
michael@0 232 }
michael@0 233
michael@0 234 /* when we encounter a new safe bag during the decoding, we need
michael@0 235 * to allocate space for the bag to be decoded to and set the
michael@0 236 * state variables appropriately. all of the safe bags are allocated
michael@0 237 * in a buffer in the outer SEC_PKCS12DecoderContext, however,
michael@0 238 * a pointer to the safeBag is also used in the sec_PKCS12SafeContentsContext
michael@0 239 * for the current bag.
michael@0 240 */
michael@0 241 static SECStatus
michael@0 242 sec_pkcs12_decoder_init_new_safe_bag(sec_PKCS12SafeContentsContext
michael@0 243 *safeContentsCtx)
michael@0 244 {
michael@0 245 void *mark = NULL;
michael@0 246 SEC_PKCS12DecoderContext *p12dcx;
michael@0 247
michael@0 248 /* make sure that the structures are defined, and there has
michael@0 249 * not been an error in the decoding
michael@0 250 */
michael@0 251 if(!safeContentsCtx || !safeContentsCtx->p12dcx
michael@0 252 || safeContentsCtx->p12dcx->error) {
michael@0 253 return SECFailure;
michael@0 254 }
michael@0 255
michael@0 256 p12dcx = safeContentsCtx->p12dcx;
michael@0 257 mark = PORT_ArenaMark(p12dcx->arena);
michael@0 258
michael@0 259 /* allocate a new safe bag, if bags already exist, grow the
michael@0 260 * list of bags, otherwise allocate a new list. the list is
michael@0 261 * NULL terminated.
michael@0 262 */
michael@0 263 p12dcx->safeBags = (!p12dcx->safeBagCount)
michael@0 264 ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
michael@0 265 : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
michael@0 266 sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
michael@0 267 p12dcx->safeBagCount + 2);
michael@0 268
michael@0 269 if(!p12dcx->safeBags) {
michael@0 270 p12dcx->errorValue = PORT_GetError();
michael@0 271 goto loser;
michael@0 272 }
michael@0 273
michael@0 274 /* append the bag to the end of the list and update the reference
michael@0 275 * in the safeContentsCtx.
michael@0 276 */
michael@0 277 p12dcx->safeBags[p12dcx->safeBagCount] =
michael@0 278 safeContentsCtx->currentSafeBag =
michael@0 279 PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
michael@0 280 if(!safeContentsCtx->currentSafeBag) {
michael@0 281 p12dcx->errorValue = PORT_GetError();
michael@0 282 goto loser;
michael@0 283 }
michael@0 284 p12dcx->safeBags[++p12dcx->safeBagCount] = NULL;
michael@0 285
michael@0 286 safeContentsCtx->currentSafeBag->slot = safeContentsCtx->p12dcx->slot;
michael@0 287 safeContentsCtx->currentSafeBag->pwitem = safeContentsCtx->p12dcx->pwitem;
michael@0 288 safeContentsCtx->currentSafeBag->swapUnicodeBytes =
michael@0 289 safeContentsCtx->p12dcx->swapUnicodeBytes;
michael@0 290 safeContentsCtx->currentSafeBag->arena = safeContentsCtx->p12dcx->arena;
michael@0 291 safeContentsCtx->currentSafeBag->tokenCAs =
michael@0 292 safeContentsCtx->p12dcx->tokenCAs;
michael@0 293
michael@0 294 PORT_ArenaUnmark(p12dcx->arena, mark);
michael@0 295 return SECSuccess;
michael@0 296
michael@0 297 loser:
michael@0 298
michael@0 299 /* if an error occurred, release the memory and set the error flag
michael@0 300 * the only possible errors triggered by this function are memory
michael@0 301 * related.
michael@0 302 */
michael@0 303 if(mark) {
michael@0 304 PORT_ArenaRelease(p12dcx->arena, mark);
michael@0 305 }
michael@0 306
michael@0 307 p12dcx->error = PR_TRUE;
michael@0 308 return SECFailure;
michael@0 309 }
michael@0 310
michael@0 311 /* A wrapper for updating the ASN1 context in which a safeBag is
michael@0 312 * being decoded. This function is called as a callback from
michael@0 313 * secasn1d when decoding SafeContents structures.
michael@0 314 */
michael@0 315 static void
michael@0 316 sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data,
michael@0 317 unsigned long len, int depth,
michael@0 318 SEC_ASN1EncodingPart data_kind)
michael@0 319 {
michael@0 320 sec_PKCS12SafeContentsContext *safeContentsCtx =
michael@0 321 (sec_PKCS12SafeContentsContext *)arg;
michael@0 322 SEC_PKCS12DecoderContext *p12dcx;
michael@0 323 SECStatus rv;
michael@0 324
michael@0 325 /* make sure that we are not skipping the current safeBag,
michael@0 326 * and that there are no errors. If so, just return rather
michael@0 327 * than continuing to process.
michael@0 328 */
michael@0 329 if(!safeContentsCtx || !safeContentsCtx->p12dcx
michael@0 330 || safeContentsCtx->p12dcx->error
michael@0 331 || safeContentsCtx->skipCurrentSafeBag) {
michael@0 332 return;
michael@0 333 }
michael@0 334 p12dcx = safeContentsCtx->p12dcx;
michael@0 335
michael@0 336 rv = SEC_ASN1DecoderUpdate(safeContentsCtx->currentSafeBagA1Dcx, data, len);
michael@0 337 if(rv != SECSuccess) {
michael@0 338 p12dcx->errorValue = PORT_GetError();
michael@0 339 goto loser;
michael@0 340 }
michael@0 341
michael@0 342 return;
michael@0 343
michael@0 344 loser:
michael@0 345 /* set the error, and finish the decoder context. because there
michael@0 346 * is not a way of returning an error message, it may be worth
michael@0 347 * while to do a check higher up and finish any decoding contexts
michael@0 348 * that are still open.
michael@0 349 */
michael@0 350 p12dcx->error = PR_TRUE;
michael@0 351 SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
michael@0 352 safeContentsCtx->currentSafeBagA1Dcx = NULL;
michael@0 353 return;
michael@0 354 }
michael@0 355
michael@0 356 /* notify function for decoding safeBags. This function is
michael@0 357 * used to filter safeBag types which are not supported,
michael@0 358 * initiate the decoding of nested safe contents, and decode
michael@0 359 * safeBags in general. this function is set when the decoder
michael@0 360 * context for the safeBag is first created.
michael@0 361 */
michael@0 362 static void
michael@0 363 sec_pkcs12_decoder_safe_bag_notify(void *arg, PRBool before,
michael@0 364 void *dest, int real_depth)
michael@0 365 {
michael@0 366 sec_PKCS12SafeContentsContext *safeContentsCtx =
michael@0 367 (sec_PKCS12SafeContentsContext *)arg;
michael@0 368 SEC_PKCS12DecoderContext *p12dcx;
michael@0 369 sec_PKCS12SafeBag *bag;
michael@0 370 PRBool after;
michael@0 371
michael@0 372 /* if an error is encountered, return */
michael@0 373 if(!safeContentsCtx || !safeContentsCtx->p12dcx ||
michael@0 374 safeContentsCtx->p12dcx->error) {
michael@0 375 return;
michael@0 376 }
michael@0 377 p12dcx = safeContentsCtx->p12dcx;
michael@0 378
michael@0 379 /* to make things more readable */
michael@0 380 if(before)
michael@0 381 after = PR_FALSE;
michael@0 382 else
michael@0 383 after = PR_TRUE;
michael@0 384
michael@0 385 /* have we determined the safeBagType yet? */
michael@0 386 bag = safeContentsCtx->currentSafeBag;
michael@0 387 if(bag->bagTypeTag == NULL) {
michael@0 388 if(after && (dest == &(bag->safeBagType))) {
michael@0 389 bag->bagTypeTag = SECOID_FindOID(&(bag->safeBagType));
michael@0 390 if(bag->bagTypeTag == NULL) {
michael@0 391 p12dcx->error = PR_TRUE;
michael@0 392 p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
michael@0 393 }
michael@0 394 }
michael@0 395 return;
michael@0 396 }
michael@0 397
michael@0 398 /* process the safeBag depending on it's type. those
michael@0 399 * which we do not support, are ignored. we start a decoding
michael@0 400 * context for a nested safeContents.
michael@0 401 */
michael@0 402 switch(bag->bagTypeTag->offset) {
michael@0 403 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
michael@0 404 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
michael@0 405 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
michael@0 406 break;
michael@0 407 case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
michael@0 408 /* if we are just starting to decode the safeContents, initialize
michael@0 409 * a new safeContentsCtx to process it.
michael@0 410 */
michael@0 411 if(before && (dest == &(bag->safeBagContent))) {
michael@0 412 sec_pkcs12_decoder_begin_nested_safe_contents(safeContentsCtx);
michael@0 413 } else if(after && (dest == &(bag->safeBagContent))) {
michael@0 414 /* clean up the nested decoding */
michael@0 415 sec_pkcs12_decoder_finish_nested_safe_contents(safeContentsCtx);
michael@0 416 }
michael@0 417 break;
michael@0 418 case SEC_OID_PKCS12_V1_CRL_BAG_ID:
michael@0 419 case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
michael@0 420 default:
michael@0 421 /* skip any safe bag types we don't understand or handle */
michael@0 422 safeContentsCtx->skipCurrentSafeBag = PR_TRUE;
michael@0 423 break;
michael@0 424 }
michael@0 425
michael@0 426 return;
michael@0 427 }
michael@0 428
michael@0 429 /* notify function for decoding safe contents. each entry in the
michael@0 430 * safe contents is a safeBag which needs to be allocated and
michael@0 431 * the decoding context initialized at the beginning and then
michael@0 432 * the context needs to be closed and finished at the end.
michael@0 433 *
michael@0 434 * this function is set when the safeContents decode context is
michael@0 435 * initialized.
michael@0 436 */
michael@0 437 static void
michael@0 438 sec_pkcs12_decoder_safe_contents_notify(void *arg, PRBool before,
michael@0 439 void *dest, int real_depth)
michael@0 440 {
michael@0 441 sec_PKCS12SafeContentsContext *safeContentsCtx =
michael@0 442 (sec_PKCS12SafeContentsContext*)arg;
michael@0 443 SEC_PKCS12DecoderContext *p12dcx;
michael@0 444 SECStatus rv;
michael@0 445
michael@0 446 /* if there is an error we don't want to continue processing,
michael@0 447 * just return and keep going.
michael@0 448 */
michael@0 449 if(!safeContentsCtx || !safeContentsCtx->p12dcx
michael@0 450 || safeContentsCtx->p12dcx->error) {
michael@0 451 return;
michael@0 452 }
michael@0 453 p12dcx = safeContentsCtx->p12dcx;
michael@0 454
michael@0 455 /* if we are done with the current safeBag, then we need to
michael@0 456 * finish the context and set the state variables appropriately.
michael@0 457 */
michael@0 458 if(!before) {
michael@0 459 SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
michael@0 460 SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
michael@0 461 safeContentsCtx->currentSafeBagA1Dcx = NULL;
michael@0 462 safeContentsCtx->skipCurrentSafeBag = PR_FALSE;
michael@0 463 } else {
michael@0 464 /* we are starting a new safe bag. we need to allocate space
michael@0 465 * for the bag and initialize the decoding context.
michael@0 466 */
michael@0 467 rv = sec_pkcs12_decoder_init_new_safe_bag(safeContentsCtx);
michael@0 468 if(rv != SECSuccess) {
michael@0 469 goto loser;
michael@0 470 }
michael@0 471
michael@0 472 /* set up the decoder context */
michael@0 473 safeContentsCtx->currentSafeBagA1Dcx =
michael@0 474 SEC_ASN1DecoderStart(p12dcx->arena,
michael@0 475 safeContentsCtx->currentSafeBag,
michael@0 476 sec_PKCS12SafeBagTemplate);
michael@0 477 if(!safeContentsCtx->currentSafeBagA1Dcx) {
michael@0 478 p12dcx->errorValue = PORT_GetError();
michael@0 479 goto loser;
michael@0 480 }
michael@0 481
michael@0 482 /* set the notify and filter procs so that the safe bag
michael@0 483 * data gets sent to the proper location when decoding.
michael@0 484 */
michael@0 485 SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->currentSafeBagA1Dcx,
michael@0 486 sec_pkcs12_decoder_safe_bag_notify,
michael@0 487 safeContentsCtx);
michael@0 488 SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsA1Dcx,
michael@0 489 sec_pkcs12_decoder_safe_bag_update,
michael@0 490 safeContentsCtx, PR_TRUE);
michael@0 491 }
michael@0 492
michael@0 493 return;
michael@0 494
michael@0 495 loser:
michael@0 496 /* in the event of an error, we want to close the decoding
michael@0 497 * context and clear the filter and notify procedures.
michael@0 498 */
michael@0 499 p12dcx->error = PR_TRUE;
michael@0 500
michael@0 501 if(safeContentsCtx->currentSafeBagA1Dcx) {
michael@0 502 SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
michael@0 503 safeContentsCtx->currentSafeBagA1Dcx = NULL;
michael@0 504 }
michael@0 505
michael@0 506 SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->safeContentsA1Dcx);
michael@0 507 SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
michael@0 508
michael@0 509 return;
michael@0 510 }
michael@0 511
michael@0 512 /* initialize the safeContents for decoding. this routine
michael@0 513 * is used for authenticatedSafes as well as nested safeContents.
michael@0 514 */
michael@0 515 static sec_PKCS12SafeContentsContext *
michael@0 516 sec_pkcs12_decoder_safe_contents_init_decode(SEC_PKCS12DecoderContext *p12dcx,
michael@0 517 PRBool nestedSafe)
michael@0 518 {
michael@0 519 sec_PKCS12SafeContentsContext *safeContentsCtx = NULL;
michael@0 520 const SEC_ASN1Template *theTemplate;
michael@0 521
michael@0 522 if(!p12dcx || p12dcx->error) {
michael@0 523 return NULL;
michael@0 524 }
michael@0 525
michael@0 526 /* allocate a new safeContents list or grow the existing list and
michael@0 527 * append the new safeContents onto the end.
michael@0 528 */
michael@0 529 p12dcx->safeContentsList = (!p12dcx->safeContentsCnt)
michael@0 530 ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeContentsContext *, 2)
michael@0 531 : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeContentsList,
michael@0 532 sec_PKCS12SafeContentsContext *,
michael@0 533 1 + p12dcx->safeContentsCnt,
michael@0 534 2 + p12dcx->safeContentsCnt);
michael@0 535
michael@0 536 if(!p12dcx->safeContentsList) {
michael@0 537 p12dcx->errorValue = PORT_GetError();
michael@0 538 goto loser;
michael@0 539 }
michael@0 540
michael@0 541 p12dcx->safeContentsList[p12dcx->safeContentsCnt] = safeContentsCtx =
michael@0 542 PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeContentsContext);
michael@0 543 if(!p12dcx->safeContentsList[p12dcx->safeContentsCnt]) {
michael@0 544 p12dcx->errorValue = PORT_GetError();
michael@0 545 goto loser;
michael@0 546 }
michael@0 547 p12dcx->safeContentsList[++p12dcx->safeContentsCnt] = NULL;
michael@0 548
michael@0 549 /* set up the state variables */
michael@0 550 safeContentsCtx->p12dcx = p12dcx;
michael@0 551 safeContentsCtx->arena = p12dcx->arena;
michael@0 552
michael@0 553 /* begin the decoding -- the template is based on whether we are
michael@0 554 * decoding a nested safeContents or not.
michael@0 555 */
michael@0 556 if(nestedSafe == PR_TRUE) {
michael@0 557 theTemplate = sec_PKCS12NestedSafeContentsDecodeTemplate;
michael@0 558 } else {
michael@0 559 theTemplate = sec_PKCS12SafeContentsDecodeTemplate;
michael@0 560 }
michael@0 561
michael@0 562 /* start the decoder context */
michael@0 563 safeContentsCtx->safeContentsA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
michael@0 564 &safeContentsCtx->safeContents,
michael@0 565 theTemplate);
michael@0 566
michael@0 567 if(!safeContentsCtx->safeContentsA1Dcx) {
michael@0 568 p12dcx->errorValue = PORT_GetError();
michael@0 569 goto loser;
michael@0 570 }
michael@0 571
michael@0 572 /* set the safeContents notify procedure to look for
michael@0 573 * and start the decode of safeBags.
michael@0 574 */
michael@0 575 SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsA1Dcx,
michael@0 576 sec_pkcs12_decoder_safe_contents_notify,
michael@0 577 safeContentsCtx);
michael@0 578
michael@0 579 return safeContentsCtx;
michael@0 580
michael@0 581 loser:
michael@0 582 /* in the case of an error, we want to finish the decoder
michael@0 583 * context and set the error flag.
michael@0 584 */
michael@0 585 if(safeContentsCtx && safeContentsCtx->safeContentsA1Dcx) {
michael@0 586 SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
michael@0 587 safeContentsCtx->safeContentsA1Dcx = NULL;
michael@0 588 }
michael@0 589
michael@0 590 p12dcx->error = PR_TRUE;
michael@0 591
michael@0 592 return NULL;
michael@0 593 }
michael@0 594
michael@0 595 /* wrapper for updating safeContents. this is set as the filter of
michael@0 596 * safeBag when there is a nested safeContents.
michael@0 597 */
michael@0 598 static void
michael@0 599 sec_pkcs12_decoder_nested_safe_contents_update(void *arg, const char *buf,
michael@0 600 unsigned long len, int depth,
michael@0 601 SEC_ASN1EncodingPart data_kind)
michael@0 602 {
michael@0 603 sec_PKCS12SafeContentsContext *safeContentsCtx =
michael@0 604 (sec_PKCS12SafeContentsContext *)arg;
michael@0 605 SEC_PKCS12DecoderContext *p12dcx;
michael@0 606 SECStatus rv;
michael@0 607
michael@0 608 /* check for an error */
michael@0 609 if(!safeContentsCtx || !safeContentsCtx->p12dcx
michael@0 610 || safeContentsCtx->p12dcx->error
michael@0 611 || !safeContentsCtx->safeContentsA1Dcx) {
michael@0 612 return;
michael@0 613 }
michael@0 614
michael@0 615 /* no need to update if no data sent in */
michael@0 616 if(!len || !buf) {
michael@0 617 return;
michael@0 618 }
michael@0 619
michael@0 620 /* update the decoding context */
michael@0 621 p12dcx = safeContentsCtx->p12dcx;
michael@0 622 rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len);
michael@0 623 if(rv != SECSuccess) {
michael@0 624 p12dcx->errorValue = PORT_GetError();
michael@0 625 goto loser;
michael@0 626 }
michael@0 627
michael@0 628 return;
michael@0 629
michael@0 630 loser:
michael@0 631 /* handle any errors. If a decoding context is open, close it. */
michael@0 632 p12dcx->error = PR_TRUE;
michael@0 633 if(safeContentsCtx->safeContentsA1Dcx) {
michael@0 634 SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
michael@0 635 safeContentsCtx->safeContentsA1Dcx = NULL;
michael@0 636 }
michael@0 637 }
michael@0 638
michael@0 639 /* whenever a new safeContentsSafeBag is encountered, we need
michael@0 640 * to init a safeContentsContext.
michael@0 641 */
michael@0 642 static SECStatus
michael@0 643 sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
michael@0 644 *safeContentsCtx)
michael@0 645 {
michael@0 646 /* check for an error */
michael@0 647 if(!safeContentsCtx || !safeContentsCtx->p12dcx ||
michael@0 648 safeContentsCtx->p12dcx->error) {
michael@0 649 return SECFailure;
michael@0 650 }
michael@0 651
michael@0 652 safeContentsCtx->nestedSafeContentsCtx =
michael@0 653 sec_pkcs12_decoder_safe_contents_init_decode(safeContentsCtx->p12dcx,
michael@0 654 PR_TRUE);
michael@0 655 if(!safeContentsCtx->nestedSafeContentsCtx) {
michael@0 656 return SECFailure;
michael@0 657 }
michael@0 658
michael@0 659 /* set up new filter proc */
michael@0 660 SEC_ASN1DecoderSetNotifyProc(
michael@0 661 safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx,
michael@0 662 sec_pkcs12_decoder_safe_contents_notify,
michael@0 663 safeContentsCtx->nestedSafeContentsCtx);
michael@0 664
michael@0 665 SEC_ASN1DecoderSetFilterProc(safeContentsCtx->currentSafeBagA1Dcx,
michael@0 666 sec_pkcs12_decoder_nested_safe_contents_update,
michael@0 667 safeContentsCtx->nestedSafeContentsCtx,
michael@0 668 PR_TRUE);
michael@0 669
michael@0 670 return SECSuccess;
michael@0 671 }
michael@0 672
michael@0 673 /* when the safeContents is done decoding, we need to reset the
michael@0 674 * proper filter and notify procs and close the decoding context
michael@0 675 */
michael@0 676 static SECStatus
michael@0 677 sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
michael@0 678 *safeContentsCtx)
michael@0 679 {
michael@0 680 /* check for error */
michael@0 681 if(!safeContentsCtx || !safeContentsCtx->p12dcx ||
michael@0 682 safeContentsCtx->p12dcx->error) {
michael@0 683 return SECFailure;
michael@0 684 }
michael@0 685
michael@0 686 /* clean up */
michael@0 687 SEC_ASN1DecoderClearFilterProc(safeContentsCtx->currentSafeBagA1Dcx);
michael@0 688 SEC_ASN1DecoderClearNotifyProc(
michael@0 689 safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx);
michael@0 690 SEC_ASN1DecoderFinish(
michael@0 691 safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx);
michael@0 692 safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx = NULL;
michael@0 693 safeContentsCtx->nestedSafeContentsCtx = NULL;
michael@0 694
michael@0 695 return SECSuccess;
michael@0 696 }
michael@0 697
michael@0 698 /* wrapper for updating safeContents. This is used when decoding
michael@0 699 * the nested safeContents and any authenticatedSafes.
michael@0 700 */
michael@0 701 static void
michael@0 702 sec_pkcs12_decoder_safe_contents_callback(void *arg, const char *buf,
michael@0 703 unsigned long len)
michael@0 704 {
michael@0 705 SECStatus rv;
michael@0 706 sec_PKCS12SafeContentsContext *safeContentsCtx =
michael@0 707 (sec_PKCS12SafeContentsContext *)arg;
michael@0 708 SEC_PKCS12DecoderContext *p12dcx;
michael@0 709
michael@0 710 /* check for error */
michael@0 711 if(!safeContentsCtx || !safeContentsCtx->p12dcx
michael@0 712 || safeContentsCtx->p12dcx->error
michael@0 713 || !safeContentsCtx->safeContentsA1Dcx) {
michael@0 714 return;
michael@0 715 }
michael@0 716 p12dcx = safeContentsCtx->p12dcx;
michael@0 717
michael@0 718 /* update the decoder */
michael@0 719 rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len);
michael@0 720 if(rv != SECSuccess) {
michael@0 721 /* if we fail while trying to decode a 'safe', it's probably because
michael@0 722 * we didn't have the correct password. */
michael@0 723 PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 724 p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
michael@0 725 SEC_PKCS7DecoderAbort(p12dcx->currentASafeP7Dcx,SEC_ERROR_BAD_PASSWORD);
michael@0 726 goto loser;
michael@0 727 }
michael@0 728
michael@0 729 return;
michael@0 730
michael@0 731 loser:
michael@0 732 /* set the error and finish the context */
michael@0 733 p12dcx->error = PR_TRUE;
michael@0 734 if(safeContentsCtx->safeContentsA1Dcx) {
michael@0 735 SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
michael@0 736 safeContentsCtx->safeContentsA1Dcx = NULL;
michael@0 737 }
michael@0 738
michael@0 739 return;
michael@0 740 }
michael@0 741
michael@0 742 /* this is a wrapper for the ASN1 decoder to call SEC_PKCS7DecoderUpdate
michael@0 743 */
michael@0 744 static void
michael@0 745 sec_pkcs12_decoder_wrap_p7_update(void *arg, const char *data,
michael@0 746 unsigned long len, int depth,
michael@0 747 SEC_ASN1EncodingPart data_kind)
michael@0 748 {
michael@0 749 SEC_PKCS7DecoderContext *p7dcx = (SEC_PKCS7DecoderContext *)arg;
michael@0 750
michael@0 751 SEC_PKCS7DecoderUpdate(p7dcx, data, len);
michael@0 752 }
michael@0 753
michael@0 754 /* notify function for decoding aSafes. at the beginning,
michael@0 755 * of an authenticatedSafe, we start a decode of a safeContents.
michael@0 756 * at the end, we clean up the safeContents decoder context and
michael@0 757 * reset state variables
michael@0 758 */
michael@0 759 static void
michael@0 760 sec_pkcs12_decoder_asafes_notify(void *arg, PRBool before, void *dest,
michael@0 761 int real_depth)
michael@0 762 {
michael@0 763 SEC_PKCS12DecoderContext *p12dcx;
michael@0 764 sec_PKCS12SafeContentsContext *safeContentsCtx;
michael@0 765
michael@0 766 /* make sure no error occurred. */
michael@0 767 p12dcx = (SEC_PKCS12DecoderContext *)arg;
michael@0 768 if(!p12dcx || p12dcx->error) {
michael@0 769 return;
michael@0 770 }
michael@0 771
michael@0 772 if(before) {
michael@0 773
michael@0 774 /* init a new safeContentsContext */
michael@0 775 safeContentsCtx = sec_pkcs12_decoder_safe_contents_init_decode(p12dcx,
michael@0 776 PR_FALSE);
michael@0 777 if(!safeContentsCtx) {
michael@0 778 goto loser;
michael@0 779 }
michael@0 780
michael@0 781 /* initiate the PKCS7ContentInfo decode */
michael@0 782 p12dcx->currentASafeP7Dcx = SEC_PKCS7DecoderStart(
michael@0 783 sec_pkcs12_decoder_safe_contents_callback,
michael@0 784 safeContentsCtx,
michael@0 785 p12dcx->pwfn, p12dcx->pwfnarg,
michael@0 786 sec_pkcs12_decoder_get_decrypt_key, p12dcx,
michael@0 787 sec_pkcs12_decoder_decryption_allowed);
michael@0 788 if(!p12dcx->currentASafeP7Dcx) {
michael@0 789 p12dcx->errorValue = PORT_GetError();
michael@0 790 goto loser;
michael@0 791 }
michael@0 792 SEC_ASN1DecoderSetFilterProc(p12dcx->aSafeA1Dcx,
michael@0 793 sec_pkcs12_decoder_wrap_p7_update,
michael@0 794 p12dcx->currentASafeP7Dcx, PR_TRUE);
michael@0 795 }
michael@0 796
michael@0 797 if(!before) {
michael@0 798 /* if one is being decoded, finish the decode */
michael@0 799 if(p12dcx->currentASafeP7Dcx != NULL) {
michael@0 800 SEC_PKCS7ContentInfo * cinfo;
michael@0 801 unsigned int cnt = p12dcx->safeContentsCnt - 1;
michael@0 802 safeContentsCtx = p12dcx->safeContentsList[cnt];
michael@0 803 if (safeContentsCtx->safeContentsA1Dcx) {
michael@0 804 SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
michael@0 805 safeContentsCtx->safeContentsA1Dcx = NULL;
michael@0 806 }
michael@0 807 cinfo = SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
michael@0 808 p12dcx->currentASafeP7Dcx = NULL;
michael@0 809 if(!cinfo) {
michael@0 810 p12dcx->errorValue = PORT_GetError();
michael@0 811 goto loser;
michael@0 812 }
michael@0 813 SEC_PKCS7DestroyContentInfo(cinfo); /* don't leak it */
michael@0 814 }
michael@0 815 }
michael@0 816
michael@0 817
michael@0 818 return;
michael@0 819
michael@0 820 loser:
michael@0 821 /* set the error flag */
michael@0 822 p12dcx->error = PR_TRUE;
michael@0 823 return;
michael@0 824 }
michael@0 825
michael@0 826 /* wrapper for updating asafes decoding context. this function
michael@0 827 * writes data being decoded to disk, so that a mac can be computed
michael@0 828 * later.
michael@0 829 */
michael@0 830 static void
michael@0 831 sec_pkcs12_decoder_asafes_callback(void *arg, const char *buf,
michael@0 832 unsigned long len)
michael@0 833 {
michael@0 834 SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
michael@0 835 SECStatus rv;
michael@0 836
michael@0 837 if(!p12dcx || p12dcx->error) {
michael@0 838 return;
michael@0 839 }
michael@0 840
michael@0 841 /* update the context */
michael@0 842 rv = SEC_ASN1DecoderUpdate(p12dcx->aSafeA1Dcx, buf, len);
michael@0 843 if(rv != SECSuccess) {
michael@0 844 p12dcx->errorValue = PORT_GetError();
michael@0 845 p12dcx->error = PR_TRUE;
michael@0 846 goto loser;
michael@0 847 }
michael@0 848
michael@0 849 /* if we are writing to a file, write out the new information */
michael@0 850 if(p12dcx->dWrite) {
michael@0 851 unsigned long writeLen = (*p12dcx->dWrite)(p12dcx->dArg,
michael@0 852 (unsigned char *)buf, len);
michael@0 853 if(writeLen != len) {
michael@0 854 p12dcx->errorValue = PORT_GetError();
michael@0 855 goto loser;
michael@0 856 }
michael@0 857 }
michael@0 858
michael@0 859 return;
michael@0 860
michael@0 861 loser:
michael@0 862 /* set the error flag */
michael@0 863 p12dcx->error = PR_TRUE;
michael@0 864 SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
michael@0 865 p12dcx->aSafeA1Dcx = NULL;
michael@0 866
michael@0 867 return;
michael@0 868 }
michael@0 869
michael@0 870 /* start the decode of an authenticatedSafe contentInfo.
michael@0 871 */
michael@0 872 static SECStatus
michael@0 873 sec_pkcs12_decode_start_asafes_cinfo(SEC_PKCS12DecoderContext *p12dcx)
michael@0 874 {
michael@0 875 if(!p12dcx || p12dcx->error) {
michael@0 876 return SECFailure;
michael@0 877 }
michael@0 878
michael@0 879 /* start the decode context */
michael@0 880 p12dcx->aSafeA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
michael@0 881 &p12dcx->authSafe,
michael@0 882 sec_PKCS12AuthenticatedSafeTemplate);
michael@0 883 if(!p12dcx->aSafeA1Dcx) {
michael@0 884 p12dcx->errorValue = PORT_GetError();
michael@0 885 goto loser;
michael@0 886 }
michael@0 887
michael@0 888 /* set the notify function */
michael@0 889 SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeA1Dcx,
michael@0 890 sec_pkcs12_decoder_asafes_notify, p12dcx);
michael@0 891
michael@0 892 /* begin the authSafe decoder context */
michael@0 893 p12dcx->aSafeP7Dcx = SEC_PKCS7DecoderStart(
michael@0 894 sec_pkcs12_decoder_asafes_callback, p12dcx,
michael@0 895 p12dcx->pwfn, p12dcx->pwfnarg, NULL, NULL, NULL);
michael@0 896 if(!p12dcx->aSafeP7Dcx) {
michael@0 897 p12dcx->errorValue = PORT_GetError();
michael@0 898 goto loser;
michael@0 899 }
michael@0 900
michael@0 901 /* open the temp file for writing, if the digest functions were set */
michael@0 902 if(p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE)
michael@0 903 != SECSuccess) {
michael@0 904 p12dcx->errorValue = PORT_GetError();
michael@0 905 goto loser;
michael@0 906 }
michael@0 907 /* dOpen(dArg, PR_FALSE) creates the temp file */
michael@0 908 p12dcx->dIsOpen = PR_TRUE;
michael@0 909
michael@0 910 return SECSuccess;
michael@0 911
michael@0 912 loser:
michael@0 913 p12dcx->error = PR_TRUE;
michael@0 914
michael@0 915 if(p12dcx->aSafeA1Dcx) {
michael@0 916 SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
michael@0 917 p12dcx->aSafeA1Dcx = NULL;
michael@0 918 }
michael@0 919
michael@0 920 if(p12dcx->aSafeP7Dcx) {
michael@0 921 SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
michael@0 922 p12dcx->aSafeP7Dcx = NULL;
michael@0 923 }
michael@0 924
michael@0 925 return SECFailure;
michael@0 926 }
michael@0 927
michael@0 928 /* wrapper for updating the safeContents. this function is used as
michael@0 929 * a filter for the pfx when decoding the authenticated safes
michael@0 930 */
michael@0 931 static void
michael@0 932 sec_pkcs12_decode_asafes_cinfo_update(void *arg, const char *buf,
michael@0 933 unsigned long len, int depth,
michael@0 934 SEC_ASN1EncodingPart data_kind)
michael@0 935 {
michael@0 936 SEC_PKCS12DecoderContext *p12dcx;
michael@0 937 SECStatus rv;
michael@0 938
michael@0 939 p12dcx = (SEC_PKCS12DecoderContext*)arg;
michael@0 940 if(!p12dcx || p12dcx->error) {
michael@0 941 return;
michael@0 942 }
michael@0 943
michael@0 944 /* update the safeContents decoder */
michael@0 945 rv = SEC_PKCS7DecoderUpdate(p12dcx->aSafeP7Dcx, buf, len);
michael@0 946 if(rv != SECSuccess) {
michael@0 947 p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
michael@0 948 goto loser;
michael@0 949 }
michael@0 950
michael@0 951 return;
michael@0 952
michael@0 953 loser:
michael@0 954
michael@0 955 /* did we find an error? if so, close the context and set the
michael@0 956 * error flag.
michael@0 957 */
michael@0 958 SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
michael@0 959 p12dcx->aSafeP7Dcx = NULL;
michael@0 960 p12dcx->error = PR_TRUE;
michael@0 961 }
michael@0 962
michael@0 963 /* notify procedure used while decoding the pfx. When we encounter
michael@0 964 * the authSafes, we want to trigger the decoding of authSafes as well
michael@0 965 * as when we encounter the macData, trigger the decoding of it. we do
michael@0 966 * this because we we are streaming the decoder and not decoding in place.
michael@0 967 * the pfx which is the destination, only has the version decoded into it.
michael@0 968 */
michael@0 969 static void
michael@0 970 sec_pkcs12_decoder_pfx_notify_proc(void *arg, PRBool before, void *dest,
michael@0 971 int real_depth)
michael@0 972 {
michael@0 973 SECStatus rv;
michael@0 974 SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext*)arg;
michael@0 975
michael@0 976 /* if an error occurs, clear the notifyProc and the filterProc
michael@0 977 * and continue.
michael@0 978 */
michael@0 979 if(p12dcx->error) {
michael@0 980 SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxA1Dcx);
michael@0 981 SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx);
michael@0 982 return;
michael@0 983 }
michael@0 984
michael@0 985 if(before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
michael@0 986
michael@0 987 /* we want to make sure this is a version we support */
michael@0 988 if(!sec_pkcs12_proper_version(&p12dcx->pfx)) {
michael@0 989 p12dcx->errorValue = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
michael@0 990 goto loser;
michael@0 991 }
michael@0 992
michael@0 993 /* start the decode of the aSafes cinfo... */
michael@0 994 rv = sec_pkcs12_decode_start_asafes_cinfo(p12dcx);
michael@0 995 if(rv != SECSuccess) {
michael@0 996 goto loser;
michael@0 997 }
michael@0 998
michael@0 999 /* set the filter proc to update the authenticated safes. */
michael@0 1000 SEC_ASN1DecoderSetFilterProc(p12dcx->pfxA1Dcx,
michael@0 1001 sec_pkcs12_decode_asafes_cinfo_update,
michael@0 1002 p12dcx, PR_TRUE);
michael@0 1003 }
michael@0 1004
michael@0 1005 if(!before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
michael@0 1006
michael@0 1007 /* we are done decoding the authenticatedSafes, so we need to
michael@0 1008 * finish the decoderContext and clear the filter proc
michael@0 1009 * and close the hmac callback, if present
michael@0 1010 */
michael@0 1011 p12dcx->aSafeCinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
michael@0 1012 p12dcx->aSafeP7Dcx = NULL;
michael@0 1013 if(!p12dcx->aSafeCinfo) {
michael@0 1014 p12dcx->errorValue = PORT_GetError();
michael@0 1015 goto loser;
michael@0 1016 }
michael@0 1017 SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx);
michael@0 1018 if(p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE)
michael@0 1019 != SECSuccess)) {
michael@0 1020 p12dcx->errorValue = PORT_GetError();
michael@0 1021 goto loser;
michael@0 1022 }
michael@0 1023
michael@0 1024 }
michael@0 1025
michael@0 1026 return;
michael@0 1027
michael@0 1028 loser:
michael@0 1029 p12dcx->error = PR_TRUE;
michael@0 1030 }
michael@0 1031
michael@0 1032 /* default implementations of the open/close/read/write functions for
michael@0 1033 SEC_PKCS12DecoderStart
michael@0 1034 */
michael@0 1035
michael@0 1036 #define DEFAULT_TEMP_SIZE 4096
michael@0 1037
michael@0 1038 static SECStatus
michael@0 1039 p12u_DigestOpen(void *arg, PRBool readData)
michael@0 1040 {
michael@0 1041 SEC_PKCS12DecoderContext* p12cxt = arg;
michael@0 1042
michael@0 1043 p12cxt->currentpos = 0;
michael@0 1044
michael@0 1045 if (PR_FALSE == readData) {
michael@0 1046 /* allocate an initial buffer */
michael@0 1047 p12cxt->filesize = 0;
michael@0 1048 p12cxt->allocated = DEFAULT_TEMP_SIZE;
michael@0 1049 p12cxt->buffer = PORT_Alloc(DEFAULT_TEMP_SIZE);
michael@0 1050 PR_ASSERT(p12cxt->buffer);
michael@0 1051 }
michael@0 1052 else
michael@0 1053 {
michael@0 1054 PR_ASSERT(p12cxt->buffer);
michael@0 1055 if (!p12cxt->buffer) {
michael@0 1056 return SECFailure; /* no data to read */
michael@0 1057 }
michael@0 1058 }
michael@0 1059
michael@0 1060 return SECSuccess;
michael@0 1061 }
michael@0 1062
michael@0 1063 static SECStatus
michael@0 1064 p12u_DigestClose(void *arg, PRBool removeFile)
michael@0 1065 {
michael@0 1066 SEC_PKCS12DecoderContext* p12cxt = arg;
michael@0 1067
michael@0 1068 PR_ASSERT(p12cxt);
michael@0 1069 if (!p12cxt) {
michael@0 1070 return SECFailure;
michael@0 1071 }
michael@0 1072 p12cxt->currentpos = 0;
michael@0 1073
michael@0 1074 if (PR_TRUE == removeFile) {
michael@0 1075 PR_ASSERT(p12cxt->buffer);
michael@0 1076 if (!p12cxt->buffer) {
michael@0 1077 return SECFailure;
michael@0 1078 }
michael@0 1079 if (p12cxt->buffer) {
michael@0 1080 PORT_Free(p12cxt->buffer);
michael@0 1081 p12cxt->buffer = NULL;
michael@0 1082 p12cxt->allocated = 0;
michael@0 1083 p12cxt->filesize = 0;
michael@0 1084 }
michael@0 1085 }
michael@0 1086
michael@0 1087 return SECSuccess;
michael@0 1088 }
michael@0 1089
michael@0 1090 static int
michael@0 1091 p12u_DigestRead(void *arg, unsigned char *buf, unsigned long len)
michael@0 1092 {
michael@0 1093 int toread = len;
michael@0 1094 SEC_PKCS12DecoderContext* p12cxt = arg;
michael@0 1095
michael@0 1096 if(!buf || len == 0 || !p12cxt->buffer) {
michael@0 1097 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1098 return -1;
michael@0 1099 }
michael@0 1100
michael@0 1101 if ((p12cxt->filesize - p12cxt->currentpos) < (long)len) {
michael@0 1102 /* trying to read past the end of the buffer */
michael@0 1103 toread = p12cxt->filesize - p12cxt->currentpos;
michael@0 1104 }
michael@0 1105 memcpy(buf, (char*)p12cxt->buffer + p12cxt->currentpos, toread);
michael@0 1106 p12cxt->currentpos += toread;
michael@0 1107 return toread;
michael@0 1108 }
michael@0 1109
michael@0 1110 static int
michael@0 1111 p12u_DigestWrite(void *arg, unsigned char *buf, unsigned long len)
michael@0 1112 {
michael@0 1113 SEC_PKCS12DecoderContext* p12cxt = arg;
michael@0 1114
michael@0 1115 if(!buf || len == 0) {
michael@0 1116 return -1;
michael@0 1117 }
michael@0 1118
michael@0 1119 if (p12cxt->currentpos+(long)len > p12cxt->filesize) {
michael@0 1120 p12cxt->filesize = p12cxt->currentpos + len;
michael@0 1121 }
michael@0 1122 else {
michael@0 1123 p12cxt->filesize += len;
michael@0 1124 }
michael@0 1125 if (p12cxt->filesize > p12cxt->allocated) {
michael@0 1126 void* newbuffer;
michael@0 1127 size_t newsize = p12cxt->filesize + DEFAULT_TEMP_SIZE;
michael@0 1128 newbuffer = PORT_Realloc(p12cxt->buffer, newsize);
michael@0 1129 if (NULL == newbuffer) {
michael@0 1130 return -1; /* can't extend the buffer */
michael@0 1131 }
michael@0 1132 p12cxt->buffer = newbuffer;
michael@0 1133 p12cxt->allocated = newsize;
michael@0 1134 }
michael@0 1135 PR_ASSERT(p12cxt->buffer);
michael@0 1136 memcpy((char*)p12cxt->buffer + p12cxt->currentpos, buf, len);
michael@0 1137 p12cxt->currentpos += len;
michael@0 1138 return len;
michael@0 1139 }
michael@0 1140
michael@0 1141 /* SEC_PKCS12DecoderStart
michael@0 1142 * Creates a decoder context for decoding a PKCS 12 PDU objct.
michael@0 1143 * This function sets up the initial decoding context for the
michael@0 1144 * PFX and sets the needed state variables.
michael@0 1145 *
michael@0 1146 * pwitem - the password for the hMac and any encoded safes.
michael@0 1147 * this should be changed to take a callback which retrieves
michael@0 1148 * the password. it may be possible for different safes to
michael@0 1149 * have different passwords. also, the password is already
michael@0 1150 * in unicode. it should probably be converted down below via
michael@0 1151 * a unicode conversion callback.
michael@0 1152 * slot - the slot to import the dataa into should multiple slots
michael@0 1153 * be supported based on key type and cert type?
michael@0 1154 * dOpen, dClose, dRead, dWrite - digest routines for writing data
michael@0 1155 * to a file so it could be read back and the hmac recomputed
michael@0 1156 * and verified. doesn't seem to be a way for both encoding
michael@0 1157 * and decoding to be single pass, thus the need for these
michael@0 1158 * routines.
michael@0 1159 * dArg - the argument for dOpen, etc.
michael@0 1160 *
michael@0 1161 * if NULL == dOpen == dClose == dRead == dWrite == dArg, then default
michael@0 1162 * implementations using a memory buffer are used
michael@0 1163 *
michael@0 1164 * This function returns the decoder context, if it was successful.
michael@0 1165 * Otherwise, null is returned.
michael@0 1166 */
michael@0 1167 SEC_PKCS12DecoderContext *
michael@0 1168 SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx,
michael@0 1169 digestOpenFn dOpen, digestCloseFn dClose,
michael@0 1170 digestIOFn dRead, digestIOFn dWrite, void *dArg)
michael@0 1171 {
michael@0 1172 SEC_PKCS12DecoderContext *p12dcx;
michael@0 1173 PLArenaPool *arena;
michael@0 1174
michael@0 1175 arena = PORT_NewArena(2048); /* different size? */
michael@0 1176 if(!arena) {
michael@0 1177 return NULL; /* error is already set */
michael@0 1178 }
michael@0 1179
michael@0 1180 /* allocate the decoder context and set the state variables */
michael@0 1181 p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext);
michael@0 1182 if(!p12dcx) {
michael@0 1183 goto loser; /* error is already set */
michael@0 1184 }
michael@0 1185
michael@0 1186 if (!dOpen && !dClose && !dRead && !dWrite && !dArg) {
michael@0 1187 /* use default implementations */
michael@0 1188 dOpen = p12u_DigestOpen;
michael@0 1189 dClose = p12u_DigestClose;
michael@0 1190 dRead = p12u_DigestRead;
michael@0 1191 dWrite = p12u_DigestWrite;
michael@0 1192 dArg = (void*)p12dcx;
michael@0 1193 }
michael@0 1194
michael@0 1195 p12dcx->arena = arena;
michael@0 1196 p12dcx->pwitem = pwitem;
michael@0 1197 p12dcx->slot = (slot ? PK11_ReferenceSlot(slot)
michael@0 1198 : PK11_GetInternalKeySlot());
michael@0 1199 p12dcx->wincx = wincx;
michael@0 1200 p12dcx->tokenCAs = SECPKCS12TargetTokenNoCAs;
michael@0 1201 #ifdef IS_LITTLE_ENDIAN
michael@0 1202 p12dcx->swapUnicodeBytes = PR_TRUE;
michael@0 1203 #else
michael@0 1204 p12dcx->swapUnicodeBytes = PR_FALSE;
michael@0 1205 #endif
michael@0 1206 p12dcx->errorValue = 0;
michael@0 1207 p12dcx->error = PR_FALSE;
michael@0 1208
michael@0 1209 /* start the decoding of the PFX and set the notify proc
michael@0 1210 * for the PFX item.
michael@0 1211 */
michael@0 1212 p12dcx->pfxA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx,
michael@0 1213 sec_PKCS12PFXItemTemplate);
michael@0 1214 if(!p12dcx->pfxA1Dcx) {
michael@0 1215 PK11_FreeSlot(p12dcx->slot);
michael@0 1216 goto loser;
michael@0 1217 }
michael@0 1218
michael@0 1219 SEC_ASN1DecoderSetNotifyProc(p12dcx->pfxA1Dcx,
michael@0 1220 sec_pkcs12_decoder_pfx_notify_proc,
michael@0 1221 p12dcx);
michael@0 1222
michael@0 1223 /* set up digest functions */
michael@0 1224 p12dcx->dOpen = dOpen;
michael@0 1225 p12dcx->dWrite = dWrite;
michael@0 1226 p12dcx->dClose = dClose;
michael@0 1227 p12dcx->dRead = dRead;
michael@0 1228 p12dcx->dArg = dArg;
michael@0 1229 p12dcx->dIsOpen = PR_FALSE;
michael@0 1230
michael@0 1231 p12dcx->keyList = NULL;
michael@0 1232 p12dcx->decitem.type = 0;
michael@0 1233 p12dcx->decitem.der = NULL;
michael@0 1234 p12dcx->decitem.hasKey = PR_FALSE;
michael@0 1235 p12dcx->decitem.friendlyName = NULL;
michael@0 1236 p12dcx->iteration = 0;
michael@0 1237
michael@0 1238 return p12dcx;
michael@0 1239
michael@0 1240 loser:
michael@0 1241 PORT_FreeArena(arena, PR_TRUE);
michael@0 1242 return NULL;
michael@0 1243 }
michael@0 1244
michael@0 1245 SECStatus
michael@0 1246 SEC_PKCS12DecoderSetTargetTokenCAs(SEC_PKCS12DecoderContext *p12dcx,
michael@0 1247 SECPKCS12TargetTokenCAs tokenCAs)
michael@0 1248 {
michael@0 1249 if (!p12dcx || p12dcx->error) {
michael@0 1250 return SECFailure;
michael@0 1251 }
michael@0 1252 p12dcx->tokenCAs = tokenCAs;
michael@0 1253 return SECSuccess;
michael@0 1254 }
michael@0 1255
michael@0 1256
michael@0 1257 /* SEC_PKCS12DecoderUpdate
michael@0 1258 * Streaming update sending more data to the decoder. If
michael@0 1259 * an error occurs, SECFailure is returned.
michael@0 1260 *
michael@0 1261 * p12dcx - the decoder context
michael@0 1262 * data, len - the data buffer and length of data to send to
michael@0 1263 * the update functions.
michael@0 1264 */
michael@0 1265 SECStatus
michael@0 1266 SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx,
michael@0 1267 unsigned char *data, unsigned long len)
michael@0 1268 {
michael@0 1269 SECStatus rv;
michael@0 1270
michael@0 1271 if(!p12dcx || p12dcx->error) {
michael@0 1272 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1273 return SECFailure;
michael@0 1274 }
michael@0 1275
michael@0 1276 /* update the PFX decoder context */
michael@0 1277 rv = SEC_ASN1DecoderUpdate(p12dcx->pfxA1Dcx, (const char *)data, len);
michael@0 1278 if(rv != SECSuccess) {
michael@0 1279 p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
michael@0 1280 goto loser;
michael@0 1281 }
michael@0 1282
michael@0 1283 return SECSuccess;
michael@0 1284
michael@0 1285 loser:
michael@0 1286
michael@0 1287 p12dcx->error = PR_TRUE;
michael@0 1288 return SECFailure;
michael@0 1289 }
michael@0 1290
michael@0 1291 /* This should be a nice sized buffer for reading in data (potentially large
michael@0 1292 ** amounts) to be MACed. It should be MUCH larger than HASH_LENGTH_MAX.
michael@0 1293 */
michael@0 1294 #define IN_BUF_LEN 1024
michael@0 1295 #ifdef DEBUG
michael@0 1296 static const char bufferEnd[] = { "BufferEnd" } ;
michael@0 1297 #endif
michael@0 1298 #define FUDGE 128 /* must be as large as bufferEnd or more. */
michael@0 1299
michael@0 1300 /* verify the hmac by reading the data from the temporary file
michael@0 1301 * using the routines specified when the decodingContext was
michael@0 1302 * created and return SECSuccess if the hmac matches.
michael@0 1303 */
michael@0 1304 static SECStatus
michael@0 1305 sec_pkcs12_decoder_verify_mac(SEC_PKCS12DecoderContext *p12dcx)
michael@0 1306 {
michael@0 1307 PK11Context * pk11cx = NULL;
michael@0 1308 PK11SymKey * symKey = NULL;
michael@0 1309 SECItem * params = NULL;
michael@0 1310 unsigned char * buf;
michael@0 1311 SECStatus rv = SECFailure;
michael@0 1312 SECStatus lrv;
michael@0 1313 unsigned int bufLen;
michael@0 1314 int iteration;
michael@0 1315 int bytesRead;
michael@0 1316 SECOidTag algtag;
michael@0 1317 SECItem hmacRes;
michael@0 1318 SECItem ignore = {0};
michael@0 1319 CK_MECHANISM_TYPE integrityMech;
michael@0 1320
michael@0 1321 if(!p12dcx || p12dcx->error) {
michael@0 1322 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1323 return SECFailure;
michael@0 1324 }
michael@0 1325 buf = (unsigned char *)PORT_Alloc(IN_BUF_LEN + FUDGE);
michael@0 1326 if (!buf)
michael@0 1327 return SECFailure; /* error code has been set. */
michael@0 1328
michael@0 1329 #ifdef DEBUG
michael@0 1330 memcpy(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd);
michael@0 1331 #endif
michael@0 1332
michael@0 1333 /* generate hmac key */
michael@0 1334 if(p12dcx->macData.iter.data) {
michael@0 1335 iteration = (int)DER_GetInteger(&p12dcx->macData.iter);
michael@0 1336 } else {
michael@0 1337 iteration = 1;
michael@0 1338 }
michael@0 1339
michael@0 1340 params = PK11_CreatePBEParams(&p12dcx->macData.macSalt, p12dcx->pwitem,
michael@0 1341 iteration);
michael@0 1342
michael@0 1343 algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm);
michael@0 1344 switch (algtag) {
michael@0 1345 case SEC_OID_SHA1:
michael@0 1346 integrityMech = CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN; break;
michael@0 1347 case SEC_OID_MD5:
michael@0 1348 integrityMech = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break;
michael@0 1349 case SEC_OID_MD2:
michael@0 1350 integrityMech = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break;
michael@0 1351 default:
michael@0 1352 goto loser;
michael@0 1353 }
michael@0 1354
michael@0 1355 symKey = PK11_KeyGen(NULL, integrityMech, params, 20, NULL);
michael@0 1356 PK11_DestroyPBEParams(params);
michael@0 1357 params = NULL;
michael@0 1358 if (!symKey) goto loser;
michael@0 1359 /* init hmac */
michael@0 1360 pk11cx = PK11_CreateContextBySymKey(sec_pkcs12_algtag_to_mech(algtag),
michael@0 1361 CKA_SIGN, symKey, &ignore);
michael@0 1362 if(!pk11cx) {
michael@0 1363 goto loser;
michael@0 1364 }
michael@0 1365 lrv = PK11_DigestBegin(pk11cx);
michael@0 1366 if (lrv == SECFailure ) {
michael@0 1367 goto loser;
michael@0 1368 }
michael@0 1369
michael@0 1370 /* try to open the data for readback */
michael@0 1371 if(p12dcx->dOpen && ((*p12dcx->dOpen)(p12dcx->dArg, PR_TRUE)
michael@0 1372 != SECSuccess)) {
michael@0 1373 goto loser;
michael@0 1374 }
michael@0 1375
michael@0 1376 /* read the data back IN_BUF_LEN bytes at a time and recompute
michael@0 1377 * the hmac. if fewer bytes are read than are requested, it is
michael@0 1378 * assumed that the end of file has been reached. if bytesRead
michael@0 1379 * is returned as -1, then an error occurred reading from the
michael@0 1380 * file.
michael@0 1381 */
michael@0 1382 do {
michael@0 1383 bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN);
michael@0 1384 if (bytesRead < 0) {
michael@0 1385 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_READ);
michael@0 1386 goto loser;
michael@0 1387 }
michael@0 1388 PORT_Assert(bytesRead <= IN_BUF_LEN);
michael@0 1389 PORT_Assert(!memcmp(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd));
michael@0 1390
michael@0 1391 if (bytesRead > IN_BUF_LEN) {
michael@0 1392 /* dRead callback overflowed buffer. */
michael@0 1393 PORT_SetError(SEC_ERROR_INPUT_LEN);
michael@0 1394 goto loser;
michael@0 1395 }
michael@0 1396
michael@0 1397 if (bytesRead) {
michael@0 1398 lrv = PK11_DigestOp(pk11cx, buf, bytesRead);
michael@0 1399 if (lrv == SECFailure) {
michael@0 1400 goto loser;
michael@0 1401 }
michael@0 1402 }
michael@0 1403 } while (bytesRead == IN_BUF_LEN);
michael@0 1404
michael@0 1405 /* finish the hmac context */
michael@0 1406 lrv = PK11_DigestFinal(pk11cx, buf, &bufLen, IN_BUF_LEN);
michael@0 1407 if (lrv == SECFailure ) {
michael@0 1408 goto loser;
michael@0 1409 }
michael@0 1410
michael@0 1411 hmacRes.data = buf;
michael@0 1412 hmacRes.len = bufLen;
michael@0 1413
michael@0 1414 /* is the hmac computed the same as the hmac which was decoded? */
michael@0 1415 rv = SECSuccess;
michael@0 1416 if(SECITEM_CompareItem(&hmacRes, &p12dcx->macData.safeMac.digest)
michael@0 1417 != SECEqual) {
michael@0 1418 PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
michael@0 1419 rv = SECFailure;
michael@0 1420 }
michael@0 1421
michael@0 1422 loser:
michael@0 1423 /* close the file and remove it */
michael@0 1424 if(p12dcx->dClose) {
michael@0 1425 (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
michael@0 1426 p12dcx->dIsOpen = PR_FALSE;
michael@0 1427 }
michael@0 1428
michael@0 1429 if(pk11cx) {
michael@0 1430 PK11_DestroyContext(pk11cx, PR_TRUE);
michael@0 1431 }
michael@0 1432 if (params) {
michael@0 1433 PK11_DestroyPBEParams(params);
michael@0 1434 }
michael@0 1435 if (symKey) {
michael@0 1436 PK11_FreeSymKey(symKey);
michael@0 1437 }
michael@0 1438 PORT_ZFree(buf, IN_BUF_LEN + FUDGE);
michael@0 1439
michael@0 1440 return rv;
michael@0 1441 }
michael@0 1442
michael@0 1443 /* SEC_PKCS12DecoderVerify
michael@0 1444 * Verify the macData or the signature of the decoded PKCS 12 PDU.
michael@0 1445 * If the signature or the macData do not match, SECFailure is
michael@0 1446 * returned.
michael@0 1447 *
michael@0 1448 * p12dcx - the decoder context
michael@0 1449 */
michael@0 1450 SECStatus
michael@0 1451 SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx)
michael@0 1452 {
michael@0 1453 SECStatus rv = SECSuccess;
michael@0 1454
michael@0 1455 /* make sure that no errors have occurred... */
michael@0 1456 if(!p12dcx) {
michael@0 1457 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1458 return SECFailure;
michael@0 1459 }
michael@0 1460 if(p12dcx->error) {
michael@0 1461 /* error code is already set! PORT_SetError(p12dcx->errorValue); */
michael@0 1462 return SECFailure;
michael@0 1463 }
michael@0 1464
michael@0 1465 rv = SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
michael@0 1466 p12dcx->pfxA1Dcx = NULL;
michael@0 1467 if(rv != SECSuccess) {
michael@0 1468 return rv;
michael@0 1469 }
michael@0 1470
michael@0 1471 /* check the signature or the mac depending on the type of
michael@0 1472 * integrity used.
michael@0 1473 */
michael@0 1474 if(p12dcx->pfx.encodedMacData.len) {
michael@0 1475 rv = SEC_ASN1DecodeItem(p12dcx->arena, &p12dcx->macData,
michael@0 1476 sec_PKCS12MacDataTemplate,
michael@0 1477 &p12dcx->pfx.encodedMacData);
michael@0 1478 if(rv == SECSuccess) {
michael@0 1479 return sec_pkcs12_decoder_verify_mac(p12dcx);
michael@0 1480 }
michael@0 1481 return rv;
michael@0 1482 }
michael@0 1483 if (SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner,
michael@0 1484 PR_FALSE)) {
michael@0 1485 return SECSuccess;
michael@0 1486 }
michael@0 1487 PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
michael@0 1488 return SECFailure;
michael@0 1489 }
michael@0 1490
michael@0 1491 /* SEC_PKCS12DecoderFinish
michael@0 1492 * Free any open ASN1 or PKCS7 decoder contexts and then
michael@0 1493 * free the arena pool which everything should be allocated
michael@0 1494 * from. This function should be called upon completion of
michael@0 1495 * decoding and installing of a pfx pdu. This should be
michael@0 1496 * called even if an error occurs.
michael@0 1497 *
michael@0 1498 * p12dcx - the decoder context
michael@0 1499 */
michael@0 1500 void
michael@0 1501 SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx)
michael@0 1502 {
michael@0 1503 unsigned int i;
michael@0 1504
michael@0 1505 if(!p12dcx) {
michael@0 1506 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1507 return;
michael@0 1508 }
michael@0 1509
michael@0 1510 if(p12dcx->pfxA1Dcx) {
michael@0 1511 SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
michael@0 1512 p12dcx->pfxA1Dcx = NULL;
michael@0 1513 }
michael@0 1514
michael@0 1515 if(p12dcx->aSafeA1Dcx) {
michael@0 1516 SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
michael@0 1517 p12dcx->aSafeA1Dcx = NULL;
michael@0 1518 }
michael@0 1519
michael@0 1520 /* cleanup any old ASN1 decoder contexts */
michael@0 1521 for (i = 0; i < p12dcx->safeContentsCnt; ++i) {
michael@0 1522 sec_PKCS12SafeContentsContext *safeContentsCtx, *nested;
michael@0 1523 safeContentsCtx = p12dcx->safeContentsList[i];
michael@0 1524 if (safeContentsCtx) {
michael@0 1525 nested = safeContentsCtx->nestedSafeContentsCtx;
michael@0 1526 while (nested) {
michael@0 1527 if (nested->safeContentsA1Dcx) {
michael@0 1528 SEC_ASN1DecoderFinish(nested->safeContentsA1Dcx);
michael@0 1529 nested->safeContentsA1Dcx = NULL;
michael@0 1530 }
michael@0 1531 nested = nested->nestedSafeContentsCtx;
michael@0 1532 }
michael@0 1533 if (safeContentsCtx->safeContentsA1Dcx) {
michael@0 1534 SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
michael@0 1535 safeContentsCtx->safeContentsA1Dcx = NULL;
michael@0 1536 }
michael@0 1537 }
michael@0 1538 }
michael@0 1539
michael@0 1540 if (p12dcx->currentASafeP7Dcx &&
michael@0 1541 p12dcx->currentASafeP7Dcx != p12dcx->aSafeP7Dcx) {
michael@0 1542 SEC_PKCS7ContentInfo * cinfo;
michael@0 1543 cinfo = SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
michael@0 1544 if (cinfo) {
michael@0 1545 SEC_PKCS7DestroyContentInfo(cinfo); /* don't leak it */
michael@0 1546 }
michael@0 1547 }
michael@0 1548 p12dcx->currentASafeP7Dcx = NULL;
michael@0 1549
michael@0 1550 if(p12dcx->aSafeP7Dcx) {
michael@0 1551 SEC_PKCS7ContentInfo * cinfo;
michael@0 1552 cinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
michael@0 1553 if (cinfo) {
michael@0 1554 SEC_PKCS7DestroyContentInfo(cinfo);
michael@0 1555 }
michael@0 1556 p12dcx->aSafeP7Dcx = NULL;
michael@0 1557 }
michael@0 1558
michael@0 1559 if(p12dcx->aSafeCinfo) {
michael@0 1560 SEC_PKCS7DestroyContentInfo(p12dcx->aSafeCinfo);
michael@0 1561 p12dcx->aSafeCinfo = NULL;
michael@0 1562 }
michael@0 1563
michael@0 1564 if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) {
michael@0 1565 SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE);
michael@0 1566 }
michael@0 1567 if (p12dcx->decitem.friendlyName != NULL) {
michael@0 1568 SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE);
michael@0 1569 }
michael@0 1570
michael@0 1571 if(p12dcx->slot) {
michael@0 1572 PK11_FreeSlot(p12dcx->slot);
michael@0 1573 p12dcx->slot = NULL;
michael@0 1574 }
michael@0 1575
michael@0 1576 if(p12dcx->dIsOpen && p12dcx->dClose) {
michael@0 1577 (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
michael@0 1578 p12dcx->dIsOpen = PR_FALSE;
michael@0 1579 }
michael@0 1580
michael@0 1581 if(p12dcx->arena) {
michael@0 1582 PORT_FreeArena(p12dcx->arena, PR_TRUE);
michael@0 1583 }
michael@0 1584 }
michael@0 1585
michael@0 1586 static SECStatus
michael@0 1587 sec_pkcs12_decoder_set_attribute_value(sec_PKCS12SafeBag *bag,
michael@0 1588 SECOidTag attributeType,
michael@0 1589 SECItem *attrValue)
michael@0 1590 {
michael@0 1591 int i = 0;
michael@0 1592 SECOidData *oid;
michael@0 1593
michael@0 1594 if(!bag || !attrValue) {
michael@0 1595 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1596 return SECFailure;
michael@0 1597 }
michael@0 1598
michael@0 1599 oid = SECOID_FindOIDByTag(attributeType);
michael@0 1600 if(!oid) {
michael@0 1601 return SECFailure;
michael@0 1602 }
michael@0 1603
michael@0 1604 if(!bag->attribs) {
michael@0 1605 bag->attribs =
michael@0 1606 PORT_ArenaZNewArray(bag->arena, sec_PKCS12Attribute *, 2);
michael@0 1607 } else {
michael@0 1608 while(bag->attribs[i])
michael@0 1609 i++;
michael@0 1610 bag->attribs = PORT_ArenaGrowArray(bag->arena, bag->attribs,
michael@0 1611 sec_PKCS12Attribute *, i + 1, i + 2);
michael@0 1612 }
michael@0 1613
michael@0 1614 if(!bag->attribs) {
michael@0 1615 return SECFailure;
michael@0 1616 }
michael@0 1617
michael@0 1618 bag->attribs[i] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
michael@0 1619 if(!bag->attribs) {
michael@0 1620 return SECFailure;
michael@0 1621 }
michael@0 1622
michael@0 1623 bag->attribs[i]->attrValue = PORT_ArenaZNewArray(bag->arena, SECItem *, 2);
michael@0 1624 if(!bag->attribs[i]->attrValue) {
michael@0 1625 return SECFailure;
michael@0 1626 }
michael@0 1627
michael@0 1628 bag->attribs[i+1] = NULL;
michael@0 1629 bag->attribs[i]->attrValue[0] = attrValue;
michael@0 1630 bag->attribs[i]->attrValue[1] = NULL;
michael@0 1631
michael@0 1632 return SECITEM_CopyItem(bag->arena, &bag->attribs[i]->attrType, &oid->oid);
michael@0 1633 }
michael@0 1634
michael@0 1635 static SECItem *
michael@0 1636 sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag,
michael@0 1637 SECOidTag attributeType)
michael@0 1638 {
michael@0 1639 int i;
michael@0 1640
michael@0 1641 if(!bag->attribs) {
michael@0 1642 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1643 return NULL;
michael@0 1644 }
michael@0 1645
michael@0 1646 for (i = 0; bag->attribs[i] != NULL; i++) {
michael@0 1647 if (SECOID_FindOIDTag(&bag->attribs[i]->attrType) == attributeType) {
michael@0 1648 return bag->attribs[i]->attrValue[0];
michael@0 1649 }
michael@0 1650 }
michael@0 1651 return NULL;
michael@0 1652 }
michael@0 1653
michael@0 1654 /* For now, this function will merely remove any ":"
michael@0 1655 * in the nickname which the PK11 functions may have
michael@0 1656 * placed there. This will keep dual certs from appearing
michael@0 1657 * twice under "Your" certificates when imported onto smart
michael@0 1658 * cards. Once with the name "Slot:Cert" and another with
michael@0 1659 * the nickname "Slot:Slot:Cert"
michael@0 1660 */
michael@0 1661 static void
michael@0 1662 sec_pkcs12_sanitize_nickname(PK11SlotInfo *slot, SECItem *nick)
michael@0 1663 {
michael@0 1664 char *nickname;
michael@0 1665 char *delimit;
michael@0 1666 int delimitlen;
michael@0 1667
michael@0 1668 nickname = (char*)nick->data;
michael@0 1669 if ((delimit = PORT_Strchr(nickname, ':')) != NULL) {
michael@0 1670 char *slotName;
michael@0 1671 int slotNameLen;
michael@0 1672
michael@0 1673 slotNameLen = delimit-nickname;
michael@0 1674 slotName = PORT_NewArray(char, (slotNameLen+1));
michael@0 1675 PORT_Assert(slotName);
michael@0 1676 if (slotName == NULL) {
michael@0 1677 /* What else can we do?*/
michael@0 1678 return;
michael@0 1679 }
michael@0 1680 PORT_Memcpy(slotName, nickname, slotNameLen);
michael@0 1681 slotName[slotNameLen] = '\0';
michael@0 1682 if (PORT_Strcmp(PK11_GetTokenName(slot), slotName) == 0) {
michael@0 1683 delimitlen = PORT_Strlen(delimit+1);
michael@0 1684 PORT_Memmove(nickname, delimit+1, delimitlen+1);
michael@0 1685 nick->len = delimitlen;
michael@0 1686 }
michael@0 1687 PORT_Free(slotName);
michael@0 1688 }
michael@0 1689
michael@0 1690 }
michael@0 1691
michael@0 1692 static SECItem *
michael@0 1693 sec_pkcs12_get_nickname(sec_PKCS12SafeBag *bag)
michael@0 1694 {
michael@0 1695 SECItem *src, *dest;
michael@0 1696
michael@0 1697 if(!bag) {
michael@0 1698 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1699 return NULL;
michael@0 1700 }
michael@0 1701
michael@0 1702 src = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
michael@0 1703
michael@0 1704 /* The return value src is 16-bit Unicode characters, in big-endian format.
michael@0 1705 * Check if it is NULL or empty name.
michael@0 1706 */
michael@0 1707 if(!src || !src->data || src->len < 2 || (!src->data[0] && !src->data[1])) {
michael@0 1708 return NULL;
michael@0 1709 }
michael@0 1710
michael@0 1711 dest = (SECItem*)PORT_ZAlloc(sizeof(SECItem));
michael@0 1712 if(!dest) {
michael@0 1713 goto loser;
michael@0 1714 }
michael@0 1715 if(!sec_pkcs12_convert_item_to_unicode(NULL, dest, src, PR_FALSE,
michael@0 1716 PR_FALSE, PR_FALSE)) {
michael@0 1717 goto loser;
michael@0 1718 }
michael@0 1719
michael@0 1720 sec_pkcs12_sanitize_nickname(bag->slot, dest);
michael@0 1721
michael@0 1722 return dest;
michael@0 1723
michael@0 1724 loser:
michael@0 1725 if(dest) {
michael@0 1726 SECITEM_ZfreeItem(dest, PR_TRUE);
michael@0 1727 }
michael@0 1728
michael@0 1729 bag->problem = PR_TRUE;
michael@0 1730 bag->error = PORT_GetError();
michael@0 1731 return NULL;
michael@0 1732 }
michael@0 1733
michael@0 1734 static SECStatus
michael@0 1735 sec_pkcs12_set_nickname(sec_PKCS12SafeBag *bag, SECItem *name)
michael@0 1736 {
michael@0 1737 sec_PKCS12Attribute *attr = NULL;
michael@0 1738 SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_FRIENDLY_NAME);
michael@0 1739
michael@0 1740 if(!bag || !bag->arena || !name) {
michael@0 1741 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1742 return SECFailure;
michael@0 1743 }
michael@0 1744
michael@0 1745 if(!bag->attribs) {
michael@0 1746 if(!oid) {
michael@0 1747 goto loser;
michael@0 1748 }
michael@0 1749
michael@0 1750 bag->attribs =
michael@0 1751 PORT_ArenaZNewArray(bag->arena, sec_PKCS12Attribute *, 2);
michael@0 1752 if(!bag->attribs) {
michael@0 1753 goto loser;
michael@0 1754 }
michael@0 1755 bag->attribs[0] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
michael@0 1756 if(!bag->attribs[0]) {
michael@0 1757 goto loser;
michael@0 1758 }
michael@0 1759 bag->attribs[1] = NULL;
michael@0 1760
michael@0 1761 attr = bag->attribs[0];
michael@0 1762 if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid)
michael@0 1763 != SECSuccess) {
michael@0 1764 goto loser;
michael@0 1765 }
michael@0 1766 } else {
michael@0 1767 int i;
michael@0 1768 for (i = 0; bag->attribs[i]; i++) {
michael@0 1769 if(SECOID_FindOIDTag(&bag->attribs[i]->attrType)
michael@0 1770 == SEC_OID_PKCS9_FRIENDLY_NAME) {
michael@0 1771 attr = bag->attribs[i];
michael@0 1772 break;
michael@0 1773 }
michael@0 1774 }
michael@0 1775 if(!attr) {
michael@0 1776 if(!oid) {
michael@0 1777 goto loser;
michael@0 1778 }
michael@0 1779 bag->attribs = PORT_ArenaGrowArray(bag->arena, bag->attribs,
michael@0 1780 sec_PKCS12Attribute *, i+1, i+2);
michael@0 1781 if(!bag->attribs) {
michael@0 1782 goto loser;
michael@0 1783 }
michael@0 1784 bag->attribs[i] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
michael@0 1785 if(!bag->attribs[i]) {
michael@0 1786 goto loser;
michael@0 1787 }
michael@0 1788 bag->attribs[i+1] = NULL;
michael@0 1789 attr = bag->attribs[i];
michael@0 1790 if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid)
michael@0 1791 != SECSuccess) {
michael@0 1792 goto loser;
michael@0 1793 }
michael@0 1794 }
michael@0 1795 }
michael@0 1796
michael@0 1797 PORT_Assert(attr);
michael@0 1798 if(!attr->attrValue) {
michael@0 1799 attr->attrValue = PORT_ArenaZNewArray(bag->arena, SECItem *, 2);
michael@0 1800 if(!attr->attrValue) {
michael@0 1801 goto loser;
michael@0 1802 }
michael@0 1803 attr->attrValue[0] = PORT_ArenaZNew(bag->arena, SECItem);
michael@0 1804 if(!attr->attrValue[0]) {
michael@0 1805 goto loser;
michael@0 1806 }
michael@0 1807 attr->attrValue[1] = NULL;
michael@0 1808 }
michael@0 1809
michael@0 1810 name->len = PORT_Strlen((char *)name->data);
michael@0 1811 if(!sec_pkcs12_convert_item_to_unicode(bag->arena, attr->attrValue[0],
michael@0 1812 name, PR_FALSE, PR_FALSE, PR_TRUE)) {
michael@0 1813 goto loser;
michael@0 1814 }
michael@0 1815
michael@0 1816 return SECSuccess;
michael@0 1817
michael@0 1818 loser:
michael@0 1819 bag->problem = PR_TRUE;
michael@0 1820 bag->error = PORT_GetError();
michael@0 1821 return SECFailure;
michael@0 1822 }
michael@0 1823
michael@0 1824 static SECStatus
michael@0 1825 sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key)
michael@0 1826 {
michael@0 1827 int i = 0;
michael@0 1828 SECKEYPrivateKeyInfo *pki = NULL;
michael@0 1829
michael@0 1830 if(!key) {
michael@0 1831 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1832 return SECFailure;
michael@0 1833 }
michael@0 1834
michael@0 1835 /* if the bag does *not* contain an unencrypted PrivateKeyInfo
michael@0 1836 * then we cannot convert the attributes. We are propagating
michael@0 1837 * attributes within the PrivateKeyInfo to the SafeBag level.
michael@0 1838 */
michael@0 1839 if(SECOID_FindOIDTag(&(key->safeBagType)) !=
michael@0 1840 SEC_OID_PKCS12_V1_KEY_BAG_ID) {
michael@0 1841 return SECSuccess;
michael@0 1842 }
michael@0 1843
michael@0 1844 pki = key->safeBagContent.pkcs8KeyBag;
michael@0 1845
michael@0 1846 if(!pki || !pki->attributes) {
michael@0 1847 return SECSuccess;
michael@0 1848 }
michael@0 1849
michael@0 1850 while(pki->attributes[i]) {
michael@0 1851 SECOidTag tag = SECOID_FindOIDTag(&pki->attributes[i]->attrType);
michael@0 1852
michael@0 1853 if (tag == SEC_OID_PKCS9_LOCAL_KEY_ID ||
michael@0 1854 tag == SEC_OID_PKCS9_FRIENDLY_NAME) {
michael@0 1855 SECItem *attrValue = sec_pkcs12_get_attribute_value(key, tag);
michael@0 1856 if(!attrValue) {
michael@0 1857 if(sec_pkcs12_decoder_set_attribute_value(key, tag,
michael@0 1858 pki->attributes[i]->attrValue[0])
michael@0 1859 != SECSuccess) {
michael@0 1860 key->problem = PR_TRUE;
michael@0 1861 key->error = PORT_GetError();
michael@0 1862 return SECFailure;
michael@0 1863 }
michael@0 1864 }
michael@0 1865 }
michael@0 1866 i++;
michael@0 1867 }
michael@0 1868
michael@0 1869 return SECSuccess;
michael@0 1870 }
michael@0 1871
michael@0 1872 /* retrieve the nickname for the certificate bag. first look
michael@0 1873 * in the cert bag, otherwise get it from the key.
michael@0 1874 */
michael@0 1875 static SECItem *
michael@0 1876 sec_pkcs12_get_nickname_for_cert(sec_PKCS12SafeBag *cert,
michael@0 1877 sec_PKCS12SafeBag *key)
michael@0 1878 {
michael@0 1879 SECItem *nickname;
michael@0 1880
michael@0 1881 if(!cert) {
michael@0 1882 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1883 return NULL;
michael@0 1884 }
michael@0 1885
michael@0 1886 nickname = sec_pkcs12_get_nickname(cert);
michael@0 1887 if(nickname) {
michael@0 1888 return nickname;
michael@0 1889 }
michael@0 1890
michael@0 1891 if(key) {
michael@0 1892 nickname = sec_pkcs12_get_nickname(key);
michael@0 1893
michael@0 1894 if(nickname && sec_pkcs12_set_nickname(cert, nickname)
michael@0 1895 != SECSuccess) {
michael@0 1896 SECITEM_ZfreeItem(nickname, PR_TRUE);
michael@0 1897 return NULL;
michael@0 1898 }
michael@0 1899 }
michael@0 1900
michael@0 1901 return nickname;
michael@0 1902 }
michael@0 1903
michael@0 1904 /* set the nickname for the certificate */
michael@0 1905 static SECStatus
michael@0 1906 sec_pkcs12_set_nickname_for_cert(sec_PKCS12SafeBag *cert,
michael@0 1907 sec_PKCS12SafeBag *key,
michael@0 1908 SECItem *nickname)
michael@0 1909 {
michael@0 1910 if(!nickname || !cert) {
michael@0 1911 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1912 return SECFailure;
michael@0 1913 }
michael@0 1914
michael@0 1915 if(sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) {
michael@0 1916 return SECFailure;
michael@0 1917 }
michael@0 1918
michael@0 1919 if(key) {
michael@0 1920 if(sec_pkcs12_set_nickname(key, nickname) != SECSuccess) {
michael@0 1921 cert->problem = PR_TRUE;
michael@0 1922 cert->error = key->error;
michael@0 1923 return SECFailure;
michael@0 1924 }
michael@0 1925 }
michael@0 1926
michael@0 1927 return SECSuccess;
michael@0 1928 }
michael@0 1929
michael@0 1930 /* retrieve the DER cert from the cert bag */
michael@0 1931 static SECItem *
michael@0 1932 sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert)
michael@0 1933 {
michael@0 1934 if(!cert) {
michael@0 1935 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1936 return NULL;
michael@0 1937 }
michael@0 1938
michael@0 1939 if(SECOID_FindOIDTag(&cert->safeBagType) != SEC_OID_PKCS12_V1_CERT_BAG_ID) {
michael@0 1940 return NULL;
michael@0 1941 }
michael@0 1942
michael@0 1943 /* only support X509 certs not SDSI */
michael@0 1944 if(SECOID_FindOIDTag(&cert->safeBagContent.certBag->bagID)
michael@0 1945 != SEC_OID_PKCS9_X509_CERT) {
michael@0 1946 return NULL;
michael@0 1947 }
michael@0 1948
michael@0 1949 return SECITEM_DupItem(&(cert->safeBagContent.certBag->value.x509Cert));
michael@0 1950 }
michael@0 1951
michael@0 1952 struct certNickInfo {
michael@0 1953 PLArenaPool *arena;
michael@0 1954 unsigned int nNicks;
michael@0 1955 SECItem **nickList;
michael@0 1956 unsigned int error;
michael@0 1957 };
michael@0 1958
michael@0 1959 /* callback for traversing certificates to gather the nicknames
michael@0 1960 * used in a particular traversal. for instance, when using
michael@0 1961 * CERT_TraversePermCertsForSubject, gather the nicknames and
michael@0 1962 * store them in the certNickInfo for a particular DN.
michael@0 1963 *
michael@0 1964 * this handles the case where multiple nicknames are allowed
michael@0 1965 * for the same dn, which is not currently allowed, but may be
michael@0 1966 * in the future.
michael@0 1967 */
michael@0 1968 static SECStatus
michael@0 1969 gatherNicknames(CERTCertificate *cert, void *arg)
michael@0 1970 {
michael@0 1971 struct certNickInfo *nickArg = (struct certNickInfo *)arg;
michael@0 1972 SECItem tempNick;
michael@0 1973 unsigned int i;
michael@0 1974
michael@0 1975 if(!cert || !nickArg || nickArg->error) {
michael@0 1976 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1977 return SECFailure;
michael@0 1978 }
michael@0 1979
michael@0 1980 if(!cert->nickname) {
michael@0 1981 return SECSuccess;
michael@0 1982 }
michael@0 1983
michael@0 1984 tempNick.data = (unsigned char *)cert->nickname;
michael@0 1985 tempNick.len = PORT_Strlen(cert->nickname) + 1;
michael@0 1986
michael@0 1987 /* do we already have the nickname in the list? */
michael@0 1988 if(nickArg->nNicks > 0) {
michael@0 1989
michael@0 1990 /* nicknames have been encountered, but there is no list -- bad */
michael@0 1991 if(!nickArg->nickList) {
michael@0 1992 nickArg->error = SEC_ERROR_INVALID_ARGS;
michael@0 1993 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1994 return SECFailure;
michael@0 1995 }
michael@0 1996
michael@0 1997 for(i = 0; i < nickArg->nNicks; i++) {
michael@0 1998 if(SECITEM_CompareItem(nickArg->nickList[i], &tempNick)
michael@0 1999 == SECEqual) {
michael@0 2000 return SECSuccess;
michael@0 2001 }
michael@0 2002 }
michael@0 2003 }
michael@0 2004
michael@0 2005 /* add the nickname to the list */
michael@0 2006 nickArg->nickList = (nickArg->nNicks == 0)
michael@0 2007 ? PORT_ArenaZNewArray(nickArg->arena, SECItem *, 2)
michael@0 2008 : PORT_ArenaGrowArray(nickArg->arena, nickArg->nickList, SECItem *,
michael@0 2009 nickArg->nNicks + 1, nickArg->nNicks + 2);
michael@0 2010
michael@0 2011 if(!nickArg->nickList) {
michael@0 2012 nickArg->error = SEC_ERROR_NO_MEMORY;
michael@0 2013 return SECFailure;
michael@0 2014 }
michael@0 2015
michael@0 2016 nickArg->nickList[nickArg->nNicks] =
michael@0 2017 PORT_ArenaZNew(nickArg->arena, SECItem);
michael@0 2018 if(!nickArg->nickList[nickArg->nNicks]) {
michael@0 2019 nickArg->error = PORT_GetError();
michael@0 2020 return SECFailure;
michael@0 2021 }
michael@0 2022
michael@0 2023
michael@0 2024 if(SECITEM_CopyItem(nickArg->arena, nickArg->nickList[nickArg->nNicks],
michael@0 2025 &tempNick) != SECSuccess) {
michael@0 2026 nickArg->error = PORT_GetError();
michael@0 2027 return SECFailure;
michael@0 2028 }
michael@0 2029
michael@0 2030 nickArg->nNicks++;
michael@0 2031
michael@0 2032 return SECSuccess;
michael@0 2033 }
michael@0 2034
michael@0 2035 /* traverses the certs in the data base or in the token for the
michael@0 2036 * DN to see if any certs currently have a nickname set.
michael@0 2037 * If so, return it.
michael@0 2038 */
michael@0 2039 static SECItem *
michael@0 2040 sec_pkcs12_get_existing_nick_for_dn(sec_PKCS12SafeBag *cert)
michael@0 2041 {
michael@0 2042 struct certNickInfo *nickArg = NULL;
michael@0 2043 SECItem *derCert, *returnDn = NULL;
michael@0 2044 PLArenaPool *arena = NULL;
michael@0 2045 CERTCertificate *tempCert;
michael@0 2046
michael@0 2047 if(!cert) {
michael@0 2048 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2049 return NULL;
michael@0 2050 }
michael@0 2051
michael@0 2052 derCert = sec_pkcs12_get_der_cert(cert);
michael@0 2053 if(!derCert) {
michael@0 2054 return NULL;
michael@0 2055 }
michael@0 2056
michael@0 2057 tempCert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
michael@0 2058 if(!tempCert) {
michael@0 2059 returnDn = NULL;
michael@0 2060 goto loser;
michael@0 2061 }
michael@0 2062
michael@0 2063 arena = PORT_NewArena(1024);
michael@0 2064 if(!arena) {
michael@0 2065 returnDn = NULL;
michael@0 2066 goto loser;
michael@0 2067 }
michael@0 2068 nickArg = PORT_ArenaZNew(arena, struct certNickInfo);
michael@0 2069 if(!nickArg) {
michael@0 2070 returnDn = NULL;
michael@0 2071 goto loser;
michael@0 2072 }
michael@0 2073 nickArg->error = 0;
michael@0 2074 nickArg->nNicks = 0;
michael@0 2075 nickArg->nickList = NULL;
michael@0 2076 nickArg->arena = arena;
michael@0 2077
michael@0 2078 /* if the token is local, first traverse the cert database
michael@0 2079 * then traverse the token.
michael@0 2080 */
michael@0 2081 if(PK11_TraverseCertsForSubjectInSlot(tempCert, cert->slot, gatherNicknames,
michael@0 2082 (void *)nickArg) != SECSuccess) {
michael@0 2083 returnDn = NULL;
michael@0 2084 goto loser;
michael@0 2085 }
michael@0 2086
michael@0 2087 if(nickArg->error) {
michael@0 2088 /* XXX do we want to set the error? */
michael@0 2089 returnDn = NULL;
michael@0 2090 goto loser;
michael@0 2091 }
michael@0 2092
michael@0 2093 if(nickArg->nNicks == 0) {
michael@0 2094 returnDn = NULL;
michael@0 2095 goto loser;
michael@0 2096 }
michael@0 2097
michael@0 2098 /* set it to the first name, for now. handle multiple names? */
michael@0 2099 returnDn = SECITEM_DupItem(nickArg->nickList[0]);
michael@0 2100
michael@0 2101 loser:
michael@0 2102 if(arena) {
michael@0 2103 PORT_FreeArena(arena, PR_TRUE);
michael@0 2104 }
michael@0 2105
michael@0 2106 if(tempCert) {
michael@0 2107 CERT_DestroyCertificate(tempCert);
michael@0 2108 }
michael@0 2109
michael@0 2110 if(derCert) {
michael@0 2111 SECITEM_FreeItem(derCert, PR_TRUE);
michael@0 2112 }
michael@0 2113
michael@0 2114 return (returnDn);
michael@0 2115 }
michael@0 2116
michael@0 2117 /* counts certificates found for a given traversal function */
michael@0 2118 static SECStatus
michael@0 2119 countCertificate(CERTCertificate *cert, void *arg)
michael@0 2120 {
michael@0 2121 unsigned int *nCerts = (unsigned int *)arg;
michael@0 2122
michael@0 2123 if(!cert || !arg) {
michael@0 2124 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2125 return SECFailure;
michael@0 2126 }
michael@0 2127
michael@0 2128 (*nCerts)++;
michael@0 2129 return SECSuccess;
michael@0 2130 }
michael@0 2131
michael@0 2132 static PRBool
michael@0 2133 sec_pkcs12_certs_for_nickname_exist(SECItem *nickname, PK11SlotInfo *slot)
michael@0 2134 {
michael@0 2135 unsigned int nCerts = 0;
michael@0 2136
michael@0 2137 if(!nickname || !slot) {
michael@0 2138 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2139 return PR_TRUE;
michael@0 2140 }
michael@0 2141
michael@0 2142 /* we want to check the local database first if we are importing to it */
michael@0 2143 PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate,
michael@0 2144 (void *)&nCerts);
michael@0 2145 return (PRBool)(nCerts != 0);
michael@0 2146 }
michael@0 2147
michael@0 2148 /* validate cert nickname such that there is a one-to-one relation
michael@0 2149 * between nicknames and dn's. we want to enforce the case that the
michael@0 2150 * nickname is non-NULL and that there is only one nickname per DN.
michael@0 2151 *
michael@0 2152 * if there is a problem with a nickname or the nickname is not present,
michael@0 2153 * the user will be prompted for it.
michael@0 2154 */
michael@0 2155 static void
michael@0 2156 sec_pkcs12_validate_cert_nickname(sec_PKCS12SafeBag *cert,
michael@0 2157 sec_PKCS12SafeBag *key,
michael@0 2158 SEC_PKCS12NicknameCollisionCallback nicknameCb,
michael@0 2159 CERTCertificate *leafCert)
michael@0 2160 {
michael@0 2161 SECItem *certNickname, *existingDNNick;
michael@0 2162 PRBool setNickname = PR_FALSE, cancel = PR_FALSE;
michael@0 2163 SECItem *newNickname = NULL;
michael@0 2164
michael@0 2165 if(!cert || !cert->hasKey) {
michael@0 2166 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2167 return;
michael@0 2168 }
michael@0 2169
michael@0 2170 if(!nicknameCb) {
michael@0 2171 cert->problem = PR_TRUE;
michael@0 2172 cert->error = SEC_ERROR_INVALID_ARGS;
michael@0 2173 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2174 return;
michael@0 2175 }
michael@0 2176
michael@0 2177 if(cert->hasKey && !key) {
michael@0 2178 cert->problem = PR_TRUE;
michael@0 2179 cert->error = SEC_ERROR_INVALID_ARGS;
michael@0 2180 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2181 return;
michael@0 2182 }
michael@0 2183
michael@0 2184 certNickname = sec_pkcs12_get_nickname_for_cert(cert, key);
michael@0 2185 existingDNNick = sec_pkcs12_get_existing_nick_for_dn(cert);
michael@0 2186
michael@0 2187 /* nickname is already used w/ this dn, so it is safe to return */
michael@0 2188 if(certNickname && existingDNNick &&
michael@0 2189 SECITEM_CompareItem(certNickname, existingDNNick) == SECEqual) {
michael@0 2190 goto loser;
michael@0 2191 }
michael@0 2192
michael@0 2193 /* nickname not set in pkcs 12 bags, but a nick is already used for
michael@0 2194 * this dn. set the nicks in the p12 bags and finish.
michael@0 2195 */
michael@0 2196 if(existingDNNick) {
michael@0 2197 sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick);
michael@0 2198 goto loser;
michael@0 2199 }
michael@0 2200
michael@0 2201 /* at this point, we have a certificate for which the DN is not located
michael@0 2202 * on the token. the nickname specified may or may not be NULL. if it
michael@0 2203 * is not null, we need to make sure that there are no other certificates
michael@0 2204 * with this nickname in the token for it to be valid. this imposes a
michael@0 2205 * one to one relationship between DN and nickname.
michael@0 2206 *
michael@0 2207 * if the nickname is null, we need the user to enter a nickname for
michael@0 2208 * the certificate.
michael@0 2209 *
michael@0 2210 * once we have a nickname, we make sure that the nickname is unique
michael@0 2211 * for the DN. if it is not, the user is reprompted to enter a new
michael@0 2212 * nickname.
michael@0 2213 *
michael@0 2214 * in order to exit this loop, the nickname entered is either unique
michael@0 2215 * or the user hits cancel and the certificate is not imported.
michael@0 2216 */
michael@0 2217 setNickname = PR_FALSE;
michael@0 2218 while(1) {
michael@0 2219 /* we will use the nickname so long as no other certs have the
michael@0 2220 * same nickname. and the nickname is not NULL.
michael@0 2221 */
michael@0 2222 if (certNickname && certNickname->data &&
michael@0 2223 !sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) {
michael@0 2224 if (setNickname) {
michael@0 2225 sec_pkcs12_set_nickname_for_cert(cert, key, certNickname);
michael@0 2226 }
michael@0 2227 break;
michael@0 2228 }
michael@0 2229
michael@0 2230 setNickname = PR_FALSE;
michael@0 2231 newNickname = (*nicknameCb)(certNickname, &cancel, leafCert);
michael@0 2232 if(cancel) {
michael@0 2233 cert->problem = PR_TRUE;
michael@0 2234 cert->error = SEC_ERROR_USER_CANCELLED;
michael@0 2235 break;
michael@0 2236 }
michael@0 2237
michael@0 2238 if(!newNickname) {
michael@0 2239 cert->problem = PR_TRUE;
michael@0 2240 cert->error = PORT_GetError();
michael@0 2241 break;
michael@0 2242 }
michael@0 2243
michael@0 2244 /* at this point we have a new nickname, if we have an existing
michael@0 2245 * certNickname, we need to free it and assign the new nickname
michael@0 2246 * to it to avoid a memory leak. happy?
michael@0 2247 */
michael@0 2248 if(certNickname) {
michael@0 2249 SECITEM_ZfreeItem(certNickname, PR_TRUE);
michael@0 2250 certNickname = NULL;
michael@0 2251 }
michael@0 2252
michael@0 2253 certNickname = newNickname;
michael@0 2254 setNickname = PR_TRUE;
michael@0 2255 /* go back and recheck the new nickname */
michael@0 2256 }
michael@0 2257
michael@0 2258 loser:
michael@0 2259 if(certNickname) {
michael@0 2260 SECITEM_ZfreeItem(certNickname, PR_TRUE);
michael@0 2261 }
michael@0 2262
michael@0 2263 if(existingDNNick) {
michael@0 2264 SECITEM_ZfreeItem(existingDNNick, PR_TRUE);
michael@0 2265 }
michael@0 2266 }
michael@0 2267
michael@0 2268 static void
michael@0 2269 sec_pkcs12_validate_cert(sec_PKCS12SafeBag *cert,
michael@0 2270 sec_PKCS12SafeBag *key,
michael@0 2271 SEC_PKCS12NicknameCollisionCallback nicknameCb)
michael@0 2272 {
michael@0 2273 CERTCertificate *leafCert;
michael@0 2274
michael@0 2275 if(!cert) {
michael@0 2276 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2277 return;
michael@0 2278 }
michael@0 2279
michael@0 2280 cert->validated = PR_TRUE;
michael@0 2281
michael@0 2282 if(!nicknameCb) {
michael@0 2283 cert->noInstall = PR_TRUE;
michael@0 2284 cert->problem = PR_TRUE;
michael@0 2285 cert->error = SEC_ERROR_INVALID_ARGS;
michael@0 2286 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2287 return;
michael@0 2288 }
michael@0 2289
michael@0 2290 if(!cert->safeBagContent.certBag) {
michael@0 2291 cert->noInstall = PR_TRUE;
michael@0 2292 cert->problem = PR_TRUE;
michael@0 2293 cert->error = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
michael@0 2294 return;
michael@0 2295 }
michael@0 2296
michael@0 2297 cert->noInstall = PR_FALSE;
michael@0 2298 cert->unused = PR_FALSE;
michael@0 2299 cert->problem = PR_FALSE;
michael@0 2300 cert->error = 0;
michael@0 2301
michael@0 2302 leafCert = CERT_DecodeDERCertificate(
michael@0 2303 &cert->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL);
michael@0 2304 if(!leafCert) {
michael@0 2305 cert->noInstall = PR_TRUE;
michael@0 2306 cert->problem = PR_TRUE;
michael@0 2307 cert->error = PORT_GetError();
michael@0 2308 return;
michael@0 2309 }
michael@0 2310
michael@0 2311 sec_pkcs12_validate_cert_nickname(cert, key, nicknameCb, leafCert);
michael@0 2312
michael@0 2313 CERT_DestroyCertificate(leafCert);
michael@0 2314 }
michael@0 2315
michael@0 2316 static void
michael@0 2317 sec_pkcs12_validate_key_by_cert(sec_PKCS12SafeBag *cert, sec_PKCS12SafeBag *key,
michael@0 2318 void *wincx)
michael@0 2319 {
michael@0 2320 CERTCertificate *leafCert;
michael@0 2321 SECKEYPrivateKey *privk;
michael@0 2322
michael@0 2323 if(!key) {
michael@0 2324 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2325 return;
michael@0 2326 }
michael@0 2327
michael@0 2328 key->validated = PR_TRUE;
michael@0 2329
michael@0 2330 if(!cert) {
michael@0 2331 key->problem = PR_TRUE;
michael@0 2332 key->noInstall = PR_TRUE;
michael@0 2333 key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
michael@0 2334 return;
michael@0 2335 }
michael@0 2336
michael@0 2337 leafCert = CERT_DecodeDERCertificate(
michael@0 2338 &(cert->safeBagContent.certBag->value.x509Cert), PR_FALSE, NULL);
michael@0 2339 if(!leafCert) {
michael@0 2340 key->problem = PR_TRUE;
michael@0 2341 key->noInstall = PR_TRUE;
michael@0 2342 key->error = PORT_GetError();
michael@0 2343 return;
michael@0 2344 }
michael@0 2345
michael@0 2346 privk = PK11_FindPrivateKeyFromCert(key->slot, leafCert, wincx);
michael@0 2347 if(!privk) {
michael@0 2348 privk = PK11_FindKeyByDERCert(key->slot, leafCert, wincx);
michael@0 2349 }
michael@0 2350
michael@0 2351 if(privk) {
michael@0 2352 SECKEY_DestroyPrivateKey(privk);
michael@0 2353 key->noInstall = PR_TRUE;
michael@0 2354 }
michael@0 2355
michael@0 2356 CERT_DestroyCertificate(leafCert);
michael@0 2357 }
michael@0 2358
michael@0 2359 static SECStatus
michael@0 2360 sec_pkcs12_add_cert(sec_PKCS12SafeBag *cert, PRBool keyExists, void *wincx)
michael@0 2361 {
michael@0 2362 SECItem *derCert, *nickName;
michael@0 2363 char *nickData = NULL;
michael@0 2364 PRBool isIntermediateCA;
michael@0 2365 SECStatus rv;
michael@0 2366
michael@0 2367 if(!cert) {
michael@0 2368 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2369 return SECFailure;
michael@0 2370 }
michael@0 2371
michael@0 2372 if(cert->problem || cert->noInstall || cert->installed) {
michael@0 2373 return SECSuccess;
michael@0 2374 }
michael@0 2375
michael@0 2376 derCert = &cert->safeBagContent.certBag->value.x509Cert;
michael@0 2377
michael@0 2378 PORT_Assert(!cert->problem && !cert->noInstall);
michael@0 2379
michael@0 2380 nickName = sec_pkcs12_get_nickname(cert);
michael@0 2381 if(nickName) {
michael@0 2382 nickData = (char *)nickName->data;
michael@0 2383 }
michael@0 2384
michael@0 2385 isIntermediateCA = CERT_IsCADERCert(derCert, NULL) &&
michael@0 2386 !CERT_IsRootDERCert(derCert);
michael@0 2387
michael@0 2388 if(keyExists) {
michael@0 2389 CERTCertificate *newCert;
michael@0 2390
michael@0 2391 newCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
michael@0 2392 derCert, NULL, PR_FALSE, PR_FALSE);
michael@0 2393 if(!newCert) {
michael@0 2394 if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE);
michael@0 2395 cert->error = PORT_GetError();
michael@0 2396 cert->problem = PR_TRUE;
michael@0 2397 return SECFailure;
michael@0 2398 }
michael@0 2399
michael@0 2400 rv = PK11_ImportCertForKeyToSlot(cert->slot, newCert, nickData,
michael@0 2401 PR_TRUE, wincx);
michael@0 2402 CERT_DestroyCertificate(newCert);
michael@0 2403 } else if ((cert->tokenCAs == SECPKCS12TargetTokenNoCAs) ||
michael@0 2404 ((cert->tokenCAs == SECPKCS12TargetTokenIntermediateCAs) &&
michael@0 2405 !isIntermediateCA)) {
michael@0 2406 SECItem *certList[2];
michael@0 2407 certList[0] = derCert;
michael@0 2408 certList[1] = NULL;
michael@0 2409
michael@0 2410 rv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageUserCertImport,
michael@0 2411 1, certList, NULL, PR_TRUE, PR_FALSE, nickData);
michael@0 2412 } else {
michael@0 2413 rv = PK11_ImportDERCert(cert->slot, derCert, CK_INVALID_HANDLE,
michael@0 2414 nickData, PR_FALSE);
michael@0 2415 }
michael@0 2416 if (rv) {
michael@0 2417 cert->problem = 1;
michael@0 2418 cert->error = PORT_GetError();
michael@0 2419 }
michael@0 2420 cert->installed = PR_TRUE;
michael@0 2421 if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE);
michael@0 2422 return rv;
michael@0 2423 }
michael@0 2424
michael@0 2425 static SECItem *
michael@0 2426 sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey, KeyType *type);
michael@0 2427
michael@0 2428 static SECStatus
michael@0 2429 sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECKEYPublicKey *pubKey,
michael@0 2430 unsigned int keyUsage,
michael@0 2431 SECItem *nickName, void *wincx)
michael@0 2432 {
michael@0 2433 SECStatus rv;
michael@0 2434 SECItem *publicValue = NULL;
michael@0 2435 KeyType keyType;
michael@0 2436
michael@0 2437 /* We should always have values for "key" and "pubKey"
michael@0 2438 so they can be dereferenced later. */
michael@0 2439 if(!key || !pubKey) {
michael@0 2440 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2441 return SECFailure;
michael@0 2442 }
michael@0 2443
michael@0 2444 if(key->problem || key->noInstall) {
michael@0 2445 return SECSuccess;
michael@0 2446 }
michael@0 2447
michael@0 2448 /* get the value and type from the public key */
michael@0 2449 publicValue = sec_pkcs12_get_public_value_and_type(pubKey, &keyType);
michael@0 2450 if (!publicValue) {
michael@0 2451 key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
michael@0 2452 key->problem = PR_TRUE;
michael@0 2453 return SECFailure;
michael@0 2454 }
michael@0 2455
michael@0 2456 switch(SECOID_FindOIDTag(&key->safeBagType))
michael@0 2457 {
michael@0 2458 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
michael@0 2459 rv = PK11_ImportPrivateKeyInfo(key->slot,
michael@0 2460 key->safeBagContent.pkcs8KeyBag,
michael@0 2461 nickName, publicValue, PR_TRUE, PR_TRUE,
michael@0 2462 keyUsage, wincx);
michael@0 2463 break;
michael@0 2464 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
michael@0 2465 rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot,
michael@0 2466 key->safeBagContent.pkcs8ShroudedKeyBag,
michael@0 2467 key->pwitem, nickName, publicValue,
michael@0 2468 PR_TRUE, PR_TRUE, keyType, keyUsage,
michael@0 2469 wincx);
michael@0 2470 break;
michael@0 2471 default:
michael@0 2472 key->error = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
michael@0 2473 key->problem = PR_TRUE;
michael@0 2474 if(nickName) {
michael@0 2475 SECITEM_ZfreeItem(nickName, PR_TRUE);
michael@0 2476 }
michael@0 2477 return SECFailure;
michael@0 2478 }
michael@0 2479
michael@0 2480 if(rv != SECSuccess) {
michael@0 2481 key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
michael@0 2482 key->problem = PR_TRUE;
michael@0 2483 } else {
michael@0 2484 /* try to import the public key. Failure to do so is not fatal,
michael@0 2485 * not all tokens can store the public key */
michael@0 2486 if (pubKey) {
michael@0 2487 PK11_ImportPublicKey(key->slot, pubKey, PR_TRUE);
michael@0 2488 }
michael@0 2489 key->installed = PR_TRUE;
michael@0 2490 }
michael@0 2491
michael@0 2492 return rv;
michael@0 2493 }
michael@0 2494
michael@0 2495 /*
michael@0 2496 * The correctness of the code in this file ABSOLUTELY REQUIRES
michael@0 2497 * that ALL BAGs share a single common arena.
michael@0 2498 *
michael@0 2499 * This function allocates the bag list from the arena of whatever bag
michael@0 2500 * happens to be passed to it. Each time a new bag is handed to it,
michael@0 2501 * it grows (resizes) the arena of the bag that was handed to it.
michael@0 2502 * If the bags have different arenas, it will grow the wrong arena.
michael@0 2503 *
michael@0 2504 * Worse, if the bags had separate arenas, then while destroying the bags
michael@0 2505 * in a bag list, when the bag whose arena contained the bag list was
michael@0 2506 * destroyed, the baglist itself would be destroyed, making it difficult
michael@0 2507 * or impossible to continue to destroy the bags in the destroyed list.
michael@0 2508 */
michael@0 2509 static SECStatus
michael@0 2510 sec_pkcs12_add_item_to_bag_list(sec_PKCS12SafeBag ***bagList,
michael@0 2511 sec_PKCS12SafeBag *bag)
michael@0 2512 {
michael@0 2513 sec_PKCS12SafeBag **newBagList = NULL;
michael@0 2514 int i = 0;
michael@0 2515
michael@0 2516 if(!bagList || !bag) {
michael@0 2517 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2518 return SECFailure;
michael@0 2519 }
michael@0 2520
michael@0 2521 if(!(*bagList)) {
michael@0 2522 newBagList = PORT_ArenaZNewArray(bag->arena, sec_PKCS12SafeBag *, 2);
michael@0 2523 } else {
michael@0 2524 while((*bagList)[i])
michael@0 2525 i++;
michael@0 2526 newBagList = PORT_ArenaGrowArray(bag->arena, *bagList,
michael@0 2527 sec_PKCS12SafeBag *, i + 1, i + 2);
michael@0 2528 }
michael@0 2529
michael@0 2530 if(!newBagList) {
michael@0 2531 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 2532 return SECFailure;
michael@0 2533 }
michael@0 2534
michael@0 2535 newBagList[i] = bag;
michael@0 2536 newBagList[i+1] = NULL;
michael@0 2537 *bagList = newBagList;
michael@0 2538
michael@0 2539 return SECSuccess;
michael@0 2540 }
michael@0 2541
michael@0 2542 static sec_PKCS12SafeBag **
michael@0 2543 sec_pkcs12_find_certs_for_key(sec_PKCS12SafeBag **safeBags,
michael@0 2544 sec_PKCS12SafeBag *key )
michael@0 2545 {
michael@0 2546 sec_PKCS12SafeBag **certList = NULL;
michael@0 2547 SECItem *keyId;
michael@0 2548 int i;
michael@0 2549
michael@0 2550 if(!safeBags || !safeBags[0]) {
michael@0 2551 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2552 return NULL;
michael@0 2553 }
michael@0 2554
michael@0 2555 keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID);
michael@0 2556 if(!keyId) {
michael@0 2557 return NULL;
michael@0 2558 }
michael@0 2559
michael@0 2560 for (i = 0; safeBags[i]; i++) {
michael@0 2561 if(SECOID_FindOIDTag(&(safeBags[i]->safeBagType))
michael@0 2562 == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
michael@0 2563 SECItem *certKeyId = sec_pkcs12_get_attribute_value(safeBags[i],
michael@0 2564 SEC_OID_PKCS9_LOCAL_KEY_ID);
michael@0 2565
michael@0 2566 if(certKeyId && (SECITEM_CompareItem(certKeyId, keyId)
michael@0 2567 == SECEqual)) {
michael@0 2568 if(sec_pkcs12_add_item_to_bag_list(&certList, safeBags[i])
michael@0 2569 != SECSuccess) {
michael@0 2570 /* This would leak the partial list of safeBags,
michael@0 2571 * but that list is allocated from the arena of
michael@0 2572 * one of the safebags, and will be destroyed when
michael@0 2573 * that arena is destroyed. So this is not a real leak.
michael@0 2574 */
michael@0 2575 return NULL;
michael@0 2576 }
michael@0 2577 }
michael@0 2578 }
michael@0 2579 }
michael@0 2580
michael@0 2581 return certList;
michael@0 2582 }
michael@0 2583
michael@0 2584 CERTCertList *
michael@0 2585 SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx)
michael@0 2586 {
michael@0 2587 CERTCertList *certList = NULL;
michael@0 2588 sec_PKCS12SafeBag **safeBags;
michael@0 2589 int i;
michael@0 2590
michael@0 2591 if (!p12dcx || !p12dcx->safeBags || !p12dcx->safeBags[0]) {
michael@0 2592 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2593 return NULL;
michael@0 2594 }
michael@0 2595
michael@0 2596 safeBags = p12dcx->safeBags;
michael@0 2597 certList = CERT_NewCertList();
michael@0 2598
michael@0 2599 if (certList == NULL) {
michael@0 2600 return NULL;
michael@0 2601 }
michael@0 2602
michael@0 2603 for (i = 0; safeBags[i]; i++) {
michael@0 2604 if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType))
michael@0 2605 == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
michael@0 2606 SECItem *derCert = sec_pkcs12_get_der_cert(safeBags[i]) ;
michael@0 2607 CERTCertificate *tempCert = NULL;
michael@0 2608
michael@0 2609 if (derCert == NULL)
michael@0 2610 continue;
michael@0 2611 tempCert=CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
michael@0 2612 derCert, NULL,
michael@0 2613 PR_FALSE, PR_TRUE);
michael@0 2614
michael@0 2615 if (tempCert) {
michael@0 2616 CERT_AddCertToListTail(certList,tempCert);
michael@0 2617 }
michael@0 2618 SECITEM_FreeItem(derCert,PR_TRUE);
michael@0 2619 }
michael@0 2620 /* fixed an infinite loop here, by ensuring that i gets incremented
michael@0 2621 * if derCert is NULL above.
michael@0 2622 */
michael@0 2623 }
michael@0 2624
michael@0 2625 return certList;
michael@0 2626 }
michael@0 2627 static sec_PKCS12SafeBag **
michael@0 2628 sec_pkcs12_get_key_bags(sec_PKCS12SafeBag **safeBags)
michael@0 2629 {
michael@0 2630 int i;
michael@0 2631 sec_PKCS12SafeBag **keyList = NULL;
michael@0 2632 SECOidTag bagType;
michael@0 2633
michael@0 2634 if(!safeBags || !safeBags[0]) {
michael@0 2635 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2636 return NULL;
michael@0 2637 }
michael@0 2638
michael@0 2639 for (i = 0; safeBags[i]; i++) {
michael@0 2640 bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType));
michael@0 2641 switch(bagType) {
michael@0 2642 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
michael@0 2643 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
michael@0 2644 if(sec_pkcs12_add_item_to_bag_list(&keyList, safeBags[i])
michael@0 2645 != SECSuccess) {
michael@0 2646 /* This would leak, except that keyList is allocated
michael@0 2647 * from the arena shared by all the safeBags.
michael@0 2648 */
michael@0 2649 return NULL;
michael@0 2650 }
michael@0 2651 break;
michael@0 2652 default:
michael@0 2653 break;
michael@0 2654 }
michael@0 2655 }
michael@0 2656
michael@0 2657 return keyList;
michael@0 2658 }
michael@0 2659
michael@0 2660 /* This function takes two passes over the bags, validating them
michael@0 2661 * The two passes are intended to mirror exactly the two passes in
michael@0 2662 * sec_pkcs12_install_bags. But they don't. :(
michael@0 2663 */
michael@0 2664 static SECStatus
michael@0 2665 sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags,
michael@0 2666 SEC_PKCS12NicknameCollisionCallback nicknameCb,
michael@0 2667 void *wincx)
michael@0 2668 {
michael@0 2669 sec_PKCS12SafeBag **keyList;
michael@0 2670 int i;
michael@0 2671
michael@0 2672 if(!safeBags || !nicknameCb) {
michael@0 2673 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2674 return SECFailure;
michael@0 2675 }
michael@0 2676
michael@0 2677 if(!safeBags[0]) {
michael@0 2678 return SECSuccess;
michael@0 2679 }
michael@0 2680
michael@0 2681 /* First pass. Find all the key bags.
michael@0 2682 * Find the matching cert(s) for each key.
michael@0 2683 */
michael@0 2684 keyList = sec_pkcs12_get_key_bags(safeBags);
michael@0 2685 if(keyList) {
michael@0 2686 for (i = 0; keyList[i]; ++i) {
michael@0 2687 sec_PKCS12SafeBag *key = keyList[i];
michael@0 2688 sec_PKCS12SafeBag **certList =
michael@0 2689 sec_pkcs12_find_certs_for_key(safeBags, key);
michael@0 2690
michael@0 2691 if(certList) {
michael@0 2692 int j;
michael@0 2693
michael@0 2694 if(SECOID_FindOIDTag(&(key->safeBagType)) ==
michael@0 2695 SEC_OID_PKCS12_V1_KEY_BAG_ID) {
michael@0 2696 /* if it is an unencrypted private key then make sure
michael@0 2697 * the attributes are propageted to the appropriate
michael@0 2698 * level
michael@0 2699 */
michael@0 2700 if(sec_pkcs12_get_key_info(key) != SECSuccess) {
michael@0 2701 return SECFailure;
michael@0 2702 }
michael@0 2703 }
michael@0 2704
michael@0 2705 sec_pkcs12_validate_key_by_cert(certList[0], key, wincx);
michael@0 2706 for (j = 0; certList[j]; ++j) {
michael@0 2707 sec_PKCS12SafeBag *cert = certList[j];
michael@0 2708 cert->hasKey = PR_TRUE;
michael@0 2709 if(key->problem) {
michael@0 2710 cert->problem = PR_TRUE;
michael@0 2711 cert->error = key->error;
michael@0 2712 continue;
michael@0 2713 }
michael@0 2714 sec_pkcs12_validate_cert(cert, key, nicknameCb);
michael@0 2715 if(cert->problem) {
michael@0 2716 key->problem = cert->problem;
michael@0 2717 key->error = cert->error;
michael@0 2718 }
michael@0 2719 }
michael@0 2720 }
michael@0 2721 }
michael@0 2722 }
michael@0 2723
michael@0 2724 /* Now take a second pass over the safebags and mark for installation any
michael@0 2725 * certs that were neither installed nor disqualified by the first pass.
michael@0 2726 */
michael@0 2727 for (i = 0; safeBags[i]; ++i) {
michael@0 2728 sec_PKCS12SafeBag *bag = safeBags[i];
michael@0 2729
michael@0 2730 if(!bag->validated) {
michael@0 2731 SECOidTag bagType = SECOID_FindOIDTag(&bag->safeBagType);
michael@0 2732
michael@0 2733 switch(bagType) {
michael@0 2734 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
michael@0 2735 sec_pkcs12_validate_cert(bag, NULL, nicknameCb);
michael@0 2736 break;
michael@0 2737 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
michael@0 2738 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
michael@0 2739 bag->noInstall = PR_TRUE;
michael@0 2740 bag->problem = PR_TRUE;
michael@0 2741 bag->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
michael@0 2742 break;
michael@0 2743 default:
michael@0 2744 bag->noInstall = PR_TRUE;
michael@0 2745 }
michael@0 2746 }
michael@0 2747 }
michael@0 2748
michael@0 2749 return SECSuccess;
michael@0 2750 }
michael@0 2751
michael@0 2752 SECStatus
michael@0 2753 SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx,
michael@0 2754 SEC_PKCS12NicknameCollisionCallback nicknameCb)
michael@0 2755 {
michael@0 2756 SECStatus rv;
michael@0 2757 int i, noInstallCnt, probCnt, bagCnt, errorVal = 0;
michael@0 2758 if(!p12dcx || p12dcx->error || !p12dcx->safeBags) {
michael@0 2759 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2760 return SECFailure;
michael@0 2761 }
michael@0 2762
michael@0 2763 rv = sec_pkcs12_validate_bags(p12dcx->safeBags, nicknameCb, p12dcx->wincx);
michael@0 2764 if(rv == SECSuccess) {
michael@0 2765 p12dcx->bagsVerified = PR_TRUE;
michael@0 2766 }
michael@0 2767
michael@0 2768 noInstallCnt = probCnt = bagCnt = 0;
michael@0 2769 i = 0;
michael@0 2770 while(p12dcx->safeBags[i]) {
michael@0 2771 bagCnt++;
michael@0 2772 if(p12dcx->safeBags[i]->noInstall)
michael@0 2773 noInstallCnt++;
michael@0 2774 if(p12dcx->safeBags[i]->problem) {
michael@0 2775 probCnt++;
michael@0 2776 errorVal = p12dcx->safeBags[i]->error;
michael@0 2777 }
michael@0 2778 i++;
michael@0 2779 }
michael@0 2780
michael@0 2781 /* formerly was erroneous code here that assumed that if all bags
michael@0 2782 * failed to import, then the problem was duplicated data;
michael@0 2783 * that is, it assume that the problem must be that the file had
michael@0 2784 * previously been successfully imported. But importing a
michael@0 2785 * previously imported file causes NO ERRORS at all, and this
michael@0 2786 * false assumption caused real errors to be hidden behind false
michael@0 2787 * errors about duplicated data.
michael@0 2788 */
michael@0 2789
michael@0 2790 if(probCnt) {
michael@0 2791 PORT_SetError(errorVal);
michael@0 2792 return SECFailure;
michael@0 2793 }
michael@0 2794
michael@0 2795 return rv;
michael@0 2796 }
michael@0 2797
michael@0 2798
michael@0 2799 static SECKEYPublicKey *
michael@0 2800 sec_pkcs12_get_public_key_and_usage(sec_PKCS12SafeBag *certBag,
michael@0 2801 unsigned int *usage)
michael@0 2802 {
michael@0 2803 SECKEYPublicKey *pubKey = NULL;
michael@0 2804 CERTCertificate *cert = NULL;
michael@0 2805
michael@0 2806 if(!certBag || !usage) {
michael@0 2807 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2808 return NULL;
michael@0 2809 }
michael@0 2810
michael@0 2811 *usage = 0;
michael@0 2812
michael@0 2813 cert = CERT_DecodeDERCertificate(
michael@0 2814 &certBag->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL);
michael@0 2815 if(!cert) {
michael@0 2816 return NULL;
michael@0 2817 }
michael@0 2818
michael@0 2819 *usage = cert->keyUsage;
michael@0 2820 pubKey = CERT_ExtractPublicKey(cert);
michael@0 2821 CERT_DestroyCertificate(cert);
michael@0 2822 return pubKey;
michael@0 2823 }
michael@0 2824
michael@0 2825 static SECItem *
michael@0 2826 sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey,
michael@0 2827 KeyType *type)
michael@0 2828 {
michael@0 2829 SECItem *pubValue = NULL;
michael@0 2830
michael@0 2831 if(!type || !pubKey) {
michael@0 2832 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2833 return NULL;
michael@0 2834 }
michael@0 2835
michael@0 2836 *type = pubKey->keyType;
michael@0 2837 switch(pubKey->keyType) {
michael@0 2838 case dsaKey:
michael@0 2839 pubValue = &pubKey->u.dsa.publicValue;
michael@0 2840 break;
michael@0 2841 case dhKey:
michael@0 2842 pubValue = &pubKey->u.dh.publicValue;
michael@0 2843 break;
michael@0 2844 case rsaKey:
michael@0 2845 pubValue = &pubKey->u.rsa.modulus;
michael@0 2846 break;
michael@0 2847 case ecKey:
michael@0 2848 pubValue = &pubKey->u.ec.publicValue;
michael@0 2849 break;
michael@0 2850 default:
michael@0 2851 pubValue = NULL;
michael@0 2852 }
michael@0 2853
michael@0 2854 return pubValue;
michael@0 2855 }
michael@0 2856
michael@0 2857 /* This function takes two passes over the bags, installing them in the
michael@0 2858 * desired slot. The two passes are intended to mirror exactly the
michael@0 2859 * two passes in sec_pkcs12_validate_bags.
michael@0 2860 */
michael@0 2861 static SECStatus
michael@0 2862 sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, void *wincx)
michael@0 2863 {
michael@0 2864 sec_PKCS12SafeBag **keyList;
michael@0 2865 int i;
michael@0 2866 int failedKeys = 0;
michael@0 2867
michael@0 2868 if(!safeBags) {
michael@0 2869 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2870 return SECFailure;
michael@0 2871 }
michael@0 2872
michael@0 2873 if(!safeBags[0]) {
michael@0 2874 return SECSuccess;
michael@0 2875 }
michael@0 2876
michael@0 2877 /* First pass. Find all the key bags.
michael@0 2878 * Try to install them, and any certs associated with them.
michael@0 2879 */
michael@0 2880 keyList = sec_pkcs12_get_key_bags(safeBags);
michael@0 2881 if(keyList) {
michael@0 2882 for (i = 0; keyList[i]; i++) {
michael@0 2883 SECStatus rv;
michael@0 2884 SECKEYPublicKey *pubKey = NULL;
michael@0 2885 SECItem *nickName = NULL;
michael@0 2886 sec_PKCS12SafeBag *key = keyList[i];
michael@0 2887 sec_PKCS12SafeBag **certList;
michael@0 2888 unsigned int keyUsage;
michael@0 2889
michael@0 2890 if(key->problem) {
michael@0 2891 ++failedKeys;
michael@0 2892 continue;
michael@0 2893 }
michael@0 2894
michael@0 2895 certList = sec_pkcs12_find_certs_for_key(safeBags, key);
michael@0 2896 if(certList && certList[0]) {
michael@0 2897 pubKey = sec_pkcs12_get_public_key_and_usage(certList[0],
michael@0 2898 &keyUsage);
michael@0 2899 /* use the cert's nickname, if it has one, else use the
michael@0 2900 * key's nickname, else fail.
michael@0 2901 */
michael@0 2902 nickName = sec_pkcs12_get_nickname_for_cert(certList[0], key);
michael@0 2903 } else {
michael@0 2904 nickName = sec_pkcs12_get_nickname(key);
michael@0 2905 }
michael@0 2906 if (!nickName) {
michael@0 2907 key->error = SEC_ERROR_BAD_NICKNAME;
michael@0 2908 key->problem = PR_TRUE;
michael@0 2909 rv = SECFailure;
michael@0 2910 } else if (!pubKey) {
michael@0 2911 key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
michael@0 2912 key->problem = PR_TRUE;
michael@0 2913 rv = SECFailure;
michael@0 2914 } else {
michael@0 2915 rv = sec_pkcs12_add_key(key, pubKey, keyUsage, nickName, wincx);
michael@0 2916 }
michael@0 2917 if (pubKey) {
michael@0 2918 SECKEY_DestroyPublicKey(pubKey);
michael@0 2919 pubKey = NULL;
michael@0 2920 }
michael@0 2921 if (nickName) {
michael@0 2922 SECITEM_FreeItem(nickName, PR_TRUE);
michael@0 2923 nickName = NULL;
michael@0 2924 }
michael@0 2925 if(rv != SECSuccess) {
michael@0 2926 PORT_SetError(key->error);
michael@0 2927 ++failedKeys;
michael@0 2928 }
michael@0 2929
michael@0 2930 if(certList) {
michael@0 2931 int j;
michael@0 2932
michael@0 2933 for (j = 0; certList[j]; j++) {
michael@0 2934 sec_PKCS12SafeBag *cert = certList[j];
michael@0 2935 SECStatus certRv;
michael@0 2936
michael@0 2937 if (!cert)
michael@0 2938 continue;
michael@0 2939 if(rv != SECSuccess) {
michael@0 2940 cert->problem = key->problem;
michael@0 2941 cert->error = key->error;
michael@0 2942 cert->noInstall = PR_TRUE;
michael@0 2943 continue;
michael@0 2944 }
michael@0 2945
michael@0 2946 certRv = sec_pkcs12_add_cert(cert, cert->hasKey, wincx);
michael@0 2947 if(certRv != SECSuccess) {
michael@0 2948 key->problem = cert->problem;
michael@0 2949 key->error = cert->error;
michael@0 2950 PORT_SetError(cert->error);
michael@0 2951 return SECFailure;
michael@0 2952 }
michael@0 2953 }
michael@0 2954 }
michael@0 2955 }
michael@0 2956 }
michael@0 2957 if (failedKeys)
michael@0 2958 return SECFailure;
michael@0 2959
michael@0 2960 /* Now take a second pass over the safebags and install any certs
michael@0 2961 * that were neither installed nor disqualified by the first pass.
michael@0 2962 */
michael@0 2963 for (i = 0; safeBags[i]; i++) {
michael@0 2964 sec_PKCS12SafeBag *bag = safeBags[i];
michael@0 2965
michael@0 2966 if (!bag->installed && !bag->problem && !bag->noInstall) {
michael@0 2967 SECStatus rv;
michael@0 2968 SECOidTag bagType = SECOID_FindOIDTag(&(bag->safeBagType));
michael@0 2969
michael@0 2970 switch(bagType) {
michael@0 2971 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
michael@0 2972 rv = sec_pkcs12_add_cert(bag, bag->hasKey, wincx);
michael@0 2973 if(rv != SECSuccess) {
michael@0 2974 PORT_SetError(bag->error);
michael@0 2975 return SECFailure;
michael@0 2976 }
michael@0 2977 break;
michael@0 2978 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
michael@0 2979 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
michael@0 2980 default:
michael@0 2981 break;
michael@0 2982 }
michael@0 2983 }
michael@0 2984 }
michael@0 2985
michael@0 2986 return SECSuccess;
michael@0 2987 }
michael@0 2988
michael@0 2989 SECStatus
michael@0 2990 SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx)
michael@0 2991 {
michael@0 2992 if(!p12dcx || p12dcx->error) {
michael@0 2993 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2994 return SECFailure;
michael@0 2995 }
michael@0 2996
michael@0 2997 if(!p12dcx->bagsVerified) {
michael@0 2998 return SECFailure;
michael@0 2999 }
michael@0 3000
michael@0 3001 return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx);
michael@0 3002 }
michael@0 3003
michael@0 3004 PRBool
michael@0 3005 sec_pkcs12_bagHasKey(SEC_PKCS12DecoderContext *p12dcx, sec_PKCS12SafeBag *bag)
michael@0 3006 {
michael@0 3007 int i;
michael@0 3008 SECItem *keyId;
michael@0 3009 SECItem *certKeyId;
michael@0 3010
michael@0 3011 certKeyId = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_LOCAL_KEY_ID);
michael@0 3012 if (certKeyId == NULL) {
michael@0 3013 return PR_FALSE;
michael@0 3014 }
michael@0 3015
michael@0 3016 for (i=0; p12dcx->keyList && p12dcx->keyList[i]; i++) {
michael@0 3017 keyId = sec_pkcs12_get_attribute_value(p12dcx->keyList[i],
michael@0 3018 SEC_OID_PKCS9_LOCAL_KEY_ID);
michael@0 3019 if(!keyId) {
michael@0 3020 continue;
michael@0 3021 }
michael@0 3022 if(SECITEM_CompareItem(certKeyId, keyId) == SECEqual) {
michael@0 3023 return PR_TRUE;
michael@0 3024 }
michael@0 3025 }
michael@0 3026 return PR_FALSE;
michael@0 3027 }
michael@0 3028
michael@0 3029 SECItem *
michael@0 3030 sec_pkcs12_get_friendlyName(sec_PKCS12SafeBag *bag)
michael@0 3031 {
michael@0 3032 SECItem *friendlyName;
michael@0 3033 SECItem *tempnm;
michael@0 3034
michael@0 3035 tempnm = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
michael@0 3036 friendlyName = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
michael@0 3037 if (friendlyName) {
michael@0 3038 if (!sec_pkcs12_convert_item_to_unicode(NULL, friendlyName,
michael@0 3039 tempnm, PR_TRUE, PR_FALSE, PR_FALSE)) {
michael@0 3040 SECITEM_FreeItem(friendlyName, PR_TRUE);
michael@0 3041 friendlyName = NULL;
michael@0 3042 }
michael@0 3043 }
michael@0 3044 return friendlyName;
michael@0 3045 }
michael@0 3046
michael@0 3047 /* Following two functions provide access to selected portions of the safe bags.
michael@0 3048 * Iteration is implemented per decoder context and may be accessed after
michael@0 3049 * SEC_PKCS12DecoderVerify() returns success.
michael@0 3050 * When ...DecoderIterateNext() returns SUCCESS a decoder item has been returned
michael@0 3051 * where item.type is always set; item.friendlyName is set if it is non-null;
michael@0 3052 * item.der, item.hasKey are set only for SEC_OID_PKCS12_V1_CERT_BAG_ID items.
michael@0 3053 * ...DecoderIterateNext() returns FAILURE when the list is exhausted or when
michael@0 3054 * arguments are invalid; PORT_GetError() is 0 at end-of-list.
michael@0 3055 * Caller has read-only access to decoder items. Any SECItems generated are
michael@0 3056 * owned by the decoder context and are freed by ...DecoderFinish().
michael@0 3057 */
michael@0 3058 SECStatus
michael@0 3059 SEC_PKCS12DecoderIterateInit(SEC_PKCS12DecoderContext *p12dcx)
michael@0 3060 {
michael@0 3061 if(!p12dcx || p12dcx->error) {
michael@0 3062 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3063 return SECFailure;
michael@0 3064 }
michael@0 3065
michael@0 3066 p12dcx->iteration = 0;
michael@0 3067 return SECSuccess;
michael@0 3068 }
michael@0 3069
michael@0 3070 SECStatus
michael@0 3071 SEC_PKCS12DecoderIterateNext(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3072 const SEC_PKCS12DecoderItem **ipp)
michael@0 3073 {
michael@0 3074 sec_PKCS12SafeBag *bag;
michael@0 3075
michael@0 3076 if(!p12dcx || p12dcx->error) {
michael@0 3077 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3078 return SECFailure;
michael@0 3079 }
michael@0 3080
michael@0 3081 if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) {
michael@0 3082 SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE);
michael@0 3083 }
michael@0 3084 if (p12dcx->decitem.shroudAlg != NULL) {
michael@0 3085 SECOID_DestroyAlgorithmID(p12dcx->decitem.shroudAlg, PR_TRUE);
michael@0 3086 }
michael@0 3087 if (p12dcx->decitem.friendlyName != NULL) {
michael@0 3088 SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE);
michael@0 3089 }
michael@0 3090 p12dcx->decitem.type = 0;
michael@0 3091 p12dcx->decitem.der = NULL;
michael@0 3092 p12dcx->decitem.shroudAlg = NULL;
michael@0 3093 p12dcx->decitem.friendlyName = NULL;
michael@0 3094 p12dcx->decitem.hasKey = PR_FALSE;
michael@0 3095 *ipp = NULL;
michael@0 3096 if (p12dcx->keyList == NULL) {
michael@0 3097 p12dcx->keyList = sec_pkcs12_get_key_bags(p12dcx->safeBags);
michael@0 3098 }
michael@0 3099
michael@0 3100
michael@0 3101 for (; p12dcx->iteration < p12dcx->safeBagCount; p12dcx->iteration++) {
michael@0 3102 bag = p12dcx->safeBags[p12dcx->iteration];
michael@0 3103 if(bag == NULL || bag->problem) {
michael@0 3104 continue;
michael@0 3105 }
michael@0 3106 p12dcx->decitem.type = SECOID_FindOIDTag(&(bag->safeBagType));
michael@0 3107 switch(p12dcx->decitem.type) {
michael@0 3108 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
michael@0 3109 p12dcx->decitem.der = sec_pkcs12_get_der_cert(bag);
michael@0 3110 p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
michael@0 3111 p12dcx->decitem.hasKey = sec_pkcs12_bagHasKey(p12dcx, bag);
michael@0 3112 break;
michael@0 3113 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
michael@0 3114 p12dcx->decitem.shroudAlg = PORT_ZNew(SECAlgorithmID);
michael@0 3115 if (p12dcx->decitem.shroudAlg) {
michael@0 3116 SECOID_CopyAlgorithmID(NULL, p12dcx->decitem.shroudAlg,
michael@0 3117 &bag->safeBagContent.pkcs8ShroudedKeyBag->algorithm);
michael@0 3118 }
michael@0 3119 /* fall through */
michael@0 3120 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
michael@0 3121 p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
michael@0 3122 break;
michael@0 3123 default:
michael@0 3124 /* return these even though we don't expect them */
michael@0 3125 break;
michael@0 3126 case SEC_OID_UNKNOWN:
michael@0 3127 /* ignore these */
michael@0 3128 continue;
michael@0 3129 }
michael@0 3130 *ipp = &p12dcx->decitem;
michael@0 3131 p12dcx->iteration++;
michael@0 3132 break; /* end for() */
michael@0 3133 }
michael@0 3134
michael@0 3135 PORT_SetError(0); /* end-of-list is SECFailure with no PORT error */
michael@0 3136 return ((p12dcx->decitem.type == 0) ? SECFailure : SECSuccess);
michael@0 3137 }
michael@0 3138
michael@0 3139 static SECStatus
michael@0 3140 sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3141 sec_PKCS12SafeBag *bag)
michael@0 3142 {
michael@0 3143 if(!p12dcx || p12dcx->error) {
michael@0 3144 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3145 return SECFailure;
michael@0 3146 }
michael@0 3147
michael@0 3148 p12dcx->safeBags = !p12dcx->safeBagCount
michael@0 3149 ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
michael@0 3150 : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
michael@0 3151 sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
michael@0 3152 p12dcx->safeBagCount + 2);
michael@0 3153
michael@0 3154 if(!p12dcx->safeBags) {
michael@0 3155 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3156 return SECFailure;
michael@0 3157 }
michael@0 3158
michael@0 3159 p12dcx->safeBags[p12dcx->safeBagCount] = bag;
michael@0 3160 p12dcx->safeBags[p12dcx->safeBagCount+1] = NULL;
michael@0 3161 p12dcx->safeBagCount++;
michael@0 3162
michael@0 3163 return SECSuccess;
michael@0 3164 }
michael@0 3165
michael@0 3166 static sec_PKCS12SafeBag *
michael@0 3167 sec_pkcs12_decoder_convert_old_key(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3168 void *key, PRBool isEspvk)
michael@0 3169 {
michael@0 3170 sec_PKCS12SafeBag *keyBag;
michael@0 3171 SECOidData *oid;
michael@0 3172 SECOidTag keyTag;
michael@0 3173 SECItem *keyID, *nickName, *newNickName;
michael@0 3174
michael@0 3175 if(!p12dcx || p12dcx->error || !key) {
michael@0 3176 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3177 return NULL;
michael@0 3178 }
michael@0 3179
michael@0 3180 newNickName = PORT_ArenaZNew(p12dcx->arena, SECItem);
michael@0 3181 keyBag = PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
michael@0 3182 if(!keyBag || !newNickName) {
michael@0 3183 return NULL;
michael@0 3184 }
michael@0 3185
michael@0 3186 keyBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
michael@0 3187 keyBag->slot = p12dcx->slot;
michael@0 3188 keyBag->arena = p12dcx->arena;
michael@0 3189 keyBag->pwitem = p12dcx->pwitem;
michael@0 3190 keyBag->tokenCAs = p12dcx->tokenCAs;
michael@0 3191 keyBag->oldBagType = PR_TRUE;
michael@0 3192
michael@0 3193 keyTag = (isEspvk) ? SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID :
michael@0 3194 SEC_OID_PKCS12_V1_KEY_BAG_ID;
michael@0 3195 oid = SECOID_FindOIDByTag(keyTag);
michael@0 3196 if(!oid) {
michael@0 3197 return NULL;
michael@0 3198 }
michael@0 3199
michael@0 3200 if(SECITEM_CopyItem(p12dcx->arena, &keyBag->safeBagType, &oid->oid)
michael@0 3201 != SECSuccess) {
michael@0 3202 return NULL;
michael@0 3203 }
michael@0 3204
michael@0 3205 if(isEspvk) {
michael@0 3206 SEC_PKCS12ESPVKItem *espvk = (SEC_PKCS12ESPVKItem *)key;
michael@0 3207 keyBag->safeBagContent.pkcs8ShroudedKeyBag =
michael@0 3208 espvk->espvkCipherText.pkcs8KeyShroud;
michael@0 3209 nickName = &(espvk->espvkData.uniNickName);
michael@0 3210 if(!espvk->espvkData.assocCerts || !espvk->espvkData.assocCerts[0]) {
michael@0 3211 PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
michael@0 3212 return NULL;
michael@0 3213 }
michael@0 3214 keyID = &espvk->espvkData.assocCerts[0]->digest;
michael@0 3215 } else {
michael@0 3216 SEC_PKCS12PrivateKey *pk = (SEC_PKCS12PrivateKey *)key;
michael@0 3217 keyBag->safeBagContent.pkcs8KeyBag = &pk->pkcs8data;
michael@0 3218 nickName= &(pk->pvkData.uniNickName);
michael@0 3219 if(!pk->pvkData.assocCerts || !pk->pvkData.assocCerts[0]) {
michael@0 3220 PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
michael@0 3221 return NULL;
michael@0 3222 }
michael@0 3223 keyID = &pk->pvkData.assocCerts[0]->digest;
michael@0 3224 }
michael@0 3225
michael@0 3226 if(nickName->len) {
michael@0 3227 if(nickName->len >= 2) {
michael@0 3228 if(nickName->data[0] && nickName->data[1]) {
michael@0 3229 if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName,
michael@0 3230 nickName, PR_FALSE, PR_FALSE, PR_TRUE)) {
michael@0 3231 return NULL;
michael@0 3232 }
michael@0 3233 nickName = newNickName;
michael@0 3234 } else if(nickName->data[0] && !nickName->data[1]) {
michael@0 3235 unsigned int j = 0;
michael@0 3236 unsigned char t;
michael@0 3237 for(j = 0; j < nickName->len; j+=2) {
michael@0 3238 t = nickName->data[j+1];
michael@0 3239 nickName->data[j+1] = nickName->data[j];
michael@0 3240 nickName->data[j] = t;
michael@0 3241 }
michael@0 3242 }
michael@0 3243 } else {
michael@0 3244 if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName,
michael@0 3245 nickName, PR_FALSE, PR_FALSE, PR_TRUE)) {
michael@0 3246 return NULL;
michael@0 3247 }
michael@0 3248 nickName = newNickName;
michael@0 3249 }
michael@0 3250 }
michael@0 3251
michael@0 3252 if(sec_pkcs12_decoder_set_attribute_value(keyBag,
michael@0 3253 SEC_OID_PKCS9_FRIENDLY_NAME,
michael@0 3254 nickName) != SECSuccess) {
michael@0 3255 return NULL;
michael@0 3256 }
michael@0 3257
michael@0 3258 if(sec_pkcs12_decoder_set_attribute_value(keyBag,SEC_OID_PKCS9_LOCAL_KEY_ID,
michael@0 3259 keyID) != SECSuccess) {
michael@0 3260 return NULL;
michael@0 3261 }
michael@0 3262
michael@0 3263 return keyBag;
michael@0 3264 }
michael@0 3265
michael@0 3266 static sec_PKCS12SafeBag *
michael@0 3267 sec_pkcs12_decoder_create_cert(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3268 SECItem *derCert)
michael@0 3269 {
michael@0 3270 sec_PKCS12SafeBag *certBag;
michael@0 3271 SECOidData *oid;
michael@0 3272 SGNDigestInfo *digest;
michael@0 3273 SECItem *keyId;
michael@0 3274 SECStatus rv;
michael@0 3275
michael@0 3276 if(!p12dcx || p12dcx->error || !derCert) {
michael@0 3277 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3278 return NULL;
michael@0 3279 }
michael@0 3280
michael@0 3281 keyId = PORT_ArenaZNew(p12dcx->arena, SECItem);
michael@0 3282 if(!keyId) {
michael@0 3283 return NULL;
michael@0 3284 }
michael@0 3285
michael@0 3286 digest = sec_pkcs12_compute_thumbprint(derCert);
michael@0 3287 if(!digest) {
michael@0 3288 return NULL;
michael@0 3289 }
michael@0 3290
michael@0 3291 rv = SECITEM_CopyItem(p12dcx->arena, keyId, &digest->digest);
michael@0 3292 SGN_DestroyDigestInfo(digest);
michael@0 3293 if(rv != SECSuccess) {
michael@0 3294 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3295 return NULL;
michael@0 3296 }
michael@0 3297
michael@0 3298 oid = SECOID_FindOIDByTag(SEC_OID_PKCS12_V1_CERT_BAG_ID);
michael@0 3299 certBag = PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
michael@0 3300 if(!certBag || !oid || (SECITEM_CopyItem(p12dcx->arena,
michael@0 3301 &certBag->safeBagType, &oid->oid) != SECSuccess)) {
michael@0 3302 return NULL;
michael@0 3303 }
michael@0 3304
michael@0 3305 certBag->slot = p12dcx->slot;
michael@0 3306 certBag->pwitem = p12dcx->pwitem;
michael@0 3307 certBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
michael@0 3308 certBag->arena = p12dcx->arena;
michael@0 3309 certBag->tokenCAs = p12dcx->tokenCAs;
michael@0 3310
michael@0 3311 oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_X509_CERT);
michael@0 3312 certBag->safeBagContent.certBag =
michael@0 3313 PORT_ArenaZNew(p12dcx->arena, sec_PKCS12CertBag);
michael@0 3314 if(!certBag->safeBagContent.certBag || !oid ||
michael@0 3315 (SECITEM_CopyItem(p12dcx->arena,
michael@0 3316 &certBag->safeBagContent.certBag->bagID,
michael@0 3317 &oid->oid) != SECSuccess)) {
michael@0 3318 return NULL;
michael@0 3319 }
michael@0 3320
michael@0 3321 if(SECITEM_CopyItem(p12dcx->arena,
michael@0 3322 &(certBag->safeBagContent.certBag->value.x509Cert),
michael@0 3323 derCert) != SECSuccess) {
michael@0 3324 return NULL;
michael@0 3325 }
michael@0 3326
michael@0 3327 if(sec_pkcs12_decoder_set_attribute_value(certBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
michael@0 3328 keyId) != SECSuccess) {
michael@0 3329 return NULL;
michael@0 3330 }
michael@0 3331
michael@0 3332 return certBag;
michael@0 3333 }
michael@0 3334
michael@0 3335 static sec_PKCS12SafeBag **
michael@0 3336 sec_pkcs12_decoder_convert_old_cert(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3337 SEC_PKCS12CertAndCRL *oldCert)
michael@0 3338 {
michael@0 3339 sec_PKCS12SafeBag **certList;
michael@0 3340 SECItem **derCertList;
michael@0 3341 int i, j;
michael@0 3342
michael@0 3343 if(!p12dcx || p12dcx->error || !oldCert) {
michael@0 3344 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3345 return NULL;
michael@0 3346 }
michael@0 3347
michael@0 3348 derCertList = SEC_PKCS7GetCertificateList(&oldCert->value.x509->certOrCRL);
michael@0 3349 if(!derCertList) {
michael@0 3350 return NULL;
michael@0 3351 }
michael@0 3352
michael@0 3353 i = 0;
michael@0 3354 while(derCertList[i]) i++;
michael@0 3355
michael@0 3356 certList = PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, (i + 1));
michael@0 3357 if(!certList) {
michael@0 3358 return NULL;
michael@0 3359 }
michael@0 3360
michael@0 3361 for(j = 0; j < i; j++) {
michael@0 3362 certList[j] = sec_pkcs12_decoder_create_cert(p12dcx, derCertList[j]);
michael@0 3363 if(!certList[j]) {
michael@0 3364 return NULL;
michael@0 3365 }
michael@0 3366 }
michael@0 3367
michael@0 3368 return certList;
michael@0 3369 }
michael@0 3370
michael@0 3371 static SECStatus
michael@0 3372 sec_pkcs12_decoder_convert_old_key_and_certs(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3373 void *oldKey, PRBool isEspvk,
michael@0 3374 SEC_PKCS12SafeContents *safe,
michael@0 3375 SEC_PKCS12Baggage *baggage)
michael@0 3376 {
michael@0 3377 sec_PKCS12SafeBag *key, **certList;
michael@0 3378 SEC_PKCS12CertAndCRL *oldCert;
michael@0 3379 SEC_PKCS12PVKSupportingData *pvkData;
michael@0 3380 int i;
michael@0 3381 SECItem *keyName;
michael@0 3382
michael@0 3383 if(!p12dcx || !oldKey) {
michael@0 3384 return SECFailure;
michael@0 3385 }
michael@0 3386
michael@0 3387 if(isEspvk) {
michael@0 3388 pvkData = &((SEC_PKCS12ESPVKItem *)(oldKey))->espvkData;
michael@0 3389 } else {
michael@0 3390 pvkData = &((SEC_PKCS12PrivateKey *)(oldKey))->pvkData;
michael@0 3391 }
michael@0 3392
michael@0 3393 if(!pvkData->assocCerts || !pvkData->assocCerts[0]) {
michael@0 3394 PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
michael@0 3395 return SECFailure;
michael@0 3396 }
michael@0 3397
michael@0 3398 oldCert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
michael@0 3399 SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, NULL,
michael@0 3400 pvkData->assocCerts[0]);
michael@0 3401 if(!oldCert) {
michael@0 3402 PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
michael@0 3403 return SECFailure;
michael@0 3404 }
michael@0 3405
michael@0 3406 key = sec_pkcs12_decoder_convert_old_key(p12dcx,oldKey, isEspvk);
michael@0 3407 certList = sec_pkcs12_decoder_convert_old_cert(p12dcx, oldCert);
michael@0 3408 if(!key || !certList) {
michael@0 3409 return SECFailure;
michael@0 3410 }
michael@0 3411
michael@0 3412 if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, key) != SECSuccess) {
michael@0 3413 return SECFailure;
michael@0 3414 }
michael@0 3415
michael@0 3416 keyName = sec_pkcs12_get_nickname(key);
michael@0 3417 if(!keyName) {
michael@0 3418 return SECFailure;
michael@0 3419 }
michael@0 3420
michael@0 3421 i = 0;
michael@0 3422 while(certList[i]) {
michael@0 3423 if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, certList[i])
michael@0 3424 != SECSuccess) {
michael@0 3425 return SECFailure;
michael@0 3426 }
michael@0 3427 i++;
michael@0 3428 }
michael@0 3429
michael@0 3430 certList = sec_pkcs12_find_certs_for_key(p12dcx->safeBags, key);
michael@0 3431 if(!certList) {
michael@0 3432 return SECFailure;
michael@0 3433 }
michael@0 3434
michael@0 3435 i = 0;
michael@0 3436 while(certList[i] != 0) {
michael@0 3437 if(sec_pkcs12_set_nickname(certList[i], keyName) != SECSuccess) {
michael@0 3438 return SECFailure;
michael@0 3439 }
michael@0 3440 i++;
michael@0 3441 }
michael@0 3442
michael@0 3443 return SECSuccess;
michael@0 3444 }
michael@0 3445
michael@0 3446 static SECStatus
michael@0 3447 sec_pkcs12_decoder_convert_old_safe_to_bags(SEC_PKCS12DecoderContext *p12dcx,
michael@0 3448 SEC_PKCS12SafeContents *safe,
michael@0 3449 SEC_PKCS12Baggage *baggage)
michael@0 3450 {
michael@0 3451 SECStatus rv;
michael@0 3452
michael@0 3453 if(!p12dcx || p12dcx->error) {
michael@0 3454 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3455 return SECFailure;
michael@0 3456 }
michael@0 3457
michael@0 3458 if(safe && safe->contents) {
michael@0 3459 int i = 0;
michael@0 3460 while(safe->contents[i] != NULL) {
michael@0 3461 if(SECOID_FindOIDTag(&safe->contents[i]->safeBagType)
michael@0 3462 == SEC_OID_PKCS12_KEY_BAG_ID) {
michael@0 3463 int j = 0;
michael@0 3464 SEC_PKCS12PrivateKeyBag *privBag =
michael@0 3465 safe->contents[i]->safeContent.keyBag;
michael@0 3466
michael@0 3467 while(privBag->privateKeys[j] != NULL) {
michael@0 3468 SEC_PKCS12PrivateKey *pk = privBag->privateKeys[j];
michael@0 3469 rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx,pk,
michael@0 3470 PR_FALSE, safe, baggage);
michael@0 3471 if(rv != SECSuccess) {
michael@0 3472 goto loser;
michael@0 3473 }
michael@0 3474 j++;
michael@0 3475 }
michael@0 3476 }
michael@0 3477 i++;
michael@0 3478 }
michael@0 3479 }
michael@0 3480
michael@0 3481 if(baggage && baggage->bags) {
michael@0 3482 int i = 0;
michael@0 3483 while(baggage->bags[i] != NULL) {
michael@0 3484 SEC_PKCS12BaggageItem *bag = baggage->bags[i];
michael@0 3485 int j = 0;
michael@0 3486
michael@0 3487 if(!bag->espvks) {
michael@0 3488 i++;
michael@0 3489 continue;
michael@0 3490 }
michael@0 3491
michael@0 3492 while(bag->espvks[j] != NULL) {
michael@0 3493 SEC_PKCS12ESPVKItem *espvk = bag->espvks[j];
michael@0 3494 rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx, espvk,
michael@0 3495 PR_TRUE, safe, baggage);
michael@0 3496 if(rv != SECSuccess) {
michael@0 3497 goto loser;
michael@0 3498 }
michael@0 3499 j++;
michael@0 3500 }
michael@0 3501 i++;
michael@0 3502 }
michael@0 3503 }
michael@0 3504
michael@0 3505 return SECSuccess;
michael@0 3506
michael@0 3507 loser:
michael@0 3508 return SECFailure;
michael@0 3509 }
michael@0 3510
michael@0 3511 SEC_PKCS12DecoderContext *
michael@0 3512 sec_PKCS12ConvertOldSafeToNew(PLArenaPool *arena, PK11SlotInfo *slot,
michael@0 3513 PRBool swapUnicode, SECItem *pwitem,
michael@0 3514 void *wincx, SEC_PKCS12SafeContents *safe,
michael@0 3515 SEC_PKCS12Baggage *baggage)
michael@0 3516 {
michael@0 3517 SEC_PKCS12DecoderContext *p12dcx;
michael@0 3518
michael@0 3519 if(!arena || !slot || !pwitem) {
michael@0 3520 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3521 return NULL;
michael@0 3522 }
michael@0 3523
michael@0 3524 if(!safe && !baggage) {
michael@0 3525 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 3526 return NULL;
michael@0 3527 }
michael@0 3528
michael@0 3529 p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext);
michael@0 3530 if(!p12dcx) {
michael@0 3531 return NULL;
michael@0 3532 }
michael@0 3533
michael@0 3534 p12dcx->arena = arena;
michael@0 3535 p12dcx->slot = PK11_ReferenceSlot(slot);
michael@0 3536 p12dcx->wincx = wincx;
michael@0 3537 p12dcx->error = PR_FALSE;
michael@0 3538 p12dcx->swapUnicodeBytes = swapUnicode;
michael@0 3539 p12dcx->pwitem = pwitem;
michael@0 3540 p12dcx->tokenCAs = SECPKCS12TargetTokenNoCAs;
michael@0 3541
michael@0 3542 if(sec_pkcs12_decoder_convert_old_safe_to_bags(p12dcx, safe, baggage)
michael@0 3543 != SECSuccess) {
michael@0 3544 p12dcx->error = PR_TRUE;
michael@0 3545 return NULL;
michael@0 3546 }
michael@0 3547
michael@0 3548 return p12dcx;
michael@0 3549 }

mercurial