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