keydb.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 2,311 行 · 第 1/4 页
C
2,311 行
/* * Set up the password checker in the key database. * This is done by encrypting a known plaintext with the user's key. */SECStatusSECKEY_SetKeyDBPasswordAlg(SECKEYKeyDBHandle *handle, SECItem *pwitem, SECOidTag algorithm){ DBT checkkey; SECAlgorithmID *algid = NULL; SECStatus rv = SECFailure; SECKEYDBKey *dbkey = NULL; PLArenaPool *arena; SECItem *key = NULL, *salt = NULL; SECItem *dest = NULL, test_key; if (handle == NULL) { return(SECFailure); } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if ( arena == NULL ) { rv = SECFailure; goto loser; } dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYDBKey)); if ( dbkey == NULL ) { rv = SECFailure; goto loser; } dbkey->arena = arena; /* encrypt key */ checkkey.data = test_key.data = (unsigned char *)KEYDB_PW_CHECK_STRING; checkkey.size = test_key.len = KEYDB_PW_CHECK_LEN; salt = seckey_create_rc4_salt(); if(salt == NULL) { rv = SECFailure; goto loser; } switch(algorithm) { case SEC_OID_RC4: key = seckey_create_rc4_key(pwitem, salt); if(key != NULL) { dest = seckey_rc4_cipher(key, &test_key, PR_TRUE); SECITEM_FreeItem(key, PR_TRUE); } break; default: algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, 1); if(algid != NULL) dest = SEC_PKCS5CipherData(algid, pwitem, &test_key, PR_TRUE, NULL); break; } if(dest != NULL) { rv = SECITEM_CopyItem(arena, &dbkey->salt, salt); if(rv == SECFailure) goto loser; rv = encodePWCheckEntry(arena, &dbkey->derPK, algorithm, dest); if ( rv != SECSuccess ) { goto loser; } rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE); } else { rv = SECFailure; } /* let success fall through */loser: if ( arena != NULL ) { PORT_FreeArena(arena, PR_TRUE); } if ( dest != NULL ) { SECITEM_ZfreeItem(dest, PR_TRUE); } if ( salt != NULL ) { SECITEM_ZfreeItem(salt, PR_TRUE); } return(rv);}/* * check to see if the user has typed the right password */SECStatusSECKEY_CheckKeyDBPassword(SECKEYKeyDBHandle *handle, SECItem *pwitem){ DBT checkkey; SECAlgorithmID *algid = NULL; SECStatus rv = SECFailure; SECKEYDBKey *dbkey = NULL; SECItem *key = NULL; SECItem *dest = NULL; SECOidTag algorithm; SECItem oid; SECItem encstring; PRBool update = PR_FALSE; if (handle == NULL) { goto loser; } checkkey.data = KEYDB_PW_CHECK_STRING; checkkey.size = KEYDB_PW_CHECK_LEN; dbkey = get_dbkey(handle, &checkkey); if ( dbkey == NULL ) { goto loser; } /* build the oid item */ oid.len = dbkey->derPK.data[0]; oid.data = &dbkey->derPK.data[1]; /* make sure entry is the correct length * since we are probably using a block cipher, the block will be * padded, so we may get a bit more than the exact size we need. */ if ( dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 + oid.len ) ) { goto loser; } /* find the algorithm tag */ algorithm = SECOID_FindOIDTag(&oid); /* make a secitem of the encrypted check string */ encstring.len = dbkey->derPK.len - ( oid.len + 1 ); encstring.data = &dbkey->derPK.data[oid.len+1]; switch(algorithm) { case SEC_OID_RC4: key = seckey_create_rc4_key(pwitem, &dbkey->salt); if(key != NULL) { dest = seckey_rc4_cipher(key, &encstring, PR_FALSE); SECITEM_FreeItem(key, PR_TRUE); } break; default: algid = SEC_PKCS5CreateAlgorithmID(algorithm, &dbkey->salt, 1); if(algid != NULL) { /* Decrypt - this function implements a workaround for * a previous coding error. It will decrypt values using * DES rather than 3DES, if the initial try at 3DES * decryption fails. In this case, the update flag is * set to TRUE. This indication is used later to force * an update of the database to "real" 3DES encryption. */ dest = SEC_PKCS5CipherData(algid, pwitem, &encstring, PR_FALSE, &update); SECOID_DestroyAlgorithmID(algid, PR_TRUE); } break; } if(dest == NULL) { goto loser; } if ((dest->len == KEYDB_PW_CHECK_LEN) && (PORT_Memcmp(dest->data, KEYDB_PW_CHECK_STRING, KEYDB_PW_CHECK_LEN) == 0)) { rv = SECSuccess; /* we succeeded */ if ( algorithm == SEC_OID_RC4 ) { /* partially updated database */ SECKEY_UpdateKeyDBPass2(handle, pwitem); } /* Force an update of the password to remove the incorrect DES * encryption (see the note above) */ if (update && (algorithm == SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC)) { /* data base was encoded with DES not triple des, fix it */ SECKEY_UpdateKeyDBPass2(handle,pwitem); } } loser: sec_destroy_dbkey(dbkey); if(dest != NULL) { SECITEM_ZfreeItem(dest, PR_TRUE); } return(rv);}/* * Change the database password and/or algorithm. This internal * routine does not check the old password. That must be done by * the caller. */static SECStatusChangeKeyDBPasswordAlg(SECKEYKeyDBHandle *handle, SECItem *oldpwitem, SECItem *newpwitem, SECOidTag new_algorithm){ SECStatus rv; keyList keylist; keyNode *node = NULL; SECKEYLowPrivateKey *privkey = NULL; char *nickname; DBT newkey; int ret; /* traverse the database, collecting the keys of all records */ keylist.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if ( keylist.arena == NULL ) { PORT_SetError(SEC_ERROR_NO_MEMORY); return(SECFailure); } keylist.head = NULL; /* TNH - TraverseKeys should not be public, since it exposes the underlying DBT data type. */ rv = SECKEY_TraverseKeys(handle, sec_add_key_to_list, (void *)&keylist); if ( rv != SECSuccess ) goto loser; /* walk the list, re-encrypting each entry */ node = keylist.head; while ( node != NULL ) { privkey = seckey_get_private_key(handle, &node->key, &nickname, oldpwitem); if (privkey == NULL) { PORT_SetError(SEC_ERROR_BAD_DATABASE); rv = SECFailure; goto loser; } /* delete the old record */ ret = (* handle->db->del)(handle->db, &node->key, 0); if ( ret ) { PORT_SetError(SEC_ERROR_BAD_DATABASE); rv = SECFailure; goto loser; } /* get the public key, which we use as the database index */ switch (privkey->keyType) { case rsaKey: newkey.data = privkey->u.rsa.modulus.data; newkey.size = privkey->u.rsa.modulus.len; break; case dsaKey: newkey.data = privkey->u.dsa.publicValue.data; newkey.size = privkey->u.dsa.publicValue.len; break; case dhKey: newkey.data = privkey->u.dh.publicValue.data; newkey.size = privkey->u.dh.publicValue.len; break; default: /* XXX We don't do Fortezza. */ return SECFailure; } rv = seckey_put_private_key(handle, &newkey, newpwitem, privkey, nickname, PR_TRUE, new_algorithm); if ( rv != SECSuccess ) { PORT_SetError(SEC_ERROR_BAD_DATABASE); rv = SECFailure; goto loser; } /* next node */ node = node->next; } rv = SECKEY_SetKeyDBPasswordAlg(handle, newpwitem, new_algorithm);loser: /* free the arena */ if ( keylist.arena ) { PORT_FreeArena(keylist.arena, PR_FALSE); } return(rv);}/* * Re-encrypt the entire key database with a new password. * NOTE: The really should create a new database rather than doing it * in place in the original */SECStatusSECKEY_ChangeKeyDBPasswordAlg(SECKEYKeyDBHandle *handle, SECItem *oldpwitem, SECItem *newpwitem, SECOidTag new_algorithm){ SECStatus rv; if (handle == NULL) { PORT_SetError(SEC_ERROR_BAD_DATABASE); rv = SECFailure; goto loser; } rv = SECKEY_CheckKeyDBPassword(handle, oldpwitem); if ( rv != SECSuccess ) { return(SECFailure); /* return rv? */ } rv = ChangeKeyDBPasswordAlg(handle, oldpwitem, newpwitem, new_algorithm);loser: return(rv);} /* * Second pass of updating the key db. This time we have a password. */SECStatusSECKEY_UpdateKeyDBPass2(SECKEYKeyDBHandle *handle, SECItem *pwitem){ SECStatus rv; rv = ChangeKeyDBPasswordAlg(handle, pwitem, pwitem, SECKEY_GetDefaultKeyDBAlg()); return(rv);}/* * currently updates key database from v2 to v3 */SECStatusSECKEY_UpdateKeyDBPass1(SECKEYKeyDBHandle *handle){ SECStatus rv; DBT versionKey; DBT versionData; DBT checkKey; DBT checkData; DBT saltKey; DBT saltData; DBT key; DBT data; SECItem *rc4key = NULL; SECKEYDBKey *dbkey = NULL; SECItem *oldSalt = NULL; int ret; SECItem checkitem; if ( handle->updatedb == NULL ) { return(SECSuccess); } /* * check the version record */ versionKey.data = VERSION_STRING; versionKey.size = sizeof(VERSION_STRING)-1; ret = (* handle->updatedb->get)(handle->updatedb, &versionKey, &versionData, 0 ); if (ret) { /* no version record, so old db never used */ goto done; } if ( ( versionData.size != 1 ) || ( *((unsigned char *)versionData.data) != 2 ) ) { /* corrupt or wrong version number so don't update */ goto done; } saltKey.data = SALT_STRING; saltKey.size = sizeof(SALT_STRING) - 1; ret = (* handle->updatedb->get)(handle->updatedb, &saltKey, &saltData, 0); if ( ret ) { /* no salt in old db, so it is corrupted */ goto done; } oldSalt = decodeKeyDBGlobalSalt(&saltData); if ( oldSalt == NULL ) { /* bad salt in old db, so it is corrupted */ goto done; } /* * look for a pw check entry */ checkKey.data = KEYDB_PW_CHECK_STRING; checkKey.size = KEYDB_PW_CHECK_LEN; rv = (SECStatus)(* handle->updatedb->get)(handle->updatedb, &checkKey, &checkData, 0 ); if (rv) { /* no pw check, so old db never used */ goto done; } dbkey = decode_dbkey(&checkData, 2); if ( dbkey == NULL ) { goto done; } /* put global salt into the new database now */ ret = (* handle->db->put)( handle->db, &saltKey, &saltData, 0); if ( ret ) { goto done; } checkitem = dbkey->derPK; dbkey->derPK.data = NULL; /* format the new pw check entry */ rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem); if ( rv != SECSuccess ) { goto done; } rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE); if ( rv != SECSuccess ) { goto done; } /* free the dbkey */ sec_destroy_dbkey(dbkey); dbkey = NULL; /* now traverse the database */ ret = (* handle->updatedb->seq)(handle->updatedb, &key, &data, R_FIRST); if ( ret ) { goto done; } do { /* skip version record */ if ( data.size > 1 ) { /* skip salt */ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) { if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) { continue; } } /* skip pw check entry */ if ( key.size == checkKey.size ) { if ( PORT_Memcmp(key.data, checkKey.data, key.size) == 0 ) { continue; } } /* keys stored by nickname will have 0 as the last byte of the * db key. Other keys must be stored by modulus. We will not * update those because they are left over from a keygen that * never resulted in a cert. */ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) { continue; } dbkey = decode_dbkey(&data, 2); if ( dbkey == NULL ) { continue; } /* This puts the key into the new database with the same * index (nickname) that it had before. The second pass * of the update will have the password. It will decrypt * and re-encrypt the entries using a new algorithm. */ dbkey->nickname = (char *)key.data; rv = put_dbkey(handle, &key, dbkey, PR_FALSE); dbkey->nickname = NULL; sec_destroy_dbkey(dbkey); } } while ( (* handle->updatedb->seq)(handle->updatedb, &key, &data, R_NEXT) == 0 ); dbkey = NULL;done: /* sync the database */ ret = (* handle->db->sync)(handle->db, 0); (* handle->updatedb->close)(handle->updatedb); handle->updatedb = NULL; if ( rc4key ) { SECITEM_FreeItem(rc4key, PR_TRUE); } if ( oldSalt ) { SECITEM_FreeItem(oldSalt, PR_TRUE); } if ( dbkey ) { sec_destroy_dbkey(dbkey); } return(SECSuccess);}/* * Clear out all the keys in the existing database */SECStatusSECKEY_ResetKeyDB(SECKEYKeyDBHandle *handle){ SECStatus rv; DBT key; DBT data; int ret; int errors = 0; if ( handle->db == NULL ) { return(SECSuccess); } /* now traverse the database */ ret = (* handle->db->seq)(handle->db, &key, &data, R_FIRST); if ( ret ) { goto done; } do { /* delete each entry */ ret = (* handle->db->del)(handle->db, &key, 0); if ( ret ) errors++; } while ( (* handle->db->seq)(handle->db, &key, &data, R_NEXT) == 0 ); rv = makeGlobalVersion(handle); if ( rv != SECSuccess ) { errors++; goto done; } rv = makeGlobalSalt(handle); if ( rv != SECSuccess ) { errors++; goto done; } if (handle->global_salt) { SECITEM_FreeItem(handle->global_salt,PR_TRUE); } handle->global_salt = GetKeyDBGlobalSalt(handle);done: /* sync the database */ ret = (* handle->db->sync)(handle->db, 0); return (errors == 0 ? SECSuccess : SECFailure);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?