diff --git a/contrib/pg_tde/src/access/pg_tde_tdemap.c b/contrib/pg_tde/src/access/pg_tde_tdemap.c index b0e327eb83622..d54784ce02959 100644 --- a/contrib/pg_tde/src/access/pg_tde_tdemap.c +++ b/contrib/pg_tde/src/access/pg_tde_tdemap.c @@ -696,9 +696,9 @@ pg_tde_count_relations(Oid dbOid) } bool -pg_tde_verify_principal_key_info(TDESignedPrincipalKeyInfo *signed_key_info, const TDEPrincipalKey *principal_key) +pg_tde_verify_principal_key_info(TDESignedPrincipalKeyInfo *signed_key_info, const KeyData *principal_key_data) { - return AesGcmDecrypt(principal_key->keyData, + return AesGcmDecrypt(principal_key_data->data, signed_key_info->sign_iv, MAP_ENTRY_IV_SIZE, (unsigned char *) &signed_key_info->data, sizeof(signed_key_info->data), NULL, 0, diff --git a/contrib/pg_tde/src/catalog/tde_principal_key.c b/contrib/pg_tde/src/catalog/tde_principal_key.c index 516ab7ddb4c00..da4e88cc2133f 100644 --- a/contrib/pg_tde/src/catalog/tde_principal_key.c +++ b/contrib/pg_tde/src/catalog/tde_principal_key.c @@ -677,9 +677,7 @@ pg_tde_get_key_info(PG_FUNCTION_ARGS, Oid dbOid) #endif /* FRONTEND */ /* - * Gets principal key form the keyring. - * - * Caller should hold an exclusive tde_lwlock_enc_keys lock + * Get principal key form the keyring. */ static TDEPrincipalKey * get_principal_key_from_keyring(Oid dbOid) @@ -710,6 +708,12 @@ get_principal_key_from_keyring(Oid dbOid) errmsg("failed to retrieve principal key %s from keyring with ID %d", principalKeyInfo->data.name, principalKeyInfo->data.keyringId)); + if (!pg_tde_verify_principal_key_info(principalKeyInfo, &keyInfo->data)) + ereport(ERROR, + errcode(ERRCODE_DATA_CORRUPTED), + errmsg("Failed to verify principal key header for key %s, incorrect principal key or corrupted key file", + principalKeyInfo->data.name)); + principalKey = palloc_object(TDEPrincipalKey); principalKey->keyInfo = principalKeyInfo->data; @@ -718,12 +722,6 @@ get_principal_key_from_keyring(Oid dbOid) Assert(dbOid == principalKey->keyInfo.databaseId); - if (!pg_tde_verify_principal_key_info(principalKeyInfo, principalKey)) - ereport(ERROR, - errcode(ERRCODE_DATA_CORRUPTED), - errmsg("Failed to verify principal key header for key %s, incorrect principal key or corrupted key file", - principalKeyInfo->data.name)); - pfree(keyInfo); pfree(keyring); pfree(principalKeyInfo); @@ -929,7 +927,7 @@ pg_tde_is_provider_used(Oid databaseOid, Oid providerId) void pg_tde_verify_provider_keys_in_use(GenericKeyring *modified_provider) { - TDEPrincipalKey *existing_principal_key; + TDESignedPrincipalKeyInfo *existing_principal_key; HeapTuple tuple; SysScanDesc scan; Relation rel; @@ -940,11 +938,11 @@ pg_tde_verify_provider_keys_in_use(GenericKeyring *modified_provider) LWLockAcquire(tde_lwlock_enc_keys(), LW_EXCLUSIVE); /* Check the server key that is used for WAL encryption */ - existing_principal_key = GetPrincipalKeyNoDefault(GLOBAL_DATA_TDE_OID, LW_EXCLUSIVE); + existing_principal_key = pg_tde_get_principal_key_info(GLOBAL_DATA_TDE_OID); if (existing_principal_key != NULL && - existing_principal_key->keyInfo.keyringId == modified_provider->keyring_id) + existing_principal_key->data.keyringId == modified_provider->keyring_id) { - char *key_name = existing_principal_key->keyInfo.name; + char *key_name = existing_principal_key->data.name; KeyringReturnCodes return_code; KeyInfo *proposed_key; @@ -956,8 +954,7 @@ pg_tde_verify_provider_keys_in_use(GenericKeyring *modified_provider) key_name, modified_provider->provider_name, return_code)); } - if (proposed_key->data.len != existing_principal_key->keyLength || - memcmp(proposed_key->data.data, existing_principal_key->keyData, proposed_key->data.len) != 0) + if (!pg_tde_verify_principal_key_info(existing_principal_key, &proposed_key->data)) { ereport(ERROR, errmsg("key \"%s\" from modified key provider \"%s\" does not match existing server key", @@ -965,6 +962,9 @@ pg_tde_verify_provider_keys_in_use(GenericKeyring *modified_provider) } } + if (existing_principal_key) + pfree(existing_principal_key); + /* Check all databases for usage of keys from this key provider. */ rel = table_open(DatabaseRelationId, AccessShareLock); scan = systable_beginscan(rel, 0, false, NULL, 0, NULL); @@ -973,11 +973,11 @@ pg_tde_verify_provider_keys_in_use(GenericKeyring *modified_provider) { Form_pg_database database = (Form_pg_database) GETSTRUCT(tuple); - existing_principal_key = GetPrincipalKeyNoDefault(database->oid, LW_EXCLUSIVE); + existing_principal_key = pg_tde_get_principal_key_info(database->oid); if (existing_principal_key != NULL && - existing_principal_key->keyInfo.keyringId == modified_provider->keyring_id) + existing_principal_key->data.keyringId == modified_provider->keyring_id) { - char *key_name = existing_principal_key->keyInfo.name; + char *key_name = existing_principal_key->data.name; KeyringReturnCodes return_code; KeyInfo *proposed_key; @@ -989,14 +989,16 @@ pg_tde_verify_provider_keys_in_use(GenericKeyring *modified_provider) key_name, database->datname.data, modified_provider->provider_name, return_code)); } - if (proposed_key->data.len != existing_principal_key->keyLength || - memcmp(proposed_key->data.data, existing_principal_key->keyData, proposed_key->data.len) != 0) + if (!pg_tde_verify_principal_key_info(existing_principal_key, &proposed_key->data)) { ereport(ERROR, errmsg("key \"%s\" from modified key provider \"%s\" does not match existing key used by database \"%s\"", key_name, modified_provider->provider_name, database->datname.data)); } } + + if (existing_principal_key) + pfree(existing_principal_key); } systable_endscan(scan); table_close(rel, AccessShareLock); diff --git a/contrib/pg_tde/src/include/access/pg_tde_tdemap.h b/contrib/pg_tde/src/include/access/pg_tde_tdemap.h index e19f787af2b1e..471840554093e 100644 --- a/contrib/pg_tde/src/include/access/pg_tde_tdemap.h +++ b/contrib/pg_tde/src/include/access/pg_tde_tdemap.h @@ -103,7 +103,7 @@ extern int pg_tde_count_relations(Oid dbOid); extern void pg_tde_delete_tde_files(Oid dbOid); extern TDESignedPrincipalKeyInfo *pg_tde_get_principal_key_info(Oid dbOid); -extern bool pg_tde_verify_principal_key_info(TDESignedPrincipalKeyInfo *signed_key_info, const TDEPrincipalKey *principal_key); +extern bool pg_tde_verify_principal_key_info(TDESignedPrincipalKeyInfo *signed_key_info, const KeyData *principal_key_data); extern void pg_tde_save_principal_key(const TDEPrincipalKey *principal_key, bool write_xlog); extern void pg_tde_save_principal_key_redo(const TDESignedPrincipalKeyInfo *signed_key_info); extern void pg_tde_perform_rotate_key(TDEPrincipalKey *principal_key, TDEPrincipalKey *new_principal_key, bool write_xlog); diff --git a/contrib/pg_tde/t/change_key_provider.pl b/contrib/pg_tde/t/change_key_provider.pl index edf482c2ba760..4dbc262cf2d95 100644 --- a/contrib/pg_tde/t/change_key_provider.pl +++ b/contrib/pg_tde/t/change_key_provider.pl @@ -12,8 +12,6 @@ unlink('/tmp/change_key_provider_1.per'); unlink('/tmp/change_key_provider_2.per'); -unlink('/tmp/change_key_provider_3.per'); -unlink('/tmp/change_key_provider_4.per'); my $node = PostgreSQL::Test::Cluster->new('main'); $node->init; @@ -26,7 +24,7 @@ "SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/change_key_provider_1.per');" ); PGTDE::psql($node, 'postgres', - "SELECT pg_tde_list_all_database_key_providers();"); + "SELECT * FROM pg_tde_list_all_database_key_providers();"); PGTDE::psql($node, 'postgres', "SELECT pg_tde_set_key_using_database_key_provider('test-key', 'file-vault');" ); @@ -48,7 +46,7 @@ "SELECT pg_tde_change_database_key_provider_file('file-vault', '/tmp/change_key_provider_2.per');" ); PGTDE::psql($node, 'postgres', - "SELECT pg_tde_list_all_database_key_providers();"); + "SELECT * FROM pg_tde_list_all_database_key_providers();"); PGTDE::psql($node, 'postgres', "SELECT pg_tde_verify_key();"); PGTDE::psql($node, 'postgres', "SELECT pg_tde_is_encrypted('test_enc');"); @@ -57,7 +55,28 @@ PGTDE::append_to_result_file("-- server restart"); $node->restart; -# Verify +PGTDE::psql($node, 'postgres', "SELECT pg_tde_verify_key();"); +PGTDE::psql($node, 'postgres', "SELECT pg_tde_is_encrypted('test_enc');"); +PGTDE::psql($node, 'postgres', 'SELECT * FROM test_enc ORDER BY id;'); + +# Move file, restart and then change provider +PGTDE::append_to_result_file( + "-- mv /tmp/change_key_provider_2.per /tmp/change_key_provider_1.per"); +move('/tmp/change_key_provider_2.per', '/tmp/change_key_provider_1.per'); + +PGTDE::append_to_result_file("-- server restart"); +$node->restart; + +PGTDE::psql($node, 'postgres', "SELECT pg_tde_verify_key();"); +PGTDE::psql($node, 'postgres', "SELECT pg_tde_is_encrypted('test_enc');"); +PGTDE::psql($node, 'postgres', 'SELECT * FROM test_enc ORDER BY id;'); + +PGTDE::psql($node, 'postgres', + "SELECT pg_tde_change_database_key_provider_file('file-vault', '/tmp/change_key_provider_1.per');" +); +PGTDE::psql($node, 'postgres', + "SELECT * FROM pg_tde_list_all_database_key_providers();"); + PGTDE::psql($node, 'postgres', "SELECT pg_tde_verify_key();"); PGTDE::psql($node, 'postgres', "SELECT pg_tde_is_encrypted('test_enc');"); PGTDE::psql($node, 'postgres', 'SELECT * FROM test_enc ORDER BY id;'); diff --git a/contrib/pg_tde/t/expected/change_key_provider.out b/contrib/pg_tde/t/expected/change_key_provider.out index 3ebd708be0850..4d912c77936d6 100644 --- a/contrib/pg_tde/t/expected/change_key_provider.out +++ b/contrib/pg_tde/t/expected/change_key_provider.out @@ -5,10 +5,10 @@ SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/change_key_prov (1 row) -SELECT pg_tde_list_all_database_key_providers(); - pg_tde_list_all_database_key_providers ------------------------------------------------------------------------ - (1,file-vault,file,"{""path"" : ""/tmp/change_key_provider_1.per""}") +SELECT * FROM pg_tde_list_all_database_key_providers(); + id | provider_name | provider_type | options +----+---------------+---------------+--------------------------------------------- + 1 | file-vault | file | {"path" : "/tmp/change_key_provider_1.per"} (1 row) SELECT pg_tde_set_key_using_database_key_provider('test-key', 'file-vault'); @@ -45,10 +45,10 @@ SELECT pg_tde_change_database_key_provider_file('file-vault', '/tmp/change_key_p (1 row) -SELECT pg_tde_list_all_database_key_providers(); - pg_tde_list_all_database_key_providers ------------------------------------------------------------------------ - (1,file-vault,file,"{""path"" : ""/tmp/change_key_provider_2.per""}") +SELECT * FROM pg_tde_list_all_database_key_providers(); + id | provider_name | provider_type | options +----+---------------+---------------+--------------------------------------------- + 1 | file-vault | file | {"path" : "/tmp/change_key_provider_2.per"} (1 row) SELECT pg_tde_verify_key(); @@ -90,5 +90,48 @@ SELECT * FROM test_enc ORDER BY id; 2 | 6 (2 rows) +-- mv /tmp/change_key_provider_2.per /tmp/change_key_provider_1.per +-- server restart +SELECT pg_tde_verify_key(); +psql::1: ERROR: failed to retrieve principal key test-key from keyring with ID 1 +SELECT pg_tde_is_encrypted('test_enc'); + pg_tde_is_encrypted +--------------------- + t +(1 row) + +SELECT * FROM test_enc ORDER BY id; +psql::1: ERROR: failed to retrieve principal key test-key from keyring with ID 1 +SELECT pg_tde_change_database_key_provider_file('file-vault', '/tmp/change_key_provider_1.per'); + pg_tde_change_database_key_provider_file +------------------------------------------ + +(1 row) + +SELECT * FROM pg_tde_list_all_database_key_providers(); + id | provider_name | provider_type | options +----+---------------+---------------+--------------------------------------------- + 1 | file-vault | file | {"path" : "/tmp/change_key_provider_1.per"} +(1 row) + +SELECT pg_tde_verify_key(); + pg_tde_verify_key +------------------- + +(1 row) + +SELECT pg_tde_is_encrypted('test_enc'); + pg_tde_is_encrypted +--------------------- + t +(1 row) + +SELECT * FROM test_enc ORDER BY id; + id | k +----+--- + 1 | 5 + 2 | 6 +(2 rows) + DROP EXTENSION pg_tde CASCADE; psql::1: NOTICE: drop cascades to table test_enc