Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / security / nss / lib / pk11wrap / pk11merge.c
blobef0be3432853e7f60c5df775aa2dbc6d0c97e895
1 /*
2 * Merge the source token into the target token.
3 */
5 #include "secmod.h"
6 #include "secmodi.h"
7 #include "secmodti.h"
8 #include "pk11pub.h"
9 #include "pk11priv.h"
10 #include "pkcs11.h"
11 #include "seccomon.h"
12 #include "secerr.h"
13 #include "keyhi.h"
14 #include "hasht.h"
15 #include "cert.h"
17 /*************************************************************************
19 * short utilities to aid in the merge
21 *************************************************************************/
24 * write a bunch of attributes out to an existing object.
26 static SECStatus
27 pk11_setAttributes(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
28 CK_ATTRIBUTE *setTemplate, CK_ULONG setTemplCount)
30 CK_RV crv;
31 CK_SESSION_HANDLE rwsession;
33 rwsession = PK11_GetRWSession(slot);
34 if (rwsession == CK_INVALID_SESSION) {
35 PORT_SetError(SEC_ERROR_BAD_DATA);
36 return SECFailure;
38 crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
39 setTemplate, setTemplCount);
40 PK11_RestoreROSession(slot, rwsession);
41 if (crv != CKR_OK) {
42 PORT_SetError(PK11_MapError(crv));
43 return SECFailure;
45 return SECSuccess;
50 * copy a template of attributes from a source object to a target object.
51 * if target object is not given, create it.
53 static SECStatus
54 pk11_copyAttributes(PRArenaPool *arena,
55 PK11SlotInfo *targetSlot, CK_OBJECT_HANDLE targetID,
56 PK11SlotInfo *sourceSlot, CK_OBJECT_HANDLE sourceID,
57 CK_ATTRIBUTE *copyTemplate, CK_ULONG copyTemplateCount)
59 SECStatus rv = PK11_GetAttributes(arena, sourceSlot, sourceID,
60 copyTemplate, copyTemplateCount);
61 if (rv != SECSuccess) {
62 return rv;
64 if (targetID == CK_INVALID_HANDLE) {
65 /* we need to create the object */
66 rv = PK11_CreateNewObject(targetSlot, CK_INVALID_SESSION,
67 copyTemplate, copyTemplateCount, PR_TRUE, &targetID);
68 } else {
69 /* update the existing object with the new attributes */
70 rv = pk11_setAttributes(targetSlot, targetID,
71 copyTemplate, copyTemplateCount);
73 return rv;
77 * look for a matching object across tokens.
79 static SECStatus
80 pk11_matchAcrossTokens(PRArenaPool *arena, PK11SlotInfo *targetSlot,
81 PK11SlotInfo *sourceSlot,
82 CK_ATTRIBUTE *template, CK_ULONG tsize,
83 CK_OBJECT_HANDLE id, CK_OBJECT_HANDLE *peer)
86 CK_RV crv;
87 *peer = CK_INVALID_HANDLE;
89 crv = PK11_GetAttributes(arena, sourceSlot, id, template, tsize);
90 if (crv != CKR_OK) {
91 PORT_SetError( PK11_MapError(crv) );
92 goto loser;
95 if (template[0].ulValueLen == -1) {
96 crv = CKR_ATTRIBUTE_TYPE_INVALID;
97 PORT_SetError( PK11_MapError(crv) );
98 goto loser;
101 *peer = pk11_FindObjectByTemplate(targetSlot, template, tsize);
102 return SECSuccess;
104 loser:
105 return SECFailure;
109 * Encrypt using key and parameters
111 SECStatus
112 pk11_encrypt(PK11SymKey *symKey, CK_MECHANISM_TYPE mechType, SECItem *param,
113 SECItem *input, SECItem **output)
115 PK11Context *ctxt = NULL;
116 SECStatus rv = SECSuccess;
118 if (*output) {
119 SECITEM_FreeItem(*output,PR_TRUE);
121 *output = SECITEM_AllocItem(NULL, NULL, input->len+20 /*slop*/);
122 if (!*output) {
123 rv = SECFailure;
124 goto done;
127 ctxt = PK11_CreateContextBySymKey(mechType, CKA_ENCRYPT, symKey, param);
128 if (ctxt == NULL) {
129 rv = SECFailure;
130 goto done;
133 rv = PK11_CipherOp(ctxt, (*output)->data,
134 (int *)&((*output)->len),
135 (*output)->len, input->data, input->len);
137 done:
138 if (ctxt) {
139 PK11_Finalize(ctxt);
140 PK11_DestroyContext(ctxt,PR_TRUE);
142 if (rv != SECSuccess) {
143 if (*output) {
144 SECITEM_FreeItem(*output, PR_TRUE);
145 *output = NULL;
148 return rv;
153 /*************************************************************************
155 * Private Keys
157 *************************************************************************/
160 * Fetch the key usage based on the pkcs #11 flags
162 unsigned int
163 pk11_getPrivateKeyUsage(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
165 unsigned int usage = 0;
167 if ((PK11_HasAttributeSet(slot, id, CKA_UNWRAP) ||
168 PK11_HasAttributeSet(slot,id, CKA_DECRYPT))) {
169 usage |= KU_KEY_ENCIPHERMENT;
171 if (PK11_HasAttributeSet(slot, id, CKA_DERIVE)) {
172 usage |= KU_KEY_AGREEMENT;
174 if ((PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER) ||
175 PK11_HasAttributeSet(slot, id, CKA_SIGN))) {
176 usage |= KU_DIGITAL_SIGNATURE;
178 return usage;
183 * merge a private key,
185 * Private keys are merged using PBE wrapped keys with a random
186 * value as the 'password'. Once the base key is moved, The remaining
187 * attributes (SUBJECT) is copied.
189 static SECStatus
190 pk11_mergePrivateKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
191 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
193 SECKEYPrivateKey *sourceKey = NULL;
194 CK_OBJECT_HANDLE targetKeyID;
195 SECKEYEncryptedPrivateKeyInfo *epki = NULL;
196 char *nickname = NULL;
197 SECItem nickItem;
198 SECItem pwitem;
199 SECItem publicValue;
200 PRArenaPool *arena = NULL;
201 SECStatus rv = SECSuccess;
202 unsigned int keyUsage;
203 unsigned char randomData[SHA1_LENGTH];
204 SECOidTag algTag = SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
205 CK_ATTRIBUTE privTemplate[] = {
206 { CKA_ID, NULL, 0 },
207 { CKA_CLASS, NULL, 0 }
209 CK_ULONG privTemplateCount = sizeof(privTemplate)/sizeof(privTemplate[0]);
210 CK_ATTRIBUTE privCopyTemplate[] = {
211 { CKA_SUBJECT, NULL, 0 }
213 CK_ULONG privCopyTemplateCount =
214 sizeof(privCopyTemplate)/sizeof(privCopyTemplate[0]);
216 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
217 if (arena == NULL) {
218 rv = SECFailure;
219 goto done;
222 /* check to see if the key is already in the target slot */
223 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate,
224 privTemplateCount, id, &targetKeyID);
225 if (rv != SECSuccess) {
226 goto done;
229 if (targetKeyID != CK_INVALID_HANDLE) {
230 /* match found, not an error ... */
231 goto done;
234 /* get an NSS representation of our source key */
235 sourceKey = PK11_MakePrivKey(sourceSlot, nullKey, PR_FALSE,
236 id, sourcePwArg);
237 if (sourceKey == NULL) {
238 rv = SECFailure;
239 goto done;
242 /* Load the private key */
243 /* generate a random pwitem */
244 rv = PK11_GenerateRandom(randomData, sizeof(randomData));
245 if (rv != SECSuccess) {
246 goto done;
248 pwitem.data = randomData;
249 pwitem.len = sizeof(randomData);
250 /* fetch the private key encrypted */
251 epki = PK11_ExportEncryptedPrivKeyInfo(sourceSlot, algTag, &pwitem,
252 sourceKey, 1, sourcePwArg);
253 if (epki == NULL) {
254 rv = SECFailure;
255 goto done;
257 nickname = PK11_GetObjectNickname(sourceSlot, id);
258 /* NULL nickanme is fine (in fact is often normal) */
259 if (nickname) {
260 nickItem.data = (unsigned char *)nickname;
261 nickItem.len = PORT_Strlen(nickname);
263 keyUsage = pk11_getPrivateKeyUsage(sourceSlot, id);
264 /* pass in the CKA_ID */
265 publicValue.data = privTemplate[0].pValue;
266 publicValue.len = privTemplate[0].ulValueLen;
267 rv = PK11_ImportEncryptedPrivateKeyInfo(targetSlot, epki, &pwitem,
268 nickname? &nickItem : NULL , &publicValue,
269 PR_TRUE, PR_TRUE, sourceKey->keyType, keyUsage,
270 targetPwArg);
271 if (rv != SECSuccess) {
272 goto done;
275 /* make sure it made it */
276 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate,
277 privTemplateCount, id, &targetKeyID);
278 if (rv != SECSuccess) {
279 goto done;
282 if (targetKeyID == CK_INVALID_HANDLE) {
283 /* this time the key should exist */
284 rv = SECFailure;
285 goto done;
288 /* fill in remaining attributes */
289 rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
290 privCopyTemplate, privCopyTemplateCount);
291 done:
292 /* make sure the 'key' is cleared */
293 PORT_Memset(randomData, 0, sizeof(randomData));
294 if (nickname) {
295 PORT_Free(nickname);
297 if (sourceKey) {
298 SECKEY_DestroyPrivateKey(sourceKey);
300 if (epki) {
301 SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
303 if (arena) {
304 PORT_FreeArena(arena,PR_FALSE);
306 return rv;
310 /*************************************************************************
312 * Secret Keys
314 *************************************************************************/
317 * we need to find a unique CKA_ID.
318 * The basic idea is to just increment the lowest byte.
319 * This code also handles the following corner cases:
320 * 1) the single byte overflows. On overflow we increment the next byte up
321 * and so forth until we have overflowed the entire CKA_ID.
322 * 2) If we overflow the entire CKA_ID we expand it by one byte.
323 * 3) the CKA_ID is non-existant, we create a new one with one byte.
324 * This means no matter what CKA_ID is passed, the result of this function
325 * is always a new CKA_ID, and this function will never return the same
326 * CKA_ID the it has returned in the passed.
328 static SECStatus
329 pk11_incrementID(PRArenaPool *arena, CK_ATTRIBUTE *ptemplate)
331 unsigned char *buf = ptemplate->pValue;
332 CK_ULONG len = ptemplate->ulValueLen;
334 if (buf == NULL || len == (CK_ULONG)-1) {
335 /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
336 len = 0;
337 } else {
338 CK_ULONG i;
340 /* walk from the back to front, incrementing
341 * the CKA_ID until we no longer have a carry,
342 * or have hit the front of the id. */
343 for (i=len; i != 0; i--) {
344 buf[i-1]++;
345 if (buf[i-1] != 0) {
346 /* no more carries, the increment is complete */
347 return SECSuccess;
350 /* we've now overflowed, fall through and expand the CKA_ID by
351 * one byte */
353 /* if we are here we've run the counter to zero (indicating an overflow).
354 * create an CKA_ID that is all zeros, but has one more zero than
355 * the previous CKA_ID */
356 buf = PORT_ArenaZAlloc(arena, len+1);
357 if (buf == NULL) {
358 return SECFailure;
360 ptemplate->pValue = buf;
361 ptemplate->ulValueLen = len+1;
362 return SECSuccess;
366 static CK_FLAGS
367 pk11_getSecretKeyFlags(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
369 CK_FLAGS flags = 0;
371 if (PK11_HasAttributeSet(slot, id, CKA_UNWRAP)) {
372 flags |= CKF_UNWRAP;
374 if (PK11_HasAttributeSet(slot, id, CKA_WRAP)) {
375 flags |= CKF_WRAP;
377 if (PK11_HasAttributeSet(slot, id, CKA_ENCRYPT)) {
378 flags |= CKF_ENCRYPT;
380 if (PK11_HasAttributeSet(slot, id, CKA_DECRYPT)) {
381 flags |= CKF_DECRYPT;
383 if (PK11_HasAttributeSet(slot, id, CKA_DERIVE)) {
384 flags |= CKF_DERIVE;
386 if (PK11_HasAttributeSet(slot, id, CKA_SIGN)) {
387 flags |= CKF_SIGN;
389 if (PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER)) {
390 flags |= CKF_SIGN_RECOVER;
392 if (PK11_HasAttributeSet(slot, id, CKA_VERIFY)) {
393 flags |= CKF_VERIFY;
395 if (PK11_HasAttributeSet(slot, id, CKA_VERIFY_RECOVER)) {
396 flags |= CKF_VERIFY_RECOVER;
398 return flags;
401 static const char testString[] =
402 "My Encrytion Test Data (should be at least 32 bytes long)";
404 * merge a secret key,
406 * Secret keys may collide by CKA_ID as we merge 2 token. If we collide
407 * on the CKA_ID, we need to make sure we are dealing with different keys.
408 * The reason for this is it is possible that we've merged this database
409 * before, and this key could have been merged already. If the keys are
410 * the same, we are done. If they are not, we need to update the CKA_ID of
411 * the source key and try again.
413 * Once we know we have a unique key to merge in, we use NSS's underlying
414 * key Move function which will do a key exchange if necessary to move
415 * the key from one token to another. Then we set the CKA_ID and additional
416 * pkcs #11 attributes.
418 static SECStatus
419 pk11_mergeSecretKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
420 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
422 PK11SymKey *sourceKey = NULL;
423 PK11SymKey *targetKey = NULL;
424 SECItem *sourceOutput = NULL;
425 SECItem *targetOutput = NULL;
426 SECItem *param = NULL;
427 SECItem input;
428 CK_OBJECT_HANDLE targetKeyID;
429 CK_FLAGS flags;
430 PRArenaPool *arena = NULL;
431 SECStatus rv = SECSuccess;
432 CK_MECHANISM_TYPE keyMechType, cryptoMechType;
433 CK_KEY_TYPE sourceKeyType, targetKeyType;
434 CK_ATTRIBUTE symTemplate[] = {
435 { CKA_ID, NULL, 0 },
436 { CKA_CLASS, NULL, 0 }
438 CK_ULONG symTemplateCount = sizeof(symTemplate)/sizeof(symTemplate[0]);
439 CK_ATTRIBUTE symCopyTemplate[] = {
440 { CKA_LABEL, NULL, 0 }
442 CK_ULONG symCopyTemplateCount =
443 sizeof(symCopyTemplate)/sizeof(symCopyTemplate[0]);
445 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
446 if (arena == NULL) {
447 rv = SECFailure;
448 goto done;
451 sourceKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
452 if (sourceKeyType == (CK_ULONG) -1) {
453 rv = SECFailure;
454 goto done;
457 /* get the key mechanism */
458 keyMechType = PK11_GetKeyMechanism(sourceKeyType);
459 /* get a mechanism suitable to encryption.
460 * PK11_GetKeyMechanism returns a mechanism that is unique to the key
461 * type. It tries to return encryption/decryption mechanisms, however
462 * CKM_DES3_CBC uses and abmiguous keyType, so keyMechType is returned as
463 * 'keygen' mechanism. Detect that case here */
464 cryptoMechType = keyMechType;
465 if ((keyMechType == CKM_DES3_KEY_GEN) ||
466 (keyMechType == CKM_DES2_KEY_GEN)) {
467 cryptoMechType = CKM_DES3_CBC;
470 sourceKey = PK11_SymKeyFromHandle(sourceSlot, NULL, PK11_OriginDerive,
471 keyMechType , id, PR_FALSE, sourcePwArg);
472 if (sourceKey == NULL) {
473 rv = SECFailure;
474 goto done;
477 /* check to see a key with the same CKA_ID already exists in
478 * the target slot. If it does, then we need to verify if the keys
479 * really matches. If they don't import the key with a new CKA_ID
480 * value. */
481 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot,
482 symTemplate, symTemplateCount, id, &targetKeyID);
483 if (rv != SECSuccess) {
484 goto done;
487 /* set up the input test */
488 input.data = (unsigned char *)testString;
489 input.len = PK11_GetBlockSize(cryptoMechType, NULL);
490 if (input.len < 0) {
491 rv = SECFailure;
492 goto done;
494 if (input.len == 0) {
495 input.len = sizeof (testString);
497 while (targetKeyID != CK_INVALID_HANDLE) {
498 /* test to see if the keys are identical */
499 targetKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
500 if (targetKeyType == sourceKeyType) {
501 /* same keyType - see if it's the same key */
502 targetKey = PK11_SymKeyFromHandle(targetSlot, NULL,
503 PK11_OriginDerive, keyMechType, targetKeyID, PR_FALSE,
504 targetPwArg);
505 /* get a parameter if we don't already have one */
506 if (!param) {
507 param = PK11_GenerateNewParam(cryptoMechType, sourceKey);
508 if (param == NULL) {
509 rv = SECFailure;
510 goto done;
513 /* use the source key to encrypt a reference */
514 if (!sourceOutput) {
515 rv = pk11_encrypt(sourceKey, cryptoMechType, param, &input,
516 &sourceOutput);
517 if (rv != SECSuccess) {
518 goto done;
521 /* encrypt the reference with the target key */
522 rv = pk11_encrypt(targetKey, cryptoMechType, param, &input,
523 &targetOutput);
524 if (rv == SECSuccess) {
525 if (SECITEM_ItemsAreEqual(sourceOutput, targetOutput)) {
526 /* they produce the same output, they must be the
527 * same key */
528 goto done;
530 SECITEM_FreeItem(targetOutput, PR_TRUE);
531 targetOutput = NULL;
533 PK11_FreeSymKey(targetKey);
534 targetKey = NULL;
536 /* keys aren't equal, update the KEY_ID and look again */
537 rv = pk11_incrementID(arena, &symTemplate[0]);
538 if (rv != SECSuccess) {
539 goto done;
541 targetKeyID = pk11_FindObjectByTemplate(targetSlot,
542 symTemplate, symTemplateCount);
545 /* we didn't find a matching key, import this one with the new
546 * CKAID */
547 flags = pk11_getSecretKeyFlags(sourceSlot, id);
548 targetKey = PK11_MoveSymKey(targetSlot, PK11_OriginDerive, flags, PR_TRUE,
549 sourceKey);
550 if (targetKey == NULL) {
551 rv = SECFailure;
552 goto done;
554 /* set the key new CKAID */
555 rv = pk11_setAttributes(targetSlot, targetKey->objectID, symTemplate, 1);
556 if (rv != SECSuccess) {
557 goto done;
560 /* fill in remaining attributes */
561 rv = pk11_copyAttributes(arena, targetSlot, targetKey->objectID,
562 sourceSlot, id, symCopyTemplate, symCopyTemplateCount);
563 done:
564 if (sourceKey) {
565 PK11_FreeSymKey(sourceKey);
567 if (targetKey) {
568 PK11_FreeSymKey(targetKey);
570 if (sourceOutput) {
571 SECITEM_FreeItem(sourceOutput, PR_TRUE);
573 if (targetOutput) {
574 SECITEM_FreeItem(targetOutput, PR_TRUE);
576 if (param) {
577 SECITEM_FreeItem(param, PR_TRUE);
579 if (arena) {
580 PORT_FreeArena(arena,PR_FALSE);
582 return rv;
585 /*************************************************************************
587 * Public Keys
589 *************************************************************************/
592 * Merge public key
594 * Use the high level NSS calls to extract the public key and import it
595 * into the token. Extra attributes are then copied to the new token.
597 static SECStatus
598 pk11_mergePublicKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
599 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
601 SECKEYPublicKey *sourceKey = NULL;
602 CK_OBJECT_HANDLE targetKeyID;
603 PRArenaPool *arena = NULL;
604 SECStatus rv = SECSuccess;
605 CK_ATTRIBUTE pubTemplate[] = {
606 { CKA_ID, NULL, 0 },
607 { CKA_CLASS, NULL, 0 }
609 CK_ULONG pubTemplateCount = sizeof(pubTemplate)/sizeof(pubTemplate[0]);
610 CK_ATTRIBUTE pubCopyTemplate[] = {
611 { CKA_ID, NULL, 0 },
612 { CKA_LABEL, NULL, 0 },
613 { CKA_SUBJECT, NULL, 0 }
615 CK_ULONG pubCopyTemplateCount =
616 sizeof(pubCopyTemplate)/sizeof(pubCopyTemplate[0]);
618 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
619 if (arena == NULL) {
620 rv = SECFailure;
621 goto done;
625 /* check to see if the key is already in the target slot */
626 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, pubTemplate,
627 pubTemplateCount, id, &targetKeyID);
628 if (rv != SECSuccess) {
629 goto done;
632 /* Key is already in the target slot */
633 if (targetKeyID != CK_INVALID_HANDLE) {
634 /* not an error ... */
635 goto done;
638 /* fetch an NSS representation of the public key */
639 sourceKey = PK11_ExtractPublicKey(sourceSlot, nullKey, id);
640 if (sourceKey== NULL) {
641 rv = SECFailure;
642 goto done;
645 /* load the public key into the target token. */
646 targetKeyID = PK11_ImportPublicKey(targetSlot, sourceKey, PR_TRUE);
647 if (targetKeyID == CK_INVALID_HANDLE) {
648 rv = SECFailure;
649 goto done;
652 /* fill in remaining attributes */
653 rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
654 pubCopyTemplate, pubCopyTemplateCount);
657 done:
658 if (sourceKey) {
659 SECKEY_DestroyPublicKey(sourceKey);
661 if (arena) {
662 PORT_FreeArena(arena,PR_FALSE);
664 return rv;
667 /*************************************************************************
669 * Certificates
671 *************************************************************************/
674 * merge a certificate object
676 * Use the high level NSS calls to extract and import the certificate.
678 static SECStatus
679 pk11_mergeCert(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
680 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
682 CERTCertificate *sourceCert = NULL;
683 CK_OBJECT_HANDLE targetCertID = CK_INVALID_HANDLE;
684 char *nickname = NULL;
685 SECStatus rv = SECSuccess;
686 PRArenaPool *arena = NULL;
687 CK_ATTRIBUTE sourceCKAID = {CKA_ID, NULL, 0};
688 CK_ATTRIBUTE targetCKAID = {CKA_ID, NULL, 0};
689 SECStatus lrv = SECSuccess;
690 int error;
693 sourceCert = PK11_MakeCertFromHandle(sourceSlot, id, NULL);
694 if (sourceCert == NULL) {
695 rv = SECFailure;
696 goto done;
699 nickname = PK11_GetObjectNickname(sourceSlot, id);
701 /* see if the cert is already there */
702 targetCertID = PK11_FindCertInSlot(targetSlot, sourceCert, targetPwArg);
703 if (targetCertID == CK_INVALID_HANDLE) {
704 /* cert doesn't exist load the cert in. */
705 /* OK for the nickname to be NULL, not all certs have nicknames */
706 rv = PK11_ImportCert(targetSlot, sourceCert, CK_INVALID_HANDLE,
707 nickname, PR_FALSE);
708 goto done;
711 /* the cert already exists, see if the nickname and/or CKA_ID need
712 * to be updated */
714 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
715 if (arena == NULL) {
716 rv = SECFailure;
717 goto done;
720 /* does our source have a CKA_ID ? */
721 rv = PK11_GetAttributes(arena, sourceSlot, id, &sourceCKAID, 1);
722 if (rv != SECSuccess) {
723 sourceCKAID.ulValueLen = 0;
726 /* if we have a source CKA_ID, see of we need to update the
727 * target's CKA_ID */
728 if (sourceCKAID.ulValueLen != 0) {
729 rv = PK11_GetAttributes(arena, targetSlot, targetCertID,
730 &targetCKAID, 1);
731 if (rv != SECSuccess) {
732 targetCKAID.ulValueLen = 0;
734 /* if the target has no CKA_ID, update it from the source */
735 if (targetCKAID.ulValueLen == 0) {
736 lrv=pk11_setAttributes(targetSlot, targetCertID, &sourceCKAID, 1);
737 if (lrv != SECSuccess) {
738 error = PORT_GetError();
742 rv = SECSuccess;
744 /* now check if we need to update the nickname */
745 if (nickname && *nickname) {
746 char *targetname;
747 targetname = PK11_GetObjectNickname(targetSlot, targetCertID);
748 if (!targetname || !*targetname) {
749 /* target has no nickname, or it's empty, update it */
750 rv = PK11_SetObjectNickname(targetSlot, targetCertID, nickname);
752 if (targetname) {
753 PORT_Free(targetname);
757 /* restore the error code if CKA_ID failed, but nickname didn't */
758 if ((rv == SECSuccess) && (lrv != SECSuccess)) {
759 rv = lrv;
760 PORT_SetError(error);
763 done:
764 if (nickname) {
765 PORT_Free(nickname);
767 if (sourceCert) {
768 CERT_DestroyCertificate(sourceCert);
770 if (arena) {
771 PORT_FreeArena(arena,PR_FALSE);
773 return rv;
777 /*************************************************************************
779 * Crls
781 *************************************************************************/
784 * Use the raw PKCS #11 interface to merge the CRLs.
786 * In the case where of collision, choose the newest CRL that is valid.
788 static SECStatus
789 pk11_mergeCrl(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
790 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
792 CK_OBJECT_HANDLE targetCrlID;
793 PRArenaPool *arena = NULL;
794 SECStatus rv = SECSuccess;
795 CK_ATTRIBUTE crlTemplate[] = {
796 { CKA_SUBJECT, NULL, 0 },
797 { CKA_CLASS, NULL, 0 },
798 { CKA_NSS_KRL, NULL, 0 }
800 CK_ULONG crlTemplateCount = sizeof(crlTemplate)/sizeof(crlTemplate[0]);
801 CK_ATTRIBUTE crlCopyTemplate[] = {
802 { CKA_CLASS, NULL, 0 },
803 { CKA_TOKEN, NULL, 0 },
804 { CKA_LABEL, NULL, 0 },
805 { CKA_PRIVATE, NULL, 0 },
806 { CKA_MODIFIABLE, NULL, 0 },
807 { CKA_SUBJECT, NULL, 0 },
808 { CKA_NSS_KRL, NULL, 0 },
809 { CKA_NSS_URL, NULL, 0 },
810 { CKA_VALUE, NULL, 0 }
812 CK_ULONG crlCopyTemplateCount =
813 sizeof(crlCopyTemplate)/sizeof(crlCopyTemplate[0]);
815 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
816 if (arena == NULL) {
817 rv = SECFailure;
818 goto done;
820 /* check to see if the crl is already in the target slot */
821 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, crlTemplate,
822 crlTemplateCount, id, &targetCrlID);
823 if (rv != SECSuccess) {
824 goto done;
826 if (targetCrlID != CK_INVALID_HANDLE) {
827 /* we already have a CRL, check to see which is more up-to-date. */
828 goto done;
831 /* load the CRL into the target token. */
832 rv = pk11_copyAttributes(arena, targetSlot, targetCrlID, sourceSlot, id,
833 crlCopyTemplate, crlCopyTemplateCount);
834 done:
835 if (arena) {
836 PORT_FreeArena(arena,PR_FALSE);
838 return rv;
841 /*************************************************************************
843 * SMIME objects
845 *************************************************************************/
848 * use the raw PKCS #11 interface to merge the S/MIME records
850 static SECStatus
851 pk11_mergeSmime(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
852 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
854 CK_OBJECT_HANDLE targetSmimeID;
855 PRArenaPool *arena = NULL;
856 SECStatus rv = SECSuccess;
857 CK_ATTRIBUTE smimeTemplate[] = {
858 { CKA_SUBJECT, NULL, 0 },
859 { CKA_NSS_EMAIL, NULL, 0 },
860 { CKA_CLASS, NULL, 0 },
862 CK_ULONG smimeTemplateCount =
863 sizeof(smimeTemplate)/sizeof(smimeTemplate[0]);
864 CK_ATTRIBUTE smimeCopyTemplate[] = {
865 { CKA_CLASS, NULL, 0 },
866 { CKA_TOKEN, NULL, 0 },
867 { CKA_LABEL, NULL, 0 },
868 { CKA_PRIVATE, NULL, 0 },
869 { CKA_MODIFIABLE, NULL, 0 },
870 { CKA_SUBJECT, NULL, 0 },
871 { CKA_NSS_EMAIL, NULL, 0 },
872 { CKA_NSS_SMIME_TIMESTAMP, NULL, 0 },
873 { CKA_VALUE, NULL, 0 }
875 CK_ULONG smimeCopyTemplateCount =
876 sizeof(smimeCopyTemplate)/sizeof(smimeCopyTemplate[0]);
878 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
879 if (arena == NULL) {
880 rv = SECFailure;
881 goto done;
883 /* check to see if the crl is already in the target slot */
884 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, smimeTemplate,
885 smimeTemplateCount, id, &targetSmimeID);
886 if (rv != SECSuccess) {
887 goto done;
889 if (targetSmimeID != CK_INVALID_HANDLE) {
890 /* we already have a SMIME record */
891 goto done;
894 /* load the SMime Record into the target token. */
895 rv = pk11_copyAttributes(arena, targetSlot, targetSmimeID, sourceSlot, id,
896 smimeCopyTemplate, smimeCopyTemplateCount);
897 done:
898 if (arena) {
899 PORT_FreeArena(arena,PR_FALSE);
901 return rv;
904 /*************************************************************************
906 * Trust Objects
908 *************************************************************************/
912 * decide which trust record entry wins. PR_TRUE (source) or PR_FALSE (target)
914 #define USE_TARGET PR_FALSE
915 #define USE_SOURCE PR_TRUE
916 PRBool
917 pk11_mergeTrustEntry(CK_ATTRIBUTE *target, CK_ATTRIBUTE *source)
919 CK_ULONG targetTrust = (target->ulValueLen == sizeof (CK_LONG)) ?
920 *(CK_ULONG *)target->pValue : CKT_NSS_TRUST_UNKNOWN;
921 CK_ULONG sourceTrust = (source->ulValueLen == sizeof (CK_LONG)) ?
922 *(CK_ULONG *)source->pValue : CKT_NSS_TRUST_UNKNOWN;
925 * Examine a single entry and deside if the source or target version
926 * should win out. When all the entries have been checked, if there is
927 * any case we need to update, we will write the whole source record
928 * to the target database. That means for each individual record, if the
929 * target wins, we need to update the source (in case later we have a
930 * case where the source wins). If the source wins, it already
932 if (sourceTrust == targetTrust) {
933 return USE_TARGET; /* which equates to 'do nothing' */
936 if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
937 return USE_TARGET;
940 /* target has no idea, use the source's idea of the trust value */
941 if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
942 /* source overwrites the target */
943 return USE_SOURCE;
946 /* so both the target and the source have some idea of what this
947 * trust attribute should be, and neither agree exactly.
948 * At this point, we prefer 'hard' attributes over 'soft' ones.
949 * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
950 * CKT_NSS_UNTRUTED. Soft ones are ones which don't change the
951 * actual trust of the cert (CKT_MUST_VERIFY, CKT_NSS_VALID,
952 * CKT_NSS_VALID_DELEGATOR).
954 if ((sourceTrust == CKT_NSS_MUST_VERIFY)
955 || (sourceTrust == CKT_NSS_VALID)
956 || (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
957 return USE_TARGET;
959 if ((targetTrust == CKT_NSS_MUST_VERIFY)
960 || (targetTrust == CKT_NSS_VALID)
961 || (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
962 /* source overrites the target */
963 return USE_SOURCE;
966 /* both have hard attributes, we have a conflict, let the target win. */
967 return USE_TARGET;
970 * use the raw PKCS #11 interface to merge the S/MIME records
972 static SECStatus
973 pk11_mergeTrust(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
974 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
976 CK_OBJECT_HANDLE targetTrustID;
977 PRArenaPool *arena = NULL;
978 SECStatus rv = SECSuccess;
979 int error = 0;
980 CK_ATTRIBUTE trustTemplate[] = {
981 { CKA_ISSUER, NULL, 0 },
982 { CKA_SERIAL_NUMBER, NULL, 0 },
983 { CKA_CLASS, NULL, 0 },
985 CK_ULONG trustTemplateCount =
986 sizeof(trustTemplate)/sizeof(trustTemplate[0]);
987 CK_ATTRIBUTE trustCopyTemplate[] = {
988 { CKA_CLASS, NULL, 0 },
989 { CKA_TOKEN, NULL, 0 },
990 { CKA_LABEL, NULL, 0 },
991 { CKA_PRIVATE, NULL, 0 },
992 { CKA_MODIFIABLE, NULL, 0 },
993 { CKA_ISSUER, NULL, 0},
994 { CKA_SERIAL_NUMBER, NULL, 0},
995 { CKA_CERT_SHA1_HASH, NULL, 0 },
996 { CKA_CERT_MD5_HASH, NULL, 0 },
997 { CKA_TRUST_SERVER_AUTH, NULL, 0 },
998 { CKA_TRUST_CLIENT_AUTH, NULL, 0 },
999 { CKA_TRUST_CODE_SIGNING, NULL, 0 },
1000 { CKA_TRUST_EMAIL_PROTECTION, NULL, 0 },
1001 { CKA_TRUST_STEP_UP_APPROVED, NULL, 0 }
1003 CK_ULONG trustCopyTemplateCount =
1004 sizeof(trustCopyTemplate)/sizeof(trustCopyTemplate[0]);
1006 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
1007 if (arena == NULL) {
1008 rv = SECFailure;
1009 goto done;
1011 /* check to see if the crl is already in the target slot */
1012 rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, trustTemplate,
1013 trustTemplateCount, id, &targetTrustID);
1014 if (rv != SECSuccess) {
1015 goto done;
1017 if (targetTrustID != CK_INVALID_HANDLE) {
1018 /* a matching trust record already exists, merge it in */
1019 CK_ATTRIBUTE_TYPE trustAttrs[] = {
1020 CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
1021 CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
1022 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
1023 CKA_TRUST_TIME_STAMPING
1025 CK_ULONG trustAttrsCount =
1026 sizeof(trustAttrs)/sizeof(trustAttrs[0]);
1028 int i;
1029 CK_ATTRIBUTE targetTemplate, sourceTemplate;
1031 /* existing trust record, merge the two together */
1032 for (i=0; i < trustAttrsCount; i++) {
1033 targetTemplate.type = sourceTemplate.type = trustAttrs[i];
1034 targetTemplate.pValue = sourceTemplate.pValue = NULL;
1035 targetTemplate.ulValueLen = sourceTemplate.ulValueLen = 0;
1036 PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
1037 PK11_GetAttributes(arena, targetSlot, targetTrustID,
1038 &targetTemplate, 1);
1039 if (pk11_mergeTrustEntry(&targetTemplate, &sourceTemplate)) {
1040 /* source wins, write out the source attribute to the target */
1041 SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID,
1042 &sourceTemplate, 1);
1043 if (lrv != SECSuccess) {
1044 rv = SECFailure;
1045 error = PORT_GetError();
1050 /* handle step */
1051 sourceTemplate.type = CKA_TRUST_STEP_UP_APPROVED;
1052 sourceTemplate.pValue = NULL;
1053 sourceTemplate.ulValueLen = 0;
1055 /* if the source has steup set, then set it in the target */
1056 PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
1057 if ((sourceTemplate.ulValueLen == sizeof(CK_BBOOL)) &&
1058 (sourceTemplate.pValue) &&
1059 (*(CK_BBOOL *)sourceTemplate.pValue == CK_TRUE)) {
1060 SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID,
1061 &sourceTemplate, 1);
1062 if (lrv != SECSuccess) {
1063 rv = SECFailure;
1064 error = PORT_GetError();
1068 goto done;
1072 /* load the new trust Record into the target token. */
1073 rv = pk11_copyAttributes(arena, targetSlot, targetTrustID, sourceSlot, id,
1074 trustCopyTemplate, trustCopyTemplateCount);
1075 done:
1076 if (arena) {
1077 PORT_FreeArena(arena,PR_FALSE);
1080 /* restore the error code */
1081 if (rv == SECFailure && error) {
1082 PORT_SetError(error);
1085 return rv;
1088 /*************************************************************************
1090 * Central merge code
1092 *************************************************************************/
1094 * merge a single object from sourceToken to targetToken
1096 static SECStatus
1097 pk11_mergeObject(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
1098 CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
1101 CK_OBJECT_CLASS objClass;
1104 objClass = PK11_ReadULongAttribute(sourceSlot, id, CKA_CLASS);
1105 if (objClass == (CK_ULONG) -1) {
1106 PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE );
1107 return SECFailure;
1110 switch (objClass) {
1111 case CKO_CERTIFICATE:
1112 return pk11_mergeCert(targetSlot, sourceSlot, id,
1113 targetPwArg, sourcePwArg);
1114 case CKO_NSS_TRUST:
1115 return pk11_mergeTrust(targetSlot, sourceSlot, id,
1116 targetPwArg, sourcePwArg);
1117 case CKO_PUBLIC_KEY:
1118 return pk11_mergePublicKey(targetSlot, sourceSlot, id,
1119 targetPwArg, sourcePwArg);
1120 case CKO_PRIVATE_KEY:
1121 return pk11_mergePrivateKey(targetSlot, sourceSlot, id,
1122 targetPwArg, sourcePwArg);
1123 case CKO_SECRET_KEY:
1124 return pk11_mergeSecretKey(targetSlot, sourceSlot, id,
1125 targetPwArg, sourcePwArg);
1126 case CKO_NSS_CRL:
1127 return pk11_mergeCrl(targetSlot, sourceSlot, id,
1128 targetPwArg, sourcePwArg);
1129 case CKO_NSS_SMIME:
1130 return pk11_mergeSmime(targetSlot, sourceSlot, id,
1131 targetPwArg, sourcePwArg);
1132 default:
1133 break;
1136 PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE );
1137 return SECFailure;
1140 PK11MergeLogNode *
1141 pk11_newMergeLogNode(PRArenaPool *arena,
1142 PK11SlotInfo *slot, CK_OBJECT_HANDLE id, int error)
1144 PK11MergeLogNode *newLog;
1145 PK11GenericObject *obj;
1147 newLog = PORT_ArenaZNew(arena, PK11MergeLogNode);
1148 if (newLog == NULL) {
1149 return NULL;
1152 obj = PORT_ArenaZNew(arena, PK11GenericObject);
1153 if ( !obj ) {
1154 return NULL;
1157 /* initialize it */
1158 obj->slot = slot;
1159 obj->objectID = id;
1161 newLog->object= obj;
1162 newLog->error = error;
1163 return newLog;
1167 * walk down each entry and merge it. keep track of the errors in the log
1169 static SECStatus
1170 pk11_mergeByObjectIDs(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
1171 CK_OBJECT_HANDLE *objectIDs, int count,
1172 PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
1174 SECStatus rv = SECSuccess;
1175 int error, i;
1177 for (i=0; i < count; i++) {
1178 /* try to update the entire database. On failure, keep going,
1179 * but remember the error to report back to the caller */
1180 SECStatus lrv;
1181 PK11MergeLogNode *newLog;
1183 lrv= pk11_mergeObject(targetSlot, sourceSlot, objectIDs[i],
1184 targetPwArg, sourcePwArg);
1185 if (lrv == SECSuccess) {
1186 /* merged with no problem, go to next object */
1187 continue;
1190 /* remember that we failed and why */
1191 rv = SECFailure;
1192 error = PORT_GetError();
1194 /* log the errors */
1195 if (!log) {
1196 /* not logging, go to next entry */
1197 continue;
1199 newLog = pk11_newMergeLogNode(log->arena, sourceSlot,
1200 objectIDs[i], error);
1201 if (!newLog) {
1202 /* failed to allocate entry, just keep going */
1203 continue;
1206 /* link in the errorlog entry */
1207 newLog->next = NULL;
1208 if (log->tail) {
1209 log->tail->next = newLog;
1210 } else {
1211 log->head = newLog;
1213 newLog->prev = log->tail;
1214 log->tail = newLog;
1217 /* restore the last error code */
1218 if (rv != SECSuccess) {
1219 PORT_SetError(error);
1221 return rv;
1225 * Merge all the records in sourceSlot that aren't in targetSlot
1227 * This function will return failure if not all the objects
1228 * successfully merged.
1230 * Applications can pass in an optional error log which will record
1231 * each failing object and why it failed to import. PK11MergeLog
1232 * is modelled after the CERTVerifyLog.
1234 SECStatus
1235 PK11_MergeTokens(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
1236 PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
1238 SECStatus rv = SECSuccess, lrv = SECSuccess;
1239 int error, count = 0;
1240 CK_ATTRIBUTE search[2];
1241 CK_OBJECT_HANDLE *objectIDs = NULL;
1242 CK_BBOOL ck_true = CK_TRUE;
1243 CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY;
1245 PK11_SETATTRS(&search[0], CKA_TOKEN, &ck_true, sizeof(ck_true));
1246 PK11_SETATTRS(&search[1], CKA_CLASS, &privKey, sizeof(privKey));
1248 * make sure both tokens are already authenticated if need be.
1250 rv = PK11_Authenticate(targetSlot, PR_TRUE, targetPwArg);
1251 if (rv != SECSuccess) {
1252 goto loser;
1254 rv = PK11_Authenticate(sourceSlot, PR_TRUE, sourcePwArg);
1255 if (rv != SECSuccess) {
1256 goto loser;
1259 /* turns out the old DB's are rather fragile if the private keys aren't
1260 * merged in first, so do the private keys explicity. */
1261 objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 2, &count);
1262 if (objectIDs) {
1263 lrv = pk11_mergeByObjectIDs(targetSlot, sourceSlot,
1264 objectIDs, count, log,
1265 targetPwArg, sourcePwArg);
1266 if (lrv != SECSuccess) {
1267 error = PORT_GetError();
1269 PORT_Free(objectIDs);
1270 count = 0;
1273 /* now do the rest (NOTE: this will repeat the private keys, but
1274 * that shouldnt' be an issue as we will notice they are already
1275 * merged in */
1276 objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 1, &count);
1277 if (!objectIDs) {
1278 rv = SECFailure;
1279 goto loser;
1282 rv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, objectIDs, count, log,
1283 targetPwArg, sourcePwArg);
1284 if (rv == SECSuccess) {
1285 /* if private keys failed, but the rest succeeded, be sure to let
1286 * the caller know that private keys failed and why.
1287 * NOTE: this is highly unlikely since the same keys that failed
1288 * in the previous merge call will most likely fail in this one */
1289 if (lrv != SECSuccess) {
1290 rv = lrv;
1291 PORT_SetError(error);
1295 loser:
1296 if (objectIDs) {
1297 PORT_Free(objectIDs);
1299 return rv;
1302 PK11MergeLog *
1303 PK11_CreateMergeLog(void)
1305 PRArenaPool *arena;
1306 PK11MergeLog *log;
1308 arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
1309 if (arena == NULL) {
1310 return NULL;
1313 log = PORT_ArenaZNew(arena, PK11MergeLog);
1314 if (log == NULL) {
1315 PORT_FreeArena(arena,PR_FALSE);
1316 return NULL;
1318 log->arena = arena;
1319 log->version = 1;
1320 return log;
1323 void
1324 PK11_DestroyMergeLog(PK11MergeLog *log)
1326 if (log && log->arena) {
1327 PORT_FreeArena(log->arena, PR_FALSE);