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