diff nss/lib/pk11wrap/pk11merge.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/pk11wrap/pk11merge.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,1419 @@
+/* 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;
+
+
+    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);
+    }
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)