mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-11 02:10:17 +01:00
30d33aa8e8
9934c8faef29, 3c3b381c4865, 5a67f6beee9a, 1b1eb6d77728, a8b668fd72f7, bug962760, bug743700, bug857304, bug972653, bug972450, bug971358, bug903885, bug977073, bug976111, bug949939, bug947653, bug947572, bug903885, bug979106, bug966596, bug979004, bug979752, bug980848, bug938369, bug981170, bug668130, bug974693, bug975056, bug979132, bug370717, bug979070, bug985070, bug900067, bug977673, bug519255, bug989558, bug557299, bug987263, bug369802, a751a5146718, bug992343, bug952572, bug979703, bug994883, bug994869, bug993489, bug984608, bug977869, bug667371, bug672828, bug793347, bug977869
2268 lines
52 KiB
C
2268 lines
52 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 */
|
|
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;
|
|
#ifndef NSS_DISABLE_ECC
|
|
case NSSLOWKEYECKey:
|
|
namekey.data = pubkey->u.ec.publicValue.data;
|
|
namekey.size = pubkey->u.ec.publicValue.len;
|
|
break;
|
|
#endif /* NSS_DISABLE_ECC */
|
|
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;
|
|
SECStatus rv;
|
|
|
|
param.salt = *salt;
|
|
param.iter.type = siBuffer; /* encode as signed integer */
|
|
param.iter.data = &one;
|
|
param.iter.len = 1;
|
|
edi.encryptedData = *data;
|
|
|
|
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;
|
|
}
|
|
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;
|
|
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;
|
|
#ifndef NSS_DISABLE_ECC
|
|
SECItem *fordebug = NULL;
|
|
int savelen;
|
|
#endif
|
|
|
|
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;
|
|
#ifndef NSS_DISABLE_ECC
|
|
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;
|
|
}
|
|
|
|
fordebug = &(pki->privateKey);
|
|
SEC_PRINT("seckey_encrypt_private_key()", "PrivateKey",
|
|
pk->keyType, fordebug);
|
|
|
|
break;
|
|
#endif /* NSS_DISABLE_ECC */
|
|
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;
|
|
#ifndef NSS_DISABLE_ECC
|
|
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;
|
|
#ifndef NSS_DISABLE_ECC
|
|
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
|
|
pk->keyType = NSSLOWKEYECKey;
|
|
lg_prepare_low_ec_priv_key_for_asn1(pk);
|
|
|
|
fordebug = &pki->privateKey;
|
|
SEC_PRINT("seckey_decrypt_private_key()", "PrivateKey",
|
|
pk->keyType, fordebug);
|
|
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;
|
|
#endif /* NSS_DISABLE_ECC */
|
|
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;
|
|
SECStatus rv;
|
|
|
|
oidData = SECOID_FindOIDByTag(alg);
|
|
if ( oidData == NULL ) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
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 ) {
|
|
goto loser;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
loser:
|
|
return(SECFailure);
|
|
}
|
|
|
|
|
|
#define MAX_DB_SIZE 0xffff
|
|
/*
|
|
* Clear out all the keys in the existing database
|
|
*/
|
|
static SECStatus
|
|
nsslowkey_ResetKeyDB(NSSLOWKEYDBHandle *handle)
|
|
{
|
|
SECStatus rv;
|
|
int ret;
|
|
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 */
|
|
ret = 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)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (* db->get)(db, key, data, flags);
|
|
|
|
prstat = PZ_Unlock(kdbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Put(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
|
|
{
|
|
PRStatus prstat;
|
|
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);
|
|
|
|
prstat = PZ_Unlock(kdbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Sync(NSSLOWKEYDBHandle *kdb, unsigned int flags)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (* db->sync)(db, flags);
|
|
|
|
prstat = PZ_Unlock(kdbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Del(NSSLOWKEYDBHandle *kdb, DBT *key, unsigned int flags)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (* db->del)(db, key, flags);
|
|
|
|
prstat = PZ_Unlock(kdbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
keydb_Seq(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
PZ_Lock(kdbLock);
|
|
|
|
ret = (* db->seq)(db, key, data, flags);
|
|
|
|
prstat = PZ_Unlock(kdbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static void
|
|
keydb_Close(NSSLOWKEYDBHandle *kdb)
|
|
{
|
|
PRStatus prstat;
|
|
PRLock *kdbLock = kdb->lock;
|
|
DB *db = kdb->db;
|
|
|
|
PORT_Assert(kdbLock != NULL);
|
|
SKIP_AFTER_FORK(PZ_Lock(kdbLock));
|
|
|
|
(* db->close)(db);
|
|
|
|
SKIP_AFTER_FORK(prstat = 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_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;
|
|
}
|
|
|