Skip to content

Commit 179f5d7

Browse files
committed
PG-1257 Add function for principal key removal
Add SQL function that allow user to remove principal key. Principal key can be removed if there are no encrypted tables or if there is default key. For the first case we just drop key map file completely, for the second we perfom key rotation.
1 parent 69f7a38 commit 179f5d7

File tree

8 files changed

+301
-0
lines changed

8 files changed

+301
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
CREATE EXTENSION IF NOT EXISTS pg_tde;
2+
SELECT pg_tde_add_global_key_provider_file('file-vault','/tmp/pg_tde_test_keyring.per');
3+
pg_tde_add_global_key_provider_file
4+
-------------------------------------
5+
-1
6+
(1 row)
7+
8+
-- Set the local key and delete it without any encrypted tables
9+
-- Should succeed, nothing used the key
10+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
11+
pg_tde_set_key_using_global_key_provider
12+
------------------------------------------
13+
14+
(1 row)
15+
16+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
17+
key_provider_id | key_provider_name | key_name
18+
-----------------+-------------------+-------------
19+
-1 | file-vault | test-db-key
20+
(1 row)
21+
22+
SELECT pg_tde_delete_key();
23+
pg_tde_delete_key
24+
-------------------
25+
26+
(1 row)
27+
28+
-- Set local key, encrypt a table, and delete the key
29+
-- Should fail, the is no default key to fallback
30+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
31+
pg_tde_set_key_using_global_key_provider
32+
------------------------------------------
33+
34+
(1 row)
35+
36+
CREATE TABLE test_table (id int, data text) USING tde_heap;
37+
SELECT pg_tde_delete_key();
38+
ERROR: Cannot delete principal key, there are encrypted tables and default principal key is not set
39+
HINT: Use set_key interface to set the default principal key or decrypt all tables before deleting the principal key
40+
-- Decrypt the table and delete the key
41+
-- Should succeed, there is no more encrypted tables
42+
ALTER TABLE test_table SET ACCESS METHOD heap;
43+
SELECT pg_tde_delete_key();
44+
pg_tde_delete_key
45+
-------------------
46+
47+
(1 row)
48+
49+
-- Set local key, encrypt the table then delete teable and key
50+
-- Should succeed, the table is deleted and there are no more encrypted tables
51+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
52+
pg_tde_set_key_using_global_key_provider
53+
------------------------------------------
54+
55+
(1 row)
56+
57+
ALTER TABLE test_table SET ACCESS METHOD tde_heap;
58+
DROP TABLE test_table;
59+
SELECT pg_tde_delete_key();
60+
pg_tde_delete_key
61+
-------------------
62+
63+
(1 row)
64+
65+
-- Set default key, set regular key, create table, delete regular key
66+
-- Should succeed, regular key will be rotated to default key
67+
SELECT pg_tde_set_default_key_using_global_key_provider('defalut-key','file-vault');
68+
pg_tde_set_default_key_using_global_key_provider
69+
--------------------------------------------------
70+
71+
(1 row)
72+
73+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
74+
pg_tde_set_key_using_global_key_provider
75+
------------------------------------------
76+
77+
(1 row)
78+
79+
CREATE TABLE test_table (id int, data text) USING tde_heap;
80+
SELECT pg_tde_delete_key();
81+
pg_tde_delete_key
82+
-------------------
83+
84+
(1 row)
85+
86+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
87+
key_provider_id | key_provider_name | key_name
88+
-----------------+-------------------+-------------
89+
-1 | file-vault | defalut-key
90+
(1 row)
91+
92+
-- Try to delete key when default key is used
93+
-- Should fail, table already uses the default key, so there is no key to fallback to
94+
SELECT pg_tde_delete_key();
95+
ERROR: Cannot delete principal key, there are encrypted tables and default principal key used for the database
96+
DROP TABLE test_table;
97+
DROP EXTENSION pg_tde;

