Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |