2274 lines
60 KiB
C
2274 lines
60 KiB
C
/* 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/. */
|
|
|
|
#include "lowkeyi.h"
|
|
#include "secasn1.h"
|
|
#include "secder.h"
|
|
#include "secoid.h"
|
|
#include "blapi.h"
|
|
#include "secitem.h"
|
|
#include "pcert.h"
|
|
#include "mcom_db.h"
|
|
#include "secerr.h"
|
|
|
|
#include "keydbi.h"
|
|
#include "lgdb.h"
|
|
|
|
/*
|
|
* Record keys for keydb
|
|
*/
|
|
#define SALT_STRING "global-salt"
|
|
#define VERSION_STRING "Version"
|
|
#define KEYDB_PW_CHECK_STRING "password-check"
|
|
#define KEYDB_PW_CHECK_LEN 14
|
|
#define KEYDB_FAKE_PW_CHECK_STRING "fake-password-check"
|
|
#define KEYDB_FAKE_PW_CHECK_LEN 19
|
|
|
|
/* Size of the global salt for key database */
|
|
#define SALT_LENGTH 16
|
|
|
|
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
|
|
|
|
const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(NSSLOWKEYEncryptedPrivateKeyInfo) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(NSSLOWKEYEncryptedPrivateKeyInfo, algorithm),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(NSSLOWKEYEncryptedPrivateKeyInfo, encryptedData) },
|
|
{ 0 }
|
|
};
|
|
|
|
const SEC_ASN1Template nsslowkey_PointerToEncryptedPrivateKeyInfoTemplate[] = {
|
|
{ SEC_ASN1_POINTER, 0, nsslowkey_EncryptedPrivateKeyInfoTemplate }
|
|
};
|
|
|
|
/* ====== Default key databse encryption algorithm ====== */
|
|
static void
|
|
sec_destroy_dbkey(NSSLOWKEYDBKey *dbkey)
|
|
{
|
|
if (dbkey && dbkey->arena) {
|
|
PORT_FreeArena(dbkey->arena, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_dbt(DBT *dbt)
|
|
{
|
|
if (dbt) {
|
|
PORT_Free(dbt->data);
|
|
PORT_Free(dbt);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int keydb_Get(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
|
|
unsigned int flags);
|
|
static int keydb_Put(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
|
|
unsigned int flags);
|
|
static int keydb_Sync(NSSLOWKEYDBHandle *db, unsigned int flags);
|
|
static int keydb_Del(NSSLOWKEYDBHandle *db, DBT *key, unsigned int flags);
|
|
static int keydb_Seq(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
|
|
unsigned int flags);
|
|
static void keydb_Close(NSSLOWKEYDBHandle *db);
|
|
|
|
/*
|
|
* format of key database entries for version 3 of database:
|
|
* byte offset field
|
|
* ----------- -----
|
|
* 0 version
|
|
* 1 salt-len
|
|
* 2 nn-len
|
|
* 3.. salt-data
|
|
* ... nickname
|
|
* ... encrypted-key-data
|
|
*/
|
|
static DBT *
|
|
encode_dbkey(NSSLOWKEYDBKey *dbkey, unsigned char version)
|
|
{
|
|
DBT *bufitem = NULL;
|
|
unsigned char *buf;
|
|
int nnlen;
|
|
char *nn;
|
|
|
|
bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT));
|
|
if (bufitem == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
if (dbkey->nickname) {
|
|
nn = dbkey->nickname;
|
|
nnlen = PORT_Strlen(nn) + 1;
|
|
} else {
|
|
nn = "";
|
|
nnlen = 1;
|
|
}
|
|
|
|
/* compute the length of the record */
|
|
/* 1 + 1 + 1 == version number header + salt length + nn len */
|
|
bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1;
|
|
|
|
bufitem->data = (void *)PORT_ZAlloc(bufitem->size);
|
|
if (bufitem->data == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
buf = (unsigned char *)bufitem->data;
|
|
|
|
/* set version number */
|
|
buf[0] = version;
|
|
|
|
/* set length of salt */
|
|
PORT_Assert(dbkey->salt.len < 256);
|
|
buf[1] = dbkey->salt.len;
|
|
|
|
/* set length of nickname */
|
|
PORT_Assert(nnlen < 256);
|
|
buf[2] = nnlen;
|
|
|
|
/* copy salt */
|
|
if (dbkey->salt.len > 0) {
|
|
PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
|
|
}
|
|
|
|
/* copy nickname */
|
|
PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen);
|
|
|
|
/* copy encrypted key */
|
|
PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data,
|
|
dbkey->derPK.len);
|
|
|
|
return (bufitem);
|
|
|
|
loser:
|
|
if (bufitem) {
|
|
free_dbt(bufitem);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
static NSSLOWKEYDBKey *
|
|
decode_dbkey(DBT *bufitem, int expectedVersion)
|
|
{
|
|
NSSLOWKEYDBKey *dbkey;
|
|
PLArenaPool *arena = NULL;
|
|
unsigned char *buf;
|
|
int version;
|
|
int keyoff;
|
|
int nnlen;
|
|
int saltoff;
|
|
|
|
buf = (unsigned char *)bufitem->data;
|
|
|
|
version = buf[0];
|
|
|
|
if (version != expectedVersion) {
|
|
goto loser;
|
|
}
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
|
|
if (dbkey == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
dbkey->arena = arena;
|
|
dbkey->salt.data = NULL;
|
|
dbkey->derPK.data = NULL;
|
|
|
|
dbkey->salt.len = buf[1];
|
|
dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len);
|
|
if (dbkey->salt.data == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
saltoff = 2;
|
|
keyoff = 2 + dbkey->salt.len;
|
|
|
|
if (expectedVersion >= 3) {
|
|
nnlen = buf[2];
|
|
if (nnlen) {
|
|
dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1);
|
|
if (dbkey->nickname) {
|
|
PORT_Memcpy(dbkey->nickname, &buf[keyoff + 1], nnlen);
|
|
}
|
|
}
|
|
keyoff += (nnlen + 1);
|
|
saltoff = 3;
|
|
}
|
|
|
|
PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len);
|
|
|
|
dbkey->derPK.len = bufitem->size - keyoff;
|
|
dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->derPK.len);
|
|
if (dbkey->derPK.data == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len);
|
|
|
|
return (dbkey);
|
|
|
|
loser:
|
|
|
|
if (arena) {
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
static NSSLOWKEYDBKey *
|
|
get_dbkey(NSSLOWKEYDBHandle *handle, DBT *index)
|
|
{
|
|
NSSLOWKEYDBKey *dbkey;
|
|
DBT entry;
|
|
int ret;
|
|
|
|
/* get it from the database */
|
|
ret = keydb_Get(handle, index, &entry, 0);
|
|
if (ret) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return NULL;
|
|
}
|
|
|
|
/* set up dbkey struct */
|
|
|
|
dbkey = decode_dbkey(&entry, handle->version);
|
|
|
|
return (dbkey);
|
|
}
|
|
|
|
static SECStatus
|
|
put_dbkey(NSSLOWKEYDBHandle *handle, DBT *index, NSSLOWKEYDBKey *dbkey, PRBool update)
|
|
{
|
|
DBT *keydata = NULL;
|
|
int status;
|
|
|
|
keydata = encode_dbkey(dbkey, handle->version);
|
|
if (keydata == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
/* put it in the database */
|
|
if (update) {
|
|
status = keydb_Put(handle, index, keydata, 0);
|
|
} else {
|
|
status = keydb_Put(handle, index, keydata, R_NOOVERWRITE);
|
|
}
|
|
|
|
if (status) {
|
|
goto loser;
|
|
}
|
|
|
|
/* sync the database */
|
|
status = keydb_Sync(handle, 0);
|
|
if (status) {
|
|
goto loser;
|
|
}
|
|
|
|
free_dbt(keydata);
|
|
return (SECSuccess);
|
|
|
|
loser:
|
|
if (keydata) {
|
|
free_dbt(keydata);
|
|
}
|
|
|
|
return (SECFailure);
|
|
}
|
|
|
|
SECStatus
|
|
nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle,
|
|
SECStatus (*keyfunc)(DBT *k, DBT *d, void *pdata),
|
|
void *udata)
|
|
{
|
|
DBT data;
|
|
DBT key;
|
|
SECStatus status;
|
|
int ret;
|
|
|
|
if (handle == NULL) {
|
|
return (SECFailure);
|
|
}
|
|
|
|
ret = keydb_Seq(handle, &key, &data, R_FIRST);
|
|
if (ret) {
|
|
return (SECFailure);
|
|
}
|
|
|
|
do {
|
|
/* skip version record */
|
|
if (data.size > 1) {
|
|
if (key.size == (sizeof(SALT_STRING) - 1)) {
|
|
if (PORT_Memcmp(key.data, SALT_STRING, key.size) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* skip password check */
|
|
if (key.size == KEYDB_PW_CHECK_LEN) {
|
|
if (PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
|
|
KEYDB_PW_CHECK_LEN) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
status = (*keyfunc)(&key, &data, udata);
|
|
if (status != SECSuccess) {
|
|
return (status);
|
|
}
|
|
}
|
|
} while (keydb_Seq(handle, &key, &data, R_NEXT) == 0);
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
#ifdef notdef
|
|
typedef struct keyNode {
|
|
struct keyNode *next;
|
|
DBT key;
|
|
} keyNode;
|
|
|
|
typedef struct {
|
|
PLArenaPool *arena;
|
|
keyNode *head;
|
|
} keyList;
|
|
|
|
static SECStatus
|
|
sec_add_key_to_list(DBT *key, DBT *data, void *arg)
|
|
{
|
|
keyList *keylist;
|
|
keyNode *node;
|
|
void *keydata;
|
|
|
|
keylist = (keyList *)arg;
|
|
|
|
/* allocate the node struct */
|
|
node = (keyNode *)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode));
|
|
if (node == NULL) {
|
|
return (SECFailure);
|
|
}
|
|
|
|
/* allocate room for key data */
|
|
keydata = PORT_ArenaZAlloc(keylist->arena, key->size);
|
|
if (keydata == NULL) {
|
|
return (SECFailure);
|
|
}
|
|
|
|
/* link node into list */
|
|
node->next = keylist->head;
|
|
keylist->head = node;
|
|
|
|
/* copy key into node */
|
|
PORT_Memcpy(keydata, key->data, key->size);
|
|
node->key.size = key->size;
|
|
node->key.data = keydata;
|
|
|
|
return (SECSuccess);
|
|
}
|
|
#endif
|
|
|
|
static SECItem *
|
|
decodeKeyDBGlobalSalt(DBT *saltData)
|
|
{
|
|
SECItem *saltitem;
|
|
|
|
saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
|
|
if (saltitem == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size);
|
|
if (saltitem->data == NULL) {
|
|
PORT_Free(saltitem);
|
|
return (NULL);
|
|
}
|
|
|
|
saltitem->len = saltData->size;
|
|
PORT_Memcpy(saltitem->data, saltData->data, saltitem->len);
|
|
|
|
return (saltitem);
|
|
}
|
|
|
|
static SECItem *
|
|
GetKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
DBT saltKey;
|
|
DBT saltData;
|
|
int ret;
|
|
|
|
saltKey.data = SALT_STRING;
|
|
saltKey.size = sizeof(SALT_STRING) - 1;
|
|
|
|
ret = keydb_Get(handle, &saltKey, &saltData, 0);
|
|
if (ret) {
|
|
return (NULL);
|
|
}
|
|
|
|
return (decodeKeyDBGlobalSalt(&saltData));
|
|
}
|
|
|
|
static SECStatus
|
|
StoreKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle, SECItem *salt)
|
|
{
|
|
DBT saltKey;
|
|
DBT saltData;
|
|
int status;
|
|
|
|
saltKey.data = SALT_STRING;
|
|
saltKey.size = sizeof(SALT_STRING) - 1;
|
|
|
|
saltData.data = (void *)salt->data;
|
|
saltData.size = salt->len;
|
|
|
|
/* put global salt into the database now */
|
|
status = keydb_Put(handle, &saltKey, &saltData, 0);
|
|
if (status) {
|
|
return (SECFailure);
|
|
}
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
static SECStatus
|
|
makeGlobalVersion(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
unsigned char version;
|
|
DBT versionData;
|
|
DBT versionKey;
|
|
int status;
|
|
|
|
version = NSSLOWKEY_DB_FILE_VERSION;
|
|
versionData.data = &version;
|
|
versionData.size = 1;
|
|
versionKey.data = VERSION_STRING;
|
|
versionKey.size = sizeof(VERSION_STRING) - 1;
|
|
|
|
/* put version string into the database now */
|
|
status = keydb_Put(handle, &versionKey, &versionData, 0);
|
|
if (status) {
|
|
return (SECFailure);
|
|
}
|
|
handle->version = version;
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
static SECStatus
|
|
makeGlobalSalt(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
DBT saltKey;
|
|
DBT saltData;
|
|
unsigned char saltbuf[16];
|
|
int status;
|
|
|
|
saltKey.data = SALT_STRING;
|
|
saltKey.size = sizeof(SALT_STRING) - 1;
|
|
|
|
saltData.data = (void *)saltbuf;
|
|
saltData.size = sizeof(saltbuf);
|
|
RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf));
|
|
|
|
/* put global salt into the database now */
|
|
status = keydb_Put(handle, &saltKey, &saltData, 0);
|
|
if (status) {
|
|
return (SECFailure);
|
|
}
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
static SECStatus
|
|
encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
|
|
SECItem *encCheck);
|
|
|
|
static unsigned char
|
|
nsslowkey_version(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
DBT versionKey;
|
|
DBT versionData;
|
|
int ret;
|
|
versionKey.data = VERSION_STRING;
|
|
versionKey.size = sizeof(VERSION_STRING) - 1;
|
|
|
|
if (handle->db == NULL) {
|
|
return 255;
|
|
}
|
|
|
|
/* lookup version string in database */
|
|
ret = keydb_Get(handle, &versionKey, &versionData, 0);
|
|
|
|
/* error accessing the database */
|
|
if (ret < 0) {
|
|
return 255;
|
|
}
|
|
|
|
if (ret >= 1) {
|
|
return 0;
|
|
}
|
|
return *((unsigned char *)versionData.data);
|
|
}
|
|
|
|
static PRBool
|
|
seckey_HasAServerKey(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
DBT key;
|
|
DBT data;
|
|
int ret;
|
|
PRBool found = PR_FALSE;
|
|
|
|
ret = keydb_Seq(handle, &key, &data, R_FIRST);
|
|
if (ret) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
do {
|
|
/* skip version record */
|
|
if (data.size > 1) {
|
|
/* skip salt */
|
|
if (key.size == (sizeof(SALT_STRING) - 1)) {
|
|
if (PORT_Memcmp(key.data, SALT_STRING, key.size) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
/* skip pw check entry */
|
|
if (key.size == KEYDB_PW_CHECK_LEN) {
|
|
if (PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
|
|
KEYDB_PW_CHECK_LEN) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* keys stored by nickname will have 0 as the last byte of the
|
|
* db key. Other keys must be stored by modulus. We will not
|
|
* update those because they are left over from a keygen that
|
|
* never resulted in a cert.
|
|
*/
|
|
if (((unsigned char *)key.data)[key.size - 1] != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (PORT_Strcmp(key.data, "Server-Key") == 0) {
|
|
found = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} while (keydb_Seq(handle, &key, &data, R_NEXT) == 0);
|
|
|
|
return found;
|
|
}
|
|
|
|
/* forward declare local create function */
|
|
static NSSLOWKEYDBHandle *nsslowkey_NewHandle(DB *dbHandle);
|
|
|
|
/*
|
|
* currently updates key database from v2 to v3
|
|
*/
|
|
static SECStatus
|
|
nsslowkey_UpdateKeyDBPass1(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
SECStatus rv;
|
|
DBT checkKey;
|
|
DBT checkData;
|
|
DBT saltKey;
|
|
DBT saltData;
|
|
DBT key;
|
|
DBT data;
|
|
unsigned char version;
|
|
NSSLOWKEYDBKey *dbkey = NULL;
|
|
NSSLOWKEYDBHandle *update = NULL;
|
|
SECItem *oldSalt = NULL;
|
|
int ret;
|
|
SECItem checkitem;
|
|
|
|
if (handle->updatedb == NULL) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* create a full DB Handle for our update so we
|
|
* can use the correct locks for the db primatives */
|
|
update = nsslowkey_NewHandle(handle->updatedb);
|
|
if (update == NULL) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* update has now inherited the database handle */
|
|
handle->updatedb = NULL;
|
|
|
|
/*
|
|
* check the version record
|
|
*/
|
|
version = nsslowkey_version(update);
|
|
if (version != 2) {
|
|
goto done;
|
|
}
|
|
|
|
saltKey.data = SALT_STRING;
|
|
saltKey.size = sizeof(SALT_STRING) - 1;
|
|
|
|
ret = keydb_Get(update, &saltKey, &saltData, 0);
|
|
if (ret) {
|
|
/* no salt in old db, so it is corrupted */
|
|
goto done;
|
|
}
|
|
|
|
oldSalt = decodeKeyDBGlobalSalt(&saltData);
|
|
if (oldSalt == NULL) {
|
|
/* bad salt in old db, so it is corrupted */
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* look for a pw check entry
|
|
*/
|
|
checkKey.data = KEYDB_PW_CHECK_STRING;
|
|
checkKey.size = KEYDB_PW_CHECK_LEN;
|
|
|
|
ret = keydb_Get(update, &checkKey, &checkData, 0);
|
|
if (ret) {
|
|
/*
|
|
* if we have a key, but no KEYDB_PW_CHECK_STRING, then this must
|
|
* be an old server database, and it does have a password associated
|
|
* with it. Put a fake entry in so we can identify this db when we do
|
|
* get the password for it.
|
|
*/
|
|
if (seckey_HasAServerKey(update)) {
|
|
DBT fcheckKey;
|
|
DBT fcheckData;
|
|
|
|
/*
|
|
* include a fake string
|
|
*/
|
|
fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING;
|
|
fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN;
|
|
fcheckData.data = "1";
|
|
fcheckData.size = 1;
|
|
/* put global salt into the new database now */
|
|
ret = keydb_Put(handle, &saltKey, &saltData, 0);
|
|
if (ret) {
|
|
goto done;
|
|
}
|
|
ret = keydb_Put(handle, &fcheckKey, &fcheckData, 0);
|
|
if (ret) {
|
|
goto done;
|
|
}
|
|
} else {
|
|
goto done;
|
|
}
|
|
} else {
|
|
/* put global salt into the new database now */
|
|
ret = keydb_Put(handle, &saltKey, &saltData, 0);
|
|
if (ret) {
|
|
goto done;
|
|
}
|
|
|
|
dbkey = decode_dbkey(&checkData, 2);
|
|
if (dbkey == NULL) {
|
|
goto done;
|
|
}
|
|
checkitem = dbkey->derPK;
|
|
dbkey->derPK.data = NULL;
|
|
|
|
/* format the new pw check entry */
|
|
rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE);
|
|
if (rv != SECSuccess) {
|
|
goto done;
|
|
}
|
|
|
|
/* free the dbkey */
|
|
sec_destroy_dbkey(dbkey);
|
|
dbkey = NULL;
|
|
}
|
|
|
|
/* now traverse the database */
|
|
ret = keydb_Seq(update, &key, &data, R_FIRST);
|
|
if (ret) {
|
|
goto done;
|
|
}
|
|
|
|
do {
|
|
/* skip version record */
|
|
if (data.size > 1) {
|
|
/* skip salt */
|
|
if (key.size == (sizeof(SALT_STRING) - 1)) {
|
|
if (PORT_Memcmp(key.data, SALT_STRING, key.size) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
/* skip pw check entry */
|
|
if (key.size == checkKey.size) {
|
|
if (PORT_Memcmp(key.data, checkKey.data, key.size) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* keys stored by nickname will have 0 as the last byte of the
|
|
* db key. Other keys must be stored by modulus. We will not
|
|
* update those because they are left over from a keygen that
|
|
* never resulted in a cert.
|
|
*/
|
|
if (((unsigned char *)key.data)[key.size - 1] != 0) {
|
|
continue;
|
|
}
|
|
|
|
dbkey = decode_dbkey(&data, 2);
|
|
if (dbkey == NULL) {
|
|
continue;
|
|
}
|
|
|
|
/* This puts the key into the new database with the same
|
|
* index (nickname) that it had before. The second pass
|
|
* of the update will have the password. It will decrypt
|
|
* and re-encrypt the entries using a new algorithm.
|
|
*/
|
|
dbkey->nickname = (char *)key.data;
|
|
rv = put_dbkey(handle, &key, dbkey, PR_FALSE);
|
|
dbkey->nickname = NULL;
|
|
|
|
sec_destroy_dbkey(dbkey);
|
|
}
|
|
} while (keydb_Seq(update, &key, &data, R_NEXT) == 0);
|
|
|
|
dbkey = NULL;
|
|
|
|
done:
|
|
/* sync the database */
|
|
ret = keydb_Sync(handle, 0);
|
|
|
|
nsslowkey_CloseKeyDB(update);
|
|
|
|
if (oldSalt) {
|
|
SECITEM_FreeItem(oldSalt, PR_TRUE);
|
|
}
|
|
|
|
if (dbkey) {
|
|
sec_destroy_dbkey(dbkey);
|
|
}
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
static SECStatus
|
|
openNewDB(const char *appName, const char *prefix, const char *dbname,
|
|
NSSLOWKEYDBHandle *handle, NSSLOWKEYDBNameFunc namecb, void *cbarg)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
int status = RDB_FAIL;
|
|
char *updname = NULL;
|
|
DB *updatedb = NULL;
|
|
PRBool updated = PR_FALSE;
|
|
int ret;
|
|
|
|
if (appName) {
|
|
handle->db = rdbopen(appName, prefix, "key", NO_CREATE, &status);
|
|
} else {
|
|
handle->db = dbopen(dbname, NO_CREATE, 0600, DB_HASH, 0);
|
|
}
|
|
/* if create fails then we lose */
|
|
if (handle->db == NULL) {
|
|
return (status == RDB_RETRY) ? SECWouldBlock : SECFailure;
|
|
}
|
|
|
|
/* force a transactional read, which will verify that one and only one
|
|
* process attempts the update. */
|
|
if (nsslowkey_version(handle) == NSSLOWKEY_DB_FILE_VERSION) {
|
|
/* someone else has already updated the database for us */
|
|
db_InitComplete(handle->db);
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* if we are creating a multiaccess database, see if there is a
|
|
* local database we can update from.
|
|
*/
|
|
if (appName) {
|
|
NSSLOWKEYDBHandle *updateHandle;
|
|
updatedb = dbopen(dbname, NO_RDONLY, 0600, DB_HASH, 0);
|
|
if (!updatedb) {
|
|
goto noupdate;
|
|
}
|
|
|
|
/* nsslowkey_version needs a full handle because it calls
|
|
* the kdb_Get() function, which needs to lock.
|
|
*/
|
|
updateHandle = nsslowkey_NewHandle(updatedb);
|
|
if (!updateHandle) {
|
|
updatedb->close(updatedb);
|
|
goto noupdate;
|
|
}
|
|
|
|
handle->version = nsslowkey_version(updateHandle);
|
|
if (handle->version != NSSLOWKEY_DB_FILE_VERSION) {
|
|
nsslowkey_CloseKeyDB(updateHandle);
|
|
goto noupdate;
|
|
}
|
|
|
|
/* copy the new DB from the old one */
|
|
db_Copy(handle->db, updatedb);
|
|
nsslowkey_CloseKeyDB(updateHandle);
|
|
db_InitComplete(handle->db);
|
|
return SECSuccess;
|
|
}
|
|
noupdate:
|
|
|
|
/* update the version number */
|
|
rv = makeGlobalVersion(handle);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/*
|
|
* try to update from v2 db
|
|
*/
|
|
updname = (*namecb)(cbarg, 2);
|
|
if (updname != NULL) {
|
|
handle->updatedb = dbopen(updname, NO_RDONLY, 0600, DB_HASH, 0);
|
|
PORT_Free(updname);
|
|
|
|
if (handle->updatedb) {
|
|
/*
|
|
* Try to update the db using a null password. If the db
|
|
* doesn't have a password, then this will work. If it does
|
|
* have a password, then this will fail and we will do the
|
|
* update later
|
|
*/
|
|
rv = nsslowkey_UpdateKeyDBPass1(handle);
|
|
if (rv == SECSuccess) {
|
|
updated = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* we are using the old salt if we updated from an old db */
|
|
if (!updated) {
|
|
rv = makeGlobalSalt(handle);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
/* sync the database */
|
|
ret = keydb_Sync(handle, 0);
|
|
if (ret) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
rv = SECSuccess;
|
|
|
|
loser:
|
|
db_InitComplete(handle->db);
|
|
return rv;
|
|
}
|
|
|
|
static DB *
|
|
openOldDB(const char *appName, const char *prefix, const char *dbname,
|
|
PRBool openflags)
|
|
{
|
|
DB *db = NULL;
|
|
|
|
if (appName) {
|
|
db = rdbopen(appName, prefix, "key", openflags, NULL);
|
|
} else {
|
|
db = dbopen(dbname, openflags, 0600, DB_HASH, 0);
|
|
}
|
|
|
|
return db;
|
|
}
|
|
|
|
/* check for correct version number */
|
|
static PRBool
|
|
verifyVersion(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
int version = nsslowkey_version(handle);
|
|
|
|
handle->version = version;
|
|
if (version != NSSLOWKEY_DB_FILE_VERSION) {
|
|
if (handle->db) {
|
|
keydb_Close(handle);
|
|
handle->db = NULL;
|
|
}
|
|
}
|
|
return handle->db != NULL;
|
|
}
|
|
|
|
static NSSLOWKEYDBHandle *
|
|
nsslowkey_NewHandle(DB *dbHandle)
|
|
{
|
|
NSSLOWKEYDBHandle *handle;
|
|
handle = (NSSLOWKEYDBHandle *)PORT_ZAlloc(sizeof(NSSLOWKEYDBHandle));
|
|
if (handle == NULL) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
handle->appname = NULL;
|
|
handle->dbname = NULL;
|
|
handle->global_salt = NULL;
|
|
handle->updatedb = NULL;
|
|
handle->db = dbHandle;
|
|
handle->ref = 1;
|
|
handle->lock = PZ_NewLock(nssILockKeyDB);
|
|
|
|
return handle;
|
|
}
|
|
|
|
NSSLOWKEYDBHandle *
|
|
nsslowkey_OpenKeyDB(PRBool readOnly, const char *appName, const char *prefix,
|
|
NSSLOWKEYDBNameFunc namecb, void *cbarg)
|
|
{
|
|
NSSLOWKEYDBHandle *handle = NULL;
|
|
SECStatus rv;
|
|
int openflags;
|
|
char *dbname = NULL;
|
|
|
|
handle = nsslowkey_NewHandle(NULL);
|
|
|
|
openflags = readOnly ? NO_RDONLY : NO_RDWR;
|
|
|
|
dbname = (*namecb)(cbarg, NSSLOWKEY_DB_FILE_VERSION);
|
|
if (dbname == NULL) {
|
|
goto loser;
|
|
}
|
|
handle->appname = appName ? PORT_Strdup(appName) : NULL;
|
|
handle->dbname = (appName == NULL) ? PORT_Strdup(dbname) : (prefix ? PORT_Strdup(prefix) : NULL);
|
|
handle->readOnly = readOnly;
|
|
|
|
handle->db = openOldDB(appName, prefix, dbname, openflags);
|
|
if (handle->db) {
|
|
verifyVersion(handle);
|
|
if (handle->version == 255) {
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
/* if first open fails, try to create a new DB */
|
|
if (handle->db == NULL) {
|
|
if (readOnly) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = openNewDB(appName, prefix, dbname, handle, namecb, cbarg);
|
|
/* two processes started to initialize the database at the same time.
|
|
* The multiprocess code blocked the second one, then had it retry to
|
|
* see if it can just open the database normally */
|
|
if (rv == SECWouldBlock) {
|
|
handle->db = openOldDB(appName, prefix, dbname, openflags);
|
|
verifyVersion(handle);
|
|
if (handle->db == NULL) {
|
|
goto loser;
|
|
}
|
|
} else if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
handle->global_salt = GetKeyDBGlobalSalt(handle);
|
|
if (dbname)
|
|
PORT_Free(dbname);
|
|
return handle;
|
|
|
|
loser:
|
|
|
|
if (dbname)
|
|
PORT_Free(dbname);
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
nsslowkey_CloseKeyDB(handle);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Close the database
|
|
*/
|
|
void
|
|
nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
if (handle != NULL) {
|
|
if (handle->db != NULL) {
|
|
keydb_Close(handle);
|
|
}
|
|
if (handle->updatedb) {
|
|
handle->updatedb->close(handle->updatedb);
|
|
}
|
|
if (handle->dbname)
|
|
PORT_Free(handle->dbname);
|
|
if (handle->appname)
|
|
PORT_Free(handle->appname);
|
|
if (handle->global_salt) {
|
|
SECITEM_FreeItem(handle->global_salt, PR_TRUE);
|
|
}
|
|
if (handle->lock != NULL) {
|
|
SKIP_AFTER_FORK(PZ_DestroyLock(handle->lock));
|
|
}
|
|
|
|
PORT_Free(handle);
|
|
}
|
|
}
|
|
|
|
/* Get the key database version */
|
|
int
|
|
nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
PORT_Assert(handle != NULL);
|
|
|
|
return handle->version;
|
|
}
|
|
|
|
/*
|
|
* Delete a private key that was stored in the database
|
|
*/
|
|
SECStatus
|
|
nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle, const SECItem *pubkey)
|
|
{
|
|
DBT namekey;
|
|
int ret;
|
|
|
|
if (handle == NULL) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return (SECFailure);
|
|
}
|
|
|
|
/* set up db key and data */
|
|
namekey.data = pubkey->data;
|
|
namekey.size = pubkey->len;
|
|
|
|
/* delete it from the database */
|
|
ret = keydb_Del(handle, &namekey, 0);
|
|
if (ret) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return (SECFailure);
|
|
}
|
|
|
|
/* sync the database */
|
|
ret = keydb_Sync(handle, 0);
|
|
if (ret) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return (SECFailure);
|
|
}
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
/*
|
|
* Store a key in the database, indexed by its public key modulus.(value!)
|
|
*/
|
|
SECStatus
|
|
nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle,
|
|
NSSLOWKEYPrivateKey *privkey,
|
|
SECItem *pubKeyData,
|
|
char *nickname,
|
|
SDB *sdb)
|
|
{
|
|
return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData,
|
|
nickname, sdb, PR_FALSE);
|
|
}
|
|
|
|
SECStatus
|
|
nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle,
|
|
NSSLOWKEYPrivateKey *privkey,
|
|
SECItem *pubKeyData,
|
|
char *nickname,
|
|
SDB *sdb)
|
|
{
|
|
return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData,
|
|
nickname, sdb, PR_TRUE);
|
|
}
|
|
|
|
/* see if the symetric CKA_ID already Exists.
|
|
*/
|
|
PRBool
|
|
nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id)
|
|
{
|
|
DBT namekey;
|
|
DBT dummy;
|
|
int status;
|
|
|
|
namekey.data = (char *)id->data;
|
|
namekey.size = id->len;
|
|
status = keydb_Get(handle, &namekey, &dummy, 0);
|
|
if (status) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
/* see if the public key for this cert is in the database filed
|
|
* by modulus
|
|
*/
|
|
PRBool
|
|
nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle, NSSLOWCERTCertificate *cert)
|
|
{
|
|
NSSLOWKEYPublicKey *pubkey = NULL;
|
|
DBT namekey;
|
|
DBT dummy;
|
|
int status;
|
|
|
|
/* get cert's public key */
|
|
pubkey = nsslowcert_ExtractPublicKey(cert);
|
|
if (pubkey == NULL) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
/* TNH - make key from NSSLOWKEYPublicKey */
|
|
switch (pubkey->keyType) {
|
|
case NSSLOWKEYRSAKey:
|
|
namekey.data = pubkey->u.rsa.modulus.data;
|
|
namekey.size = pubkey->u.rsa.modulus.len;
|
|
break;
|
|
case NSSLOWKEYDSAKey:
|
|
namekey.data = pubkey->u.dsa.publicValue.data;
|
|
namekey.size = pubkey->u.dsa.publicValue.len;
|
|
break;
|
|
case NSSLOWKEYDHKey:
|
|
namekey.data = pubkey->u.dh.publicValue.data;
|
|
namekey.size = pubkey->u.dh.publicValue.len;
|
|
break;
|
|
case NSSLOWKEYECKey:
|
|
namekey.data = pubkey->u.ec.publicValue.data;
|
|
namekey.size = pubkey->u.ec.publicValue.len;
|
|
break;
|
|
default:
|
|
/* XXX We don't do Fortezza or DH yet. */
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (handle->version != 3) {
|
|
unsigned char buf[SHA1_LENGTH];
|
|
SHA1_HashBuf(buf, namekey.data, namekey.size);
|
|
/* NOTE: don't use pubkey after this! it's now thrashed */
|
|
PORT_Memcpy(namekey.data, buf, sizeof(buf));
|
|
namekey.size = sizeof(buf);
|
|
}
|
|
|
|
status = keydb_Get(handle, &namekey, &dummy, 0);
|
|
/* some databases have the key stored as a signed value */
|
|
if (status) {
|
|
unsigned char *buf = (unsigned char *)PORT_Alloc(namekey.size + 1);
|
|
if (buf) {
|
|
PORT_Memcpy(&buf[1], namekey.data, namekey.size);
|
|
buf[0] = 0;
|
|
namekey.data = buf;
|
|
namekey.size++;
|
|
status = keydb_Get(handle, &namekey, &dummy, 0);
|
|
PORT_Free(buf);
|
|
}
|
|
}
|
|
lg_nsslowkey_DestroyPublicKey(pubkey);
|
|
if (status) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
typedef struct NSSLowPasswordDataParamStr {
|
|
SECItem salt;
|
|
SECItem iter;
|
|
} NSSLowPasswordDataParam;
|
|
|
|
static const SEC_ASN1Template NSSLOWPasswordParamTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLowPasswordDataParam) },
|
|
{ SEC_ASN1_OCTET_STRING, offsetof(NSSLowPasswordDataParam, salt) },
|
|
{ SEC_ASN1_INTEGER, offsetof(NSSLowPasswordDataParam, iter) },
|
|
{ 0 }
|
|
};
|
|
struct LGEncryptedDataInfoStr {
|
|
SECAlgorithmID algorithm;
|
|
SECItem encryptedData;
|
|
};
|
|
typedef struct LGEncryptedDataInfoStr LGEncryptedDataInfo;
|
|
|
|
const SEC_ASN1Template lg_EncryptedDataInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(LGEncryptedDataInfo) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(LGEncryptedDataInfo, algorithm),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(LGEncryptedDataInfo, encryptedData) },
|
|
{ 0 }
|
|
};
|
|
|
|
static SECItem *
|
|
nsslowkey_EncodePW(SECOidTag alg, const SECItem *salt, SECItem *data)
|
|
{
|
|
NSSLowPasswordDataParam param;
|
|
LGEncryptedDataInfo edi;
|
|
PLArenaPool *arena;
|
|
unsigned char one = 1;
|
|
SECItem *epw = NULL;
|
|
SECItem *encParam;
|
|
int iterLen = 0;
|
|
int saltLen;
|
|
SECStatus rv;
|
|
|
|
param.salt = *salt;
|
|
param.iter.type = siBuffer; /* encode as signed integer */
|
|
param.iter.data = &one;
|
|
param.iter.len = 1;
|
|
edi.encryptedData = *data;
|
|
|
|
iterLen = salt->len > 1 ? salt->data[salt->len - 1] : 2;
|
|
saltLen = (salt->len - iterLen) - 1;
|
|
/* if the resulting saltLen is a sha hash length, then assume that
|
|
* the iteration count is tacked on the end of the buffer */
|
|
if ((saltLen == SHA1_LENGTH) || (saltLen == SHA256_LENGTH) || (saltLen == SHA384_LENGTH) || (saltLen == SHA224_LENGTH) ||
|
|
(saltLen == SHA512_LENGTH)) {
|
|
param.iter.data = &salt->data[saltLen];
|
|
param.iter.len = iterLen;
|
|
param.salt.len = saltLen;
|
|
}
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
encParam = SEC_ASN1EncodeItem(arena, NULL, ¶m,
|
|
NSSLOWPasswordParamTemplate);
|
|
if (encParam == NULL) {
|
|
goto loser;
|
|
}
|
|
rv = SECOID_SetAlgorithmID(arena, &edi.algorithm, alg, encParam);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
epw = SEC_ASN1EncodeItem(NULL, NULL, &edi, lg_EncryptedDataInfoTemplate);
|
|
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return epw;
|
|
}
|
|
|
|
static SECItem *
|
|
nsslowkey_DecodePW(const SECItem *derData, SECOidTag *alg, SECItem *salt)
|
|
{
|
|
NSSLowPasswordDataParam param;
|
|
LGEncryptedDataInfo edi;
|
|
PLArenaPool *arena;
|
|
SECItem *pwe = NULL;
|
|
SECStatus rv;
|
|
|
|
salt->data = NULL;
|
|
param.iter.type = siBuffer; /* decode as signed integer */
|
|
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
rv = SEC_QuickDERDecodeItem(arena, &edi, lg_EncryptedDataInfoTemplate,
|
|
derData);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
*alg = SECOID_GetAlgorithmTag(&edi.algorithm);
|
|
rv = SEC_QuickDERDecodeItem(arena, ¶m, NSSLOWPasswordParamTemplate,
|
|
&edi.algorithm.parameters);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
/* if the iteration count isn't one, tack it at the end of the salt */
|
|
if (!((param.iter.len == 1) && (param.iter.data[0] == 1))) {
|
|
int total_len = param.salt.len + param.iter.len + 1;
|
|
salt->data = PORT_Alloc(total_len);
|
|
if (salt->data == NULL) {
|
|
goto loser;
|
|
}
|
|
PORT_Memcpy(salt->data, param.salt.data, param.salt.len);
|
|
PORT_Memcpy(&salt->data[param.salt.len], param.iter.data,
|
|
param.iter.len);
|
|
salt->data[total_len - 1] = param.iter.len;
|
|
salt->len = total_len;
|
|
} else {
|
|
rv = SECITEM_CopyItem(NULL, salt, ¶m.salt);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
}
|
|
pwe = SECITEM_DupItem(&edi.encryptedData);
|
|
|
|
loser:
|
|
if (!pwe && salt->data) {
|
|
PORT_Free(salt->data);
|
|
salt->data = NULL;
|
|
}
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return pwe;
|
|
}
|
|
|
|
/*
|
|
* check to see if the user has a password
|
|
*/
|
|
static SECStatus
|
|
nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle, NSSLOWKEYPasswordEntry *entry)
|
|
{
|
|
DBT checkkey; /*, checkdata; */
|
|
NSSLOWKEYDBKey *dbkey = NULL;
|
|
SECItem *global_salt = NULL;
|
|
SECItem *item = NULL;
|
|
SECItem entryData, oid;
|
|
SECItem none = { siBuffer, NULL, 0 };
|
|
SECStatus rv = SECFailure;
|
|
SECOidTag algorithm;
|
|
|
|
if (handle == NULL) {
|
|
/* PORT_SetError */
|
|
return (SECFailure);
|
|
}
|
|
|
|
global_salt = GetKeyDBGlobalSalt(handle);
|
|
if (!global_salt) {
|
|
global_salt = &none;
|
|
}
|
|
if (global_salt->len > sizeof(entry->data)) {
|
|
/* PORT_SetError */
|
|
goto loser;
|
|
}
|
|
|
|
PORT_Memcpy(entry->data, global_salt->data, global_salt->len);
|
|
entry->salt.data = entry->data;
|
|
entry->salt.len = global_salt->len;
|
|
entry->value.data = &entry->data[entry->salt.len];
|
|
|
|
checkkey.data = KEYDB_PW_CHECK_STRING;
|
|
checkkey.size = KEYDB_PW_CHECK_LEN;
|
|
dbkey = get_dbkey(handle, &checkkey);
|
|
if (dbkey == NULL) {
|
|
/* handle 'FAKE' check here */
|
|
goto loser;
|
|
}
|
|
|
|
oid.len = dbkey->derPK.data[0];
|
|
oid.data = &dbkey->derPK.data[1];
|
|
|
|
if (dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 + oid.len)) {
|
|
goto loser;
|
|
}
|
|
algorithm = SECOID_FindOIDTag(&oid);
|
|
entryData.type = siBuffer;
|
|
entryData.len = dbkey->derPK.len - (oid.len + 1);
|
|
entryData.data = &dbkey->derPK.data[oid.len + 1];
|
|
|
|
item = nsslowkey_EncodePW(algorithm, &dbkey->salt, &entryData);
|
|
if (!item || (item->len + entry->salt.len) > sizeof(entry->data)) {
|
|
goto loser;
|
|
}
|
|
PORT_Memcpy(entry->value.data, item->data, item->len);
|
|
entry->value.len = item->len;
|
|
rv = SECSuccess;
|
|
|
|
loser:
|
|
if (item) {
|
|
SECITEM_FreeItem(item, PR_TRUE);
|
|
}
|
|
if (dbkey) {
|
|
sec_destroy_dbkey(dbkey);
|
|
}
|
|
if (global_salt != &none) {
|
|
SECITEM_FreeItem(global_salt, PR_TRUE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* check to see if the user has a password
|
|
*/
|
|
static SECStatus
|
|
nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle, NSSLOWKEYPasswordEntry *entry)
|
|
{
|
|
DBT checkkey;
|
|
NSSLOWKEYDBKey *dbkey = NULL;
|
|
SECItem *item = NULL;
|
|
SECItem salt;
|
|
SECOidTag algid = SEC_OID_UNKNOWN;
|
|
SECStatus rv = SECFailure;
|
|
PLArenaPool *arena;
|
|
int ret;
|
|
|
|
if (handle == NULL) {
|
|
/* PORT_SetError */
|
|
return (SECFailure);
|
|
}
|
|
|
|
checkkey.data = KEYDB_PW_CHECK_STRING;
|
|
checkkey.size = KEYDB_PW_CHECK_LEN;
|
|
|
|
salt.data = NULL;
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL) {
|
|
return SECFailure;
|
|
}
|
|
|
|
item = nsslowkey_DecodePW(&entry->value, &algid, &salt);
|
|
if (item == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
dbkey = PORT_ArenaZNew(arena, NSSLOWKEYDBKey);
|
|
if (dbkey == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
dbkey->arena = arena;
|
|
|
|
rv = SECITEM_CopyItem(arena, &dbkey->salt, &salt);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = encodePWCheckEntry(arena, &dbkey->derPK, algid, item);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
if (handle->global_salt) {
|
|
SECITEM_FreeItem(handle->global_salt, PR_TRUE);
|
|
handle->global_salt = NULL;
|
|
}
|
|
rv = StoreKeyDBGlobalSalt(handle, &entry->salt);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
ret = keydb_Sync(handle, 0);
|
|
if (ret) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
handle->global_salt = GetKeyDBGlobalSalt(handle);
|
|
|
|
loser:
|
|
if (item) {
|
|
SECITEM_FreeItem(item, PR_TRUE);
|
|
}
|
|
if (arena) {
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
}
|
|
if (salt.data) {
|
|
PORT_Free(salt.data);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
#ifdef EC_DEBUG
|
|
#define SEC_PRINT(str1, str2, num, sitem) \
|
|
printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
|
|
str1, str2, num, sitem->len); \
|
|
for (i = 0; i < sitem->len; i++) { \
|
|
printf("%02x:", sitem->data[i]); \
|
|
} \
|
|
printf("\n")
|
|
#else
|
|
#define SEC_PRINT(a, b, c, d)
|
|
#endif /* EC_DEBUG */
|
|
|
|
SECStatus
|
|
seckey_encrypt_private_key(PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk,
|
|
SDB *sdbpw, SECItem *result)
|
|
{
|
|
NSSLOWKEYPrivateKeyInfo *pki = NULL;
|
|
SECStatus rv = SECFailure;
|
|
PLArenaPool *temparena = NULL;
|
|
SECItem *der_item = NULL;
|
|
SECItem *cipherText = NULL;
|
|
SECItem *dummy = NULL;
|
|
#ifdef EC_DEBUG
|
|
SECItem *fordebug = NULL;
|
|
#endif
|
|
int savelen;
|
|
|
|
temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
|
if (temparena == NULL)
|
|
goto loser;
|
|
|
|
/* allocate structures */
|
|
pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
|
|
sizeof(NSSLOWKEYPrivateKeyInfo));
|
|
der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem));
|
|
if ((pki == NULL) || (der_item == NULL))
|
|
goto loser;
|
|
|
|
/* setup private key info */
|
|
dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version),
|
|
NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
|
|
if (dummy == NULL)
|
|
goto loser;
|
|
|
|
/* Encode the key, and set the algorithm (with params) */
|
|
switch (pk->keyType) {
|
|
case NSSLOWKEYRSAKey:
|
|
lg_prepare_low_rsa_priv_key_for_asn1(pk);
|
|
dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
|
|
lg_nsslowkey_RSAPrivateKeyTemplate);
|
|
if (dummy == NULL) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
|
|
SEC_OID_PKCS1_RSA_ENCRYPTION, 0);
|
|
if (rv == SECFailure) {
|
|
goto loser;
|
|
}
|
|
|
|
break;
|
|
case NSSLOWKEYDSAKey:
|
|
lg_prepare_low_dsa_priv_key_for_asn1(pk);
|
|
dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
|
|
lg_nsslowkey_DSAPrivateKeyTemplate);
|
|
if (dummy == NULL) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
|
|
dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params,
|
|
lg_nsslowkey_PQGParamsTemplate);
|
|
if (dummy == NULL) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
|
|
SEC_OID_ANSIX9_DSA_SIGNATURE, dummy);
|
|
if (rv == SECFailure) {
|
|
goto loser;
|
|
}
|
|
|
|
break;
|
|
case NSSLOWKEYDHKey:
|
|
lg_prepare_low_dh_priv_key_for_asn1(pk);
|
|
dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
|
|
lg_nsslowkey_DHPrivateKeyTemplate);
|
|
if (dummy == NULL) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
|
|
SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy);
|
|
if (rv == SECFailure) {
|
|
goto loser;
|
|
}
|
|
break;
|
|
case NSSLOWKEYECKey:
|
|
lg_prepare_low_ec_priv_key_for_asn1(pk);
|
|
/* Public value is encoded as a bit string so adjust length
|
|
* to be in bits before ASN encoding and readjust
|
|
* immediately after.
|
|
*
|
|
* Since the SECG specification recommends not including the
|
|
* parameters as part of ECPrivateKey, we zero out the curveOID
|
|
* length before encoding and restore it later.
|
|
*/
|
|
pk->u.ec.publicValue.len <<= 3;
|
|
savelen = pk->u.ec.ecParams.curveOID.len;
|
|
pk->u.ec.ecParams.curveOID.len = 0;
|
|
dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
|
|
lg_nsslowkey_ECPrivateKeyTemplate);
|
|
pk->u.ec.ecParams.curveOID.len = savelen;
|
|
pk->u.ec.publicValue.len >>= 3;
|
|
|
|
if (dummy == NULL) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
dummy = &pk->u.ec.ecParams.DEREncoding;
|
|
|
|
/* At this point dummy should contain the encoded params */
|
|
rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
|
|
SEC_OID_ANSIX962_EC_PUBLIC_KEY, dummy);
|
|
|
|
if (rv == SECFailure) {
|
|
goto loser;
|
|
}
|
|
|
|
#ifdef EC_DEBUG
|
|
fordebug = &(pki->privateKey);
|
|
SEC_PRINT("seckey_encrypt_private_key()", "PrivateKey",
|
|
pk->keyType, fordebug);
|
|
#endif
|
|
|
|
break;
|
|
default:
|
|
/* We don't support DH or Fortezza private keys yet */
|
|
PORT_Assert(PR_FALSE);
|
|
break;
|
|
}
|
|
|
|
/* setup encrypted private key info */
|
|
dummy = SEC_ASN1EncodeItem(temparena, der_item, pki,
|
|
lg_nsslowkey_PrivateKeyInfoTemplate);
|
|
|
|
SEC_PRINT("seckey_encrypt_private_key()", "PrivateKeyInfo",
|
|
pk->keyType, der_item);
|
|
|
|
if (dummy == NULL) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
rv = lg_util_encrypt(temparena, sdbpw, dummy, &cipherText);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = SECITEM_CopyItem(permarena, result, cipherText);
|
|
|
|
loser:
|
|
|
|
if (temparena != NULL)
|
|
PORT_FreeArena(temparena, PR_TRUE);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static SECStatus
|
|
seckey_put_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, SDB *sdbpw,
|
|
NSSLOWKEYPrivateKey *pk, char *nickname, PRBool update)
|
|
{
|
|
NSSLOWKEYDBKey *dbkey = NULL;
|
|
PLArenaPool *arena = NULL;
|
|
SECStatus rv = SECFailure;
|
|
|
|
if ((keydb == NULL) || (index == NULL) || (sdbpw == NULL) ||
|
|
(pk == NULL))
|
|
return SECFailure;
|
|
|
|
arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
|
if (arena == NULL)
|
|
return SECFailure;
|
|
|
|
dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
|
|
if (dbkey == NULL)
|
|
goto loser;
|
|
dbkey->arena = arena;
|
|
dbkey->nickname = nickname;
|
|
|
|
rv = seckey_encrypt_private_key(arena, pk, sdbpw, &dbkey->derPK);
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
|
|
rv = put_dbkey(keydb, index, dbkey, update);
|
|
|
|
/* let success fall through */
|
|
loser:
|
|
if (arena != NULL)
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Store a key in the database, indexed by its public key modulus.
|
|
* Note that the nickname is optional. It was only used by keyutil.
|
|
*/
|
|
SECStatus
|
|
nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle,
|
|
NSSLOWKEYPrivateKey *privkey,
|
|
SECItem *pubKeyData,
|
|
char *nickname,
|
|
SDB *sdbpw,
|
|
PRBool update)
|
|
{
|
|
DBT namekey;
|
|
SECStatus rv;
|
|
|
|
if (handle == NULL) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return (SECFailure);
|
|
}
|
|
|
|
/* set up db key and data */
|
|
namekey.data = pubKeyData->data;
|
|
namekey.size = pubKeyData->len;
|
|
|
|
/* encrypt the private key */
|
|
rv = seckey_put_private_key(handle, &namekey, sdbpw, privkey, nickname,
|
|
update);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static NSSLOWKEYPrivateKey *
|
|
seckey_decrypt_private_key(SECItem *epki,
|
|
SDB *sdbpw)
|
|
{
|
|
NSSLOWKEYPrivateKey *pk = NULL;
|
|
NSSLOWKEYPrivateKeyInfo *pki = NULL;
|
|
SECStatus rv = SECFailure;
|
|
PLArenaPool *temparena = NULL, *permarena = NULL;
|
|
SECItem *dest = NULL;
|
|
#ifdef EC_DEBUG
|
|
SECItem *fordebug = NULL;
|
|
#endif
|
|
|
|
if ((epki == NULL) || (sdbpw == NULL))
|
|
goto loser;
|
|
|
|
temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
|
permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
|
if ((temparena == NULL) || (permarena == NULL))
|
|
goto loser;
|
|
|
|
/* allocate temporary items */
|
|
pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
|
|
sizeof(NSSLOWKEYPrivateKeyInfo));
|
|
|
|
/* allocate permanent arena items */
|
|
pk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(permarena,
|
|
sizeof(NSSLOWKEYPrivateKey));
|
|
|
|
if ((pk == NULL) || (pki == NULL))
|
|
goto loser;
|
|
|
|
pk->arena = permarena;
|
|
|
|
rv = lg_util_decrypt(sdbpw, epki, &dest);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
if (dest != NULL) {
|
|
SECItem newPrivateKey;
|
|
SECItem newAlgParms;
|
|
|
|
SEC_PRINT("seckey_decrypt_private_key()", "PrivateKeyInfo", -1,
|
|
dest);
|
|
|
|
rv = SEC_QuickDERDecodeItem(temparena, pki,
|
|
lg_nsslowkey_PrivateKeyInfoTemplate, dest);
|
|
if (rv == SECSuccess) {
|
|
switch (SECOID_GetAlgorithmTag(&pki->algorithm)) {
|
|
case SEC_OID_X500_RSA_ENCRYPTION:
|
|
case SEC_OID_PKCS1_RSA_ENCRYPTION:
|
|
pk->keyType = NSSLOWKEYRSAKey;
|
|
lg_prepare_low_rsa_priv_key_for_asn1(pk);
|
|
if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
|
|
&pki->privateKey))
|
|
break;
|
|
rv = SEC_QuickDERDecodeItem(permarena, pk,
|
|
lg_nsslowkey_RSAPrivateKeyTemplate,
|
|
&newPrivateKey);
|
|
if (rv == SECSuccess) {
|
|
break;
|
|
}
|
|
/* Try decoding with the alternative template, but only allow
|
|
* a zero-length modulus for a secret key object.
|
|
* See bug 715073.
|
|
*/
|
|
rv = SEC_QuickDERDecodeItem(permarena, pk,
|
|
lg_nsslowkey_RSAPrivateKeyTemplate2,
|
|
&newPrivateKey);
|
|
/* A publicExponent of 0 is the defining property of a secret
|
|
* key disguised as an RSA key. When decoding with the
|
|
* alternative template, only accept a secret key with an
|
|
* improperly encoded modulus and a publicExponent of 0.
|
|
*/
|
|
if (rv == SECSuccess) {
|
|
if (pk->u.rsa.modulus.len == 2 &&
|
|
pk->u.rsa.modulus.data[0] == SEC_ASN1_INTEGER &&
|
|
pk->u.rsa.modulus.data[1] == 0 &&
|
|
pk->u.rsa.publicExponent.len == 1 &&
|
|
pk->u.rsa.publicExponent.data[0] == 0) {
|
|
/* Fix the zero-length integer by setting it to 0. */
|
|
pk->u.rsa.modulus.data = pk->u.rsa.publicExponent.data;
|
|
pk->u.rsa.modulus.len = pk->u.rsa.publicExponent.len;
|
|
} else {
|
|
PORT_SetError(SEC_ERROR_BAD_DER);
|
|
rv = SECFailure;
|
|
}
|
|
}
|
|
break;
|
|
case SEC_OID_ANSIX9_DSA_SIGNATURE:
|
|
pk->keyType = NSSLOWKEYDSAKey;
|
|
lg_prepare_low_dsa_priv_key_for_asn1(pk);
|
|
if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
|
|
&pki->privateKey))
|
|
break;
|
|
rv = SEC_QuickDERDecodeItem(permarena, pk,
|
|
lg_nsslowkey_DSAPrivateKeyTemplate,
|
|
&newPrivateKey);
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
|
|
if (SECSuccess != SECITEM_CopyItem(permarena, &newAlgParms,
|
|
&pki->algorithm.parameters))
|
|
break;
|
|
rv = SEC_QuickDERDecodeItem(permarena, &pk->u.dsa.params,
|
|
lg_nsslowkey_PQGParamsTemplate,
|
|
&newAlgParms);
|
|
break;
|
|
case SEC_OID_X942_DIFFIE_HELMAN_KEY:
|
|
pk->keyType = NSSLOWKEYDHKey;
|
|
lg_prepare_low_dh_priv_key_for_asn1(pk);
|
|
if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
|
|
&pki->privateKey))
|
|
break;
|
|
rv = SEC_QuickDERDecodeItem(permarena, pk,
|
|
lg_nsslowkey_DHPrivateKeyTemplate,
|
|
&newPrivateKey);
|
|
break;
|
|
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
|
|
pk->keyType = NSSLOWKEYECKey;
|
|
lg_prepare_low_ec_priv_key_for_asn1(pk);
|
|
|
|
#ifdef EC_DEBUG
|
|
fordebug = &pki->privateKey;
|
|
SEC_PRINT("seckey_decrypt_private_key()", "PrivateKey",
|
|
pk->keyType, fordebug);
|
|
#endif
|
|
if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
|
|
&pki->privateKey))
|
|
break;
|
|
rv = SEC_QuickDERDecodeItem(permarena, pk,
|
|
lg_nsslowkey_ECPrivateKeyTemplate,
|
|
&newPrivateKey);
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
|
|
lg_prepare_low_ecparams_for_asn1(&pk->u.ec.ecParams);
|
|
|
|
rv = SECITEM_CopyItem(permarena,
|
|
&pk->u.ec.ecParams.DEREncoding,
|
|
&pki->algorithm.parameters);
|
|
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
|
|
/* Fill out the rest of EC params */
|
|
rv = LGEC_FillParams(permarena, &pk->u.ec.ecParams.DEREncoding,
|
|
&pk->u.ec.ecParams);
|
|
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
|
|
if (pk->u.ec.publicValue.len != 0) {
|
|
pk->u.ec.publicValue.len >>= 3;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
} else if (PORT_GetError() == SEC_ERROR_BAD_DER) {
|
|
PORT_SetError(SEC_ERROR_BAD_PASSWORD);
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
/* let success fall through */
|
|
loser:
|
|
if (temparena != NULL)
|
|
PORT_FreeArena(temparena, PR_TRUE);
|
|
if (dest != NULL)
|
|
SECITEM_ZfreeItem(dest, PR_TRUE);
|
|
|
|
if (rv != SECSuccess) {
|
|
if (permarena != NULL)
|
|
PORT_FreeArena(permarena, PR_TRUE);
|
|
pk = NULL;
|
|
}
|
|
|
|
return pk;
|
|
}
|
|
|
|
static NSSLOWKEYPrivateKey *
|
|
seckey_decode_encrypted_private_key(NSSLOWKEYDBKey *dbkey, SDB *sdbpw)
|
|
{
|
|
if ((dbkey == NULL) || (sdbpw == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
return seckey_decrypt_private_key(&(dbkey->derPK), sdbpw);
|
|
}
|
|
|
|
static NSSLOWKEYPrivateKey *
|
|
seckey_get_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, char **nickname,
|
|
SDB *sdbpw)
|
|
{
|
|
NSSLOWKEYDBKey *dbkey = NULL;
|
|
NSSLOWKEYPrivateKey *pk = NULL;
|
|
|
|
if ((keydb == NULL) || (index == NULL) || (sdbpw == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
dbkey = get_dbkey(keydb, index);
|
|
if (dbkey == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
if (nickname) {
|
|
if (dbkey->nickname && (dbkey->nickname[0] != 0)) {
|
|
*nickname = PORT_Strdup(dbkey->nickname);
|
|
} else {
|
|
*nickname = NULL;
|
|
}
|
|
}
|
|
|
|
pk = seckey_decode_encrypted_private_key(dbkey, sdbpw);
|
|
|
|
/* let success fall through */
|
|
loser:
|
|
|
|
if (dbkey != NULL) {
|
|
sec_destroy_dbkey(dbkey);
|
|
}
|
|
|
|
return pk;
|
|
}
|
|
|
|
/*
|
|
* Find a key in the database, indexed by its public key modulus
|
|
* This is used to find keys that have been stored before their
|
|
* certificate arrives. Once the certificate arrives the key
|
|
* is looked up by the public modulus in the certificate, and the
|
|
* re-stored by its nickname.
|
|
*/
|
|
NSSLOWKEYPrivateKey *
|
|
nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus,
|
|
SDB *sdbpw)
|
|
{
|
|
DBT namekey;
|
|
NSSLOWKEYPrivateKey *pk = NULL;
|
|
|
|
if (handle == NULL) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return NULL;
|
|
}
|
|
|
|
/* set up db key */
|
|
namekey.data = modulus->data;
|
|
namekey.size = modulus->len;
|
|
|
|
pk = seckey_get_private_key(handle, &namekey, NULL, sdbpw);
|
|
|
|
/* no need to free dbkey, since its on the stack, and the data it
|
|
* points to is owned by the database
|
|
*/
|
|
return (pk);
|
|
}
|
|
|
|
char *
|
|
nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle,
|
|
SECItem *modulus, SDB *sdbpw)
|
|
{
|
|
DBT namekey;
|
|
NSSLOWKEYPrivateKey *pk = NULL;
|
|
char *nickname = NULL;
|
|
|
|
if (handle == NULL) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATABASE);
|
|
return NULL;
|
|
}
|
|
|
|
/* set up db key */
|
|
namekey.data = modulus->data;
|
|
namekey.size = modulus->len;
|
|
|
|
pk = seckey_get_private_key(handle, &namekey, &nickname, sdbpw);
|
|
if (pk) {
|
|
lg_nsslowkey_DestroyPrivateKey(pk);
|
|
}
|
|
|
|
/* no need to free dbkey, since its on the stack, and the data it
|
|
* points to is owned by the database
|
|
*/
|
|
return (nickname);
|
|
}
|
|
/* ===== ENCODING ROUTINES ===== */
|
|
|
|
static SECStatus
|
|
encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
|
|
SECItem *encCheck)
|
|
{
|
|
SECOidData *oidData;
|
|
|
|
oidData = SECOID_FindOIDByTag(alg);
|
|
if (oidData == NULL) {
|
|
return SECFailure;
|
|
}
|
|
|
|
entry->len = 1 + oidData->oid.len + encCheck->len;
|
|
if (arena) {
|
|
entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len);
|
|
} else {
|
|
entry->data = (unsigned char *)PORT_Alloc(entry->len);
|
|
}
|
|
|
|
if (entry->data == NULL) {
|
|
return SECFailure;
|
|
}
|
|
|
|
/* first length of oid */
|
|
entry->data[0] = (unsigned char)oidData->oid.len;
|
|
/* next oid itself */
|
|
PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len);
|
|
/* finally the encrypted check string */
|
|
PORT_Memcpy(&entry->data[1 + oidData->oid.len], encCheck->data,
|
|
encCheck->len);
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
#define MAX_DB_SIZE 0xffff
|
|
/*
|
|
* Clear out all the keys in the existing database
|
|
*/
|
|
static SECStatus
|
|
nsslowkey_ResetKeyDB(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
SECStatus rv;
|
|
int errors = 0;
|
|
|
|
if (handle->db == NULL) {
|
|
return (SECSuccess);
|
|
}
|
|
|
|
if (handle->readOnly) {
|
|
/* set an error code */
|
|
return SECFailure;
|
|
}
|
|
|
|
if (handle->appname == NULL && handle->dbname == NULL) {
|
|
return SECFailure;
|
|
}
|
|
|
|
keydb_Close(handle);
|
|
if (handle->appname) {
|
|
handle->db =
|
|
rdbopen(handle->appname, handle->dbname, "key", NO_CREATE, NULL);
|
|
} else {
|
|
handle->db = dbopen(handle->dbname, NO_CREATE, 0600, DB_HASH, 0);
|
|
}
|
|
if (handle->db == NULL) {
|
|
/* set an error code */
|
|
return SECFailure;
|
|
}
|
|
|
|
rv = makeGlobalVersion(handle);
|
|
if (rv != SECSuccess) {
|
|
errors++;
|
|
goto done;
|
|
}
|
|
|
|
if (handle->global_salt) {
|
|
rv = StoreKeyDBGlobalSalt(handle, handle->global_salt);
|
|
} else {
|
|
rv = makeGlobalSalt(handle);
|
|
if (rv == SECSuccess) {
|
|
handle->global_salt = GetKeyDBGlobalSalt(handle);
|
|
}
|
|
}
|
|
if (rv != SECSuccess) {
|
|
errors++;
|
|
}
|
|
|
|
done:
|
|
/* sync the database */
|
|
(void)keydb_Sync(handle, 0);
|
|
db_InitComplete(handle->db);
|
|
|
|
return (errors == 0 ? SECSuccess : SECFailure);
|
|
}
|
|
|
|
static int
|
|
keydb_Get(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
|
|
{
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (*db->get)(db, key, data, flags);
|
|
|
|
(void)PZ_Unlock(kdbLock);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Put(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
|
|
{
|
|
int ret = 0;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (*db->put)(db, key, data, flags);
|
|
|
|
(void)PZ_Unlock(kdbLock);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Sync(NSSLOWKEYDBHandle *kdb, unsigned int flags)
|
|
{
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (*db->sync)(db, flags);
|
|
|
|
(void)PZ_Unlock(kdbLock);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Del(NSSLOWKEYDBHandle *kdb, DBT *key, unsigned int flags)
|
|
{
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (*db->del)(db, key, flags);
|
|
|
|
(void)PZ_Unlock(kdbLock);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Seq(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
|
|
{
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (*db->seq)(db, key, data, flags);
|
|
|
|
(void)PZ_Unlock(kdbLock);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static void
|
|
keydb_Close(NSSLOWKEYDBHandle *kdb)
|
|
{
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
SKIP_AFTER_FORK(PZ_Lock(kdbLock));
|
|
|
|
(*db->close)(db);
|
|
|
|
SKIP_AFTER_FORK(PZ_Unlock(kdbLock));
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* SDB Entry Points for the Key DB
|
|
*/
|
|
|
|
CK_RV
|
|
lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
|
|
{
|
|
NSSLOWKEYDBHandle *keydb;
|
|
NSSLOWKEYPasswordEntry entry;
|
|
SECStatus rv;
|
|
|
|
keydb = lg_getKeyDB(sdb);
|
|
if (keydb == NULL) {
|
|
return CKR_TOKEN_WRITE_PROTECTED;
|
|
}
|
|
if (PORT_Strcmp(id, "password") != 0) {
|
|
/* shouldn't happen */
|
|
return CKR_GENERAL_ERROR; /* no extra data stored */
|
|
}
|
|
rv = nsslowkey_GetPWCheckEntry(keydb, &entry);
|
|
if (rv != SECSuccess) {
|
|
return CKR_GENERAL_ERROR;
|
|
}
|
|
item1->len = entry.salt.len;
|
|
PORT_Memcpy(item1->data, entry.salt.data, item1->len);
|
|
item2->len = entry.value.len;
|
|
PORT_Memcpy(item2->data, entry.value.data, item2->len);
|
|
return CKR_OK;
|
|
}
|
|
|
|
CK_RV
|
|
lg_PutMetaData(SDB *sdb, const char *id,
|
|
const SECItem *item1, const SECItem *item2)
|
|
{
|
|
NSSLOWKEYDBHandle *keydb;
|
|
NSSLOWKEYPasswordEntry entry;
|
|
SECStatus rv;
|
|
|
|
keydb = lg_getKeyDB(sdb);
|
|
if (keydb == NULL) {
|
|
return CKR_TOKEN_WRITE_PROTECTED;
|
|
}
|
|
if (PORT_Strcmp(id, "password") != 0) {
|
|
/* shouldn't happen */
|
|
return CKR_GENERAL_ERROR; /* no extra data stored */
|
|
}
|
|
entry.salt = *item1;
|
|
entry.value = *item2;
|
|
rv = nsslowkey_PutPWCheckEntry(keydb, &entry);
|
|
if (rv != SECSuccess) {
|
|
return CKR_GENERAL_ERROR;
|
|
}
|
|
return CKR_OK;
|
|
}
|
|
|
|
CK_RV
|
|
lg_DestroyMetaData(SDB *db, const char *id)
|
|
{
|
|
return CKR_GENERAL_ERROR; /* no extra data stored */
|
|
}
|
|
|
|
CK_RV
|
|
lg_Reset(SDB *sdb)
|
|
{
|
|
NSSLOWKEYDBHandle *keydb;
|
|
SECStatus rv;
|
|
|
|
keydb = lg_getKeyDB(sdb);
|
|
if (keydb == NULL) {
|
|
return CKR_TOKEN_WRITE_PROTECTED;
|
|
}
|
|
rv = nsslowkey_ResetKeyDB(keydb);
|
|
if (rv != SECSuccess) {
|
|
return CKR_GENERAL_ERROR;
|
|
}
|
|
return CKR_OK;
|
|
}
|