contrib/pg_tde/pg_tde--1.0-rc.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ RETURNS VOID
454454
LANGUAGE C
455455
AS 'MODULE_PATHNAME';
456456

457+
CREATE FUNCTION pg_tde_delete_key()
458+
RETURNS VOID
459+
LANGUAGE C
460+
AS 'MODULE_PATHNAME';
461+
457462
CREATE FUNCTION pg_tde_key_info()
458463
RETURNS TABLE ( key_name TEXT,
459464
key_provider_name TEXT,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
CREATE EXTENSION IF NOT EXISTS pg_tde;
2+
3+
SELECT pg_tde_add_global_key_provider_file('file-vault','/tmp/pg_tde_test_keyring.per');
4+
5+
-- Set the local key and delete it without any encrypted tables
6+
-- Should succeed, nothing used the key
7+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
8+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
9+
SELECT pg_tde_delete_key();
10+
11+
-- Set local key, encrypt a table, and delete the key
12+
-- Should fail, the is no default key to fallback
13+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
14+
CREATE TABLE test_table (id int, data text) USING tde_heap;
15+
SELECT pg_tde_delete_key();
16+
17+
-- Decrypt the table and delete the key
18+
-- Should succeed, there is no more encrypted tables
19+
ALTER TABLE test_table SET ACCESS METHOD heap;
20+
SELECT pg_tde_delete_key();
21+
22+
-- Set local key, encrypt the table then delete teable and key
23+
-- Should succeed, the table is deleted and there are no more encrypted tables
24+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
25+
ALTER TABLE test_table SET ACCESS METHOD tde_heap;
26+
DROP TABLE test_table;
27+
SELECT pg_tde_delete_key();
28+
29+
-- Set default key, set regular key, create table, delete regular key
30+
-- Should succeed, regular key will be rotated to default key
31+
SELECT pg_tde_set_default_key_using_global_key_provider('defalut-key','file-vault');
32+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
33+
CREATE TABLE test_table (id int, data text) USING tde_heap;
34+
SELECT pg_tde_delete_key();
35+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
36+
37+
-- Try to delete key when default key is used
38+
-- Should fail, table already uses the default key, so there is no key to fallback to
39+
SELECT pg_tde_delete_key();
40+
41+
DROP TABLE test_table;
42+
DROP EXTENSION pg_tde;

contrib/pg_tde/src/access/pg_tde_tdemap.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,87 @@ pg_tde_perform_rotate_key(TDEPrincipalKey *principal_key, TDEPrincipalKey *new_p
544544
}
545545
}
546546

