8000 Defer decryption of relation keys · percona/postgres@1a04d5d · GitHub
[go: up one dir, main page]

Skip to content

Commit 1a04d5d

Browse files
committed
Defer decryption of relation keys
Don't decrypt relation keys until they're actually needed. In some cases tde_mdopen() is called after the transaction is committed which means that we're not longer able to abort the transaction if we fail when we fetch the principal key. This happens, for example, if dropping an encrypted table. Previously this would cause postmaster to panic if it didn't have access to the principal key.
1 parent 5816caa commit 1a04d5d

File tree

3 files changed

+55
-11
lines changed

3 files changed

+55
-11
lines changed

contrib/pg_tde/src/smgr/pg_tde_smgr.c

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ typedef enum TDEMgrRelationDataEncryptionStatus
1414

1515
/* This is an encrypted relation, and we have the key available. */
1616
RELATION_KEY_AVAILABLE = 1,
17+
18+
/* This is an encrypted relation, but we haven't loaded the key yet. */
19+
RELATION_KEY_NOT_AVAILABLE = 2,
1720
} TDEMgrRelationDataEncryptionStatus;
1821

1922
typedef struct TDESMgrRelationData
@@ -36,6 +39,16 @@ typedef TDESMgrRelationData *TDESMgrRelation;
3639

3740
static void CalcBlockIv(ForkNumber forknum, BlockNumber bn, const unsigned char *base_iv, unsigned char *iv);
3841

