From 9180179166525bd5b6ac66ddda4c74501cbf1161 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 6 Apr 2021 14:23:49 -0400 Subject: [PATCH] cfe-12-rel_over_cfe-11-gist squash commit --- contrib/bloom/blinsert.c | 3 + src/backend/access/gist/gist.c | 51 ++++---- src/backend/access/gist/gistbuild.c | 12 ++ src/backend/access/hash/hashpage.c | 1 + src/backend/access/heap/rewriteheap.c | 7 ++ src/backend/access/nbtree/nbtree.c | 3 + src/backend/access/nbtree/nbtsort.c | 4 +- src/backend/access/spgist/spginsert.c | 6 + src/backend/access/transam/xlog.c | 2 + src/backend/bootstrap/bootstrap.c | 1 + src/backend/catalog/storage.c | 7 +- src/backend/crypto/Makefile | 1 + src/backend/crypto/bufenc.c (new) | 174 ++++++++++++++++++++++++++ src/backend/postmaster/postmaster.c | 2 + src/backend/storage/buffer/bufmgr.c | 22 +++- src/backend/storage/buffer/localbuf.c | 8 +- src/backend/storage/file/copydir.c | 27 +++- src/backend/storage/file/reinit.c | 2 +- src/backend/storage/page/bufpage.c | 51 +++++++- src/backend/tcop/postgres.c | 2 + src/include/access/gist.h | 5 +- src/include/crypto/bufenc.h (new) | 28 +++++ src/include/storage/bufpage.h | 18 ++- src/include/storage/copydir.h | 2 +- 24 files changed, 392 insertions(+), 47 deletions(-) diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c index d37ceef753..cb04aac963 100644 --- a/contrib/bloom/blinsert.c +++ b/contrib/bloom/blinsert.c @@ -177,7 +177,10 @@ blbuildempty(Relation index) * XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record. Therefore, we need * this even when wal_level=minimal. */ + PageEncryptInplace(metapage, INIT_FORKNUM, RelationIsPermanent(index), + BLOOM_METAPAGE_BLKNO); PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO); + smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO, (char *) metapage, true); log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 5ca5f17db3..5d5f109055 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -503,17 +503,14 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, if (is_build) recptr = !FileEncryptionEnabled ? GistBuildLSN : LSNForEncryption(RelationIsPermanent(rel)); + else if (RelationNeedsWAL(rel)) + recptr = gistXLogSplit(is_leaf, + dist, oldrlink, oldnsn, leftchildbuf, + markfollowright); + else if (FileEncryptionEnabled) + recptr = LSNForEncryption(RelationIsPermanent(rel)); else - { - if (RelationNeedsWAL(rel)) - recptr = gistXLogSplit(is_leaf, - dist, oldrlink, oldnsn, leftchildbuf, - markfollowright); - else if (FileEncryptionEnabled) - recptr = LSNForEncryption(RelationIsPermanent(rel)); - else - recptr = gistGetFakeLSN(rel); - } + recptr = gistGetFakeLSN(rel); for (ptr = dist; ptr; ptr = ptr->next) PageSetLSN(ptr->page, recptr); @@ -573,28 +570,26 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, if (is_build) recptr = !FileEncryptionEnabled ? GistBuildLSN : LSNForEncryption(RelationIsPermanent(rel)); - else + else if (RelationNeedsWAL(rel)) { - if (RelationNeedsWAL(rel)) - { - OffsetNumber ndeloffs = 0, - deloffs[1]; + OffsetNumber ndeloffs = 0, + deloffs[1]; - if (OffsetNumberIsValid(oldoffnum)) - { - deloffs[0] = oldoffnum; - ndeloffs = 1; - } - - recptr = gistXLogUpdate(buffer, - deloffs, ndeloffs, itup, ntup, - leftchildbuf); + if (OffsetNumberIsValid(oldoffnum)) + { + deloffs[0] = oldoffnum; + ndeloffs = 1; } - else if (FileEncryptionEnabled) - recptr = LSNForEncryption(RelationIsPermanent(rel)); - else - recptr = gistGetFakeLSN(rel); + + recptr = gistXLogUpdate(buffer, + deloffs, ndeloffs, itup, ntup, + leftchildbuf); } + else if (FileEncryptionEnabled) + recptr = LSNForEncryption(RelationIsPermanent(rel)); + else + recptr = gistGetFakeLSN(rel); + PageSetLSN(page, recptr); if (newblkno) diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index a8b82483e8..bd3dc96eb9 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -454,6 +454,12 @@ gist_indexsortbuild(GISTBuildState *state) RelationOpenSmgr(state->indexrel); PageSetLSN(pagestate->page, !FileEncryptionEnabled ? GistBuildLSN : LSNForEncryption(RelationIsPermanent(state->indexrel))); + /* Make sure LSNs are vaild, and if encryption, are not constant. */ + Assert(!XLogRecPtrIsInvalid(PageGetLSN(pagestate->page)) && + (!FileEncryptionEnabled || + PageGetLSN(pagestate->page) != GistBuildLSN)); + PageEncryptInplace(pagestate->page, MAIN_FORKNUM, RelationIsPermanent(state->indexrel), + GIST_ROOT_BLKNO); PageSetChecksumInplace(pagestate->page, GIST_ROOT_BLKNO); smgrwrite(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, pagestate->page, true); @@ -577,6 +583,12 @@ gist_indexsortbuild_flush_ready_pages(GISTBuildState *state) PageSetLSN(page, !FileEncryptionEnabled ? GistBuildLSN : LSNForEncryption(RelationIsPermanent(state->indexrel))); + /* Make sure LSNs are vaild, and if encryption, are not constant. */ + Assert(!XLogRecPtrIsInvalid(PageGetLSN(page)) && + (!FileEncryptionEnabled || + PageGetLSN(page) != GistBuildLSN)); + PageEncryptInplace(page, MAIN_FORKNUM, RelationIsPermanent(state->indexrel), + blkno); PageSetChecksumInplace(page, blkno); smgrextend(state->indexrel->rd_smgr, MAIN_FORKNUM, blkno, page, true); diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index 49a9867787..eae9a9f04a 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -1025,6 +1025,7 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks) true); RelationOpenSmgr(rel); + PageEncryptInplace(page, MAIN_FORKNUM, RelationIsPermanent(rel), lastblock); PageSetChecksumInplace(page, lastblock); smgrextend(rel->rd_smgr, MAIN_FORKNUM, lastblock, zerobuf.data, false); diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 1aff62cd42..981b73c101 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -324,6 +324,9 @@ end_heap_rewrite(RewriteState state) state->rs_buffer, true); + PageEncryptInplace(state->rs_buffer, MAIN_FORKNUM, + RelationIsPermanent(state->rs_new_rel), + state->rs_blockno); PageSetChecksumInplace(state->rs_buffer, state->rs_blockno); RelationOpenSmgr(state->rs_new_rel); @@ -697,6 +700,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup) */ RelationOpenSmgr(state->rs_new_rel); + /* XXX Do we need to encrypt a copy of the page here? */ + PageEncryptInplace(page, MAIN_FORKNUM, + RelationIsPermanent(state->rs_old_rel), + state->rs_blockno); PageSetChecksumInplace(page, state->rs_blockno); smgrextend(state->rs_new_rel->rd_smgr, MAIN_FORKNUM, diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 9282c9ea22..2dbb2d3ebc 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -162,7 +162,10 @@ btbuildempty(Relation index) * XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record. Therefore, we need * this even when wal_level=minimal. */ + PageEncryptInplace(metapage, INIT_FORKNUM, RelationIsPermanent(index), + BTREE_METAPAGE); PageSetChecksumInplace(metapage, BTREE_METAPAGE); + smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE, (char *) metapage, true); log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index 2c4d7f6e25..6dbc1b6f25 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -657,13 +657,15 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno) { if (!wstate->btws_zeropage) wstate->btws_zeropage = (Page) palloc0(BLCKSZ); - /* don't set checksum for all-zero page */ + /* don't set checksum or encryption for all-zero page */ smgrextend(wstate->index->rd_smgr, MAIN_FORKNUM, wstate->btws_pages_written++, (char *) wstate->btws_zeropage, true); } + PageEncryptInplace(page, MAIN_FORKNUM, RelationIsPermanent(wstate->index), + blkno); PageSetChecksumInplace(page, blkno); /* diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c index 1af0af7da2..4fd5645c6f 100644 --- a/src/backend/access/spgist/spginsert.c +++ b/src/backend/access/spgist/spginsert.c @@ -168,6 +168,8 @@ spgbuildempty(Relation index) * of their existing content when the corresponding create records are * replayed. */ + PageEncryptInplace(page, INIT_FORKNUM, RelationIsPermanent(index), + SPGIST_METAPAGE_BLKNO); PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO, (char *) page, true); @@ -177,6 +179,8 @@ spgbuildempty(Relation index) /* Likewise for the root page. */ SpGistInitPage(page, SPGIST_LEAF); + PageEncryptInplace(page, INIT_FORKNUM, RelationIsPermanent(index), + SPGIST_ROOT_BLKNO); PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO, (char *) page, true); @@ -186,6 +190,8 @@ spgbuildempty(Relation index) /* Likewise for the null-tuples root page. */ SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS); + PageEncryptInplace(page, INIT_FORKNUM, RelationIsPermanent(index), + SPGIST_NULL_BLKNO); PageSetChecksumInplace(page, SPGIST_NULL_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO, (char *) page, true); diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index dba690bc5e..7ac0ca8b4c 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -45,6 +45,7 @@ #include "common/controldata_utils.h" #include "crypto/kmgr.h" #include "executor/instrument.h" +#include "crypto/bufenc.h" #include "miscadmin.h" #include "pg_trace.h" #include "pgstat.h" @@ -5413,6 +5414,7 @@ BootStrapXLOG(void) WriteControlFile(); BootStrapKmgr(); + InitializeBufferEncryption(); if (terminal_fd != -1) { diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index ce6419ddd6..e8545a72b8 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -29,6 +29,7 @@ #include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "common/link-canary.h" +#include "crypto/bufenc.h" #include "libpq/pqsignal.h" #include "miscadmin.h" #include "nodes/makefuncs.h" diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index cba7a9ada0..7f18020b9f 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -443,8 +443,9 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst, smgrread(src, forkNum, blkno, buf.data); - if (!PageIsVerifiedExtended(page, blkno, - PIV_LOG_WARNING | PIV_REPORT_STAT)) + if (!PageIsVerifiedExtended(page, forkNum, + relpersistence == RELPERSISTENCE_PERMANENT, + blkno, PIV_LOG_WARNING | PIV_REPORT_STAT)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("invalid page in block %u of relation %s", @@ -461,6 +462,8 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst, if (use_wal) log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false); + PageEncryptInplace(page, forkNum, + relpersistence == RELPERSISTENCE_PERMANENT, blkno); PageSetChecksumInplace(page, blkno); /* diff --git a/src/backend/crypto/Makefile b/src/backend/crypto/Makefile index c27362029d..8985a66875 100644 --- a/src/backend/crypto/Makefile +++ b/src/backend/crypto/Makefile @@ -13,6 +13,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = \ + bufenc.o \ kmgr.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/crypto/bufenc.c b/src/backend/crypto/bufenc.c new file mode 100644 index 0000000000..0c70c131e1 --- /dev/null +++ b/src/backend/crypto/bufenc.c @@ -0,0 +1,174 @@ +/*------------------------------------------------------------------------- + * + * bufenc.c + * + * Copyright (c) 2020, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/crypto/bufenc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "miscadmin.h" +#include "lib/stringinfo.h" + +#include "access/gist.h" +#include "access/xlog.h" +#include "crypto/bufenc.h" +#include "storage/bufpage.h" +#include "storage/fd.h" + +/* + * We use the page LSN, page number, and permanent-bit to indicate if a fake + * LSN was used to create a nonce for each page. + */ +#define BUFENC_IV_SIZE 16 + +static unsigned char buf_encryption_iv[BUFENC_IV_SIZE]; + +PgCipherCtx *BufEncCtx = NULL; +PgCipherCtx *BufDecCtx = NULL; + +static void set_buffer_encryption_iv(Page page, BlockNumber blkno, + bool relation_is_permanent); + +void +InitializeBufferEncryption(void) +{ + const CryptoKey *key; + + if (!FileEncryptionEnabled) + return; + + key = KmgrGetKey(KMGR_KEY_ID_REL); + + BufEncCtx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, + (unsigned char *) key->key, + (key->klen), true); + if (!BufEncCtx) + elog(ERROR, "cannot intialize encryption context"); + + BufDecCtx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, + (unsigned char *) key->key, + (key->klen), false); + if (!BufDecCtx) + elog(ERROR, "cannot intialize decryption context"); +} + +/* Encrypt the given page with the relation key */ +void +EncryptPage(Page page, bool relation_is_permanent, BlockNumber blkno) +{ + unsigned char *ptr = (unsigned char *) page + PageEncryptOffset; + bool is_gist_page_or_similar; + + int enclen; + + Assert(BufEncCtx != NULL); + + /* + * Permanent pages have valid LSNs, and non-permanent pages usually have + * invalid (not set) LSNs. (One exception are GiST fake LSNs, see below.) + * However, we need valid ones on all pages for encryption. There are too + * many places that set the page LSN for permanent pages to do the same + * for non-permanent pages, so we just set it here. + * + * Also, while permanent relations get new LSNs every time the page is + * modified, for non-permanent relations do not, so we just update the LSN + * here before it is encrypted. + * + * GiST indexes uses LSNs, which are also stored in NSN fields, to detect + * page splits. Therefore, we allow the GiST code to assign LSNs and we + * don't change them here. + */ + + /* Permanent relations should already have valid LSNs. */ + Assert(!XLogRecPtrIsInvalid(PageGetLSN(page)) || !relation_is_permanent); + + /* + * Check if the page has a special size == GISTPageOpaqueData, a valid + * GIST_PAGE_ID, no invalid GiST flag bits are set, and a valid LSN. This + * is true for all GiST pages, and perhaps a few pages that are not. The + * only downside of guessing wrong is that we might not update the LSN for + * some non-permanent relation page changes, and therefore reuse the IV, + * which seems acceptable. + */ + is_gist_page_or_similar = + (PageGetSpecialSize(page) == MAXALIGN(sizeof(GISTPageOpaqueData)) && + GistPageGetOpaque(page)->gist_page_id == GIST_PAGE_ID && + (GistPageGetOpaque(page)->flags & ~GIST_FLAG_BITMASK) == 0 && + !XLogRecPtrIsInvalid(PageGetLSN(page))); + + if (!relation_is_permanent && !is_gist_page_or_similar) + PageSetLSN(page, LSNForEncryption(relation_is_permanent)); + + set_buffer_encryption_iv(page, blkno, relation_is_permanent); + if (unlikely(!pg_cipher_encrypt(BufEncCtx, PG_CIPHER_AES_CTR, + (const unsigned char *) ptr, /* input */ + SizeOfPageEncryption, + ptr, /* length */ + &enclen, /* resulting length */ + buf_encryption_iv, /* iv */ + BUFENC_IV_SIZE, + NULL, 0))) + elog(ERROR, "cannot encrypt page %u", blkno); + + Assert(enclen == SizeOfPageEncryption); +} + +/* Decrypt the given page with the relation key */ +void +DecryptPage(Page page, bool relation_is_permanent, BlockNumber blkno) +{ + unsigned char *ptr = (unsigned char *) page + PageEncryptOffset; + int enclen; + + Assert(BufDecCtx != NULL); + + set_buffer_encryption_iv(page, blkno, relation_is_permanent); + if (unlikely(!pg_cipher_decrypt(BufDecCtx, PG_CIPHER_AES_CTR, + (const unsigned char *) ptr, /* input */ + SizeOfPageEncryption, + ptr, /* output */ + &enclen, /* resulting length */ + buf_encryption_iv, /* iv */ + BUFENC_IV_SIZE, + NULL, 0))) + elog(ERROR, "cannot decrypt page %u", blkno); + + Assert(enclen == SizeOfPageEncryption); +} + +/* Construct iv for the given page */ +static void +set_buffer_encryption_iv(Page page, BlockNumber blkno, + bool relation_is_permanent) +{ + unsigned char *p = buf_encryption_iv; + + MemSet(buf_encryption_iv, 0, BUFENC_IV_SIZE); + + /* page lsn (8 byte) */ + memcpy(p, &((PageHeader) page)->pd_lsn, sizeof(PageXLogRecPtr)); + p += sizeof(PageXLogRecPtr); + + /* block number (4 byte) */ + memcpy(p, &blkno, sizeof(BlockNumber)); + p += sizeof(BlockNumber); + + /* + * Mark use of fake LSNs in IV so if the real and fake LSN counters + * overlap, the IV will remain unique. XXX Is there a better value? + */ + if (!relation_is_permanent) + *p++ = 0x80; + + /* + * The maximum required counter for AES-CTR is 2048, which fits in the + * last three bytes. + */ +} diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 11dd3eadff..df238c6260 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -100,6 +100,7 @@ #include "common/file_perm.h" #include "common/ip.h" #include "common/string.h" +#include "crypto/bufenc.h" #include "lib/ilist.h" #include "libpq/auth.h" #include "libpq/libpq.h" @@ -1339,6 +1340,7 @@ PostmasterMain(int argc, char *argv[]) RemovePgTempFiles(); InitializeKmgr(); + InitializeBufferEncryption(); if (terminal_fd != -1) close(terminal_fd); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 367fed5e85..7d913e038a 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -37,6 +37,7 @@ #include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/storage.h" +#include "crypto/bufenc.h" #include "executor/instrument.h" #include "lib/binaryheap.h" #include "miscadmin.h" @@ -930,7 +931,9 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, } /* check for garbage data */ - if (!PageIsVerifiedExtended((Page) bufBlock, blockNum, + if (!PageIsVerifiedExtended((Page) bufBlock, forkNum, + relpersistence == RELPERSISTENCE_PERMANENT, + blockNum, PIV_LOG_WARNING | PIV_REPORT_STAT)) { if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages) @@ -2810,12 +2813,24 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln) */ bufBlock = BufHdrGetBlock(buf); + if (FileEncryptionEnabled) + { + /* + * Technically BM_PERMANENT could indicate an init fork, but that's + * okay since forkNum would also tell us not to encrypt init forks. + */ + bufToWrite = PageEncryptCopy((Page) bufBlock, buf->tag.forkNum, + buf_state & BM_PERMANENT, buf->tag.blockNum); + bufToWrite = PageSetChecksumCopy((Page) bufToWrite, buf->tag.blockNum); + } + else + bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum); + /* * Update page checksum if desired. Since we have only shared lock on the * buffer, other processes might be updating hint bits in it, so we must * copy the page to private storage if we do checksumming. */ - bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum); if (track_io_timing) INSTR_TIME_SET_CURRENT(io_start); @@ -3474,6 +3489,9 @@ FlushRelationBuffers(Relation rel) errcallback.previous = error_context_stack; error_context_stack = &errcallback; + /* XXX should we be writing a copy of the page here? */ + PageEncryptInplace(localpage, bufHdr->tag.forkNum, + RelationIsPermanent(rel), bufHdr->tag.blockNum); PageSetChecksumInplace(localpage, bufHdr->tag.blockNum); smgrwrite(rel->rd_smgr, diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea3..1033467885 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -159,7 +159,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, } return bufHdr; } - + #ifdef LBDEBUG fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n", smgr->smgr_rnode.node.relNode, forkNum, blockNum, @@ -217,6 +217,12 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, /* Find smgr relation for buffer */ oreln = smgropen(bufHdr->tag.rnode, MyBackendId); + /* + * Technically BM_PERMANENT could indicate an init fork, but that's + * okay since forkNum would also tell us not to encrypt init forks. + */ + PageEncryptInplace(localpage, bufHdr->tag.forkNum, + buf_state & BM_PERMANENT, bufHdr->tag.blockNum); PageSetChecksumInplace(localpage, bufHdr->tag.blockNum); /* And write... */ diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c index da8b7cbeca..f41387abd3 100644 --- a/src/backend/storage/file/copydir.c +++ b/src/backend/storage/file/copydir.c @@ -22,6 +22,8 @@ #include #include +#include "access/xloginsert.h" +#include "crypto/bufenc.h" #include "miscadmin.h" #include "pgstat.h" #include "storage/copydir.h" @@ -74,7 +76,7 @@ copydir(char *fromdir, char *todir, bool recurse) copydir(fromfile, tofile, true); } else if (S_ISREG(fst.st_mode)) - copy_file(fromfile, tofile); + copy_file(fromfile, tofile, false); } FreeDir(xldir); @@ -124,7 +126,7 @@ copydir(char *fromdir, char *todir, bool recurse) * copy one file */ void -copy_file(char *fromfile, char *tofile) +copy_file(char *fromfile, char *tofile, bool encrypt_init_file) { char *buffer; int srcfd; @@ -132,9 +134,8 @@ copy_file(char *fromfile, char *tofile) int nbytes; off_t offset; off_t flush_offset; - /* Size of copy buffer (read and write requests) */ -#define COPY_BUF_SIZE (8 * BLCKSZ) + int copy_buf_size = (encrypt_init_file) ? BLCKSZ : 8 * BLCKSZ; /* * Size of data flush requests. It seems beneficial on most platforms to @@ -149,7 +150,7 @@ copy_file(char *fromfile, char *tofile) #endif /* Use palloc to ensure we get a maxaligned buffer */ - buffer = palloc(COPY_BUF_SIZE); + buffer = palloc(copy_buf_size); /* * Open the files @@ -187,7 +188,7 @@ copy_file(char *fromfile, char *tofile) } pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ); - nbytes = read(srcfd, buffer, COPY_BUF_SIZE); + nbytes = read(srcfd, buffer, copy_buf_size); pgstat_report_wait_end(); if (nbytes < 0) ereport(ERROR, @@ -195,6 +196,20 @@ copy_file(char *fromfile, char *tofile) errmsg("could not read file \"%s\": %m", fromfile))); if (nbytes == 0) break; + /* + * When we copy an init fork page to be part of an empty unlogged + * relation, its real LSN must be replaced with a fake one, and the + * page encrypted. + */ + if (encrypt_init_file) + { + Page page = (Page) buffer; + + Assert(nbytes == BLCKSZ); + PageSetLSN(page, LSNForEncryption(false)); + PageEncryptInplace(page, MAIN_FORKNUM, false, offset / BLCKSZ); + } + errno = 0; pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE); if ((int) write(dstfd, buffer, nbytes) != nbytes) diff --git a/src/backend/storage/file/reinit.c b/src/backend/storage/file/reinit.c index 40c758d789..9953bb2e1f 100644 --- a/src/backend/storage/file/reinit.c +++ b/src/backend/storage/file/reinit.c @@ -300,7 +300,7 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) /* OK, we're ready to perform the actual copy. */ elog(DEBUG2, "copying %s to %s", srcpath, dstpath); - copy_file(srcpath, dstpath); + copy_file(srcpath, dstpath, true); } FreeDir(dbspace_dir); diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index 5d5989c2f5..28cc9d3b36 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -17,6 +17,7 @@ #include "access/htup_details.h" #include "access/itup.h" #include "access/xlog.h" +#include "crypto/bufenc.h" #include "pgstat.h" #include "storage/checksum.h" #include "utils/memdebug.h" @@ -85,7 +86,8 @@ PageInit(Page page, Size pageSize, Size specialSize) * to pgstat. */ bool -PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags) +PageIsVerifiedExtended(Page page, ForkNumber forknum, bool relation_is_permanent, + BlockNumber blkno, int flags) { PageHeader p = (PageHeader) page; size_t *pagebytes; @@ -108,6 +110,8 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags) checksum_failure = true; } + PageDecryptInplace(page, forknum, relation_is_permanent, blkno); + /* * The following checks don't prove the header is correct, only that * it looks sane enough to allow into the buffer pool. Later usage of @@ -1433,3 +1437,48 @@ PageSetChecksumInplace(Page page, BlockNumber blkno) ((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno); } + +char * +PageEncryptCopy(Page page, ForkNumber forknum, bool relation_is_permanent, + BlockNumber blkno) +{ + static char *pageCopy = NULL; + + /* If we don't need a checksum, just return the passed-in data */ + if (PageIsNew(page) || !PageNeedsToBeEncrypted(forknum)) + return (char *) page; + + /* + * We allocate the copy space once and use it over on each subsequent + * call. The point of palloc'ing here, rather than having a static char + * array, is first to ensure adequate alignment for the checksumming code + * and second to avoid wasting space in processes that never call this. + */ + if (pageCopy == NULL) + pageCopy = MemoryContextAlloc(TopMemoryContext, BLCKSZ); + + memcpy(pageCopy, (char *) page, BLCKSZ); + EncryptPage(pageCopy, relation_is_permanent, blkno); + return pageCopy; +} + +void +PageEncryptInplace(Page page, ForkNumber forknum, bool relation_is_permanent, + BlockNumber blkno) +{ + if (PageIsNew(page) || !PageNeedsToBeEncrypted(forknum)) + return; + + EncryptPage(page, relation_is_permanent, blkno); +} + + +void +PageDecryptInplace(Page page, ForkNumber forknum, bool relation_is_permanent, + BlockNumber blkno) +{ + if (PageIsNew(page) || !PageNeedsToBeEncrypted(forknum)) + return; + + DecryptPage(page, relation_is_permanent, blkno); +} diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index d36ac60da3..d667e6fbdb 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -42,6 +42,7 @@ #include "catalog/pg_type.h" #include "commands/async.h" #include "commands/prepare.h" +#include "crypto/bufenc.h" #include "executor/spi.h" #include "jit/jit.h" #include "libpq/libpq.h" @@ -4039,6 +4040,7 @@ PostgresMain(int argc, char *argv[], if (!IsUnderPostmaster) { InitializeKmgr(); + InitializeBufferEncryption(); if (terminal_fd != -1) close(terminal_fd); diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 4b06575d98..04f11fb369 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -41,7 +41,7 @@ #define GISTNProcs 11 /* - * Page opaque data in a GiST index page. + * Page opaque data flags in a GiST index page. */ #define F_LEAF (1 << 0) /* leaf page */ #define F_DELETED (1 << 1) /* the page has been deleted */ @@ -51,6 +51,9 @@ #define F_HAS_GARBAGE (1 << 4) /* some tuples on the page are dead, * but not deleted yet */ +/* Specifies the bits that can be set in the GiST flags field */ +#define GIST_FLAG_BITMASK 0x1F + /* * NSN (node sequence number) is a special-purpose LSN which is stored on each * index page in GISTPageOpaqueData and updated only during page splits. By diff --git a/src/include/crypto/bufenc.h b/src/include/crypto/bufenc.h new file mode 100644 index 0000000000..cc86ceb821 --- /dev/null +++ b/src/include/crypto/bufenc.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * bufenc.h + * + * Portions Copyright (c) 2021, PostgreSQL Global Development Group + * + * src/include/crypto/bufenc.h + * + *------------------------------------------------------------------------- + */ +#ifndef BUFENC_H +#define BUFENC_H + +#include "storage/bufmgr.h" +#include "crypto/kmgr.h" + +/* Cluster encryption encrypts only main forks */ +#define PageNeedsToBeEncrypted(forknum) \ + (FileEncryptionEnabled && (forknum) == MAIN_FORKNUM) + + +extern void InitializeBufferEncryption(void); +extern void EncryptPage(Page page, bool relation_is_permanent, + BlockNumber blkno); +extern void DecryptPage(Page page, bool relation_is_permanent, + BlockNumber blkno); + +#endif /* BUFENC_H */ diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 359b749f7f..f9d97e7c04 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -15,6 +15,7 @@ #define BUFPAGE_H #include "access/xlogdefs.h" +#include "common/relpath.h" #include "storage/block.h" #include "storage/item.h" #include "storage/off.h" @@ -164,6 +165,8 @@ typedef struct PageHeaderData } PageHeaderData; typedef PageHeaderData *PageHeader; +#define PageEncryptOffset offsetof(PageHeaderData, pd_special) +#define SizeOfPageEncryption (BLCKSZ - PageEncryptOffset) /* * pd_flags contains the following flag bits. Undefined bits are initialized @@ -418,8 +421,8 @@ do { \ ((overwrite) ? PAI_OVERWRITE : 0) | \ ((is_heap) ? PAI_IS_HEAP : 0)) -#define PageIsVerified(page, blkno) \ - PageIsVerifiedExtended(page, blkno, \ +#define PageIsVerified(page, relation_is_permanent, blkno) \ + PageIsVerifiedExtended(page, MAIN_FORKNUM, relation_is_permanent, blkno, \ PIV_LOG_WARNING | PIV_REPORT_STAT) /* @@ -433,7 +436,10 @@ StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)), "BLCKSZ has to be a multiple of sizeof(size_t)"); extern void PageInit(Page page, Size pageSize, Size specialSize); -extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags); +extern bool PageIsVerifiedExtended(Page page, ForkNumber forknum, + bool relation_is_permanent, + BlockNumber blkno, + int flags); extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size, OffsetNumber offsetNumber, int flags); extern Page PageGetTempPage(Page page); @@ -452,5 +458,11 @@ extern bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize); extern char *PageSetChecksumCopy(Page page, BlockNumber blkno); extern void PageSetChecksumInplace(Page page, BlockNumber blkno); +extern char *PageEncryptCopy(Page page, ForkNumber forknum, + bool relation_is_permanent, BlockNumber blkno); +extern void PageEncryptInplace(Page page, ForkNumber forknum, + bool relation_is_permanent, BlockNumber blkno); +extern void PageDecryptInplace(Page page, ForkNumber forknum, + bool relation_is_permanent, BlockNumber blkno); #endif /* BUFPAGE_H */ diff --git a/src/include/storage/copydir.h b/src/include/storage/copydir.h index 2c3936b0da..669c04d581 100644 --- a/src/include/storage/copydir.h +++ b/src/include/storage/copydir.h @@ -14,6 +14,6 @@ #define COPYDIR_H extern void copydir(char *fromdir, char *todir, bool recurse); -extern void copy_file(char *fromfile, char *tofile); +extern void copy_file(char *fromfile, char *tofile, bool encrypt_init_file); #endif /* COPYDIR_H */ -- 2.20.1