security/nss/lib/pk11wrap/pk11merge.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 }

mercurial