raven-rhel6/nss-softokn/nss-softokn-3.67-read-write-db-locks.patch
2024-02-21 20:14:44 +06:00

589 lines
21 KiB
Diff

diff --git a/lib/softoken/lgglue.c b/lib/softoken/lgglue.c
--- a/lib/softoken/lgglue.c
+++ b/lib/softoken/lgglue.c
@@ -179,23 +179,23 @@ sftkdb_encrypt_stub(PLArenaPool *arena,
}
/* if we aren't the key handle, try the other handle */
if (handle->type != SFTK_KEYDB_TYPE) {
handle = handle->peerDB;
}
/* not a key handle */
- if (handle == NULL || handle->passwordLock == NULL) {
+ if (handle == NULL || !sftkdb_passwordLockIsInited(handle)) {
return SECFailure;
}
- PZ_Lock(handle->passwordLock);
+ sftkdb_passwordReaderLock(handle);
if (handle->passwordKey.data == NULL) {
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
/* PORT_SetError */
return SECFailure;
}
key = handle->newKey ? handle->newKey : &handle->passwordKey;
if (sftk_isLegacyIterationCountAllowed()) {
if (handle->newKey) {
iterationCount = handle->newDefaultIterationCount;
} else {
@@ -203,17 +203,17 @@ sftkdb_encrypt_stub(PLArenaPool *arena,
}
} else {
iterationCount = 1;
}
rv = sftkdb_EncryptAttribute(arena, handle, sdb, key, iterationCount,
CK_INVALID_HANDLE, CKT_INVALID_TYPE,
plainText, cipherText);
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
return rv;
}
/*
* stub files for legacy db's to be able to encrypt and decrypt
* various keys and attributes.
*/
@@ -230,31 +230,31 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *c
/* if we aren't the key handle, try the other handle */
oldKey = handle->oldKey;
if (handle->type != SFTK_KEYDB_TYPE) {
handle = handle->peerDB;
}
/* not a key handle */
- if (handle == NULL || handle->passwordLock == NULL) {
+ if (handle == NULL || !sftkdb_passwordLockIsInited(handle)) {
return SECFailure;
}
- PZ_Lock(handle->passwordLock);
+ sftkdb_passwordReaderLock(handle);
if (handle->passwordKey.data == NULL) {
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
/* PORT_SetError */
return SECFailure;
}
rv = sftkdb_DecryptAttribute(NULL, oldKey ? oldKey : &handle->passwordKey,
CK_INVALID_HANDLE,
CKT_INVALID_TYPE,
cipherText, plainText);
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
return rv;
}
static const char *LEGACY_LIB_NAME =
SHLIB_PREFIX "nssdbm" SHLIB_VERSION "." SHLIB_SUFFIX;
/*
* 2 bools to tell us if we've check the legacy library successfully or
diff --git a/lib/softoken/sftkdb.c b/lib/softoken/sftkdb.c
--- a/lib/softoken/sftkdb.c
+++ b/lib/softoken/sftkdb.c
@@ -376,29 +376,29 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te
/* This code depends on the fact that the cipherText is bigger
* than the plain text */
SECItem cipherText;
SECItem *plainText;
SECStatus rv;
cipherText.data = ntemplate[i].pValue;
cipherText.len = ntemplate[i].ulValueLen;
- PZ_Lock(handle->passwordLock);
+ sftkdb_passwordReaderLock(handle);
if (handle->passwordKey.data == NULL) {
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
template[i].ulValueLen = -1;
crv = CKR_USER_NOT_LOGGED_IN;
continue;
}
rv = sftkdb_DecryptAttribute(handle,
&handle->passwordKey,
objectID,
ntemplate[i].type,
&cipherText, &plainText);
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
if (rv != SECSuccess) {
PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
template[i].ulValueLen = -1;
crv = CKR_GENERAL_ERROR;
continue;
}
PORT_Assert(template[i].ulValueLen >= plainText->len);
if (template[i].ulValueLen < plainText->len) {
@@ -441,30 +441,30 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te
plainText.data = ntemplate[i].pValue;
plainText.len = ntemplate[i].ulValueLen;
/*
* we do a second check holding the lock just in case the user
* loggout while we were trying to get the signature.
*/
- PZ_Lock(keyHandle->passwordLock);
+ sftkdb_passwordReaderLock(keyHandle);
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);
+ sftkdb_passwordReaderUnlock(keyHandle);
continue;
}
rv = sftkdb_VerifyAttribute(keyHandle,
&keyHandle->passwordKey,
objectID, ntemplate[i].type,
&plainText, &signText);
- PZ_Unlock(keyHandle->passwordLock);
+ sftkdb_passwordReaderUnlock(keyHandle);
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 */
}
}
@@ -553,28 +553,28 @@ sftk_signTemplate(PLArenaPool *arena, SF
for (i = 0; i < count; i++) {
if (sftkdb_isAuthenticatedAttribute(template[i].type)) {
SECStatus rv;
SECItem *signText;
SECItem plainText;
plainText.data = template[i].pValue;
plainText.len = template[i].ulValueLen;
- PZ_Lock(keyHandle->passwordLock);
+ sftkdb_passwordReaderLock(keyHandle);
if (keyHandle->passwordKey.data == NULL) {
- PZ_Unlock(keyHandle->passwordLock);
+ sftkdb_passwordReaderUnlock(keyHandle);
crv = CKR_USER_NOT_LOGGED_IN;
goto loser;
}
rv = sftkdb_SignAttribute(arena, keyHandle, keyTarget,
&keyHandle->passwordKey,
keyHandle->defaultIterationCount,
objectID, template[i].type,
&plainText, &signText);
- PZ_Unlock(keyHandle->passwordLock);
+ sftkdb_passwordReaderUnlock(keyHandle);
if (rv != SECSuccess) {
crv = CKR_GENERAL_ERROR; /* better error code here? */
goto loser;
}
crv = sftkdb_PutAttributeSignature(handle, keyTarget, objectID,
template[i].type, signText);
if (crv != CKR_OK) {
goto loser;
@@ -733,29 +733,29 @@ sftk_ExtractTemplate(PLArenaPool *arena,
if (doEnc && sftkdb_isPrivateAttribute(tp->type)) {
/* we have a private attribute */
SECItem *cipherText;
SECItem plainText;
SECStatus rv;
plainText.data = tp->pValue;
plainText.len = tp->ulValueLen;
- PZ_Lock(handle->passwordLock);
+ sftkdb_passwordReaderLock(handle);
if (handle->passwordKey.data == NULL) {
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
*crv = CKR_USER_NOT_LOGGED_IN;
break;
}
rv = sftkdb_EncryptAttribute(arena, handle, db,
&handle->passwordKey,
handle->defaultIterationCount,
objectID,
tp->type,
&plainText, &cipherText);
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
if (rv == SECSuccess) {
tp->pValue = cipherText->data;
tp->ulValueLen = cipherText->len;
} else {
*crv = CKR_GENERAL_ERROR; /* better error code here? */
break;
}
PORT_Memset(plainText.data, 0, plainText.len);
@@ -1604,18 +1604,18 @@ sftkdb_CloseDB(SFTKDBHandle *handle)
if (handle->db->sdb_SetForkState) {
(*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize);
}
(*handle->db->sdb_Close)(handle->db);
}
if (handle->passwordKey.data) {
SECITEM_ZfreeItem(&handle->passwordKey, PR_FALSE);
}
- if (handle->passwordLock) {
- SKIP_AFTER_FORK(PZ_DestroyLock(handle->passwordLock));
+ if (sftkdb_passwordLockIsInited(handle)) {
+ SKIP_AFTER_FORK(sftkdb_passwordLockDestroy(handle));
}
if (handle->updatePasswordKey) {
SECITEM_ZfreeItem(handle->updatePasswordKey, PR_TRUE);
}
if (handle->updateID) {
PORT_Free(handle->updateID);
}
PORT_Free(handle);
@@ -2665,18 +2665,20 @@ sftk_NewDBHandle(SDB *sdb, int type, PRB
handle->oldKey = NULL;
handle->updatePasswordKey = NULL;
handle->updateID = NULL;
handle->type = type;
handle->usesLegacyStorage = legacy;
handle->passwordKey.data = NULL;
handle->passwordKey.len = 0;
handle->passwordLock = NULL;
+ handle->passwordWriterCond = NULL;
+ handle->passwordReaderCond = NULL;
if (type == SFTK_KEYDB_TYPE) {
- handle->passwordLock = PZ_NewLock(nssILockAttribute);
+ (void) sftkdb_passwordLockInit(handle);
}
sdb->app_private = handle;
return handle;
}
/*
* reset the key database to it's uninitialized state. This call
* will clear all the key entried.
diff --git a/lib/softoken/sftkdbti.h b/lib/softoken/sftkdbti.h
--- a/lib/softoken/sftkdbti.h
+++ b/lib/softoken/sftkdbti.h
@@ -1,29 +1,35 @@
/* 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/. */
#ifndef SFTKDBTI_H
#define SFTKDBTI_H 1
+#include <prcvar.h>
/*
* private defines
*/
struct SFTKDBHandleStr {
SDB *db;
PRInt32 ref;
CK_OBJECT_HANDLE type;
SECItem passwordKey;
int defaultIterationCount;
SECItem *newKey;
int newDefaultIterationCount;
SECItem *oldKey;
SECItem *updatePasswordKey;
PZLock *passwordLock;
+ PRCondVar *passwordWriterCond;
+ PRCondVar *passwordReaderCond;
+ PRBool passwordWriterActive;
+ int passwordWriters;
+ int passwordReaders;
SFTKDBHandle *peerDB;
SDB *update;
char *updateID;
PRBool updateDBIsInit;
PRBool usesLegacyStorage;
};
#define SFTK_KEYDB_TYPE 0x40000000
@@ -74,9 +80,18 @@ CK_RV sftkdb_GetAttributeSignature(SFTKD
CK_OBJECT_HANDLE objectID,
CK_ATTRIBUTE_TYPE type,
SECItem *signText);
CK_RV
sftkdb_DestroyAttributeSignature(SFTKDBHandle *handle, SDB *db,
CK_OBJECT_HANDLE objectID,
CK_ATTRIBUTE_TYPE type);
+/* password lock functions */
+SECStatus sftkdb_passwordLockInit(SFTKDBHandle *keydb);
+void sftkdb_passwordLockDestroy(SFTKDBHandle *keydb);
+PRBool sftkdb_passwordLockIsInited(SFTKDBHandle *keydb);
+void sftkdb_passwordReaderLock(SFTKDBHandle *keydb);
+void sftkdb_passwordWriterLock(SFTKDBHandle *keydb);
+void sftkdb_passwordReaderUnlock(SFTKDBHandle *keydb);
+void sftkdb_passwordWriterUnlock(SFTKDBHandle *keydb);
+
#endif
diff --git a/lib/softoken/sftkpwd.c b/lib/softoken/sftkpwd.c
--- a/lib/softoken/sftkpwd.c
+++ b/lib/softoken/sftkpwd.c
@@ -627,44 +627,164 @@ sftkdb_SignAttribute(PLArenaPool *arena,
loser:
PORT_Memset(signData, 0, sizeof signData);
if (param) {
nsspkcs5_DestroyPBEParameter(param);
}
return rv;
}
+SECStatus
+sftkdb_passwordLockInit(SFTKDBHandle *keydb)
+{
+ keydb->passwordLock = PZ_NewLock(nssILockAttribute);
+ if (keydb->passwordLock == NULL) {
+ return SECFailure;
+ }
+ keydb->passwordWriterCond = PR_NewCondVar(keydb->passwordLock);
+ if (keydb->passwordWriterCond == NULL) {
+ PZ_DestroyLock(keydb->passwordLock);
+ keydb->passwordLock = NULL;
+ return SECFailure;
+ }
+ keydb->passwordReaderCond = PR_NewCondVar(keydb->passwordLock);
+ if (keydb->passwordReaderCond == NULL) {
+ PR_DestroyCondVar(keydb->passwordWriterCond);
+ PZ_DestroyLock(keydb->passwordLock);
+ keydb->passwordWriterCond = NULL;
+ keydb->passwordLock = NULL;
+ return SECFailure;
+ }
+ keydb->passwordWriters = 0;
+ keydb->passwordReaders = 0;
+ keydb->passwordWriterActive = PR_FALSE;
+ return SECSuccess;
+}
+
+void
+sftkdb_passwordLockDestroy(SFTKDBHandle *keydb)
+{
+ PR_DestroyCondVar(keydb->passwordWriterCond);
+ keydb->passwordWriterCond = NULL;
+ PR_DestroyCondVar(keydb->passwordReaderCond);
+ keydb->passwordReaderCond = NULL;
+ PZ_DestroyLock(keydb->passwordLock);
+ keydb->passwordLock = NULL;
+}
+
+PRBool
+sftkdb_passwordLockIsInited(SFTKDBHandle *keydb)
+{
+ return (keydb->passwordLock) && (keydb->passwordWriterCond)
+ && (keydb->passwordReaderCond);
+}
+
+/* we need reader/writer locks for the database because we have servers
+ * that use the database key in their transactions on multi-cpu systems.
+ * This means that all the other cpus doing private key operations become
+ * effectively single threaded. We implement our specific reader/writer
+ * locks because we want writer locks to interrupt reader streams to
+ * prevent starvation on the writer side. */
+void
+sftkdb_passwordReaderLock(SFTKDBHandle *keydb)
+{
+ PZ_Lock(keydb->passwordLock);
+ /* if there is a writer waiting or running wait until the
+ * writer has completed before we continue. This prevents
+ * writer starvation in a loaded system */
+ while (keydb->passwordWriters) {
+ PR_WaitCondVar(keydb->passwordReaderCond, PR_INTERVAL_NO_TIMEOUT);
+ }
+ /* allow multiple readers */
+ keydb->passwordReaders++;
+ PORT_Assert(keydb->passwordWriterActive == PR_FALSE);
+ PZ_Unlock(keydb->passwordLock);
+}
+
+/* We can only have 1 active writer or multiple active readers.
+ * Readers will wait for an active writer. This means lots of write
+ * operations can stall the readers. Write operations are rare, though and
+ * only happen on database update, or on initial login.
+ */
+void
+sftkdb_passwordWriterLock(SFTKDBHandle *keydb)
+{
+ PZ_Lock(keydb->passwordLock);
+ keydb->passwordWriters++;
+ /* if we have any readers or writers, wait for the writer condition */
+ while (keydb->passwordReaders || keydb->passwordWriterActive) {
+ PR_WaitCondVar(keydb->passwordWriterCond, PR_INTERVAL_NO_TIMEOUT);
+ }
+ /* only one writer allowed at a time */
+ keydb->passwordWriterActive = PR_TRUE;
+ PZ_Unlock(keydb->passwordLock);
+}
+
+/* unlock, decrements the reader counter, and if we have not active readers,
+ * notify any waiting writers. */
+void
+sftkdb_passwordReaderUnlock(SFTKDBHandle *keydb)
+{
+ PZ_Lock(keydb->passwordLock);
+ PORT_Assert(keydb->passwordReaders);
+ keydb->passwordReaders--;
+ if (keydb->passwordReaders == 0) {
+ if (keydb->passwordWriters) {
+ PR_NotifyCondVar(keydb->passwordWriterCond);
+ }
+ }
+ PZ_Unlock(keydb->passwordLock);
+}
+
+/* unlock, if there are more writers, wake one of them up. If there or no more
+ * writers but readers, wake up all the readers. */
+void
+sftkdb_passwordWriterUnlock(SFTKDBHandle *keydb)
+{
+ PZ_Lock(keydb->passwordLock);
+ PORT_Assert(keydb->passwordWriters);
+ PORT_Assert(keydb->passwordWriterActive);
+ keydb->passwordWriterActive = PR_FALSE;
+ keydb->passwordWriters--;
+ if (keydb->passwordWriters) {
+ PR_NotifyCondVar(keydb->passwordWriterCond);
+ } else {
+ PR_NotifyAllCondVar(keydb->passwordReaderCond);
+ }
+ PZ_Unlock(keydb->passwordLock);
+}
+
/*
* safely swith the passed in key for the one caches in the keydb handle
*
* A key attached to the handle tells us the the token is logged in.
* We can used the key attached to the handle in sftkdb_EncryptAttribute
* and sftkdb_DecryptAttribute calls.
*/
static void
sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey, int iterationCount)
{
unsigned char *data;
int len;
- if (keydb->passwordLock == NULL) {
+ if (!sftkdb_passwordLockIsInited(keydb)) {
PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
return;
}
/* an atomic pointer set would be nice */
- SKIP_AFTER_FORK(PZ_Lock(keydb->passwordLock));
+ SKIP_AFTER_FORK(sftkdb_passwordWriterLock(keydb));
data = keydb->passwordKey.data;
len = keydb->passwordKey.len;
keydb->passwordKey.data = passKey->data;
keydb->passwordKey.len = passKey->len;
keydb->defaultIterationCount = iterationCount;
passKey->data = data;
passKey->len = len;
- SKIP_AFTER_FORK(PZ_Unlock(keydb->passwordLock));
+ SKIP_AFTER_FORK(sftkdb_passwordWriterUnlock(keydb));
}
/*
* returns true if we are in a middle of a merge style update.
*/
PRBool
sftkdb_InUpdateMerge(SFTKDBHandle *keydb)
{
@@ -700,21 +820,21 @@ sftkdb_GetUpdatePasswordKey(SFTKDBHandle
handle = handle->peerDB;
}
/* don't have one */
if (!handle) {
return NULL;
}
- PZ_Lock(handle->passwordLock);
+ sftkdb_passwordReaderLock(handle);
if (handle->updatePasswordKey) {
key = SECITEM_DupItem(handle->updatePasswordKey);
}
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordReaderUnlock(handle);
return key;
}
/*
* free the update password key from a handle.
*/
void
@@ -727,22 +847,22 @@ sftkdb_FreeUpdatePasswordKey(SFTKDBHandl
return;
}
/* if we're a cert db, we don't have one */
if (handle->type == SFTK_CERTDB_TYPE) {
return;
}
- PZ_Lock(handle->passwordLock);
+ sftkdb_passwordWriterLock(handle);
if (handle->updatePasswordKey) {
key = handle->updatePasswordKey;
handle->updatePasswordKey = NULL;
}
- PZ_Unlock(handle->passwordLock);
+ sftkdb_passwordWriterUnlock(handle);
if (key) {
SECITEM_ZfreeItem(key, PR_TRUE);
}
return;
}
@@ -999,25 +1119,25 @@ sftkdb_finishPasswordCheck(SFTKDBHandle
* the user. Clear our sessions out to simulate a token
* removal. C_GetTokenInfo will change the token description
* and the token will still appear to be logged out.
* 2) If we already have the source DB password, this password is
* for the target database. We can now move forward with the
* update, as we now have both required passwords.
*
*/
- PZ_Lock(keydb->passwordLock);
+ sftkdb_passwordWriterLock(keydb);
if (sftkdb_NeedUpdateDBPassword(keydb)) {
/* Squirrel this special key away.
* This has the side effect of turning sftkdb_NeedLegacyPW off,
* as well as changing which database is returned from
* SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword()
* and sftkdb_HasPasswordSet()) */
keydb->updatePasswordKey = SECITEM_DupItem(key);
- PZ_Unlock(keydb->passwordLock);
+ sftkdb_passwordWriterUnlock(keydb);
if (keydb->updatePasswordKey == NULL) {
/* PORT_Error set by SECITEM_DupItem */
rv = SECFailure;
goto done;
}
/* Simulate a token removal -- we need to do this any
* any case at this point so the token name is correct. */
@@ -1072,17 +1192,17 @@ sftkdb_finishPasswordCheck(SFTKDBHandle
goto done;
} else {
/* there is no password, just fall through to update.
* update will write the source DB's password record
* into the target DB just like it would in a non-merge
* update case. */
}
} else {
- PZ_Unlock(keydb->passwordLock);
+ sftkdb_passwordWriterUnlock(keydb);
}
/* load the keys, so the keydb can parse it's key set */
sftkdb_switchKeys(keydb, key, iterationCount);
/* we need to update, do it now */
if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update) {
/* update the peer certdb if it exists */
if (keydb->peerDB) {