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