Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * Merge the source token into the target token. |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | #include "secmod.h" |
michael@0 | 10 | #include "secmodi.h" |
michael@0 | 11 | #include "secmodti.h" |
michael@0 | 12 | #include "pk11pub.h" |
michael@0 | 13 | #include "pk11priv.h" |
michael@0 | 14 | #include "pkcs11.h" |
michael@0 | 15 | #include "seccomon.h" |
michael@0 | 16 | #include "secerr.h" |
michael@0 | 17 | #include "keyhi.h" |
michael@0 | 18 | #include "hasht.h" |
michael@0 | 19 | #include "cert.h" |
michael@0 | 20 | #include "certdb.h" |
michael@0 | 21 | |
michael@0 | 22 | /************************************************************************* |
michael@0 | 23 | * |
michael@0 | 24 | * short utilities to aid in the merge |
michael@0 | 25 | * |
michael@0 | 26 | *************************************************************************/ |
michael@0 | 27 | |
michael@0 | 28 | /* |
michael@0 | 29 | * write a bunch of attributes out to an existing object. |
michael@0 | 30 | */ |
michael@0 | 31 | static SECStatus |
michael@0 | 32 | pk11_setAttributes(PK11SlotInfo *slot, CK_OBJECT_HANDLE id, |
michael@0 | 33 | CK_ATTRIBUTE *setTemplate, CK_ULONG setTemplCount) |
michael@0 | 34 | { |
michael@0 | 35 | CK_RV crv; |
michael@0 | 36 | CK_SESSION_HANDLE rwsession; |
michael@0 | 37 | |
michael@0 | 38 | rwsession = PK11_GetRWSession(slot); |
michael@0 | 39 | if (rwsession == CK_INVALID_SESSION) { |
michael@0 | 40 | PORT_SetError(SEC_ERROR_BAD_DATA); |
michael@0 | 41 | return SECFailure; |
michael@0 | 42 | } |
michael@0 | 43 | crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id, |
michael@0 | 44 | setTemplate, setTemplCount); |
michael@0 | 45 | PK11_RestoreROSession(slot, rwsession); |
michael@0 | 46 | if (crv != CKR_OK) { |
michael@0 | 47 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 48 | return SECFailure; |
michael@0 | 49 | } |
michael@0 | 50 | return SECSuccess; |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | |
michael@0 | 54 | /* |
michael@0 | 55 | * copy a template of attributes from a source object to a target object. |
michael@0 | 56 | * if target object is not given, create it. |
michael@0 | 57 | */ |
michael@0 | 58 | static SECStatus |
michael@0 | 59 | pk11_copyAttributes(PLArenaPool *arena, |
michael@0 | 60 | PK11SlotInfo *targetSlot, CK_OBJECT_HANDLE targetID, |
michael@0 | 61 | PK11SlotInfo *sourceSlot, CK_OBJECT_HANDLE sourceID, |
michael@0 | 62 | CK_ATTRIBUTE *copyTemplate, CK_ULONG copyTemplateCount) |
michael@0 | 63 | { |
michael@0 | 64 | SECStatus rv = PK11_GetAttributes(arena, sourceSlot, sourceID, |
michael@0 | 65 | copyTemplate, copyTemplateCount); |
michael@0 | 66 | if (rv != SECSuccess) { |
michael@0 | 67 | return rv; |
michael@0 | 68 | } |
michael@0 | 69 | if (targetID == CK_INVALID_HANDLE) { |
michael@0 | 70 | /* we need to create the object */ |
michael@0 | 71 | rv = PK11_CreateNewObject(targetSlot, CK_INVALID_SESSION, |
michael@0 | 72 | copyTemplate, copyTemplateCount, PR_TRUE, &targetID); |
michael@0 | 73 | } else { |
michael@0 | 74 | /* update the existing object with the new attributes */ |
michael@0 | 75 | rv = pk11_setAttributes(targetSlot, targetID, |
michael@0 | 76 | copyTemplate, copyTemplateCount); |
michael@0 | 77 | } |
michael@0 | 78 | return rv; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | /* |
michael@0 | 82 | * look for a matching object across tokens. |
michael@0 | 83 | */ |
michael@0 | 84 | static SECStatus |
michael@0 | 85 | pk11_matchAcrossTokens(PLArenaPool *arena, PK11SlotInfo *targetSlot, |
michael@0 | 86 | PK11SlotInfo *sourceSlot, |
michael@0 | 87 | CK_ATTRIBUTE *template, CK_ULONG tsize, |
michael@0 | 88 | CK_OBJECT_HANDLE id, CK_OBJECT_HANDLE *peer) |
michael@0 | 89 | { |
michael@0 | 90 | |
michael@0 | 91 | CK_RV crv; |
michael@0 | 92 | *peer = CK_INVALID_HANDLE; |
michael@0 | 93 | |
michael@0 | 94 | crv = PK11_GetAttributes(arena, sourceSlot, id, template, tsize); |
michael@0 | 95 | if (crv != CKR_OK) { |
michael@0 | 96 | PORT_SetError( PK11_MapError(crv) ); |
michael@0 | 97 | goto loser; |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | if (template[0].ulValueLen == -1) { |
michael@0 | 101 | crv = CKR_ATTRIBUTE_TYPE_INVALID; |
michael@0 | 102 | PORT_SetError( PK11_MapError(crv) ); |
michael@0 | 103 | goto loser; |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | *peer = pk11_FindObjectByTemplate(targetSlot, template, tsize); |
michael@0 | 107 | return SECSuccess; |
michael@0 | 108 | |
michael@0 | 109 | loser: |
michael@0 | 110 | return SECFailure; |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | /* |
michael@0 | 114 | * Encrypt using key and parameters |
michael@0 | 115 | */ |
michael@0 | 116 | SECStatus |
michael@0 | 117 | pk11_encrypt(PK11SymKey *symKey, CK_MECHANISM_TYPE mechType, SECItem *param, |
michael@0 | 118 | SECItem *input, SECItem **output) |
michael@0 | 119 | { |
michael@0 | 120 | PK11Context *ctxt = NULL; |
michael@0 | 121 | SECStatus rv = SECSuccess; |
michael@0 | 122 | |
michael@0 | 123 | if (*output) { |
michael@0 | 124 | SECITEM_FreeItem(*output,PR_TRUE); |
michael@0 | 125 | } |
michael@0 | 126 | *output = SECITEM_AllocItem(NULL, NULL, input->len+20 /*slop*/); |
michael@0 | 127 | if (!*output) { |
michael@0 | 128 | rv = SECFailure; |
michael@0 | 129 | goto done; |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | ctxt = PK11_CreateContextBySymKey(mechType, CKA_ENCRYPT, symKey, param); |
michael@0 | 133 | if (ctxt == NULL) { |
michael@0 | 134 | rv = SECFailure; |
michael@0 | 135 | goto done; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | rv = PK11_CipherOp(ctxt, (*output)->data, |
michael@0 | 139 | (int *)&((*output)->len), |
michael@0 | 140 | (*output)->len, input->data, input->len); |
michael@0 | 141 | |
michael@0 | 142 | done: |
michael@0 | 143 | if (ctxt) { |
michael@0 | 144 | PK11_Finalize(ctxt); |
michael@0 | 145 | PK11_DestroyContext(ctxt,PR_TRUE); |
michael@0 | 146 | } |
michael@0 | 147 | if (rv != SECSuccess) { |
michael@0 | 148 | if (*output) { |
michael@0 | 149 | SECITEM_FreeItem(*output, PR_TRUE); |
michael@0 | 150 | *output = NULL; |
michael@0 | 151 | } |
michael@0 | 152 | } |
michael@0 | 153 | return rv; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | |
michael@0 | 157 | |
michael@0 | 158 | /************************************************************************* |
michael@0 | 159 | * |
michael@0 | 160 | * Private Keys |
michael@0 | 161 | * |
michael@0 | 162 | *************************************************************************/ |
michael@0 | 163 | |
michael@0 | 164 | /* |
michael@0 | 165 | * Fetch the key usage based on the pkcs #11 flags |
michael@0 | 166 | */ |
michael@0 | 167 | unsigned int |
michael@0 | 168 | pk11_getPrivateKeyUsage(PK11SlotInfo *slot, CK_OBJECT_HANDLE id) |
michael@0 | 169 | { |
michael@0 | 170 | unsigned int usage = 0; |
michael@0 | 171 | |
michael@0 | 172 | if ((PK11_HasAttributeSet(slot, id, CKA_UNWRAP,PR_FALSE) || |
michael@0 | 173 | PK11_HasAttributeSet(slot,id, CKA_DECRYPT,PR_FALSE))) { |
michael@0 | 174 | usage |= KU_KEY_ENCIPHERMENT; |
michael@0 | 175 | } |
michael@0 | 176 | if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) { |
michael@0 | 177 | usage |= KU_KEY_AGREEMENT; |
michael@0 | 178 | } |
michael@0 | 179 | if ((PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE) || |
michael@0 | 180 | PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE))) { |
michael@0 | 181 | usage |= KU_DIGITAL_SIGNATURE; |
michael@0 | 182 | } |
michael@0 | 183 | return usage; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | |
michael@0 | 187 | /* |
michael@0 | 188 | * merge a private key, |
michael@0 | 189 | * |
michael@0 | 190 | * Private keys are merged using PBE wrapped keys with a random |
michael@0 | 191 | * value as the 'password'. Once the base key is moved, The remaining |
michael@0 | 192 | * attributes (SUBJECT) is copied. |
michael@0 | 193 | */ |
michael@0 | 194 | static SECStatus |
michael@0 | 195 | pk11_mergePrivateKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 196 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 197 | { |
michael@0 | 198 | SECKEYPrivateKey *sourceKey = NULL; |
michael@0 | 199 | CK_OBJECT_HANDLE targetKeyID; |
michael@0 | 200 | SECKEYEncryptedPrivateKeyInfo *epki = NULL; |
michael@0 | 201 | char *nickname = NULL; |
michael@0 | 202 | SECItem nickItem; |
michael@0 | 203 | SECItem pwitem; |
michael@0 | 204 | SECItem publicValue; |
michael@0 | 205 | PLArenaPool *arena = NULL; |
michael@0 | 206 | SECStatus rv = SECSuccess; |
michael@0 | 207 | unsigned int keyUsage; |
michael@0 | 208 | unsigned char randomData[SHA1_LENGTH]; |
michael@0 | 209 | SECOidTag algTag = SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC; |
michael@0 | 210 | CK_ATTRIBUTE privTemplate[] = { |
michael@0 | 211 | { CKA_ID, NULL, 0 }, |
michael@0 | 212 | { CKA_CLASS, NULL, 0 } |
michael@0 | 213 | }; |
michael@0 | 214 | CK_ULONG privTemplateCount = sizeof(privTemplate)/sizeof(privTemplate[0]); |
michael@0 | 215 | CK_ATTRIBUTE privCopyTemplate[] = { |
michael@0 | 216 | { CKA_SUBJECT, NULL, 0 } |
michael@0 | 217 | }; |
michael@0 | 218 | CK_ULONG privCopyTemplateCount = |
michael@0 | 219 | sizeof(privCopyTemplate)/sizeof(privCopyTemplate[0]); |
michael@0 | 220 | |
michael@0 | 221 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 222 | if (arena == NULL) { |
michael@0 | 223 | rv = SECFailure; |
michael@0 | 224 | goto done; |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | /* check to see if the key is already in the target slot */ |
michael@0 | 228 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate, |
michael@0 | 229 | privTemplateCount, id, &targetKeyID); |
michael@0 | 230 | if (rv != SECSuccess) { |
michael@0 | 231 | goto done; |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | if (targetKeyID != CK_INVALID_HANDLE) { |
michael@0 | 235 | /* match found, not an error ... */ |
michael@0 | 236 | goto done; |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | /* get an NSS representation of our source key */ |
michael@0 | 240 | sourceKey = PK11_MakePrivKey(sourceSlot, nullKey, PR_FALSE, |
michael@0 | 241 | id, sourcePwArg); |
michael@0 | 242 | if (sourceKey == NULL) { |
michael@0 | 243 | rv = SECFailure; |
michael@0 | 244 | goto done; |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | /* Load the private key */ |
michael@0 | 248 | /* generate a random pwitem */ |
michael@0 | 249 | rv = PK11_GenerateRandom(randomData, sizeof(randomData)); |
michael@0 | 250 | if (rv != SECSuccess) { |
michael@0 | 251 | goto done; |
michael@0 | 252 | } |
michael@0 | 253 | pwitem.data = randomData; |
michael@0 | 254 | pwitem.len = sizeof(randomData); |
michael@0 | 255 | /* fetch the private key encrypted */ |
michael@0 | 256 | epki = PK11_ExportEncryptedPrivKeyInfo(sourceSlot, algTag, &pwitem, |
michael@0 | 257 | sourceKey, 1, sourcePwArg); |
michael@0 | 258 | if (epki == NULL) { |
michael@0 | 259 | rv = SECFailure; |
michael@0 | 260 | goto done; |
michael@0 | 261 | } |
michael@0 | 262 | nickname = PK11_GetObjectNickname(sourceSlot, id); |
michael@0 | 263 | /* NULL nickanme is fine (in fact is often normal) */ |
michael@0 | 264 | if (nickname) { |
michael@0 | 265 | nickItem.data = (unsigned char *)nickname; |
michael@0 | 266 | nickItem.len = PORT_Strlen(nickname); |
michael@0 | 267 | } |
michael@0 | 268 | keyUsage = pk11_getPrivateKeyUsage(sourceSlot, id); |
michael@0 | 269 | /* pass in the CKA_ID */ |
michael@0 | 270 | publicValue.data = privTemplate[0].pValue; |
michael@0 | 271 | publicValue.len = privTemplate[0].ulValueLen; |
michael@0 | 272 | rv = PK11_ImportEncryptedPrivateKeyInfo(targetSlot, epki, &pwitem, |
michael@0 | 273 | nickname? &nickItem : NULL , &publicValue, |
michael@0 | 274 | PR_TRUE, PR_TRUE, sourceKey->keyType, keyUsage, |
michael@0 | 275 | targetPwArg); |
michael@0 | 276 | if (rv != SECSuccess) { |
michael@0 | 277 | goto done; |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | /* make sure it made it */ |
michael@0 | 281 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate, |
michael@0 | 282 | privTemplateCount, id, &targetKeyID); |
michael@0 | 283 | if (rv != SECSuccess) { |
michael@0 | 284 | goto done; |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | if (targetKeyID == CK_INVALID_HANDLE) { |
michael@0 | 288 | /* this time the key should exist */ |
michael@0 | 289 | rv = SECFailure; |
michael@0 | 290 | goto done; |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | /* fill in remaining attributes */ |
michael@0 | 294 | rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id, |
michael@0 | 295 | privCopyTemplate, privCopyTemplateCount); |
michael@0 | 296 | done: |
michael@0 | 297 | /* make sure the 'key' is cleared */ |
michael@0 | 298 | PORT_Memset(randomData, 0, sizeof(randomData)); |
michael@0 | 299 | if (nickname) { |
michael@0 | 300 | PORT_Free(nickname); |
michael@0 | 301 | } |
michael@0 | 302 | if (sourceKey) { |
michael@0 | 303 | SECKEY_DestroyPrivateKey(sourceKey); |
michael@0 | 304 | } |
michael@0 | 305 | if (epki) { |
michael@0 | 306 | SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); |
michael@0 | 307 | } |
michael@0 | 308 | if (arena) { |
michael@0 | 309 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 310 | } |
michael@0 | 311 | return rv; |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | |
michael@0 | 315 | /************************************************************************* |
michael@0 | 316 | * |
michael@0 | 317 | * Secret Keys |
michael@0 | 318 | * |
michael@0 | 319 | *************************************************************************/ |
michael@0 | 320 | |
michael@0 | 321 | /* |
michael@0 | 322 | * we need to find a unique CKA_ID. |
michael@0 | 323 | * The basic idea is to just increment the lowest byte. |
michael@0 | 324 | * This code also handles the following corner cases: |
michael@0 | 325 | * 1) the single byte overflows. On overflow we increment the next byte up |
michael@0 | 326 | * and so forth until we have overflowed the entire CKA_ID. |
michael@0 | 327 | * 2) If we overflow the entire CKA_ID we expand it by one byte. |
michael@0 | 328 | * 3) the CKA_ID is non-existent, we create a new one with one byte. |
michael@0 | 329 | * This means no matter what CKA_ID is passed, the result of this function |
michael@0 | 330 | * is always a new CKA_ID, and this function will never return the same |
michael@0 | 331 | * CKA_ID the it has returned in the passed. |
michael@0 | 332 | */ |
michael@0 | 333 | static SECStatus |
michael@0 | 334 | pk11_incrementID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate) |
michael@0 | 335 | { |
michael@0 | 336 | unsigned char *buf = ptemplate->pValue; |
michael@0 | 337 | CK_ULONG len = ptemplate->ulValueLen; |
michael@0 | 338 | |
michael@0 | 339 | if (buf == NULL || len == (CK_ULONG)-1) { |
michael@0 | 340 | /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */ |
michael@0 | 341 | len = 0; |
michael@0 | 342 | } else { |
michael@0 | 343 | CK_ULONG i; |
michael@0 | 344 | |
michael@0 | 345 | /* walk from the back to front, incrementing |
michael@0 | 346 | * the CKA_ID until we no longer have a carry, |
michael@0 | 347 | * or have hit the front of the id. */ |
michael@0 | 348 | for (i=len; i != 0; i--) { |
michael@0 | 349 | buf[i-1]++; |
michael@0 | 350 | if (buf[i-1] != 0) { |
michael@0 | 351 | /* no more carries, the increment is complete */ |
michael@0 | 352 | return SECSuccess; |
michael@0 | 353 | } |
michael@0 | 354 | } |
michael@0 | 355 | /* we've now overflowed, fall through and expand the CKA_ID by |
michael@0 | 356 | * one byte */ |
michael@0 | 357 | } |
michael@0 | 358 | /* if we are here we've run the counter to zero (indicating an overflow). |
michael@0 | 359 | * create an CKA_ID that is all zeros, but has one more zero than |
michael@0 | 360 | * the previous CKA_ID */ |
michael@0 | 361 | buf = PORT_ArenaZAlloc(arena, len+1); |
michael@0 | 362 | if (buf == NULL) { |
michael@0 | 363 | return SECFailure; |
michael@0 | 364 | } |
michael@0 | 365 | ptemplate->pValue = buf; |
michael@0 | 366 | ptemplate->ulValueLen = len+1; |
michael@0 | 367 | return SECSuccess; |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | |
michael@0 | 371 | static CK_FLAGS |
michael@0 | 372 | pk11_getSecretKeyFlags(PK11SlotInfo *slot, CK_OBJECT_HANDLE id) |
michael@0 | 373 | { |
michael@0 | 374 | CK_FLAGS flags = 0; |
michael@0 | 375 | |
michael@0 | 376 | if (PK11_HasAttributeSet(slot, id, CKA_UNWRAP, PR_FALSE)) { |
michael@0 | 377 | flags |= CKF_UNWRAP; |
michael@0 | 378 | } |
michael@0 | 379 | if (PK11_HasAttributeSet(slot, id, CKA_WRAP, PR_FALSE)) { |
michael@0 | 380 | flags |= CKF_WRAP; |
michael@0 | 381 | } |
michael@0 | 382 | if (PK11_HasAttributeSet(slot, id, CKA_ENCRYPT, PR_FALSE)) { |
michael@0 | 383 | flags |= CKF_ENCRYPT; |
michael@0 | 384 | } |
michael@0 | 385 | if (PK11_HasAttributeSet(slot, id, CKA_DECRYPT, PR_FALSE)) { |
michael@0 | 386 | flags |= CKF_DECRYPT; |
michael@0 | 387 | } |
michael@0 | 388 | if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) { |
michael@0 | 389 | flags |= CKF_DERIVE; |
michael@0 | 390 | } |
michael@0 | 391 | if (PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE)) { |
michael@0 | 392 | flags |= CKF_SIGN; |
michael@0 | 393 | } |
michael@0 | 394 | if (PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE)) { |
michael@0 | 395 | flags |= CKF_SIGN_RECOVER; |
michael@0 | 396 | } |
michael@0 | 397 | if (PK11_HasAttributeSet(slot, id, CKA_VERIFY, PR_FALSE)) { |
michael@0 | 398 | flags |= CKF_VERIFY; |
michael@0 | 399 | } |
michael@0 | 400 | if (PK11_HasAttributeSet(slot, id, CKA_VERIFY_RECOVER, PR_FALSE)) { |
michael@0 | 401 | flags |= CKF_VERIFY_RECOVER; |
michael@0 | 402 | } |
michael@0 | 403 | return flags; |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | static const char testString[] = |
michael@0 | 407 | "My Encrytion Test Data (should be at least 32 bytes long)"; |
michael@0 | 408 | /* |
michael@0 | 409 | * merge a secret key, |
michael@0 | 410 | * |
michael@0 | 411 | * Secret keys may collide by CKA_ID as we merge 2 token. If we collide |
michael@0 | 412 | * on the CKA_ID, we need to make sure we are dealing with different keys. |
michael@0 | 413 | * The reason for this is it is possible that we've merged this database |
michael@0 | 414 | * before, and this key could have been merged already. If the keys are |
michael@0 | 415 | * the same, we are done. If they are not, we need to update the CKA_ID of |
michael@0 | 416 | * the source key and try again. |
michael@0 | 417 | * |
michael@0 | 418 | * Once we know we have a unique key to merge in, we use NSS's underlying |
michael@0 | 419 | * key Move function which will do a key exchange if necessary to move |
michael@0 | 420 | * the key from one token to another. Then we set the CKA_ID and additional |
michael@0 | 421 | * pkcs #11 attributes. |
michael@0 | 422 | */ |
michael@0 | 423 | static SECStatus |
michael@0 | 424 | pk11_mergeSecretKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 425 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 426 | { |
michael@0 | 427 | PK11SymKey *sourceKey = NULL; |
michael@0 | 428 | PK11SymKey *targetKey = NULL; |
michael@0 | 429 | SECItem *sourceOutput = NULL; |
michael@0 | 430 | SECItem *targetOutput = NULL; |
michael@0 | 431 | SECItem *param = NULL; |
michael@0 | 432 | int blockSize; |
michael@0 | 433 | SECItem input; |
michael@0 | 434 | CK_OBJECT_HANDLE targetKeyID; |
michael@0 | 435 | CK_FLAGS flags; |
michael@0 | 436 | PLArenaPool *arena = NULL; |
michael@0 | 437 | SECStatus rv = SECSuccess; |
michael@0 | 438 | CK_MECHANISM_TYPE keyMechType, cryptoMechType; |
michael@0 | 439 | CK_KEY_TYPE sourceKeyType, targetKeyType; |
michael@0 | 440 | CK_ATTRIBUTE symTemplate[] = { |
michael@0 | 441 | { CKA_ID, NULL, 0 }, |
michael@0 | 442 | { CKA_CLASS, NULL, 0 } |
michael@0 | 443 | }; |
michael@0 | 444 | CK_ULONG symTemplateCount = sizeof(symTemplate)/sizeof(symTemplate[0]); |
michael@0 | 445 | CK_ATTRIBUTE symCopyTemplate[] = { |
michael@0 | 446 | { CKA_LABEL, NULL, 0 } |
michael@0 | 447 | }; |
michael@0 | 448 | CK_ULONG symCopyTemplateCount = |
michael@0 | 449 | sizeof(symCopyTemplate)/sizeof(symCopyTemplate[0]); |
michael@0 | 450 | |
michael@0 | 451 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 452 | if (arena == NULL) { |
michael@0 | 453 | rv = SECFailure; |
michael@0 | 454 | goto done; |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | sourceKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE); |
michael@0 | 458 | if (sourceKeyType == (CK_ULONG) -1) { |
michael@0 | 459 | rv = SECFailure; |
michael@0 | 460 | goto done; |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | /* get the key mechanism */ |
michael@0 | 464 | keyMechType = PK11_GetKeyMechanism(sourceKeyType); |
michael@0 | 465 | /* get a mechanism suitable to encryption. |
michael@0 | 466 | * PK11_GetKeyMechanism returns a mechanism that is unique to the key |
michael@0 | 467 | * type. It tries to return encryption/decryption mechanisms, however |
michael@0 | 468 | * CKM_DES3_CBC uses and abmiguous keyType, so keyMechType is returned as |
michael@0 | 469 | * 'keygen' mechanism. Detect that case here */ |
michael@0 | 470 | cryptoMechType = keyMechType; |
michael@0 | 471 | if ((keyMechType == CKM_DES3_KEY_GEN) || |
michael@0 | 472 | (keyMechType == CKM_DES2_KEY_GEN)) { |
michael@0 | 473 | cryptoMechType = CKM_DES3_CBC; |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | sourceKey = PK11_SymKeyFromHandle(sourceSlot, NULL, PK11_OriginDerive, |
michael@0 | 477 | keyMechType , id, PR_FALSE, sourcePwArg); |
michael@0 | 478 | if (sourceKey == NULL) { |
michael@0 | 479 | rv = SECFailure; |
michael@0 | 480 | goto done; |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | /* check to see a key with the same CKA_ID already exists in |
michael@0 | 484 | * the target slot. If it does, then we need to verify if the keys |
michael@0 | 485 | * really matches. If they don't import the key with a new CKA_ID |
michael@0 | 486 | * value. */ |
michael@0 | 487 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, |
michael@0 | 488 | symTemplate, symTemplateCount, id, &targetKeyID); |
michael@0 | 489 | if (rv != SECSuccess) { |
michael@0 | 490 | goto done; |
michael@0 | 491 | } |
michael@0 | 492 | |
michael@0 | 493 | /* set up the input test */ |
michael@0 | 494 | input.data = (unsigned char *)testString; |
michael@0 | 495 | blockSize = PK11_GetBlockSize(cryptoMechType, NULL); |
michael@0 | 496 | if (blockSize < 0) { |
michael@0 | 497 | rv = SECFailure; |
michael@0 | 498 | goto done; |
michael@0 | 499 | } |
michael@0 | 500 | input.len = blockSize; |
michael@0 | 501 | if (input.len == 0) { |
michael@0 | 502 | input.len = sizeof (testString); |
michael@0 | 503 | } |
michael@0 | 504 | while (targetKeyID != CK_INVALID_HANDLE) { |
michael@0 | 505 | /* test to see if the keys are identical */ |
michael@0 | 506 | targetKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE); |
michael@0 | 507 | if (targetKeyType == sourceKeyType) { |
michael@0 | 508 | /* same keyType - see if it's the same key */ |
michael@0 | 509 | targetKey = PK11_SymKeyFromHandle(targetSlot, NULL, |
michael@0 | 510 | PK11_OriginDerive, keyMechType, targetKeyID, PR_FALSE, |
michael@0 | 511 | targetPwArg); |
michael@0 | 512 | /* get a parameter if we don't already have one */ |
michael@0 | 513 | if (!param) { |
michael@0 | 514 | param = PK11_GenerateNewParam(cryptoMechType, sourceKey); |
michael@0 | 515 | if (param == NULL) { |
michael@0 | 516 | rv = SECFailure; |
michael@0 | 517 | goto done; |
michael@0 | 518 | } |
michael@0 | 519 | } |
michael@0 | 520 | /* use the source key to encrypt a reference */ |
michael@0 | 521 | if (!sourceOutput) { |
michael@0 | 522 | rv = pk11_encrypt(sourceKey, cryptoMechType, param, &input, |
michael@0 | 523 | &sourceOutput); |
michael@0 | 524 | if (rv != SECSuccess) { |
michael@0 | 525 | goto done; |
michael@0 | 526 | } |
michael@0 | 527 | } |
michael@0 | 528 | /* encrypt the reference with the target key */ |
michael@0 | 529 | rv = pk11_encrypt(targetKey, cryptoMechType, param, &input, |
michael@0 | 530 | &targetOutput); |
michael@0 | 531 | if (rv == SECSuccess) { |
michael@0 | 532 | if (SECITEM_ItemsAreEqual(sourceOutput, targetOutput)) { |
michael@0 | 533 | /* they produce the same output, they must be the |
michael@0 | 534 | * same key */ |
michael@0 | 535 | goto done; |
michael@0 | 536 | } |
michael@0 | 537 | SECITEM_FreeItem(targetOutput, PR_TRUE); |
michael@0 | 538 | targetOutput = NULL; |
michael@0 | 539 | } |
michael@0 | 540 | PK11_FreeSymKey(targetKey); |
michael@0 | 541 | targetKey = NULL; |
michael@0 | 542 | } |
michael@0 | 543 | /* keys aren't equal, update the KEY_ID and look again */ |
michael@0 | 544 | rv = pk11_incrementID(arena, &symTemplate[0]); |
michael@0 | 545 | if (rv != SECSuccess) { |
michael@0 | 546 | goto done; |
michael@0 | 547 | } |
michael@0 | 548 | targetKeyID = pk11_FindObjectByTemplate(targetSlot, |
michael@0 | 549 | symTemplate, symTemplateCount); |
michael@0 | 550 | } |
michael@0 | 551 | |
michael@0 | 552 | /* we didn't find a matching key, import this one with the new |
michael@0 | 553 | * CKAID */ |
michael@0 | 554 | flags = pk11_getSecretKeyFlags(sourceSlot, id); |
michael@0 | 555 | targetKey = PK11_MoveSymKey(targetSlot, PK11_OriginDerive, flags, PR_TRUE, |
michael@0 | 556 | sourceKey); |
michael@0 | 557 | if (targetKey == NULL) { |
michael@0 | 558 | rv = SECFailure; |
michael@0 | 559 | goto done; |
michael@0 | 560 | } |
michael@0 | 561 | /* set the key new CKAID */ |
michael@0 | 562 | rv = pk11_setAttributes(targetSlot, targetKey->objectID, symTemplate, 1); |
michael@0 | 563 | if (rv != SECSuccess) { |
michael@0 | 564 | goto done; |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | /* fill in remaining attributes */ |
michael@0 | 568 | rv = pk11_copyAttributes(arena, targetSlot, targetKey->objectID, |
michael@0 | 569 | sourceSlot, id, symCopyTemplate, symCopyTemplateCount); |
michael@0 | 570 | done: |
michael@0 | 571 | if (sourceKey) { |
michael@0 | 572 | PK11_FreeSymKey(sourceKey); |
michael@0 | 573 | } |
michael@0 | 574 | if (targetKey) { |
michael@0 | 575 | PK11_FreeSymKey(targetKey); |
michael@0 | 576 | } |
michael@0 | 577 | if (sourceOutput) { |
michael@0 | 578 | SECITEM_FreeItem(sourceOutput, PR_TRUE); |
michael@0 | 579 | } |
michael@0 | 580 | if (targetOutput) { |
michael@0 | 581 | SECITEM_FreeItem(targetOutput, PR_TRUE); |
michael@0 | 582 | } |
michael@0 | 583 | if (param) { |
michael@0 | 584 | SECITEM_FreeItem(param, PR_TRUE); |
michael@0 | 585 | } |
michael@0 | 586 | if (arena) { |
michael@0 | 587 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 588 | } |
michael@0 | 589 | return rv; |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | /************************************************************************* |
michael@0 | 593 | * |
michael@0 | 594 | * Public Keys |
michael@0 | 595 | * |
michael@0 | 596 | *************************************************************************/ |
michael@0 | 597 | |
michael@0 | 598 | /* |
michael@0 | 599 | * Merge public key |
michael@0 | 600 | * |
michael@0 | 601 | * Use the high level NSS calls to extract the public key and import it |
michael@0 | 602 | * into the token. Extra attributes are then copied to the new token. |
michael@0 | 603 | */ |
michael@0 | 604 | static SECStatus |
michael@0 | 605 | pk11_mergePublicKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 606 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 607 | { |
michael@0 | 608 | SECKEYPublicKey *sourceKey = NULL; |
michael@0 | 609 | CK_OBJECT_HANDLE targetKeyID; |
michael@0 | 610 | PLArenaPool *arena = NULL; |
michael@0 | 611 | SECStatus rv = SECSuccess; |
michael@0 | 612 | CK_ATTRIBUTE pubTemplate[] = { |
michael@0 | 613 | { CKA_ID, NULL, 0 }, |
michael@0 | 614 | { CKA_CLASS, NULL, 0 } |
michael@0 | 615 | }; |
michael@0 | 616 | CK_ULONG pubTemplateCount = sizeof(pubTemplate)/sizeof(pubTemplate[0]); |
michael@0 | 617 | CK_ATTRIBUTE pubCopyTemplate[] = { |
michael@0 | 618 | { CKA_ID, NULL, 0 }, |
michael@0 | 619 | { CKA_LABEL, NULL, 0 }, |
michael@0 | 620 | { CKA_SUBJECT, NULL, 0 } |
michael@0 | 621 | }; |
michael@0 | 622 | CK_ULONG pubCopyTemplateCount = |
michael@0 | 623 | sizeof(pubCopyTemplate)/sizeof(pubCopyTemplate[0]); |
michael@0 | 624 | |
michael@0 | 625 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 626 | if (arena == NULL) { |
michael@0 | 627 | rv = SECFailure; |
michael@0 | 628 | goto done; |
michael@0 | 629 | } |
michael@0 | 630 | |
michael@0 | 631 | |
michael@0 | 632 | /* check to see if the key is already in the target slot */ |
michael@0 | 633 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, pubTemplate, |
michael@0 | 634 | pubTemplateCount, id, &targetKeyID); |
michael@0 | 635 | if (rv != SECSuccess) { |
michael@0 | 636 | goto done; |
michael@0 | 637 | } |
michael@0 | 638 | |
michael@0 | 639 | /* Key is already in the target slot */ |
michael@0 | 640 | if (targetKeyID != CK_INVALID_HANDLE) { |
michael@0 | 641 | /* not an error ... */ |
michael@0 | 642 | goto done; |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | /* fetch an NSS representation of the public key */ |
michael@0 | 646 | sourceKey = PK11_ExtractPublicKey(sourceSlot, nullKey, id); |
michael@0 | 647 | if (sourceKey== NULL) { |
michael@0 | 648 | rv = SECFailure; |
michael@0 | 649 | goto done; |
michael@0 | 650 | } |
michael@0 | 651 | |
michael@0 | 652 | /* load the public key into the target token. */ |
michael@0 | 653 | targetKeyID = PK11_ImportPublicKey(targetSlot, sourceKey, PR_TRUE); |
michael@0 | 654 | if (targetKeyID == CK_INVALID_HANDLE) { |
michael@0 | 655 | rv = SECFailure; |
michael@0 | 656 | goto done; |
michael@0 | 657 | } |
michael@0 | 658 | |
michael@0 | 659 | /* fill in remaining attributes */ |
michael@0 | 660 | rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id, |
michael@0 | 661 | pubCopyTemplate, pubCopyTemplateCount); |
michael@0 | 662 | |
michael@0 | 663 | |
michael@0 | 664 | done: |
michael@0 | 665 | if (sourceKey) { |
michael@0 | 666 | SECKEY_DestroyPublicKey(sourceKey); |
michael@0 | 667 | } |
michael@0 | 668 | if (arena) { |
michael@0 | 669 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 670 | } |
michael@0 | 671 | return rv; |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | /************************************************************************* |
michael@0 | 675 | * |
michael@0 | 676 | * Certificates |
michael@0 | 677 | * |
michael@0 | 678 | *************************************************************************/ |
michael@0 | 679 | |
michael@0 | 680 | /* |
michael@0 | 681 | * Two copies of the source code for this algorithm exist in NSS. |
michael@0 | 682 | * Changes must be made in both copies. |
michael@0 | 683 | * The other copy is in sftkdb_resolveConflicts() in softoken/sftkdb.c. |
michael@0 | 684 | */ |
michael@0 | 685 | static char * |
michael@0 | 686 | pk11_IncrementNickname(char *nickname) |
michael@0 | 687 | { |
michael@0 | 688 | char *newNickname = NULL; |
michael@0 | 689 | int end; |
michael@0 | 690 | int digit; |
michael@0 | 691 | int len = strlen(nickname); |
michael@0 | 692 | |
michael@0 | 693 | /* does nickname end with " #n*" ? */ |
michael@0 | 694 | for (end = len - 1; |
michael@0 | 695 | end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0'; |
michael@0 | 696 | end--) /* just scan */ ; |
michael@0 | 697 | if (len >= 3 && |
michael@0 | 698 | end < (len - 1) /* at least one digit */ && |
michael@0 | 699 | nickname[end] == '#' && |
michael@0 | 700 | nickname[end - 1] == ' ') { |
michael@0 | 701 | /* Already has a suitable suffix string */ |
michael@0 | 702 | } else { |
michael@0 | 703 | /* ... append " #2" to the name */ |
michael@0 | 704 | static const char num2[] = " #2"; |
michael@0 | 705 | newNickname = PORT_Realloc(nickname, len + sizeof(num2)); |
michael@0 | 706 | if (newNickname) { |
michael@0 | 707 | PORT_Strcat(newNickname, num2); |
michael@0 | 708 | } else { |
michael@0 | 709 | PORT_Free(nickname); |
michael@0 | 710 | } |
michael@0 | 711 | return newNickname; |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | for (end = len - 1; |
michael@0 | 715 | end >= 0 && (digit = nickname[end]) <= '9' && digit >= '0'; |
michael@0 | 716 | end--) { |
michael@0 | 717 | if (digit < '9') { |
michael@0 | 718 | nickname[end]++; |
michael@0 | 719 | return nickname; |
michael@0 | 720 | } |
michael@0 | 721 | nickname[end] = '0'; |
michael@0 | 722 | } |
michael@0 | 723 | |
michael@0 | 724 | /* we overflowed, insert a new '1' for a carry in front of the number */ |
michael@0 | 725 | newNickname = PORT_Realloc(nickname, len + 2); |
michael@0 | 726 | if (newNickname) { |
michael@0 | 727 | newNickname[++end] = '1'; |
michael@0 | 728 | PORT_Memset(&newNickname[end + 1], '0', len - end); |
michael@0 | 729 | newNickname[len + 1] = 0; |
michael@0 | 730 | } else { |
michael@0 | 731 | PORT_Free(nickname); |
michael@0 | 732 | } |
michael@0 | 733 | return newNickname; |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | /* |
michael@0 | 737 | * merge a certificate object |
michael@0 | 738 | * |
michael@0 | 739 | * Use the high level NSS calls to extract and import the certificate. |
michael@0 | 740 | */ |
michael@0 | 741 | static SECStatus |
michael@0 | 742 | pk11_mergeCert(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 743 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 744 | { |
michael@0 | 745 | CERTCertificate *sourceCert = NULL; |
michael@0 | 746 | CK_OBJECT_HANDLE targetCertID = CK_INVALID_HANDLE; |
michael@0 | 747 | char *nickname = NULL; |
michael@0 | 748 | SECStatus rv = SECSuccess; |
michael@0 | 749 | PLArenaPool *arena = NULL; |
michael@0 | 750 | CK_ATTRIBUTE sourceCKAID = {CKA_ID, NULL, 0}; |
michael@0 | 751 | CK_ATTRIBUTE targetCKAID = {CKA_ID, NULL, 0}; |
michael@0 | 752 | SECStatus lrv = SECSuccess; |
michael@0 | 753 | int error; |
michael@0 | 754 | |
michael@0 | 755 | |
michael@0 | 756 | sourceCert = PK11_MakeCertFromHandle(sourceSlot, id, NULL); |
michael@0 | 757 | if (sourceCert == NULL) { |
michael@0 | 758 | rv = SECFailure; |
michael@0 | 759 | goto done; |
michael@0 | 760 | } |
michael@0 | 761 | |
michael@0 | 762 | nickname = PK11_GetObjectNickname(sourceSlot, id); |
michael@0 | 763 | |
michael@0 | 764 | /* The database code will prevent nickname collisions for certs with |
michael@0 | 765 | * different subjects. This code will prevent us from getting |
michael@0 | 766 | * actual import errors */ |
michael@0 | 767 | if (nickname) { |
michael@0 | 768 | const char *tokenName = PK11_GetTokenName(targetSlot); |
michael@0 | 769 | char *tokenNickname = NULL; |
michael@0 | 770 | |
michael@0 | 771 | do { |
michael@0 | 772 | tokenNickname = PR_smprintf("%s:%s",tokenName, nickname); |
michael@0 | 773 | if (!tokenNickname) { |
michael@0 | 774 | break; |
michael@0 | 775 | } |
michael@0 | 776 | if (!SEC_CertNicknameConflict(tokenNickname, |
michael@0 | 777 | &sourceCert->derSubject, CERT_GetDefaultCertDB())) { |
michael@0 | 778 | break; |
michael@0 | 779 | } |
michael@0 | 780 | nickname = pk11_IncrementNickname(nickname); |
michael@0 | 781 | if (!nickname) { |
michael@0 | 782 | break; |
michael@0 | 783 | } |
michael@0 | 784 | PR_smprintf_free(tokenNickname); |
michael@0 | 785 | } while (1); |
michael@0 | 786 | if (tokenNickname) { |
michael@0 | 787 | PR_smprintf_free(tokenNickname); |
michael@0 | 788 | } |
michael@0 | 789 | } |
michael@0 | 790 | |
michael@0 | 791 | |
michael@0 | 792 | |
michael@0 | 793 | /* see if the cert is already there */ |
michael@0 | 794 | targetCertID = PK11_FindCertInSlot(targetSlot, sourceCert, targetPwArg); |
michael@0 | 795 | if (targetCertID == CK_INVALID_HANDLE) { |
michael@0 | 796 | /* cert doesn't exist load the cert in. */ |
michael@0 | 797 | /* OK for the nickname to be NULL, not all certs have nicknames */ |
michael@0 | 798 | rv = PK11_ImportCert(targetSlot, sourceCert, CK_INVALID_HANDLE, |
michael@0 | 799 | nickname, PR_FALSE); |
michael@0 | 800 | goto done; |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | /* the cert already exists, see if the nickname and/or CKA_ID need |
michael@0 | 804 | * to be updated */ |
michael@0 | 805 | |
michael@0 | 806 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 807 | if (arena == NULL) { |
michael@0 | 808 | rv = SECFailure; |
michael@0 | 809 | goto done; |
michael@0 | 810 | } |
michael@0 | 811 | |
michael@0 | 812 | /* does our source have a CKA_ID ? */ |
michael@0 | 813 | rv = PK11_GetAttributes(arena, sourceSlot, id, &sourceCKAID, 1); |
michael@0 | 814 | if (rv != SECSuccess) { |
michael@0 | 815 | sourceCKAID.ulValueLen = 0; |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | /* if we have a source CKA_ID, see of we need to update the |
michael@0 | 819 | * target's CKA_ID */ |
michael@0 | 820 | if (sourceCKAID.ulValueLen != 0) { |
michael@0 | 821 | rv = PK11_GetAttributes(arena, targetSlot, targetCertID, |
michael@0 | 822 | &targetCKAID, 1); |
michael@0 | 823 | if (rv != SECSuccess) { |
michael@0 | 824 | targetCKAID.ulValueLen = 0; |
michael@0 | 825 | } |
michael@0 | 826 | /* if the target has no CKA_ID, update it from the source */ |
michael@0 | 827 | if (targetCKAID.ulValueLen == 0) { |
michael@0 | 828 | lrv=pk11_setAttributes(targetSlot, targetCertID, &sourceCKAID, 1); |
michael@0 | 829 | if (lrv != SECSuccess) { |
michael@0 | 830 | error = PORT_GetError(); |
michael@0 | 831 | } |
michael@0 | 832 | } |
michael@0 | 833 | } |
michael@0 | 834 | rv = SECSuccess; |
michael@0 | 835 | |
michael@0 | 836 | /* now check if we need to update the nickname */ |
michael@0 | 837 | if (nickname && *nickname) { |
michael@0 | 838 | char *targetname; |
michael@0 | 839 | targetname = PK11_GetObjectNickname(targetSlot, targetCertID); |
michael@0 | 840 | if (!targetname || !*targetname) { |
michael@0 | 841 | /* target has no nickname, or it's empty, update it */ |
michael@0 | 842 | rv = PK11_SetObjectNickname(targetSlot, targetCertID, nickname); |
michael@0 | 843 | } |
michael@0 | 844 | if (targetname) { |
michael@0 | 845 | PORT_Free(targetname); |
michael@0 | 846 | } |
michael@0 | 847 | } |
michael@0 | 848 | |
michael@0 | 849 | /* restore the error code if CKA_ID failed, but nickname didn't */ |
michael@0 | 850 | if ((rv == SECSuccess) && (lrv != SECSuccess)) { |
michael@0 | 851 | rv = lrv; |
michael@0 | 852 | PORT_SetError(error); |
michael@0 | 853 | } |
michael@0 | 854 | |
michael@0 | 855 | done: |
michael@0 | 856 | if (nickname) { |
michael@0 | 857 | PORT_Free(nickname); |
michael@0 | 858 | } |
michael@0 | 859 | if (sourceCert) { |
michael@0 | 860 | CERT_DestroyCertificate(sourceCert); |
michael@0 | 861 | } |
michael@0 | 862 | if (arena) { |
michael@0 | 863 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 864 | } |
michael@0 | 865 | return rv; |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | |
michael@0 | 869 | /************************************************************************* |
michael@0 | 870 | * |
michael@0 | 871 | * Crls |
michael@0 | 872 | * |
michael@0 | 873 | *************************************************************************/ |
michael@0 | 874 | |
michael@0 | 875 | /* |
michael@0 | 876 | * Use the raw PKCS #11 interface to merge the CRLs. |
michael@0 | 877 | * |
michael@0 | 878 | * In the case where of collision, choose the newest CRL that is valid. |
michael@0 | 879 | */ |
michael@0 | 880 | static SECStatus |
michael@0 | 881 | pk11_mergeCrl(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 882 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 883 | { |
michael@0 | 884 | CK_OBJECT_HANDLE targetCrlID; |
michael@0 | 885 | PLArenaPool *arena = NULL; |
michael@0 | 886 | SECStatus rv = SECSuccess; |
michael@0 | 887 | CK_ATTRIBUTE crlTemplate[] = { |
michael@0 | 888 | { CKA_SUBJECT, NULL, 0 }, |
michael@0 | 889 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 890 | { CKA_NSS_KRL, NULL, 0 } |
michael@0 | 891 | }; |
michael@0 | 892 | CK_ULONG crlTemplateCount = sizeof(crlTemplate)/sizeof(crlTemplate[0]); |
michael@0 | 893 | CK_ATTRIBUTE crlCopyTemplate[] = { |
michael@0 | 894 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 895 | { CKA_TOKEN, NULL, 0 }, |
michael@0 | 896 | { CKA_LABEL, NULL, 0 }, |
michael@0 | 897 | { CKA_PRIVATE, NULL, 0 }, |
michael@0 | 898 | { CKA_MODIFIABLE, NULL, 0 }, |
michael@0 | 899 | { CKA_SUBJECT, NULL, 0 }, |
michael@0 | 900 | { CKA_NSS_KRL, NULL, 0 }, |
michael@0 | 901 | { CKA_NSS_URL, NULL, 0 }, |
michael@0 | 902 | { CKA_VALUE, NULL, 0 } |
michael@0 | 903 | }; |
michael@0 | 904 | CK_ULONG crlCopyTemplateCount = |
michael@0 | 905 | sizeof(crlCopyTemplate)/sizeof(crlCopyTemplate[0]); |
michael@0 | 906 | |
michael@0 | 907 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 908 | if (arena == NULL) { |
michael@0 | 909 | rv = SECFailure; |
michael@0 | 910 | goto done; |
michael@0 | 911 | } |
michael@0 | 912 | /* check to see if the crl is already in the target slot */ |
michael@0 | 913 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, crlTemplate, |
michael@0 | 914 | crlTemplateCount, id, &targetCrlID); |
michael@0 | 915 | if (rv != SECSuccess) { |
michael@0 | 916 | goto done; |
michael@0 | 917 | } |
michael@0 | 918 | if (targetCrlID != CK_INVALID_HANDLE) { |
michael@0 | 919 | /* we already have a CRL, check to see which is more up-to-date. */ |
michael@0 | 920 | goto done; |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | /* load the CRL into the target token. */ |
michael@0 | 924 | rv = pk11_copyAttributes(arena, targetSlot, targetCrlID, sourceSlot, id, |
michael@0 | 925 | crlCopyTemplate, crlCopyTemplateCount); |
michael@0 | 926 | done: |
michael@0 | 927 | if (arena) { |
michael@0 | 928 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 929 | } |
michael@0 | 930 | return rv; |
michael@0 | 931 | } |
michael@0 | 932 | |
michael@0 | 933 | /************************************************************************* |
michael@0 | 934 | * |
michael@0 | 935 | * SMIME objects |
michael@0 | 936 | * |
michael@0 | 937 | *************************************************************************/ |
michael@0 | 938 | |
michael@0 | 939 | /* |
michael@0 | 940 | * use the raw PKCS #11 interface to merge the S/MIME records |
michael@0 | 941 | */ |
michael@0 | 942 | static SECStatus |
michael@0 | 943 | pk11_mergeSmime(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 944 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 945 | { |
michael@0 | 946 | CK_OBJECT_HANDLE targetSmimeID; |
michael@0 | 947 | PLArenaPool *arena = NULL; |
michael@0 | 948 | SECStatus rv = SECSuccess; |
michael@0 | 949 | CK_ATTRIBUTE smimeTemplate[] = { |
michael@0 | 950 | { CKA_SUBJECT, NULL, 0 }, |
michael@0 | 951 | { CKA_NSS_EMAIL, NULL, 0 }, |
michael@0 | 952 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 953 | }; |
michael@0 | 954 | CK_ULONG smimeTemplateCount = |
michael@0 | 955 | sizeof(smimeTemplate)/sizeof(smimeTemplate[0]); |
michael@0 | 956 | CK_ATTRIBUTE smimeCopyTemplate[] = { |
michael@0 | 957 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 958 | { CKA_TOKEN, NULL, 0 }, |
michael@0 | 959 | { CKA_LABEL, NULL, 0 }, |
michael@0 | 960 | { CKA_PRIVATE, NULL, 0 }, |
michael@0 | 961 | { CKA_MODIFIABLE, NULL, 0 }, |
michael@0 | 962 | { CKA_SUBJECT, NULL, 0 }, |
michael@0 | 963 | { CKA_NSS_EMAIL, NULL, 0 }, |
michael@0 | 964 | { CKA_NSS_SMIME_TIMESTAMP, NULL, 0 }, |
michael@0 | 965 | { CKA_VALUE, NULL, 0 } |
michael@0 | 966 | }; |
michael@0 | 967 | CK_ULONG smimeCopyTemplateCount = |
michael@0 | 968 | sizeof(smimeCopyTemplate)/sizeof(smimeCopyTemplate[0]); |
michael@0 | 969 | |
michael@0 | 970 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 971 | if (arena == NULL) { |
michael@0 | 972 | rv = SECFailure; |
michael@0 | 973 | goto done; |
michael@0 | 974 | } |
michael@0 | 975 | /* check to see if the crl is already in the target slot */ |
michael@0 | 976 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, smimeTemplate, |
michael@0 | 977 | smimeTemplateCount, id, &targetSmimeID); |
michael@0 | 978 | if (rv != SECSuccess) { |
michael@0 | 979 | goto done; |
michael@0 | 980 | } |
michael@0 | 981 | if (targetSmimeID != CK_INVALID_HANDLE) { |
michael@0 | 982 | /* we already have a SMIME record */ |
michael@0 | 983 | goto done; |
michael@0 | 984 | } |
michael@0 | 985 | |
michael@0 | 986 | /* load the SMime Record into the target token. */ |
michael@0 | 987 | rv = pk11_copyAttributes(arena, targetSlot, targetSmimeID, sourceSlot, id, |
michael@0 | 988 | smimeCopyTemplate, smimeCopyTemplateCount); |
michael@0 | 989 | done: |
michael@0 | 990 | if (arena) { |
michael@0 | 991 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 992 | } |
michael@0 | 993 | return rv; |
michael@0 | 994 | } |
michael@0 | 995 | |
michael@0 | 996 | /************************************************************************* |
michael@0 | 997 | * |
michael@0 | 998 | * Trust Objects |
michael@0 | 999 | * |
michael@0 | 1000 | *************************************************************************/ |
michael@0 | 1001 | |
michael@0 | 1002 | |
michael@0 | 1003 | /* |
michael@0 | 1004 | * decide which trust record entry wins. PR_TRUE (source) or PR_FALSE (target) |
michael@0 | 1005 | */ |
michael@0 | 1006 | #define USE_TARGET PR_FALSE |
michael@0 | 1007 | #define USE_SOURCE PR_TRUE |
michael@0 | 1008 | PRBool |
michael@0 | 1009 | pk11_mergeTrustEntry(CK_ATTRIBUTE *target, CK_ATTRIBUTE *source) |
michael@0 | 1010 | { |
michael@0 | 1011 | CK_ULONG targetTrust = (target->ulValueLen == sizeof (CK_LONG)) ? |
michael@0 | 1012 | *(CK_ULONG *)target->pValue : CKT_NSS_TRUST_UNKNOWN; |
michael@0 | 1013 | CK_ULONG sourceTrust = (source->ulValueLen == sizeof (CK_LONG)) ? |
michael@0 | 1014 | *(CK_ULONG *)source->pValue : CKT_NSS_TRUST_UNKNOWN; |
michael@0 | 1015 | |
michael@0 | 1016 | /* |
michael@0 | 1017 | * Examine a single entry and deside if the source or target version |
michael@0 | 1018 | * should win out. When all the entries have been checked, if there is |
michael@0 | 1019 | * any case we need to update, we will write the whole source record |
michael@0 | 1020 | * to the target database. That means for each individual record, if the |
michael@0 | 1021 | * target wins, we need to update the source (in case later we have a |
michael@0 | 1022 | * case where the source wins). If the source wins, it already |
michael@0 | 1023 | */ |
michael@0 | 1024 | if (sourceTrust == targetTrust) { |
michael@0 | 1025 | return USE_TARGET; /* which equates to 'do nothing' */ |
michael@0 | 1026 | } |
michael@0 | 1027 | |
michael@0 | 1028 | if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) { |
michael@0 | 1029 | return USE_TARGET; |
michael@0 | 1030 | } |
michael@0 | 1031 | |
michael@0 | 1032 | /* target has no idea, use the source's idea of the trust value */ |
michael@0 | 1033 | if (targetTrust == CKT_NSS_TRUST_UNKNOWN) { |
michael@0 | 1034 | /* source overwrites the target */ |
michael@0 | 1035 | return USE_SOURCE; |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | /* so both the target and the source have some idea of what this |
michael@0 | 1039 | * trust attribute should be, and neither agree exactly. |
michael@0 | 1040 | * At this point, we prefer 'hard' attributes over 'soft' ones. |
michael@0 | 1041 | * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and |
michael@0 | 1042 | * CKT_NSS_UNTRUTED. Soft ones are ones which don't change the |
michael@0 | 1043 | * actual trust of the cert (CKT_MUST_VERIFY, CKT_NSS_VALID, |
michael@0 | 1044 | * CKT_NSS_VALID_DELEGATOR). |
michael@0 | 1045 | */ |
michael@0 | 1046 | if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST) |
michael@0 | 1047 | || (sourceTrust == CKT_NSS_VALID_DELEGATOR)) { |
michael@0 | 1048 | return USE_TARGET; |
michael@0 | 1049 | } |
michael@0 | 1050 | if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST) |
michael@0 | 1051 | || (targetTrust == CKT_NSS_VALID_DELEGATOR)) { |
michael@0 | 1052 | /* source overrites the target */ |
michael@0 | 1053 | return USE_SOURCE; |
michael@0 | 1054 | } |
michael@0 | 1055 | |
michael@0 | 1056 | /* both have hard attributes, we have a conflict, let the target win. */ |
michael@0 | 1057 | return USE_TARGET; |
michael@0 | 1058 | } |
michael@0 | 1059 | /* |
michael@0 | 1060 | * use the raw PKCS #11 interface to merge the S/MIME records |
michael@0 | 1061 | */ |
michael@0 | 1062 | static SECStatus |
michael@0 | 1063 | pk11_mergeTrust(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 1064 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 1065 | { |
michael@0 | 1066 | CK_OBJECT_HANDLE targetTrustID; |
michael@0 | 1067 | PLArenaPool *arena = NULL; |
michael@0 | 1068 | SECStatus rv = SECSuccess; |
michael@0 | 1069 | int error = 0; |
michael@0 | 1070 | CK_ATTRIBUTE trustTemplate[] = { |
michael@0 | 1071 | { CKA_ISSUER, NULL, 0 }, |
michael@0 | 1072 | { CKA_SERIAL_NUMBER, NULL, 0 }, |
michael@0 | 1073 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 1074 | }; |
michael@0 | 1075 | CK_ULONG trustTemplateCount = |
michael@0 | 1076 | sizeof(trustTemplate)/sizeof(trustTemplate[0]); |
michael@0 | 1077 | CK_ATTRIBUTE trustCopyTemplate[] = { |
michael@0 | 1078 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 1079 | { CKA_TOKEN, NULL, 0 }, |
michael@0 | 1080 | { CKA_LABEL, NULL, 0 }, |
michael@0 | 1081 | { CKA_PRIVATE, NULL, 0 }, |
michael@0 | 1082 | { CKA_MODIFIABLE, NULL, 0 }, |
michael@0 | 1083 | { CKA_ISSUER, NULL, 0}, |
michael@0 | 1084 | { CKA_SERIAL_NUMBER, NULL, 0}, |
michael@0 | 1085 | { CKA_CERT_SHA1_HASH, NULL, 0 }, |
michael@0 | 1086 | { CKA_CERT_MD5_HASH, NULL, 0 }, |
michael@0 | 1087 | { CKA_TRUST_SERVER_AUTH, NULL, 0 }, |
michael@0 | 1088 | { CKA_TRUST_CLIENT_AUTH, NULL, 0 }, |
michael@0 | 1089 | { CKA_TRUST_CODE_SIGNING, NULL, 0 }, |
michael@0 | 1090 | { CKA_TRUST_EMAIL_PROTECTION, NULL, 0 }, |
michael@0 | 1091 | { CKA_TRUST_STEP_UP_APPROVED, NULL, 0 } |
michael@0 | 1092 | }; |
michael@0 | 1093 | CK_ULONG trustCopyTemplateCount = |
michael@0 | 1094 | sizeof(trustCopyTemplate)/sizeof(trustCopyTemplate[0]); |
michael@0 | 1095 | |
michael@0 | 1096 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 1097 | if (arena == NULL) { |
michael@0 | 1098 | rv = SECFailure; |
michael@0 | 1099 | goto done; |
michael@0 | 1100 | } |
michael@0 | 1101 | /* check to see if the crl is already in the target slot */ |
michael@0 | 1102 | rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, trustTemplate, |
michael@0 | 1103 | trustTemplateCount, id, &targetTrustID); |
michael@0 | 1104 | if (rv != SECSuccess) { |
michael@0 | 1105 | goto done; |
michael@0 | 1106 | } |
michael@0 | 1107 | if (targetTrustID != CK_INVALID_HANDLE) { |
michael@0 | 1108 | /* a matching trust record already exists, merge it in */ |
michael@0 | 1109 | CK_ATTRIBUTE_TYPE trustAttrs[] = { |
michael@0 | 1110 | CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, |
michael@0 | 1111 | CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION, |
michael@0 | 1112 | CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, |
michael@0 | 1113 | CKA_TRUST_TIME_STAMPING |
michael@0 | 1114 | }; |
michael@0 | 1115 | CK_ULONG trustAttrsCount = |
michael@0 | 1116 | sizeof(trustAttrs)/sizeof(trustAttrs[0]); |
michael@0 | 1117 | |
michael@0 | 1118 | CK_ULONG i; |
michael@0 | 1119 | CK_ATTRIBUTE targetTemplate, sourceTemplate; |
michael@0 | 1120 | |
michael@0 | 1121 | /* existing trust record, merge the two together */ |
michael@0 | 1122 | for (i=0; i < trustAttrsCount; i++) { |
michael@0 | 1123 | targetTemplate.type = sourceTemplate.type = trustAttrs[i]; |
michael@0 | 1124 | targetTemplate.pValue = sourceTemplate.pValue = NULL; |
michael@0 | 1125 | targetTemplate.ulValueLen = sourceTemplate.ulValueLen = 0; |
michael@0 | 1126 | PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1); |
michael@0 | 1127 | PK11_GetAttributes(arena, targetSlot, targetTrustID, |
michael@0 | 1128 | &targetTemplate, 1); |
michael@0 | 1129 | if (pk11_mergeTrustEntry(&targetTemplate, &sourceTemplate)) { |
michael@0 | 1130 | /* source wins, write out the source attribute to the target */ |
michael@0 | 1131 | SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID, |
michael@0 | 1132 | &sourceTemplate, 1); |
michael@0 | 1133 | if (lrv != SECSuccess) { |
michael@0 | 1134 | rv = SECFailure; |
michael@0 | 1135 | error = PORT_GetError(); |
michael@0 | 1136 | } |
michael@0 | 1137 | } |
michael@0 | 1138 | } |
michael@0 | 1139 | |
michael@0 | 1140 | /* handle step */ |
michael@0 | 1141 | sourceTemplate.type = CKA_TRUST_STEP_UP_APPROVED; |
michael@0 | 1142 | sourceTemplate.pValue = NULL; |
michael@0 | 1143 | sourceTemplate.ulValueLen = 0; |
michael@0 | 1144 | |
michael@0 | 1145 | /* if the source has steup set, then set it in the target */ |
michael@0 | 1146 | PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1); |
michael@0 | 1147 | if ((sourceTemplate.ulValueLen == sizeof(CK_BBOOL)) && |
michael@0 | 1148 | (sourceTemplate.pValue) && |
michael@0 | 1149 | (*(CK_BBOOL *)sourceTemplate.pValue == CK_TRUE)) { |
michael@0 | 1150 | SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID, |
michael@0 | 1151 | &sourceTemplate, 1); |
michael@0 | 1152 | if (lrv != SECSuccess) { |
michael@0 | 1153 | rv = SECFailure; |
michael@0 | 1154 | error = PORT_GetError(); |
michael@0 | 1155 | } |
michael@0 | 1156 | } |
michael@0 | 1157 | |
michael@0 | 1158 | goto done; |
michael@0 | 1159 | |
michael@0 | 1160 | } |
michael@0 | 1161 | |
michael@0 | 1162 | /* load the new trust Record into the target token. */ |
michael@0 | 1163 | rv = pk11_copyAttributes(arena, targetSlot, targetTrustID, sourceSlot, id, |
michael@0 | 1164 | trustCopyTemplate, trustCopyTemplateCount); |
michael@0 | 1165 | done: |
michael@0 | 1166 | if (arena) { |
michael@0 | 1167 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 1168 | } |
michael@0 | 1169 | |
michael@0 | 1170 | /* restore the error code */ |
michael@0 | 1171 | if (rv == SECFailure && error) { |
michael@0 | 1172 | PORT_SetError(error); |
michael@0 | 1173 | } |
michael@0 | 1174 | |
michael@0 | 1175 | return rv; |
michael@0 | 1176 | } |
michael@0 | 1177 | |
michael@0 | 1178 | /************************************************************************* |
michael@0 | 1179 | * |
michael@0 | 1180 | * Central merge code |
michael@0 | 1181 | * |
michael@0 | 1182 | *************************************************************************/ |
michael@0 | 1183 | /* |
michael@0 | 1184 | * merge a single object from sourceToken to targetToken |
michael@0 | 1185 | */ |
michael@0 | 1186 | static SECStatus |
michael@0 | 1187 | pk11_mergeObject(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 1188 | CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg) |
michael@0 | 1189 | { |
michael@0 | 1190 | |
michael@0 | 1191 | CK_OBJECT_CLASS objClass; |
michael@0 | 1192 | |
michael@0 | 1193 | |
michael@0 | 1194 | objClass = PK11_ReadULongAttribute(sourceSlot, id, CKA_CLASS); |
michael@0 | 1195 | if (objClass == (CK_ULONG) -1) { |
michael@0 | 1196 | PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE ); |
michael@0 | 1197 | return SECFailure; |
michael@0 | 1198 | } |
michael@0 | 1199 | |
michael@0 | 1200 | switch (objClass) { |
michael@0 | 1201 | case CKO_CERTIFICATE: |
michael@0 | 1202 | return pk11_mergeCert(targetSlot, sourceSlot, id, |
michael@0 | 1203 | targetPwArg, sourcePwArg); |
michael@0 | 1204 | case CKO_NSS_TRUST: |
michael@0 | 1205 | return pk11_mergeTrust(targetSlot, sourceSlot, id, |
michael@0 | 1206 | targetPwArg, sourcePwArg); |
michael@0 | 1207 | case CKO_PUBLIC_KEY: |
michael@0 | 1208 | return pk11_mergePublicKey(targetSlot, sourceSlot, id, |
michael@0 | 1209 | targetPwArg, sourcePwArg); |
michael@0 | 1210 | case CKO_PRIVATE_KEY: |
michael@0 | 1211 | return pk11_mergePrivateKey(targetSlot, sourceSlot, id, |
michael@0 | 1212 | targetPwArg, sourcePwArg); |
michael@0 | 1213 | case CKO_SECRET_KEY: |
michael@0 | 1214 | return pk11_mergeSecretKey(targetSlot, sourceSlot, id, |
michael@0 | 1215 | targetPwArg, sourcePwArg); |
michael@0 | 1216 | case CKO_NSS_CRL: |
michael@0 | 1217 | return pk11_mergeCrl(targetSlot, sourceSlot, id, |
michael@0 | 1218 | targetPwArg, sourcePwArg); |
michael@0 | 1219 | case CKO_NSS_SMIME: |
michael@0 | 1220 | return pk11_mergeSmime(targetSlot, sourceSlot, id, |
michael@0 | 1221 | targetPwArg, sourcePwArg); |
michael@0 | 1222 | default: |
michael@0 | 1223 | break; |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE ); |
michael@0 | 1227 | return SECFailure; |
michael@0 | 1228 | } |
michael@0 | 1229 | |
michael@0 | 1230 | PK11MergeLogNode * |
michael@0 | 1231 | pk11_newMergeLogNode(PLArenaPool *arena, |
michael@0 | 1232 | PK11SlotInfo *slot, CK_OBJECT_HANDLE id, int error) |
michael@0 | 1233 | { |
michael@0 | 1234 | PK11MergeLogNode *newLog; |
michael@0 | 1235 | PK11GenericObject *obj; |
michael@0 | 1236 | |
michael@0 | 1237 | newLog = PORT_ArenaZNew(arena, PK11MergeLogNode); |
michael@0 | 1238 | if (newLog == NULL) { |
michael@0 | 1239 | return NULL; |
michael@0 | 1240 | } |
michael@0 | 1241 | |
michael@0 | 1242 | obj = PORT_ArenaZNew(arena, PK11GenericObject); |
michael@0 | 1243 | if ( !obj ) { |
michael@0 | 1244 | return NULL; |
michael@0 | 1245 | } |
michael@0 | 1246 | |
michael@0 | 1247 | /* initialize it */ |
michael@0 | 1248 | obj->slot = slot; |
michael@0 | 1249 | obj->objectID = id; |
michael@0 | 1250 | |
michael@0 | 1251 | newLog->object= obj; |
michael@0 | 1252 | newLog->error = error; |
michael@0 | 1253 | return newLog; |
michael@0 | 1254 | } |
michael@0 | 1255 | |
michael@0 | 1256 | /* |
michael@0 | 1257 | * walk down each entry and merge it. keep track of the errors in the log |
michael@0 | 1258 | */ |
michael@0 | 1259 | static SECStatus |
michael@0 | 1260 | pk11_mergeByObjectIDs(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 1261 | CK_OBJECT_HANDLE *objectIDs, int count, |
michael@0 | 1262 | PK11MergeLog *log, void *targetPwArg, void *sourcePwArg) |
michael@0 | 1263 | { |
michael@0 | 1264 | SECStatus rv = SECSuccess; |
michael@0 | 1265 | int error, i; |
michael@0 | 1266 | |
michael@0 | 1267 | for (i=0; i < count; i++) { |
michael@0 | 1268 | /* try to update the entire database. On failure, keep going, |
michael@0 | 1269 | * but remember the error to report back to the caller */ |
michael@0 | 1270 | SECStatus lrv; |
michael@0 | 1271 | PK11MergeLogNode *newLog; |
michael@0 | 1272 | |
michael@0 | 1273 | lrv= pk11_mergeObject(targetSlot, sourceSlot, objectIDs[i], |
michael@0 | 1274 | targetPwArg, sourcePwArg); |
michael@0 | 1275 | if (lrv == SECSuccess) { |
michael@0 | 1276 | /* merged with no problem, go to next object */ |
michael@0 | 1277 | continue; |
michael@0 | 1278 | } |
michael@0 | 1279 | |
michael@0 | 1280 | /* remember that we failed and why */ |
michael@0 | 1281 | rv = SECFailure; |
michael@0 | 1282 | error = PORT_GetError(); |
michael@0 | 1283 | |
michael@0 | 1284 | /* log the errors */ |
michael@0 | 1285 | if (!log) { |
michael@0 | 1286 | /* not logging, go to next entry */ |
michael@0 | 1287 | continue; |
michael@0 | 1288 | } |
michael@0 | 1289 | newLog = pk11_newMergeLogNode(log->arena, sourceSlot, |
michael@0 | 1290 | objectIDs[i], error); |
michael@0 | 1291 | if (!newLog) { |
michael@0 | 1292 | /* failed to allocate entry, just keep going */ |
michael@0 | 1293 | continue; |
michael@0 | 1294 | } |
michael@0 | 1295 | |
michael@0 | 1296 | /* link in the errorlog entry */ |
michael@0 | 1297 | newLog->next = NULL; |
michael@0 | 1298 | if (log->tail) { |
michael@0 | 1299 | log->tail->next = newLog; |
michael@0 | 1300 | } else { |
michael@0 | 1301 | log->head = newLog; |
michael@0 | 1302 | } |
michael@0 | 1303 | newLog->prev = log->tail; |
michael@0 | 1304 | log->tail = newLog; |
michael@0 | 1305 | } |
michael@0 | 1306 | |
michael@0 | 1307 | /* restore the last error code */ |
michael@0 | 1308 | if (rv != SECSuccess) { |
michael@0 | 1309 | PORT_SetError(error); |
michael@0 | 1310 | } |
michael@0 | 1311 | return rv; |
michael@0 | 1312 | } |
michael@0 | 1313 | |
michael@0 | 1314 | /* |
michael@0 | 1315 | * Merge all the records in sourceSlot that aren't in targetSlot |
michael@0 | 1316 | * |
michael@0 | 1317 | * This function will return failure if not all the objects |
michael@0 | 1318 | * successfully merged. |
michael@0 | 1319 | * |
michael@0 | 1320 | * Applications can pass in an optional error log which will record |
michael@0 | 1321 | * each failing object and why it failed to import. PK11MergeLog |
michael@0 | 1322 | * is modelled after the CERTVerifyLog. |
michael@0 | 1323 | */ |
michael@0 | 1324 | SECStatus |
michael@0 | 1325 | PK11_MergeTokens(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot, |
michael@0 | 1326 | PK11MergeLog *log, void *targetPwArg, void *sourcePwArg) |
michael@0 | 1327 | { |
michael@0 | 1328 | SECStatus rv = SECSuccess, lrv = SECSuccess; |
michael@0 | 1329 | int error, count = 0; |
michael@0 | 1330 | CK_ATTRIBUTE search[2]; |
michael@0 | 1331 | CK_OBJECT_HANDLE *objectIDs = NULL; |
michael@0 | 1332 | CK_BBOOL ck_true = CK_TRUE; |
michael@0 | 1333 | CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY; |
michael@0 | 1334 | |
michael@0 | 1335 | PK11_SETATTRS(&search[0], CKA_TOKEN, &ck_true, sizeof(ck_true)); |
michael@0 | 1336 | PK11_SETATTRS(&search[1], CKA_CLASS, &privKey, sizeof(privKey)); |
michael@0 | 1337 | /* |
michael@0 | 1338 | * make sure both tokens are already authenticated if need be. |
michael@0 | 1339 | */ |
michael@0 | 1340 | rv = PK11_Authenticate(targetSlot, PR_TRUE, targetPwArg); |
michael@0 | 1341 | if (rv != SECSuccess) { |
michael@0 | 1342 | goto loser; |
michael@0 | 1343 | } |
michael@0 | 1344 | rv = PK11_Authenticate(sourceSlot, PR_TRUE, sourcePwArg); |
michael@0 | 1345 | if (rv != SECSuccess) { |
michael@0 | 1346 | goto loser; |
michael@0 | 1347 | } |
michael@0 | 1348 | |
michael@0 | 1349 | /* turns out the old DB's are rather fragile if the private keys aren't |
michael@0 | 1350 | * merged in first, so do the private keys explicity. */ |
michael@0 | 1351 | objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 2, &count); |
michael@0 | 1352 | if (objectIDs) { |
michael@0 | 1353 | lrv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, |
michael@0 | 1354 | objectIDs, count, log, |
michael@0 | 1355 | targetPwArg, sourcePwArg); |
michael@0 | 1356 | if (lrv != SECSuccess) { |
michael@0 | 1357 | error = PORT_GetError(); |
michael@0 | 1358 | } |
michael@0 | 1359 | PORT_Free(objectIDs); |
michael@0 | 1360 | count = 0; |
michael@0 | 1361 | } |
michael@0 | 1362 | |
michael@0 | 1363 | /* now do the rest (NOTE: this will repeat the private keys, but |
michael@0 | 1364 | * that shouldnt' be an issue as we will notice they are already |
michael@0 | 1365 | * merged in */ |
michael@0 | 1366 | objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 1, &count); |
michael@0 | 1367 | if (!objectIDs) { |
michael@0 | 1368 | rv = SECFailure; |
michael@0 | 1369 | goto loser; |
michael@0 | 1370 | } |
michael@0 | 1371 | |
michael@0 | 1372 | rv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, objectIDs, count, log, |
michael@0 | 1373 | targetPwArg, sourcePwArg); |
michael@0 | 1374 | if (rv == SECSuccess) { |
michael@0 | 1375 | /* if private keys failed, but the rest succeeded, be sure to let |
michael@0 | 1376 | * the caller know that private keys failed and why. |
michael@0 | 1377 | * NOTE: this is highly unlikely since the same keys that failed |
michael@0 | 1378 | * in the previous merge call will most likely fail in this one */ |
michael@0 | 1379 | if (lrv != SECSuccess) { |
michael@0 | 1380 | rv = lrv; |
michael@0 | 1381 | PORT_SetError(error); |
michael@0 | 1382 | } |
michael@0 | 1383 | } |
michael@0 | 1384 | |
michael@0 | 1385 | loser: |
michael@0 | 1386 | if (objectIDs) { |
michael@0 | 1387 | PORT_Free(objectIDs); |
michael@0 | 1388 | } |
michael@0 | 1389 | return rv; |
michael@0 | 1390 | } |
michael@0 | 1391 | |
michael@0 | 1392 | PK11MergeLog * |
michael@0 | 1393 | PK11_CreateMergeLog(void) |
michael@0 | 1394 | { |
michael@0 | 1395 | PLArenaPool *arena; |
michael@0 | 1396 | PK11MergeLog *log; |
michael@0 | 1397 | |
michael@0 | 1398 | arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); |
michael@0 | 1399 | if (arena == NULL) { |
michael@0 | 1400 | return NULL; |
michael@0 | 1401 | } |
michael@0 | 1402 | |
michael@0 | 1403 | log = PORT_ArenaZNew(arena, PK11MergeLog); |
michael@0 | 1404 | if (log == NULL) { |
michael@0 | 1405 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 1406 | return NULL; |
michael@0 | 1407 | } |
michael@0 | 1408 | log->arena = arena; |
michael@0 | 1409 | log->version = 1; |
michael@0 | 1410 | return log; |
michael@0 | 1411 | } |
michael@0 | 1412 | |
michael@0 | 1413 | void |
michael@0 | 1414 | PK11_DestroyMergeLog(PK11MergeLog *log) |
michael@0 | 1415 | { |
michael@0 | 1416 | if (log && log->arena) { |
michael@0 | 1417 | PORT_FreeArena(log->arena, PR_FALSE); |
michael@0 | 1418 | } |
michael@0 | 1419 | } |