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 + -
显示快捷键?