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