security/nss/lib/softoken/sftkdb.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/softoken/sftkdb.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2737 @@
     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 + *  The following code handles the storage of PKCS 11 modules used by the
     1.9 + * NSS. For the rest of NSS, only one kind of database handle exists:
    1.10 + *
    1.11 + *     SFTKDBHandle
    1.12 + *
    1.13 + * There is one SFTKDBHandle for the each key database and one for each cert 
    1.14 + * database. These databases are opened as associated pairs, one pair per
    1.15 + * slot. SFTKDBHandles are reference counted objects.
    1.16 + *
    1.17 + * Each SFTKDBHandle points to a low level database handle (SDB). This handle
    1.18 + * represents the underlying physical database. These objects are not 
    1.19 + * reference counted, an are 'owned' by their respective SFTKDBHandles.
    1.20 + *
    1.21 + *  
    1.22 + */
    1.23 +#include "sftkdb.h"
    1.24 +#include "sftkdbti.h"
    1.25 +#include "pkcs11t.h"
    1.26 +#include "pkcs11i.h"
    1.27 +#include "sdb.h"
    1.28 +#include "prprf.h" 
    1.29 +#include "pratom.h"
    1.30 +#include "lgglue.h"
    1.31 +#include "utilpars.h"
    1.32 +#include "secerr.h"
    1.33 +#include "softoken.h"
    1.34 +
    1.35 +/*
    1.36 + * We want all databases to have the same binary representation independent of
    1.37 + * endianness or length of the host architecture. In general PKCS #11 attributes
    1.38 + * are endian/length independent except those attributes that pass CK_ULONG.
    1.39 + *
    1.40 + * The following functions fixes up the CK_ULONG type attributes so that the data
    1.41 + * base sees a machine independent view. CK_ULONGs are stored as 4 byte network
    1.42 + * byte order values (big endian).
    1.43 + */
    1.44 +#define BBP 8
    1.45 +
    1.46 +static PRBool
    1.47 +sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type) 
    1.48 +{
    1.49 +    switch(type) {
    1.50 +    case CKA_CERTIFICATE_CATEGORY:
    1.51 +    case CKA_CERTIFICATE_TYPE:
    1.52 +    case CKA_CLASS:
    1.53 +    case CKA_JAVA_MIDP_SECURITY_DOMAIN:
    1.54 +    case CKA_KEY_GEN_MECHANISM:
    1.55 +    case CKA_KEY_TYPE:
    1.56 +    case CKA_MECHANISM_TYPE:
    1.57 +    case CKA_MODULUS_BITS:
    1.58 +    case CKA_PRIME_BITS:
    1.59 +    case CKA_SUBPRIME_BITS:
    1.60 +    case CKA_VALUE_BITS:
    1.61 +    case CKA_VALUE_LEN:
    1.62 +
    1.63 +    case CKA_TRUST_DIGITAL_SIGNATURE:
    1.64 +    case CKA_TRUST_NON_REPUDIATION:
    1.65 +    case CKA_TRUST_KEY_ENCIPHERMENT:
    1.66 +    case CKA_TRUST_DATA_ENCIPHERMENT:
    1.67 +    case CKA_TRUST_KEY_AGREEMENT:
    1.68 +    case CKA_TRUST_KEY_CERT_SIGN:
    1.69 +    case CKA_TRUST_CRL_SIGN:
    1.70 +
    1.71 +    case CKA_TRUST_SERVER_AUTH:
    1.72 +    case CKA_TRUST_CLIENT_AUTH:
    1.73 +    case CKA_TRUST_CODE_SIGNING:
    1.74 +    case CKA_TRUST_EMAIL_PROTECTION:
    1.75 +    case CKA_TRUST_IPSEC_END_SYSTEM:
    1.76 +    case CKA_TRUST_IPSEC_TUNNEL:
    1.77 +    case CKA_TRUST_IPSEC_USER:
    1.78 +    case CKA_TRUST_TIME_STAMPING:
    1.79 +    case CKA_TRUST_STEP_UP_APPROVED:
    1.80 +	return PR_TRUE;
    1.81 +    default:
    1.82 +	break;
    1.83 +    }
    1.84 +    return PR_FALSE;
    1.85 +    
    1.86 +}
    1.87 +
    1.88 +/* are the attributes private? */
    1.89 +static PRBool
    1.90 +sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type) 
    1.91 +{
    1.92 +    switch(type) {
    1.93 +    case CKA_VALUE:
    1.94 +    case CKA_PRIVATE_EXPONENT:
    1.95 +    case CKA_PRIME_1:
    1.96 +    case CKA_PRIME_2:
    1.97 +    case CKA_EXPONENT_1:
    1.98 +    case CKA_EXPONENT_2:
    1.99 +    case CKA_COEFFICIENT:
   1.100 +	return PR_TRUE;
   1.101 +    default:
   1.102 +	break;
   1.103 +    }
   1.104 +    return PR_FALSE;
   1.105 +}
   1.106 +
   1.107 +/* These attributes must be authenticated with an hmac. */
   1.108 +static PRBool
   1.109 +sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type) 
   1.110 +{
   1.111 +    switch(type) {
   1.112 +    case CKA_MODULUS:
   1.113 +    case CKA_PUBLIC_EXPONENT:
   1.114 +    case CKA_CERT_SHA1_HASH:
   1.115 +    case CKA_CERT_MD5_HASH:
   1.116 +    case CKA_TRUST_SERVER_AUTH:
   1.117 +    case CKA_TRUST_CLIENT_AUTH:
   1.118 +    case CKA_TRUST_EMAIL_PROTECTION:
   1.119 +    case CKA_TRUST_CODE_SIGNING:
   1.120 +    case CKA_TRUST_STEP_UP_APPROVED:
   1.121 +    case CKA_NSS_OVERRIDE_EXTENSIONS:
   1.122 +	return PR_TRUE;
   1.123 +    default:
   1.124 +	break;
   1.125 +    }
   1.126 +    return PR_FALSE;
   1.127 +}
   1.128 +
   1.129 +/*
   1.130 + * convert a native ULONG to a database ulong. Database ulong's
   1.131 + * are all 4 byte big endian values.
   1.132 + */
   1.133 +void
   1.134 +sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value)
   1.135 +{ 
   1.136 +    int i;
   1.137 +
   1.138 +    for (i=0; i < SDB_ULONG_SIZE; i++) {
   1.139 +	data[i] = (value >> (SDB_ULONG_SIZE-1-i)*BBP) & 0xff;
   1.140 +    }
   1.141 +}
   1.142 +
   1.143 +/*
   1.144 + * convert a database ulong back to a native ULONG. (reverse of the above
   1.145 + * function.
   1.146 + */
   1.147 +static CK_ULONG
   1.148 +sftk_SDBULong2ULong(unsigned char *data)
   1.149 +{
   1.150 +    int i;
   1.151 +    CK_ULONG value = 0;
   1.152 +
   1.153 +    for (i=0; i < SDB_ULONG_SIZE; i++) {
   1.154 +	value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE-1-i)*BBP);
   1.155 +    }
   1.156 +    return value;
   1.157 +}
   1.158 +
   1.159 +/*
   1.160 + * fix up the input templates. Our fixed up ints are stored in data and must
   1.161 + * be freed by the caller. The new template must also be freed. If there are no
   1.162 + * CK_ULONG attributes, the orignal template is passed in as is.
   1.163 + */
   1.164 +static CK_ATTRIBUTE *
   1.165 +sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, 
   1.166 +			unsigned char **dataOut)
   1.167 +{
   1.168 +    int i;
   1.169 +    int ulongCount = 0;
   1.170 +    unsigned char *data;
   1.171 +    CK_ATTRIBUTE *ntemplate;
   1.172 +
   1.173 +    *dataOut = NULL;
   1.174 +
   1.175 +    /* first count the number of CK_ULONG attributes */
   1.176 +    for (i=0; i < count; i++) {
   1.177 +	/* Don't 'fixup' NULL values */
   1.178 +	if (!template[i].pValue) {
   1.179 +	    continue;
   1.180 +	}
   1.181 +	if (template[i].ulValueLen == sizeof (CK_ULONG)) {
   1.182 +	    if ( sftkdb_isULONGAttribute(template[i].type)) {
   1.183 +		ulongCount++;
   1.184 +	    }
   1.185 +	}
   1.186 +    }
   1.187 +    /* no attributes to fixup, just call on through */
   1.188 +    if (ulongCount == 0) {
   1.189 +	return (CK_ATTRIBUTE *)template;
   1.190 +    }
   1.191 +
   1.192 +    /* allocate space for new ULONGS */
   1.193 +    data = (unsigned char *)PORT_Alloc(SDB_ULONG_SIZE*ulongCount);
   1.194 +    if (!data) {
   1.195 +	return NULL;
   1.196 +    }
   1.197 +
   1.198 +    /* allocate new template */
   1.199 +    ntemplate = PORT_NewArray(CK_ATTRIBUTE,count);
   1.200 +    if (!ntemplate) {
   1.201 +	PORT_Free(data);
   1.202 +	return NULL;
   1.203 +    }
   1.204 +    *dataOut = data;
   1.205 +    /* copy the old template, fixup the actual ulongs */
   1.206 +    for (i=0; i < count; i++) {
   1.207 +	ntemplate[i] = template[i];
   1.208 +	/* Don't 'fixup' NULL values */
   1.209 +	if (!template[i].pValue) {
   1.210 +	    continue;
   1.211 +	}
   1.212 +	if (template[i].ulValueLen == sizeof (CK_ULONG)) {
   1.213 +	    if ( sftkdb_isULONGAttribute(template[i].type) ) {
   1.214 +		CK_ULONG value = *(CK_ULONG *) template[i].pValue;
   1.215 +		sftk_ULong2SDBULong(data, value);
   1.216 +		ntemplate[i].pValue = data;
   1.217 +		ntemplate[i].ulValueLen = SDB_ULONG_SIZE;
   1.218 +		data += SDB_ULONG_SIZE;
   1.219 +	    }
   1.220 +	}
   1.221 +    }
   1.222 +    return ntemplate;
   1.223 +}
   1.224 +
   1.225 +
   1.226 +static const char SFTKDB_META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x";
   1.227 +
   1.228 +/*
   1.229 + * return a string describing the database type (key or cert)
   1.230 + */
   1.231 +const char *
   1.232 +sftkdb_TypeString(SFTKDBHandle *handle)
   1.233 +{
   1.234 +   return (handle->type == SFTK_KEYDB_TYPE) ? "key" : "cert";
   1.235 +}
   1.236 +
   1.237 +/*
   1.238 + * Some attributes are signed with an Hmac and a pbe key generated from
   1.239 + * the password. This signature is stored indexed by object handle and
   1.240 + * attribute type in the meta data table in the key database.
   1.241 + *
   1.242 + * Signature entries are indexed by the string
   1.243 + * sig_[cert/key]_{ObjectID}_{Attribute}
   1.244 + *
   1.245 + * This function fetches that pkcs5 signature. Caller supplies a SECItem
   1.246 + * pre-allocated to the appropriate size if the SECItem is too small the
   1.247 + * function will fail with CKR_BUFFER_TOO_SMALL.
   1.248 + */
   1.249 +static CK_RV
   1.250 +sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle, 
   1.251 +		CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
   1.252 +		SECItem *signText)
   1.253 +{
   1.254 +    SDB *db;
   1.255 +    char id[30];
   1.256 +    CK_RV crv;
   1.257 +
   1.258 +    db = SFTK_GET_SDB(keyHandle);
   1.259 +
   1.260 +    sprintf(id, SFTKDB_META_SIG_TEMPLATE,
   1.261 +	sftkdb_TypeString(handle),
   1.262 +	(unsigned int)objectID, (unsigned int)type);
   1.263 +
   1.264 +    crv = (*db->sdb_GetMetaData)(db, id, signText, NULL);
   1.265 +    return crv;
   1.266 +}
   1.267 +
   1.268 +/*
   1.269 + * Some attributes are signed with an Hmac and a pbe key generated from
   1.270 + * the password. This signature is stored indexed by object handle and
   1.271 + * attribute type in the meta data table in the key database.
   1.272 + *
   1.273 + * Signature entries are indexed by the string
   1.274 + * sig_[cert/key]_{ObjectID}_{Attribute}
   1.275 + *
   1.276 + * This function stores that pkcs5 signature.
   1.277 + */
   1.278 +CK_RV
   1.279 +sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget, 
   1.280 +		CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
   1.281 +		SECItem *signText)
   1.282 +{
   1.283 +    char id[30];
   1.284 +    CK_RV crv;
   1.285 +
   1.286 +    sprintf(id, SFTKDB_META_SIG_TEMPLATE,
   1.287 +	sftkdb_TypeString(handle),
   1.288 +	(unsigned int)objectID, (unsigned int)type);
   1.289 +
   1.290 +    crv = (*keyTarget->sdb_PutMetaData)(keyTarget, id, signText, NULL);
   1.291 +    return crv;
   1.292 +}
   1.293 +
   1.294 +/*
   1.295 + * fix up returned data. NOTE: sftkdb_fixupTemplateIn has already allocated
   1.296 + * separate data sections for the database ULONG values.
   1.297 + */
   1.298 +static CK_RV
   1.299 +sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID,
   1.300 +		CK_ATTRIBUTE *ntemplate, int count, SFTKDBHandle *handle)
   1.301 +{
   1.302 +    int i;
   1.303 +    CK_RV crv = CKR_OK;
   1.304 +    SFTKDBHandle *keyHandle;
   1.305 +    PRBool checkSig = PR_TRUE;
   1.306 +    PRBool checkEnc = PR_TRUE;
   1.307 +
   1.308 +    PORT_Assert(handle);
   1.309 +
   1.310 +    /* find the key handle */
   1.311 +    keyHandle = handle;
   1.312 +    if (handle->type != SFTK_KEYDB_TYPE) {
   1.313 +	checkEnc = PR_FALSE;
   1.314 +	keyHandle = handle->peerDB;
   1.315 +    }
   1.316 +
   1.317 +    if ((keyHandle == NULL) || 
   1.318 +	((SFTK_GET_SDB(keyHandle)->sdb_flags & SDB_HAS_META) == 0)  ||
   1.319 +	(keyHandle->passwordKey.data == NULL)) {
   1.320 +	checkSig = PR_FALSE;
   1.321 +    }
   1.322 +
   1.323 +    for (i=0; i < count; i++) {
   1.324 +	CK_ULONG length = template[i].ulValueLen;
   1.325 +	template[i].ulValueLen = ntemplate[i].ulValueLen;
   1.326 +	/* fixup ulongs */
   1.327 +	if (ntemplate[i].ulValueLen == SDB_ULONG_SIZE) {
   1.328 +	    if (sftkdb_isULONGAttribute(template[i].type)) {
   1.329 +		if (template[i].pValue) {
   1.330 +		    CK_ULONG value;
   1.331 +		    unsigned char *data;
   1.332 +
   1.333 +		    data = (unsigned char *)ntemplate[i].pValue;
   1.334 +		    value = sftk_SDBULong2ULong(ntemplate[i].pValue);
   1.335 +		    if (length < sizeof(CK_ULONG)) {
   1.336 +			template[i].ulValueLen = -1;
   1.337 +			crv = CKR_BUFFER_TOO_SMALL;
   1.338 +			continue;
   1.339 +		    } 
   1.340 +		    PORT_Memcpy(template[i].pValue,&value,sizeof(CK_ULONG));
   1.341 +		}
   1.342 +		template[i].ulValueLen = sizeof(CK_ULONG);
   1.343 +	    }
   1.344 +	}
   1.345 +
   1.346 +	/* if no data was retrieved, no need to process encrypted or signed
   1.347 +	 * attributes */
   1.348 +	if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) {
   1.349 +	    continue;
   1.350 +	}
   1.351 +
   1.352 +	/* fixup private attributes */
   1.353 +	if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) {
   1.354 +	    /* we have a private attribute */
   1.355 +	    /* This code depends on the fact that the cipherText is bigger
   1.356 +	     * than the plain text */
   1.357 +	    SECItem cipherText;
   1.358 +	    SECItem *plainText;
   1.359 +	    SECStatus rv;
   1.360 +
   1.361 +	    cipherText.data = ntemplate[i].pValue;
   1.362 +	    cipherText.len = ntemplate[i].ulValueLen;
   1.363 +    	    PZ_Lock(handle->passwordLock);
   1.364 +	    if (handle->passwordKey.data == NULL) {
   1.365 +		PZ_Unlock(handle->passwordLock);
   1.366 +		template[i].ulValueLen = -1;
   1.367 +		crv = CKR_USER_NOT_LOGGED_IN;
   1.368 +		continue;
   1.369 +	    }
   1.370 +	    rv = sftkdb_DecryptAttribute(&handle->passwordKey, 
   1.371 +					&cipherText, &plainText);
   1.372 +	    PZ_Unlock(handle->passwordLock);
   1.373 +	    if (rv != SECSuccess) {
   1.374 +		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
   1.375 +		template[i].ulValueLen = -1;
   1.376 +		crv = CKR_GENERAL_ERROR;
   1.377 +		continue;
   1.378 +	    }
   1.379 +	    PORT_Assert(template[i].ulValueLen >= plainText->len);
   1.380 +	    if (template[i].ulValueLen < plainText->len) {
   1.381 +		SECITEM_FreeItem(plainText,PR_TRUE);
   1.382 +		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
   1.383 +		template[i].ulValueLen = -1;
   1.384 +		crv = CKR_GENERAL_ERROR;
   1.385 +		continue;
   1.386 +	    }
   1.387 +		
   1.388 +	    /* copy the plain text back into the template */
   1.389 +	    PORT_Memcpy(template[i].pValue, plainText->data, plainText->len);
   1.390 +	    template[i].ulValueLen = plainText->len;
   1.391 +	    SECITEM_FreeItem(plainText,PR_TRUE);
   1.392 +	}
   1.393 +	/* make sure signed attributes are valid */
   1.394 +	if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) {
   1.395 +	    SECStatus rv;
   1.396 +	    SECItem signText;
   1.397 +	    SECItem plainText;
   1.398 +	    unsigned char signData[SDB_MAX_META_DATA_LEN];
   1.399 +
   1.400 +	    signText.data = signData;
   1.401 +	    signText.len = sizeof(signData);
   1.402 +
   1.403 +	    rv = sftkdb_getAttributeSignature(handle, keyHandle, 
   1.404 +				objectID, ntemplate[i].type, &signText);
   1.405 +	    if (rv != SECSuccess) {
   1.406 +		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
   1.407 +		template[i].ulValueLen = -1;
   1.408 +		crv = CKR_DATA_INVALID; /* better error code? */
   1.409 +		continue;
   1.410 +	    }
   1.411 +
   1.412 +	    plainText.data = ntemplate[i].pValue;
   1.413 +	    plainText.len = ntemplate[i].ulValueLen;
   1.414 +
   1.415 +	    /*
   1.416 +	     * we do a second check holding the lock just in case the user
   1.417 +	     * loggout while we were trying to get the signature.
   1.418 +	     */
   1.419 +    	    PZ_Lock(keyHandle->passwordLock);
   1.420 +	    if (keyHandle->passwordKey.data == NULL) {
   1.421 +		/* if we are no longer logged in, no use checking the other
   1.422 +		 * Signatures either. */
   1.423 +		checkSig = PR_FALSE; 
   1.424 +		PZ_Unlock(keyHandle->passwordLock);
   1.425 +		continue;
   1.426 +	    }
   1.427 +
   1.428 +	    rv = sftkdb_VerifyAttribute(&keyHandle->passwordKey, 
   1.429 +				objectID, ntemplate[i].type,
   1.430 +				&plainText, &signText);
   1.431 +	    PZ_Unlock(keyHandle->passwordLock);
   1.432 +	    if (rv != SECSuccess) {
   1.433 +		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
   1.434 +		template[i].ulValueLen = -1;
   1.435 +		crv = CKR_SIGNATURE_INVALID; /* better error code? */
   1.436 +	    }
   1.437 +	    /* This Attribute is fine */
   1.438 +	}
   1.439 +    }
   1.440 +    return crv;
   1.441 +}
   1.442 +
   1.443 +/*
   1.444 + * Some attributes are signed with an HMAC and a pbe key generated from
   1.445 + * the password. This signature is stored indexed by object handle and
   1.446 + *
   1.447 + * Those attributes are:
   1.448 + * 1) Trust object hashes and trust values.
   1.449 + * 2) public key values.
   1.450 + *
   1.451 + * Certs themselves are considered properly authenticated by virtue of their
   1.452 + * signature, or their matching hash with the trust object.
   1.453 + *
   1.454 + * These signature is only checked for objects coming from shared databases. 
   1.455 + * Older dbm style databases have such no signature checks. HMACs are also 
   1.456 + * only checked when the token is logged in, as it requires a pbe generated 
   1.457 + * from the password.
   1.458 + *
   1.459 + * Tokens which have no key database (and therefore no master password) do not
   1.460 + * have any stored signature values. Signature values are stored in the key
   1.461 + * database, since the signature data is tightly coupled to the key database
   1.462 + * password. 
   1.463 + *
   1.464 + * This function takes a template of attributes that were either created or
   1.465 + * modified. These attributes are checked to see if the need to be signed.
   1.466 + * If they do, then this function signs the attributes and writes them
   1.467 + * to the meta data store.
   1.468 + *
   1.469 + * This function can fail if there are attributes that must be signed, but
   1.470 + * the token is not logged in.
   1.471 + *
   1.472 + * The caller is expected to abort any transaction he was in in the
   1.473 + * event of a failure of this function.
   1.474 + */
   1.475 +static CK_RV
   1.476 +sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle, 
   1.477 +		  PRBool mayBeUpdateDB,
   1.478 +		  CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
   1.479 +		  CK_ULONG count)
   1.480 +{
   1.481 +    int i;
   1.482 +    CK_RV crv;
   1.483 +    SFTKDBHandle *keyHandle = handle;
   1.484 +    SDB *keyTarget = NULL;
   1.485 +    PRBool usingPeerDB = PR_FALSE;
   1.486 +    PRBool inPeerDBTransaction = PR_FALSE;
   1.487 +
   1.488 +    PORT_Assert(handle);
   1.489 +
   1.490 +    if (handle->type != SFTK_KEYDB_TYPE) {
   1.491 +	keyHandle = handle->peerDB;
   1.492 +	usingPeerDB = PR_TRUE;
   1.493 +    }
   1.494 +
   1.495 +    /* no key DB defined? then no need to sign anything */
   1.496 +    if (keyHandle == NULL) {
   1.497 +	crv = CKR_OK;
   1.498 +	goto loser;
   1.499 +    }
   1.500 +
   1.501 +    /* When we are in a middle of an update, we have an update database set, 
   1.502 +     * but we want to write to the real database. The bool mayBeUpdateDB is
   1.503 +     * set to TRUE if it's possible that we want to write an update database
   1.504 +     * rather than a primary */
   1.505 +    keyTarget = (mayBeUpdateDB && keyHandle->update) ? 
   1.506 +		keyHandle->update : keyHandle->db;
   1.507 +
   1.508 +    /* skip the the database does not support meta data */
   1.509 +    if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) {
   1.510 +	crv = CKR_OK;
   1.511 +	goto loser;
   1.512 +    }
   1.513 +
   1.514 +    /* If we had to switch databases, we need to initialize a transaction. */
   1.515 +    if (usingPeerDB) {
   1.516 +	crv = (*keyTarget->sdb_Begin)(keyTarget);
   1.517 +	if (crv != CKR_OK) {
   1.518 +	    goto loser;
   1.519 +	}
   1.520 +	inPeerDBTransaction = PR_TRUE;
   1.521 +    }
   1.522 +
   1.523 +    for (i=0; i < count; i ++) {
   1.524 +	if (sftkdb_isAuthenticatedAttribute(template[i].type)) {
   1.525 +	    SECStatus rv;
   1.526 +	    SECItem *signText;
   1.527 +	    SECItem plainText;
   1.528 +
   1.529 +	    plainText.data = template[i].pValue;
   1.530 +	    plainText.len = template[i].ulValueLen;
   1.531 +	    PZ_Lock(keyHandle->passwordLock);
   1.532 +	    if (keyHandle->passwordKey.data == NULL) {
   1.533 +		PZ_Unlock(keyHandle->passwordLock);
   1.534 +		crv = CKR_USER_NOT_LOGGED_IN;
   1.535 +		goto loser;
   1.536 +	    }
   1.537 +	    rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey, 
   1.538 +				objectID, template[i].type,
   1.539 +				&plainText, &signText);
   1.540 +	    PZ_Unlock(keyHandle->passwordLock);
   1.541 +	    if (rv != SECSuccess) {
   1.542 +		crv = CKR_GENERAL_ERROR; /* better error code here? */
   1.543 +		goto loser;
   1.544 +	    }
   1.545 +	    rv = sftkdb_PutAttributeSignature(handle, keyTarget, 
   1.546 +				objectID, template[i].type, signText);
   1.547 +	    if (rv != SECSuccess) {
   1.548 +		crv = CKR_GENERAL_ERROR; /* better error code here? */
   1.549 +		goto loser;
   1.550 +	    }
   1.551 +	}
   1.552 +    }
   1.553 +    crv = CKR_OK;
   1.554 +
   1.555 +    /* If necessary, commit the transaction */
   1.556 +    if (inPeerDBTransaction) {
   1.557 +	crv = (*keyTarget->sdb_Commit)(keyTarget);
   1.558 +	if (crv != CKR_OK) {
   1.559 +	    goto loser;
   1.560 +	}
   1.561 +	inPeerDBTransaction = PR_FALSE;
   1.562 +    }
   1.563 +
   1.564 +loser:
   1.565 +    if (inPeerDBTransaction) {
   1.566 +	/* The transaction must have failed. Abort. */
   1.567 +	(*keyTarget->sdb_Abort)(keyTarget);
   1.568 +	PORT_Assert(crv != CKR_OK);
   1.569 +	if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
   1.570 +    }
   1.571 +    return crv;
   1.572 +}
   1.573 +
   1.574 +static CK_RV
   1.575 +sftkdb_CreateObject(PLArenaPool *arena, SFTKDBHandle *handle,
   1.576 +	SDB *db, CK_OBJECT_HANDLE *objectID,
   1.577 +        CK_ATTRIBUTE *template, CK_ULONG count)
   1.578 +{
   1.579 +    PRBool inTransaction = PR_FALSE;
   1.580 +    CK_RV crv;
   1.581 +
   1.582 +    inTransaction = PR_TRUE;
   1.583 +    
   1.584 +    crv = (*db->sdb_CreateObject)(db, objectID, template, count);
   1.585 +    if (crv != CKR_OK) {
   1.586 +	goto loser;
   1.587 +    }
   1.588 +    crv = sftk_signTemplate(arena, handle, (db == handle->update),
   1.589 +					*objectID, template, count);
   1.590 +loser:
   1.591 +
   1.592 +    return crv;
   1.593 +}
   1.594 +
   1.595 +
   1.596 +CK_ATTRIBUTE * 
   1.597 +sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, 
   1.598 +		     SFTKDBHandle *handle,CK_ULONG *pcount, 
   1.599 +		     CK_RV *crv)
   1.600 +{
   1.601 +    int count;
   1.602 +    CK_ATTRIBUTE *template;
   1.603 +    int i, templateIndex;
   1.604 +    SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
   1.605 +    PRBool doEnc = PR_TRUE;
   1.606 +
   1.607 +    *crv = CKR_OK;
   1.608 +
   1.609 +    if (sessObject == NULL) {
   1.610 +	*crv = CKR_GENERAL_ERROR; /* internal programming error */
   1.611 +	return NULL;
   1.612 +    }
   1.613 +
   1.614 +    PORT_Assert(handle);
   1.615 +    /* find the key handle */
   1.616 +    if (handle->type != SFTK_KEYDB_TYPE) {
   1.617 +	doEnc = PR_FALSE;
   1.618 +    }
   1.619 +
   1.620 +    PZ_Lock(sessObject->attributeLock);
   1.621 +    count = 0;
   1.622 +    for (i=0; i < sessObject->hashSize; i++) {
   1.623 +	SFTKAttribute *attr;
   1.624 +   	for (attr=sessObject->head[i]; attr; attr=attr->next) {
   1.625 +	    count++;
   1.626 +	}
   1.627 +    }
   1.628 +    template = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, count);
   1.629 +    if (template == NULL) {
   1.630 +        PZ_Unlock(sessObject->attributeLock);
   1.631 +	*crv = CKR_HOST_MEMORY;
   1.632 +	return NULL;
   1.633 +    }
   1.634 +    templateIndex = 0;
   1.635 +    for (i=0; i < sessObject->hashSize; i++) {
   1.636 +	SFTKAttribute *attr;
   1.637 +   	for (attr=sessObject->head[i]; attr; attr=attr->next) {
   1.638 +	    CK_ATTRIBUTE *tp = &template[templateIndex++];
   1.639 +	    /* copy the attribute */
   1.640 +	    *tp = attr->attrib;
   1.641 +
   1.642 +	    /* fixup  ULONG s */
   1.643 +	    if ((tp->ulValueLen == sizeof (CK_ULONG)) &&
   1.644 +		(sftkdb_isULONGAttribute(tp->type)) ) {
   1.645 +		CK_ULONG value = *(CK_ULONG *) tp->pValue;
   1.646 +		unsigned char *data;
   1.647 +
   1.648 +		tp->pValue = PORT_ArenaAlloc(arena, SDB_ULONG_SIZE);
   1.649 +		data = (unsigned char *)tp->pValue;
   1.650 +		if (data == NULL) {
   1.651 +		    *crv = CKR_HOST_MEMORY;
   1.652 +		    break;
   1.653 +		}
   1.654 +		sftk_ULong2SDBULong(data, value);
   1.655 +		tp->ulValueLen = SDB_ULONG_SIZE;
   1.656 +	    }
   1.657 +
   1.658 +	    /* encrypt private attributes */
   1.659 +	    if (doEnc && sftkdb_isPrivateAttribute(tp->type)) {
   1.660 +		/* we have a private attribute */
   1.661 +		SECItem *cipherText;
   1.662 +		SECItem plainText;
   1.663 +		SECStatus rv;
   1.664 +
   1.665 +		plainText.data = tp->pValue;
   1.666 +		plainText.len = tp->ulValueLen;
   1.667 +		PZ_Lock(handle->passwordLock);
   1.668 +		if (handle->passwordKey.data == NULL) {
   1.669 +		    PZ_Unlock(handle->passwordLock);
   1.670 +		    *crv = CKR_USER_NOT_LOGGED_IN;
   1.671 +		    break;
   1.672 +		}
   1.673 +		rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey, 
   1.674 +						&plainText, &cipherText);
   1.675 +		PZ_Unlock(handle->passwordLock);
   1.676 +		if (rv == SECSuccess) {
   1.677 +		    tp->pValue = cipherText->data;
   1.678 +		    tp->ulValueLen = cipherText->len;
   1.679 +		} else {
   1.680 +		    *crv = CKR_GENERAL_ERROR; /* better error code here? */
   1.681 +		    break;
   1.682 +		}
   1.683 +		PORT_Memset(plainText.data, 0, plainText.len);
   1.684 +	    }
   1.685 +	}
   1.686 +    }
   1.687 +    PORT_Assert(templateIndex <= count);
   1.688 +    PZ_Unlock(sessObject->attributeLock);
   1.689 +
   1.690 +    if (*crv != CKR_OK) {
   1.691 +	return NULL;
   1.692 +    }
   1.693 +    if (pcount) {
   1.694 +	*pcount = count;
   1.695 +    }
   1.696 +    return template;
   1.697 +
   1.698 +}
   1.699 +
   1.700 +/*
   1.701 + * return a pointer to the attribute in the give template.
   1.702 + * The return value is not const, as the caller may modify
   1.703 + * the given attribute value, but such modifications will
   1.704 + * modify the actual value in the template.
   1.705 + */
   1.706 +static CK_ATTRIBUTE *
   1.707 +sftkdb_getAttributeFromTemplate(CK_ATTRIBUTE_TYPE attribute, 
   1.708 +			    CK_ATTRIBUTE *ptemplate, CK_ULONG len)
   1.709 +{
   1.710 +    CK_ULONG i;
   1.711 +
   1.712 +    for (i=0; i < len; i++) {
   1.713 +	if (attribute == ptemplate[i].type) {
   1.714 +	    return &ptemplate[i];
   1.715 +	}
   1.716 +    }
   1.717 +    return NULL;
   1.718 +}
   1.719 +
   1.720 +static const CK_ATTRIBUTE *
   1.721 +sftkdb_getAttributeFromConstTemplate(CK_ATTRIBUTE_TYPE attribute, 
   1.722 +				const CK_ATTRIBUTE *ptemplate, CK_ULONG len)
   1.723 +{
   1.724 +    CK_ULONG i;
   1.725 +
   1.726 +    for (i=0; i < len; i++) {
   1.727 +	if (attribute == ptemplate[i].type) {
   1.728 +	    return &ptemplate[i];
   1.729 +	}
   1.730 +    }
   1.731 +    return NULL;
   1.732 +}
   1.733 +
   1.734 +
   1.735 +/*
   1.736 + * fetch a template which identifies 'unique' entries based on object type
   1.737 + */
   1.738 +static CK_RV
   1.739 +sftkdb_getFindTemplate(CK_OBJECT_CLASS objectType, unsigned char *objTypeData,
   1.740 +			CK_ATTRIBUTE *findTemplate, CK_ULONG *findCount,
   1.741 +			CK_ATTRIBUTE *ptemplate, int len)
   1.742 +{
   1.743 +    CK_ATTRIBUTE *attr;
   1.744 +    CK_ULONG count = 1;
   1.745 +
   1.746 +    sftk_ULong2SDBULong(objTypeData, objectType);
   1.747 +    findTemplate[0].type = CKA_CLASS;
   1.748 +    findTemplate[0].pValue = objTypeData;
   1.749 +    findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
   1.750 +
   1.751 +    switch (objectType) {
   1.752 +    case CKO_CERTIFICATE:
   1.753 +    case CKO_NSS_TRUST:
   1.754 +	attr = sftkdb_getAttributeFromTemplate(CKA_ISSUER, ptemplate, len);
   1.755 +	if (attr == NULL) {
   1.756 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.757 +	}
   1.758 +	findTemplate[1] = *attr;
   1.759 +	attr = sftkdb_getAttributeFromTemplate(CKA_SERIAL_NUMBER, 
   1.760 +					ptemplate, len);
   1.761 +	if (attr == NULL) {
   1.762 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.763 +	}
   1.764 +	findTemplate[2] = *attr;
   1.765 +	count = 3;
   1.766 +	break;
   1.767 +	
   1.768 +    case CKO_PRIVATE_KEY:
   1.769 +    case CKO_PUBLIC_KEY:
   1.770 +    case CKO_SECRET_KEY:
   1.771 +	attr = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, len);
   1.772 +	if (attr == NULL) {
   1.773 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.774 +	}
   1.775 +	if (attr->ulValueLen == 0) {
   1.776 +	    /* key is too generic to determine that it's unique, usually
   1.777 +	     * happens in the key gen case */
   1.778 +	    return CKR_OBJECT_HANDLE_INVALID;
   1.779 +	}
   1.780 +	
   1.781 +	findTemplate[1] = *attr;
   1.782 +	count = 2;
   1.783 +	break;
   1.784 +
   1.785 +    case CKO_NSS_CRL:
   1.786 +	attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
   1.787 +	if (attr == NULL) {
   1.788 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.789 +	}
   1.790 +	findTemplate[1] = *attr;
   1.791 +	count = 2;
   1.792 +	break;
   1.793 +
   1.794 +    case CKO_NSS_SMIME:
   1.795 +	attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
   1.796 +	if (attr == NULL) {
   1.797 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.798 +	}
   1.799 +	findTemplate[1] = *attr;
   1.800 +	attr = sftkdb_getAttributeFromTemplate(CKA_NSS_EMAIL, ptemplate, len);
   1.801 +	if (attr == NULL) {
   1.802 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.803 +	}
   1.804 +	findTemplate[2] = *attr;
   1.805 +	count = 3;
   1.806 +	break;
   1.807 +    default:
   1.808 +	attr = sftkdb_getAttributeFromTemplate(CKA_VALUE, ptemplate, len);
   1.809 +	if (attr == NULL) {
   1.810 +	    return CKR_TEMPLATE_INCOMPLETE;
   1.811 +	}
   1.812 +	findTemplate[1] = *attr;
   1.813 +	count = 2;
   1.814 +	break;
   1.815 +    }
   1.816 +    *findCount = count;
   1.817 +
   1.818 +    return CKR_OK;
   1.819 +}
   1.820 +
   1.821 +/*
   1.822 + * look to see if this object already exists and return its object ID if
   1.823 + * it does.
   1.824 + */
   1.825 +static CK_RV
   1.826 +sftkdb_lookupObject(SDB *db, CK_OBJECT_CLASS objectType, 
   1.827 +		 CK_OBJECT_HANDLE *id, CK_ATTRIBUTE *ptemplate, CK_ULONG len)
   1.828 +{
   1.829 +    CK_ATTRIBUTE findTemplate[3];
   1.830 +    CK_ULONG count = 1;
   1.831 +    CK_ULONG objCount = 0;
   1.832 +    SDBFind *find = NULL;
   1.833 +    unsigned char objTypeData[SDB_ULONG_SIZE];
   1.834 +    CK_RV crv;
   1.835 +
   1.836 +    *id = CK_INVALID_HANDLE;
   1.837 +    if (objectType == CKO_NSS_CRL) {
   1.838 +	return CKR_OK;
   1.839 +    }
   1.840 +    crv = sftkdb_getFindTemplate(objectType, objTypeData,
   1.841 +			findTemplate, &count, ptemplate, len);
   1.842 +
   1.843 +    if (crv == CKR_OBJECT_HANDLE_INVALID) {
   1.844 +	/* key is too generic to determine that it's unique, usually
   1.845 +	 * happens in the key gen case, tell the caller to go ahead
   1.846 +	 * and just create it */
   1.847 +	return CKR_OK;
   1.848 +    }
   1.849 +    if (crv != CKR_OK) {
   1.850 +	return crv;
   1.851 +    }
   1.852 +
   1.853 +    /* use the raw find, so we get the correct database */
   1.854 +    crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find);
   1.855 +    if (crv != CKR_OK) {
   1.856 +	return crv;
   1.857 +    }
   1.858 +    (*db->sdb_FindObjects)(db, find, id, 1, &objCount);
   1.859 +    (*db->sdb_FindObjectsFinal)(db, find);
   1.860 +
   1.861 +    if (objCount == 0) {
   1.862 +	*id = CK_INVALID_HANDLE;
   1.863 +    }
   1.864 +    return CKR_OK;
   1.865 +}
   1.866 +
   1.867 +
   1.868 +/*
   1.869 + * check to see if this template conflicts with others in our current database.
   1.870 + */
   1.871 +static CK_RV
   1.872 +sftkdb_checkConflicts(SDB *db, CK_OBJECT_CLASS objectType, 
   1.873 +			const CK_ATTRIBUTE *ptemplate, CK_ULONG len, 
   1.874 +			CK_OBJECT_HANDLE sourceID)
   1.875 +{
   1.876 +    CK_ATTRIBUTE findTemplate[2];
   1.877 +    unsigned char objTypeData[SDB_ULONG_SIZE];
   1.878 +    /* we may need to allocate some temporaries. Keep track of what was 
   1.879 +     * allocated so we can free it in the end */
   1.880 +    unsigned char *temp1 = NULL; 
   1.881 +    unsigned char *temp2 = NULL;
   1.882 +    CK_ULONG objCount = 0;
   1.883 +    SDBFind *find = NULL;
   1.884 +    CK_OBJECT_HANDLE id;
   1.885 +    const CK_ATTRIBUTE *attr, *attr2;
   1.886 +    CK_RV crv;
   1.887 +    CK_ATTRIBUTE subject;
   1.888 +
   1.889 +    /* Currently the only conflict is with nicknames pointing to the same 
   1.890 +     * subject when creating or modifying a certificate. */
   1.891 +    /* If the object is not a cert, no problem. */
   1.892 +    if (objectType != CKO_CERTIFICATE) {
   1.893 +	return CKR_OK;
   1.894 +    }
   1.895 +    /* if not setting a nickname then there's still no problem */
   1.896 +    attr = sftkdb_getAttributeFromConstTemplate(CKA_LABEL, ptemplate, len);
   1.897 +    if ((attr == NULL) || (attr->ulValueLen == 0)) {
   1.898 +	return CKR_OK;
   1.899 +    }
   1.900 +    /* fetch the subject of the source. For creation and merge, this should
   1.901 +     * be found in the template */
   1.902 +    attr2 = sftkdb_getAttributeFromConstTemplate(CKA_SUBJECT, ptemplate, len);
   1.903 +    if (sourceID == CK_INVALID_HANDLE) {
   1.904 +	if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen < 0)) {
   1.905 +	    crv = CKR_TEMPLATE_INCOMPLETE; 
   1.906 +	    goto done;
   1.907 +	}
   1.908 +    } else if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen <= 0)) {
   1.909 +	/* sourceID is set if we are trying to modify an existing entry instead
   1.910 +	 * of creating a new one. In this case the subject may not be (probably
   1.911 +	 * isn't) in the template, we have to read it from the database */
   1.912 +    	subject.type = CKA_SUBJECT;
   1.913 +    	subject.pValue = NULL;
   1.914 +    	subject.ulValueLen = 0;
   1.915 +    	crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1);
   1.916 +	if (crv != CKR_OK) {
   1.917 +	    goto done;
   1.918 +	}
   1.919 +	if ((CK_LONG)subject.ulValueLen < 0) {
   1.920 +	    crv = CKR_DEVICE_ERROR; /* closest pkcs11 error to corrupted DB */
   1.921 +	    goto done;
   1.922 +	}
   1.923 +	temp1 = subject.pValue = PORT_Alloc(++subject.ulValueLen);
   1.924 +	if (temp1 == NULL) {
   1.925 +	    crv = CKR_HOST_MEMORY;
   1.926 +	    goto done;
   1.927 +	}
   1.928 +    	crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1);
   1.929 +	if (crv != CKR_OK) {
   1.930 +	    goto done;
   1.931 +	}
   1.932 +	attr2 = &subject;
   1.933 +    }
   1.934 +    
   1.935 +    /* check for another cert in the database with the same nickname */
   1.936 +    sftk_ULong2SDBULong(objTypeData, objectType);
   1.937 +    findTemplate[0].type = CKA_CLASS;
   1.938 +    findTemplate[0].pValue = objTypeData;
   1.939 +    findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
   1.940 +    findTemplate[1] = *attr;
   1.941 +
   1.942 +    crv = (*db->sdb_FindObjectsInit)(db, findTemplate, 2, &find);
   1.943 +    if (crv != CKR_OK) {
   1.944 +	goto done;
   1.945 +    }
   1.946 +    (*db->sdb_FindObjects)(db, find, &id, 1, &objCount);
   1.947 +    (*db->sdb_FindObjectsFinal)(db, find);
   1.948 +
   1.949 +    /* object count == 0 means no conflicting certs found, 
   1.950 +     * go on with the operation */
   1.951 +    if (objCount == 0) {
   1.952 +	crv = CKR_OK;
   1.953 +	goto done;
   1.954 +    }
   1.955 +
   1.956 +    /* There is a least one cert that shares the nickname, make sure it also
   1.957 +     * matches the subject. */
   1.958 +    findTemplate[0] = *attr2;
   1.959 +    /* we know how big the source subject was. Use that length to create the 
   1.960 +     * space for the target. If it's not enough space, then it means the 
   1.961 +     * source subject is too big, and therefore not a match. GetAttributeValue 
   1.962 +     * will return CKR_BUFFER_TOO_SMALL. Otherwise it should be exactly enough 
   1.963 +     * space (or enough space to be able to compare the result. */
   1.964 +    temp2 = findTemplate[0].pValue = PORT_Alloc(++findTemplate[0].ulValueLen);
   1.965 +    if (temp2 == NULL) {
   1.966 +	crv = CKR_HOST_MEMORY;
   1.967 +	goto done;
   1.968 +    }
   1.969 +    crv = (*db->sdb_GetAttributeValue)(db, id, findTemplate, 1);
   1.970 +    if (crv != CKR_OK) {
   1.971 +	if (crv == CKR_BUFFER_TOO_SMALL) {
   1.972 +	    /* if our buffer is too small, then the Subjects clearly do 
   1.973 +	     * not match */
   1.974 +	    crv = CKR_ATTRIBUTE_VALUE_INVALID;
   1.975 +	    goto loser;
   1.976 +	}
   1.977 +	/* otherwise we couldn't get the value, just fail */
   1.978 +	goto done;
   1.979 +    }
   1.980 +	
   1.981 +    /* Ok, we have both subjects, make sure they are the same. 
   1.982 +     * Compare the subjects */
   1.983 +    if ((findTemplate[0].ulValueLen != attr2->ulValueLen) || 
   1.984 +	(attr2->ulValueLen > 0 &&
   1.985 +	 PORT_Memcmp(findTemplate[0].pValue, attr2->pValue, attr2->ulValueLen) 
   1.986 +	 != 0)) {
   1.987 +    	crv = CKR_ATTRIBUTE_VALUE_INVALID; 
   1.988 +	goto loser;
   1.989 +    }
   1.990 +    crv = CKR_OK;
   1.991 +    
   1.992 +done:
   1.993 +    /* If we've failed for some other reason than a conflict, make sure we 
   1.994 +     * return an error code other than CKR_ATTRIBUTE_VALUE_INVALID. 
   1.995 +     * (NOTE: neither sdb_FindObjectsInit nor sdb_GetAttributeValue should 
   1.996 +     * return CKR_ATTRIBUTE_VALUE_INVALID, so the following is paranoia).
   1.997 +     */
   1.998 +    if (crv == CKR_ATTRIBUTE_VALUE_INVALID) {
   1.999 +	crv = CKR_GENERAL_ERROR; /* clearly a programming error */
  1.1000 +    }
  1.1001 +
  1.1002 +    /* exit point if we found a conflict */
  1.1003 +loser:
  1.1004 +    PORT_Free(temp1);
  1.1005 +    PORT_Free(temp2);
  1.1006 +    return crv;
  1.1007 +}
  1.1008 +
  1.1009 +/*
  1.1010 + * try to update the template to fix any errors. This is only done 
  1.1011 + * during update.
  1.1012 + *
  1.1013 + * NOTE: we must update the template or return an error, or the update caller 
  1.1014 + * will loop forever!
  1.1015 + *
  1.1016 + * Two copies of the source code for this algorithm exist in NSS.  
  1.1017 + * Changes must be made in both copies.
  1.1018 + * The other copy is in pk11_IncrementNickname() in pk11wrap/pk11merge.c.
  1.1019 + *
  1.1020 + */
  1.1021 +static CK_RV
  1.1022 +sftkdb_resolveConflicts(PLArenaPool *arena, CK_OBJECT_CLASS objectType,
  1.1023 +			CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
  1.1024 +{
  1.1025 +    CK_ATTRIBUTE *attr;
  1.1026 +    char *nickname, *newNickname;
  1.1027 +    int end, digit;
  1.1028 +
  1.1029 +    /* sanity checks. We should never get here with these errors */
  1.1030 +    if (objectType != CKO_CERTIFICATE) {
  1.1031 +	return CKR_GENERAL_ERROR; /* shouldn't happen */
  1.1032 +    }
  1.1033 +    attr = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
  1.1034 +    if ((attr == NULL) || (attr->ulValueLen == 0)) {
  1.1035 +	return CKR_GENERAL_ERROR; /* shouldn't happen */
  1.1036 +    }
  1.1037 +
  1.1038 +    /* update the nickname */
  1.1039 +    /* is there a number at the end of the nickname already?
  1.1040 +     * if so just increment that number  */
  1.1041 +    nickname = (char *)attr->pValue;
  1.1042 +
  1.1043 +    /* does nickname end with " #n*" ? */
  1.1044 +    for (end = attr->ulValueLen - 1; 
  1.1045 +         end >= 2 && (digit = nickname[end]) <= '9' &&  digit >= '0'; 
  1.1046 +	 end--)  /* just scan */ ;
  1.1047 +    if (attr->ulValueLen >= 3 &&
  1.1048 +        end < (attr->ulValueLen - 1) /* at least one digit */ &&
  1.1049 +	nickname[end]     == '#'  && 
  1.1050 +	nickname[end - 1] == ' ') {
  1.1051 +    	/* Already has a suitable suffix string */
  1.1052 +    } else {
  1.1053 +	/* ... append " #2" to the name */
  1.1054 +	static const char num2[] = " #2";
  1.1055 +	newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + sizeof(num2));
  1.1056 +	if (!newNickname) {
  1.1057 +	    return CKR_HOST_MEMORY;
  1.1058 +	}
  1.1059 +	PORT_Memcpy(newNickname, nickname, attr->ulValueLen);
  1.1060 +	PORT_Memcpy(&newNickname[attr->ulValueLen], num2, sizeof(num2));
  1.1061 +	attr->pValue = newNickname; /* modifies ptemplate */
  1.1062 +	attr->ulValueLen += 3;      /* 3 is strlen(num2)  */
  1.1063 +	return CKR_OK;
  1.1064 +    }
  1.1065 +
  1.1066 +    for (end = attr->ulValueLen - 1; 
  1.1067 +	 end >= 0 && (digit = nickname[end]) <= '9' &&  digit >= '0'; 
  1.1068 +	 end--) {
  1.1069 +	if (digit < '9') {
  1.1070 +	    nickname[end]++;
  1.1071 +	    return CKR_OK;
  1.1072 +	}
  1.1073 +	nickname[end] = '0';
  1.1074 +    }
  1.1075 +
  1.1076 +    /* we overflowed, insert a new '1' for a carry in front of the number */
  1.1077 +    newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + 1);
  1.1078 +    if (!newNickname) {
  1.1079 +	return CKR_HOST_MEMORY;
  1.1080 +    }
  1.1081 +    /* PORT_Memcpy should handle len of '0' */
  1.1082 +    PORT_Memcpy(newNickname, nickname, ++end);
  1.1083 +    newNickname[end] = '1';
  1.1084 +    PORT_Memset(&newNickname[end+1],'0',attr->ulValueLen - end);
  1.1085 +    attr->pValue = newNickname;
  1.1086 +    attr->ulValueLen++;
  1.1087 +    return CKR_OK;
  1.1088 +}
  1.1089 +
  1.1090 +/*
  1.1091 + * set an attribute and sign it if necessary
  1.1092 + */
  1.1093 +static CK_RV
  1.1094 +sftkdb_setAttributeValue(PLArenaPool *arena, SFTKDBHandle *handle,
  1.1095 +	SDB *db, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template, 
  1.1096 +	CK_ULONG count)
  1.1097 +{
  1.1098 +    CK_RV crv;
  1.1099 +    crv = (*db->sdb_SetAttributeValue)(db, objectID, template, count);
  1.1100 +    if (crv != CKR_OK) {
  1.1101 +	return crv;
  1.1102 +    }
  1.1103 +    crv = sftk_signTemplate(arena, handle, db == handle->update, 
  1.1104 +				objectID, template, count);
  1.1105 +    return crv;
  1.1106 +}
  1.1107 +
  1.1108 +/*
  1.1109 + * write a softoken object out to the database.
  1.1110 + */
  1.1111 +CK_RV
  1.1112 +sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, 
  1.1113 +	     CK_OBJECT_HANDLE *objectID)
  1.1114 +{
  1.1115 +    CK_ATTRIBUTE *template;
  1.1116 +    PLArenaPool *arena;
  1.1117 +    CK_ULONG count;
  1.1118 +    CK_RV crv;
  1.1119 +    SDB *db;
  1.1120 +    PRBool inTransaction = PR_FALSE;
  1.1121 +    CK_OBJECT_HANDLE id;
  1.1122 +
  1.1123 +    *objectID = CK_INVALID_HANDLE;
  1.1124 +
  1.1125 +    if (handle == NULL) {
  1.1126 +	return  CKR_TOKEN_WRITE_PROTECTED;
  1.1127 +    }
  1.1128 +    db = SFTK_GET_SDB(handle);
  1.1129 +
  1.1130 +    /*
  1.1131 +     * we have opened a new database, but we have not yet updated it. We are
  1.1132 +     * still running pointing to the old database (so the application can 
  1.1133 +     * still read). We don't want to write to the old database at this point,
  1.1134 +     * however, since it leads to user confusion. So at this point we simply
  1.1135 +     * require a user login. Let NSS know this so it can prompt the user.
  1.1136 +     */
  1.1137 +    if (db == handle->update) {
  1.1138 +	return CKR_USER_NOT_LOGGED_IN;
  1.1139 +    }
  1.1140 +
  1.1141 +    arena = PORT_NewArena(256);
  1.1142 +    if (arena ==  NULL) {
  1.1143 +	return CKR_HOST_MEMORY;
  1.1144 +    }
  1.1145 +
  1.1146 +    template = sftk_ExtractTemplate(arena, object, handle, &count, &crv);
  1.1147 +    if (!template) {
  1.1148 +	goto loser;
  1.1149 +    }
  1.1150 +
  1.1151 +    crv = (*db->sdb_Begin)(db);
  1.1152 +    if (crv != CKR_OK) {
  1.1153 +	goto loser;
  1.1154 +    }
  1.1155 +    inTransaction = PR_TRUE;
  1.1156 +
  1.1157 +    /*
  1.1158 +     * We want to make the base database as free from object specific knowledge
  1.1159 +     * as possible. To maintain compatibility, keep some of the desirable
  1.1160 +     * object specific semantics of the old database.
  1.1161 +     * 
  1.1162 +     * These were 2 fold:
  1.1163 +     *  1) there were certain conflicts (like trying to set the same nickname 
  1.1164 +     * on two different subjects) that would return an error.
  1.1165 +     *  2) Importing the 'same' object would silently update that object.
  1.1166 +     *
  1.1167 +     * The following 2 functions mimic the desirable effects of these two
  1.1168 +     * semantics without pushing any object knowledge to the underlying database
  1.1169 +     * code.
  1.1170 +     */
  1.1171 +
  1.1172 +    /* make sure we don't have attributes that conflict with the existing DB */
  1.1173 +    crv = sftkdb_checkConflicts(db, object->objclass, template, count,
  1.1174 +				 CK_INVALID_HANDLE);
  1.1175 +    if (crv != CKR_OK) {
  1.1176 +	goto loser;
  1.1177 +    }
  1.1178 +    /* Find any copies that match this particular object */
  1.1179 +    crv = sftkdb_lookupObject(db, object->objclass, &id, template, count);
  1.1180 +    if (crv != CKR_OK) {
  1.1181 +	goto loser;
  1.1182 +    }
  1.1183 +    if (id == CK_INVALID_HANDLE) {
  1.1184 +        crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count);
  1.1185 +    } else {
  1.1186 +	/* object already exists, modify it's attributes */
  1.1187 +	*objectID = id;
  1.1188 +        crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count);
  1.1189 +    }
  1.1190 +    if (crv != CKR_OK) {
  1.1191 +	goto loser;
  1.1192 +    }
  1.1193 +
  1.1194 +    crv = (*db->sdb_Commit)(db);
  1.1195 +    inTransaction = PR_FALSE;
  1.1196 +
  1.1197 +loser:
  1.1198 +    if (inTransaction) {
  1.1199 +	(*db->sdb_Abort)(db);
  1.1200 +	/* It is trivial to show the following code cannot
  1.1201 +	 * happen unless something is horribly wrong with our compilier or
  1.1202 +	 * hardware */
  1.1203 +	PORT_Assert(crv != CKR_OK);
  1.1204 +	if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
  1.1205 +    }
  1.1206 +
  1.1207 +    if (arena) {
  1.1208 +	PORT_FreeArena(arena,PR_FALSE);
  1.1209 +    }
  1.1210 +    if (crv == CKR_OK) {
  1.1211 +	*objectID |= (handle->type | SFTK_TOKEN_TYPE);
  1.1212 +    } 
  1.1213 +    return crv;
  1.1214 +}
  1.1215 +
  1.1216 +
  1.1217 +CK_RV 
  1.1218 +sftkdb_FindObjectsInit(SFTKDBHandle *handle, const CK_ATTRIBUTE *template,
  1.1219 +				 CK_ULONG count, SDBFind **find) 
  1.1220 +{
  1.1221 +    unsigned char *data = NULL;
  1.1222 +    CK_ATTRIBUTE *ntemplate = NULL;
  1.1223 +    CK_RV crv;
  1.1224 +    SDB *db;
  1.1225 +
  1.1226 +    if (handle == NULL) {
  1.1227 +	return CKR_OK;
  1.1228 +    }
  1.1229 +    db = SFTK_GET_SDB(handle);
  1.1230 +
  1.1231 +    if (count !=  0) {
  1.1232 +	ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
  1.1233 +	if (ntemplate == NULL) {
  1.1234 +	    return CKR_HOST_MEMORY;
  1.1235 +	}
  1.1236 +    }
  1.1237 +	
  1.1238 +    crv = (*db->sdb_FindObjectsInit)(db, ntemplate, 
  1.1239 +					     count, find);
  1.1240 +    if (data) {
  1.1241 +	PORT_Free(ntemplate);
  1.1242 +	PORT_Free(data);
  1.1243 +    }
  1.1244 +    return crv;
  1.1245 +}
  1.1246 +
  1.1247 +CK_RV 
  1.1248 +sftkdb_FindObjects(SFTKDBHandle *handle, SDBFind *find, 
  1.1249 +			CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count)
  1.1250 +{
  1.1251 +    CK_RV crv;
  1.1252 +    SDB *db;
  1.1253 +
  1.1254 +    if (handle == NULL) {
  1.1255 +	*count = 0;
  1.1256 +	return CKR_OK;
  1.1257 +    }
  1.1258 +    db = SFTK_GET_SDB(handle);
  1.1259 +
  1.1260 +    crv = (*db->sdb_FindObjects)(db, find, ids, 
  1.1261 +					    arraySize, count);
  1.1262 +    if (crv == CKR_OK) {
  1.1263 +	int i;
  1.1264 +	for (i=0; i < *count; i++) {
  1.1265 +	    ids[i] |= (handle->type | SFTK_TOKEN_TYPE);
  1.1266 +	}
  1.1267 +    }
  1.1268 +    return crv;
  1.1269 +}
  1.1270 +
  1.1271 +CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find)
  1.1272 +{
  1.1273 +    SDB *db;
  1.1274 +    if (handle == NULL) {
  1.1275 +	return CKR_OK;
  1.1276 +    }
  1.1277 +    db = SFTK_GET_SDB(handle);
  1.1278 +    return (*db->sdb_FindObjectsFinal)(db, find);
  1.1279 +}
  1.1280 +
  1.1281 +CK_RV
  1.1282 +sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID,
  1.1283 +                                CK_ATTRIBUTE *template, CK_ULONG count)
  1.1284 +{
  1.1285 +    CK_RV crv,crv2;
  1.1286 +    CK_ATTRIBUTE *ntemplate;
  1.1287 +    unsigned char *data = NULL;
  1.1288 +    SDB *db;
  1.1289 +
  1.1290 +    if (handle == NULL) {
  1.1291 +	return CKR_GENERAL_ERROR;
  1.1292 +    }
  1.1293 +
  1.1294 +    /* short circuit common attributes */
  1.1295 +    if (count == 1 && 
  1.1296 +	  (template[0].type == CKA_TOKEN || 
  1.1297 +	   template[0].type == CKA_PRIVATE ||
  1.1298 +	   template[0].type == CKA_SENSITIVE)) {
  1.1299 +	CK_BBOOL boolVal = CK_TRUE;
  1.1300 +
  1.1301 +	if (template[0].pValue == NULL) {
  1.1302 +	    template[0].ulValueLen = sizeof(CK_BBOOL);
  1.1303 +	    return CKR_OK;
  1.1304 +	}
  1.1305 +	if (template[0].ulValueLen < sizeof(CK_BBOOL)) {
  1.1306 +	    template[0].ulValueLen = -1;
  1.1307 +	    return CKR_BUFFER_TOO_SMALL;
  1.1308 +	}
  1.1309 +
  1.1310 +	if ((template[0].type == CKA_PRIVATE) &&
  1.1311 +    				(handle->type != SFTK_KEYDB_TYPE)) {
  1.1312 +	    boolVal = CK_FALSE;
  1.1313 +	}
  1.1314 +	if ((template[0].type == CKA_SENSITIVE) &&
  1.1315 +    				(handle->type != SFTK_KEYDB_TYPE)) {
  1.1316 +	    boolVal = CK_FALSE;
  1.1317 +	}
  1.1318 +	*(CK_BBOOL *)template[0].pValue = boolVal;
  1.1319 +	template[0].ulValueLen = sizeof(CK_BBOOL);
  1.1320 +	return CKR_OK;
  1.1321 +    }
  1.1322 +
  1.1323 +    db = SFTK_GET_SDB(handle);
  1.1324 +    /* nothing to do */
  1.1325 +    if (count == 0) {
  1.1326 +	return CKR_OK;
  1.1327 +    }
  1.1328 +    ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
  1.1329 +    if (ntemplate == NULL) {
  1.1330 +	return CKR_HOST_MEMORY;
  1.1331 +    }
  1.1332 +    objectID &= SFTK_OBJ_ID_MASK;
  1.1333 +    crv = (*db->sdb_GetAttributeValue)(db, objectID, 
  1.1334 +						ntemplate, count);
  1.1335 +    crv2 = sftkdb_fixupTemplateOut(template, objectID, ntemplate, 
  1.1336 +						count, handle);
  1.1337 +    if (crv == CKR_OK) crv = crv2;
  1.1338 +    if (data) {
  1.1339 +	PORT_Free(ntemplate);
  1.1340 +	PORT_Free(data);
  1.1341 +    }
  1.1342 +    return crv;
  1.1343 +
  1.1344 +}
  1.1345 +
  1.1346 +CK_RV
  1.1347 +sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object,
  1.1348 +                                const CK_ATTRIBUTE *template, CK_ULONG count)
  1.1349 +{
  1.1350 +    CK_ATTRIBUTE *ntemplate;
  1.1351 +    unsigned char *data = NULL;
  1.1352 +    PLArenaPool *arena = NULL;
  1.1353 +    SDB *db;
  1.1354 +    CK_RV crv = CKR_OK;
  1.1355 +    CK_OBJECT_HANDLE objectID = (object->handle & SFTK_OBJ_ID_MASK);
  1.1356 +    PRBool inTransaction = PR_FALSE;
  1.1357 +
  1.1358 +    if (handle == NULL) {
  1.1359 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1360 +    }
  1.1361 +
  1.1362 +    db = SFTK_GET_SDB(handle);
  1.1363 +    /* nothing to do */
  1.1364 +    if (count == 0) {
  1.1365 +	return CKR_OK;
  1.1366 +    }
  1.1367 +    /*
  1.1368 +     * we have opened a new database, but we have not yet updated it. We are
  1.1369 +     * still running  pointing to the old database (so the application can 
  1.1370 +     * still read). We don't want to write to the old database at this point,
  1.1371 +     * however, since it leads to user confusion. So at this point we simply
  1.1372 +     * require a user login. Let NSS know this so it can prompt the user.
  1.1373 +     */
  1.1374 +    if (db == handle->update) {
  1.1375 +	return CKR_USER_NOT_LOGGED_IN;
  1.1376 +    }
  1.1377 +
  1.1378 +    ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
  1.1379 +    if (ntemplate == NULL) {
  1.1380 +	return CKR_HOST_MEMORY;
  1.1381 +    }
  1.1382 +
  1.1383 +    /* make sure we don't have attributes that conflict with the existing DB */
  1.1384 +    crv = sftkdb_checkConflicts(db, object->objclass, template, count, objectID);
  1.1385 +    if (crv != CKR_OK) {
  1.1386 +	goto loser;
  1.1387 +    }
  1.1388 +
  1.1389 +    arena = PORT_NewArena(256);
  1.1390 +    if (arena ==  NULL) {
  1.1391 +	crv = CKR_HOST_MEMORY;
  1.1392 +	goto loser;
  1.1393 +    }
  1.1394 +
  1.1395 +    crv = (*db->sdb_Begin)(db);
  1.1396 +    if (crv != CKR_OK) {
  1.1397 +	goto loser;
  1.1398 +    }
  1.1399 +    inTransaction = PR_TRUE;
  1.1400 +    crv = sftkdb_setAttributeValue(arena, handle, db, 
  1.1401 +				   objectID, template, count);
  1.1402 +    if (crv != CKR_OK) {
  1.1403 +	goto loser;
  1.1404 +    }
  1.1405 +    crv = (*db->sdb_Commit)(db);
  1.1406 +loser:
  1.1407 +    if (crv != CKR_OK && inTransaction) {
  1.1408 +	(*db->sdb_Abort)(db);
  1.1409 +    }
  1.1410 +    if (data) {
  1.1411 +	PORT_Free(ntemplate);
  1.1412 +	PORT_Free(data);
  1.1413 +    }
  1.1414 +    if (arena) {
  1.1415 +	PORT_FreeArena(arena, PR_FALSE);
  1.1416 +    }
  1.1417 +    return crv;
  1.1418 +}
  1.1419 +
  1.1420 +CK_RV
  1.1421 +sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID)
  1.1422 +{
  1.1423 +    CK_RV crv = CKR_OK;
  1.1424 +    SDB *db;
  1.1425 +
  1.1426 +    if (handle == NULL) {
  1.1427 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1428 +    }
  1.1429 +    db = SFTK_GET_SDB(handle);
  1.1430 +    objectID &= SFTK_OBJ_ID_MASK;
  1.1431 +    crv = (*db->sdb_Begin)(db);
  1.1432 +    if (crv != CKR_OK) {
  1.1433 +	goto loser;
  1.1434 +    }
  1.1435 +    crv = (*db->sdb_DestroyObject)(db, objectID);
  1.1436 +    if (crv != CKR_OK) {
  1.1437 +	goto loser;
  1.1438 +    }
  1.1439 +    crv = (*db->sdb_Commit)(db);
  1.1440 +loser:
  1.1441 +    if (crv != CKR_OK) {
  1.1442 +	(*db->sdb_Abort)(db);
  1.1443 +    }
  1.1444 +    return crv;
  1.1445 +}
  1.1446 +
  1.1447 +CK_RV
  1.1448 +sftkdb_CloseDB(SFTKDBHandle *handle)
  1.1449 +{
  1.1450 +#ifdef NO_FORK_CHECK
  1.1451 +    PRBool parentForkedAfterC_Initialize = PR_FALSE;
  1.1452 +#endif
  1.1453 +    if (handle == NULL) {
  1.1454 +	return CKR_OK;
  1.1455 +    }
  1.1456 +    if (handle->update) {
  1.1457 +        if (handle->db->sdb_SetForkState) {
  1.1458 +            (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
  1.1459 +        }
  1.1460 +	(*handle->update->sdb_Close)(handle->update);
  1.1461 +    }
  1.1462 +    if (handle->db) {
  1.1463 +        if (handle->db->sdb_SetForkState) {
  1.1464 +            (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
  1.1465 +        }
  1.1466 +	(*handle->db->sdb_Close)(handle->db);
  1.1467 +    }
  1.1468 +    if (handle->passwordKey.data) {
  1.1469 +	PORT_ZFree(handle->passwordKey.data, handle->passwordKey.len);
  1.1470 +    }
  1.1471 +    if (handle->passwordLock) {
  1.1472 +	SKIP_AFTER_FORK(PZ_DestroyLock(handle->passwordLock));
  1.1473 +    }
  1.1474 +    if (handle->updatePasswordKey) {
  1.1475 +	SECITEM_FreeItem(handle->updatePasswordKey, PR_TRUE);
  1.1476 +    }
  1.1477 +    if (handle->updateID) {
  1.1478 +	PORT_Free(handle->updateID);
  1.1479 +    }
  1.1480 +    PORT_Free(handle);
  1.1481 +    return CKR_OK;
  1.1482 +}
  1.1483 +
  1.1484 +/*
  1.1485 + * reset a database to it's uninitialized state. 
  1.1486 + */
  1.1487 +static CK_RV
  1.1488 +sftkdb_ResetDB(SFTKDBHandle *handle)
  1.1489 +{
  1.1490 +    CK_RV crv = CKR_OK;
  1.1491 +    SDB *db;
  1.1492 +    if (handle == NULL) {
  1.1493 +	return CKR_TOKEN_WRITE_PROTECTED;
  1.1494 +    }
  1.1495 +    db = SFTK_GET_SDB(handle);
  1.1496 +    crv = (*db->sdb_Begin)(db);
  1.1497 +    if (crv != CKR_OK) {
  1.1498 +	goto loser;
  1.1499 +    }
  1.1500 +    crv = (*db->sdb_Reset)(db);
  1.1501 +    if (crv != CKR_OK) {
  1.1502 +	goto loser;
  1.1503 +    }
  1.1504 +    crv = (*db->sdb_Commit)(db);
  1.1505 +loser:
  1.1506 +    if (crv != CKR_OK) {
  1.1507 +	(*db->sdb_Abort)(db);
  1.1508 +    }
  1.1509 +    return crv;
  1.1510 +}
  1.1511 +
  1.1512 +
  1.1513 +CK_RV
  1.1514 +sftkdb_Begin(SFTKDBHandle *handle)
  1.1515 +{
  1.1516 +    CK_RV crv = CKR_OK;
  1.1517 +    SDB *db;
  1.1518 +
  1.1519 +    if (handle == NULL) {
  1.1520 +	return CKR_OK;
  1.1521 +    }
  1.1522 +    db = SFTK_GET_SDB(handle);
  1.1523 +    if (db) {
  1.1524 +	crv = (*db->sdb_Begin)(db);
  1.1525 +    }
  1.1526 +    return crv;
  1.1527 +}
  1.1528 +
  1.1529 +CK_RV
  1.1530 +sftkdb_Commit(SFTKDBHandle *handle)
  1.1531 +{
  1.1532 +    CK_RV crv = CKR_OK;
  1.1533 +    SDB *db;
  1.1534 +
  1.1535 +    if (handle == NULL) {
  1.1536 +	return CKR_OK;
  1.1537 +    }
  1.1538 +    db = SFTK_GET_SDB(handle);
  1.1539 +    if (db) {
  1.1540 +	(*db->sdb_Commit)(db);
  1.1541 +    }
  1.1542 +    return crv;
  1.1543 +}
  1.1544 +
  1.1545 +CK_RV
  1.1546 +sftkdb_Abort(SFTKDBHandle *handle)
  1.1547 +{
  1.1548 +    CK_RV crv = CKR_OK;
  1.1549 +    SDB *db;
  1.1550 +
  1.1551 +    if (handle == NULL) {
  1.1552 +	return CKR_OK;
  1.1553 +    }
  1.1554 +    db = SFTK_GET_SDB(handle);
  1.1555 +    if (db) {
  1.1556 +	crv = (db->sdb_Abort)(db);
  1.1557 +    }
  1.1558 +    return crv;
  1.1559 +}
  1.1560 +
  1.1561 +
  1.1562 +/*
  1.1563 + * functions to update the database from an old database
  1.1564 + */
  1.1565 +
  1.1566 +/*
  1.1567 + * known attributes
  1.1568 + */
  1.1569 +static const CK_ATTRIBUTE_TYPE known_attributes[] = {
  1.1570 +    CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
  1.1571 +    CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
  1.1572 +    CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
  1.1573 +    CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
  1.1574 +    CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
  1.1575 +    CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
  1.1576 +    CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
  1.1577 +    CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
  1.1578 +    CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
  1.1579 +    CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
  1.1580 +    CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, 
  1.1581 +    CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
  1.1582 +    CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
  1.1583 +    CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
  1.1584 +    CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
  1.1585 +    CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
  1.1586 +    CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
  1.1587 +    CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
  1.1588 +    CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
  1.1589 +    CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
  1.1590 +    CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
  1.1591 +    CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL,
  1.1592 +    CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP,
  1.1593 +    CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES,
  1.1594 +    CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED,
  1.1595 +    CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC,
  1.1596 +    CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
  1.1597 +    CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
  1.1598 +    CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
  1.1599 +    CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
  1.1600 +    CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
  1.1601 +    CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
  1.1602 +    CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
  1.1603 +    CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
  1.1604 +};
  1.1605 +
  1.1606 +static int known_attributes_size= sizeof(known_attributes)/
  1.1607 +			   sizeof(known_attributes[0]);
  1.1608 +
  1.1609 +static CK_RV
  1.1610 +sftkdb_GetObjectTemplate(SDB *source, CK_OBJECT_HANDLE id,
  1.1611 +		CK_ATTRIBUTE *ptemplate, CK_ULONG *max)
  1.1612 +{
  1.1613 +    int i,j;
  1.1614 +    CK_RV crv;
  1.1615 +
  1.1616 +    if (*max < known_attributes_size) {
  1.1617 +	*max = known_attributes_size;
  1.1618 +	return CKR_BUFFER_TOO_SMALL;
  1.1619 +    }
  1.1620 +    for (i=0; i < known_attributes_size; i++) {
  1.1621 +	ptemplate[i].type = known_attributes[i];
  1.1622 +	ptemplate[i].pValue = NULL;
  1.1623 +	ptemplate[i].ulValueLen = 0;
  1.1624 +    }
  1.1625 +
  1.1626 +    crv = (*source->sdb_GetAttributeValue)(source, id, 
  1.1627 +					ptemplate, known_attributes_size);
  1.1628 +
  1.1629 +    if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
  1.1630 +	return crv;
  1.1631 +    }
  1.1632 +
  1.1633 +    for (i=0, j=0; i < known_attributes_size; i++, j++) {
  1.1634 +	while (i < known_attributes_size && (ptemplate[i].ulValueLen == -1)) {
  1.1635 +	    i++;
  1.1636 +	}
  1.1637 +	if (i >= known_attributes_size) {
  1.1638 +	    break;
  1.1639 +	}
  1.1640 +	/* cheap optimization */
  1.1641 +	if (i == j) {
  1.1642 +	   continue;
  1.1643 +	}
  1.1644 +	ptemplate[j] = ptemplate[i];
  1.1645 +    }
  1.1646 +    *max = j;
  1.1647 +    return CKR_OK;
  1.1648 +}
  1.1649 +
  1.1650 +static const char SFTKDB_META_UPDATE_TEMPLATE[] = "upd_%s_%s";
  1.1651 +
  1.1652 +/*
  1.1653 + * check to see if we have already updated this database.
  1.1654 + * a NULL updateID means we are trying to do an in place 
  1.1655 + * single database update. In that case we have already
  1.1656 + * determined that an update was necessary.
  1.1657 + */
  1.1658 +static PRBool 
  1.1659 +sftkdb_hasUpdate(const char *typeString, SDB *db, const char *updateID)
  1.1660 +{
  1.1661 +    char *id;
  1.1662 +    CK_RV crv;
  1.1663 +    SECItem dummy = { 0, NULL, 0 };
  1.1664 +    unsigned char dummyData[SDB_MAX_META_DATA_LEN];
  1.1665 +
  1.1666 +    if (!updateID) {
  1.1667 +	return PR_FALSE;
  1.1668 +    }
  1.1669 +    id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
  1.1670 +    if (id == NULL) {
  1.1671 +	return PR_FALSE;
  1.1672 +    }
  1.1673 +    dummy.data = dummyData;
  1.1674 +    dummy.len = sizeof(dummyData);
  1.1675 +
  1.1676 +    crv = (*db->sdb_GetMetaData)(db, id, &dummy, NULL);
  1.1677 +    PR_smprintf_free(id);
  1.1678 +    return crv == CKR_OK ? PR_TRUE : PR_FALSE;
  1.1679 +}
  1.1680 +
  1.1681 +/*
  1.1682 + * we just completed an update, store the update id
  1.1683 + * so we don't need to do it again. If non was given,
  1.1684 + * there is nothing to do.
  1.1685 + */
  1.1686 +static CK_RV
  1.1687 +sftkdb_putUpdate(const char *typeString, SDB *db, const char *updateID)
  1.1688 +{
  1.1689 +    char *id;
  1.1690 +    CK_RV crv;
  1.1691 +    SECItem dummy = { 0, NULL, 0 };
  1.1692 +
  1.1693 +    /* if no id was given, nothing to do */
  1.1694 +    if (updateID == NULL) {
  1.1695 +	return CKR_OK;
  1.1696 +    }
  1.1697 +
  1.1698 +    dummy.data = (unsigned char *)updateID;
  1.1699 +    dummy.len = PORT_Strlen(updateID);
  1.1700 +
  1.1701 +    id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
  1.1702 +    if (id == NULL) {
  1.1703 +	return PR_FALSE;
  1.1704 +    }
  1.1705 +
  1.1706 +    crv = (*db->sdb_PutMetaData)(db, id, &dummy, NULL);
  1.1707 +    PR_smprintf_free(id);
  1.1708 +    return crv;
  1.1709 +}
  1.1710 +
  1.1711 +/*
  1.1712 + * get a ULong attribute from a template:
  1.1713 + * NOTE: this is a raw templated stored in database order!
  1.1714 + */
  1.1715 +static CK_ULONG
  1.1716 +sftkdb_getULongFromTemplate(CK_ATTRIBUTE_TYPE type, 
  1.1717 +			CK_ATTRIBUTE *ptemplate, CK_ULONG len)
  1.1718 +{
  1.1719 +    CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(type,
  1.1720 +					ptemplate, len);
  1.1721 +
  1.1722 +    if (attr && attr->pValue && attr->ulValueLen == SDB_ULONG_SIZE) {
  1.1723 +	return sftk_SDBULong2ULong(attr->pValue);
  1.1724 +    }
  1.1725 +    return (CK_ULONG)-1;
  1.1726 +}
  1.1727 +
  1.1728 +/*
  1.1729 + * we need to find a unique CKA_ID.
  1.1730 + *  The basic idea is to just increment the lowest byte.
  1.1731 + *  This code also handles the following corner cases:
  1.1732 + *   1) the single byte overflows. On overflow we increment the next byte up 
  1.1733 + *    and so forth until we have overflowed the entire CKA_ID.
  1.1734 + *   2) If we overflow the entire CKA_ID we expand it by one byte.
  1.1735 + *   3) the CKA_ID is non-existant, we create a new one with one byte.
  1.1736 + *    This means no matter what CKA_ID is passed, the result of this function 
  1.1737 + *    is always a new CKA_ID, and this function will never return the same 
  1.1738 + *    CKA_ID the it has returned in the passed.
  1.1739 + */
  1.1740 +static CK_RV
  1.1741 +sftkdb_incrementCKAID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
  1.1742 +{
  1.1743 +    unsigned char *buf = ptemplate->pValue;
  1.1744 +    CK_ULONG len = ptemplate->ulValueLen;
  1.1745 +
  1.1746 +    if (buf == NULL || len == (CK_ULONG)-1) {
  1.1747 +	/* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
  1.1748 +	len = 0;
  1.1749 +    } else {
  1.1750 +	CK_ULONG i;
  1.1751 +
  1.1752 +	/* walk from the back to front, incrementing
  1.1753 +	 * the CKA_ID until we no longer have a carry,
  1.1754 +	 * or have hit the front of the id. */
  1.1755 +	for (i=len; i != 0; i--) {
  1.1756 +	    buf[i-1]++;
  1.1757 +	    if (buf[i-1] != 0) {
  1.1758 +		/* no more carries, the increment is complete */
  1.1759 +		return CKR_OK;
  1.1760 +	     }
  1.1761 +	}
  1.1762 +	/* we've now overflowed, fall through and expand the CKA_ID by 
  1.1763 +	 * one byte */
  1.1764 +    } 
  1.1765 +    buf = PORT_ArenaAlloc(arena, len+1);
  1.1766 +    if (!buf) {
  1.1767 +	return CKR_HOST_MEMORY;
  1.1768 +    }
  1.1769 +    if (len > 0) {
  1.1770 +	 PORT_Memcpy(buf, ptemplate->pValue, len);
  1.1771 +    }
  1.1772 +    buf[len] = 0;
  1.1773 +    ptemplate->pValue = buf;
  1.1774 +    ptemplate->ulValueLen = len+1;
  1.1775 +    return CKR_OK;
  1.1776 +}
  1.1777 +
  1.1778 +/*
  1.1779 + * drop an attribute from a template.
  1.1780 + */
  1.1781 +void
  1.1782 +sftkdb_dropAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE *ptemplate, 
  1.1783 +			CK_ULONG *plen)
  1.1784 +{
  1.1785 +   CK_ULONG count = *plen;
  1.1786 +   CK_ULONG i;
  1.1787 +
  1.1788 +   for (i=0; i < count; i++) {
  1.1789 +	if (attr->type == ptemplate[i].type) {
  1.1790 +	    break;
  1.1791 +	}
  1.1792 +   }
  1.1793 +
  1.1794 +   if (i == count) {
  1.1795 +	/* attribute not found */
  1.1796 +	return;
  1.1797 +   }
  1.1798 +
  1.1799 +   /* copy the remaining attributes up */
  1.1800 +   for ( i++; i < count; i++) {
  1.1801 +	ptemplate[i-1] = ptemplate[i];
  1.1802 +   }
  1.1803 +
  1.1804 +   /* decrement the template size */
  1.1805 +   *plen = count -1;
  1.1806 +}
  1.1807 +
  1.1808 +/*
  1.1809 + * create some defines for the following functions to document the meaning
  1.1810 + * of true/false. (make's it easier to remember what means what.
  1.1811 + */
  1.1812 +typedef enum {
  1.1813 +	SFTKDB_DO_NOTHING = 0,
  1.1814 +	SFTKDB_ADD_OBJECT,
  1.1815 +	SFTKDB_MODIFY_OBJECT,
  1.1816 +	SFTKDB_DROP_ATTRIBUTE
  1.1817 +} sftkdbUpdateStatus;
  1.1818 +
  1.1819 +/*
  1.1820 + * helper function to reconcile a single trust entry.
  1.1821 + *   Identify which trust entry we want to keep.
  1.1822 + *   If we don't need to do anything (the records are already equal).
  1.1823 + *       return SFTKDB_DO_NOTHING.
  1.1824 + *   If we want to use the source version,
  1.1825 + *       return SFTKDB_MODIFY_OBJECT
  1.1826 + *   If we want to use the target version,
  1.1827 + *       return SFTKDB_DROP_ATTRIBUTE
  1.1828 + *
  1.1829 + *   In the end the caller will remove any attributes in the source
  1.1830 + *   template when SFTKDB_DROP_ATTRIBUTE is specified, then use do a 
  1.1831 + *   set attributes with that template on the target if we received 
  1.1832 + *   any SFTKDB_MODIFY_OBJECT returns.
  1.1833 + */
  1.1834 +sftkdbUpdateStatus
  1.1835 +sftkdb_reconcileTrustEntry(PLArenaPool *arena, CK_ATTRIBUTE *target,
  1.1836 +			   CK_ATTRIBUTE *source)
  1.1837 +{
  1.1838 +    CK_ULONG targetTrust = sftkdb_getULongFromTemplate(target->type,
  1.1839 +			target, 1);
  1.1840 +    CK_ULONG sourceTrust = sftkdb_getULongFromTemplate(target->type,
  1.1841 +			source, 1);
  1.1842 +
  1.1843 +    /*
  1.1844 +     * try to pick the best solution between the source and the
  1.1845 +     * target. Update the source template if we want the target value
  1.1846 +     * to win out. Prefer cases where we don't actually update the
  1.1847 +     * trust entry.
  1.1848 +     */
  1.1849 +
  1.1850 +    /* they are the same, everything is already kosher */
  1.1851 +    if (targetTrust == sourceTrust) {
  1.1852 +	return SFTKDB_DO_NOTHING;
  1.1853 +    }
  1.1854 +
  1.1855 +    /* handle the case where the source Trust attribute may be a bit
  1.1856 +     * flakey */
  1.1857 +    if (sourceTrust == (CK_ULONG)-1) {
  1.1858 +	/*
  1.1859 +	 * The source Trust is invalid. We know that the target Trust
  1.1860 +	 * must be valid here, otherwise the above 
  1.1861 +	 * targetTrust == sourceTrust check would have succeeded.
  1.1862 +	 */
  1.1863 +	return SFTKDB_DROP_ATTRIBUTE;
  1.1864 +    }
  1.1865 +
  1.1866 +    /* target is invalid, use the source's idea of the trust value */
  1.1867 +    if (targetTrust == (CK_ULONG)-1) {
  1.1868 +	/* overwriting the target in this case is OK */
  1.1869 +	return SFTKDB_MODIFY_OBJECT;
  1.1870 +    }
  1.1871 +
  1.1872 +    /* at this point we know that both attributes exist and have the
  1.1873 +     * appropriate length (SDB_ULONG_SIZE). We no longer need to check
  1.1874 +     * ulValueLen for either attribute.
  1.1875 +     */
  1.1876 +    if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
  1.1877 +	return SFTKDB_DROP_ATTRIBUTE;
  1.1878 +    }
  1.1879 +
  1.1880 +    /* target has no idea, use the source's idea of the trust value */
  1.1881 +    if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
  1.1882 +	/* overwriting the target in this case is OK */
  1.1883 +	return SFTKDB_MODIFY_OBJECT;
  1.1884 +    }
  1.1885 +
  1.1886 +    /* so both the target and the source have some idea of what this 
  1.1887 +     * trust attribute should be, and neither agree exactly. 
  1.1888 +     * At this point, we prefer 'hard' attributes over 'soft' ones. 
  1.1889 +     * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
  1.1890 +     * CKT_NSS_NOT_TRUTED. Soft ones are ones which don't change the
  1.1891 +     * actual trust of the cert (CKT_MUST_VERIFY_TRUST, 
  1.1892 +     * CKT_NSS_VALID_DELEGATOR).
  1.1893 +     */
  1.1894 +    if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST) 
  1.1895 +	|| (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
  1.1896 +	return SFTKDB_DROP_ATTRIBUTE;
  1.1897 +    }
  1.1898 +    if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST) 
  1.1899 +	|| (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
  1.1900 +	/* again, overwriting the target in this case is OK */
  1.1901 +	return SFTKDB_MODIFY_OBJECT;
  1.1902 +    }
  1.1903 +
  1.1904 +    /* both have hard attributes, we have a conflict, let the target win. */
  1.1905 +    return SFTKDB_DROP_ATTRIBUTE;
  1.1906 +}
  1.1907 +
  1.1908 +const CK_ATTRIBUTE_TYPE sftkdb_trustList[] =
  1.1909 +	{ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
  1.1910 +	  CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
  1.1911 +	  CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
  1.1912 +	  CKA_TRUST_TIME_STAMPING };
  1.1913 +
  1.1914 +#define SFTK_TRUST_TEMPLATE_COUNT \
  1.1915 +		(sizeof(sftkdb_trustList)/sizeof(sftkdb_trustList[0]))
  1.1916 +/*
  1.1917 + * Run through the list of known trust types, and reconcile each trust
  1.1918 + * entry one by one. Keep track of we really need to write out the source
  1.1919 + * trust object (overwriting the existing one).
  1.1920 + */
  1.1921 +static sftkdbUpdateStatus
  1.1922 +sftkdb_reconcileTrust(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
  1.1923 +		      CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
  1.1924 +{
  1.1925 +    CK_ATTRIBUTE trustTemplate[SFTK_TRUST_TEMPLATE_COUNT];
  1.1926 +    unsigned char trustData[SFTK_TRUST_TEMPLATE_COUNT*SDB_ULONG_SIZE];
  1.1927 +    sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
  1.1928 +    CK_ULONG i;
  1.1929 +    CK_RV crv;
  1.1930 +
  1.1931 +
  1.1932 +    for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT;  i++) {
  1.1933 +	trustTemplate[i].type = sftkdb_trustList[i];
  1.1934 +	trustTemplate[i].pValue = &trustData[i*SDB_ULONG_SIZE];
  1.1935 +	trustTemplate[i].ulValueLen = SDB_ULONG_SIZE;
  1.1936 +    }
  1.1937 +    crv = (*db->sdb_GetAttributeValue)(db, id, 
  1.1938 +				trustTemplate, SFTK_TRUST_TEMPLATE_COUNT);
  1.1939 +    if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
  1.1940 +	/* target trust has some problems, update it */
  1.1941 +	update = SFTKDB_MODIFY_OBJECT;
  1.1942 +	goto done;
  1.1943 +    }
  1.1944 +
  1.1945 +    for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) {
  1.1946 +	CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(
  1.1947 +			trustTemplate[i].type, ptemplate, *plen);
  1.1948 +	sftkdbUpdateStatus status;
  1.1949 +
  1.1950 +
  1.1951 +	/* if target trust value doesn't exist, nothing to merge */
  1.1952 +	if (trustTemplate[i].ulValueLen == (CK_ULONG)-1) {
  1.1953 +	    /* if the source exists, then we want the source entry,
  1.1954 +	     * go ahead and update */
  1.1955 +	    if (attr && attr->ulValueLen != (CK_ULONG)-1) {
  1.1956 +		update = SFTKDB_MODIFY_OBJECT;
  1.1957 +	    }
  1.1958 +	    continue;
  1.1959 +	}
  1.1960 +
  1.1961 +	/*
  1.1962 +	 * the source doesn't have the attribute, go to the next attribute
  1.1963 +	 */
  1.1964 +	if (attr == NULL) {
  1.1965 +	    continue;
  1.1966 +		
  1.1967 +	}
  1.1968 +	status = sftkdb_reconcileTrustEntry(arena, &trustTemplate[i], attr);
  1.1969 +	if (status == SFTKDB_MODIFY_OBJECT) {
  1.1970 +	    update = SFTKDB_MODIFY_OBJECT;
  1.1971 +	} else if (status == SFTKDB_DROP_ATTRIBUTE) {
  1.1972 +	    /* drop the source copy of the attribute, we are going with
  1.1973 +	     * the target's version */
  1.1974 +	    sftkdb_dropAttribute(attr, ptemplate, plen);
  1.1975 +	}
  1.1976 +    }
  1.1977 +
  1.1978 +    /* finally manage stepup */
  1.1979 +    if (update == SFTKDB_MODIFY_OBJECT) {
  1.1980 +	CK_BBOOL stepUpBool = CK_FALSE;
  1.1981 +	/* if we are going to write from the source, make sure we don't
  1.1982 +	 * overwrite the stepup bit if it's on*/
  1.1983 +	trustTemplate[0].type = CKA_TRUST_STEP_UP_APPROVED;
  1.1984 +	trustTemplate[0].pValue = &stepUpBool;
  1.1985 +	trustTemplate[0].ulValueLen = sizeof(stepUpBool);
  1.1986 +    	crv = (*db->sdb_GetAttributeValue)(db, id, trustTemplate, 1);
  1.1987 +	if ((crv == CKR_OK) && (stepUpBool == CK_TRUE)) {
  1.1988 +	    sftkdb_dropAttribute(trustTemplate, ptemplate, plen);
  1.1989 +	}
  1.1990 +    } else {
  1.1991 +	/* we currently aren't going to update. If the source stepup bit is
  1.1992 +	 * on however, do an update so the target gets it as well */
  1.1993 +	CK_ATTRIBUTE *attr;
  1.1994 +
  1.1995 +	attr = sftkdb_getAttributeFromTemplate(CKA_TRUST_STEP_UP_APPROVED,
  1.1996 +			ptemplate, *plen);
  1.1997 +	if (attr && (attr->ulValueLen == sizeof(CK_BBOOL)) &&  
  1.1998 +			(*(CK_BBOOL *)(attr->pValue) == CK_TRUE)) {
  1.1999 +		update = SFTKDB_MODIFY_OBJECT;
  1.2000 +	}
  1.2001 +    }
  1.2002 +    
  1.2003 +done:
  1.2004 +    return update;
  1.2005 +}
  1.2006 +
  1.2007 +static sftkdbUpdateStatus
  1.2008 +sftkdb_handleIDAndName(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
  1.2009 +		      CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
  1.2010 +{
  1.2011 +    sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
  1.2012 +    CK_ATTRIBUTE *attr1, *attr2;
  1.2013 +    CK_ATTRIBUTE ttemplate[2] = {
  1.2014 +	{CKA_ID, NULL, 0},
  1.2015 +	{CKA_LABEL, NULL, 0}
  1.2016 +    };
  1.2017 +    CK_RV crv;
  1.2018 +
  1.2019 +    attr1 = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
  1.2020 +    attr2 = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, *plen);
  1.2021 +
  1.2022 +    /* if the source has neither an id nor label, don't bother updating */
  1.2023 +    if ( (!attr1 || attr1->ulValueLen == 0) &&
  1.2024 +	 (! attr2 ||  attr2->ulValueLen == 0) ) {
  1.2025 +	return SFTKDB_DO_NOTHING;
  1.2026 +    }
  1.2027 +
  1.2028 +    /* the source has either an id or a label, see what the target has */
  1.2029 +    crv = (*db->sdb_GetAttributeValue)(db, id, ttemplate, 2);
  1.2030 +
  1.2031 +    /* if the target has neither, update from the source */
  1.2032 +    if ( ((ttemplate[0].ulValueLen == 0) || 
  1.2033 +	  (ttemplate[0].ulValueLen == (CK_ULONG)-1))  &&
  1.2034 +         ((ttemplate[1].ulValueLen == 0) || 
  1.2035 +	  (ttemplate[1].ulValueLen == (CK_ULONG)-1)) ) {
  1.2036 +	return SFTKDB_MODIFY_OBJECT;
  1.2037 +    }
  1.2038 +
  1.2039 +    /* check the CKA_ID */
  1.2040 +    if ((ttemplate[0].ulValueLen != 0) && 
  1.2041 +	(ttemplate[0].ulValueLen != (CK_ULONG)-1)) {
  1.2042 +	/* we have a CKA_ID in the target, don't overwrite
  1.2043 +	 * the target with an empty CKA_ID from the source*/
  1.2044 +	if (attr1 && attr1->ulValueLen == 0) {
  1.2045 +	    sftkdb_dropAttribute(attr1, ptemplate, plen);
  1.2046 +	}
  1.2047 +    } else if (attr1 && attr1->ulValueLen != 0) {
  1.2048 +	/* source has a CKA_ID, but the target doesn't, update the target */
  1.2049 +	update = SFTKDB_MODIFY_OBJECT;
  1.2050 +    }
  1.2051 +
  1.2052 +
  1.2053 +    /* check the nickname */
  1.2054 +    if ((ttemplate[1].ulValueLen != 0) && 
  1.2055 +	(ttemplate[1].ulValueLen != (CK_ULONG)-1)) {
  1.2056 +
  1.2057 +	/* we have a nickname in the target, and we don't have to update
  1.2058 +	 * the CKA_ID. We are done. NOTE: if we add addition attributes
  1.2059 +	 * in this check, this shortcut can only go on the last of them. */
  1.2060 +	if (update == SFTKDB_DO_NOTHING) {
  1.2061 +	    return update;
  1.2062 +	}
  1.2063 +	/* we have a nickname in the target, don't overwrite
  1.2064 +	 * the target with an empty nickname from the source */
  1.2065 +	if (attr2 && attr2->ulValueLen == 0) {
  1.2066 +	    sftkdb_dropAttribute(attr2, ptemplate, plen);
  1.2067 +	}
  1.2068 +    } else if (attr2 && attr2->ulValueLen != 0) {
  1.2069 +	/* source has a nickname, but the target doesn't, update the target */
  1.2070 +	update = SFTKDB_MODIFY_OBJECT;
  1.2071 +    }
  1.2072 +
  1.2073 +    return update;
  1.2074 +}
  1.2075 +
  1.2076 +
  1.2077 +		
  1.2078 +/*
  1.2079 + * This function updates the template before we write the object out.
  1.2080 + *
  1.2081 + * If we are going to skip updating this object, return PR_FALSE.
  1.2082 + * If it should be updated we return PR_TRUE.
  1.2083 + * To help readability, these have been defined 
  1.2084 + * as SFTK_DONT_UPDATE and SFTK_UPDATE respectively.
  1.2085 + */
  1.2086 +static PRBool
  1.2087 +sftkdb_updateObjectTemplate(PLArenaPool *arena, SDB *db,
  1.2088 +		    CK_OBJECT_CLASS objectType, 
  1.2089 +		    CK_ATTRIBUTE *ptemplate, CK_ULONG *plen,
  1.2090 +		    CK_OBJECT_HANDLE *targetID)
  1.2091 +{
  1.2092 +    PRBool done; /* should we repeat the loop? */
  1.2093 +    CK_OBJECT_HANDLE id;
  1.2094 +    CK_RV crv = CKR_OK;
  1.2095 +
  1.2096 +    do {
  1.2097 + 	crv = sftkdb_checkConflicts(db, objectType, ptemplate, 
  1.2098 +						*plen, CK_INVALID_HANDLE);
  1.2099 +	if (crv != CKR_ATTRIBUTE_VALUE_INVALID) {
  1.2100 +	    break;
  1.2101 +	}
  1.2102 +	crv = sftkdb_resolveConflicts(arena, objectType, ptemplate, plen);
  1.2103 +    } while (crv == CKR_OK);
  1.2104 +
  1.2105 +    if (crv != CKR_OK) {
  1.2106 +	return SFTKDB_DO_NOTHING;
  1.2107 +    }
  1.2108 +
  1.2109 +    do {
  1.2110 +	done = PR_TRUE;
  1.2111 +	crv = sftkdb_lookupObject(db, objectType, &id, ptemplate, *plen);
  1.2112 +	if (crv != CKR_OK) {
  1.2113 +	    return SFTKDB_DO_NOTHING;
  1.2114 +	}
  1.2115 +
  1.2116 +	/* This object already exists, merge it, don't update */
  1.2117 +	if (id != CK_INVALID_HANDLE) {
  1.2118 +    	    CK_ATTRIBUTE *attr = NULL;
  1.2119 +	    /* special post processing for attributes */
  1.2120 +	    switch (objectType) {
  1.2121 +	    case CKO_CERTIFICATE:
  1.2122 +	    case CKO_PUBLIC_KEY:
  1.2123 +	    case CKO_PRIVATE_KEY:
  1.2124 +		/* update target's CKA_ID and labels if they don't already 
  1.2125 +		 * exist */
  1.2126 +		*targetID = id;
  1.2127 +		return sftkdb_handleIDAndName(arena, db, id, ptemplate, plen);
  1.2128 +	    case CKO_NSS_TRUST:
  1.2129 +		/* if we have conflicting trust object types,
  1.2130 +		 * we need to reconcile them */
  1.2131 +		*targetID = id;
  1.2132 +		return sftkdb_reconcileTrust(arena, db, id, ptemplate, plen);
  1.2133 +	    case CKO_SECRET_KEY:
  1.2134 +		/* secret keys in the old database are all sdr keys, 
  1.2135 +		 * unfortunately they all appear to have the same CKA_ID, 
  1.2136 +		 * even though they are truly different keys, so we always 
  1.2137 +		 * want to update these keys, but we need to 
  1.2138 +		 * give them a new CKA_ID */
  1.2139 +		/* NOTE: this changes ptemplate */
  1.2140 +		attr = sftkdb_getAttributeFromTemplate(CKA_ID,ptemplate,*plen);
  1.2141 +		crv = attr ? sftkdb_incrementCKAID(arena, attr) 
  1.2142 +		           : CKR_HOST_MEMORY; 
  1.2143 +		/* in the extremely rare event that we needed memory and
  1.2144 +		 * couldn't get it, just drop the key */
  1.2145 +		if (crv != CKR_OK) {
  1.2146 +		    return SFTKDB_DO_NOTHING;
  1.2147 +		}
  1.2148 +		done = PR_FALSE; /* repeat this find loop */
  1.2149 +		break;
  1.2150 +	    default:
  1.2151 +		/* for all other objects, if we found the equivalent object,
  1.2152 +		 * don't update it */
  1.2153 +	        return SFTKDB_DO_NOTHING;
  1.2154 +	    }
  1.2155 +	}
  1.2156 +    } while (!done);
  1.2157 +
  1.2158 +    /* this object doesn't exist, update it */
  1.2159 +    return SFTKDB_ADD_OBJECT;
  1.2160 +}
  1.2161 +
  1.2162 +
  1.2163 +#define MAX_ATTRIBUTES 500
  1.2164 +static CK_RV
  1.2165 +sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, 
  1.2166 +		   SECItem *key)
  1.2167 +{
  1.2168 +    CK_ATTRIBUTE template[MAX_ATTRIBUTES];
  1.2169 +    CK_ATTRIBUTE *ptemplate;
  1.2170 +    CK_ULONG max_attributes = MAX_ATTRIBUTES;
  1.2171 +    CK_OBJECT_CLASS objectType;
  1.2172 +    SDB *source = handle->update;
  1.2173 +    SDB *target = handle->db;
  1.2174 +    int i;
  1.2175 +    CK_RV crv;
  1.2176 +    PLArenaPool *arena = NULL;
  1.2177 +
  1.2178 +    arena = PORT_NewArena(256);
  1.2179 +    if (arena ==  NULL) {
  1.2180 +	return CKR_HOST_MEMORY;
  1.2181 +    }
  1.2182 +
  1.2183 +    ptemplate = &template[0];
  1.2184 +    id &= SFTK_OBJ_ID_MASK;
  1.2185 +    crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes);
  1.2186 +    if (crv == CKR_BUFFER_TOO_SMALL) {
  1.2187 +	ptemplate = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, max_attributes);
  1.2188 +	if (ptemplate == NULL) {
  1.2189 +	    crv = CKR_HOST_MEMORY;
  1.2190 +	} else {
  1.2191 +            crv = sftkdb_GetObjectTemplate(source, id, 
  1.2192 +					   ptemplate, &max_attributes);
  1.2193 +	}
  1.2194 +    }
  1.2195 +    if (crv != CKR_OK) {
  1.2196 +	goto loser;
  1.2197 +    }
  1.2198 +
  1.2199 +    for (i=0; i < max_attributes; i++) {
  1.2200 +	ptemplate[i].pValue = PORT_ArenaAlloc(arena,ptemplate[i].ulValueLen);
  1.2201 +	if (ptemplate[i].pValue == NULL) {
  1.2202 +	    crv = CKR_HOST_MEMORY;
  1.2203 +	    goto loser;
  1.2204 +	}
  1.2205 +    }
  1.2206 +    crv = (*source->sdb_GetAttributeValue)(source, id, 
  1.2207 +					   ptemplate, max_attributes);
  1.2208 +    if (crv != CKR_OK) {
  1.2209 +	goto loser;
  1.2210 +    }
  1.2211 +
  1.2212 +    objectType = sftkdb_getULongFromTemplate(CKA_CLASS, ptemplate,
  1.2213 +							 max_attributes);
  1.2214 +
  1.2215 +    /*
  1.2216 +     * Update Object updates the object template if necessary then returns 
  1.2217 +     * whether or not we need to actually write the object out to our target 
  1.2218 +     * database.
  1.2219 +     */
  1.2220 +    if (!handle->updateID) {
  1.2221 +	    crv = sftkdb_CreateObject(arena, handle, target, &id, 
  1.2222 +				ptemplate, max_attributes);
  1.2223 +    } else {
  1.2224 +	sftkdbUpdateStatus update_status;
  1.2225 +	update_status  = sftkdb_updateObjectTemplate(arena, target, 
  1.2226 +			objectType, ptemplate, &max_attributes, &id);
  1.2227 +	switch (update_status) {
  1.2228 +	case SFTKDB_ADD_OBJECT:
  1.2229 +	    crv = sftkdb_CreateObject(arena, handle, target, &id, 
  1.2230 +				ptemplate, max_attributes);
  1.2231 +	    break;
  1.2232 +	case SFTKDB_MODIFY_OBJECT:
  1.2233 +    	    crv = sftkdb_setAttributeValue(arena, handle, target, 
  1.2234 +				   id, ptemplate, max_attributes);
  1.2235 +	    break;
  1.2236 +	case SFTKDB_DO_NOTHING:
  1.2237 +	case SFTKDB_DROP_ATTRIBUTE:
  1.2238 +	    break;
  1.2239 +	}
  1.2240 +    } 
  1.2241 +
  1.2242 +loser:
  1.2243 +    if (arena) {
  1.2244 +	PORT_FreeArena(arena,PR_TRUE);
  1.2245 +    }
  1.2246 +    return crv;
  1.2247 +}
  1.2248 +	
  1.2249 +
  1.2250 +#define MAX_IDS 10
  1.2251 +/*
  1.2252 + * update a new database from an old one, now that we have the key
  1.2253 + */
  1.2254 +CK_RV
  1.2255 +sftkdb_Update(SFTKDBHandle *handle, SECItem *key)
  1.2256 +{
  1.2257 +    SDBFind *find = NULL;
  1.2258 +    CK_ULONG idCount = MAX_IDS;
  1.2259 +    CK_OBJECT_HANDLE ids[MAX_IDS];
  1.2260 +    SECItem *updatePasswordKey = NULL;
  1.2261 +    CK_RV crv, crv2;
  1.2262 +    PRBool inTransaction = PR_FALSE;
  1.2263 +    int i;
  1.2264 +
  1.2265 +    if (handle == NULL) {
  1.2266 +	return CKR_OK;
  1.2267 +    }
  1.2268 +    if (handle->update == NULL) {
  1.2269 +	return CKR_OK;
  1.2270 +    }
  1.2271 +
  1.2272 +    /*
  1.2273 +     * put the whole update under a transaction. This allows us to handle
  1.2274 +     * any possible race conditions between with the updateID check.
  1.2275 +     */
  1.2276 +    crv = (*handle->db->sdb_Begin)(handle->db);
  1.2277 +    if (crv != CKR_OK) {
  1.2278 +	goto loser;
  1.2279 +    }
  1.2280 +    inTransaction = PR_TRUE;
  1.2281 +    
  1.2282 +    /* some one else has already updated this db */
  1.2283 +    if (sftkdb_hasUpdate(sftkdb_TypeString(handle), 
  1.2284 +			 handle->db, handle->updateID)) {
  1.2285 +	crv = CKR_OK;
  1.2286 +	goto done;
  1.2287 +    }
  1.2288 +
  1.2289 +    updatePasswordKey = sftkdb_GetUpdatePasswordKey(handle);
  1.2290 +    if (updatePasswordKey) {
  1.2291 +	/* pass the source DB key to the legacy code, 
  1.2292 +	 * so it can decrypt things */
  1.2293 +	handle->oldKey = updatePasswordKey;
  1.2294 +    }
  1.2295 +    
  1.2296 +    /* find all the objects */
  1.2297 +    crv = sftkdb_FindObjectsInit(handle, NULL, 0, &find);
  1.2298 +
  1.2299 +    if (crv != CKR_OK) {
  1.2300 +	goto loser;
  1.2301 +    }
  1.2302 +    while ((crv == CKR_OK) && (idCount == MAX_IDS)) {
  1.2303 +	crv = sftkdb_FindObjects(handle, find, ids, MAX_IDS, &idCount);
  1.2304 +	for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
  1.2305 +	    crv = sftkdb_mergeObject(handle, ids[i], key);
  1.2306 +	}
  1.2307 +    }
  1.2308 +    crv2 = sftkdb_FindObjectsFinal(handle, find);
  1.2309 +    if (crv == CKR_OK) crv = crv2;
  1.2310 +
  1.2311 +loser:
  1.2312 +    /* no longer need the old key value */
  1.2313 +    handle->oldKey = NULL;
  1.2314 +
  1.2315 +    /* update the password - even if we didn't update objects */
  1.2316 +    if (handle->type == SFTK_KEYDB_TYPE) {
  1.2317 +	SECItem item1, item2;
  1.2318 +	unsigned char data1[SDB_MAX_META_DATA_LEN];
  1.2319 +	unsigned char data2[SDB_MAX_META_DATA_LEN];
  1.2320 +
  1.2321 +	item1.data = data1;
  1.2322 + 	item1.len = sizeof(data1);
  1.2323 +	item2.data = data2;
  1.2324 + 	item2.len = sizeof(data2);
  1.2325 +
  1.2326 +	/* if the target db already has a password, skip this. */
  1.2327 +	crv = (*handle->db->sdb_GetMetaData)(handle->db, "password",
  1.2328 +			&item1, &item2);
  1.2329 +	if (crv == CKR_OK) {
  1.2330 +	    goto done;
  1.2331 +	}
  1.2332 +
  1.2333 +
  1.2334 +	/* nope, update it from the source */
  1.2335 +	crv = (*handle->update->sdb_GetMetaData)(handle->update, "password",
  1.2336 +			&item1, &item2);
  1.2337 +	if (crv != CKR_OK) {
  1.2338 +	    goto done;
  1.2339 +	}
  1.2340 +	crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1,
  1.2341 +						&item2);
  1.2342 +	if (crv != CKR_OK) {
  1.2343 +	    goto done;
  1.2344 +	}
  1.2345 +    }
  1.2346 +
  1.2347 +done:
  1.2348 +    /* finally mark this up to date db up to date */
  1.2349 +    /* some one else has already updated this db */
  1.2350 +    if (crv == CKR_OK) {
  1.2351 +	crv = sftkdb_putUpdate(sftkdb_TypeString(handle), 
  1.2352 +				handle->db, handle->updateID);
  1.2353 +    }
  1.2354 +
  1.2355 +    if (inTransaction) {
  1.2356 +	if (crv == CKR_OK) {
  1.2357 +	    crv = (*handle->db->sdb_Commit)(handle->db);
  1.2358 +	} else {
  1.2359 +	    (*handle->db->sdb_Abort)(handle->db);
  1.2360 +	}
  1.2361 +    }
  1.2362 +    if (handle->update) {
  1.2363 +	(*handle->update->sdb_Close)(handle->update);
  1.2364 +	handle->update = NULL;
  1.2365 +    }
  1.2366 +    if (handle->updateID) {
  1.2367 +	PORT_Free(handle->updateID);
  1.2368 +	handle->updateID = NULL;
  1.2369 +    }
  1.2370 +    sftkdb_FreeUpdatePasswordKey(handle);
  1.2371 +    if (updatePasswordKey) {
  1.2372 +	SECITEM_ZfreeItem(updatePasswordKey, PR_TRUE);
  1.2373 +    }
  1.2374 +    handle->updateDBIsInit = PR_FALSE;
  1.2375 +    return crv;
  1.2376 +}
  1.2377 +
  1.2378 +/******************************************************************
  1.2379 + * DB handle managing functions.
  1.2380 + * 
  1.2381 + * These functions are called by softoken to initialize, acquire,
  1.2382 + * and release database handles.
  1.2383 + */
  1.2384 +
  1.2385 +const char *
  1.2386 +sftkdb_GetUpdateID(SFTKDBHandle *handle)
  1.2387 +{
  1.2388 +    return handle->updateID;
  1.2389 +}
  1.2390 +
  1.2391 +/* release a database handle */
  1.2392 +void
  1.2393 +sftk_freeDB(SFTKDBHandle *handle)
  1.2394 +{
  1.2395 +    PRInt32 ref;
  1.2396 +
  1.2397 +    if (!handle) return;
  1.2398 +    ref = PR_ATOMIC_DECREMENT(&handle->ref);
  1.2399 +    if (ref == 0) {
  1.2400 +	sftkdb_CloseDB(handle);
  1.2401 +    }
  1.2402 +    return;
  1.2403 +}
  1.2404 +
  1.2405 +
  1.2406 +/*
  1.2407 + * acquire a database handle for a certificate db  
  1.2408 + * (database for public objects) 
  1.2409 + */
  1.2410 +SFTKDBHandle *
  1.2411 +sftk_getCertDB(SFTKSlot *slot)
  1.2412 +{
  1.2413 +    SFTKDBHandle *dbHandle;
  1.2414 +
  1.2415 +    PZ_Lock(slot->slotLock);
  1.2416 +    dbHandle = slot->certDB;
  1.2417 +    if (dbHandle) {
  1.2418 +        PR_ATOMIC_INCREMENT(&dbHandle->ref);
  1.2419 +    }
  1.2420 +    PZ_Unlock(slot->slotLock);
  1.2421 +    return dbHandle;
  1.2422 +}
  1.2423 +
  1.2424 +/*
  1.2425 + * acquire a database handle for a key database 
  1.2426 + * (database for private objects)
  1.2427 + */
  1.2428 +SFTKDBHandle *
  1.2429 +sftk_getKeyDB(SFTKSlot *slot)
  1.2430 +{
  1.2431 +    SFTKDBHandle *dbHandle;
  1.2432 +
  1.2433 +    SKIP_AFTER_FORK(PZ_Lock(slot->slotLock));
  1.2434 +    dbHandle = slot->keyDB;
  1.2435 +    if (dbHandle) {
  1.2436 +        PR_ATOMIC_INCREMENT(&dbHandle->ref);
  1.2437 +    }
  1.2438 +    SKIP_AFTER_FORK(PZ_Unlock(slot->slotLock));
  1.2439 +    return dbHandle;
  1.2440 +}
  1.2441 +
  1.2442 +/*
  1.2443 + * acquire the database for a specific object. NOTE: objectID must point
  1.2444 + * to a Token object!
  1.2445 + */
  1.2446 +SFTKDBHandle *
  1.2447 +sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID)
  1.2448 +{
  1.2449 +    SFTKDBHandle *dbHandle;
  1.2450 +
  1.2451 +    PZ_Lock(slot->slotLock);
  1.2452 +    dbHandle = objectID & SFTK_KEYDB_TYPE ? slot->keyDB : slot->certDB;
  1.2453 +    if (dbHandle) {
  1.2454 +        PR_ATOMIC_INCREMENT(&dbHandle->ref);
  1.2455 +    }
  1.2456 +    PZ_Unlock(slot->slotLock);
  1.2457 +    return dbHandle;
  1.2458 +}
  1.2459 +
  1.2460 +/*
  1.2461 + * initialize a new database handle
  1.2462 + */
  1.2463 +static SFTKDBHandle *
  1.2464 +sftk_NewDBHandle(SDB *sdb, int type)
  1.2465 +{
  1.2466 +   SFTKDBHandle *handle = PORT_New(SFTKDBHandle);
  1.2467 +   handle->ref = 1;
  1.2468 +   handle->db = sdb;
  1.2469 +   handle->update = NULL;
  1.2470 +   handle->peerDB = NULL;
  1.2471 +   handle->newKey = NULL;
  1.2472 +   handle->oldKey = NULL;
  1.2473 +   handle->updatePasswordKey = NULL;
  1.2474 +   handle->updateID = NULL;
  1.2475 +   handle->type = type;
  1.2476 +   handle->passwordKey.data = NULL;
  1.2477 +   handle->passwordKey.len = 0;
  1.2478 +   handle->passwordLock = NULL;
  1.2479 +   if (type == SFTK_KEYDB_TYPE) {
  1.2480 +	handle->passwordLock = PZ_NewLock(nssILockAttribute);
  1.2481 +   }
  1.2482 +   sdb->app_private = handle;
  1.2483 +   return handle;
  1.2484 +}
  1.2485 +
  1.2486 +/*
  1.2487 + * reset the key database to it's uninitialized state. This call
  1.2488 + * will clear all the key entried.
  1.2489 + */
  1.2490 +SECStatus
  1.2491 +sftkdb_ResetKeyDB(SFTKDBHandle *handle)
  1.2492 +{
  1.2493 +    CK_RV crv;
  1.2494 +
  1.2495 +    /* only rest the key db */
  1.2496 +    if (handle->type != SFTK_KEYDB_TYPE) {
  1.2497 +	return SECFailure;
  1.2498 +    }
  1.2499 +    crv = sftkdb_ResetDB(handle);
  1.2500 +    if (crv != CKR_OK) {
  1.2501 +	/* set error */
  1.2502 +	return SECFailure;
  1.2503 +    }
  1.2504 +    return SECSuccess;
  1.2505 +}
  1.2506 +
  1.2507 +static PRBool
  1.2508 +sftk_oldVersionExists(const char *dir, int version)
  1.2509 +{
  1.2510 +    int i;
  1.2511 +    PRStatus exists = PR_FAILURE;
  1.2512 +    char *file = NULL;
  1.2513 +
  1.2514 +    for (i=version; i > 1 ; i--) {
  1.2515 +	file = PR_smprintf("%s%d.db",dir,i);
  1.2516 +	if (file == NULL) {
  1.2517 +	    continue;
  1.2518 +	}
  1.2519 +	exists = PR_Access(file, PR_ACCESS_EXISTS);
  1.2520 +	PR_smprintf_free(file);
  1.2521 +	if (exists == PR_SUCCESS) {
  1.2522 +	    return PR_TRUE;
  1.2523 +	}
  1.2524 +    }
  1.2525 +    return PR_FALSE;
  1.2526 +}
  1.2527 +
  1.2528 +static PRBool
  1.2529 +sftk_hasLegacyDB(const char *confdir, const char *certPrefix, 
  1.2530 +		 const char *keyPrefix, int certVersion, int keyVersion)
  1.2531 +{
  1.2532 +    char *dir;
  1.2533 +    PRBool exists;
  1.2534 +
  1.2535 +    if (certPrefix == NULL) {
  1.2536 +	certPrefix = "";
  1.2537 +    }
  1.2538 +
  1.2539 +    if (keyPrefix == NULL) {
  1.2540 +	keyPrefix = "";
  1.2541 +    }
  1.2542 +
  1.2543 +    dir= PR_smprintf("%s/%scert", confdir, certPrefix);
  1.2544 +    if (dir == NULL) {
  1.2545 +	return PR_FALSE;
  1.2546 +    }
  1.2547 +
  1.2548 +    exists = sftk_oldVersionExists(dir, certVersion);
  1.2549 +    PR_smprintf_free(dir);
  1.2550 +    if (exists) {
  1.2551 +	return PR_TRUE;
  1.2552 +    }
  1.2553 +
  1.2554 +    dir= PR_smprintf("%s/%skey", confdir, keyPrefix);
  1.2555 +    if (dir == NULL) {
  1.2556 +	return PR_FALSE;
  1.2557 +    }
  1.2558 +
  1.2559 +    exists = sftk_oldVersionExists(dir, keyVersion);
  1.2560 +    PR_smprintf_free(dir);
  1.2561 +    return exists;
  1.2562 +}
  1.2563 +
  1.2564 +/*
  1.2565 + * initialize certificate and key database handles as a pair.
  1.2566 + *
  1.2567 + * This function figures out what type of database we are opening and
  1.2568 + * calls the appropriate low level function to open the database.
  1.2569 + * It also figures out whether or not to setup up automatic update.
  1.2570 + */
  1.2571 +CK_RV 
  1.2572 +sftk_DBInit(const char *configdir, const char *certPrefix,
  1.2573 +                const char *keyPrefix, const char *updatedir,
  1.2574 +		const char *updCertPrefix, const char *updKeyPrefix, 
  1.2575 +		const char *updateID, PRBool readOnly, PRBool noCertDB,
  1.2576 +                PRBool noKeyDB, PRBool forceOpen, PRBool isFIPS,
  1.2577 +                SFTKDBHandle **certDB, SFTKDBHandle **keyDB)
  1.2578 +{
  1.2579 +    const char *confdir;
  1.2580 +    NSSDBType dbType = NSS_DB_TYPE_NONE;
  1.2581 +    char *appName = NULL;
  1.2582 +    SDB *keySDB, *certSDB;
  1.2583 +    CK_RV crv = CKR_OK;
  1.2584 +    int flags = SDB_RDONLY;
  1.2585 +    PRBool newInit = PR_FALSE;
  1.2586 +    PRBool needUpdate = PR_FALSE;
  1.2587 +
  1.2588 +    if (!readOnly) {
  1.2589 +	flags = SDB_CREATE;
  1.2590 +    }
  1.2591 +
  1.2592 +    *certDB = NULL;
  1.2593 +    *keyDB = NULL;
  1.2594 +
  1.2595 +    if (noKeyDB && noCertDB) {
  1.2596 +	return CKR_OK;
  1.2597 +    }
  1.2598 +    confdir = _NSSUTIL_EvaluateConfigDir(configdir, &dbType, &appName);
  1.2599 +
  1.2600 +    /*
  1.2601 +     * now initialize the appropriate database
  1.2602 +     */
  1.2603 +    switch (dbType) {
  1.2604 +    case NSS_DB_TYPE_LEGACY:
  1.2605 +	crv = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
  1.2606 +		 isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
  1.2607 +	break;
  1.2608 +    case NSS_DB_TYPE_MULTIACCESS:
  1.2609 +	crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags,
  1.2610 +		isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
  1.2611 +	break;
  1.2612 +    case NSS_DB_TYPE_SQL:
  1.2613 +    case NSS_DB_TYPE_EXTERN: /* SHOULD open a loadable db */
  1.2614 +	crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags, 
  1.2615 +		noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit);
  1.2616 +
  1.2617 +        /*
  1.2618 +	 * if we failed to open the DB's read only, use the old ones if
  1.2619 +	 * the exists.
  1.2620 +	 */
  1.2621 +	if (crv != CKR_OK) {
  1.2622 +	    if ((flags == SDB_RDONLY)  &&
  1.2623 +	         sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
  1.2624 +	    /* we have legacy databases, if we failed to open the new format 
  1.2625 +	     * DB's read only, just use the legacy ones */
  1.2626 +		crv = sftkdbCall_open(confdir, certPrefix, 
  1.2627 +			keyPrefix, 8, 3, flags, isFIPS, 
  1.2628 +			noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB);
  1.2629 +	    }
  1.2630 +	/* Handle the database merge case.
  1.2631 +         *
  1.2632 +         * For the merge case, we need help from the application. Only
  1.2633 +         * the application knows where the old database is, and what unique
  1.2634 +         * identifier it has associated with it.
  1.2635 +         *
  1.2636 +         * If the client supplies these values, we use them to determine
  1.2637 +         * if we need to update.
  1.2638 +         */
  1.2639 +	} else if (
  1.2640 +	      /* both update params have been supplied */
  1.2641 +	      updatedir  && *updatedir && updateID  && *updateID
  1.2642 +	      /* old dbs exist? */
  1.2643 +	      && sftk_hasLegacyDB(updatedir, updCertPrefix, updKeyPrefix, 8, 3) 
  1.2644 +	      /* and they have not yet been updated? */
  1.2645 +	      && ((noKeyDB || !sftkdb_hasUpdate("key", keySDB, updateID)) 
  1.2646 +	      || (noCertDB || !sftkdb_hasUpdate("cert", certSDB, updateID)))) {
  1.2647 +	    /* we need to update */
  1.2648 +	    confdir = updatedir;
  1.2649 +	    certPrefix = updCertPrefix;
  1.2650 +	    keyPrefix = updKeyPrefix;
  1.2651 +	    needUpdate = PR_TRUE;
  1.2652 +	} else if (newInit) {
  1.2653 +	    /* if the new format DB was also a newly created DB, and we
  1.2654 +	     * succeeded, then need to update that new database with data
  1.2655 +	     * from the existing legacy DB */
  1.2656 +	    if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
  1.2657 +		needUpdate = PR_TRUE;
  1.2658 +	    }
  1.2659 +	}
  1.2660 +	break;
  1.2661 +    default:
  1.2662 +	crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST 
  1.2663 +				  * return one of the types we already 
  1.2664 +				  * specified. */
  1.2665 +    }
  1.2666 +    if (crv != CKR_OK) {
  1.2667 +	goto done;
  1.2668 +    }
  1.2669 +    if (!noCertDB) {
  1.2670 +	*certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE);
  1.2671 +    } else {
  1.2672 +	*certDB = NULL;
  1.2673 +    }
  1.2674 +    if (!noKeyDB) {
  1.2675 +	*keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE);
  1.2676 +    } else {
  1.2677 +	*keyDB = NULL;
  1.2678 +    }
  1.2679 +
  1.2680 +    /* link them together */
  1.2681 +    if (*certDB) {
  1.2682 +	(*certDB)->peerDB = *keyDB;
  1.2683 +    }
  1.2684 +    if (*keyDB) {
  1.2685 +	(*keyDB)->peerDB = *certDB;
  1.2686 +    }
  1.2687 +
  1.2688 +    /*
  1.2689 +     * if we need to update, open the legacy database and
  1.2690 +     * mark the handle as needing update.
  1.2691 +     */
  1.2692 +    if (needUpdate) {
  1.2693 +	SDB *updateCert = NULL;
  1.2694 +	SDB *updateKey = NULL;
  1.2695 +	CK_RV crv2;
  1.2696 +
  1.2697 +	crv2 = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
  1.2698 +		isFIPS, noCertDB ? NULL : &updateCert, 
  1.2699 +		noKeyDB ? NULL : &updateKey);
  1.2700 +	if (crv2 == CKR_OK) {
  1.2701 +	    if (*certDB) {
  1.2702 +		(*certDB)->update = updateCert;
  1.2703 +		(*certDB)->updateID = updateID && *updateID 
  1.2704 +				? PORT_Strdup(updateID) : NULL;
  1.2705 +		updateCert->app_private = (*certDB);
  1.2706 +	    }
  1.2707 +	    if (*keyDB) {
  1.2708 +		PRBool tokenRemoved = PR_FALSE;
  1.2709 +		(*keyDB)->update = updateKey;
  1.2710 +		(*keyDB)->updateID = updateID && *updateID ? 
  1.2711 +					PORT_Strdup(updateID) : NULL;
  1.2712 +		updateKey->app_private = (*keyDB);
  1.2713 +		(*keyDB)->updateDBIsInit = PR_TRUE;
  1.2714 +		(*keyDB)->updateDBIsInit = 
  1.2715 +			(sftkdb_HasPasswordSet(*keyDB) == SECSuccess) ?
  1.2716 +			 PR_TRUE : PR_FALSE;
  1.2717 +		/* if the password on the key db is NULL, kick off our update
  1.2718 +		 * chain of events */
  1.2719 +		sftkdb_CheckPassword((*keyDB), "", &tokenRemoved);
  1.2720 +	    } else {
  1.2721 +		/* we don't have a key DB, update the certificate DB now */
  1.2722 +		sftkdb_Update(*certDB, NULL);
  1.2723 +	    }
  1.2724 +	}
  1.2725 +    }
  1.2726 +done:
  1.2727 +    if (appName) {
  1.2728 +	PORT_Free(appName);
  1.2729 +    }
  1.2730 +   return forceOpen ? CKR_OK : crv;
  1.2731 +}
  1.2732 +
  1.2733 +CK_RV 
  1.2734 +sftkdb_Shutdown(void)
  1.2735 +{
  1.2736 +  s_shutdown();
  1.2737 +  sftkdbCall_Shutdown();
  1.2738 +  return CKR_OK;
  1.2739 +}
  1.2740 +

mercurial