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