diff nss/lib/softoken/sftkdb.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/sftkdb.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,2737 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* 
+ *  The following code handles the storage of PKCS 11 modules used by the
+ * NSS. For the rest of NSS, only one kind of database handle exists:
+ *
+ *     SFTKDBHandle
+ *
+ * There is one SFTKDBHandle for the each key database and one for each cert 
+ * database. These databases are opened as associated pairs, one pair per
+ * slot. SFTKDBHandles are reference counted objects.
+ *
+ * Each SFTKDBHandle points to a low level database handle (SDB). This handle
+ * represents the underlying physical database. These objects are not 
+ * reference counted, an are 'owned' by their respective SFTKDBHandles.
+ *
+ *  
+ */
+#include "sftkdb.h"
+#include "sftkdbti.h"
+#include "pkcs11t.h"
+#include "pkcs11i.h"
+#include "sdb.h"
+#include "prprf.h" 
+#include "pratom.h"
+#include "lgglue.h"
+#include "utilpars.h"
+#include "secerr.h"
+#include "softoken.h"
+
+/*
+ * We want all databases to have the same binary representation independent of
+ * endianness or length of the host architecture. In general PKCS #11 attributes
+ * are endian/length independent except those attributes that pass CK_ULONG.
+ *
+ * The following functions fixes up the CK_ULONG type attributes so that the data
+ * base sees a machine independent view. CK_ULONGs are stored as 4 byte network
+ * byte order values (big endian).
+ */
+#define BBP 8
+
+static PRBool
+sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type) 
+{
+    switch(type) {
+    case CKA_CERTIFICATE_CATEGORY:
+    case CKA_CERTIFICATE_TYPE:
+    case CKA_CLASS:
+    case CKA_JAVA_MIDP_SECURITY_DOMAIN:
+    case CKA_KEY_GEN_MECHANISM:
+    case CKA_KEY_TYPE:
+    case CKA_MECHANISM_TYPE:
+    case CKA_MODULUS_BITS:
+    case CKA_PRIME_BITS:
+    case CKA_SUBPRIME_BITS:
+    case CKA_VALUE_BITS:
+    case CKA_VALUE_LEN:
+
+    case CKA_TRUST_DIGITAL_SIGNATURE:
+    case CKA_TRUST_NON_REPUDIATION:
+    case CKA_TRUST_KEY_ENCIPHERMENT:
+    case CKA_TRUST_DATA_ENCIPHERMENT:
+    case CKA_TRUST_KEY_AGREEMENT:
+    case CKA_TRUST_KEY_CERT_SIGN:
+    case CKA_TRUST_CRL_SIGN:
+
+    case CKA_TRUST_SERVER_AUTH:
+    case CKA_TRUST_CLIENT_AUTH:
+    case CKA_TRUST_CODE_SIGNING:
+    case CKA_TRUST_EMAIL_PROTECTION:
+    case CKA_TRUST_IPSEC_END_SYSTEM:
+    case CKA_TRUST_IPSEC_TUNNEL:
+    case CKA_TRUST_IPSEC_USER:
+    case CKA_TRUST_TIME_STAMPING:
+    case CKA_TRUST_STEP_UP_APPROVED:
+	return PR_TRUE;
+    default:
+	break;
+    }
+    return PR_FALSE;
+    
+}
+
+/* are the attributes private? */
+static PRBool
+sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type) 
+{
+    switch(type) {
+    case CKA_VALUE:
+    case CKA_PRIVATE_EXPONENT:
+    case CKA_PRIME_1:
+    case CKA_PRIME_2:
+    case CKA_EXPONENT_1:
+    case CKA_EXPONENT_2:
+    case CKA_COEFFICIENT:
+	return PR_TRUE;
+    default:
+	break;
+    }
+    return PR_FALSE;
+}
+
+/* These attributes must be authenticated with an hmac. */
+static PRBool
+sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type) 
+{
+    switch(type) {
+    case CKA_MODULUS:
+    case CKA_PUBLIC_EXPONENT:
+    case CKA_CERT_SHA1_HASH:
+    case CKA_CERT_MD5_HASH:
+    case CKA_TRUST_SERVER_AUTH:
+    case CKA_TRUST_CLIENT_AUTH:
+    case CKA_TRUST_EMAIL_PROTECTION:
+    case CKA_TRUST_CODE_SIGNING:
+    case CKA_TRUST_STEP_UP_APPROVED:
+    case CKA_NSS_OVERRIDE_EXTENSIONS:
+	return PR_TRUE;
+    default:
+	break;
+    }
+    return PR_FALSE;
+}
+
+/*
+ * convert a native ULONG to a database ulong. Database ulong's
+ * are all 4 byte big endian values.
+ */
+void
+sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value)
+{ 
+    int i;
+
+    for (i=0; i < SDB_ULONG_SIZE; i++) {
+	data[i] = (value >> (SDB_ULONG_SIZE-1-i)*BBP) & 0xff;
+    }
+}
+
+/*
+ * convert a database ulong back to a native ULONG. (reverse of the above
+ * function.
+ */
+static CK_ULONG
+sftk_SDBULong2ULong(unsigned char *data)
+{
+    int i;
+    CK_ULONG value = 0;
+
+    for (i=0; i < SDB_ULONG_SIZE; i++) {
+	value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE-1-i)*BBP);
+    }
+    return value;
+}
+
+/*
+ * fix up the input templates. Our fixed up ints are stored in data and must
+ * be freed by the caller. The new template must also be freed. If there are no
+ * CK_ULONG attributes, the orignal template is passed in as is.
+ */
+static CK_ATTRIBUTE *
+sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, 
+			unsigned char **dataOut)
+{
+    int i;
+    int ulongCount = 0;
+    unsigned char *data;
+    CK_ATTRIBUTE *ntemplate;
+
+    *dataOut = NULL;
+
+    /* first count the number of CK_ULONG attributes */
+    for (i=0; i < count; i++) {
+	/* Don't 'fixup' NULL values */
+	if (!template[i].pValue) {
+	    continue;
+	}
+	if (template[i].ulValueLen == sizeof (CK_ULONG)) {
+	    if ( sftkdb_isULONGAttribute(template[i].type)) {
+		ulongCount++;
+	    }
+	}
+    }
+    /* no attributes to fixup, just call on through */
+    if (ulongCount == 0) {
+	return (CK_ATTRIBUTE *)template;
+    }
+
+    /* allocate space for new ULONGS */
+    data = (unsigned char *)PORT_Alloc(SDB_ULONG_SIZE*ulongCount);
+    if (!data) {
+	return NULL;
+    }
+
+    /* allocate new template */
+    ntemplate = PORT_NewArray(CK_ATTRIBUTE,count);
+    if (!ntemplate) {
+	PORT_Free(data);
+	return NULL;
+    }
+    *dataOut = data;
+    /* copy the old template, fixup the actual ulongs */
+    for (i=0; i < count; i++) {
+	ntemplate[i] = template[i];
+	/* Don't 'fixup' NULL values */
+	if (!template[i].pValue) {
+	    continue;
+	}
+	if (template[i].ulValueLen == sizeof (CK_ULONG)) {
+	    if ( sftkdb_isULONGAttribute(template[i].type) ) {
+		CK_ULONG value = *(CK_ULONG *) template[i].pValue;
+		sftk_ULong2SDBULong(data, value);
+		ntemplate[i].pValue = data;
+		ntemplate[i].ulValueLen = SDB_ULONG_SIZE;
+		data += SDB_ULONG_SIZE;
+	    }
+	}
+    }
+    return ntemplate;
+}
+
+
+static const char SFTKDB_META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x";
+
+/*
+ * return a string describing the database type (key or cert)
+ */
+const char *
+sftkdb_TypeString(SFTKDBHandle *handle)
+{
+   return (handle->type == SFTK_KEYDB_TYPE) ? "key" : "cert";
+}
+
+/*
+ * Some attributes are signed with an Hmac and a pbe key generated from
+ * the password. This signature is stored indexed by object handle and
+ * attribute type in the meta data table in the key database.
+ *
+ * Signature entries are indexed by the string
+ * sig_[cert/key]_{ObjectID}_{Attribute}
+ *
+ * This function fetches that pkcs5 signature. Caller supplies a SECItem
+ * pre-allocated to the appropriate size if the SECItem is too small the
+ * function will fail with CKR_BUFFER_TOO_SMALL.
+ */
+static CK_RV
+sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle, 
+		CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
+		SECItem *signText)
+{
+    SDB *db;
+    char id[30];
+    CK_RV crv;
+
+    db = SFTK_GET_SDB(keyHandle);
+
+    sprintf(id, SFTKDB_META_SIG_TEMPLATE,
+	sftkdb_TypeString(handle),
+	(unsigned int)objectID, (unsigned int)type);
+
+    crv = (*db->sdb_GetMetaData)(db, id, signText, NULL);
+    return crv;
+}
+
+/*
+ * Some attributes are signed with an Hmac and a pbe key generated from
+ * the password. This signature is stored indexed by object handle and
+ * attribute type in the meta data table in the key database.
+ *
+ * Signature entries are indexed by the string
+ * sig_[cert/key]_{ObjectID}_{Attribute}
+ *
+ * This function stores that pkcs5 signature.
+ */
+CK_RV
+sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget, 
+		CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
+		SECItem *signText)
+{
+    char id[30];
+    CK_RV crv;
+
+    sprintf(id, SFTKDB_META_SIG_TEMPLATE,
+	sftkdb_TypeString(handle),
+	(unsigned int)objectID, (unsigned int)type);
+
+    crv = (*keyTarget->sdb_PutMetaData)(keyTarget, id, signText, NULL);
+    return crv;
+}
+
+/*
+ * fix up returned data. NOTE: sftkdb_fixupTemplateIn has already allocated
+ * separate data sections for the database ULONG values.
+ */
+static CK_RV
+sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID,
+		CK_ATTRIBUTE *ntemplate, int count, SFTKDBHandle *handle)
+{
+    int i;
+    CK_RV crv = CKR_OK;
+    SFTKDBHandle *keyHandle;
+    PRBool checkSig = PR_TRUE;
+    PRBool checkEnc = PR_TRUE;
+
+    PORT_Assert(handle);
+
+    /* find the key handle */
+    keyHandle = handle;
+    if (handle->type != SFTK_KEYDB_TYPE) {
+	checkEnc = PR_FALSE;
+	keyHandle = handle->peerDB;
+    }
+
+    if ((keyHandle == NULL) || 
+	((SFTK_GET_SDB(keyHandle)->sdb_flags & SDB_HAS_META) == 0)  ||
+	(keyHandle->passwordKey.data == NULL)) {
+	checkSig = PR_FALSE;
+    }
+
+    for (i=0; i < count; i++) {
+	CK_ULONG length = template[i].ulValueLen;
+	template[i].ulValueLen = ntemplate[i].ulValueLen;
+	/* fixup ulongs */
+	if (ntemplate[i].ulValueLen == SDB_ULONG_SIZE) {
+	    if (sftkdb_isULONGAttribute(template[i].type)) {
+		if (template[i].pValue) {
+		    CK_ULONG value;
+		    unsigned char *data;
+
+		    data = (unsigned char *)ntemplate[i].pValue;
+		    value = sftk_SDBULong2ULong(ntemplate[i].pValue);
+		    if (length < sizeof(CK_ULONG)) {
+			template[i].ulValueLen = -1;
+			crv = CKR_BUFFER_TOO_SMALL;
+			continue;
+		    } 
+		    PORT_Memcpy(template[i].pValue,&value,sizeof(CK_ULONG));
+		}
+		template[i].ulValueLen = sizeof(CK_ULONG);
+	    }
+	}
+
+	/* if no data was retrieved, no need to process encrypted or signed
+	 * attributes */
+	if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) {
+	    continue;
+	}
+
+	/* fixup private attributes */
+	if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) {
+	    /* we have a private attribute */
+	    /* This code depends on the fact that the cipherText is bigger
+	     * than the plain text */
+	    SECItem cipherText;
+	    SECItem *plainText;
+	    SECStatus rv;
+
+	    cipherText.data = ntemplate[i].pValue;
+	    cipherText.len = ntemplate[i].ulValueLen;
+    	    PZ_Lock(handle->passwordLock);
+	    if (handle->passwordKey.data == NULL) {
+		PZ_Unlock(handle->passwordLock);
+		template[i].ulValueLen = -1;
+		crv = CKR_USER_NOT_LOGGED_IN;
+		continue;
+	    }
+	    rv = sftkdb_DecryptAttribute(&handle->passwordKey, 
+					&cipherText, &plainText);
+	    PZ_Unlock(handle->passwordLock);
+	    if (rv != SECSuccess) {
+		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+		template[i].ulValueLen = -1;
+		crv = CKR_GENERAL_ERROR;
+		continue;
+	    }
+	    PORT_Assert(template[i].ulValueLen >= plainText->len);
+	    if (template[i].ulValueLen < plainText->len) {
+		SECITEM_FreeItem(plainText,PR_TRUE);
+		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+		template[i].ulValueLen = -1;
+		crv = CKR_GENERAL_ERROR;
+		continue;
+	    }
+		
+	    /* copy the plain text back into the template */
+	    PORT_Memcpy(template[i].pValue, plainText->data, plainText->len);
+	    template[i].ulValueLen = plainText->len;
+	    SECITEM_FreeItem(plainText,PR_TRUE);
+	}
+	/* make sure signed attributes are valid */
+	if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) {
+	    SECStatus rv;
+	    SECItem signText;
+	    SECItem plainText;
+	    unsigned char signData[SDB_MAX_META_DATA_LEN];
+
+	    signText.data = signData;
+	    signText.len = sizeof(signData);
+
+	    rv = sftkdb_getAttributeSignature(handle, keyHandle, 
+				objectID, ntemplate[i].type, &signText);
+	    if (rv != SECSuccess) {
+		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+		template[i].ulValueLen = -1;
+		crv = CKR_DATA_INVALID; /* better error code? */
+		continue;
+	    }
+
+	    plainText.data = ntemplate[i].pValue;
+	    plainText.len = ntemplate[i].ulValueLen;
+
+	    /*
+	     * we do a second check holding the lock just in case the user
+	     * loggout while we were trying to get the signature.
+	     */
+    	    PZ_Lock(keyHandle->passwordLock);
+	    if (keyHandle->passwordKey.data == NULL) {
+		/* if we are no longer logged in, no use checking the other
+		 * Signatures either. */
+		checkSig = PR_FALSE; 
+		PZ_Unlock(keyHandle->passwordLock);
+		continue;
+	    }
+
+	    rv = sftkdb_VerifyAttribute(&keyHandle->passwordKey, 
+				objectID, ntemplate[i].type,
+				&plainText, &signText);
+	    PZ_Unlock(keyHandle->passwordLock);
+	    if (rv != SECSuccess) {
+		PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+		template[i].ulValueLen = -1;
+		crv = CKR_SIGNATURE_INVALID; /* better error code? */
+	    }
+	    /* This Attribute is fine */
+	}
+    }
+    return crv;
+}
+
+/*
+ * Some attributes are signed with an HMAC and a pbe key generated from
+ * the password. This signature is stored indexed by object handle and
+ *
+ * Those attributes are:
+ * 1) Trust object hashes and trust values.
+ * 2) public key values.
+ *
+ * Certs themselves are considered properly authenticated by virtue of their
+ * signature, or their matching hash with the trust object.
+ *
+ * These signature is only checked for objects coming from shared databases. 
+ * Older dbm style databases have such no signature checks. HMACs are also 
+ * only checked when the token is logged in, as it requires a pbe generated 
+ * from the password.
+ *
+ * Tokens which have no key database (and therefore no master password) do not
+ * have any stored signature values. Signature values are stored in the key
+ * database, since the signature data is tightly coupled to the key database
+ * password. 
+ *
+ * This function takes a template of attributes that were either created or
+ * modified. These attributes are checked to see if the need to be signed.
+ * If they do, then this function signs the attributes and writes them
+ * to the meta data store.
+ *
+ * This function can fail if there are attributes that must be signed, but
+ * the token is not logged in.
+ *
+ * The caller is expected to abort any transaction he was in in the
+ * event of a failure of this function.
+ */
+static CK_RV
+sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle, 
+		  PRBool mayBeUpdateDB,
+		  CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
+		  CK_ULONG count)
+{
+    int i;
+    CK_RV crv;
+    SFTKDBHandle *keyHandle = handle;
+    SDB *keyTarget = NULL;
+    PRBool usingPeerDB = PR_FALSE;
+    PRBool inPeerDBTransaction = PR_FALSE;
+
+    PORT_Assert(handle);
+
+    if (handle->type != SFTK_KEYDB_TYPE) {
+	keyHandle = handle->peerDB;
+	usingPeerDB = PR_TRUE;
+    }
+
+    /* no key DB defined? then no need to sign anything */
+    if (keyHandle == NULL) {
+	crv = CKR_OK;
+	goto loser;
+    }
+
+    /* When we are in a middle of an update, we have an update database set, 
+     * but we want to write to the real database. The bool mayBeUpdateDB is
+     * set to TRUE if it's possible that we want to write an update database
+     * rather than a primary */
+    keyTarget = (mayBeUpdateDB && keyHandle->update) ? 
+		keyHandle->update : keyHandle->db;
+
+    /* skip the the database does not support meta data */
+    if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) {
+	crv = CKR_OK;
+	goto loser;
+    }
+
+    /* If we had to switch databases, we need to initialize a transaction. */
+    if (usingPeerDB) {
+	crv = (*keyTarget->sdb_Begin)(keyTarget);
+	if (crv != CKR_OK) {
+	    goto loser;
+	}
+	inPeerDBTransaction = PR_TRUE;
+    }
+
+    for (i=0; i < count; i ++) {
+	if (sftkdb_isAuthenticatedAttribute(template[i].type)) {
+	    SECStatus rv;
+	    SECItem *signText;
+	    SECItem plainText;
+
+	    plainText.data = template[i].pValue;
+	    plainText.len = template[i].ulValueLen;
+	    PZ_Lock(keyHandle->passwordLock);
+	    if (keyHandle->passwordKey.data == NULL) {
+		PZ_Unlock(keyHandle->passwordLock);
+		crv = CKR_USER_NOT_LOGGED_IN;
+		goto loser;
+	    }
+	    rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey, 
+				objectID, template[i].type,
+				&plainText, &signText);
+	    PZ_Unlock(keyHandle->passwordLock);
+	    if (rv != SECSuccess) {
+		crv = CKR_GENERAL_ERROR; /* better error code here? */
+		goto loser;
+	    }
+	    rv = sftkdb_PutAttributeSignature(handle, keyTarget, 
+				objectID, template[i].type, signText);
+	    if (rv != SECSuccess) {
+		crv = CKR_GENERAL_ERROR; /* better error code here? */
+		goto loser;
+	    }
+	}
+    }
+    crv = CKR_OK;
+
+    /* If necessary, commit the transaction */
+    if (inPeerDBTransaction) {
+	crv = (*keyTarget->sdb_Commit)(keyTarget);
+	if (crv != CKR_OK) {
+	    goto loser;
+	}
+	inPeerDBTransaction = PR_FALSE;
+    }
+
+loser:
+    if (inPeerDBTransaction) {
+	/* The transaction must have failed. Abort. */
+	(*keyTarget->sdb_Abort)(keyTarget);
+	PORT_Assert(crv != CKR_OK);
+	if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
+    }
+    return crv;
+}
+
+static CK_RV
+sftkdb_CreateObject(PLArenaPool *arena, SFTKDBHandle *handle,
+	SDB *db, CK_OBJECT_HANDLE *objectID,
+        CK_ATTRIBUTE *template, CK_ULONG count)
+{
+    PRBool inTransaction = PR_FALSE;
+    CK_RV crv;
+
+    inTransaction = PR_TRUE;
+    
+    crv = (*db->sdb_CreateObject)(db, objectID, template, count);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    crv = sftk_signTemplate(arena, handle, (db == handle->update),
+					*objectID, template, count);
+loser:
+
+    return crv;
+}
+
+
+CK_ATTRIBUTE * 
+sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, 
+		     SFTKDBHandle *handle,CK_ULONG *pcount, 
+		     CK_RV *crv)
+{
+    int count;
+    CK_ATTRIBUTE *template;
+    int i, templateIndex;
+    SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+    PRBool doEnc = PR_TRUE;
+
+    *crv = CKR_OK;
+
+    if (sessObject == NULL) {
+	*crv = CKR_GENERAL_ERROR; /* internal programming error */
+	return NULL;
+    }
+
+    PORT_Assert(handle);
+    /* find the key handle */
+    if (handle->type != SFTK_KEYDB_TYPE) {
+	doEnc = PR_FALSE;
+    }
+
+    PZ_Lock(sessObject->attributeLock);
+    count = 0;
+    for (i=0; i < sessObject->hashSize; i++) {
+	SFTKAttribute *attr;
+   	for (attr=sessObject->head[i]; attr; attr=attr->next) {
+	    count++;
+	}
+    }
+    template = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, count);
+    if (template == NULL) {
+        PZ_Unlock(sessObject->attributeLock);
+	*crv = CKR_HOST_MEMORY;
+	return NULL;
+    }
+    templateIndex = 0;
+    for (i=0; i < sessObject->hashSize; i++) {
+	SFTKAttribute *attr;
+   	for (attr=sessObject->head[i]; attr; attr=attr->next) {
+	    CK_ATTRIBUTE *tp = &template[templateIndex++];
+	    /* copy the attribute */
+	    *tp = attr->attrib;
+
+	    /* fixup  ULONG s */
+	    if ((tp->ulValueLen == sizeof (CK_ULONG)) &&
+		(sftkdb_isULONGAttribute(tp->type)) ) {
+		CK_ULONG value = *(CK_ULONG *) tp->pValue;
+		unsigned char *data;
+
+		tp->pValue = PORT_ArenaAlloc(arena, SDB_ULONG_SIZE);
+		data = (unsigned char *)tp->pValue;
+		if (data == NULL) {
+		    *crv = CKR_HOST_MEMORY;
+		    break;
+		}
+		sftk_ULong2SDBULong(data, value);
+		tp->ulValueLen = SDB_ULONG_SIZE;
+	    }
+
+	    /* encrypt private attributes */
+	    if (doEnc && sftkdb_isPrivateAttribute(tp->type)) {
+		/* we have a private attribute */
+		SECItem *cipherText;
+		SECItem plainText;
+		SECStatus rv;
+
+		plainText.data = tp->pValue;
+		plainText.len = tp->ulValueLen;
+		PZ_Lock(handle->passwordLock);
+		if (handle->passwordKey.data == NULL) {
+		    PZ_Unlock(handle->passwordLock);
+		    *crv = CKR_USER_NOT_LOGGED_IN;
+		    break;
+		}
+		rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey, 
+						&plainText, &cipherText);
+		PZ_Unlock(handle->passwordLock);
+		if (rv == SECSuccess) {
+		    tp->pValue = cipherText->data;
+		    tp->ulValueLen = cipherText->len;
+		} else {
+		    *crv = CKR_GENERAL_ERROR; /* better error code here? */
+		    break;
+		}
+		PORT_Memset(plainText.data, 0, plainText.len);
+	    }
+	}
+    }
+    PORT_Assert(templateIndex <= count);
+    PZ_Unlock(sessObject->attributeLock);
+
+    if (*crv != CKR_OK) {
+	return NULL;
+    }
+    if (pcount) {
+	*pcount = count;
+    }
+    return template;
+
+}
+
+/*
+ * return a pointer to the attribute in the give template.
+ * The return value is not const, as the caller may modify
+ * the given attribute value, but such modifications will
+ * modify the actual value in the template.
+ */
+static CK_ATTRIBUTE *
+sftkdb_getAttributeFromTemplate(CK_ATTRIBUTE_TYPE attribute, 
+			    CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+    CK_ULONG i;
+
+    for (i=0; i < len; i++) {
+	if (attribute == ptemplate[i].type) {
+	    return &ptemplate[i];
+	}
+    }
+    return NULL;
+}
+
+static const CK_ATTRIBUTE *
+sftkdb_getAttributeFromConstTemplate(CK_ATTRIBUTE_TYPE attribute, 
+				const CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+    CK_ULONG i;
+
+    for (i=0; i < len; i++) {
+	if (attribute == ptemplate[i].type) {
+	    return &ptemplate[i];
+	}
+    }
+    return NULL;
+}
+
+
+/*
+ * fetch a template which identifies 'unique' entries based on object type
+ */
+static CK_RV
+sftkdb_getFindTemplate(CK_OBJECT_CLASS objectType, unsigned char *objTypeData,
+			CK_ATTRIBUTE *findTemplate, CK_ULONG *findCount,
+			CK_ATTRIBUTE *ptemplate, int len)
+{
+    CK_ATTRIBUTE *attr;
+    CK_ULONG count = 1;
+
+    sftk_ULong2SDBULong(objTypeData, objectType);
+    findTemplate[0].type = CKA_CLASS;
+    findTemplate[0].pValue = objTypeData;
+    findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
+
+    switch (objectType) {
+    case CKO_CERTIFICATE:
+    case CKO_NSS_TRUST:
+	attr = sftkdb_getAttributeFromTemplate(CKA_ISSUER, ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	findTemplate[1] = *attr;
+	attr = sftkdb_getAttributeFromTemplate(CKA_SERIAL_NUMBER, 
+					ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	findTemplate[2] = *attr;
+	count = 3;
+	break;
+	
+    case CKO_PRIVATE_KEY:
+    case CKO_PUBLIC_KEY:
+    case CKO_SECRET_KEY:
+	attr = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	if (attr->ulValueLen == 0) {
+	    /* key is too generic to determine that it's unique, usually
+	     * happens in the key gen case */
+	    return CKR_OBJECT_HANDLE_INVALID;
+	}
+	
+	findTemplate[1] = *attr;
+	count = 2;
+	break;
+
+    case CKO_NSS_CRL:
+	attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	findTemplate[1] = *attr;
+	count = 2;
+	break;
+
+    case CKO_NSS_SMIME:
+	attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	findTemplate[1] = *attr;
+	attr = sftkdb_getAttributeFromTemplate(CKA_NSS_EMAIL, ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	findTemplate[2] = *attr;
+	count = 3;
+	break;
+    default:
+	attr = sftkdb_getAttributeFromTemplate(CKA_VALUE, ptemplate, len);
+	if (attr == NULL) {
+	    return CKR_TEMPLATE_INCOMPLETE;
+	}
+	findTemplate[1] = *attr;
+	count = 2;
+	break;
+    }
+    *findCount = count;
+
+    return CKR_OK;
+}
+
+/*
+ * look to see if this object already exists and return its object ID if
+ * it does.
+ */
+static CK_RV
+sftkdb_lookupObject(SDB *db, CK_OBJECT_CLASS objectType, 
+		 CK_OBJECT_HANDLE *id, CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+    CK_ATTRIBUTE findTemplate[3];
+    CK_ULONG count = 1;
+    CK_ULONG objCount = 0;
+    SDBFind *find = NULL;
+    unsigned char objTypeData[SDB_ULONG_SIZE];
+    CK_RV crv;
+
+    *id = CK_INVALID_HANDLE;
+    if (objectType == CKO_NSS_CRL) {
+	return CKR_OK;
+    }
+    crv = sftkdb_getFindTemplate(objectType, objTypeData,
+			findTemplate, &count, ptemplate, len);
+
+    if (crv == CKR_OBJECT_HANDLE_INVALID) {
+	/* key is too generic to determine that it's unique, usually
+	 * happens in the key gen case, tell the caller to go ahead
+	 * and just create it */
+	return CKR_OK;
+    }
+    if (crv != CKR_OK) {
+	return crv;
+    }
+
+    /* use the raw find, so we get the correct database */
+    crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find);
+    if (crv != CKR_OK) {
+	return crv;
+    }
+    (*db->sdb_FindObjects)(db, find, id, 1, &objCount);
+    (*db->sdb_FindObjectsFinal)(db, find);
+
+    if (objCount == 0) {
+	*id = CK_INVALID_HANDLE;
+    }
+    return CKR_OK;
+}
+
+
+/*
+ * check to see if this template conflicts with others in our current database.
+ */
+static CK_RV
+sftkdb_checkConflicts(SDB *db, CK_OBJECT_CLASS objectType, 
+			const CK_ATTRIBUTE *ptemplate, CK_ULONG len, 
+			CK_OBJECT_HANDLE sourceID)
+{
+    CK_ATTRIBUTE findTemplate[2];
+    unsigned char objTypeData[SDB_ULONG_SIZE];
+    /* we may need to allocate some temporaries. Keep track of what was 
+     * allocated so we can free it in the end */
+    unsigned char *temp1 = NULL; 
+    unsigned char *temp2 = NULL;
+    CK_ULONG objCount = 0;
+    SDBFind *find = NULL;
+    CK_OBJECT_HANDLE id;
+    const CK_ATTRIBUTE *attr, *attr2;
+    CK_RV crv;
+    CK_ATTRIBUTE subject;
+
+    /* Currently the only conflict is with nicknames pointing to the same 
+     * subject when creating or modifying a certificate. */
+    /* If the object is not a cert, no problem. */
+    if (objectType != CKO_CERTIFICATE) {
+	return CKR_OK;
+    }
+    /* if not setting a nickname then there's still no problem */
+    attr = sftkdb_getAttributeFromConstTemplate(CKA_LABEL, ptemplate, len);
+    if ((attr == NULL) || (attr->ulValueLen == 0)) {
+	return CKR_OK;
+    }
+    /* fetch the subject of the source. For creation and merge, this should
+     * be found in the template */
+    attr2 = sftkdb_getAttributeFromConstTemplate(CKA_SUBJECT, ptemplate, len);
+    if (sourceID == CK_INVALID_HANDLE) {
+	if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen < 0)) {
+	    crv = CKR_TEMPLATE_INCOMPLETE; 
+	    goto done;
+	}
+    } else if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen <= 0)) {
+	/* sourceID is set if we are trying to modify an existing entry instead
+	 * of creating a new one. In this case the subject may not be (probably
+	 * isn't) in the template, we have to read it from the database */
+    	subject.type = CKA_SUBJECT;
+    	subject.pValue = NULL;
+    	subject.ulValueLen = 0;
+    	crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1);
+	if (crv != CKR_OK) {
+	    goto done;
+	}
+	if ((CK_LONG)subject.ulValueLen < 0) {
+	    crv = CKR_DEVICE_ERROR; /* closest pkcs11 error to corrupted DB */
+	    goto done;
+	}
+	temp1 = subject.pValue = PORT_Alloc(++subject.ulValueLen);
+	if (temp1 == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    goto done;
+	}
+    	crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1);
+	if (crv != CKR_OK) {
+	    goto done;
+	}
+	attr2 = &subject;
+    }
+    
+    /* check for another cert in the database with the same nickname */
+    sftk_ULong2SDBULong(objTypeData, objectType);
+    findTemplate[0].type = CKA_CLASS;
+    findTemplate[0].pValue = objTypeData;
+    findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
+    findTemplate[1] = *attr;
+
+    crv = (*db->sdb_FindObjectsInit)(db, findTemplate, 2, &find);
+    if (crv != CKR_OK) {
+	goto done;
+    }
+    (*db->sdb_FindObjects)(db, find, &id, 1, &objCount);
+    (*db->sdb_FindObjectsFinal)(db, find);
+
+    /* object count == 0 means no conflicting certs found, 
+     * go on with the operation */
+    if (objCount == 0) {
+	crv = CKR_OK;
+	goto done;
+    }
+
+    /* There is a least one cert that shares the nickname, make sure it also
+     * matches the subject. */
+    findTemplate[0] = *attr2;
+    /* we know how big the source subject was. Use that length to create the 
+     * space for the target. If it's not enough space, then it means the 
+     * source subject is too big, and therefore not a match. GetAttributeValue 
+     * will return CKR_BUFFER_TOO_SMALL. Otherwise it should be exactly enough 
+     * space (or enough space to be able to compare the result. */
+    temp2 = findTemplate[0].pValue = PORT_Alloc(++findTemplate[0].ulValueLen);
+    if (temp2 == NULL) {
+	crv = CKR_HOST_MEMORY;
+	goto done;
+    }
+    crv = (*db->sdb_GetAttributeValue)(db, id, findTemplate, 1);
+    if (crv != CKR_OK) {
+	if (crv == CKR_BUFFER_TOO_SMALL) {
+	    /* if our buffer is too small, then the Subjects clearly do 
+	     * not match */
+	    crv = CKR_ATTRIBUTE_VALUE_INVALID;
+	    goto loser;
+	}
+	/* otherwise we couldn't get the value, just fail */
+	goto done;
+    }
+	
+    /* Ok, we have both subjects, make sure they are the same. 
+     * Compare the subjects */
+    if ((findTemplate[0].ulValueLen != attr2->ulValueLen) || 
+	(attr2->ulValueLen > 0 &&
+	 PORT_Memcmp(findTemplate[0].pValue, attr2->pValue, attr2->ulValueLen) 
+	 != 0)) {
+    	crv = CKR_ATTRIBUTE_VALUE_INVALID; 
+	goto loser;
+    }
+    crv = CKR_OK;
+    
+done:
+    /* If we've failed for some other reason than a conflict, make sure we 
+     * return an error code other than CKR_ATTRIBUTE_VALUE_INVALID. 
+     * (NOTE: neither sdb_FindObjectsInit nor sdb_GetAttributeValue should 
+     * return CKR_ATTRIBUTE_VALUE_INVALID, so the following is paranoia).
+     */
+    if (crv == CKR_ATTRIBUTE_VALUE_INVALID) {
+	crv = CKR_GENERAL_ERROR; /* clearly a programming error */
+    }
+
+    /* exit point if we found a conflict */
+loser:
+    PORT_Free(temp1);
+    PORT_Free(temp2);
+    return crv;
+}
+
+/*
+ * try to update the template to fix any errors. This is only done 
+ * during update.
+ *
+ * NOTE: we must update the template or return an error, or the update caller 
+ * will loop forever!
+ *
+ * Two copies of the source code for this algorithm exist in NSS.  
+ * Changes must be made in both copies.
+ * The other copy is in pk11_IncrementNickname() in pk11wrap/pk11merge.c.
+ *
+ */
+static CK_RV
+sftkdb_resolveConflicts(PLArenaPool *arena, CK_OBJECT_CLASS objectType,
+			CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
+{
+    CK_ATTRIBUTE *attr;
+    char *nickname, *newNickname;
+    int end, digit;
+
+    /* sanity checks. We should never get here with these errors */
+    if (objectType != CKO_CERTIFICATE) {
+	return CKR_GENERAL_ERROR; /* shouldn't happen */
+    }
+    attr = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
+    if ((attr == NULL) || (attr->ulValueLen == 0)) {
+	return CKR_GENERAL_ERROR; /* shouldn't happen */
+    }
+
+    /* update the nickname */
+    /* is there a number at the end of the nickname already?
+     * if so just increment that number  */
+    nickname = (char *)attr->pValue;
+
+    /* does nickname end with " #n*" ? */
+    for (end = attr->ulValueLen - 1; 
+         end >= 2 && (digit = nickname[end]) <= '9' &&  digit >= '0'; 
+	 end--)  /* just scan */ ;
+    if (attr->ulValueLen >= 3 &&
+        end < (attr->ulValueLen - 1) /* at least one digit */ &&
+	nickname[end]     == '#'  && 
+	nickname[end - 1] == ' ') {
+    	/* Already has a suitable suffix string */
+    } else {
+	/* ... append " #2" to the name */
+	static const char num2[] = " #2";
+	newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + sizeof(num2));
+	if (!newNickname) {
+	    return CKR_HOST_MEMORY;
+	}
+	PORT_Memcpy(newNickname, nickname, attr->ulValueLen);
+	PORT_Memcpy(&newNickname[attr->ulValueLen], num2, sizeof(num2));
+	attr->pValue = newNickname; /* modifies ptemplate */
+	attr->ulValueLen += 3;      /* 3 is strlen(num2)  */
+	return CKR_OK;
+    }
+
+    for (end = attr->ulValueLen - 1; 
+	 end >= 0 && (digit = nickname[end]) <= '9' &&  digit >= '0'; 
+	 end--) {
+	if (digit < '9') {
+	    nickname[end]++;
+	    return CKR_OK;
+	}
+	nickname[end] = '0';
+    }
+
+    /* we overflowed, insert a new '1' for a carry in front of the number */
+    newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + 1);
+    if (!newNickname) {
+	return CKR_HOST_MEMORY;
+    }
+    /* PORT_Memcpy should handle len of '0' */
+    PORT_Memcpy(newNickname, nickname, ++end);
+    newNickname[end] = '1';
+    PORT_Memset(&newNickname[end+1],'0',attr->ulValueLen - end);
+    attr->pValue = newNickname;
+    attr->ulValueLen++;
+    return CKR_OK;
+}
+
+/*
+ * set an attribute and sign it if necessary
+ */
+static CK_RV
+sftkdb_setAttributeValue(PLArenaPool *arena, SFTKDBHandle *handle,
+	SDB *db, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template, 
+	CK_ULONG count)
+{
+    CK_RV crv;
+    crv = (*db->sdb_SetAttributeValue)(db, objectID, template, count);
+    if (crv != CKR_OK) {
+	return crv;
+    }
+    crv = sftk_signTemplate(arena, handle, db == handle->update, 
+				objectID, template, count);
+    return crv;
+}
+
+/*
+ * write a softoken object out to the database.
+ */
+CK_RV
+sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, 
+	     CK_OBJECT_HANDLE *objectID)
+{
+    CK_ATTRIBUTE *template;
+    PLArenaPool *arena;
+    CK_ULONG count;
+    CK_RV crv;
+    SDB *db;
+    PRBool inTransaction = PR_FALSE;
+    CK_OBJECT_HANDLE id;
+
+    *objectID = CK_INVALID_HANDLE;
+
+    if (handle == NULL) {
+	return  CKR_TOKEN_WRITE_PROTECTED;
+    }
+    db = SFTK_GET_SDB(handle);
+
+    /*
+     * we have opened a new database, but we have not yet updated it. We are
+     * still running pointing to the old database (so the application can 
+     * still read). We don't want to write to the old database at this point,
+     * however, since it leads to user confusion. So at this point we simply
+     * require a user login. Let NSS know this so it can prompt the user.
+     */
+    if (db == handle->update) {
+	return CKR_USER_NOT_LOGGED_IN;
+    }
+
+    arena = PORT_NewArena(256);
+    if (arena ==  NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    template = sftk_ExtractTemplate(arena, object, handle, &count, &crv);
+    if (!template) {
+	goto loser;
+    }
+
+    crv = (*db->sdb_Begin)(db);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    inTransaction = PR_TRUE;
+
+    /*
+     * We want to make the base database as free from object specific knowledge
+     * as possible. To maintain compatibility, keep some of the desirable
+     * object specific semantics of the old database.
+     * 
+     * These were 2 fold:
+     *  1) there were certain conflicts (like trying to set the same nickname 
+     * on two different subjects) that would return an error.
+     *  2) Importing the 'same' object would silently update that object.
+     *
+     * The following 2 functions mimic the desirable effects of these two
+     * semantics without pushing any object knowledge to the underlying database
+     * code.
+     */
+
+    /* make sure we don't have attributes that conflict with the existing DB */
+    crv = sftkdb_checkConflicts(db, object->objclass, template, count,
+				 CK_INVALID_HANDLE);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    /* Find any copies that match this particular object */
+    crv = sftkdb_lookupObject(db, object->objclass, &id, template, count);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    if (id == CK_INVALID_HANDLE) {
+        crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count);
+    } else {
+	/* object already exists, modify it's attributes */
+	*objectID = id;
+        crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count);
+    }
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+
+    crv = (*db->sdb_Commit)(db);
+    inTransaction = PR_FALSE;
+
+loser:
+    if (inTransaction) {
+	(*db->sdb_Abort)(db);
+	/* It is trivial to show the following code cannot
+	 * happen unless something is horribly wrong with our compilier or
+	 * hardware */
+	PORT_Assert(crv != CKR_OK);
+	if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
+    }
+
+    if (arena) {
+	PORT_FreeArena(arena,PR_FALSE);
+    }
+    if (crv == CKR_OK) {
+	*objectID |= (handle->type | SFTK_TOKEN_TYPE);
+    } 
+    return crv;
+}
+
+
+CK_RV 
+sftkdb_FindObjectsInit(SFTKDBHandle *handle, const CK_ATTRIBUTE *template,
+				 CK_ULONG count, SDBFind **find) 
+{
+    unsigned char *data = NULL;
+    CK_ATTRIBUTE *ntemplate = NULL;
+    CK_RV crv;
+    SDB *db;
+
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    db = SFTK_GET_SDB(handle);
+
+    if (count !=  0) {
+	ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
+	if (ntemplate == NULL) {
+	    return CKR_HOST_MEMORY;
+	}
+    }
+	
+    crv = (*db->sdb_FindObjectsInit)(db, ntemplate, 
+					     count, find);
+    if (data) {
+	PORT_Free(ntemplate);
+	PORT_Free(data);
+    }
+    return crv;
+}
+
+CK_RV 
+sftkdb_FindObjects(SFTKDBHandle *handle, SDBFind *find, 
+			CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count)
+{
+    CK_RV crv;
+    SDB *db;
+
+    if (handle == NULL) {
+	*count = 0;
+	return CKR_OK;
+    }
+    db = SFTK_GET_SDB(handle);
+
+    crv = (*db->sdb_FindObjects)(db, find, ids, 
+					    arraySize, count);
+    if (crv == CKR_OK) {
+	int i;
+	for (i=0; i < *count; i++) {
+	    ids[i] |= (handle->type | SFTK_TOKEN_TYPE);
+	}
+    }
+    return crv;
+}
+
+CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find)
+{
+    SDB *db;
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    db = SFTK_GET_SDB(handle);
+    return (*db->sdb_FindObjectsFinal)(db, find);
+}
+
+CK_RV
+sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID,
+                                CK_ATTRIBUTE *template, CK_ULONG count)
+{
+    CK_RV crv,crv2;
+    CK_ATTRIBUTE *ntemplate;
+    unsigned char *data = NULL;
+    SDB *db;
+
+    if (handle == NULL) {
+	return CKR_GENERAL_ERROR;
+    }
+
+    /* short circuit common attributes */
+    if (count == 1 && 
+	  (template[0].type == CKA_TOKEN || 
+	   template[0].type == CKA_PRIVATE ||
+	   template[0].type == CKA_SENSITIVE)) {
+	CK_BBOOL boolVal = CK_TRUE;
+
+	if (template[0].pValue == NULL) {
+	    template[0].ulValueLen = sizeof(CK_BBOOL);
+	    return CKR_OK;
+	}
+	if (template[0].ulValueLen < sizeof(CK_BBOOL)) {
+	    template[0].ulValueLen = -1;
+	    return CKR_BUFFER_TOO_SMALL;
+	}
+
+	if ((template[0].type == CKA_PRIVATE) &&
+    				(handle->type != SFTK_KEYDB_TYPE)) {
+	    boolVal = CK_FALSE;
+	}
+	if ((template[0].type == CKA_SENSITIVE) &&
+    				(handle->type != SFTK_KEYDB_TYPE)) {
+	    boolVal = CK_FALSE;
+	}
+	*(CK_BBOOL *)template[0].pValue = boolVal;
+	template[0].ulValueLen = sizeof(CK_BBOOL);
+	return CKR_OK;
+    }
+
+    db = SFTK_GET_SDB(handle);
+    /* nothing to do */
+    if (count == 0) {
+	return CKR_OK;
+    }
+    ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
+    if (ntemplate == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+    objectID &= SFTK_OBJ_ID_MASK;
+    crv = (*db->sdb_GetAttributeValue)(db, objectID, 
+						ntemplate, count);
+    crv2 = sftkdb_fixupTemplateOut(template, objectID, ntemplate, 
+						count, handle);
+    if (crv == CKR_OK) crv = crv2;
+    if (data) {
+	PORT_Free(ntemplate);
+	PORT_Free(data);
+    }
+    return crv;
+
+}
+
+CK_RV
+sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object,
+                                const CK_ATTRIBUTE *template, CK_ULONG count)
+{
+    CK_ATTRIBUTE *ntemplate;
+    unsigned char *data = NULL;
+    PLArenaPool *arena = NULL;
+    SDB *db;
+    CK_RV crv = CKR_OK;
+    CK_OBJECT_HANDLE objectID = (object->handle & SFTK_OBJ_ID_MASK);
+    PRBool inTransaction = PR_FALSE;
+
+    if (handle == NULL) {
+	return CKR_TOKEN_WRITE_PROTECTED;
+    }
+
+    db = SFTK_GET_SDB(handle);
+    /* nothing to do */
+    if (count == 0) {
+	return CKR_OK;
+    }
+    /*
+     * we have opened a new database, but we have not yet updated it. We are
+     * still running  pointing to the old database (so the application can 
+     * still read). We don't want to write to the old database at this point,
+     * however, since it leads to user confusion. So at this point we simply
+     * require a user login. Let NSS know this so it can prompt the user.
+     */
+    if (db == handle->update) {
+	return CKR_USER_NOT_LOGGED_IN;
+    }
+
+    ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
+    if (ntemplate == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    /* make sure we don't have attributes that conflict with the existing DB */
+    crv = sftkdb_checkConflicts(db, object->objclass, template, count, objectID);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+
+    arena = PORT_NewArena(256);
+    if (arena ==  NULL) {
+	crv = CKR_HOST_MEMORY;
+	goto loser;
+    }
+
+    crv = (*db->sdb_Begin)(db);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    inTransaction = PR_TRUE;
+    crv = sftkdb_setAttributeValue(arena, handle, db, 
+				   objectID, template, count);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    crv = (*db->sdb_Commit)(db);
+loser:
+    if (crv != CKR_OK && inTransaction) {
+	(*db->sdb_Abort)(db);
+    }
+    if (data) {
+	PORT_Free(ntemplate);
+	PORT_Free(data);
+    }
+    if (arena) {
+	PORT_FreeArena(arena, PR_FALSE);
+    }
+    return crv;
+}
+
+CK_RV
+sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID)
+{
+    CK_RV crv = CKR_OK;
+    SDB *db;
+
+    if (handle == NULL) {
+	return CKR_TOKEN_WRITE_PROTECTED;
+    }
+    db = SFTK_GET_SDB(handle);
+    objectID &= SFTK_OBJ_ID_MASK;
+    crv = (*db->sdb_Begin)(db);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    crv = (*db->sdb_DestroyObject)(db, objectID);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    crv = (*db->sdb_Commit)(db);
+loser:
+    if (crv != CKR_OK) {
+	(*db->sdb_Abort)(db);
+    }
+    return crv;
+}
+
+CK_RV
+sftkdb_CloseDB(SFTKDBHandle *handle)
+{
+#ifdef NO_FORK_CHECK
+    PRBool parentForkedAfterC_Initialize = PR_FALSE;
+#endif
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    if (handle->update) {
+        if (handle->db->sdb_SetForkState) {
+            (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
+        }
+	(*handle->update->sdb_Close)(handle->update);
+    }
+    if (handle->db) {
+        if (handle->db->sdb_SetForkState) {
+            (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
+        }
+	(*handle->db->sdb_Close)(handle->db);
+    }
+    if (handle->passwordKey.data) {
+	PORT_ZFree(handle->passwordKey.data, handle->passwordKey.len);
+    }
+    if (handle->passwordLock) {
+	SKIP_AFTER_FORK(PZ_DestroyLock(handle->passwordLock));
+    }
+    if (handle->updatePasswordKey) {
+	SECITEM_FreeItem(handle->updatePasswordKey, PR_TRUE);
+    }
+    if (handle->updateID) {
+	PORT_Free(handle->updateID);
+    }
+    PORT_Free(handle);
+    return CKR_OK;
+}
+
+/*
+ * reset a database to it's uninitialized state. 
+ */
+static CK_RV
+sftkdb_ResetDB(SFTKDBHandle *handle)
+{
+    CK_RV crv = CKR_OK;
+    SDB *db;
+    if (handle == NULL) {
+	return CKR_TOKEN_WRITE_PROTECTED;
+    }
+    db = SFTK_GET_SDB(handle);
+    crv = (*db->sdb_Begin)(db);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    crv = (*db->sdb_Reset)(db);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    crv = (*db->sdb_Commit)(db);
+loser:
+    if (crv != CKR_OK) {
+	(*db->sdb_Abort)(db);
+    }
+    return crv;
+}
+
+
+CK_RV
+sftkdb_Begin(SFTKDBHandle *handle)
+{
+    CK_RV crv = CKR_OK;
+    SDB *db;
+
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    db = SFTK_GET_SDB(handle);
+    if (db) {
+	crv = (*db->sdb_Begin)(db);
+    }
+    return crv;
+}
+
+CK_RV
+sftkdb_Commit(SFTKDBHandle *handle)
+{
+    CK_RV crv = CKR_OK;
+    SDB *db;
+
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    db = SFTK_GET_SDB(handle);
+    if (db) {
+	(*db->sdb_Commit)(db);
+    }
+    return crv;
+}
+
+CK_RV
+sftkdb_Abort(SFTKDBHandle *handle)
+{
+    CK_RV crv = CKR_OK;
+    SDB *db;
+
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    db = SFTK_GET_SDB(handle);
+    if (db) {
+	crv = (db->sdb_Abort)(db);
+    }
+    return crv;
+}
+
+
+/*
+ * functions to update the database from an old database
+ */
+
+/*
+ * known attributes
+ */
+static const CK_ATTRIBUTE_TYPE known_attributes[] = {
+    CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
+    CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
+    CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
+    CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
+    CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+    CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
+    CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
+    CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
+    CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
+    CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
+    CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, 
+    CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
+    CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
+    CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
+    CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
+    CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
+    CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
+    CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
+    CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
+    CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
+    CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
+    CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL,
+    CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP,
+    CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES,
+    CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED,
+    CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC,
+    CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
+    CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
+    CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
+    CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
+    CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
+    CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
+    CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
+    CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
+};
+
+static int known_attributes_size= sizeof(known_attributes)/
+			   sizeof(known_attributes[0]);
+
+static CK_RV
+sftkdb_GetObjectTemplate(SDB *source, CK_OBJECT_HANDLE id,
+		CK_ATTRIBUTE *ptemplate, CK_ULONG *max)
+{
+    int i,j;
+    CK_RV crv;
+
+    if (*max < known_attributes_size) {
+	*max = known_attributes_size;
+	return CKR_BUFFER_TOO_SMALL;
+    }
+    for (i=0; i < known_attributes_size; i++) {
+	ptemplate[i].type = known_attributes[i];
+	ptemplate[i].pValue = NULL;
+	ptemplate[i].ulValueLen = 0;
+    }
+
+    crv = (*source->sdb_GetAttributeValue)(source, id, 
+					ptemplate, known_attributes_size);
+
+    if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
+	return crv;
+    }
+
+    for (i=0, j=0; i < known_attributes_size; i++, j++) {
+	while (i < known_attributes_size && (ptemplate[i].ulValueLen == -1)) {
+	    i++;
+	}
+	if (i >= known_attributes_size) {
+	    break;
+	}
+	/* cheap optimization */
+	if (i == j) {
+	   continue;
+	}
+	ptemplate[j] = ptemplate[i];
+    }
+    *max = j;
+    return CKR_OK;
+}
+
+static const char SFTKDB_META_UPDATE_TEMPLATE[] = "upd_%s_%s";
+
+/*
+ * check to see if we have already updated this database.
+ * a NULL updateID means we are trying to do an in place 
+ * single database update. In that case we have already
+ * determined that an update was necessary.
+ */
+static PRBool 
+sftkdb_hasUpdate(const char *typeString, SDB *db, const char *updateID)
+{
+    char *id;
+    CK_RV crv;
+    SECItem dummy = { 0, NULL, 0 };
+    unsigned char dummyData[SDB_MAX_META_DATA_LEN];
+
+    if (!updateID) {
+	return PR_FALSE;
+    }
+    id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
+    if (id == NULL) {
+	return PR_FALSE;
+    }
+    dummy.data = dummyData;
+    dummy.len = sizeof(dummyData);
+
+    crv = (*db->sdb_GetMetaData)(db, id, &dummy, NULL);
+    PR_smprintf_free(id);
+    return crv == CKR_OK ? PR_TRUE : PR_FALSE;
+}
+
+/*
+ * we just completed an update, store the update id
+ * so we don't need to do it again. If non was given,
+ * there is nothing to do.
+ */
+static CK_RV
+sftkdb_putUpdate(const char *typeString, SDB *db, const char *updateID)
+{
+    char *id;
+    CK_RV crv;
+    SECItem dummy = { 0, NULL, 0 };
+
+    /* if no id was given, nothing to do */
+    if (updateID == NULL) {
+	return CKR_OK;
+    }
+
+    dummy.data = (unsigned char *)updateID;
+    dummy.len = PORT_Strlen(updateID);
+
+    id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
+    if (id == NULL) {
+	return PR_FALSE;
+    }
+
+    crv = (*db->sdb_PutMetaData)(db, id, &dummy, NULL);
+    PR_smprintf_free(id);
+    return crv;
+}
+
+/*
+ * get a ULong attribute from a template:
+ * NOTE: this is a raw templated stored in database order!
+ */
+static CK_ULONG
+sftkdb_getULongFromTemplate(CK_ATTRIBUTE_TYPE type, 
+			CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+    CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(type,
+					ptemplate, len);
+
+    if (attr && attr->pValue && attr->ulValueLen == SDB_ULONG_SIZE) {
+	return sftk_SDBULong2ULong(attr->pValue);
+    }
+    return (CK_ULONG)-1;
+}
+
+/*
+ * we need to find a unique CKA_ID.
+ *  The basic idea is to just increment the lowest byte.
+ *  This code also handles the following corner cases:
+ *   1) the single byte overflows. On overflow we increment the next byte up 
+ *    and so forth until we have overflowed the entire CKA_ID.
+ *   2) If we overflow the entire CKA_ID we expand it by one byte.
+ *   3) the CKA_ID is non-existant, we create a new one with one byte.
+ *    This means no matter what CKA_ID is passed, the result of this function 
+ *    is always a new CKA_ID, and this function will never return the same 
+ *    CKA_ID the it has returned in the passed.
+ */
+static CK_RV
+sftkdb_incrementCKAID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
+{
+    unsigned char *buf = ptemplate->pValue;
+    CK_ULONG len = ptemplate->ulValueLen;
+
+    if (buf == NULL || len == (CK_ULONG)-1) {
+	/* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
+	len = 0;
+    } else {
+	CK_ULONG i;
+
+	/* walk from the back to front, incrementing
+	 * the CKA_ID until we no longer have a carry,
+	 * or have hit the front of the id. */
+	for (i=len; i != 0; i--) {
+	    buf[i-1]++;
+	    if (buf[i-1] != 0) {
+		/* no more carries, the increment is complete */
+		return CKR_OK;
+	     }
+	}
+	/* we've now overflowed, fall through and expand the CKA_ID by 
+	 * one byte */
+    } 
+    buf = PORT_ArenaAlloc(arena, len+1);
+    if (!buf) {
+	return CKR_HOST_MEMORY;
+    }
+    if (len > 0) {
+	 PORT_Memcpy(buf, ptemplate->pValue, len);
+    }
+    buf[len] = 0;
+    ptemplate->pValue = buf;
+    ptemplate->ulValueLen = len+1;
+    return CKR_OK;
+}
+
+/*
+ * drop an attribute from a template.
+ */
+void
+sftkdb_dropAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE *ptemplate, 
+			CK_ULONG *plen)
+{
+   CK_ULONG count = *plen;
+   CK_ULONG i;
+
+   for (i=0; i < count; i++) {
+	if (attr->type == ptemplate[i].type) {
+	    break;
+	}
+   }
+
+   if (i == count) {
+	/* attribute not found */
+	return;
+   }
+
+   /* copy the remaining attributes up */
+   for ( i++; i < count; i++) {
+	ptemplate[i-1] = ptemplate[i];
+   }
+
+   /* decrement the template size */
+   *plen = count -1;
+}
+
+/*
+ * create some defines for the following functions to document the meaning
+ * of true/false. (make's it easier to remember what means what.
+ */
+typedef enum {
+	SFTKDB_DO_NOTHING = 0,
+	SFTKDB_ADD_OBJECT,
+	SFTKDB_MODIFY_OBJECT,
+	SFTKDB_DROP_ATTRIBUTE
+} sftkdbUpdateStatus;
+
+/*
+ * helper function to reconcile a single trust entry.
+ *   Identify which trust entry we want to keep.
+ *   If we don't need to do anything (the records are already equal).
+ *       return SFTKDB_DO_NOTHING.
+ *   If we want to use the source version,
+ *       return SFTKDB_MODIFY_OBJECT
+ *   If we want to use the target version,
+ *       return SFTKDB_DROP_ATTRIBUTE
+ *
+ *   In the end the caller will remove any attributes in the source
+ *   template when SFTKDB_DROP_ATTRIBUTE is specified, then use do a 
+ *   set attributes with that template on the target if we received 
+ *   any SFTKDB_MODIFY_OBJECT returns.
+ */
+sftkdbUpdateStatus
+sftkdb_reconcileTrustEntry(PLArenaPool *arena, CK_ATTRIBUTE *target,
+			   CK_ATTRIBUTE *source)
+{
+    CK_ULONG targetTrust = sftkdb_getULongFromTemplate(target->type,
+			target, 1);
+    CK_ULONG sourceTrust = sftkdb_getULongFromTemplate(target->type,
+			source, 1);
+
+    /*
+     * try to pick the best solution between the source and the
+     * target. Update the source template if we want the target value
+     * to win out. Prefer cases where we don't actually update the
+     * trust entry.
+     */
+
+    /* they are the same, everything is already kosher */
+    if (targetTrust == sourceTrust) {
+	return SFTKDB_DO_NOTHING;
+    }
+
+    /* handle the case where the source Trust attribute may be a bit
+     * flakey */
+    if (sourceTrust == (CK_ULONG)-1) {
+	/*
+	 * The source Trust is invalid. We know that the target Trust
+	 * must be valid here, otherwise the above 
+	 * targetTrust == sourceTrust check would have succeeded.
+	 */
+	return SFTKDB_DROP_ATTRIBUTE;
+    }
+
+    /* target is invalid, use the source's idea of the trust value */
+    if (targetTrust == (CK_ULONG)-1) {
+	/* overwriting the target in this case is OK */
+	return SFTKDB_MODIFY_OBJECT;
+    }
+
+    /* at this point we know that both attributes exist and have the
+     * appropriate length (SDB_ULONG_SIZE). We no longer need to check
+     * ulValueLen for either attribute.
+     */
+    if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
+	return SFTKDB_DROP_ATTRIBUTE;
+    }
+
+    /* target has no idea, use the source's idea of the trust value */
+    if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
+	/* overwriting the target in this case is OK */
+	return SFTKDB_MODIFY_OBJECT;
+    }
+
+    /* so both the target and the source have some idea of what this 
+     * trust attribute should be, and neither agree exactly. 
+     * At this point, we prefer 'hard' attributes over 'soft' ones. 
+     * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
+     * CKT_NSS_NOT_TRUTED. Soft ones are ones which don't change the
+     * actual trust of the cert (CKT_MUST_VERIFY_TRUST, 
+     * CKT_NSS_VALID_DELEGATOR).
+     */
+    if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST) 
+	|| (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
+	return SFTKDB_DROP_ATTRIBUTE;
+    }
+    if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST) 
+	|| (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
+	/* again, overwriting the target in this case is OK */
+	return SFTKDB_MODIFY_OBJECT;
+    }
+
+    /* both have hard attributes, we have a conflict, let the target win. */
+    return SFTKDB_DROP_ATTRIBUTE;
+}
+
+const CK_ATTRIBUTE_TYPE sftkdb_trustList[] =
+	{ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
+	  CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
+	  CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
+	  CKA_TRUST_TIME_STAMPING };
+
+#define SFTK_TRUST_TEMPLATE_COUNT \
+		(sizeof(sftkdb_trustList)/sizeof(sftkdb_trustList[0]))
+/*
+ * Run through the list of known trust types, and reconcile each trust
+ * entry one by one. Keep track of we really need to write out the source
+ * trust object (overwriting the existing one).
+ */
+static sftkdbUpdateStatus
+sftkdb_reconcileTrust(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
+		      CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
+{
+    CK_ATTRIBUTE trustTemplate[SFTK_TRUST_TEMPLATE_COUNT];
+    unsigned char trustData[SFTK_TRUST_TEMPLATE_COUNT*SDB_ULONG_SIZE];
+    sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
+    CK_ULONG i;
+    CK_RV crv;
+
+
+    for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT;  i++) {
+	trustTemplate[i].type = sftkdb_trustList[i];
+	trustTemplate[i].pValue = &trustData[i*SDB_ULONG_SIZE];
+	trustTemplate[i].ulValueLen = SDB_ULONG_SIZE;
+    }
+    crv = (*db->sdb_GetAttributeValue)(db, id, 
+				trustTemplate, SFTK_TRUST_TEMPLATE_COUNT);
+    if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
+	/* target trust has some problems, update it */
+	update = SFTKDB_MODIFY_OBJECT;
+	goto done;
+    }
+
+    for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) {
+	CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(
+			trustTemplate[i].type, ptemplate, *plen);
+	sftkdbUpdateStatus status;
+
+
+	/* if target trust value doesn't exist, nothing to merge */
+	if (trustTemplate[i].ulValueLen == (CK_ULONG)-1) {
+	    /* if the source exists, then we want the source entry,
+	     * go ahead and update */
+	    if (attr && attr->ulValueLen != (CK_ULONG)-1) {
+		update = SFTKDB_MODIFY_OBJECT;
+	    }
+	    continue;
+	}
+
+	/*
+	 * the source doesn't have the attribute, go to the next attribute
+	 */
+	if (attr == NULL) {
+	    continue;
+		
+	}
+	status = sftkdb_reconcileTrustEntry(arena, &trustTemplate[i], attr);
+	if (status == SFTKDB_MODIFY_OBJECT) {
+	    update = SFTKDB_MODIFY_OBJECT;
+	} else if (status == SFTKDB_DROP_ATTRIBUTE) {
+	    /* drop the source copy of the attribute, we are going with
+	     * the target's version */
+	    sftkdb_dropAttribute(attr, ptemplate, plen);
+	}
+    }
+
+    /* finally manage stepup */
+    if (update == SFTKDB_MODIFY_OBJECT) {
+	CK_BBOOL stepUpBool = CK_FALSE;
+	/* if we are going to write from the source, make sure we don't
+	 * overwrite the stepup bit if it's on*/
+	trustTemplate[0].type = CKA_TRUST_STEP_UP_APPROVED;
+	trustTemplate[0].pValue = &stepUpBool;
+	trustTemplate[0].ulValueLen = sizeof(stepUpBool);
+    	crv = (*db->sdb_GetAttributeValue)(db, id, trustTemplate, 1);
+	if ((crv == CKR_OK) && (stepUpBool == CK_TRUE)) {
+	    sftkdb_dropAttribute(trustTemplate, ptemplate, plen);
+	}
+    } else {
+	/* we currently aren't going to update. If the source stepup bit is
+	 * on however, do an update so the target gets it as well */
+	CK_ATTRIBUTE *attr;
+
+	attr = sftkdb_getAttributeFromTemplate(CKA_TRUST_STEP_UP_APPROVED,
+			ptemplate, *plen);
+	if (attr && (attr->ulValueLen == sizeof(CK_BBOOL)) &&  
+			(*(CK_BBOOL *)(attr->pValue) == CK_TRUE)) {
+		update = SFTKDB_MODIFY_OBJECT;
+	}
+    }
+    
+done:
+    return update;
+}
+
+static sftkdbUpdateStatus
+sftkdb_handleIDAndName(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
+		      CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
+{
+    sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
+    CK_ATTRIBUTE *attr1, *attr2;
+    CK_ATTRIBUTE ttemplate[2] = {
+	{CKA_ID, NULL, 0},
+	{CKA_LABEL, NULL, 0}
+    };
+    CK_RV crv;
+
+    attr1 = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
+    attr2 = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, *plen);
+
+    /* if the source has neither an id nor label, don't bother updating */
+    if ( (!attr1 || attr1->ulValueLen == 0) &&
+	 (! attr2 ||  attr2->ulValueLen == 0) ) {
+	return SFTKDB_DO_NOTHING;
+    }
+
+    /* the source has either an id or a label, see what the target has */
+    crv = (*db->sdb_GetAttributeValue)(db, id, ttemplate, 2);
+
+    /* if the target has neither, update from the source */
+    if ( ((ttemplate[0].ulValueLen == 0) || 
+	  (ttemplate[0].ulValueLen == (CK_ULONG)-1))  &&
+         ((ttemplate[1].ulValueLen == 0) || 
+	  (ttemplate[1].ulValueLen == (CK_ULONG)-1)) ) {
+	return SFTKDB_MODIFY_OBJECT;
+    }
+
+    /* check the CKA_ID */
+    if ((ttemplate[0].ulValueLen != 0) && 
+	(ttemplate[0].ulValueLen != (CK_ULONG)-1)) {
+	/* we have a CKA_ID in the target, don't overwrite
+	 * the target with an empty CKA_ID from the source*/
+	if (attr1 && attr1->ulValueLen == 0) {
+	    sftkdb_dropAttribute(attr1, ptemplate, plen);
+	}
+    } else if (attr1 && attr1->ulValueLen != 0) {
+	/* source has a CKA_ID, but the target doesn't, update the target */
+	update = SFTKDB_MODIFY_OBJECT;
+    }
+
+
+    /* check the nickname */
+    if ((ttemplate[1].ulValueLen != 0) && 
+	(ttemplate[1].ulValueLen != (CK_ULONG)-1)) {
+
+	/* we have a nickname in the target, and we don't have to update
+	 * the CKA_ID. We are done. NOTE: if we add addition attributes
+	 * in this check, this shortcut can only go on the last of them. */
+	if (update == SFTKDB_DO_NOTHING) {
+	    return update;
+	}
+	/* we have a nickname in the target, don't overwrite
+	 * the target with an empty nickname from the source */
+	if (attr2 && attr2->ulValueLen == 0) {
+	    sftkdb_dropAttribute(attr2, ptemplate, plen);
+	}
+    } else if (attr2 && attr2->ulValueLen != 0) {
+	/* source has a nickname, but the target doesn't, update the target */
+	update = SFTKDB_MODIFY_OBJECT;
+    }
+
+    return update;
+}
+
+
+		
+/*
+ * This function updates the template before we write the object out.
+ *
+ * If we are going to skip updating this object, return PR_FALSE.
+ * If it should be updated we return PR_TRUE.
+ * To help readability, these have been defined 
+ * as SFTK_DONT_UPDATE and SFTK_UPDATE respectively.
+ */
+static PRBool
+sftkdb_updateObjectTemplate(PLArenaPool *arena, SDB *db,
+		    CK_OBJECT_CLASS objectType, 
+		    CK_ATTRIBUTE *ptemplate, CK_ULONG *plen,
+		    CK_OBJECT_HANDLE *targetID)
+{
+    PRBool done; /* should we repeat the loop? */
+    CK_OBJECT_HANDLE id;
+    CK_RV crv = CKR_OK;
+
+    do {
+ 	crv = sftkdb_checkConflicts(db, objectType, ptemplate, 
+						*plen, CK_INVALID_HANDLE);
+	if (crv != CKR_ATTRIBUTE_VALUE_INVALID) {
+	    break;
+	}
+	crv = sftkdb_resolveConflicts(arena, objectType, ptemplate, plen);
+    } while (crv == CKR_OK);
+
+    if (crv != CKR_OK) {
+	return SFTKDB_DO_NOTHING;
+    }
+
+    do {
+	done = PR_TRUE;
+	crv = sftkdb_lookupObject(db, objectType, &id, ptemplate, *plen);
+	if (crv != CKR_OK) {
+	    return SFTKDB_DO_NOTHING;
+	}
+
+	/* This object already exists, merge it, don't update */
+	if (id != CK_INVALID_HANDLE) {
+    	    CK_ATTRIBUTE *attr = NULL;
+	    /* special post processing for attributes */
+	    switch (objectType) {
+	    case CKO_CERTIFICATE:
+	    case CKO_PUBLIC_KEY:
+	    case CKO_PRIVATE_KEY:
+		/* update target's CKA_ID and labels if they don't already 
+		 * exist */
+		*targetID = id;
+		return sftkdb_handleIDAndName(arena, db, id, ptemplate, plen);
+	    case CKO_NSS_TRUST:
+		/* if we have conflicting trust object types,
+		 * we need to reconcile them */
+		*targetID = id;
+		return sftkdb_reconcileTrust(arena, db, id, ptemplate, plen);
+	    case CKO_SECRET_KEY:
+		/* secret keys in the old database are all sdr keys, 
+		 * unfortunately they all appear to have the same CKA_ID, 
+		 * even though they are truly different keys, so we always 
+		 * want to update these keys, but we need to 
+		 * give them a new CKA_ID */
+		/* NOTE: this changes ptemplate */
+		attr = sftkdb_getAttributeFromTemplate(CKA_ID,ptemplate,*plen);
+		crv = attr ? sftkdb_incrementCKAID(arena, attr) 
+		           : CKR_HOST_MEMORY; 
+		/* in the extremely rare event that we needed memory and
+		 * couldn't get it, just drop the key */
+		if (crv != CKR_OK) {
+		    return SFTKDB_DO_NOTHING;
+		}
+		done = PR_FALSE; /* repeat this find loop */
+		break;
+	    default:
+		/* for all other objects, if we found the equivalent object,
+		 * don't update it */
+	        return SFTKDB_DO_NOTHING;
+	    }
+	}
+    } while (!done);
+
+    /* this object doesn't exist, update it */
+    return SFTKDB_ADD_OBJECT;
+}
+
+
+#define MAX_ATTRIBUTES 500
+static CK_RV
+sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, 
+		   SECItem *key)
+{
+    CK_ATTRIBUTE template[MAX_ATTRIBUTES];
+    CK_ATTRIBUTE *ptemplate;
+    CK_ULONG max_attributes = MAX_ATTRIBUTES;
+    CK_OBJECT_CLASS objectType;
+    SDB *source = handle->update;
+    SDB *target = handle->db;
+    int i;
+    CK_RV crv;
+    PLArenaPool *arena = NULL;
+
+    arena = PORT_NewArena(256);
+    if (arena ==  NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    ptemplate = &template[0];
+    id &= SFTK_OBJ_ID_MASK;
+    crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes);
+    if (crv == CKR_BUFFER_TOO_SMALL) {
+	ptemplate = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, max_attributes);
+	if (ptemplate == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	} else {
+            crv = sftkdb_GetObjectTemplate(source, id, 
+					   ptemplate, &max_attributes);
+	}
+    }
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+
+    for (i=0; i < max_attributes; i++) {
+	ptemplate[i].pValue = PORT_ArenaAlloc(arena,ptemplate[i].ulValueLen);
+	if (ptemplate[i].pValue == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    goto loser;
+	}
+    }
+    crv = (*source->sdb_GetAttributeValue)(source, id, 
+					   ptemplate, max_attributes);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+
+    objectType = sftkdb_getULongFromTemplate(CKA_CLASS, ptemplate,
+							 max_attributes);
+
+    /*
+     * Update Object updates the object template if necessary then returns 
+     * whether or not we need to actually write the object out to our target 
+     * database.
+     */
+    if (!handle->updateID) {
+	    crv = sftkdb_CreateObject(arena, handle, target, &id, 
+				ptemplate, max_attributes);
+    } else {
+	sftkdbUpdateStatus update_status;
+	update_status  = sftkdb_updateObjectTemplate(arena, target, 
+			objectType, ptemplate, &max_attributes, &id);
+	switch (update_status) {
+	case SFTKDB_ADD_OBJECT:
+	    crv = sftkdb_CreateObject(arena, handle, target, &id, 
+				ptemplate, max_attributes);
+	    break;
+	case SFTKDB_MODIFY_OBJECT:
+    	    crv = sftkdb_setAttributeValue(arena, handle, target, 
+				   id, ptemplate, max_attributes);
+	    break;
+	case SFTKDB_DO_NOTHING:
+	case SFTKDB_DROP_ATTRIBUTE:
+	    break;
+	}
+    } 
+
+loser:
+    if (arena) {
+	PORT_FreeArena(arena,PR_TRUE);
+    }
+    return crv;
+}
+	
+
+#define MAX_IDS 10
+/*
+ * update a new database from an old one, now that we have the key
+ */
+CK_RV
+sftkdb_Update(SFTKDBHandle *handle, SECItem *key)
+{
+    SDBFind *find = NULL;
+    CK_ULONG idCount = MAX_IDS;
+    CK_OBJECT_HANDLE ids[MAX_IDS];
+    SECItem *updatePasswordKey = NULL;
+    CK_RV crv, crv2;
+    PRBool inTransaction = PR_FALSE;
+    int i;
+
+    if (handle == NULL) {
+	return CKR_OK;
+    }
+    if (handle->update == NULL) {
+	return CKR_OK;
+    }
+
+    /*
+     * put the whole update under a transaction. This allows us to handle
+     * any possible race conditions between with the updateID check.
+     */
+    crv = (*handle->db->sdb_Begin)(handle->db);
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    inTransaction = PR_TRUE;
+    
+    /* some one else has already updated this db */
+    if (sftkdb_hasUpdate(sftkdb_TypeString(handle), 
+			 handle->db, handle->updateID)) {
+	crv = CKR_OK;
+	goto done;
+    }
+
+    updatePasswordKey = sftkdb_GetUpdatePasswordKey(handle);
+    if (updatePasswordKey) {
+	/* pass the source DB key to the legacy code, 
+	 * so it can decrypt things */
+	handle->oldKey = updatePasswordKey;
+    }
+    
+    /* find all the objects */
+    crv = sftkdb_FindObjectsInit(handle, NULL, 0, &find);
+
+    if (crv != CKR_OK) {
+	goto loser;
+    }
+    while ((crv == CKR_OK) && (idCount == MAX_IDS)) {
+	crv = sftkdb_FindObjects(handle, find, ids, MAX_IDS, &idCount);
+	for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
+	    crv = sftkdb_mergeObject(handle, ids[i], key);
+	}
+    }
+    crv2 = sftkdb_FindObjectsFinal(handle, find);
+    if (crv == CKR_OK) crv = crv2;
+
+loser:
+    /* no longer need the old key value */
+    handle->oldKey = NULL;
+
+    /* update the password - even if we didn't update objects */
+    if (handle->type == SFTK_KEYDB_TYPE) {
+	SECItem item1, item2;
+	unsigned char data1[SDB_MAX_META_DATA_LEN];
+	unsigned char data2[SDB_MAX_META_DATA_LEN];
+
+	item1.data = data1;
+ 	item1.len = sizeof(data1);
+	item2.data = data2;
+ 	item2.len = sizeof(data2);
+
+	/* if the target db already has a password, skip this. */
+	crv = (*handle->db->sdb_GetMetaData)(handle->db, "password",
+			&item1, &item2);
+	if (crv == CKR_OK) {
+	    goto done;
+	}
+
+
+	/* nope, update it from the source */
+	crv = (*handle->update->sdb_GetMetaData)(handle->update, "password",
+			&item1, &item2);
+	if (crv != CKR_OK) {
+	    goto done;
+	}
+	crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1,
+						&item2);
+	if (crv != CKR_OK) {
+	    goto done;
+	}
+    }
+
+done:
+    /* finally mark this up to date db up to date */
+    /* some one else has already updated this db */
+    if (crv == CKR_OK) {
+	crv = sftkdb_putUpdate(sftkdb_TypeString(handle), 
+				handle->db, handle->updateID);
+    }
+
+    if (inTransaction) {
+	if (crv == CKR_OK) {
+	    crv = (*handle->db->sdb_Commit)(handle->db);
+	} else {
+	    (*handle->db->sdb_Abort)(handle->db);
+	}
+    }
+    if (handle->update) {
+	(*handle->update->sdb_Close)(handle->update);
+	handle->update = NULL;
+    }
+    if (handle->updateID) {
+	PORT_Free(handle->updateID);
+	handle->updateID = NULL;
+    }
+    sftkdb_FreeUpdatePasswordKey(handle);
+    if (updatePasswordKey) {
+	SECITEM_ZfreeItem(updatePasswordKey, PR_TRUE);
+    }
+    handle->updateDBIsInit = PR_FALSE;
+    return crv;
+}
+
+/******************************************************************
+ * DB handle managing functions.
+ * 
+ * These functions are called by softoken to initialize, acquire,
+ * and release database handles.
+ */
+
+const char *
+sftkdb_GetUpdateID(SFTKDBHandle *handle)
+{
+    return handle->updateID;
+}
+
+/* release a database handle */
+void
+sftk_freeDB(SFTKDBHandle *handle)
+{
+    PRInt32 ref;
+
+    if (!handle) return;
+    ref = PR_ATOMIC_DECREMENT(&handle->ref);
+    if (ref == 0) {
+	sftkdb_CloseDB(handle);
+    }
+    return;
+}
+
+
+/*
+ * acquire a database handle for a certificate db  
+ * (database for public objects) 
+ */
+SFTKDBHandle *
+sftk_getCertDB(SFTKSlot *slot)
+{
+    SFTKDBHandle *dbHandle;
+
+    PZ_Lock(slot->slotLock);
+    dbHandle = slot->certDB;
+    if (dbHandle) {
+        PR_ATOMIC_INCREMENT(&dbHandle->ref);
+    }
+    PZ_Unlock(slot->slotLock);
+    return dbHandle;
+}
+
+/*
+ * acquire a database handle for a key database 
+ * (database for private objects)
+ */
+SFTKDBHandle *
+sftk_getKeyDB(SFTKSlot *slot)
+{
+    SFTKDBHandle *dbHandle;
+
+    SKIP_AFTER_FORK(PZ_Lock(slot->slotLock));
+    dbHandle = slot->keyDB;
+    if (dbHandle) {
+        PR_ATOMIC_INCREMENT(&dbHandle->ref);
+    }
+    SKIP_AFTER_FORK(PZ_Unlock(slot->slotLock));
+    return dbHandle;
+}
+
+/*
+ * acquire the database for a specific object. NOTE: objectID must point
+ * to a Token object!
+ */
+SFTKDBHandle *
+sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID)
+{
+    SFTKDBHandle *dbHandle;
+
+    PZ_Lock(slot->slotLock);
+    dbHandle = objectID & SFTK_KEYDB_TYPE ? slot->keyDB : slot->certDB;
+    if (dbHandle) {
+        PR_ATOMIC_INCREMENT(&dbHandle->ref);
+    }
+    PZ_Unlock(slot->slotLock);
+    return dbHandle;
+}
+
+/*
+ * initialize a new database handle
+ */
+static SFTKDBHandle *
+sftk_NewDBHandle(SDB *sdb, int type)
+{
+   SFTKDBHandle *handle = PORT_New(SFTKDBHandle);
+   handle->ref = 1;
+   handle->db = sdb;
+   handle->update = NULL;
+   handle->peerDB = NULL;
+   handle->newKey = NULL;
+   handle->oldKey = NULL;
+   handle->updatePasswordKey = NULL;
+   handle->updateID = NULL;
+   handle->type = type;
+   handle->passwordKey.data = NULL;
+   handle->passwordKey.len = 0;
+   handle->passwordLock = NULL;
+   if (type == SFTK_KEYDB_TYPE) {
+	handle->passwordLock = PZ_NewLock(nssILockAttribute);
+   }
+   sdb->app_private = handle;
+   return handle;
+}
+
+/*
+ * reset the key database to it's uninitialized state. This call
+ * will clear all the key entried.
+ */
+SECStatus
+sftkdb_ResetKeyDB(SFTKDBHandle *handle)
+{
+    CK_RV crv;
+
+    /* only rest the key db */
+    if (handle->type != SFTK_KEYDB_TYPE) {
+	return SECFailure;
+    }
+    crv = sftkdb_ResetDB(handle);
+    if (crv != CKR_OK) {
+	/* set error */
+	return SECFailure;
+    }
+    return SECSuccess;
+}
+
+static PRBool
+sftk_oldVersionExists(const char *dir, int version)
+{
+    int i;
+    PRStatus exists = PR_FAILURE;
+    char *file = NULL;
+
+    for (i=version; i > 1 ; i--) {
+	file = PR_smprintf("%s%d.db",dir,i);
+	if (file == NULL) {
+	    continue;
+	}
+	exists = PR_Access(file, PR_ACCESS_EXISTS);
+	PR_smprintf_free(file);
+	if (exists == PR_SUCCESS) {
+	    return PR_TRUE;
+	}
+    }
+    return PR_FALSE;
+}
+
+static PRBool
+sftk_hasLegacyDB(const char *confdir, const char *certPrefix, 
+		 const char *keyPrefix, int certVersion, int keyVersion)
+{
+    char *dir;
+    PRBool exists;
+
+    if (certPrefix == NULL) {
+	certPrefix = "";
+    }
+
+    if (keyPrefix == NULL) {
+	keyPrefix = "";
+    }
+
+    dir= PR_smprintf("%s/%scert", confdir, certPrefix);
+    if (dir == NULL) {
+	return PR_FALSE;
+    }
+
+    exists = sftk_oldVersionExists(dir, certVersion);
+    PR_smprintf_free(dir);
+    if (exists) {
+	return PR_TRUE;
+    }
+
+    dir= PR_smprintf("%s/%skey", confdir, keyPrefix);
+    if (dir == NULL) {
+	return PR_FALSE;
+    }
+
+    exists = sftk_oldVersionExists(dir, keyVersion);
+    PR_smprintf_free(dir);
+    return exists;
+}
+
+/*
+ * initialize certificate and key database handles as a pair.
+ *
+ * This function figures out what type of database we are opening and
+ * calls the appropriate low level function to open the database.
+ * It also figures out whether or not to setup up automatic update.
+ */
+CK_RV 
+sftk_DBInit(const char *configdir, const char *certPrefix,
+                const char *keyPrefix, const char *updatedir,
+		const char *updCertPrefix, const char *updKeyPrefix, 
+		const char *updateID, PRBool readOnly, PRBool noCertDB,
+                PRBool noKeyDB, PRBool forceOpen, PRBool isFIPS,
+                SFTKDBHandle **certDB, SFTKDBHandle **keyDB)
+{
+    const char *confdir;
+    NSSDBType dbType = NSS_DB_TYPE_NONE;
+    char *appName = NULL;
+    SDB *keySDB, *certSDB;
+    CK_RV crv = CKR_OK;
+    int flags = SDB_RDONLY;
+    PRBool newInit = PR_FALSE;
+    PRBool needUpdate = PR_FALSE;
+
+    if (!readOnly) {
+	flags = SDB_CREATE;
+    }
+
+    *certDB = NULL;
+    *keyDB = NULL;
+
+    if (noKeyDB && noCertDB) {
+	return CKR_OK;
+    }
+    confdir = _NSSUTIL_EvaluateConfigDir(configdir, &dbType, &appName);
+
+    /*
+     * now initialize the appropriate database
+     */
+    switch (dbType) {
+    case NSS_DB_TYPE_LEGACY:
+	crv = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
+		 isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
+	break;
+    case NSS_DB_TYPE_MULTIACCESS:
+	crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags,
+		isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
+	break;
+    case NSS_DB_TYPE_SQL:
+    case NSS_DB_TYPE_EXTERN: /* SHOULD open a loadable db */
+	crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags, 
+		noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit);
+
+        /*
+	 * if we failed to open the DB's read only, use the old ones if
+	 * the exists.
+	 */
+	if (crv != CKR_OK) {
+	    if ((flags == SDB_RDONLY)  &&
+	         sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
+	    /* we have legacy databases, if we failed to open the new format 
+	     * DB's read only, just use the legacy ones */
+		crv = sftkdbCall_open(confdir, certPrefix, 
+			keyPrefix, 8, 3, flags, isFIPS, 
+			noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB);
+	    }
+	/* Handle the database merge case.
+         *
+         * For the merge case, we need help from the application. Only
+         * the application knows where the old database is, and what unique
+         * identifier it has associated with it.
+         *
+         * If the client supplies these values, we use them to determine
+         * if we need to update.
+         */
+	} else if (
+	      /* both update params have been supplied */
+	      updatedir  && *updatedir && updateID  && *updateID
+	      /* old dbs exist? */
+	      && sftk_hasLegacyDB(updatedir, updCertPrefix, updKeyPrefix, 8, 3) 
+	      /* and they have not yet been updated? */
+	      && ((noKeyDB || !sftkdb_hasUpdate("key", keySDB, updateID)) 
+	      || (noCertDB || !sftkdb_hasUpdate("cert", certSDB, updateID)))) {
+	    /* we need to update */
+	    confdir = updatedir;
+	    certPrefix = updCertPrefix;
+	    keyPrefix = updKeyPrefix;
+	    needUpdate = PR_TRUE;
+	} else if (newInit) {
+	    /* if the new format DB was also a newly created DB, and we
+	     * succeeded, then need to update that new database with data
+	     * from the existing legacy DB */
+	    if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
+		needUpdate = PR_TRUE;
+	    }
+	}
+	break;
+    default:
+	crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST 
+				  * return one of the types we already 
+				  * specified. */
+    }
+    if (crv != CKR_OK) {
+	goto done;
+    }
+    if (!noCertDB) {
+	*certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE);
+    } else {
+	*certDB = NULL;
+    }
+    if (!noKeyDB) {
+	*keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE);
+    } else {
+	*keyDB = NULL;
+    }
+
+    /* link them together */
+    if (*certDB) {
+	(*certDB)->peerDB = *keyDB;
+    }
+    if (*keyDB) {
+	(*keyDB)->peerDB = *certDB;
+    }
+
+    /*
+     * if we need to update, open the legacy database and
+     * mark the handle as needing update.
+     */
+    if (needUpdate) {
+	SDB *updateCert = NULL;
+	SDB *updateKey = NULL;
+	CK_RV crv2;
+
+	crv2 = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
+		isFIPS, noCertDB ? NULL : &updateCert, 
+		noKeyDB ? NULL : &updateKey);
+	if (crv2 == CKR_OK) {
+	    if (*certDB) {
+		(*certDB)->update = updateCert;
+		(*certDB)->updateID = updateID && *updateID 
+				? PORT_Strdup(updateID) : NULL;
+		updateCert->app_private = (*certDB);
+	    }
+	    if (*keyDB) {
+		PRBool tokenRemoved = PR_FALSE;
+		(*keyDB)->update = updateKey;
+		(*keyDB)->updateID = updateID && *updateID ? 
+					PORT_Strdup(updateID) : NULL;
+		updateKey->app_private = (*keyDB);
+		(*keyDB)->updateDBIsInit = PR_TRUE;
+		(*keyDB)->updateDBIsInit = 
+			(sftkdb_HasPasswordSet(*keyDB) == SECSuccess) ?
+			 PR_TRUE : PR_FALSE;
+		/* if the password on the key db is NULL, kick off our update
+		 * chain of events */
+		sftkdb_CheckPassword((*keyDB), "", &tokenRemoved);
+	    } else {
+		/* we don't have a key DB, update the certificate DB now */
+		sftkdb_Update(*certDB, NULL);
+	    }
+	}
+    }
+done:
+    if (appName) {
+	PORT_Free(appName);
+    }
+   return forceOpen ? CKR_OK : crv;
+}
+
+CK_RV 
+sftkdb_Shutdown(void)
+{
+  s_shutdown();
+  sftkdbCall_Shutdown();
+  return CKR_OK;
+}
+
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)