547+
/*
548+
* Checks if the key map file for the given database has any non-empty entries.
549+
*
550+
* The caller must hold the lock on the files before calling this function.
551+
*/
552+
bool
553+
pg_tde_is_key_map_empty(Oid dbOid)
554+
{
555+
char path[MAXPGPATH];
556+
off_t pos;
557+
int fd;
558+
559+
pg_tde_set_db_file_path(dbOid, path);
560+
561+
fd = pg_tde_open_file_read(path, false, &pos);
562+
563+
while (1)
564+
{
565+
TDEMapEntry map_entry;
566+
567+
if (!pg_tde_read_one_map_entry(fd, &map_entry, &pos))
568+
break;
569+
570+
if (map_entry.type != MAP_ENTRY_EMPTY)
571+
{
572+
close(fd);
573+
return false;
574+
}
575+
}
576+
577+
close(fd);
578+
return true;
579+
}
580+
581+
582+
void
583+
pg_tde_delete_principal_key_redo(Oid dbOid)
584+
{
585+
LWLockAcquire(tde_lwlock_enc_keys(), LW_EXCLUSIVE);
586+
587+
/* This is called during recovery, so no XLog records allowed */
588+
pg_tde_delete_principal_key(dbOid, false);
589+
590+
LWLockRelease(tde_lwlock_enc_keys());
591+
}
592+
593+
/*
594+
* Deletes the principal key for the database. This fucntion checks if key map
595+
* file has any entries, and if not, it removes the file. Otherwise raises an error.
596+
*
597+
* The caller must hold an exclusive lock on the files before calling this function.
598+
*/
599+
void
600+
pg_tde_delete_principal_key(Oid dbOid, bool write_xlog)
601+
{
602+
char path[MAXPGPATH];
603+
604+
pg_tde_set_db_file_path(dbOid, path);
605+
606+
/*
607+
* Check that there aro no local keys in the map file, i.e. nothing is
608+
* encrypted with the principal key.
609+
*/
610+
if (!pg_tde_is_key_map_empty(dbOid))
611+
{
612+
ereport(ERROR,
613+
errcode(ERRCODE_INTERNAL_ERROR),
614+
errmsg("could not delete key, key map file is not empty"));
615+
}
616+
617+
if (write_xlog)
618+
{
619+
XLogBeginInsert();
620+
XLogRegisterData((char *) &dbOid, sizeof(Oid));
621+
XLogInsert(RM_TDERMGR_ID, XLOG_TDE_DELETE_PRINCIPAL_KEY);
622+
}
623+
624+
/* Remove whole key map file */
625+
durable_unlink(path, ERROR);
626+
}
627+
547628
/*
548629
* It's called by seg_write inside crit section so no pallocs, hence
549630
* needs keyfile_path

contrib/pg_tde/src/access/pg_tde_xlog.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ tdeheap_rmgr_redo(XLogReaderState *record)
6767

6868
xl_tde_perform_rotate_key(xlrec);
6969
}
70+
else if (info == XLOG_TDE_DELETE_PRINCIPAL_KEY)
71+
{
72+
Oid dbOid = *((Oid *) XLogRecGetData(record));
73+
74+
pg_tde_delete_principal_key_redo(dbOid);
75+
}
7076
else if (info == XLOG_TDE_WRITE_KEY_PROVIDER)
7177
{
7278
KeyringProviderRecordInFile *xlrec = (KeyringProviderRecordInFile *) XLogRecGetData(record);

contrib/pg_tde/src/catalog/tde_principal_key.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#ifndef FRONTEND
3636
#include "access/genam.h"
3737
#include "access/table.h"
38+
#include "access/tableam.h"
39+
#include "access/heapam.h"
3840
#include "common/pg_tde_shmem.h"
3941
#include "funcapi.h"
4042
#include "lib/dshash.h"
@@ -101,11 +103,13 @@ static void set_principal_key_with_keyring(const char *key_name,
101103
Oid dbOid,
102104
bool ensure_new_key);
103105
static bool pg_tde_verify_principal_key_internal(Oid databaseOid);
106+
static void pg_tde_rotate_default_key_for_database(TDEPrincipalKey *oldKey, TDEPrincipalKey *newKeyTemplate);
104107

105108
PG_FUNCTION_INFO_V1(pg_tde_set_default_key_using_global_key_provider);
106109
PG_FUNCTION_INFO_V1(pg_tde_set_key_using_database_key_provider);
107110
PG_FUNCTION_INFO_V1(pg_tde_set_key_using_global_key_provider);
108111
PG_FUNCTION_INFO_V1(pg_tde_set_server_key_using_global_key_provider);
112+
PG_FUNCTION_INFO_V1(pg_tde_delete_key);
109113

110114
static void pg_tde_set_principal_key_internal(Oid providerOid, Oid dbOid, const char *principal_key_name, const char *provider_name, bool ensure_new_key);
111115

@@ -594,6 +598,68 @@ pg_tde_set_principal_key_internal(Oid providerOid, Oid dbOid, const char *key_na
594598
}
595599
}
596600

601+
602+
/*
603+
* SQL interface to delete principal key.
604+
*
605+
* This operation allowed if there is no any encrypted tables in the database or
606+
* if the default principal key is set for the database. In second case,
607+
* key for database rotated to the default key.
608+
*/
609+
610+
Datum
611+
pg_tde_delete_key(PG_FUNCTION_ARGS)
612+
{
613+
TDEPrincipalKey *principal_key;
614+
TDEPrincipalKey *default_principal_key;
615+
616+
LWLockAcquire(tde_lwlock_enc_keys(), LW_EXCLUSIVE);
617+
618+
principal_key = GetPrincipalKeyNoDefault(MyDatabaseId, LW_EXCLUSIVE);
619+
if (principal_key == NULL)
620+
{
621+
ereport(ERROR,
622+
errmsg("Principal key does not exists for the database"),
623+
errhint("Use set_key interface to set the principal key"));
624+
}
625+
626+
/*
627+
* If database has something encryted, we can try to fallback to the
628+
* default principal key
629+
*/
630+
if (!pg_tde_is_key_map_empty(MyDatabaseId))
631+
{
632+
default_principal_key = GetPrincipalKeyNoDefault(DEFAULT_DATA_TDE_OID, LW_EXCLUSIVE);
633+
if (default_principal_key == NULL)
634+
{
635+
ereport(ERROR,
636+
errmsg("Cannot delete principal key, there are encrypted tables and default principal key is not set"),
637+
errhint("Use set_key interface to set the default principal key or decrypt all tables before deleting the principal key"));
638+
}
639+
640+
/*
641+
* If database already encrypted with default principal key, there is
642+
* nothing to do
643+
*/
644+
if (pg_tde_is_same_principal_key(principal_key, default_principal_key))
645+
{
646+
ereport(ERROR,
647+
errmsg("Cannot delete principal key, there are encrypted tables and default principal key used for the database"));
648+
}
649+
650+
pg_tde_rotate_default_key_for_database(principal_key, default_principal_key);
651+
652+
LWLockRelease(tde_lwlock_enc_keys());
653+
PG_RETURN_VOID();
654+
}
655+
656+
pg_tde_delete_principal_key(MyDatabaseId, true);
657+
clear_principal_key_cache(MyDatabaseId);
658+
659+
LWLockRelease(tde_lwlock_enc_keys());
660+
PG_RETURN_VOID();
661+
}
662+
597663
PG_FUNCTION_INFO_V1(pg_tde_key_info);
598664
Datum
599665
pg_tde_key_info(PG_FUNCTION_ARGS)

