diff --git a/Makefile.in b/Makefile.in index 199675b4..069818a5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -15,6 +15,7 @@ pgtde_is_encrypted \ multi_insert \ trigger_on_view \ insert_update_delete \ +keyprovider_dependency \ vault_v2_test TAP_TESTS = 1 diff --git a/expected/keyprovider_dependency.out b/expected/keyprovider_dependency.out new file mode 100644 index 00000000..9d641c7c --- /dev/null +++ b/expected/keyprovider_dependency.out @@ -0,0 +1,33 @@ +CREATE EXTENSION pg_tde; +SELECT pg_tde_add_key_provider_file('mk-file-valut','/tmp/pg_tde_test_keyring.per'); + pg_tde_add_key_provider_file +------------------------------ + 1 +(1 row) + +SELECT pg_tde_add_key_provider_file('free-file-valut','/tmp/pg_tde_test_keyring_2.per'); + pg_tde_add_key_provider_file +------------------------------ + 2 +(1 row) + +SELECT pg_tde_add_key_provider_vault_v2('V2-Wallet','vault-token','percona.com/vault-v2/percona','/mount/dev','ca-cert-auth'); + pg_tde_add_key_provider_vault_v2 +---------------------------------- + 3 +(1 row) + +SELECT pg_tde_set_master_key('test-db-master-key','mk-file-valut'); + pg_tde_set_master_key +----------------------- + +(1 row) + +-- Try dropping the in-use key provider +DELETE FROM percona_tde.pg_tde_key_provider WHERE provider_name = 'mk-file-valut'; -- Should fail +ERROR: Key provider "mk-file-valut" cannot be deleted +DETAIL: The master key for the database depends on this key provider. +-- Now delete the un-used key provider +DELETE FROM percona_tde.pg_tde_key_provider WHERE provider_name = 'free-file-valut'; -- Should pass +DELETE FROM percona_tde.pg_tde_key_provider WHERE provider_name = 'V2-Wallet'; -- Should pass +DROP EXTENSION pg_tde; diff --git a/meson.build b/meson.build index 28ec3bbb..1cbda860 100644 --- a/meson.build +++ b/meson.build @@ -73,6 +73,7 @@ tests += { 'update_compare_indexes', 'pgtde_is_encrypted', 'multi_insert', + 'keyprovider_dependency', 'trigger_on_view', 'insert_update_delete', 'vault_v2_test', diff --git a/pg_tde--1.0.sql b/pg_tde--1.0.sql index aecc5b69..1048b8af 100644 --- a/pg_tde--1.0.sql +++ b/pg_tde--1.0.sql @@ -16,6 +16,18 @@ CREATE TABLE percona_tde.pg_tde_key_provider(provider_id SERIAL, -- in include/catalog/tde_keyring.h and src/catalog/tde_keyring.c files. SELECT pg_catalog.pg_extension_config_dump('percona_tde.pg_tde_key_provider', ''); + +-- Trigger function to check master key dependency on key provider row +CREATE FUNCTION keyring_delete_dependency_check_trigger() +RETURNS TRIGGER +AS 'MODULE_PATHNAME' +LANGUAGE C; + +CREATE TRIGGER pg_tde_key_provider_delete_dependency_check_trigger +BEFORE DELETE ON percona_tde.pg_tde_key_provider +FOR EACH ROW +EXECUTE FUNCTION keyring_delete_dependency_check_trigger(); + -- Key Provider Management CREATE OR REPLACE FUNCTION pg_tde_add_key_provider(provider_type VARCHAR(10), provider_name VARCHAR(128), options JSON) diff --git a/sql/keyprovider_dependency.sql b/sql/keyprovider_dependency.sql new file mode 100644 index 00000000..61354977 --- /dev/null +++ b/sql/keyprovider_dependency.sql @@ -0,0 +1,15 @@ +CREATE EXTENSION pg_tde; + +SELECT pg_tde_add_key_provider_file('mk-file-valut','/tmp/pg_tde_test_keyring.per'); +SELECT pg_tde_add_key_provider_file('free-file-valut','/tmp/pg_tde_test_keyring_2.per'); +SELECT pg_tde_add_key_provider_vault_v2('V2-Wallet','vault-token','percona.com/vault-v2/percona','/mount/dev','ca-cert-auth'); + +SELECT pg_tde_set_master_key('test-db-master-key','mk-file-valut'); + +-- Try dropping the in-use key provider +DELETE FROM percona_tde.pg_tde_key_provider WHERE provider_name = 'mk-file-valut'; -- Should fail +-- Now delete the un-used key provider +DELETE FROM percona_tde.pg_tde_key_provider WHERE provider_name = 'free-file-valut'; -- Should pass +DELETE FROM percona_tde.pg_tde_key_provider WHERE provider_name = 'V2-Wallet'; -- Should pass + +DROP EXTENSION pg_tde; diff --git a/sql/vault_v2_test.sql b/sql/vault_v2_test.sql index a2be5af5..e9039aee 100644 --- a/sql/vault_v2_test.sql +++ b/sql/vault_v2_test.sql @@ -1,7 +1,7 @@ CREATE EXTENSION pg_tde; \getenv root_token ROOT_TOKEN -SELECT pg_tde_add_key_provider_vault_v2('vault-v2',:'root_token','http://127.0.0.1:8200','secret',NULL); +SELECT pg_tde_add_key_provider_vault_v2('vault-v2','root_token','http://127.0.0.1:8200','secret',NULL); SELECT pg_tde_set_master_key('vault-v2-master-key','vault-v2'); CREATE TABLE test_enc( diff --git a/src/catalog/tde_keyring.c b/src/catalog/tde_keyring.c index 0520b9fc..9f934778 100644 --- a/src/catalog/tde_keyring.c +++ b/src/catalog/tde_keyring.c @@ -11,6 +11,7 @@ */ #include "catalog/tde_keyring.h" +#include "catalog/tde_master_key.h" #include "access/skey.h" #include "access/relscan.h" #include "utils/builtins.h" @@ -21,6 +22,10 @@ #include "access/heapam.h" #include "utils/snapmgr.h" #include "utils/fmgroids.h" +#include "executor/spi.h" +#include "fmgr.h" + +PG_FUNCTION_INFO_V1(keyring_delete_dependency_check_trigger); /* Must match the catalog table definition */ #define PG_TDE_KEY_PROVIDER_ID_ATTRNUM 1 @@ -264,6 +269,73 @@ debug_print_kerying(GenericKeyring *keyring) } } +/* + * Trigger function on keyring catalog to ensure the keyring + * used by the master key should not get deleted. + */ +Datum +keyring_delete_dependency_check_trigger(PG_FUNCTION_ARGS) +{ + TriggerData *trig_data = (TriggerData *)fcinfo->context; + Oid master_key_keyring_id; + + if (!CALLED_AS_TRIGGER(fcinfo)) + { + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("keyring dependency check trigger: not fired by trigger manager"))); + } + + if (!TRIGGER_FIRED_BEFORE(trig_data->tg_event)) + { + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("keyring dependency check trigger: trigger should be fired before delete"))); + } + master_key_keyring_id = GetMasterKeyProviderId(); + if (master_key_keyring_id == InvalidOid) + { + /* No master key set. We are good to delete anything */ + return PointerGetDatum(trig_data->tg_trigtuple); + } + + if (TRIGGER_FIRED_BY_DELETE(trig_data->tg_event)) + { + HeapTuple oldtuple = trig_data->tg_trigtuple; + + if (oldtuple != NULL && SPI_connect() == SPI_OK_CONNECT) + { + Datum datum; + bool isnull; + Oid provider_id; + datum = heap_getattr(oldtuple, PG_TDE_KEY_PROVIDER_ID_ATTRNUM, trig_data->tg_relation->rd_att, &isnull); + provider_id = DatumGetInt32(datum); + if (provider_id == master_key_keyring_id) + { + char *keyring_name; + datum = heap_getattr(oldtuple, PG_TDE_KEY_PROVIDER_NAME_ATTRNUM, trig_data->tg_relation->rd_att, &isnull); + keyring_name = TextDatumGetCString(datum); + + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("Key provider \"%s\" cannot be deleted", keyring_name), + errdetail("The master key for the database depends on this key provider."))); + SPI_finish(); + trig_data->tg_trigtuple = NULL; + return PointerGetDatum(NULL); + } + SPI_finish(); + } + } + else + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("keyring_delete_dependency_check_trigger: unsupported event type"))); + + /* Indicate that the operation should proceed */ + return PointerGetDatum(trig_data->tg_trigtuple); +} + /* Testing function */ PG_FUNCTION_INFO_V1(pg_tde_get_keyprovider); Datum pg_tde_get_keyprovider(PG_FUNCTION_ARGS); diff --git a/src/catalog/tde_master_key.c b/src/catalog/tde_master_key.c index e55514be..76c0e20f 100644 --- a/src/catalog/tde_master_key.c +++ b/src/catalog/tde_master_key.c @@ -444,6 +444,31 @@ SetMasterKey(const char *key_name, const char *provider_name) return set_master_key_with_keyring(key_name, keyring); } +/* + * Returns the provider ID of the keyring that holds the master key + * Return InvalidOid if the master key is not set for the database + */ +Oid +GetMasterKeyProviderId(void) +{ + TDEMasterKey *masterKey = NULL; + TDEMasterKeyInfo *masterKeyInfo = NULL; + Oid keyringId = InvalidOid; + + masterKey = get_master_key_from_cache(true); + if (masterKey) + return masterKey->keyringId; + + /* Master key not present in cache. Try Loading it from the info file */ + masterKeyInfo = get_master_key_info(); + if (masterKeyInfo) + { + keyringId = masterKeyInfo->keyringId; + pfree(masterKeyInfo); + } + return keyringId; +} + /* * ------------------------------ * Master key cache realted stuff diff --git a/src/include/catalog/tde_master_key.h b/src/include/catalog/tde_master_key.h index 511e63cd..357746ef 100644 --- a/src/include/catalog/tde_master_key.h +++ b/src/include/catalog/tde_master_key.h @@ -42,6 +42,7 @@ typedef struct TDEMasterKeyInfo extern void InitializeMasterKeyInfo(void); extern TDEMasterKey* GetMasterKey(void); -TDEMasterKey* SetMasterKey(const char* key_name, const char* provider_name); +extern TDEMasterKey* SetMasterKey(const char* key_name, const char* provider_name); +extern Oid GetMasterKeyProviderId(void); #endif /*TDE_MASTER_KEY_H*/