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