contrib/pg_tde/src/include/access/pg_tde_tdemap.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ extern bool pg_tde_verify_principal_key_info(TDESignedPrincipalKeyInfo *signed_k
107107
extern void pg_tde_save_principal_key(const TDEPrincipalKey *principal_key, bool write_xlog);
108108
extern void pg_tde_save_principal_key_redo(const TDESignedPrincipalKeyInfo *signed_key_info);
109109
extern void pg_tde_perform_rotate_key(TDEPrincipalKey *principal_key, TDEPrincipalKey *new_principal_key, bool write_xlog);
110+
extern void pg_tde_delete_principal_key(Oid dbOid, bool write_xlog);
111+
extern void pg_tde_delete_principal_key_redo(Oid dbOid);
112+
extern bool pg_tde_is_key_map_empty(Oid dbOid);
110113

111114
const char *tde_sprint_key(InternalKey *k);
112115

contrib/pg_tde/src/include/access/pg_tde_xlog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define XLOG_TDE_ROTATE_PRINCIPAL_KEY 0x20
1818
#define XLOG_TDE_WRITE_KEY_PROVIDER 0x30
1919
#define XLOG_TDE_INSTALL_EXTENSION 0x40
20+
#define XLOG_TDE_DELETE_PRINCIPAL_KEY 0x50
2021

2122
/* ID 140 is registered for Percona TDE extension: https://wiki.postgresql.org/wiki/CustomWALResourceManagers */
2223
#define RM_TDERMGR_ID 140

0 commit comments

Comments
 (0)