mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-13 03:10:10 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
1419 lines
40 KiB
C
1419 lines
40 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* Merge the source token into the target token.
|
|
*/
|
|
|
|
#include "secmod.h"
|
|
#include "secmodi.h"
|
|
#include "secmodti.h"
|
|
#include "pk11pub.h"
|
|
#include "pk11priv.h"
|
|
#include "pkcs11.h"
|
|
#include "seccomon.h"
|
|
#include "secerr.h"
|
|
#include "keyhi.h"
|
|
#include "hasht.h"
|
|
#include "cert.h"
|
|
#include "certdb.h"
|
|
|
|
/*************************************************************************
|
|
*
|
|
* short utilities to aid in the merge
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* write a bunch of attributes out to an existing object.
|
|
*/
|
|
static SECStatus
|
|
pk11_setAttributes(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
|
|
CK_ATTRIBUTE *setTemplate, CK_ULONG setTemplCount)
|
|
{
|
|
CK_RV crv;
|
|
CK_SESSION_HANDLE rwsession;
|
|
|
|
rwsession = PK11_GetRWSession(slot);
|
|
if (rwsession == CK_INVALID_SESSION) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATA);
|
|
return SECFailure;
|
|
}
|
|
crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
|
|
setTemplate, setTemplCount);
|
|
PK11_RestoreROSession(slot, rwsession);
|
|
if (crv != CKR_OK) {
|
|
PORT_SetError(PK11_MapError(crv));
|
|
return SECFailure;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
|
|
/*
|
|
* copy a template of attributes from a source object to a target object.
|
|
* if target object is not given, create it.
|
|
*/
|
|
static SECStatus
|
|
pk11_copyAttributes(PLArenaPool *arena,
|
|
PK11SlotInfo *targetSlot, CK_OBJECT_HANDLE targetID,
|
|
PK11SlotInfo *sourceSlot, CK_OBJECT_HANDLE sourceID,
|
|
CK_ATTRIBUTE *copyTemplate, CK_ULONG copyTemplateCount)
|
|
{
|
|
SECStatus rv = PK11_GetAttributes(arena, sourceSlot, sourceID,
|
|
copyTemplate, copyTemplateCount);
|
|
if (rv != SECSuccess) {
|
|
return rv;
|
|
}
|
|
if (targetID == CK_INVALID_HANDLE) {
|
|
/* we need to create the object */
|
|
rv = PK11_CreateNewObject(targetSlot, CK_INVALID_SESSION,
|
|
copyTemplate, copyTemplateCount, PR_TRUE, &targetID);
|
|
} else {
|
|
/* update the existing object with the new attributes */
|
|
rv = pk11_setAttributes(targetSlot, targetID,
|
|
copyTemplate, copyTemplateCount);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* look for a matching object across tokens.
|
|
*/
|
|
static SECStatus
|
|
pk11_matchAcrossTokens(PLArenaPool *arena, PK11SlotInfo *targetSlot,
|
|
PK11SlotInfo *sourceSlot,
|
|
CK_ATTRIBUTE *template, CK_ULONG tsize,
|
|
CK_OBJECT_HANDLE id, CK_OBJECT_HANDLE *peer)
|
|
{
|
|
|
|
CK_RV crv;
|
|
*peer = CK_INVALID_HANDLE;
|
|
|
|
crv = PK11_GetAttributes(arena, sourceSlot, id, template, tsize);
|
|
if (crv != CKR_OK) {
|
|
PORT_SetError( PK11_MapError(crv) );
|
|
goto loser;
|
|
}
|
|
|
|
if (template[0].ulValueLen == -1) {
|
|
crv = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
PORT_SetError( PK11_MapError(crv) );
|
|
goto loser;
|
|
}
|
|
|
|
*peer = pk11_FindObjectByTemplate(targetSlot, template, tsize);
|
|
return SECSuccess;
|
|
|
|
loser:
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* Encrypt using key and parameters
|
|
*/
|
|
SECStatus
|
|
pk11_encrypt(PK11SymKey *symKey, CK_MECHANISM_TYPE mechType, SECItem *param,
|
|
SECItem *input, SECItem **output)
|
|
{
|
|
PK11Context *ctxt = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
|
|
if (*output) {
|
|
SECITEM_FreeItem(*output,PR_TRUE);
|
|
}
|
|
*output = SECITEM_AllocItem(NULL, NULL, input->len+20 /*slop*/);
|
|
if (!*output) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
ctxt = PK11_CreateContextBySymKey(mechType, CKA_ENCRYPT, symKey, param);
|
|
if (ctxt == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
rv = PK11_CipherOp(ctxt, (*output)->data,
|
|
(int *)&((*output)->len),
|
|
(*output)->len, input->data, input->len);
|
|
|
|
done:
|
|
if (ctxt) {
|
|
PK11_Finalize(ctxt);
|
|
PK11_DestroyContext(ctxt,PR_TRUE);
|
|
}
|
|
if (rv != SECSuccess) {
|
|
if (*output) {
|
|
SECITEM_FreeItem(*output, PR_TRUE);
|
|
*output = NULL;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Private Keys
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Fetch the key usage based on the pkcs #11 flags
|
|
*/
|
|
unsigned int
|
|
pk11_getPrivateKeyUsage(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
|
|
{
|
|
unsigned int usage = 0;
|
|
|
|
if ((PK11_HasAttributeSet(slot, id, CKA_UNWRAP,PR_FALSE) ||
|
|
PK11_HasAttributeSet(slot,id, CKA_DECRYPT,PR_FALSE))) {
|
|
usage |= KU_KEY_ENCIPHERMENT;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) {
|
|
usage |= KU_KEY_AGREEMENT;
|
|
}
|
|
if ((PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE) ||
|
|
PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE))) {
|
|
usage |= KU_DIGITAL_SIGNATURE;
|
|
}
|
|
return usage;
|
|
}
|
|
|
|
|
|
/*
|
|
* merge a private key,
|
|
*
|
|
* Private keys are merged using PBE wrapped keys with a random
|
|
* value as the 'password'. Once the base key is moved, The remaining
|
|
* attributes (SUBJECT) is copied.
|
|
*/
|
|
static SECStatus
|
|
pk11_mergePrivateKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
SECKEYPrivateKey *sourceKey = NULL;
|
|
CK_OBJECT_HANDLE targetKeyID;
|
|
SECKEYEncryptedPrivateKeyInfo *epki = NULL;
|
|
char *nickname = NULL;
|
|
SECItem nickItem;
|
|
SECItem pwitem;
|
|
SECItem publicValue;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
unsigned int keyUsage;
|
|
unsigned char randomData[SHA1_LENGTH];
|
|
SECOidTag algTag = SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
|
|
CK_ATTRIBUTE privTemplate[] = {
|
|
{ CKA_ID, NULL, 0 },
|
|
{ CKA_CLASS, NULL, 0 }
|
|
};
|
|
CK_ULONG privTemplateCount = sizeof(privTemplate)/sizeof(privTemplate[0]);
|
|
CK_ATTRIBUTE privCopyTemplate[] = {
|
|
{ CKA_SUBJECT, NULL, 0 }
|
|
};
|
|
CK_ULONG privCopyTemplateCount =
|
|
sizeof(privCopyTemplate)/sizeof(privCopyTemplate[0]);
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* check to see if the key is already in the target slot */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate,
|
|
privTemplateCount, id, &targetKeyID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
if (targetKeyID != CK_INVALID_HANDLE) {
|
|
/* match found, not an error ... */
|
|
goto done;
|
|
}
|
|
|
|
/* get an NSS representation of our source key */
|
|
sourceKey = PK11_MakePrivKey(sourceSlot, nullKey, PR_FALSE,
|
|
id, sourcePwArg);
|
|
if (sourceKey == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* Load the private key */
|
|
/* generate a random pwitem */
|
|
rv = PK11_GenerateRandom(randomData, sizeof(randomData));
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
pwitem.data = randomData;
|
|
pwitem.len = sizeof(randomData);
|
|
/* fetch the private key encrypted */
|
|
epki = PK11_ExportEncryptedPrivKeyInfo(sourceSlot, algTag, &pwitem,
|
|
sourceKey, 1, sourcePwArg);
|
|
if (epki == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
nickname = PK11_GetObjectNickname(sourceSlot, id);
|
|
/* NULL nickanme is fine (in fact is often normal) */
|
|
if (nickname) {
|
|
nickItem.data = (unsigned char *)nickname;
|
|
nickItem.len = PORT_Strlen(nickname);
|
|
}
|
|
keyUsage = pk11_getPrivateKeyUsage(sourceSlot, id);
|
|
/* pass in the CKA_ID */
|
|
publicValue.data = privTemplate[0].pValue;
|
|
publicValue.len = privTemplate[0].ulValueLen;
|
|
rv = PK11_ImportEncryptedPrivateKeyInfo(targetSlot, epki, &pwitem,
|
|
nickname? &nickItem : NULL , &publicValue,
|
|
PR_TRUE, PR_TRUE, sourceKey->keyType, keyUsage,
|
|
targetPwArg);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
/* make sure it made it */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate,
|
|
privTemplateCount, id, &targetKeyID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
if (targetKeyID == CK_INVALID_HANDLE) {
|
|
/* this time the key should exist */
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* fill in remaining attributes */
|
|
rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
|
|
privCopyTemplate, privCopyTemplateCount);
|
|
done:
|
|
/* make sure the 'key' is cleared */
|
|
PORT_Memset(randomData, 0, sizeof(randomData));
|
|
if (nickname) {
|
|
PORT_Free(nickname);
|
|
}
|
|
if (sourceKey) {
|
|
SECKEY_DestroyPrivateKey(sourceKey);
|
|
}
|
|
if (epki) {
|
|
SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
|
|
}
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Secret Keys
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* we need to find a unique CKA_ID.
|
|
* The basic idea is to just increment the lowest byte.
|
|
* This code also handles the following corner cases:
|
|
* 1) the single byte overflows. On overflow we increment the next byte up
|
|
* and so forth until we have overflowed the entire CKA_ID.
|
|
* 2) If we overflow the entire CKA_ID we expand it by one byte.
|
|
* 3) the CKA_ID is non-existent, we create a new one with one byte.
|
|
* This means no matter what CKA_ID is passed, the result of this function
|
|
* is always a new CKA_ID, and this function will never return the same
|
|
* CKA_ID the it has returned in the passed.
|
|
*/
|
|
static SECStatus
|
|
pk11_incrementID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
|
|
{
|
|
unsigned char *buf = ptemplate->pValue;
|
|
CK_ULONG len = ptemplate->ulValueLen;
|
|
|
|
if (buf == NULL || len == (CK_ULONG)-1) {
|
|
/* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
|
|
len = 0;
|
|
} else {
|
|
CK_ULONG i;
|
|
|
|
/* walk from the back to front, incrementing
|
|
* the CKA_ID until we no longer have a carry,
|
|
* or have hit the front of the id. */
|
|
for (i=len; i != 0; i--) {
|
|
buf[i-1]++;
|
|
if (buf[i-1] != 0) {
|
|
/* no more carries, the increment is complete */
|
|
return SECSuccess;
|
|
}
|
|
}
|
|
/* we've now overflowed, fall through and expand the CKA_ID by
|
|
* one byte */
|
|
}
|
|
/* if we are here we've run the counter to zero (indicating an overflow).
|
|
* create an CKA_ID that is all zeros, but has one more zero than
|
|
* the previous CKA_ID */
|
|
buf = PORT_ArenaZAlloc(arena, len+1);
|
|
if (buf == NULL) {
|
|
return SECFailure;
|
|
}
|
|
ptemplate->pValue = buf;
|
|
ptemplate->ulValueLen = len+1;
|
|
return SECSuccess;
|
|
}
|
|
|
|
|
|
static CK_FLAGS
|
|
pk11_getSecretKeyFlags(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
|
|
{
|
|
CK_FLAGS flags = 0;
|
|
|
|
if (PK11_HasAttributeSet(slot, id, CKA_UNWRAP, PR_FALSE)) {
|
|
flags |= CKF_UNWRAP;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_WRAP, PR_FALSE)) {
|
|
flags |= CKF_WRAP;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_ENCRYPT, PR_FALSE)) {
|
|
flags |= CKF_ENCRYPT;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_DECRYPT, PR_FALSE)) {
|
|
flags |= CKF_DECRYPT;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) {
|
|
flags |= CKF_DERIVE;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE)) {
|
|
flags |= CKF_SIGN;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE)) {
|
|
flags |= CKF_SIGN_RECOVER;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_VERIFY, PR_FALSE)) {
|
|
flags |= CKF_VERIFY;
|
|
}
|
|
if (PK11_HasAttributeSet(slot, id, CKA_VERIFY_RECOVER, PR_FALSE)) {
|
|
flags |= CKF_VERIFY_RECOVER;
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
static const char testString[] =
|
|
"My Encrytion Test Data (should be at least 32 bytes long)";
|
|
/*
|
|
* merge a secret key,
|
|
*
|
|
* Secret keys may collide by CKA_ID as we merge 2 token. If we collide
|
|
* on the CKA_ID, we need to make sure we are dealing with different keys.
|
|
* The reason for this is it is possible that we've merged this database
|
|
* before, and this key could have been merged already. If the keys are
|
|
* the same, we are done. If they are not, we need to update the CKA_ID of
|
|
* the source key and try again.
|
|
*
|
|
* Once we know we have a unique key to merge in, we use NSS's underlying
|
|
* key Move function which will do a key exchange if necessary to move
|
|
* the key from one token to another. Then we set the CKA_ID and additional
|
|
* pkcs #11 attributes.
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeSecretKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
PK11SymKey *sourceKey = NULL;
|
|
PK11SymKey *targetKey = NULL;
|
|
SECItem *sourceOutput = NULL;
|
|
SECItem *targetOutput = NULL;
|
|
SECItem *param = NULL;
|
|
int blockSize;
|
|
SECItem input;
|
|
CK_OBJECT_HANDLE targetKeyID;
|
|
CK_FLAGS flags;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
CK_MECHANISM_TYPE keyMechType, cryptoMechType;
|
|
CK_KEY_TYPE sourceKeyType, targetKeyType;
|
|
CK_ATTRIBUTE symTemplate[] = {
|
|
{ CKA_ID, NULL, 0 },
|
|
{ CKA_CLASS, NULL, 0 }
|
|
};
|
|
CK_ULONG symTemplateCount = sizeof(symTemplate)/sizeof(symTemplate[0]);
|
|
CK_ATTRIBUTE symCopyTemplate[] = {
|
|
{ CKA_LABEL, NULL, 0 }
|
|
};
|
|
CK_ULONG symCopyTemplateCount =
|
|
sizeof(symCopyTemplate)/sizeof(symCopyTemplate[0]);
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
sourceKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
|
|
if (sourceKeyType == (CK_ULONG) -1) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* get the key mechanism */
|
|
keyMechType = PK11_GetKeyMechanism(sourceKeyType);
|
|
/* get a mechanism suitable to encryption.
|
|
* PK11_GetKeyMechanism returns a mechanism that is unique to the key
|
|
* type. It tries to return encryption/decryption mechanisms, however
|
|
* CKM_DES3_CBC uses and abmiguous keyType, so keyMechType is returned as
|
|
* 'keygen' mechanism. Detect that case here */
|
|
cryptoMechType = keyMechType;
|
|
if ((keyMechType == CKM_DES3_KEY_GEN) ||
|
|
(keyMechType == CKM_DES2_KEY_GEN)) {
|
|
cryptoMechType = CKM_DES3_CBC;
|
|
}
|
|
|
|
sourceKey = PK11_SymKeyFromHandle(sourceSlot, NULL, PK11_OriginDerive,
|
|
keyMechType , id, PR_FALSE, sourcePwArg);
|
|
if (sourceKey == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* check to see a key with the same CKA_ID already exists in
|
|
* the target slot. If it does, then we need to verify if the keys
|
|
* really matches. If they don't import the key with a new CKA_ID
|
|
* value. */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot,
|
|
symTemplate, symTemplateCount, id, &targetKeyID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
/* set up the input test */
|
|
input.data = (unsigned char *)testString;
|
|
blockSize = PK11_GetBlockSize(cryptoMechType, NULL);
|
|
if (blockSize < 0) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
input.len = blockSize;
|
|
if (input.len == 0) {
|
|
input.len = sizeof (testString);
|
|
}
|
|
while (targetKeyID != CK_INVALID_HANDLE) {
|
|
/* test to see if the keys are identical */
|
|
targetKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
|
|
if (targetKeyType == sourceKeyType) {
|
|
/* same keyType - see if it's the same key */
|
|
targetKey = PK11_SymKeyFromHandle(targetSlot, NULL,
|
|
PK11_OriginDerive, keyMechType, targetKeyID, PR_FALSE,
|
|
targetPwArg);
|
|
/* get a parameter if we don't already have one */
|
|
if (!param) {
|
|
param = PK11_GenerateNewParam(cryptoMechType, sourceKey);
|
|
if (param == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
}
|
|
/* use the source key to encrypt a reference */
|
|
if (!sourceOutput) {
|
|
rv = pk11_encrypt(sourceKey, cryptoMechType, param, &input,
|
|
&sourceOutput);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
}
|
|
/* encrypt the reference with the target key */
|
|
rv = pk11_encrypt(targetKey, cryptoMechType, param, &input,
|
|
&targetOutput);
|
|
if (rv == SECSuccess) {
|
|
if (SECITEM_ItemsAreEqual(sourceOutput, targetOutput)) {
|
|
/* they produce the same output, they must be the
|
|
* same key */
|
|
goto done;
|
|
}
|
|
SECITEM_FreeItem(targetOutput, PR_TRUE);
|
|
targetOutput = NULL;
|
|
}
|
|
PK11_FreeSymKey(targetKey);
|
|
targetKey = NULL;
|
|
}
|
|
/* keys aren't equal, update the KEY_ID and look again */
|
|
rv = pk11_incrementID(arena, &symTemplate[0]);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
targetKeyID = pk11_FindObjectByTemplate(targetSlot,
|
|
symTemplate, symTemplateCount);
|
|
}
|
|
|
|
/* we didn't find a matching key, import this one with the new
|
|
* CKAID */
|
|
flags = pk11_getSecretKeyFlags(sourceSlot, id);
|
|
targetKey = PK11_MoveSymKey(targetSlot, PK11_OriginDerive, flags, PR_TRUE,
|
|
sourceKey);
|
|
if (targetKey == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
/* set the key new CKAID */
|
|
rv = pk11_setAttributes(targetSlot, targetKey->objectID, symTemplate, 1);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
/* fill in remaining attributes */
|
|
rv = pk11_copyAttributes(arena, targetSlot, targetKey->objectID,
|
|
sourceSlot, id, symCopyTemplate, symCopyTemplateCount);
|
|
done:
|
|
if (sourceKey) {
|
|
PK11_FreeSymKey(sourceKey);
|
|
}
|
|
if (targetKey) {
|
|
PK11_FreeSymKey(targetKey);
|
|
}
|
|
if (sourceOutput) {
|
|
SECITEM_FreeItem(sourceOutput, PR_TRUE);
|
|
}
|
|
if (targetOutput) {
|
|
SECITEM_FreeItem(targetOutput, PR_TRUE);
|
|
}
|
|
if (param) {
|
|
SECITEM_FreeItem(param, PR_TRUE);
|
|
}
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Public Keys
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Merge public key
|
|
*
|
|
* Use the high level NSS calls to extract the public key and import it
|
|
* into the token. Extra attributes are then copied to the new token.
|
|
*/
|
|
static SECStatus
|
|
pk11_mergePublicKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
SECKEYPublicKey *sourceKey = NULL;
|
|
CK_OBJECT_HANDLE targetKeyID;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
CK_ATTRIBUTE pubTemplate[] = {
|
|
{ CKA_ID, NULL, 0 },
|
|
{ CKA_CLASS, NULL, 0 }
|
|
};
|
|
CK_ULONG pubTemplateCount = sizeof(pubTemplate)/sizeof(pubTemplate[0]);
|
|
CK_ATTRIBUTE pubCopyTemplate[] = {
|
|
{ CKA_ID, NULL, 0 },
|
|
{ CKA_LABEL, NULL, 0 },
|
|
{ CKA_SUBJECT, NULL, 0 }
|
|
};
|
|
CK_ULONG pubCopyTemplateCount =
|
|
sizeof(pubCopyTemplate)/sizeof(pubCopyTemplate[0]);
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
|
|
/* check to see if the key is already in the target slot */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, pubTemplate,
|
|
pubTemplateCount, id, &targetKeyID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
/* Key is already in the target slot */
|
|
if (targetKeyID != CK_INVALID_HANDLE) {
|
|
/* not an error ... */
|
|
goto done;
|
|
}
|
|
|
|
/* fetch an NSS representation of the public key */
|
|
sourceKey = PK11_ExtractPublicKey(sourceSlot, nullKey, id);
|
|
if (sourceKey== NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* load the public key into the target token. */
|
|
targetKeyID = PK11_ImportPublicKey(targetSlot, sourceKey, PR_TRUE);
|
|
if (targetKeyID == CK_INVALID_HANDLE) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* fill in remaining attributes */
|
|
rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
|
|
pubCopyTemplate, pubCopyTemplateCount);
|
|
|
|
|
|
done:
|
|
if (sourceKey) {
|
|
SECKEY_DestroyPublicKey(sourceKey);
|
|
}
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Certificates
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Two copies of the source code for this algorithm exist in NSS.
|
|
* Changes must be made in both copies.
|
|
* The other copy is in sftkdb_resolveConflicts() in softoken/sftkdb.c.
|
|
*/
|
|
static char *
|
|
pk11_IncrementNickname(char *nickname)
|
|
{
|
|
char *newNickname = NULL;
|
|
int end;
|
|
int digit;
|
|
int len = strlen(nickname);
|
|
|
|
/* does nickname end with " #n*" ? */
|
|
for (end = len - 1;
|
|
end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0';
|
|
end--) /* just scan */ ;
|
|
if (len >= 3 &&
|
|
end < (len - 1) /* at least one digit */ &&
|
|
nickname[end] == '#' &&
|
|
nickname[end - 1] == ' ') {
|
|
/* Already has a suitable suffix string */
|
|
} else {
|
|
/* ... append " #2" to the name */
|
|
static const char num2[] = " #2";
|
|
newNickname = PORT_Realloc(nickname, len + sizeof(num2));
|
|
if (newNickname) {
|
|
PORT_Strcat(newNickname, num2);
|
|
} else {
|
|
PORT_Free(nickname);
|
|
}
|
|
return newNickname;
|
|
}
|
|
|
|
for (end = len - 1;
|
|
end >= 0 && (digit = nickname[end]) <= '9' && digit >= '0';
|
|
end--) {
|
|
if (digit < '9') {
|
|
nickname[end]++;
|
|
return nickname;
|
|
}
|
|
nickname[end] = '0';
|
|
}
|
|
|
|
/* we overflowed, insert a new '1' for a carry in front of the number */
|
|
newNickname = PORT_Realloc(nickname, len + 2);
|
|
if (newNickname) {
|
|
newNickname[++end] = '1';
|
|
PORT_Memset(&newNickname[end + 1], '0', len - end);
|
|
newNickname[len + 1] = 0;
|
|
} else {
|
|
PORT_Free(nickname);
|
|
}
|
|
return newNickname;
|
|
}
|
|
|
|
/*
|
|
* merge a certificate object
|
|
*
|
|
* Use the high level NSS calls to extract and import the certificate.
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeCert(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
CERTCertificate *sourceCert = NULL;
|
|
CK_OBJECT_HANDLE targetCertID = CK_INVALID_HANDLE;
|
|
char *nickname = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
PLArenaPool *arena = NULL;
|
|
CK_ATTRIBUTE sourceCKAID = {CKA_ID, NULL, 0};
|
|
CK_ATTRIBUTE targetCKAID = {CKA_ID, NULL, 0};
|
|
SECStatus lrv = SECSuccess;
|
|
int error = SEC_ERROR_LIBRARY_FAILURE;
|
|
|
|
sourceCert = PK11_MakeCertFromHandle(sourceSlot, id, NULL);
|
|
if (sourceCert == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
nickname = PK11_GetObjectNickname(sourceSlot, id);
|
|
|
|
/* The database code will prevent nickname collisions for certs with
|
|
* different subjects. This code will prevent us from getting
|
|
* actual import errors */
|
|
if (nickname) {
|
|
const char *tokenName = PK11_GetTokenName(targetSlot);
|
|
char *tokenNickname = NULL;
|
|
|
|
do {
|
|
tokenNickname = PR_smprintf("%s:%s",tokenName, nickname);
|
|
if (!tokenNickname) {
|
|
break;
|
|
}
|
|
if (!SEC_CertNicknameConflict(tokenNickname,
|
|
&sourceCert->derSubject, CERT_GetDefaultCertDB())) {
|
|
break;
|
|
}
|
|
nickname = pk11_IncrementNickname(nickname);
|
|
if (!nickname) {
|
|
break;
|
|
}
|
|
PR_smprintf_free(tokenNickname);
|
|
} while (1);
|
|
if (tokenNickname) {
|
|
PR_smprintf_free(tokenNickname);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* see if the cert is already there */
|
|
targetCertID = PK11_FindCertInSlot(targetSlot, sourceCert, targetPwArg);
|
|
if (targetCertID == CK_INVALID_HANDLE) {
|
|
/* cert doesn't exist load the cert in. */
|
|
/* OK for the nickname to be NULL, not all certs have nicknames */
|
|
rv = PK11_ImportCert(targetSlot, sourceCert, CK_INVALID_HANDLE,
|
|
nickname, PR_FALSE);
|
|
goto done;
|
|
}
|
|
|
|
/* the cert already exists, see if the nickname and/or CKA_ID need
|
|
* to be updated */
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
/* does our source have a CKA_ID ? */
|
|
rv = PK11_GetAttributes(arena, sourceSlot, id, &sourceCKAID, 1);
|
|
if (rv != SECSuccess) {
|
|
sourceCKAID.ulValueLen = 0;
|
|
}
|
|
|
|
/* if we have a source CKA_ID, see of we need to update the
|
|
* target's CKA_ID */
|
|
if (sourceCKAID.ulValueLen != 0) {
|
|
rv = PK11_GetAttributes(arena, targetSlot, targetCertID,
|
|
&targetCKAID, 1);
|
|
if (rv != SECSuccess) {
|
|
targetCKAID.ulValueLen = 0;
|
|
}
|
|
/* if the target has no CKA_ID, update it from the source */
|
|
if (targetCKAID.ulValueLen == 0) {
|
|
lrv=pk11_setAttributes(targetSlot, targetCertID, &sourceCKAID, 1);
|
|
if (lrv != SECSuccess) {
|
|
error = PORT_GetError();
|
|
}
|
|
}
|
|
}
|
|
rv = SECSuccess;
|
|
|
|
/* now check if we need to update the nickname */
|
|
if (nickname && *nickname) {
|
|
char *targetname;
|
|
targetname = PK11_GetObjectNickname(targetSlot, targetCertID);
|
|
if (!targetname || !*targetname) {
|
|
/* target has no nickname, or it's empty, update it */
|
|
rv = PK11_SetObjectNickname(targetSlot, targetCertID, nickname);
|
|
}
|
|
if (targetname) {
|
|
PORT_Free(targetname);
|
|
}
|
|
}
|
|
|
|
/* restore the error code if CKA_ID failed, but nickname didn't */
|
|
if ((rv == SECSuccess) && (lrv != SECSuccess)) {
|
|
rv = lrv;
|
|
PORT_SetError(error);
|
|
}
|
|
|
|
done:
|
|
if (nickname) {
|
|
PORT_Free(nickname);
|
|
}
|
|
if (sourceCert) {
|
|
CERT_DestroyCertificate(sourceCert);
|
|
}
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Crls
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Use the raw PKCS #11 interface to merge the CRLs.
|
|
*
|
|
* In the case where of collision, choose the newest CRL that is valid.
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeCrl(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
CK_OBJECT_HANDLE targetCrlID;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
CK_ATTRIBUTE crlTemplate[] = {
|
|
{ CKA_SUBJECT, NULL, 0 },
|
|
{ CKA_CLASS, NULL, 0 },
|
|
{ CKA_NSS_KRL, NULL, 0 }
|
|
};
|
|
CK_ULONG crlTemplateCount = sizeof(crlTemplate)/sizeof(crlTemplate[0]);
|
|
CK_ATTRIBUTE crlCopyTemplate[] = {
|
|
{ CKA_CLASS, NULL, 0 },
|
|
{ CKA_TOKEN, NULL, 0 },
|
|
{ CKA_LABEL, NULL, 0 },
|
|
{ CKA_PRIVATE, NULL, 0 },
|
|
{ CKA_MODIFIABLE, NULL, 0 },
|
|
{ CKA_SUBJECT, NULL, 0 },
|
|
{ CKA_NSS_KRL, NULL, 0 },
|
|
{ CKA_NSS_URL, NULL, 0 },
|
|
{ CKA_VALUE, NULL, 0 }
|
|
};
|
|
CK_ULONG crlCopyTemplateCount =
|
|
sizeof(crlCopyTemplate)/sizeof(crlCopyTemplate[0]);
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
/* check to see if the crl is already in the target slot */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, crlTemplate,
|
|
crlTemplateCount, id, &targetCrlID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
if (targetCrlID != CK_INVALID_HANDLE) {
|
|
/* we already have a CRL, check to see which is more up-to-date. */
|
|
goto done;
|
|
}
|
|
|
|
/* load the CRL into the target token. */
|
|
rv = pk11_copyAttributes(arena, targetSlot, targetCrlID, sourceSlot, id,
|
|
crlCopyTemplate, crlCopyTemplateCount);
|
|
done:
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* SMIME objects
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* use the raw PKCS #11 interface to merge the S/MIME records
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeSmime(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
CK_OBJECT_HANDLE targetSmimeID;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
CK_ATTRIBUTE smimeTemplate[] = {
|
|
{ CKA_SUBJECT, NULL, 0 },
|
|
{ CKA_NSS_EMAIL, NULL, 0 },
|
|
{ CKA_CLASS, NULL, 0 },
|
|
};
|
|
CK_ULONG smimeTemplateCount =
|
|
sizeof(smimeTemplate)/sizeof(smimeTemplate[0]);
|
|
CK_ATTRIBUTE smimeCopyTemplate[] = {
|
|
{ CKA_CLASS, NULL, 0 },
|
|
{ CKA_TOKEN, NULL, 0 },
|
|
{ CKA_LABEL, NULL, 0 },
|
|
{ CKA_PRIVATE, NULL, 0 },
|
|
{ CKA_MODIFIABLE, NULL, 0 },
|
|
{ CKA_SUBJECT, NULL, 0 },
|
|
{ CKA_NSS_EMAIL, NULL, 0 },
|
|
{ CKA_NSS_SMIME_TIMESTAMP, NULL, 0 },
|
|
{ CKA_VALUE, NULL, 0 }
|
|
};
|
|
CK_ULONG smimeCopyTemplateCount =
|
|
sizeof(smimeCopyTemplate)/sizeof(smimeCopyTemplate[0]);
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
/* check to see if the crl is already in the target slot */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, smimeTemplate,
|
|
smimeTemplateCount, id, &targetSmimeID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
if (targetSmimeID != CK_INVALID_HANDLE) {
|
|
/* we already have a SMIME record */
|
|
goto done;
|
|
}
|
|
|
|
/* load the SMime Record into the target token. */
|
|
rv = pk11_copyAttributes(arena, targetSlot, targetSmimeID, sourceSlot, id,
|
|
smimeCopyTemplate, smimeCopyTemplateCount);
|
|
done:
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Trust Objects
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
/*
|
|
* decide which trust record entry wins. PR_TRUE (source) or PR_FALSE (target)
|
|
*/
|
|
#define USE_TARGET PR_FALSE
|
|
#define USE_SOURCE PR_TRUE
|
|
PRBool
|
|
pk11_mergeTrustEntry(CK_ATTRIBUTE *target, CK_ATTRIBUTE *source)
|
|
{
|
|
CK_ULONG targetTrust = (target->ulValueLen == sizeof (CK_LONG)) ?
|
|
*(CK_ULONG *)target->pValue : CKT_NSS_TRUST_UNKNOWN;
|
|
CK_ULONG sourceTrust = (source->ulValueLen == sizeof (CK_LONG)) ?
|
|
*(CK_ULONG *)source->pValue : CKT_NSS_TRUST_UNKNOWN;
|
|
|
|
/*
|
|
* Examine a single entry and deside if the source or target version
|
|
* should win out. When all the entries have been checked, if there is
|
|
* any case we need to update, we will write the whole source record
|
|
* to the target database. That means for each individual record, if the
|
|
* target wins, we need to update the source (in case later we have a
|
|
* case where the source wins). If the source wins, it already
|
|
*/
|
|
if (sourceTrust == targetTrust) {
|
|
return USE_TARGET; /* which equates to 'do nothing' */
|
|
}
|
|
|
|
if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
|
|
return USE_TARGET;
|
|
}
|
|
|
|
/* target has no idea, use the source's idea of the trust value */
|
|
if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
|
|
/* source overwrites the target */
|
|
return USE_SOURCE;
|
|
}
|
|
|
|
/* so both the target and the source have some idea of what this
|
|
* trust attribute should be, and neither agree exactly.
|
|
* At this point, we prefer 'hard' attributes over 'soft' ones.
|
|
* 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
|
|
* CKT_NSS_UNTRUTED. Soft ones are ones which don't change the
|
|
* actual trust of the cert (CKT_MUST_VERIFY, CKT_NSS_VALID,
|
|
* CKT_NSS_VALID_DELEGATOR).
|
|
*/
|
|
if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST)
|
|
|| (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
|
|
return USE_TARGET;
|
|
}
|
|
if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST)
|
|
|| (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
|
|
/* source overrites the target */
|
|
return USE_SOURCE;
|
|
}
|
|
|
|
/* both have hard attributes, we have a conflict, let the target win. */
|
|
return USE_TARGET;
|
|
}
|
|
/*
|
|
* use the raw PKCS #11 interface to merge the S/MIME records
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeTrust(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
CK_OBJECT_HANDLE targetTrustID;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
int error = 0;
|
|
CK_ATTRIBUTE trustTemplate[] = {
|
|
{ CKA_ISSUER, NULL, 0 },
|
|
{ CKA_SERIAL_NUMBER, NULL, 0 },
|
|
{ CKA_CLASS, NULL, 0 },
|
|
};
|
|
CK_ULONG trustTemplateCount =
|
|
sizeof(trustTemplate)/sizeof(trustTemplate[0]);
|
|
CK_ATTRIBUTE trustCopyTemplate[] = {
|
|
{ CKA_CLASS, NULL, 0 },
|
|
{ CKA_TOKEN, NULL, 0 },
|
|
{ CKA_LABEL, NULL, 0 },
|
|
{ CKA_PRIVATE, NULL, 0 },
|
|
{ CKA_MODIFIABLE, NULL, 0 },
|
|
{ CKA_ISSUER, NULL, 0},
|
|
{ CKA_SERIAL_NUMBER, NULL, 0},
|
|
{ CKA_CERT_SHA1_HASH, NULL, 0 },
|
|
{ CKA_CERT_MD5_HASH, NULL, 0 },
|
|
{ CKA_TRUST_SERVER_AUTH, NULL, 0 },
|
|
{ CKA_TRUST_CLIENT_AUTH, NULL, 0 },
|
|
{ CKA_TRUST_CODE_SIGNING, NULL, 0 },
|
|
{ CKA_TRUST_EMAIL_PROTECTION, NULL, 0 },
|
|
{ CKA_TRUST_STEP_UP_APPROVED, NULL, 0 }
|
|
};
|
|
CK_ULONG trustCopyTemplateCount =
|
|
sizeof(trustCopyTemplate)/sizeof(trustCopyTemplate[0]);
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
/* check to see if the crl is already in the target slot */
|
|
rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, trustTemplate,
|
|
trustTemplateCount, id, &targetTrustID);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
if (targetTrustID != CK_INVALID_HANDLE) {
|
|
/* a matching trust record already exists, merge it in */
|
|
CK_ATTRIBUTE_TYPE trustAttrs[] = {
|
|
CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
|
|
CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
|
|
CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
|
|
CKA_TRUST_TIME_STAMPING
|
|
};
|
|
CK_ULONG trustAttrsCount =
|
|
sizeof(trustAttrs)/sizeof(trustAttrs[0]);
|
|
|
|
CK_ULONG i;
|
|
CK_ATTRIBUTE targetTemplate, sourceTemplate;
|
|
|
|
/* existing trust record, merge the two together */
|
|
for (i=0; i < trustAttrsCount; i++) {
|
|
targetTemplate.type = sourceTemplate.type = trustAttrs[i];
|
|
targetTemplate.pValue = sourceTemplate.pValue = NULL;
|
|
targetTemplate.ulValueLen = sourceTemplate.ulValueLen = 0;
|
|
PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
|
|
PK11_GetAttributes(arena, targetSlot, targetTrustID,
|
|
&targetTemplate, 1);
|
|
if (pk11_mergeTrustEntry(&targetTemplate, &sourceTemplate)) {
|
|
/* source wins, write out the source attribute to the target */
|
|
SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID,
|
|
&sourceTemplate, 1);
|
|
if (lrv != SECSuccess) {
|
|
rv = SECFailure;
|
|
error = PORT_GetError();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* handle step */
|
|
sourceTemplate.type = CKA_TRUST_STEP_UP_APPROVED;
|
|
sourceTemplate.pValue = NULL;
|
|
sourceTemplate.ulValueLen = 0;
|
|
|
|
/* if the source has steup set, then set it in the target */
|
|
PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
|
|
if ((sourceTemplate.ulValueLen == sizeof(CK_BBOOL)) &&
|
|
(sourceTemplate.pValue) &&
|
|
(*(CK_BBOOL *)sourceTemplate.pValue == CK_TRUE)) {
|
|
SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID,
|
|
&sourceTemplate, 1);
|
|
if (lrv != SECSuccess) {
|
|
rv = SECFailure;
|
|
error = PORT_GetError();
|
|
}
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/* load the new trust Record into the target token. */
|
|
rv = pk11_copyAttributes(arena, targetSlot, targetTrustID, sourceSlot, id,
|
|
trustCopyTemplate, trustCopyTemplateCount);
|
|
done:
|
|
if (arena) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
}
|
|
|
|
/* restore the error code */
|
|
if (rv == SECFailure && error) {
|
|
PORT_SetError(error);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Central merge code
|
|
*
|
|
*************************************************************************/
|
|
/*
|
|
* merge a single object from sourceToken to targetToken
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeObject(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
|
|
CK_OBJECT_CLASS objClass;
|
|
|
|
|
|
objClass = PK11_ReadULongAttribute(sourceSlot, id, CKA_CLASS);
|
|
if (objClass == (CK_ULONG) -1) {
|
|
PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE );
|
|
return SECFailure;
|
|
}
|
|
|
|
switch (objClass) {
|
|
case CKO_CERTIFICATE:
|
|
return pk11_mergeCert(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
case CKO_NSS_TRUST:
|
|
return pk11_mergeTrust(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
case CKO_PUBLIC_KEY:
|
|
return pk11_mergePublicKey(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
case CKO_PRIVATE_KEY:
|
|
return pk11_mergePrivateKey(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
case CKO_SECRET_KEY:
|
|
return pk11_mergeSecretKey(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
case CKO_NSS_CRL:
|
|
return pk11_mergeCrl(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
case CKO_NSS_SMIME:
|
|
return pk11_mergeSmime(targetSlot, sourceSlot, id,
|
|
targetPwArg, sourcePwArg);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE );
|
|
return SECFailure;
|
|
}
|
|
|
|
PK11MergeLogNode *
|
|
pk11_newMergeLogNode(PLArenaPool *arena,
|
|
PK11SlotInfo *slot, CK_OBJECT_HANDLE id, int error)
|
|
{
|
|
PK11MergeLogNode *newLog;
|
|
PK11GenericObject *obj;
|
|
|
|
newLog = PORT_ArenaZNew(arena, PK11MergeLogNode);
|
|
if (newLog == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
obj = PORT_ArenaZNew(arena, PK11GenericObject);
|
|
if ( !obj ) {
|
|
return NULL;
|
|
}
|
|
|
|
/* initialize it */
|
|
obj->slot = slot;
|
|
obj->objectID = id;
|
|
|
|
newLog->object= obj;
|
|
newLog->error = error;
|
|
return newLog;
|
|
}
|
|
|
|
/*
|
|
* walk down each entry and merge it. keep track of the errors in the log
|
|
*/
|
|
static SECStatus
|
|
pk11_mergeByObjectIDs(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
CK_OBJECT_HANDLE *objectIDs, int count,
|
|
PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
SECStatus rv = SECSuccess;
|
|
int error, i;
|
|
|
|
for (i=0; i < count; i++) {
|
|
/* try to update the entire database. On failure, keep going,
|
|
* but remember the error to report back to the caller */
|
|
SECStatus lrv;
|
|
PK11MergeLogNode *newLog;
|
|
|
|
lrv= pk11_mergeObject(targetSlot, sourceSlot, objectIDs[i],
|
|
targetPwArg, sourcePwArg);
|
|
if (lrv == SECSuccess) {
|
|
/* merged with no problem, go to next object */
|
|
continue;
|
|
}
|
|
|
|
/* remember that we failed and why */
|
|
rv = SECFailure;
|
|
error = PORT_GetError();
|
|
|
|
/* log the errors */
|
|
if (!log) {
|
|
/* not logging, go to next entry */
|
|
continue;
|
|
}
|
|
newLog = pk11_newMergeLogNode(log->arena, sourceSlot,
|
|
objectIDs[i], error);
|
|
if (!newLog) {
|
|
/* failed to allocate entry, just keep going */
|
|
continue;
|
|
}
|
|
|
|
/* link in the errorlog entry */
|
|
newLog->next = NULL;
|
|
if (log->tail) {
|
|
log->tail->next = newLog;
|
|
} else {
|
|
log->head = newLog;
|
|
}
|
|
newLog->prev = log->tail;
|
|
log->tail = newLog;
|
|
}
|
|
|
|
/* restore the last error code */
|
|
if (rv != SECSuccess) {
|
|
PORT_SetError(error);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Merge all the records in sourceSlot that aren't in targetSlot
|
|
*
|
|
* This function will return failure if not all the objects
|
|
* successfully merged.
|
|
*
|
|
* Applications can pass in an optional error log which will record
|
|
* each failing object and why it failed to import. PK11MergeLog
|
|
* is modelled after the CERTVerifyLog.
|
|
*/
|
|
SECStatus
|
|
PK11_MergeTokens(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
|
|
PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
|
|
{
|
|
SECStatus rv = SECSuccess, lrv = SECSuccess;
|
|
int error, count = 0;
|
|
CK_ATTRIBUTE search[2];
|
|
CK_OBJECT_HANDLE *objectIDs = NULL;
|
|
CK_BBOOL ck_true = CK_TRUE;
|
|
CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY;
|
|
|
|
PK11_SETATTRS(&search[0], CKA_TOKEN, &ck_true, sizeof(ck_true));
|
|
PK11_SETATTRS(&search[1], CKA_CLASS, &privKey, sizeof(privKey));
|
|
/*
|
|
* make sure both tokens are already authenticated if need be.
|
|
*/
|
|
rv = PK11_Authenticate(targetSlot, PR_TRUE, targetPwArg);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
rv = PK11_Authenticate(sourceSlot, PR_TRUE, sourcePwArg);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* turns out the old DB's are rather fragile if the private keys aren't
|
|
* merged in first, so do the private keys explicity. */
|
|
objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 2, &count);
|
|
if (objectIDs) {
|
|
lrv = pk11_mergeByObjectIDs(targetSlot, sourceSlot,
|
|
objectIDs, count, log,
|
|
targetPwArg, sourcePwArg);
|
|
if (lrv != SECSuccess) {
|
|
error = PORT_GetError();
|
|
}
|
|
PORT_Free(objectIDs);
|
|
count = 0;
|
|
}
|
|
|
|
/* now do the rest (NOTE: this will repeat the private keys, but
|
|
* that shouldnt' be an issue as we will notice they are already
|
|
* merged in */
|
|
objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 1, &count);
|
|
if (!objectIDs) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
rv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, objectIDs, count, log,
|
|
targetPwArg, sourcePwArg);
|
|
if (rv == SECSuccess) {
|
|
/* if private keys failed, but the rest succeeded, be sure to let
|
|
* the caller know that private keys failed and why.
|
|
* NOTE: this is highly unlikely since the same keys that failed
|
|
* in the previous merge call will most likely fail in this one */
|
|
if (lrv != SECSuccess) {
|
|
rv = lrv;
|
|
PORT_SetError(error);
|
|
}
|
|
}
|
|
|
|
loser:
|
|
if (objectIDs) {
|
|
PORT_Free(objectIDs);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
PK11MergeLog *
|
|
PK11_CreateMergeLog(void)
|
|
{
|
|
PLArenaPool *arena;
|
|
PK11MergeLog *log;
|
|
|
|
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
log = PORT_ArenaZNew(arena, PK11MergeLog);
|
|
if (log == NULL) {
|
|
PORT_FreeArena(arena,PR_FALSE);
|
|
return NULL;
|
|
}
|
|
log->arena = arena;
|
|
log->version = 1;
|
|
return log;
|
|
}
|
|
|
|
void
|
|
PK11_DestroyMergeLog(PK11MergeLog *log)
|
|
{
|
|
if (log && log->arena) {
|
|
PORT_FreeArena(log->arena, PR_FALSE);
|
|
}
|
|
}
|