diff options
Diffstat (limited to 'security/nss/lib/softoken/sftkdb.c')
-rw-r--r-- | security/nss/lib/softoken/sftkdb.c | 2737 |
1 files changed, 0 insertions, 2737 deletions
diff --git a/security/nss/lib/softoken/sftkdb.c b/security/nss/lib/softoken/sftkdb.c deleted file mode 100644 index 5495871ad..000000000 --- a/security/nss/lib/softoken/sftkdb.c +++ /dev/null @@ -1,2737 +0,0 @@ -/* 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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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; -} - |