security/nss/lib/pk11wrap/pk11merge.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/pk11wrap/pk11merge.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1419 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * Merge the source token into the target token.
    1.10 + */
    1.11 +
    1.12 +#include "secmod.h"
    1.13 +#include "secmodi.h"
    1.14 +#include "secmodti.h"
    1.15 +#include "pk11pub.h"
    1.16 +#include "pk11priv.h"
    1.17 +#include "pkcs11.h"
    1.18 +#include "seccomon.h"
    1.19 +#include "secerr.h"
    1.20 +#include "keyhi.h"
    1.21 +#include "hasht.h"
    1.22 +#include "cert.h"
    1.23 +#include "certdb.h"
    1.24 +
    1.25 +/*************************************************************************
    1.26 + *
    1.27 + *             short utilities to aid in the merge
    1.28 + *
    1.29 + *************************************************************************/
    1.30 +
    1.31 +/*
    1.32 + * write a bunch of attributes out to an existing object.
    1.33 + */
    1.34 +static SECStatus
    1.35 +pk11_setAttributes(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
    1.36 +		CK_ATTRIBUTE *setTemplate, CK_ULONG setTemplCount)
    1.37 +{
    1.38 +    CK_RV crv;
    1.39 +    CK_SESSION_HANDLE rwsession;
    1.40 +
    1.41 +    rwsession = PK11_GetRWSession(slot);
    1.42 +    if (rwsession == CK_INVALID_SESSION) {
    1.43 +    	PORT_SetError(SEC_ERROR_BAD_DATA);
    1.44 +    	return SECFailure;
    1.45 +    }
    1.46 +    crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
    1.47 +			setTemplate, setTemplCount);
    1.48 +    PK11_RestoreROSession(slot, rwsession);
    1.49 +    if (crv != CKR_OK) {
    1.50 +	PORT_SetError(PK11_MapError(crv));
    1.51 +	return SECFailure;
    1.52 +    }
    1.53 +    return SECSuccess;
    1.54 +}
    1.55 +
    1.56 +
    1.57 +/*
    1.58 + * copy a template of attributes from a source object to a target object.
    1.59 + * if target object is not given, create it.
    1.60 + */
    1.61 +static SECStatus
    1.62 +pk11_copyAttributes(PLArenaPool *arena,
    1.63 +	PK11SlotInfo *targetSlot, CK_OBJECT_HANDLE targetID,
    1.64 +	PK11SlotInfo *sourceSlot, CK_OBJECT_HANDLE sourceID,
    1.65 +	CK_ATTRIBUTE *copyTemplate, CK_ULONG copyTemplateCount)
    1.66 +{
    1.67 +    SECStatus rv = PK11_GetAttributes(arena, sourceSlot, sourceID, 
    1.68 +				copyTemplate, copyTemplateCount);
    1.69 +    if (rv != SECSuccess) {
    1.70 +	return rv;
    1.71 +    }
    1.72 +    if (targetID == CK_INVALID_HANDLE) {
    1.73 +	/* we need to create the object */
    1.74 +	rv = PK11_CreateNewObject(targetSlot, CK_INVALID_SESSION, 
    1.75 +		copyTemplate, copyTemplateCount, PR_TRUE, &targetID);
    1.76 +    } else {
    1.77 +	/* update the existing object with the new attributes */
    1.78 +	rv = pk11_setAttributes(targetSlot, targetID, 
    1.79 +			copyTemplate, copyTemplateCount);
    1.80 +    }
    1.81 +    return rv;
    1.82 +}
    1.83 +
    1.84 +/*
    1.85 + * look for a matching object across tokens.
    1.86 + */
    1.87 +static SECStatus
    1.88 +pk11_matchAcrossTokens(PLArenaPool *arena, PK11SlotInfo *targetSlot,
    1.89 +		       PK11SlotInfo *sourceSlot,
    1.90 +		       CK_ATTRIBUTE *template, CK_ULONG tsize, 
    1.91 +		       CK_OBJECT_HANDLE id, CK_OBJECT_HANDLE *peer)
    1.92 +{
    1.93 +
    1.94 +    CK_RV crv;
    1.95 +    *peer = CK_INVALID_HANDLE;
    1.96 +
    1.97 +    crv = PK11_GetAttributes(arena, sourceSlot, id, template, tsize);
    1.98 +    if (crv != CKR_OK) {
    1.99 + 	PORT_SetError( PK11_MapError(crv) );
   1.100 +	goto loser;
   1.101 +    }
   1.102 +
   1.103 +    if (template[0].ulValueLen == -1) {
   1.104 +	crv = CKR_ATTRIBUTE_TYPE_INVALID;
   1.105 + 	PORT_SetError( PK11_MapError(crv) );
   1.106 +	goto loser;
   1.107 +    }
   1.108 +
   1.109 +    *peer = pk11_FindObjectByTemplate(targetSlot, template, tsize);
   1.110 +    return SECSuccess;
   1.111 +
   1.112 +loser:
   1.113 +    return SECFailure;
   1.114 +}
   1.115 +
   1.116 +/*
   1.117 + * Encrypt using key and parameters
   1.118 + */
   1.119 +SECStatus
   1.120 +pk11_encrypt(PK11SymKey *symKey, CK_MECHANISM_TYPE mechType, SECItem *param,
   1.121 +	SECItem *input, SECItem **output)
   1.122 +{
   1.123 +    PK11Context *ctxt = NULL;
   1.124 +    SECStatus rv = SECSuccess;
   1.125 +
   1.126 +    if (*output) {
   1.127 +	SECITEM_FreeItem(*output,PR_TRUE);
   1.128 +    }
   1.129 +    *output = SECITEM_AllocItem(NULL, NULL, input->len+20 /*slop*/);
   1.130 +    if (!*output) {
   1.131 +	rv = SECFailure;
   1.132 +	goto done;
   1.133 +    }
   1.134 +
   1.135 +    ctxt = PK11_CreateContextBySymKey(mechType, CKA_ENCRYPT, symKey, param);
   1.136 +    if (ctxt == NULL) {
   1.137 +	rv = SECFailure;
   1.138 +	goto done;
   1.139 +    }
   1.140 +
   1.141 +    rv = PK11_CipherOp(ctxt, (*output)->data, 
   1.142 +		(int *)&((*output)->len), 
   1.143 +		(*output)->len, input->data, input->len);
   1.144 +
   1.145 +done:
   1.146 +    if (ctxt) {
   1.147 +	PK11_Finalize(ctxt);
   1.148 +	PK11_DestroyContext(ctxt,PR_TRUE);
   1.149 +    }
   1.150 +    if (rv != SECSuccess) {
   1.151 +	if (*output) {
   1.152 +	    SECITEM_FreeItem(*output, PR_TRUE);
   1.153 +	    *output = NULL;
   1.154 +	}
   1.155 +    }
   1.156 +    return rv;
   1.157 +}
   1.158 +
   1.159 +
   1.160 +
   1.161 +/*************************************************************************
   1.162 + *
   1.163 + *            Private Keys
   1.164 + *
   1.165 + *************************************************************************/
   1.166 +
   1.167 +/*
   1.168 + * Fetch the key usage based on the pkcs #11 flags
   1.169 + */
   1.170 +unsigned int
   1.171 +pk11_getPrivateKeyUsage(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
   1.172 +{
   1.173 +    unsigned int usage = 0;
   1.174 +
   1.175 +    if ((PK11_HasAttributeSet(slot, id, CKA_UNWRAP,PR_FALSE) || 
   1.176 +			PK11_HasAttributeSet(slot,id, CKA_DECRYPT,PR_FALSE))) {
   1.177 +	usage |= KU_KEY_ENCIPHERMENT;
   1.178 +    }
   1.179 +    if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) {
   1.180 +	usage |= KU_KEY_AGREEMENT;
   1.181 +    }
   1.182 +    if ((PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE) || 
   1.183 +			PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE))) {
   1.184 +	usage |= KU_DIGITAL_SIGNATURE;
   1.185 +    }
   1.186 +    return usage;
   1.187 +}
   1.188 +    
   1.189 +	
   1.190 +/*
   1.191 + * merge a private key, 
   1.192 + *
   1.193 + * Private keys are merged using PBE wrapped keys with a random
   1.194 + * value as the 'password'. Once the base key is moved, The remaining
   1.195 + * attributes (SUBJECT) is copied.
   1.196 + */
   1.197 +static SECStatus
   1.198 +pk11_mergePrivateKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
   1.199 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
   1.200 +{
   1.201 +    SECKEYPrivateKey *sourceKey = NULL;
   1.202 +    CK_OBJECT_HANDLE targetKeyID;
   1.203 +    SECKEYEncryptedPrivateKeyInfo *epki = NULL;
   1.204 +    char *nickname = NULL;
   1.205 +    SECItem nickItem;
   1.206 +    SECItem pwitem;
   1.207 +    SECItem publicValue;
   1.208 +    PLArenaPool *arena = NULL;
   1.209 +    SECStatus rv = SECSuccess;
   1.210 +    unsigned int keyUsage;
   1.211 +    unsigned char randomData[SHA1_LENGTH];
   1.212 +    SECOidTag algTag = SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
   1.213 +    CK_ATTRIBUTE privTemplate[] = {
   1.214 +	{ CKA_ID, NULL, 0 },
   1.215 +	{ CKA_CLASS, NULL, 0 }
   1.216 +    };
   1.217 +    CK_ULONG privTemplateCount = sizeof(privTemplate)/sizeof(privTemplate[0]);
   1.218 +    CK_ATTRIBUTE privCopyTemplate[] = {
   1.219 +	{ CKA_SUBJECT, NULL, 0 }
   1.220 +    };
   1.221 +    CK_ULONG privCopyTemplateCount = 
   1.222 +		sizeof(privCopyTemplate)/sizeof(privCopyTemplate[0]);
   1.223 +
   1.224 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
   1.225 +    if (arena == NULL) {
   1.226 +	rv = SECFailure;
   1.227 +	goto done;
   1.228 +    }
   1.229 +
   1.230 +    /* check to see if the key is already in the target slot */
   1.231 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate, 
   1.232 +				privTemplateCount, id, &targetKeyID);
   1.233 +    if (rv != SECSuccess) {
   1.234 +	goto done;
   1.235 +    }
   1.236 +
   1.237 +    if (targetKeyID != CK_INVALID_HANDLE) {
   1.238 +	/* match found,  not an error ... */
   1.239 +	goto done;
   1.240 +    }
   1.241 +
   1.242 +    /* get an NSS representation of our source key */
   1.243 +    sourceKey = PK11_MakePrivKey(sourceSlot, nullKey, PR_FALSE, 
   1.244 +				 id, sourcePwArg);
   1.245 +    if (sourceKey == NULL) {
   1.246 +	rv = SECFailure;
   1.247 +	goto done;
   1.248 +    }
   1.249 +
   1.250 +    /* Load the private key */
   1.251 +    /* generate a random pwitem */
   1.252 +    rv = PK11_GenerateRandom(randomData, sizeof(randomData));
   1.253 +    if (rv != SECSuccess) {
   1.254 +	goto done;
   1.255 +    }
   1.256 +    pwitem.data = randomData;
   1.257 +    pwitem.len = sizeof(randomData);
   1.258 +    /* fetch the private key encrypted */
   1.259 +    epki = PK11_ExportEncryptedPrivKeyInfo(sourceSlot, algTag, &pwitem, 
   1.260 +					   sourceKey, 1, sourcePwArg);
   1.261 +    if (epki == NULL) {
   1.262 +	rv = SECFailure;
   1.263 +	goto done;
   1.264 +    }
   1.265 +    nickname = PK11_GetObjectNickname(sourceSlot, id);
   1.266 +    /* NULL nickanme is fine (in fact is often normal) */
   1.267 +    if (nickname)  {
   1.268 +	nickItem.data = (unsigned char *)nickname;
   1.269 +	nickItem.len = PORT_Strlen(nickname);
   1.270 +    }
   1.271 +    keyUsage = pk11_getPrivateKeyUsage(sourceSlot, id);
   1.272 +    /* pass in the CKA_ID */
   1.273 +    publicValue.data = privTemplate[0].pValue;
   1.274 +    publicValue.len = privTemplate[0].ulValueLen;
   1.275 +    rv = PK11_ImportEncryptedPrivateKeyInfo(targetSlot, epki, &pwitem,
   1.276 +			nickname? &nickItem : NULL , &publicValue, 
   1.277 +			PR_TRUE, PR_TRUE, sourceKey->keyType, keyUsage, 
   1.278 +			targetPwArg);
   1.279 +    if (rv != SECSuccess) {
   1.280 +	goto done;
   1.281 +    }
   1.282 +
   1.283 +    /* make sure it made it */
   1.284 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate, 
   1.285 +				privTemplateCount, id, &targetKeyID);
   1.286 +    if (rv != SECSuccess) {
   1.287 +	goto done;
   1.288 +    }
   1.289 +
   1.290 +    if (targetKeyID == CK_INVALID_HANDLE) {
   1.291 +	/* this time the key should exist */
   1.292 +	rv = SECFailure;
   1.293 +	goto done;
   1.294 +    }
   1.295 +
   1.296 +    /* fill in remaining attributes */
   1.297 +    rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
   1.298 +				privCopyTemplate, privCopyTemplateCount);
   1.299 +done:
   1.300 +    /* make sure the 'key' is cleared */
   1.301 +    PORT_Memset(randomData, 0, sizeof(randomData));
   1.302 +    if (nickname) {
   1.303 +	PORT_Free(nickname);
   1.304 +    }
   1.305 +    if (sourceKey) {
   1.306 +	SECKEY_DestroyPrivateKey(sourceKey);
   1.307 +    }
   1.308 +    if (epki) {
   1.309 +	SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
   1.310 +    }
   1.311 +    if (arena) {
   1.312 +         PORT_FreeArena(arena,PR_FALSE);
   1.313 +    }
   1.314 +    return rv;
   1.315 +}
   1.316 +
   1.317 +
   1.318 +/*************************************************************************
   1.319 + *
   1.320 + *            Secret Keys
   1.321 + *
   1.322 + *************************************************************************/
   1.323 +
   1.324 +/*
   1.325 + * we need to find a unique CKA_ID.
   1.326 + *  The basic idea is to just increment the lowest byte.
   1.327 + *  This code also handles the following corner cases:
   1.328 + *   1) the single byte overflows. On overflow we increment the next byte up 
   1.329 + *    and so forth until we have overflowed the entire CKA_ID.
   1.330 + *   2) If we overflow the entire CKA_ID we expand it by one byte.
   1.331 + *   3) the CKA_ID is non-existent, we create a new one with one byte.
   1.332 + *    This means no matter what CKA_ID is passed, the result of this function 
   1.333 + *    is always a new CKA_ID, and this function will never return the same 
   1.334 + *    CKA_ID the it has returned in the passed.
   1.335 + */
   1.336 +static SECStatus
   1.337 +pk11_incrementID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
   1.338 +{
   1.339 +    unsigned char *buf = ptemplate->pValue;
   1.340 +    CK_ULONG len = ptemplate->ulValueLen;
   1.341 +
   1.342 +    if (buf == NULL || len == (CK_ULONG)-1) {
   1.343 +	/* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
   1.344 +	len = 0;
   1.345 +    } else {
   1.346 +	CK_ULONG i;
   1.347 +
   1.348 +	/* walk from the back to front, incrementing
   1.349 +	 * the CKA_ID until we no longer have a carry,
   1.350 +	 * or have hit the front of the id. */
   1.351 +	for (i=len; i != 0; i--) {
   1.352 +	    buf[i-1]++;
   1.353 +	    if (buf[i-1] != 0) {
   1.354 +		/* no more carries, the increment is complete */
   1.355 +		return SECSuccess;
   1.356 +	     }
   1.357 +	}
   1.358 +	/* we've now overflowed, fall through and expand the CKA_ID by 
   1.359 +	 * one byte */
   1.360 +    } 
   1.361 +    /* if we are here we've run the counter to zero (indicating an overflow).
   1.362 +     * create an CKA_ID that is all zeros, but has one more zero than
   1.363 +     * the previous CKA_ID */
   1.364 +    buf = PORT_ArenaZAlloc(arena, len+1);
   1.365 +    if (buf == NULL) {
   1.366 +	return SECFailure;
   1.367 +    }
   1.368 +    ptemplate->pValue = buf;
   1.369 +    ptemplate->ulValueLen = len+1;
   1.370 +    return SECSuccess;
   1.371 +}
   1.372 +
   1.373 +
   1.374 +static CK_FLAGS
   1.375 +pk11_getSecretKeyFlags(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
   1.376 +{
   1.377 +    CK_FLAGS flags = 0;
   1.378 +
   1.379 +    if (PK11_HasAttributeSet(slot, id, CKA_UNWRAP, PR_FALSE)) {
   1.380 +	flags |= CKF_UNWRAP;
   1.381 +    }
   1.382 +    if (PK11_HasAttributeSet(slot, id, CKA_WRAP, PR_FALSE)) {
   1.383 +	flags |= CKF_WRAP;
   1.384 +    }
   1.385 +    if (PK11_HasAttributeSet(slot, id, CKA_ENCRYPT, PR_FALSE)) {
   1.386 +	flags |= CKF_ENCRYPT;
   1.387 +    }
   1.388 +    if (PK11_HasAttributeSet(slot, id, CKA_DECRYPT, PR_FALSE)) {
   1.389 +	flags |= CKF_DECRYPT;
   1.390 +    }
   1.391 +    if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) {
   1.392 +	flags |= CKF_DERIVE;
   1.393 +    }
   1.394 +    if (PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE)) {
   1.395 +	flags |= CKF_SIGN;
   1.396 +    }
   1.397 +    if (PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE)) {
   1.398 +	flags |= CKF_SIGN_RECOVER;
   1.399 +    }
   1.400 +    if (PK11_HasAttributeSet(slot, id, CKA_VERIFY, PR_FALSE)) {
   1.401 +	flags |= CKF_VERIFY;
   1.402 +    }
   1.403 +    if (PK11_HasAttributeSet(slot, id, CKA_VERIFY_RECOVER, PR_FALSE)) {
   1.404 +	flags |= CKF_VERIFY_RECOVER;
   1.405 +    }
   1.406 +    return flags;
   1.407 +}
   1.408 +
   1.409 +static const char testString[] = 
   1.410 +	"My Encrytion Test Data (should be at least 32 bytes long)";
   1.411 +/*
   1.412 + * merge a secret key, 
   1.413 + *
   1.414 + * Secret keys may collide by CKA_ID as we merge 2 token. If we collide
   1.415 + * on the CKA_ID, we need to make sure we are dealing with different keys.
   1.416 + * The reason for this is it is possible that we've merged this database
   1.417 + * before, and this key could have been merged already.  If the keys are
   1.418 + * the same, we are done. If they are not, we need to update the CKA_ID of
   1.419 + * the source key and try again.
   1.420 + * 
   1.421 + * Once we know we have a unique key to merge in, we use NSS's underlying
   1.422 + * key Move function which will do a key exchange if necessary to move
   1.423 + * the key from one token to another. Then we set the CKA_ID and additional
   1.424 + * pkcs #11 attributes.
   1.425 + */
   1.426 +static SECStatus
   1.427 +pk11_mergeSecretKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
   1.428 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
   1.429 +{
   1.430 +    PK11SymKey *sourceKey = NULL;
   1.431 +    PK11SymKey *targetKey = NULL;
   1.432 +    SECItem *sourceOutput = NULL;
   1.433 +    SECItem *targetOutput = NULL;
   1.434 +    SECItem *param = NULL;
   1.435 +    int blockSize;
   1.436 +    SECItem input;
   1.437 +    CK_OBJECT_HANDLE targetKeyID;
   1.438 +    CK_FLAGS flags;
   1.439 +    PLArenaPool *arena = NULL;
   1.440 +    SECStatus rv = SECSuccess;
   1.441 +    CK_MECHANISM_TYPE keyMechType, cryptoMechType;
   1.442 +    CK_KEY_TYPE sourceKeyType, targetKeyType;
   1.443 +    CK_ATTRIBUTE symTemplate[] = {
   1.444 +	{ CKA_ID, NULL, 0 },
   1.445 +	{ CKA_CLASS, NULL, 0 }
   1.446 +    };
   1.447 +    CK_ULONG symTemplateCount = sizeof(symTemplate)/sizeof(symTemplate[0]);
   1.448 +    CK_ATTRIBUTE symCopyTemplate[] = {
   1.449 +	{ CKA_LABEL, NULL, 0 }
   1.450 +    };
   1.451 +    CK_ULONG symCopyTemplateCount = 
   1.452 +		sizeof(symCopyTemplate)/sizeof(symCopyTemplate[0]);
   1.453 +
   1.454 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
   1.455 +    if (arena == NULL) {
   1.456 +	rv = SECFailure;
   1.457 +	goto done;
   1.458 +    }
   1.459 +
   1.460 +    sourceKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
   1.461 +    if (sourceKeyType == (CK_ULONG) -1) {
   1.462 +	rv = SECFailure;
   1.463 +	goto done;
   1.464 +    }
   1.465 +
   1.466 +    /* get the key mechanism */
   1.467 +    keyMechType = PK11_GetKeyMechanism(sourceKeyType);
   1.468 +    /* get a mechanism suitable to encryption.
   1.469 +     * PK11_GetKeyMechanism returns a mechanism that is unique to the key
   1.470 +     * type. It tries to return encryption/decryption mechanisms, however
   1.471 +     * CKM_DES3_CBC uses and abmiguous keyType, so keyMechType is returned as
   1.472 +     * 'keygen' mechanism. Detect that case here */
   1.473 +    cryptoMechType =  keyMechType;
   1.474 +    if ((keyMechType == CKM_DES3_KEY_GEN) ||  
   1.475 +				(keyMechType == CKM_DES2_KEY_GEN)) {
   1.476 +	cryptoMechType = CKM_DES3_CBC;
   1.477 +    }
   1.478 +
   1.479 +    sourceKey = PK11_SymKeyFromHandle(sourceSlot, NULL, PK11_OriginDerive,
   1.480 +				keyMechType , id, PR_FALSE, sourcePwArg);
   1.481 +    if (sourceKey == NULL) {
   1.482 +	rv = SECFailure;
   1.483 +	goto done;
   1.484 +    }
   1.485 +
   1.486 +    /* check to see a key with the same CKA_ID  already exists in 
   1.487 +     * the target slot. If it does, then we need to verify if the keys
   1.488 +     * really matches. If they don't import the key with a new CKA_ID
   1.489 +     * value. */
   1.490 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot,
   1.491 +			symTemplate, symTemplateCount, id, &targetKeyID);
   1.492 +    if (rv != SECSuccess) {
   1.493 +	goto done;
   1.494 +    }
   1.495 +
   1.496 +    /* set up the input test */
   1.497 +    input.data = (unsigned char *)testString;
   1.498 +    blockSize = PK11_GetBlockSize(cryptoMechType, NULL);
   1.499 +    if (blockSize < 0) {
   1.500 +	rv = SECFailure;
   1.501 +	goto done;
   1.502 +    }
   1.503 +    input.len = blockSize;
   1.504 +    if (input.len == 0) {
   1.505 +	input.len = sizeof (testString);
   1.506 +    }
   1.507 +    while (targetKeyID != CK_INVALID_HANDLE) {
   1.508 +	/* test to see if the keys are identical */
   1.509 +	targetKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
   1.510 +	if (targetKeyType == sourceKeyType) {
   1.511 +		/* same keyType  - see if it's the same key */
   1.512 +		targetKey = PK11_SymKeyFromHandle(targetSlot, NULL, 
   1.513 +			PK11_OriginDerive, keyMechType, targetKeyID, PR_FALSE,
   1.514 +			targetPwArg);
   1.515 +		/* get a parameter if we don't already have one */
   1.516 +		if (!param) {
   1.517 +		    param = PK11_GenerateNewParam(cryptoMechType, sourceKey);
   1.518 +		    if (param == NULL) {
   1.519 +			rv = SECFailure;
   1.520 +			goto done;
   1.521 +		    }
   1.522 +		}
   1.523 +		/* use the source key to encrypt a reference */
   1.524 +		if (!sourceOutput) {
   1.525 +		    rv = pk11_encrypt(sourceKey, cryptoMechType, param, &input,
   1.526 +			&sourceOutput);
   1.527 +		    if (rv != SECSuccess) {
   1.528 +			goto done;
   1.529 +		    }
   1.530 +		}
   1.531 +		/* encrypt the reference with the target key */
   1.532 +		rv = pk11_encrypt(targetKey, cryptoMechType, param, &input,
   1.533 +			&targetOutput);
   1.534 +		if (rv == SECSuccess) {
   1.535 +		    if (SECITEM_ItemsAreEqual(sourceOutput, targetOutput)) {
   1.536 +			/* they produce the same output, they must be the
   1.537 +			 * same key */
   1.538 +			goto done;
   1.539 +		    }
   1.540 +		    SECITEM_FreeItem(targetOutput, PR_TRUE);
   1.541 +		    targetOutput = NULL;
   1.542 +		}
   1.543 +		PK11_FreeSymKey(targetKey);
   1.544 +		targetKey = NULL;
   1.545 +	}
   1.546 +	/* keys aren't equal, update the KEY_ID and look again */
   1.547 +	rv = pk11_incrementID(arena, &symTemplate[0]);
   1.548 +	if (rv != SECSuccess) {
   1.549 +	    goto done;
   1.550 +	}
   1.551 +	targetKeyID = pk11_FindObjectByTemplate(targetSlot, 
   1.552 +					symTemplate, symTemplateCount);
   1.553 +    }
   1.554 +
   1.555 +    /* we didn't find a matching key, import this one with the new
   1.556 +     * CKAID */
   1.557 +    flags = pk11_getSecretKeyFlags(sourceSlot, id);
   1.558 +    targetKey = PK11_MoveSymKey(targetSlot, PK11_OriginDerive, flags, PR_TRUE,
   1.559 +			sourceKey);
   1.560 +    if (targetKey == NULL) {
   1.561 +	rv = SECFailure;
   1.562 +	goto done;
   1.563 +    }
   1.564 +    /* set the key new CKAID */
   1.565 +    rv = pk11_setAttributes(targetSlot, targetKey->objectID, symTemplate, 1);
   1.566 +    if (rv != SECSuccess) {
   1.567 +	goto done;
   1.568 +    }
   1.569 +
   1.570 +    /* fill in remaining attributes */
   1.571 +    rv = pk11_copyAttributes(arena, targetSlot, targetKey->objectID, 
   1.572 +			sourceSlot, id, symCopyTemplate, symCopyTemplateCount);
   1.573 +done:
   1.574 +    if (sourceKey) {
   1.575 +	PK11_FreeSymKey(sourceKey);
   1.576 +    }
   1.577 +    if (targetKey) {
   1.578 +	PK11_FreeSymKey(targetKey);
   1.579 +    }
   1.580 +    if (sourceOutput) {
   1.581 +	SECITEM_FreeItem(sourceOutput, PR_TRUE);
   1.582 +    }
   1.583 +    if (targetOutput) {
   1.584 +	SECITEM_FreeItem(targetOutput, PR_TRUE);
   1.585 +    }
   1.586 +    if (param) {
   1.587 +	SECITEM_FreeItem(param, PR_TRUE);
   1.588 +    }
   1.589 +    if (arena) {
   1.590 +         PORT_FreeArena(arena,PR_FALSE);
   1.591 +    }
   1.592 +    return rv;
   1.593 +}
   1.594 +
   1.595 +/*************************************************************************
   1.596 + *
   1.597 + *            Public Keys
   1.598 + *
   1.599 + *************************************************************************/
   1.600 +
   1.601 +/*
   1.602 + * Merge public key
   1.603 + *
   1.604 + * Use the high level NSS calls to extract the public key and import it
   1.605 + * into the token. Extra attributes are then copied to the new token.
   1.606 + */
   1.607 +static SECStatus
   1.608 +pk11_mergePublicKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
   1.609 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
   1.610 +{
   1.611 +    SECKEYPublicKey *sourceKey = NULL;
   1.612 +    CK_OBJECT_HANDLE targetKeyID;
   1.613 +    PLArenaPool *arena = NULL;
   1.614 +    SECStatus rv = SECSuccess;
   1.615 +    CK_ATTRIBUTE pubTemplate[] = {
   1.616 +	{ CKA_ID, NULL, 0 },
   1.617 +	{ CKA_CLASS, NULL, 0 }
   1.618 +    };
   1.619 +    CK_ULONG pubTemplateCount = sizeof(pubTemplate)/sizeof(pubTemplate[0]);
   1.620 +    CK_ATTRIBUTE pubCopyTemplate[] = {
   1.621 +	{ CKA_ID, NULL, 0 },
   1.622 +	{ CKA_LABEL, NULL, 0 },
   1.623 +	{ CKA_SUBJECT, NULL, 0 }
   1.624 +    };
   1.625 +    CK_ULONG pubCopyTemplateCount = 
   1.626 +		sizeof(pubCopyTemplate)/sizeof(pubCopyTemplate[0]);
   1.627 +
   1.628 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
   1.629 +    if (arena == NULL) {
   1.630 +	rv = SECFailure;
   1.631 +	goto done;
   1.632 +    }
   1.633 +
   1.634 +
   1.635 +    /* check to see if the key is already in the target slot */
   1.636 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, pubTemplate, 
   1.637 +				pubTemplateCount, id, &targetKeyID);
   1.638 +    if (rv != SECSuccess) {
   1.639 +	goto done;
   1.640 +    }
   1.641 +
   1.642 +    /* Key is already in the target slot */
   1.643 +    if (targetKeyID != CK_INVALID_HANDLE) {
   1.644 +	/* not an error ... */
   1.645 +	goto done;
   1.646 +    }
   1.647 +
   1.648 +    /* fetch an NSS representation of the public key */
   1.649 +    sourceKey = PK11_ExtractPublicKey(sourceSlot, nullKey, id);
   1.650 +    if (sourceKey== NULL) {
   1.651 +	rv = SECFailure;
   1.652 +	goto done;
   1.653 +    }
   1.654 +
   1.655 +    /* load the public key into the target token. */
   1.656 +    targetKeyID = PK11_ImportPublicKey(targetSlot, sourceKey, PR_TRUE);
   1.657 +    if (targetKeyID == CK_INVALID_HANDLE) {
   1.658 +	rv = SECFailure;
   1.659 +	goto done;
   1.660 +    }
   1.661 +
   1.662 +    /* fill in remaining attributes */
   1.663 +    rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
   1.664 +				pubCopyTemplate, pubCopyTemplateCount);
   1.665 +
   1.666 +
   1.667 +done:
   1.668 +    if (sourceKey) {
   1.669 +	SECKEY_DestroyPublicKey(sourceKey);
   1.670 +    }
   1.671 +    if (arena) {
   1.672 +         PORT_FreeArena(arena,PR_FALSE);
   1.673 +    }
   1.674 +    return rv;
   1.675 +}
   1.676 +
   1.677 +/*************************************************************************
   1.678 + *
   1.679 + *            Certificates
   1.680 + *
   1.681 + *************************************************************************/
   1.682 +
   1.683 +/*
   1.684 + * Two copies of the source code for this algorithm exist in NSS.  
   1.685 + * Changes must be made in both copies.
   1.686 + * The other copy is in sftkdb_resolveConflicts() in softoken/sftkdb.c.
   1.687 + */
   1.688 +static char *
   1.689 +pk11_IncrementNickname(char *nickname)
   1.690 +{
   1.691 +    char *newNickname = NULL;
   1.692 +    int end;
   1.693 +    int digit;
   1.694 +    int len = strlen(nickname);
   1.695 +
   1.696 +    /* does nickname end with " #n*" ? */
   1.697 +    for (end = len - 1; 
   1.698 +         end >= 2 && (digit = nickname[end]) <= '9' &&  digit >= '0'; 
   1.699 +	 end--)  /* just scan */ ;
   1.700 +    if (len >= 3 &&
   1.701 +        end < (len - 1) /* at least one digit */ &&
   1.702 +	nickname[end]     == '#'  && 
   1.703 +	nickname[end - 1] == ' ') {
   1.704 +    	/* Already has a suitable suffix string */
   1.705 +    } else {
   1.706 +	/* ... append " #2" to the name */
   1.707 +	static const char num2[] = " #2";
   1.708 +	newNickname = PORT_Realloc(nickname, len + sizeof(num2));
   1.709 +	if (newNickname) {
   1.710 +	    PORT_Strcat(newNickname, num2);
   1.711 +	} else {
   1.712 +	    PORT_Free(nickname);
   1.713 +	}
   1.714 +	return newNickname;
   1.715 +    }
   1.716 +
   1.717 +    for (end = len - 1; 
   1.718 +	 end >= 0 && (digit = nickname[end]) <= '9' &&  digit >= '0'; 
   1.719 +	 end--) {
   1.720 +	if (digit < '9') {
   1.721 +	    nickname[end]++;
   1.722 +	    return nickname;
   1.723 +	}
   1.724 +	nickname[end] = '0';
   1.725 +    }
   1.726 +
   1.727 +    /* we overflowed, insert a new '1' for a carry in front of the number */
   1.728 +    newNickname = PORT_Realloc(nickname, len + 2);
   1.729 +    if (newNickname) {
   1.730 +	newNickname[++end] = '1';
   1.731 +	PORT_Memset(&newNickname[end + 1], '0', len - end);
   1.732 +	newNickname[len + 1] = 0;
   1.733 +    } else {
   1.734 +	PORT_Free(nickname);
   1.735 +    }
   1.736 +    return newNickname;
   1.737 +}
   1.738 +
   1.739 +/*
   1.740 + * merge a certificate object
   1.741 + *
   1.742 + * Use the high level NSS calls to extract and import the certificate.
   1.743 + */
   1.744 +static SECStatus
   1.745 +pk11_mergeCert(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
   1.746 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
   1.747 +{
   1.748 +    CERTCertificate *sourceCert = NULL;
   1.749 +    CK_OBJECT_HANDLE targetCertID = CK_INVALID_HANDLE;
   1.750 +    char *nickname = NULL;
   1.751 +    SECStatus rv = SECSuccess;
   1.752 +    PLArenaPool *arena = NULL;
   1.753 +    CK_ATTRIBUTE sourceCKAID = {CKA_ID, NULL, 0};
   1.754 +    CK_ATTRIBUTE targetCKAID = {CKA_ID, NULL, 0};
   1.755 +    SECStatus lrv = SECSuccess;
   1.756 +    int error;
   1.757 +
   1.758 +
   1.759 +    sourceCert = PK11_MakeCertFromHandle(sourceSlot, id, NULL);
   1.760 +    if (sourceCert == NULL) {
   1.761 +	rv = SECFailure;
   1.762 +	goto done;
   1.763 +    }
   1.764 +
   1.765 +    nickname = PK11_GetObjectNickname(sourceSlot, id);
   1.766 +
   1.767 +    /* The database code will prevent nickname collisions for certs with
   1.768 +     * different subjects. This code will prevent us from getting
   1.769 +     * actual import errors */
   1.770 +    if (nickname) {
   1.771 +	const char *tokenName = PK11_GetTokenName(targetSlot);
   1.772 +	char *tokenNickname = NULL;
   1.773 +
   1.774 +	do {
   1.775 +	    tokenNickname = PR_smprintf("%s:%s",tokenName, nickname);
   1.776 +	    if (!tokenNickname) {
   1.777 +		break;
   1.778 +	    }
   1.779 +	    if (!SEC_CertNicknameConflict(tokenNickname, 
   1.780 +			&sourceCert->derSubject, CERT_GetDefaultCertDB())) {
   1.781 +		break;
   1.782 +	     }
   1.783 +	    nickname = pk11_IncrementNickname(nickname);
   1.784 +	    if (!nickname) {
   1.785 +		break;
   1.786 +	    }
   1.787 +	    PR_smprintf_free(tokenNickname);
   1.788 +	} while (1);
   1.789 +	if (tokenNickname) {
   1.790 +	    PR_smprintf_free(tokenNickname);
   1.791 +	}
   1.792 +    }
   1.793 +
   1.794 +	
   1.795 +
   1.796 +    /* see if the cert is already there */
   1.797 +    targetCertID = PK11_FindCertInSlot(targetSlot, sourceCert, targetPwArg);
   1.798 +    if (targetCertID == CK_INVALID_HANDLE) {
   1.799 +	/* cert doesn't exist load the cert in. */
   1.800 +	/* OK for the nickname to be NULL, not all certs have nicknames */
   1.801 +	rv = PK11_ImportCert(targetSlot, sourceCert, CK_INVALID_HANDLE,
   1.802 +			     nickname, PR_FALSE);
   1.803 +	goto done;
   1.804 +    }
   1.805 +
   1.806 +    /* the cert already exists, see if the nickname and/or  CKA_ID need
   1.807 +     * to be updated */
   1.808 +
   1.809 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
   1.810 +    if (arena == NULL) {
   1.811 +	rv = SECFailure;
   1.812 +	goto done;
   1.813 +    }
   1.814 +
   1.815 +    /* does our source have a CKA_ID ? */
   1.816 +    rv = PK11_GetAttributes(arena, sourceSlot, id,  &sourceCKAID, 1);
   1.817 +    if (rv != SECSuccess) {
   1.818 +	sourceCKAID.ulValueLen = 0;
   1.819 +    }
   1.820 +
   1.821 +    /* if we have a source CKA_ID, see of we need to update the
   1.822 +     * target's CKA_ID */
   1.823 +    if (sourceCKAID.ulValueLen != 0) {
   1.824 +	rv = PK11_GetAttributes(arena, targetSlot, targetCertID,
   1.825 +				    &targetCKAID, 1);
   1.826 +	if (rv != SECSuccess) {
   1.827 +	    targetCKAID.ulValueLen = 0;
   1.828 +	}
   1.829 +	/* if the target has no CKA_ID, update it from the source */
   1.830 +	if (targetCKAID.ulValueLen == 0) {
   1.831 +	    lrv=pk11_setAttributes(targetSlot, targetCertID, &sourceCKAID, 1);
   1.832 +	    if (lrv != SECSuccess) {
   1.833 +		error = PORT_GetError();
   1.834 +	    }
   1.835 +	}
   1.836 +    }
   1.837 +    rv = SECSuccess;
   1.838 +
   1.839 +    /* now check if we need to update the nickname */
   1.840 +    if (nickname && *nickname) {
   1.841 +	char *targetname;
   1.842 +	targetname = PK11_GetObjectNickname(targetSlot, targetCertID);
   1.843 +	if (!targetname || !*targetname) {
   1.844 +	    /* target has no nickname, or it's empty, update it */
   1.845 +	    rv = PK11_SetObjectNickname(targetSlot, targetCertID, nickname);
   1.846 +	}
   1.847 +	if (targetname) {
   1.848 +	    PORT_Free(targetname);
   1.849 +	}
   1.850 +    }
   1.851 +
   1.852 +    /* restore the error code if CKA_ID failed, but nickname didn't */
   1.853 +    if ((rv == SECSuccess) && (lrv != SECSuccess)) {
   1.854 +	rv = lrv;
   1.855 +	PORT_SetError(error);
   1.856 +    }
   1.857 +
   1.858 +done:
   1.859 +    if (nickname) {
   1.860 +	PORT_Free(nickname);
   1.861 +    }
   1.862 +    if (sourceCert) {
   1.863 +	CERT_DestroyCertificate(sourceCert);
   1.864 +    }
   1.865 +    if (arena) {
   1.866 +         PORT_FreeArena(arena,PR_FALSE);
   1.867 +    }
   1.868 +    return rv;
   1.869 +}
   1.870 +
   1.871 +
   1.872 +/*************************************************************************
   1.873 + *
   1.874 + *            Crls
   1.875 + *
   1.876 + *************************************************************************/
   1.877 +
   1.878 +/*
   1.879 + * Use the raw PKCS #11 interface to merge the CRLs.
   1.880 + *
   1.881 + * In the case where of collision, choose the newest CRL that is valid.
   1.882 + */
   1.883 +static SECStatus
   1.884 +pk11_mergeCrl(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
   1.885 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
   1.886 +{
   1.887 +    CK_OBJECT_HANDLE targetCrlID;
   1.888 +    PLArenaPool *arena = NULL;
   1.889 +    SECStatus rv = SECSuccess;
   1.890 +    CK_ATTRIBUTE crlTemplate[] = {
   1.891 +	{ CKA_SUBJECT, NULL, 0 },
   1.892 +	{ CKA_CLASS, NULL, 0 },
   1.893 +	{ CKA_NSS_KRL, NULL, 0 }
   1.894 +    };
   1.895 +    CK_ULONG crlTemplateCount = sizeof(crlTemplate)/sizeof(crlTemplate[0]);
   1.896 +    CK_ATTRIBUTE crlCopyTemplate[] = {
   1.897 +	{ CKA_CLASS, NULL, 0 },
   1.898 +	{ CKA_TOKEN, NULL, 0 },
   1.899 +	{ CKA_LABEL, NULL, 0 },
   1.900 +	{ CKA_PRIVATE, NULL, 0 },
   1.901 +	{ CKA_MODIFIABLE, NULL, 0 },
   1.902 +	{ CKA_SUBJECT, NULL, 0 },
   1.903 +	{ CKA_NSS_KRL, NULL, 0 },
   1.904 +	{ CKA_NSS_URL, NULL, 0 },
   1.905 +	{ CKA_VALUE, NULL, 0 }
   1.906 +    };
   1.907 +    CK_ULONG crlCopyTemplateCount = 
   1.908 +		sizeof(crlCopyTemplate)/sizeof(crlCopyTemplate[0]);
   1.909 +
   1.910 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
   1.911 +    if (arena == NULL) {
   1.912 +	rv = SECFailure;
   1.913 +	goto done;
   1.914 +    }
   1.915 +    /* check to see if the crl is already in the target slot */
   1.916 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, crlTemplate, 
   1.917 +				crlTemplateCount, id, &targetCrlID);
   1.918 +    if (rv != SECSuccess) {
   1.919 +	goto done;
   1.920 +    }
   1.921 +    if (targetCrlID != CK_INVALID_HANDLE) {
   1.922 +	/* we already have a CRL, check to see which is more up-to-date. */
   1.923 +	goto done;
   1.924 +    }
   1.925 +
   1.926 +    /* load the CRL into the target token. */
   1.927 +    rv = pk11_copyAttributes(arena, targetSlot, targetCrlID, sourceSlot, id,
   1.928 +				crlCopyTemplate, crlCopyTemplateCount);
   1.929 +done:
   1.930 +    if (arena) {
   1.931 +         PORT_FreeArena(arena,PR_FALSE);
   1.932 +    }
   1.933 +    return rv;
   1.934 +}
   1.935 +
   1.936 +/*************************************************************************
   1.937 + *
   1.938 + *            SMIME objects
   1.939 + *
   1.940 + *************************************************************************/
   1.941 +
   1.942 +/*
   1.943 + * use the raw PKCS #11 interface to merge the S/MIME records
   1.944 + */
   1.945 +static SECStatus
   1.946 +pk11_mergeSmime(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
   1.947 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
   1.948 +{
   1.949 +    CK_OBJECT_HANDLE targetSmimeID;
   1.950 +    PLArenaPool *arena = NULL;
   1.951 +    SECStatus rv = SECSuccess;
   1.952 +    CK_ATTRIBUTE smimeTemplate[] = {
   1.953 +	{ CKA_SUBJECT, NULL, 0 },
   1.954 +	{ CKA_NSS_EMAIL, NULL, 0 },
   1.955 +	{ CKA_CLASS, NULL, 0 },
   1.956 +    };
   1.957 +    CK_ULONG smimeTemplateCount = 
   1.958 +		sizeof(smimeTemplate)/sizeof(smimeTemplate[0]);
   1.959 +    CK_ATTRIBUTE smimeCopyTemplate[] = {
   1.960 +	{ CKA_CLASS, NULL, 0 },
   1.961 +	{ CKA_TOKEN, NULL, 0 },
   1.962 +	{ CKA_LABEL, NULL, 0 },
   1.963 +	{ CKA_PRIVATE, NULL, 0 },
   1.964 +	{ CKA_MODIFIABLE, NULL, 0 },
   1.965 +	{ CKA_SUBJECT, NULL, 0 },
   1.966 +	{ CKA_NSS_EMAIL, NULL, 0 },
   1.967 +	{ CKA_NSS_SMIME_TIMESTAMP, NULL, 0 },
   1.968 +	{ CKA_VALUE, NULL, 0 }
   1.969 +    };
   1.970 +    CK_ULONG smimeCopyTemplateCount = 
   1.971 +		sizeof(smimeCopyTemplate)/sizeof(smimeCopyTemplate[0]);
   1.972 +
   1.973 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
   1.974 +    if (arena == NULL) {
   1.975 +	rv = SECFailure;
   1.976 +	goto done;
   1.977 +    }
   1.978 +    /* check to see if the crl is already in the target slot */
   1.979 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, smimeTemplate, 
   1.980 +				smimeTemplateCount, id, &targetSmimeID);
   1.981 +    if (rv != SECSuccess) {
   1.982 +	goto done;
   1.983 +    }
   1.984 +    if (targetSmimeID != CK_INVALID_HANDLE) {
   1.985 +	/* we already have a SMIME record */
   1.986 +	goto done;
   1.987 +    }
   1.988 +
   1.989 +    /* load the SMime Record into the target token. */
   1.990 +    rv = pk11_copyAttributes(arena, targetSlot, targetSmimeID, sourceSlot, id,
   1.991 +				smimeCopyTemplate, smimeCopyTemplateCount);
   1.992 +done:
   1.993 +    if (arena) {
   1.994 +         PORT_FreeArena(arena,PR_FALSE);
   1.995 +    }
   1.996 +    return rv;
   1.997 +}
   1.998 +
   1.999 +/*************************************************************************
  1.1000 + *
  1.1001 + *            Trust Objects
  1.1002 + *
  1.1003 + *************************************************************************/
  1.1004 +
  1.1005 +
  1.1006 +/*
  1.1007 + * decide which trust record entry wins. PR_TRUE (source) or PR_FALSE (target)
  1.1008 + */
  1.1009 +#define USE_TARGET PR_FALSE
  1.1010 +#define USE_SOURCE PR_TRUE
  1.1011 +PRBool
  1.1012 +pk11_mergeTrustEntry(CK_ATTRIBUTE *target, CK_ATTRIBUTE *source)
  1.1013 +{
  1.1014 +    CK_ULONG targetTrust = (target->ulValueLen == sizeof (CK_LONG)) ?
  1.1015 +		*(CK_ULONG *)target->pValue : CKT_NSS_TRUST_UNKNOWN;
  1.1016 +    CK_ULONG sourceTrust = (source->ulValueLen == sizeof (CK_LONG)) ?
  1.1017 +		*(CK_ULONG *)source->pValue : CKT_NSS_TRUST_UNKNOWN;
  1.1018 +
  1.1019 +    /*
  1.1020 +     * Examine a single entry and deside if the source or target version
  1.1021 +     * should win out. When all the entries have been checked, if there is
  1.1022 +     * any case we need to update, we will write the whole source record
  1.1023 +     * to the target database. That means for each individual record, if the
  1.1024 +     * target wins, we need to update the source (in case later we have a
  1.1025 +     * case where the source wins). If the source wins, it already 
  1.1026 +     */
  1.1027 +    if (sourceTrust == targetTrust) {
  1.1028 +	return USE_TARGET;  /* which equates to 'do nothing' */
  1.1029 +    }
  1.1030 +
  1.1031 +    if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
  1.1032 +	return USE_TARGET; 
  1.1033 +    }
  1.1034 +
  1.1035 +    /* target has no idea, use the source's idea of the trust value */
  1.1036 +    if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
  1.1037 +	/* source overwrites the target */
  1.1038 +	return USE_SOURCE;
  1.1039 +    }
  1.1040 +
  1.1041 +    /* so both the target and the source have some idea of what this 
  1.1042 +     * trust attribute should be, and neither agree exactly. 
  1.1043 +     * At this point, we prefer 'hard' attributes over 'soft' ones. 
  1.1044 +     * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
  1.1045 +     * CKT_NSS_UNTRUTED. Soft ones are ones which don't change the
  1.1046 +     * actual trust of the cert (CKT_MUST_VERIFY, CKT_NSS_VALID,
  1.1047 +     * CKT_NSS_VALID_DELEGATOR).
  1.1048 +     */
  1.1049 +    if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST) 
  1.1050 +	|| (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
  1.1051 +	return USE_TARGET;
  1.1052 +    }
  1.1053 +    if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST) 
  1.1054 +	|| (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
  1.1055 +	/* source overrites the target */
  1.1056 +	return USE_SOURCE;
  1.1057 +    }
  1.1058 +
  1.1059 +    /* both have hard attributes, we have a conflict, let the target win. */
  1.1060 +    return USE_TARGET;
  1.1061 +}
  1.1062 +/*
  1.1063 + * use the raw PKCS #11 interface to merge the S/MIME records
  1.1064 + */
  1.1065 +static SECStatus
  1.1066 +pk11_mergeTrust(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
  1.1067 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
  1.1068 +{
  1.1069 +    CK_OBJECT_HANDLE targetTrustID;
  1.1070 +    PLArenaPool *arena = NULL;
  1.1071 +    SECStatus rv = SECSuccess;
  1.1072 +    int error = 0;
  1.1073 +    CK_ATTRIBUTE trustTemplate[] = {
  1.1074 +	{ CKA_ISSUER, NULL, 0 },
  1.1075 +	{ CKA_SERIAL_NUMBER, NULL, 0 },
  1.1076 +	{ CKA_CLASS, NULL, 0 },
  1.1077 +    };
  1.1078 +    CK_ULONG trustTemplateCount = 
  1.1079 +		sizeof(trustTemplate)/sizeof(trustTemplate[0]);
  1.1080 +    CK_ATTRIBUTE trustCopyTemplate[] = {
  1.1081 +	{ CKA_CLASS, NULL, 0 },
  1.1082 +	{ CKA_TOKEN, NULL, 0 },
  1.1083 +	{ CKA_LABEL, NULL, 0 },
  1.1084 +	{ CKA_PRIVATE, NULL, 0 },
  1.1085 +	{ CKA_MODIFIABLE, NULL, 0 },
  1.1086 +	{ CKA_ISSUER, NULL, 0},
  1.1087 +	{ CKA_SERIAL_NUMBER, NULL, 0},
  1.1088 +	{ CKA_CERT_SHA1_HASH, NULL, 0 },
  1.1089 +	{ CKA_CERT_MD5_HASH, NULL, 0 },
  1.1090 +	{ CKA_TRUST_SERVER_AUTH, NULL, 0 },
  1.1091 +	{ CKA_TRUST_CLIENT_AUTH, NULL, 0 },
  1.1092 +	{ CKA_TRUST_CODE_SIGNING, NULL, 0 },
  1.1093 +	{ CKA_TRUST_EMAIL_PROTECTION, NULL, 0 },
  1.1094 +	{ CKA_TRUST_STEP_UP_APPROVED, NULL, 0 }
  1.1095 +    };
  1.1096 +    CK_ULONG trustCopyTemplateCount = 
  1.1097 +		sizeof(trustCopyTemplate)/sizeof(trustCopyTemplate[0]);
  1.1098 +
  1.1099 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
  1.1100 +    if (arena == NULL) {
  1.1101 +	rv = SECFailure;
  1.1102 +	goto done;
  1.1103 +    }
  1.1104 +    /* check to see if the crl is already in the target slot */
  1.1105 +    rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, trustTemplate, 
  1.1106 +				trustTemplateCount, id, &targetTrustID);
  1.1107 +    if (rv != SECSuccess) {
  1.1108 +	goto done;
  1.1109 +    }
  1.1110 +    if (targetTrustID != CK_INVALID_HANDLE) {
  1.1111 +	/* a matching trust record already exists, merge it in */
  1.1112 +	CK_ATTRIBUTE_TYPE trustAttrs[] = {
  1.1113 +	    CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
  1.1114 +	    CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION, 
  1.1115 +	    CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, 
  1.1116 +	    CKA_TRUST_TIME_STAMPING
  1.1117 +	};
  1.1118 +	CK_ULONG trustAttrsCount = 
  1.1119 +		sizeof(trustAttrs)/sizeof(trustAttrs[0]);
  1.1120 +
  1.1121 +	CK_ULONG i;
  1.1122 +	CK_ATTRIBUTE targetTemplate, sourceTemplate;
  1.1123 +
  1.1124 +	/* existing trust record, merge the two together */
  1.1125 +        for (i=0; i < trustAttrsCount; i++) {
  1.1126 +	    targetTemplate.type = sourceTemplate.type = trustAttrs[i];
  1.1127 +	    targetTemplate.pValue = sourceTemplate.pValue = NULL;
  1.1128 +	    targetTemplate.ulValueLen = sourceTemplate.ulValueLen = 0;
  1.1129 +	    PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
  1.1130 +	    PK11_GetAttributes(arena, targetSlot, targetTrustID, 
  1.1131 +							&targetTemplate, 1);
  1.1132 +	    if (pk11_mergeTrustEntry(&targetTemplate, &sourceTemplate)) {
  1.1133 +		/* source wins, write out the source attribute to the target */
  1.1134 +		SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID, 
  1.1135 +				   &sourceTemplate, 1);
  1.1136 +		if (lrv != SECSuccess) {
  1.1137 +		    rv = SECFailure;
  1.1138 +		    error = PORT_GetError();
  1.1139 +		}
  1.1140 +	    }
  1.1141 +	}
  1.1142 +
  1.1143 +	/* handle step */
  1.1144 +	sourceTemplate.type = CKA_TRUST_STEP_UP_APPROVED;
  1.1145 +	sourceTemplate.pValue = NULL;
  1.1146 +	sourceTemplate.ulValueLen = 0;
  1.1147 +
  1.1148 +	/* if the source has steup set, then set it in the target */
  1.1149 +	PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
  1.1150 +	if ((sourceTemplate.ulValueLen == sizeof(CK_BBOOL)) && 
  1.1151 +		(sourceTemplate.pValue) &&
  1.1152 +		(*(CK_BBOOL *)sourceTemplate.pValue == CK_TRUE)) {
  1.1153 +	    SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID, 
  1.1154 +				   &sourceTemplate, 1);
  1.1155 +	    if (lrv != SECSuccess) {
  1.1156 +		rv = SECFailure;
  1.1157 +		error = PORT_GetError();
  1.1158 +	    }
  1.1159 +	}
  1.1160 +
  1.1161 +	goto done;
  1.1162 +
  1.1163 +    }
  1.1164 +
  1.1165 +    /* load the new trust Record into the target token. */
  1.1166 +    rv = pk11_copyAttributes(arena, targetSlot, targetTrustID, sourceSlot, id,
  1.1167 +				trustCopyTemplate, trustCopyTemplateCount);
  1.1168 +done:
  1.1169 +    if (arena) {
  1.1170 +         PORT_FreeArena(arena,PR_FALSE);
  1.1171 +    }
  1.1172 +
  1.1173 +    /* restore the error code */
  1.1174 +    if (rv == SECFailure && error) {
  1.1175 +	PORT_SetError(error);
  1.1176 +    }
  1.1177 +	
  1.1178 +    return rv;
  1.1179 +}
  1.1180 +
  1.1181 +/*************************************************************************
  1.1182 + *
  1.1183 + *            Central merge code
  1.1184 + *
  1.1185 + *************************************************************************/
  1.1186 +/*
  1.1187 + * merge a single object from sourceToken to targetToken
  1.1188 + */
  1.1189 +static SECStatus
  1.1190 +pk11_mergeObject(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
  1.1191 +		CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
  1.1192 +{
  1.1193 +
  1.1194 +    CK_OBJECT_CLASS objClass;
  1.1195 +
  1.1196 +
  1.1197 +    objClass = PK11_ReadULongAttribute(sourceSlot, id, CKA_CLASS);
  1.1198 +    if (objClass == (CK_ULONG) -1) {
  1.1199 +	PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE );
  1.1200 +	return SECFailure;
  1.1201 +    }
  1.1202 +
  1.1203 +    switch (objClass) {
  1.1204 +    case CKO_CERTIFICATE:
  1.1205 +	return pk11_mergeCert(targetSlot, sourceSlot, id, 
  1.1206 +					targetPwArg, sourcePwArg);
  1.1207 +    case CKO_NSS_TRUST:
  1.1208 +	return pk11_mergeTrust(targetSlot, sourceSlot, id, 
  1.1209 +					targetPwArg, sourcePwArg);
  1.1210 +    case CKO_PUBLIC_KEY:
  1.1211 +	return pk11_mergePublicKey(targetSlot, sourceSlot, id,
  1.1212 +					targetPwArg, sourcePwArg);
  1.1213 +    case CKO_PRIVATE_KEY:
  1.1214 +	return pk11_mergePrivateKey(targetSlot, sourceSlot, id, 
  1.1215 +					targetPwArg, sourcePwArg);
  1.1216 +    case CKO_SECRET_KEY:
  1.1217 +	return pk11_mergeSecretKey(targetSlot, sourceSlot, id, 
  1.1218 +					targetPwArg, sourcePwArg);
  1.1219 +    case CKO_NSS_CRL:
  1.1220 +	return pk11_mergeCrl(targetSlot, sourceSlot, id, 
  1.1221 +					targetPwArg, sourcePwArg);
  1.1222 +    case CKO_NSS_SMIME:
  1.1223 +	return pk11_mergeSmime(targetSlot, sourceSlot, id, 
  1.1224 +					targetPwArg, sourcePwArg);
  1.1225 +    default:
  1.1226 +	break;
  1.1227 +    }
  1.1228 +
  1.1229 +    PORT_SetError( SEC_ERROR_UNKNOWN_OBJECT_TYPE );
  1.1230 +    return SECFailure;
  1.1231 +}
  1.1232 +
  1.1233 +PK11MergeLogNode *
  1.1234 +pk11_newMergeLogNode(PLArenaPool *arena,
  1.1235 +		     PK11SlotInfo *slot, CK_OBJECT_HANDLE id, int error)
  1.1236 +{
  1.1237 +    PK11MergeLogNode *newLog;
  1.1238 +    PK11GenericObject *obj;
  1.1239 +
  1.1240 +    newLog = PORT_ArenaZNew(arena, PK11MergeLogNode);
  1.1241 +    if (newLog == NULL) {
  1.1242 +	return NULL;
  1.1243 +    }
  1.1244 +
  1.1245 +    obj = PORT_ArenaZNew(arena, PK11GenericObject);
  1.1246 +    if ( !obj ) {
  1.1247 +	return NULL;
  1.1248 +    }
  1.1249 +
  1.1250 +    /* initialize it */
  1.1251 +    obj->slot = slot;
  1.1252 +    obj->objectID = id;
  1.1253 +
  1.1254 +    newLog->object= obj;
  1.1255 +    newLog->error = error;
  1.1256 +    return newLog;
  1.1257 +}
  1.1258 +
  1.1259 +/*
  1.1260 + * walk down each entry and merge it. keep track of the errors in the log
  1.1261 + */
  1.1262 +static SECStatus
  1.1263 +pk11_mergeByObjectIDs(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
  1.1264 +		CK_OBJECT_HANDLE *objectIDs, int count,
  1.1265 +		PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
  1.1266 +{
  1.1267 +    SECStatus rv = SECSuccess;
  1.1268 +    int error, i;
  1.1269 +    
  1.1270 +    for (i=0; i < count; i++) {
  1.1271 +	/* try to update the entire database. On failure, keep going,
  1.1272 +	 * but remember the error to report back to the caller */
  1.1273 +	SECStatus lrv;
  1.1274 +	PK11MergeLogNode *newLog;
  1.1275 +
  1.1276 +	lrv= pk11_mergeObject(targetSlot, sourceSlot, objectIDs[i], 
  1.1277 +				targetPwArg, sourcePwArg);
  1.1278 +	if (lrv == SECSuccess) {
  1.1279 +	   /* merged with no problem, go to next object */
  1.1280 +	   continue;
  1.1281 +	}
  1.1282 +
  1.1283 +	/* remember that we failed and why */
  1.1284 +	rv = SECFailure;
  1.1285 +	error = PORT_GetError();
  1.1286 +
  1.1287 +	/* log the errors */
  1.1288 +	if (!log) {
  1.1289 +	    /* not logging, go to next entry */
  1.1290 +	    continue;
  1.1291 +	}
  1.1292 +	newLog = pk11_newMergeLogNode(log->arena, sourceSlot, 
  1.1293 +				      objectIDs[i], error);
  1.1294 +	if (!newLog) {
  1.1295 +	    /* failed to allocate entry, just keep going */
  1.1296 +	    continue;
  1.1297 +	}
  1.1298 +
  1.1299 +	/* link in the errorlog entry */
  1.1300 +	newLog->next = NULL;
  1.1301 +	if (log->tail) {
  1.1302 +	    log->tail->next = newLog;
  1.1303 +	} else {
  1.1304 +	    log->head = newLog;
  1.1305 +	}
  1.1306 +	newLog->prev = log->tail;
  1.1307 +	log->tail = newLog;
  1.1308 +    }
  1.1309 +
  1.1310 +    /* restore the last error code */
  1.1311 +    if (rv != SECSuccess) {
  1.1312 +	PORT_SetError(error);
  1.1313 +    }
  1.1314 +    return rv;
  1.1315 +}
  1.1316 +
  1.1317 +/*
  1.1318 + * Merge all the records in sourceSlot that aren't in targetSlot
  1.1319 + * 
  1.1320 + *   This function will return failure if not all the objects
  1.1321 + *   successfully merged.
  1.1322 + *
  1.1323 + *   Applications can pass in an optional error log which will record
  1.1324 + *   each failing object and why it failed to import. PK11MergeLog
  1.1325 + *   is modelled after the CERTVerifyLog.
  1.1326 + */
  1.1327 +SECStatus
  1.1328 +PK11_MergeTokens(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
  1.1329 +		PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
  1.1330 +{
  1.1331 +    SECStatus rv = SECSuccess, lrv = SECSuccess;
  1.1332 +    int error, count = 0;
  1.1333 +    CK_ATTRIBUTE search[2];
  1.1334 +    CK_OBJECT_HANDLE *objectIDs = NULL;
  1.1335 +    CK_BBOOL ck_true = CK_TRUE;
  1.1336 +    CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY;
  1.1337 +
  1.1338 +    PK11_SETATTRS(&search[0], CKA_TOKEN, &ck_true, sizeof(ck_true));
  1.1339 +    PK11_SETATTRS(&search[1], CKA_CLASS, &privKey, sizeof(privKey));
  1.1340 +    /*
  1.1341 +     * make sure both tokens are already authenticated if need be.
  1.1342 +     */
  1.1343 +    rv = PK11_Authenticate(targetSlot, PR_TRUE, targetPwArg);
  1.1344 +    if (rv != SECSuccess) {
  1.1345 +	goto loser;
  1.1346 +    }
  1.1347 +    rv = PK11_Authenticate(sourceSlot, PR_TRUE, sourcePwArg);
  1.1348 +    if (rv != SECSuccess) {
  1.1349 +	goto loser;
  1.1350 +    }
  1.1351 +
  1.1352 +    /* turns out the old DB's are rather fragile if the private keys aren't
  1.1353 +     * merged in first, so do the private keys explicity. */
  1.1354 +    objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 2, &count);
  1.1355 +    if (objectIDs) {
  1.1356 +	lrv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, 
  1.1357 +				    objectIDs, count, log, 
  1.1358 +				    targetPwArg, sourcePwArg);
  1.1359 +	if (lrv != SECSuccess) {
  1.1360 +	    error = PORT_GetError();
  1.1361 +	}
  1.1362 +	PORT_Free(objectIDs);
  1.1363 +	count = 0;
  1.1364 +    }
  1.1365 +
  1.1366 +    /* now do the rest  (NOTE: this will repeat the private keys, but
  1.1367 +     * that shouldnt' be an issue as we will notice they are already
  1.1368 +     * merged in */
  1.1369 +    objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 1, &count);
  1.1370 +    if (!objectIDs) {
  1.1371 +	rv = SECFailure;
  1.1372 +	goto loser;
  1.1373 +    }
  1.1374 +
  1.1375 +    rv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, objectIDs, count, log, 
  1.1376 +			targetPwArg, sourcePwArg);
  1.1377 +    if (rv == SECSuccess) {
  1.1378 +	/* if private keys failed, but the rest succeeded, be sure to let
  1.1379 +	 * the caller know that private keys failed and why.
  1.1380 +	 * NOTE: this is highly unlikely since the same keys that failed
  1.1381 +	 * in the previous merge call will most likely fail in this one */
  1.1382 +	if (lrv != SECSuccess) {
  1.1383 +	    rv = lrv;
  1.1384 +	    PORT_SetError(error);
  1.1385 +	}
  1.1386 +    }
  1.1387 +
  1.1388 +loser:
  1.1389 +    if (objectIDs) {
  1.1390 +	PORT_Free(objectIDs);
  1.1391 +    }
  1.1392 +    return rv;
  1.1393 +}
  1.1394 +
  1.1395 +PK11MergeLog *
  1.1396 +PK11_CreateMergeLog(void)
  1.1397 +{
  1.1398 +    PLArenaPool *arena;
  1.1399 +    PK11MergeLog *log;
  1.1400 +
  1.1401 +    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
  1.1402 +    if (arena == NULL) {
  1.1403 +	return NULL;
  1.1404 +    }
  1.1405 +
  1.1406 +    log = PORT_ArenaZNew(arena, PK11MergeLog);
  1.1407 +    if (log == NULL) {
  1.1408 +         PORT_FreeArena(arena,PR_FALSE);
  1.1409 +	return NULL;
  1.1410 +    }
  1.1411 +    log->arena = arena;
  1.1412 +    log->version = 1;
  1.1413 +    return log;
  1.1414 +}
  1.1415 +
  1.1416 +void
  1.1417 +PK11_DestroyMergeLog(PK11MergeLog *log)
  1.1418 +{
  1.1419 +   if (log && log->arena) {
  1.1420 +	PORT_FreeArena(log->arena, PR_FALSE);
  1.1421 +    }
  1.1422 +}

mercurial