Skip to content

Commit

Permalink
Disallow deletion of keyring used by Master key.
Browse files Browse the repository at this point in the history
The commit adds the before-delete trigger on the keyring catalog to ensure that
the key provider used by the master key should not get deleted.
  • Loading branch information
codeforall committed Mar 1, 2024
1 parent 665b580 commit e47fb9a
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 2 deletions.
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pgtde_is_encrypted \
multi_insert \
trigger_on_view \
insert_update_delete \
keyprovider_dependency \
vault_v2_test
TAP_TESTS = 1

Expand Down
33 changes: 33 additions & 0 deletions expected/keyprovider_dependency.out
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ tests += {
'update_compare_indexes',
'pgtde_is_encrypted',
'multi_insert',
'keyprovider_dependency',
'trigger_on_view',
'insert_update_delete',
'vault_v2_test',
Expand Down
12 changes: 12 additions & 0 deletions pg_tde--1.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
15 changes: 15 additions & 0 deletions sql/keyprovider_dependency.sql
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion sql/vault_v2_test.sql
Original file line number Diff line number Diff line change
@@ -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(
Expand Down
72 changes: 72 additions & 0 deletions src/catalog/tde_keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 25 additions & 0 deletions src/catalog/tde_master_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/include/catalog/tde_master_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*/

0 comments on commit e47fb9a

Please sign in to comment.