42+
static bool
43+
tde_smgr_is_encrypted(const RelFileLocatorBackend *smgr_rlocator)
44+
{
45+
/* Do not try to encrypt/decrypt catalog tables */
46+
if (IsCatalogRelationOid(smgr_rlocator->locator.relNumber))
47+
return false;
48+
49+
return IsSMGRRelationEncrypted(*smgr_rlocator);
50+
}
51+
3952
static InternalKey *
4053
tde_smgr_get_key(const RelFileLocatorBackend *smgr_rlocator)
4154
{
@@ -80,18 +93,26 @@ tde_mdwritev(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
8093
const void **buffers, BlockNumber nblocks, bool skipFsync)
8194
{
8295
TDESMgrRelation tdereln = (TDESMgrRelation) reln;
83-
InternalKey *int_key = &tdereln->relKey;
8496

8597
if (tdereln->encryption_status == RELATION_NOT_ENCRYPTED)
8698
{
8799
mdwritev(reln, forknum, blocknum, buffers, nblocks, skipFsync);
88100
}
89101
else
90102
{
103+
InternalKey *int_key;
91104
unsigned char *local_blocks = palloc(BLCKSZ * (nblocks + 1));
92105
unsigned char *local_blocks_aligned = (unsigned char *) TYPEALIGN(PG_IO_ALIGN_SIZE, local_blocks);
93106
void **local_buffers = palloc_array(void *, nblocks);
94107

108+
if (tdereln->encryption_status == RELATION_KEY_NOT_AVAILABLE)
109+
{
110+
tdereln->relKey = *tde_smgr_get_key(&reln->smgr_rlocator);
111+
tdereln->encryption_status = RELATION_KEY_AVAILABLE;
112+
}
113+
114+
int_key = &tdereln->relKey;
115+
95116
for (int i = 0; i < nblocks; ++i)
96117
{
97118
BlockNumber bn = blocknum + i;
@@ -144,18 +165,26 @@ tde_mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
144165
const void *buffer, bool skipFsync)
145166
{
146167
TDESMgrRelation tdereln = (TDESMgrRelation) reln;
147-
InternalKey *int_key = &tdereln->relKey;
148168

149169
if (tdereln->encryption_status == RELATION_NOT_ENCRYPTED)
150170
{
151171
mdextend(reln, forknum, blocknum, buffer, skipFsync);
152172
}
153173
else
154174
{
175+
InternalKey *int_key;
155176
unsigned char *local_blocks = palloc(BLCKSZ * (1 + 1));
156177
unsigned char *local_blocks_aligned = (unsigned char *) TYPEALIGN(PG_IO_ALIGN_SIZE, local_blocks);
157178
unsigned char iv[16];
158179

180+
if (tdereln->encryption_status == RELATION_KEY_NOT_AVAILABLE)
181+
{
182+
tdereln->relKey = *tde_smgr_get_key(&reln->smgr_rlocator);
183+
tdereln->encryption_status = RELATION_KEY_AVAILABLE;
184+
}
185+
186+
int_key = &tdereln->relKey;
187+
159188
CalcBlockIv(forknum, blocknum, int_key->base_iv, iv);
160189

161190
AesEncrypt(int_key->key, iv, ((unsigned char *) buffer), BLCKSZ, local_blocks_aligned);
@@ -171,12 +200,19 @@ tde_mdreadv(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
171200
void **buffers, BlockNumber nblocks)
172201
{
173202
TDESMgrRelation tdereln = (TDESMgrRelation) reln;
174-
InternalKey *int_key = &tdereln->relKey;
203+
InternalKey *int_key;
175204

176205
mdreadv(reln, forknum, blocknum, buffers, nblocks);
177206

178207
if (tdereln->encryption_status == RELATION_NOT_ENCRYPTED)
179208
return;
209+
else if (tdereln->encryption_status == RELATION_KEY_NOT_AVAILABLE)
210+
{
211+
tdereln->relKey = *tde_smgr_get_key(&reln->smgr_rlocator);
212+
tdereln->encryption_status = RELATION_KEY_AVAILABLE;
213+
}
214+
215+
int_key = &tdereln->relKey;
180216

181217
for (int i = 0; i < nblocks; ++i)
182218
{
@@ -260,21 +296,21 @@ tde_mdcreate(RelFileLocator relold, SMgrRelation reln, ForkNumber forknum, bool
260296

261297
/*
262298
* mdopen() -- Initialize newly-opened relation.
299+
*
300+
* The current transaction might already be commited when this function is
301+
* called, so do not call any code that uses ereport(ERROR) or otherwise tries
302+
* to abort the transaction.
263303
*/
264304
static void
265305
tde_mdopen(SMgrRelation reln)
266306
{
267307
TDESMgrRelation tdereln = (TDESMgrRelation) reln;
268-
InternalKey *key;
269308

270309
mdopen(reln);
271310

272-
key = tde_smgr_get_key(&reln->smgr_rlocator);
273-
274-
if (key)
311+
if (tde_smgr_is_encrypted(&reln->smgr_rlocator))
275312
{
276-
tdereln->encryption_status = RELATION_KEY_AVAILABLE;
277-
tdereln->relKey = *key;
313+
tdereln->encryption_status = RELATION_KEY_NOT_AVAILABLE;
278314
}
279315
else
280316
{

contrib/pg_tde/t/001_basic.pl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
);
2525

2626
PGTDE::psql($node, 'postgres',
27-
"SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/pg_tde_test_keyring.per');"
27+
"SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/pg_tde_test_001_basic.per');"
2828
);
2929

3030
PGTDE::psql($node, 'postgres',
@@ -56,6 +56,12 @@
5656
$strings .= `strings $tablefile | grep foo`;
5757
PGTDE::append_to_result_file($strings);
5858

59+
60+
# An encrypted table can be dropped even if we don't have access to the principal key.
61+
$node->stop;
62+
unlink('/tmp/pg_tde_test_001_basic.per');
63+
$node->start;
64+
PGTDE::psql($node, 'postgres', 'SELECT pg_tde_verify_key()');
5965
PGTDE::psql($node, 'postgres', 'DROP TABLE test_enc;');
6066

6167
PGTDE::psql($node, 'postgres', 'DROP EXTENSION pg_tde;');

contrib/pg_tde/t/expected/001_basic.out

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SELECT extname, extversion FROM pg_extension WHERE extname = 'pg_tde';
88
CREATE TABLE test_enc (id SERIAL, k INTEGER, PRIMARY KEY (id)) USING tde_heap;
99
psql:<stdin>:1: ERROR: principal key not configured
1010
HINT: create one using pg_tde_set_key before using encrypted tables
11-
SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/pg_tde_test_keyring.per');
11+
SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/pg_tde_test_001_basic.per');
1212
pg_tde_add_database_key_provider_file
1313
---------------------------------------
1414
1
@@ -39,5 +39,7 @@ SELECT * FROM test_enc ORDER BY id;
3939

4040
TABLEFILE FOUND: yes
4141
CONTAINS FOO (should be empty):
42+
SELECT pg_tde_verify_key()
43+
psql:<stdin>:1: ERROR: failed to retrieve principal key test-db-key from keyring with ID 1
4244
DROP TABLE test_enc;
4345
DROP EXTENSION pg_tde;

0 commit comments

Comments
 (0)
0