/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * The following code handles the storage of PKCS 11 modules used by the * NSS. For the rest of NSS, only one kind of database handle exists: * * SFTKDBHandle * * There is one SFTKDBHandle for the each key database and one for each cert * database. These databases are opened as associated pairs, one pair per * slot. SFTKDBHandles are reference counted objects. * * Each SFTKDBHandle points to a low level database handle (SDB). This handle * represents the underlying physical database. These objects are not * reference counted, an are 'owned' by their respective SFTKDBHandles. * *
*/ #include"sftkdb.h" #include"sftkdbti.h" #include"pkcs11t.h" #include"pkcs11i.h" #include"sdb.h" #include"prprf.h" #include"pratom.h" #include"lgglue.h" #include"utilpars.h" #include"secerr.h" #include"softoken.h" #ifdefined(_WIN32) #include <windows.h> #endif
/* * We want all databases to have the same binary representation independent of * endianness or length of the host architecture. In general PKCS #11 attributes * are endian/length independent except those attributes that pass CK_ULONG. * * The following functions fixes up the CK_ULONG type attributes so that the data * base sees a machine independent view. CK_ULONGs are stored as 4 byte network * byte order values (big endian).
*/ #define BBP 8
PRBool
sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type)
{ switch (type) { case CKA_CERTIFICATE_CATEGORY: case CKA_CERTIFICATE_TYPE: case CKA_CLASS: case CKA_JAVA_MIDP_SECURITY_DOMAIN: case CKA_KEY_GEN_MECHANISM: case CKA_KEY_TYPE: case CKA_MECHANISM_TYPE: case CKA_MODULUS_BITS: case CKA_PRIME_BITS: case CKA_SUBPRIME_BITS: case CKA_VALUE_BITS: case CKA_VALUE_LEN:
case CKA_TRUST_DIGITAL_SIGNATURE: case CKA_TRUST_NON_REPUDIATION: case CKA_TRUST_KEY_ENCIPHERMENT: case CKA_TRUST_DATA_ENCIPHERMENT: case CKA_TRUST_KEY_AGREEMENT: case CKA_TRUST_KEY_CERT_SIGN: case CKA_TRUST_CRL_SIGN:
case CKA_TRUST_SERVER_AUTH: case CKA_TRUST_CLIENT_AUTH: case CKA_TRUST_CODE_SIGNING: case CKA_TRUST_EMAIL_PROTECTION: case CKA_TRUST_IPSEC_END_SYSTEM: case CKA_TRUST_IPSEC_TUNNEL: case CKA_TRUST_IPSEC_USER: case CKA_TRUST_TIME_STAMPING: case CKA_TRUST_STEP_UP_APPROVED: return PR_TRUE; default: break;
} return PR_FALSE;
}
/* are the attributes private? */ static PRBool
sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type)
{ switch (type) { case CKA_VALUE: case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: return PR_TRUE; default: break;
} return PR_FALSE;
}
/* These attributes must be authenticated with an hmac. */ static PRBool
sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type)
{ switch (type) { case CKA_MODULUS: case CKA_PUBLIC_EXPONENT: case CKA_CERT_SHA1_HASH: case CKA_CERT_MD5_HASH: case CKA_TRUST_SERVER_AUTH: case CKA_TRUST_CLIENT_AUTH: case CKA_TRUST_EMAIL_PROTECTION: case CKA_TRUST_CODE_SIGNING: case CKA_TRUST_STEP_UP_APPROVED: case CKA_NSS_OVERRIDE_EXTENSIONS: return PR_TRUE; default: break;
} return PR_FALSE;
} /* * convert a native ULONG to a database ulong. Database ulong's * are all 4 byte big endian values.
*/ void
sftk_ULong2SDBULong(unsignedchar *data, CK_ULONG value)
{ int i;
for (i = 0; i < SDB_ULONG_SIZE; i++) {
data[i] = (value >> (SDB_ULONG_SIZE - 1 - i) * BBP) & 0xff;
}
}
/* * convert a database ulong back to a native ULONG. (reverse of the above * function).
*/ static CK_ULONG
sftk_SDBULong2ULong(unsignedchar *data)
{ int i;
CK_ULONG value = 0;
for (i = 0; i < SDB_ULONG_SIZE; i++) {
value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE - 1 - i) * BBP);
} return value;
}
/* certain trust records are default values, which are the values * returned if the signature check fails anyway.
* In those cases, we can skip the signature check. */
PRBool
sftkdb_isNullTrust(const CK_ATTRIBUTE *template)
{ switch (template->type) { case CKA_TRUST_SERVER_AUTH: case CKA_TRUST_CLIENT_AUTH: case CKA_TRUST_EMAIL_PROTECTION: case CKA_TRUST_CODE_SIGNING: if (template->ulValueLen != SDB_ULONG_SIZE) { break;
} if (sftk_SDBULong2ULong(template->pValue) ==
CKT_NSS_TRUST_UNKNOWN) { return PR_TRUE;
} break; case CKA_TRUST_STEP_UP_APPROVED: if (template->ulValueLen != 1) { break;
} if (*((unsignedchar *)(template->pValue)) == 0) { return PR_TRUE;
} break; default: break;
} return PR_FALSE;
}
/* * fix up the input templates. Our fixed up ints are stored in data and must * be freed by the caller. The new template must also be freed. If there are no * CK_ULONG attributes, the orignal template is passed in as is.
*/ static CK_ATTRIBUTE *
sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, unsignedchar **dataOut, int *dataOutSize)
{ int i; int ulongCount = 0; unsignedchar *data;
CK_ATTRIBUTE *ntemplate;
*dataOut = NULL;
*dataOutSize = 0;
/* first count the number of CK_ULONG attributes */ for (i = 0; i < count; i++) { /* Don't 'fixup' NULL values */ if (!template[i].pValue) { continue;
} if (template[i].ulValueLen == sizeof(CK_ULONG)) { if (sftkdb_isULONGAttribute(template[i].type)) {
ulongCount++;
}
}
} /* no attributes to fixup, just call on through */ if (ulongCount == 0) { return (CK_ATTRIBUTE *)template;
}
/* allocate space for new ULONGS */
data = (unsignedchar *)PORT_Alloc(SDB_ULONG_SIZE * ulongCount); if (!data) { return NULL;
}
/* allocate new template */
ntemplate = PORT_NewArray(CK_ATTRIBUTE, count); if (!ntemplate) {
PORT_Free(data); return NULL;
}
*dataOut = data;
*dataOutSize = SDB_ULONG_SIZE * ulongCount; /* copy the old template, fixup the actual ulongs */ for (i = 0; i < count; i++) {
ntemplate[i] = template[i]; /* Don't 'fixup' NULL values */ if (!template[i].pValue) { continue;
} if (template[i].ulValueLen == sizeof(CK_ULONG)) { if (sftkdb_isULONGAttribute(template[i].type)) {
CK_ULONG value = *(CK_ULONG *)template[i].pValue;
sftk_ULong2SDBULong(data, value);
ntemplate[i].pValue = data;
ntemplate[i].ulValueLen = SDB_ULONG_SIZE;
data += SDB_ULONG_SIZE;
}
}
} return ntemplate;
}
/* * return a string describing the database type (key or cert)
*/ constchar *
sftkdb_TypeString(SFTKDBHandle *handle)
{ return (handle->type == SFTK_KEYDB_TYPE) ? "key" : "cert";
}
/* * Some attributes are signed with an Hmac and a pbe key generated from * the password. This signature is stored indexed by object handle and * attribute type in the meta data table in the key database. * * Signature entries are indexed by the string * sig_[cert/key]_{ObjectID}_{Attribute} * * This function fetches that pkcs5 signature. Caller supplies a SECItem * pre-allocated to the appropriate size if the SECItem is too small the * function will fail with CKR_BUFFER_TOO_SMALL.
*/ static CK_RV
sftkdb_getRawAttributeSignature(SFTKDBHandle *handle, SDB *db,
CK_OBJECT_HANDLE objectID,
CK_ATTRIBUTE_TYPE type,
SECItem *signText)
{ char id[30];
CK_RV crv;
/* * Some attributes are signed with an Hmac and a pbe key generated from * the password. This signature is stored indexed by object handle and * attribute type in the meta data table in the key database. * * Signature entries are indexed by the string * sig_[cert/key]_{ObjectID}_{Attribute} * * This function stores that pkcs5 signature.
*/
CK_RV
sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget,
CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
SECItem *signText)
{ char id[30];
CK_RV crv;
/* if no data was retrieved, no need to process encrypted or signed
* attributes */ if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) { continue;
}
/* fixup private attributes */ if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) { /* we have a private attribute */ /* This code depends on the fact that the cipherText is bigger
* than the plain text */
SECItem cipherText;
SECItem *plainText;
SECStatus rv;
/* copy the plain text back into the template */
PORT_Memcpy(template[i].pValue, plainText->data, plainText->len); template[i].ulValueLen = plainText->len;
SECITEM_ZfreeItem(plainText, PR_TRUE);
} /* make sure signed attributes are valid */ if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type) && !sftkdb_isNullTrust(&ntemplate[i])) {
SECStatus rv;
CK_RV local_crv;
SECItem signText;
SECItem plainText; unsignedchar signData[SDB_MAX_META_DATA_LEN];
/* Use a local variable so that we don't clobber any already * set error. This function returns either CKR_OK or the last
* found error in the template */
local_crv = sftkdb_GetAttributeSignature(handle, keyHandle,
objectID,
ntemplate[i].type,
&signText); if (local_crv != CKR_OK) {
PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); template[i].ulValueLen = -1;
crv = local_crv; continue;
}
/* * we do a second check holding the lock just in case the user * loggout while we were trying to get the signature.
*/
PZ_Lock(keyHandle->passwordLock); if (keyHandle->passwordKey.data == NULL) { /* if we are no longer logged in, no use checking the other
* Signatures either. */
checkSig = PR_FALSE;
PZ_Unlock(keyHandle->passwordLock); continue;
}
rv = sftkdb_VerifyAttribute(keyHandle,
&keyHandle->passwordKey,
objectID, ntemplate[i].type,
&plainText, &signText);
PZ_Unlock(keyHandle->passwordLock); if (rv != SECSuccess) {
PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); template[i].ulValueLen = -1;
crv = CKR_SIGNATURE_INVALID; /* better error code? */
} /* This Attribute is fine */
}
} return crv;
}
/* * Some attributes are signed with an HMAC and a pbe key generated from * the password. This signature is stored indexed by object handle and * * Those attributes are: * 1) Trust object hashes and trust values. * 2) public key values. * * Certs themselves are considered properly authenticated by virtue of their * signature, or their matching hash with the trust object. * * These signature is only checked for objects coming from shared databases. * Older dbm style databases have such no signature checks. HMACs are also * only checked when the token is logged in, as it requires a pbe generated * from the password. * * Tokens which have no key database (and therefore no master password) do not * have any stored signature values. Signature values are stored in the key * database, since the signature data is tightly coupled to the key database * password. * * This function takes a template of attributes that were either created or * modified. These attributes are checked to see if the need to be signed. * If they do, then this function signs the attributes and writes them * to the meta data store. * * This function can fail if there are attributes that must be signed, but * the token is not logged in. * * The caller is expected to abort any transaction he was in in the * event of a failure of this function.
*/ static CK_RV
sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle,
PRBool mayBeUpdateDB,
CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
CK_ULONG count)
{ unsignedint i;
CK_RV crv;
SFTKDBHandle *keyHandle = handle;
SDB *keyTarget = NULL;
PRBool usingPeerDB = PR_FALSE;
PRBool inPeerDBTransaction = PR_FALSE;
/* no key DB defined? then no need to sign anything */ if (keyHandle == NULL) {
crv = CKR_OK; goto loser;
}
/* When we are in a middle of an update, we have an update database set, * but we want to write to the real database. The bool mayBeUpdateDB is * set to TRUE if it's possible that we want to write an update database
* rather than a primary */
keyTarget = (mayBeUpdateDB && keyHandle->update) ? keyHandle->update : keyHandle->db;
/* skip the the database does not support meta data */ if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) {
crv = CKR_OK; goto loser;
}
/* If we had to switch databases, we need to initialize a transaction. */ if (usingPeerDB) {
crv = (*keyTarget->sdb_Begin)(keyTarget); if (crv != CKR_OK) { goto loser;
}
inPeerDBTransaction = PR_TRUE;
}
for (i = 0; i < count; i++) { if (sftkdb_isAuthenticatedAttribute(template[i].type)) {
SECStatus rv;
SECItem *signText;
SECItem plainText;
/* if we don't have a meta table, we didn't write any signature objects */ if ((db->sdb_flags & SDB_HAS_META) == 0) { return CKR_OK;
} for (i = 0; i < max_attributes; i++) {
CK_ATTRIBUTE *att = &ptemplate[i];
CK_ATTRIBUTE_TYPE type = att->type; if (sftkdb_isPrivateAttribute(type)) { /* move the signature from one object handle to another and delete
* the old entry */
SECItem signature; unsignedchar signData[SDB_MAX_META_DATA_LEN];
signature.data = signData;
signature.len = sizeof(signData);
crv = sftkdb_getRawAttributeSignature(handle, db, oldID, type,
&signature); if (crv != CKR_OK) { /* NOTE: if we ever change our default write from AES_CBC * to AES_KW, We'll need to change this to a continue as
* we won't need the integrity record for AES_KW */ break;
}
crv = sftkdb_PutAttributeSignature(handle, db, newID, type,
&signature); if (crv != CKR_OK) { break;
} /* now get rid of the old one */
crv = sftkdb_DestroyAttributeSignature(handle, db, oldID, type); if (crv != CKR_OK) { break;
}
}
} return crv;
}
if (*crv != CKR_OK) { return NULL;
} if (pcount) {
*pcount = count;
} returntemplate;
}
/* * return a pointer to the attribute in the give template. * The return value is not const, as the caller may modify * the given attribute value, but such modifications will * modify the actual value in the template.
*/ static CK_ATTRIBUTE *
sftkdb_getAttributeFromTemplate(CK_ATTRIBUTE_TYPE attribute,
CK_ATTRIBUTE *ptemplate, CK_ULONG len)
{
CK_ULONG i;
for (i = 0; i < len; i++) { if (attribute == ptemplate[i].type) { return &ptemplate[i];
}
} return NULL;
}
case CKO_PRIVATE_KEY: case CKO_PUBLIC_KEY: case CKO_SECRET_KEY:
attr = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, len); if (attr == NULL) { return CKR_TEMPLATE_INCOMPLETE;
} if (attr->ulValueLen == 0) { /* key is too generic to determine that it's unique, usually
* happens in the key gen case */ return CKR_OBJECT_HANDLE_INVALID;
}
if (crv == CKR_OBJECT_HANDLE_INVALID) { /* key is too generic to determine that it's unique, usually * happens in the key gen case, tell the caller to go ahead
* and just create it */ return CKR_OK;
} if (crv != CKR_OK) { return crv;
}
/* use the raw find, so we get the correct database */
crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find); if (crv != CKR_OK) { return crv;
}
(*db->sdb_FindObjects)(db, find, id, 1, &objCount);
(*db->sdb_FindObjectsFinal)(db, find);
/* * check to see if this template conflicts with others in our current database.
*/ static CK_RV
sftkdb_checkConflicts(SDB *db, CK_OBJECT_CLASS objectType, const CK_ATTRIBUTE *ptemplate, CK_ULONG len,
CK_OBJECT_HANDLE sourceID)
{
CK_ATTRIBUTE findTemplate[2]; unsignedchar objTypeData[SDB_ULONG_SIZE]; /* we may need to allocate some temporaries. Keep track of what was
* allocated so we can free it in the end */ unsignedchar *temp1 = NULL; unsignedchar *temp2 = NULL;
CK_ULONG objCount = 0;
SDBFind *find = NULL;
CK_OBJECT_HANDLE id; const CK_ATTRIBUTE *attr, *attr2;
CK_RV crv;
CK_ATTRIBUTE subject;
/* Currently the only conflict is with nicknames pointing to the same
* subject when creating or modifying a certificate. */ /* If the object is not a cert, no problem. */ if (objectType != CKO_CERTIFICATE) { return CKR_OK;
} /* if not setting a nickname then there's still no problem */
attr = sftkdb_getAttributeFromConstTemplate(CKA_LABEL, ptemplate, len); if ((attr == NULL) || (attr->ulValueLen == 0)) { return CKR_OK;
} /* fetch the subject of the source. For creation and merge, this should
* be found in the template */
attr2 = sftkdb_getAttributeFromConstTemplate(CKA_SUBJECT, ptemplate, len); if (sourceID == CK_INVALID_HANDLE) { if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen < 0)) {
crv = CKR_TEMPLATE_INCOMPLETE; goto done;
}
} elseif ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen <= 0)) { /* sourceID is set if we are trying to modify an existing entry instead * of creating a new one. In this case the subject may not be (probably
* isn't) in the template, we have to read it from the database */
subject.type = CKA_SUBJECT;
subject.pValue = NULL;
subject.ulValueLen = 0;
crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1); if (crv != CKR_OK) { goto done;
} if ((CK_LONG)subject.ulValueLen < 0) {
crv = CKR_DEVICE_ERROR; /* closest pkcs11 error to corrupted DB */ goto done;
}
temp1 = subject.pValue = PORT_Alloc(++subject.ulValueLen); if (temp1 == NULL) {
crv = CKR_HOST_MEMORY; goto done;
}
crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1); if (crv != CKR_OK) { goto done;
}
attr2 = &subject;
}
/* check for another cert in the database with the same nickname */
sftk_ULong2SDBULong(objTypeData, objectType);
findTemplate[0].type = CKA_CLASS;
findTemplate[0].pValue = objTypeData;
findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
findTemplate[1] = *attr;
/* object count == 0 means no conflicting certs found,
* go on with the operation */ if (objCount == 0) {
crv = CKR_OK; goto done;
}
/* There is a least one cert that shares the nickname, make sure it also
* matches the subject. */
findTemplate[0] = *attr2; /* we know how big the source subject was. Use that length to create the * space for the target. If it's not enough space, then it means the * source subject is too big, and therefore not a match. GetAttributeValue * will return CKR_BUFFER_TOO_SMALL. Otherwise it should be exactly enough
* space (or enough space to be able to compare the result. */
temp2 = findTemplate[0].pValue = PORT_Alloc(++findTemplate[0].ulValueLen); if (temp2 == NULL) {
crv = CKR_HOST_MEMORY; goto done;
}
crv = (*db->sdb_GetAttributeValue)(db, id, findTemplate, 1); if (crv != CKR_OK) { if (crv == CKR_BUFFER_TOO_SMALL) { /* if our buffer is too small, then the Subjects clearly do
* not match */
crv = CKR_ATTRIBUTE_VALUE_INVALID; goto loser;
} /* otherwise we couldn't get the value, just fail */ goto done;
}
/* Ok, we have both subjects, make sure they are the same.
* Compare the subjects */ if ((findTemplate[0].ulValueLen != attr2->ulValueLen) ||
(attr2->ulValueLen > 0 &&
PORT_Memcmp(findTemplate[0].pValue, attr2->pValue, attr2->ulValueLen) != 0)) {
crv = CKR_ATTRIBUTE_VALUE_INVALID; goto loser;
}
crv = CKR_OK;
done: /* If we've failed for some other reason than a conflict, make sure we * return an error code other than CKR_ATTRIBUTE_VALUE_INVALID. * (NOTE: neither sdb_FindObjectsInit nor sdb_GetAttributeValue should * return CKR_ATTRIBUTE_VALUE_INVALID, so the following is paranoia).
*/ if (crv == CKR_ATTRIBUTE_VALUE_INVALID) {
crv = CKR_GENERAL_ERROR; /* clearly a programming error */
}
/* exit point if we found a conflict */
loser:
PORT_Free(temp1);
PORT_Free(temp2); return crv;
}
/* * try to update the template to fix any errors. This is only done * during update. * * NOTE: we must update the template or return an error, or the update caller * will loop forever! * * Two copies of the source code for this algorithm exist in NSS. * Changes must be made in both copies. * The other copy is in pk11_IncrementNickname() in pk11wrap/pk11merge.c. *
*/ static CK_RV
sftkdb_resolveConflicts(PLArenaPool *arena, CK_OBJECT_CLASS objectType,
CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
{
CK_ATTRIBUTE *attr; char *nickname, *newNickname; unsignedint end, digit;
/* sanity checks. We should never get here with these errors */ if (objectType != CKO_CERTIFICATE) { return CKR_GENERAL_ERROR; /* shouldn't happen */
}
attr = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen); if ((attr == NULL) || (attr->ulValueLen == 0)) { return CKR_GENERAL_ERROR; /* shouldn't happen */
}
/* update the nickname */ /* is there a number at the end of the nickname already?
* if so just increment that number */
nickname = (char *)attr->pValue;
/* does nickname end with " #n*" ? */ for (end = attr->ulValueLen - 1;
end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0';
end--) /* just scan */
; if (attr->ulValueLen >= 3 &&
end < (attr->ulValueLen - 1) /* at least one digit */ &&
nickname[end] == '#' &&
nickname[end - 1] == ' ') { /* Already has a suitable suffix string */
} else { /* ... append " #2" to the name */ staticconstchar num2[] = " #2";
newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + sizeof(num2)); if (!newNickname) { return CKR_HOST_MEMORY;
}
PORT_Memcpy(newNickname, nickname, attr->ulValueLen);
PORT_Memcpy(&newNickname[attr->ulValueLen], num2, sizeof(num2));
attr->pValue = newNickname; /* modifies ptemplate */
attr->ulValueLen += 3; /* 3 is strlen(num2) */ return CKR_OK;
}
/* we overflowed, insert a new '1' for a carry in front of the number */
newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + 1); if (!newNickname) { return CKR_HOST_MEMORY;
} /* PORT_Memcpy should handle len of '0' */
PORT_Memcpy(newNickname, nickname, ++end);
newNickname[end] = '1';
PORT_Memset(&newNickname[end + 1], '0', attr->ulValueLen - end);
attr->pValue = newNickname;
attr->ulValueLen++; return CKR_OK;
}
/* * set an attribute and sign it if necessary
*/ static CK_RV
sftkdb_setAttributeValue(PLArenaPool *arena, SFTKDBHandle *handle,
SDB *db, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
CK_ULONG count)
{
CK_RV crv;
crv = (*db->sdb_SetAttributeValue)(db, objectID, template, count); if (crv != CKR_OK) { return crv;
}
crv = sftk_signTemplate(arena, handle, db == handle->update,
objectID, template, count); return crv;
}
/* * write a softoken object out to the database.
*/
CK_RV
sftkdb_write(SFTKDBHandle *handle, SFTKObject *object,
CK_OBJECT_HANDLE *objectID)
{
CK_ATTRIBUTE *template;
PLArenaPool *arena;
CK_ULONG count;
CK_RV crv;
SDB *db;
PRBool inTransaction = PR_FALSE;
CK_OBJECT_HANDLE id, candidateID;
*objectID = CK_INVALID_HANDLE;
if (handle == NULL) { return CKR_TOKEN_WRITE_PROTECTED;
}
db = SFTK_GET_SDB(handle);
/* * we have opened a new database, but we have not yet updated it. We are * still running pointing to the old database (so the application can * still read). We don't want to write to the old database at this point, * however, since it leads to user confusion. So at this point we simply * require a user login. Let NSS know this so it can prompt the user.
*/ if (db == handle->update) { return CKR_USER_NOT_LOGGED_IN;
}
/* * We want to make the base database as free from object specific knowledge * as possible. To maintain compatibility, keep some of the desirable * object specific semantics of the old database. * * These were 2 fold: * 1) there were certain conflicts (like trying to set the same nickname * on two different subjects) that would return an error. * 2) Importing the 'same' object would silently update that object. * * The following 2 functions mimic the desirable effects of these two * semantics without pushing any object knowledge to the underlying database * code.
*/
/* make sure we don't have attributes that conflict with the existing DB */
crv = sftkdb_checkConflicts(db, object->objclass, template, count,
CK_INVALID_HANDLE); if (crv != CKR_OK) { goto loser;
} /* Find any copies that match this particular object */
crv = sftkdb_lookupObject(db, object->objclass, &id, template, count); if (crv != CKR_OK) { goto loser;
} if (id == CK_INVALID_HANDLE) {
*objectID = candidateID;
crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count);
} else { /* object already exists, modify it's attributes */
*objectID = id; /* The object ID changed from our candidate, we need to move any
* signature attribute signatures to the new object ID. */
crv = sftkdb_fixupSignatures(handle, db, candidateID, id, template, count); if (crv != CKR_OK) { goto loser;
}
crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count);
} if (crv != CKR_OK) { goto loser;
}
crv = (*db->sdb_Commit)(db);
inTransaction = PR_FALSE;
loser: if (inTransaction) {
(*db->sdb_Abort)(db); /* It is trivial to show the following code cannot * happen unless something is horribly wrong with our compilier or
* hardware */
PORT_Assert(crv != CKR_OK); if (crv == CKR_OK)
crv = CKR_GENERAL_ERROR;
}
if (arena) {
PORT_FreeArena(arena, PR_TRUE);
} if (crv == CKR_OK) {
*objectID |= (handle->type | SFTK_TOKEN_TYPE);
} return crv;
}
if (handle == NULL) { return CKR_TOKEN_WRITE_PROTECTED;
}
db = SFTK_GET_SDB(handle); /* nothing to do */ if (count == 0) { return CKR_OK;
} /* * we have opened a new database, but we have not yet updated it. We are * still running pointing to the old database (so the application can * still read). We don't want to write to the old database at this point, * however, since it leads to user confusion. So at this point we simply * require a user login. Let NSS know this so it can prompt the user.
*/ if (db == handle->update) { return CKR_USER_NOT_LOGGED_IN;
}
/* make sure we don't have attributes that conflict with the existing DB */
crv = sftkdb_checkConflicts(db, object->objclass, ntemplate, count,
objectID); if (crv != CKR_OK) { goto loser;
}
/* * check to see if we have already updated this database. * a NULL updateID means we are trying to do an in place * single database update. In that case we have already * determined that an update was necessary.
*/ static PRBool
sftkdb_hasUpdate(constchar *typeString, SDB *db, constchar *updateID)
{ char *id;
CK_RV crv;
SECItem dummy = { 0, NULL, 0 }; unsignedchar dummyData[SDB_MAX_META_DATA_LEN];
if (!updateID) { return PR_FALSE;
}
id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID); if (id == NULL) { return PR_FALSE;
}
dummy.data = dummyData;
dummy.len = sizeof(dummyData);
/* * we just completed an update, store the update id * so we don't need to do it again. If non was given, * there is nothing to do.
*/ static CK_RV
sftkdb_putUpdate(constchar *typeString, SDB *db, constchar *updateID)
{ char *id;
CK_RV crv;
SECItem dummy = { 0, NULL, 0 };
/* if no id was given, nothing to do */ if (updateID == NULL) { return CKR_OK;
}
/* * get a ULong attribute from a template: * NOTE: this is a raw templated stored in database order!
*/ static CK_ULONG
sftkdb_getULongFromTemplate(CK_ATTRIBUTE_TYPE type,
CK_ATTRIBUTE *ptemplate, CK_ULONG len)
{
CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(type,
ptemplate, len);
/* * we need to find a unique CKA_ID. * The basic idea is to just increment the lowest byte. * This code also handles the following corner cases: * 1) the single byte overflows. On overflow we increment the next byte up * and so forth until we have overflowed the entire CKA_ID. * 2) If we overflow the entire CKA_ID we expand it by one byte. * 3) the CKA_ID is non-existant, we create a new one with one byte. * This means no matter what CKA_ID is passed, the result of this function * is always a new CKA_ID, and this function will never return the same * CKA_ID the it has returned in the passed.
*/ static CK_RV
sftkdb_incrementCKAID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
{ unsignedchar *buf = ptemplate->pValue;
CK_ULONG len = ptemplate->ulValueLen;
if (buf == NULL || len == (CK_ULONG)-1) { /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
len = 0;
} else {
CK_ULONG i;
/* walk from the back to front, incrementing * the CKA_ID until we no longer have a carry,
* or have hit the front of the id. */ for (i = len; i != 0; i--) {
buf[i - 1]++; if (buf[i - 1] != 0) { /* no more carries, the increment is complete */ return CKR_OK;
}
} /* we've now overflowed, fall through and expand the CKA_ID by
* one byte */
}
buf = PORT_ArenaAlloc(arena, len + 1); if (!buf) { return CKR_HOST_MEMORY;
} if (len > 0) {
PORT_Memcpy(buf, ptemplate->pValue, len);
}
buf[len] = 0;
ptemplate->pValue = buf;
ptemplate->ulValueLen = len + 1; return CKR_OK;
}
/* * drop an attribute from a template.
*/ void
sftkdb_dropAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE *ptemplate,
CK_ULONG *plen)
{
CK_ULONG count = *plen;
CK_ULONG i;
for (i = 0; i < count; i++) { if (attr->type == ptemplate[i].type) { break;
}
}
if (i == count) { /* attribute not found */ return;
}
/* copy the remaining attributes up */ for (i++; i < count; i++) {
ptemplate[i - 1] = ptemplate[i];
}
/* * create some defines for the following functions to document the meaning * of true/false. (make's it easier to remember what means what.
*/ typedefenum {
SFTKDB_DO_NOTHING = 0,
SFTKDB_ADD_OBJECT,
SFTKDB_MODIFY_OBJECT,
SFTKDB_DROP_ATTRIBUTE
} sftkdbUpdateStatus;
/* * helper function to reconcile a single trust entry. * Identify which trust entry we want to keep. * If we don't need to do anything (the records are already equal). * return SFTKDB_DO_NOTHING. * If we want to use the source version, * return SFTKDB_MODIFY_OBJECT * If we want to use the target version, * return SFTKDB_DROP_ATTRIBUTE * * In the end the caller will remove any attributes in the source * template when SFTKDB_DROP_ATTRIBUTE is specified, then use do a * set attributes with that template on the target if we received * any SFTKDB_MODIFY_OBJECT returns.
*/
sftkdbUpdateStatus
sftkdb_reconcileTrustEntry(PLArenaPool *arena, CK_ATTRIBUTE *target,
CK_ATTRIBUTE *source)
{
CK_ULONG targetTrust = sftkdb_getULongFromTemplate(target->type,
target, 1);
CK_ULONG sourceTrust = sftkdb_getULongFromTemplate(target->type,
source, 1);
/* * try to pick the best solution between the source and the * target. Update the source template if we want the target value * to win out. Prefer cases where we don't actually update the * trust entry.
*/
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.58 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.