| /* 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; |
| 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 = 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_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; |
| } |