From 6402e69577a38db617c7489b7467de64b818ef44 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Wed, 30 Oct 2019 16:18:43 +0900 Subject: [PATCH v2 4/5] WAL encryption --- src/backend/access/transam/xlog.c | 85 +++++++++++++++++++------ src/backend/access/transam/xlogreader.c | 19 ++++++ src/backend/access/transam/xlogutils.c | 4 ++ src/backend/replication/walsender.c | 5 ++ src/include/access/xlog_internal.h | 1 + src/include/access/xlogreader.h | 1 + src/include/storage/encryption.h | 7 ++ 7 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index fe7b253df4..c0d4b3bdd3 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -911,6 +911,7 @@ static void WriteControlFile(void); static void ReadControlFile(void); static char *str_time(pg_time_t tnow); static bool CheckForStandbyTrigger(void); +static void XLogWritePages(char *from, Size nbytes, uint32 startoffset); #ifdef WAL_DEBUG static void xlog_outrec(StringInfo buf, XLogReaderState *record); @@ -2487,34 +2488,33 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { char *from; Size nbytes; - Size nleft; - int written; /* OK to write the page(s) */ from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ; nbytes = npages * (Size) XLOG_BLCKSZ; - nleft = nbytes; - do + + if (DataEncryptionEnabled()) { - errno = 0; - pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); - pgstat_report_wait_end(); - if (written <= 0) + int i; + + /* Encrypt xlog pages one by one */ + for (i = 0; i < npages; i++) { - if (errno == EINTR) - continue; - ereport(PANIC, - (errcode_for_file_access(), - errmsg("could not write to log file %s " - "at offset %u, length %zu: %m", - XLogFileNameP(ThisTimeLineID, openLogSegNo), - startoffset, nleft))); + char *buftowrite; + Size nwrite = Min(nbytes, XLOG_BLCKSZ); + + buftowrite = EncryptXLog(from, nwrite, openLogSegNo, + startoffset); + XLogWritePages(buftowrite, nwrite, startoffset); + startoffset += nwrite; + from += XLOG_BLCKSZ; } - nleft -= written; - from += written; - startoffset += written; - } while (nleft > 0); + } + else + { + XLogWritePages(from, npages * (Size) XLOG_BLCKSZ, startoffset); + startoffset += npages * (Size) XLOG_BLCKSZ; + } npages = 0; @@ -2630,6 +2630,39 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) } } +/* + * Write XLOG pages starting from 'startoffset'. + */ +static void +XLogWritePages(char *from, Size nbytes, uint32 startoffset) +{ + Size nleft; + Size nwritten; + + nleft = nbytes; + do + { + errno = 0; + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); + nwritten = pg_pwrite(openLogFile, from, nleft, startoffset); + pgstat_report_wait_end(); + if (nwritten <= 0) + { + if (errno == EINTR) + continue; + ereport(PANIC, + (errcode_for_file_access(), + errmsg("could not write to log file %s " + "at offset %u, length %zu: %m", + XLogFileNameP(ThisTimeLineID, openLogSegNo), + startoffset, nleft))); + } + nleft -= nwritten; + from += nwritten; + startoffset += nwritten; + } while (nleft > 0); +} + /* * Record the LSN for an asynchronous transaction commit/abort * and nudge the WALWriter if there is work for it to do. @@ -11707,6 +11740,16 @@ retry: xlogreader->seg.ws_tli = curFileTLI; + /* + * Decrypt read record so that we can validate page both short header + * and possibly long header. + */ + if (DataEncryptionEnabled()) + { + DecryptXLog(readBuf, XLOG_BLCKSZ, readSegNo, readOff); + xlogreader->encrypted = false; + } + /* * Check the page header immediately, so that we can retry immediately if * it's not valid. This may seem unnecessary, because XLogReadRecord() diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index c8b0d2303d..1710d74d0e 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -27,6 +27,7 @@ #ifndef FRONTEND #include "miscadmin.h" +#include "storage/encryption.h" #include "utils/memutils.h" #endif @@ -585,6 +586,16 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen) /* we can be sure to have enough WAL available, we scrolled back */ Assert(readLen == XLOG_BLCKSZ); +#ifndef FRONTEND + if (state->encrypted) + { + uint32 off = XLogSegmentOffset(targetSegmentPtr, state->segcxt.ws_segsize); + + DecryptXLog(state->readBuf, XLOG_BLCKSZ, targetSegNo, off); + state->encrypted = false; + } +#endif + if (!XLogReaderValidatePageHeader(state, targetSegmentPtr, state->readBuf)) goto err; @@ -620,6 +631,14 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen) goto err; } +#ifndef FRONTEND + if (state->encrypted) + { + DecryptXLog(state->readBuf, XLOG_BLCKSZ, targetSegNo, targetPageOff); + state->encrypted = false; + } +#endif + /* * Now that we know we have the full header, validate it. */ diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index 5f1e5ba75d..d83b7a5a65 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -25,6 +25,7 @@ #include "access/xlogutils.h" #include "miscadmin.h" #include "pgstat.h" +#include "storage/encryption.h" #include "storage/smgr.h" #include "utils/guc.h" #include "utils/hsearch.h" @@ -1023,6 +1024,9 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, XLogRead(cur_page, state->segcxt.ws_segsize, state->seg.ws_tli, targetPagePtr, XLOG_BLCKSZ); + if (DataEncryptionEnabled()) + state->encrypted = true; + /* number of valid bytes in the buffer */ return count; } diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index b0ebe5039c..691ac9f0f0 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -77,6 +77,7 @@ #include "replication/walsender.h" #include "replication/walsender_private.h" #include "storage/condition_variable.h" +#include "storage/encryption.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pmsignal.h" @@ -788,6 +789,10 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req /* now actually read the data, we know it's there */ XLogRead(sendCxt, cur_page, targetPagePtr, XLOG_BLCKSZ); + /* Decrypt xlog page if needed */ + if (DataEncryptionEnabled()) + state->encrypted = true; + return count; } diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 3f0de6625d..e11830e747 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -53,6 +53,7 @@ typedef struct XLogPageHeaderData } XLogPageHeaderData; #define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData)) +#define XLogEncryptionOffset SizeOfXLogShortPHD typedef XLogPageHeaderData *XLogPageHeader; diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h index 1bbee386e8..515f931157 100644 --- a/src/include/access/xlogreader.h +++ b/src/include/access/xlogreader.h @@ -164,6 +164,7 @@ struct XLogReaderState */ char *readBuf; uint32 readLen; + bool encrypted; /* readBuf is encrypted? */ /* last read XLOG position for data currently in readBuf */ WALSegmentContext segcxt; diff --git a/src/include/storage/encryption.h b/src/include/storage/encryption.h index a8007fecb1..d38100d621 100644 --- a/src/include/storage/encryption.h +++ b/src/include/storage/encryption.h @@ -40,9 +40,16 @@ * The size in byte for counter of AES-CTR mode in nonce. */ #define ENC_BUFFER_AES_COUNTER_SIZE 4 +#define ENC_WAL_AES_COUNTER_SIZE 4 /* bufenc.c */ extern void DecryptBufferBlock(BlockNumber blocknum, Page page); extern void EncryptBufferBlock(BlockNumber blocknum, Page page); +/* walenc.c */ +extern char *EncryptXLog(char *page, Size nbytes, XLogSegNo segno, + uint32 offset); +extern void DecryptXLog(char *page, Size nbytes, XLogSegNo segno, + uint32 offset); + #endif /* ENCRYPTION_H */ -- 2.23.0