summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/Makefile2
-rw-r--r--src/backend/access/transam/xlog.c21
-rw-r--r--src/backend/bootstrap/bootstrap.c21
-rw-r--r--src/backend/crypto/Makefile18
-rw-r--r--src/backend/crypto/kmgr.c372
-rw-r--r--src/backend/main/main.c3
-rw-r--r--src/backend/postmaster/pgstat.c9
-rw-r--r--src/backend/postmaster/postmaster.c13
-rw-r--r--src/backend/replication/basebackup.c5
-rw-r--r--src/backend/storage/ipc/ipci.c3
-rw-r--r--src/backend/storage/lmgr/lwlocknames.txt1
-rw-r--r--src/backend/tcop/postgres.c25
-rw-r--r--src/backend/utils/misc/guc.c24
-rw-r--r--src/backend/utils/misc/pg_controldata.c11
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample5
15 files changed, 526 insertions, 7 deletions
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 9706a95848..4ace302038 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -21,7 +21,7 @@ SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
main nodes optimizer partitioning port postmaster \
regex replication rewrite \
statistics storage tcop tsearch utils $(top_builddir)/src/timezone \
- jit
+ jit crypto
include $(srcdir)/common.mk
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 9867e1b403..48ca46a941 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -44,11 +44,13 @@
#include "commands/tablespace.h"
#include "common/controldata_utils.h"
#include "executor/instrument.h"
+#include "crypto/kmgr.h"
#include "miscadmin.h"
#include "pg_trace.h"
#include "pgstat.h"
#include "port/atomics.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/postmaster.h"
#include "postmaster/startup.h"
#include "postmaster/walwriter.h"
#include "replication/basebackup.h"
@@ -81,6 +83,7 @@
#include "utils/timestamp.h"
extern uint32 bootstrap_data_checksum_version;
+extern int bootstrap_file_encryption_keylen;
/* Unsupported old recovery command file names (relative to $PGDATA) */
#define RECOVERY_COMMAND_FILE "recovery.conf"
@@ -4618,6 +4621,7 @@ InitControlFile(uint64 sysidentifier)
ControlFile->wal_log_hints = wal_log_hints;
ControlFile->track_commit_timestamp = track_commit_timestamp;
ControlFile->data_checksum_version = bootstrap_data_checksum_version;
+ ControlFile->file_encryption_keylen = bootstrap_file_encryption_keylen;
}
static void
@@ -4717,6 +4721,7 @@ ReadControlFile(void)
pg_crc32c crc;
int fd;
static char wal_segsz_str[20];
+ static char file_encryption_keylen_str[20];
int r;
/*
@@ -4905,6 +4910,12 @@ ReadControlFile(void)
/* Make the initdb settings visible as GUC variables, too */
SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no",
PGC_INTERNAL, PGC_S_OVERRIDE);
+
+ Assert(ControlFile != NULL);
+ snprintf(file_encryption_keylen_str, sizeof(file_encryption_keylen_str), "%d",
+ ControlFile->file_encryption_keylen);
+ SetConfigOption("file_encryption_keylen", file_encryption_keylen_str, PGC_INTERNAL,
+ PGC_S_OVERRIDE);
}
/*
@@ -5354,6 +5365,16 @@ BootStrapXLOG(void)
/* some additional ControlFile fields are set in WriteControlFile() */
WriteControlFile();
+ /* Enable file encryption if required */
+ if (ControlFile->file_encryption_keylen > 0)
+ BootStrapKmgr();
+
+ if (terminal_fd != -1)
+ {
+ close(terminal_fd);
+ terminal_fd = -1;
+ }
+
/* Bootstrap the commit log, too */
BootStrapCLOG();
BootStrapCommitTs();
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index a7ed93fdc1..bf93135a48 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -28,12 +28,14 @@
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "common/link-canary.h"
+#include "crypto/kmgr.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "pg_getopt.h"
#include "pgstat.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/postmaster.h"
#include "postmaster/startup.h"
#include "postmaster/walwriter.h"
#include "replication/walreceiver.h"
@@ -51,6 +53,8 @@
#include "utils/relmapper.h"
uint32 bootstrap_data_checksum_version = 0; /* No checksum */
+int bootstrap_file_encryption_keylen = 0; /* disabled */
+char *bootstrap_old_key_datadir = NULL; /* disabled */
static void CheckerModeMain(void);
@@ -224,7 +228,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
/* If no -x argument, we are a CheckerProcess */
MyAuxProcType = CheckerProcess;
- while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:X:-:")) != -1)
+ while ((flag = getopt(argc, argv, "B:c:d:D:FkK:r:R:u:x:X:-:")) != -1)
{
switch (flag)
{
@@ -253,9 +257,18 @@ AuxiliaryProcessMain(int argc, char *argv[])
case 'k':
bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION;
break;
+ case 'K':
+ bootstrap_file_encryption_keylen = atoi(optarg);
+ break;
+ case 'u':
+ bootstrap_old_key_datadir = pstrdup(optarg);
+ break;
case 'r':
strlcpy(OutputFileName, optarg, MAXPGPATH);
break;
+ case 'R':
+ terminal_fd = atoi(optarg);
+ break;
case 'x':
MyAuxProcType = atoi(optarg);
break;
@@ -312,6 +325,12 @@ AuxiliaryProcessMain(int argc, char *argv[])
proc_exit(1);
}
+ if (bootstrap_file_encryption_keylen != 0 &&
+ bootstrap_file_encryption_keylen != 128 &&
+ bootstrap_file_encryption_keylen != 192 &&
+ bootstrap_file_encryption_keylen != 256)
+ elog(PANIC, "unrecognized file encryption length: %d", bootstrap_file_encryption_keylen);
+
switch (MyAuxProcType)
{
case StartupProcess:
diff --git a/src/backend/crypto/Makefile b/src/backend/crypto/Makefile
new file mode 100644
index 0000000000..c27362029d
--- /dev/null
+++ b/src/backend/crypto/Makefile
@@ -0,0 +1,18 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for src/backend/crypto
+#
+# IDENTIFICATION
+# src/backend/crypto/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/crypto
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = \
+ kmgr.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c
new file mode 100644
index 0000000000..4e701e0275
--- /dev/null
+++ b/src/backend/crypto/kmgr.c
@@ -0,0 +1,372 @@
+/*-------------------------------------------------------------------------
+ *
+ * kmgr.c
+ * Cluster file encryption routines
+ *
+ * Cluster file encryption is enabled if user requests it during initdb.
+ * During bootstrap, we generate data encryption keys, wrap them with the
+ * cluster-level key, and store them into each file located at KMGR_DIR.
+ * Once generated, these are not changed. During startup, we decrypt all
+ * internal keys and load them to the shared memory space. Internal keys
+ * on the shared memory are read-only. All wrapping and unwrapping key
+ * routines require the OpenSSL library.
+ *
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/crypto/kmgr.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "common/file_perm.h"
+#include "common/hex_decode.h"
+#include "common/kmgr_utils.h"
+#include "common/sha2.h"
+#include "access/xlog.h"
+#include "crypto/kmgr.h"
+#include "storage/copydir.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "utils/builtins.h"
+#include "utils/memutils.h"
+/* Struct stores file encryption keys in plaintext format */
+typedef struct KmgrShmemData
+{
+ CryptoKey intlKeys[KMGR_MAX_INTERNAL_KEYS];
+} KmgrShmemData;
+static KmgrShmemData *KmgrShmem;
+
+/* GUC variables */
+char *cluster_key_command = NULL;
+int file_encryption_keylen = 0;
+
+CryptoKey bootstrap_keys[KMGR_MAX_INTERNAL_KEYS];
+
+extern char *bootstrap_old_key_datadir;
+extern int bootstrap_file_encryption_keylen;
+
+static void bzeroKmgrKeys(int status, Datum arg);
+static void KmgrSaveCryptoKeys(const char *dir, CryptoKey *keys);
+static CryptoKey *generate_crypto_key(int len);
+
+/*
+ * This function must be called ONCE during initdb.
+ */
+void
+BootStrapKmgr(void)
+{
+ char live_path[MAXPGPATH];
+ CryptoKey *keys_wrap;
+ int nkeys;
+ char cluster_key_hex[ALLOC_KMGR_CLUSTER_KEY_LEN];
+ int cluster_key_hex_len;
+ unsigned char cluster_key[KMGR_CLUSTER_KEY_LEN];
+
+#ifndef USE_OPENSSL
+ ereport(ERROR,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ (errmsg("cluster file encryption is not supported because OpenSSL is not supported by this build"),
+ errhint("Compile with --with-openssl to use this feature."))));
+#endif
+
+ snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR);
+
+ /* copy cluster file encryption keys from an old cluster? */
+ if (bootstrap_old_key_datadir != NULL)
+ {
+ char old_key_dir[MAXPGPATH];
+
+ snprintf(old_key_dir, sizeof(old_key_dir), "%s/%s",
+ bootstrap_old_key_datadir, LIVE_KMGR_DIR);
+ copydir(old_key_dir, LIVE_KMGR_DIR, true);
+ }
+ /* create empty directory */
+ else
+ {
+ if (mkdir(LIVE_KMGR_DIR, pg_dir_create_mode) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not create cluster file encryption directory \"%s\": %m",
+ LIVE_KMGR_DIR)));
+ }
+
+ /*
+ * Get key encryption key from the cluster_key command. The cluster_key
+ * command might want to check for the existance of files in the
+ * live directory, so run this _after_ copying the directory in place.
+ */
+ cluster_key_hex_len = kmgr_run_cluster_key_command(cluster_key_command,
+ cluster_key_hex,
+ ALLOC_KMGR_CLUSTER_KEY_LEN,
+ live_path);
+
+ if (hex_decode(cluster_key_hex, cluster_key_hex_len, (char*) cluster_key) !=
+ KMGR_CLUSTER_KEY_LEN)
+ ereport(ERROR,
+ (errmsg("cluster key must be %d hexadecimal characters",
+ KMGR_CLUSTER_KEY_LEN * 2)));
+
+ /* generate new cluster file encryption keys */
+ if (bootstrap_old_key_datadir == NULL)
+ {
+ CryptoKey bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS];
+ PgCipherCtx *cluster_key_ctx;
+
+ /* Create KEK encryption context */
+ cluster_key_ctx = pg_cipher_ctx_create(PG_CIPHER_AES_GCM, cluster_key,
+ KMGR_CLUSTER_KEY_LEN, true);
+ if (!cluster_key_ctx)
+ elog(ERROR, "could not initialize encryption context");
+
+ /* Wrap all data encryption keys by key encryption key */
+ for (int id = 0; id < KMGR_MAX_INTERNAL_KEYS; id++)
+ {
+ CryptoKey *key;
+
+ /* generate a data encryption key */
+ key = generate_crypto_key(bootstrap_file_encryption_keylen);
+
+ /* Set this key's ID */
+ key->pgkey_id = id;
+
+ if (!kmgr_wrap_key(cluster_key_ctx, key, &(bootstrap_keys_wrap[id])))
+ {
+ pg_cipher_ctx_free(cluster_key_ctx);
+ elog(ERROR, "failed to wrap data encryption key");
+ }
+
+ explicit_bzero(key, sizeof(CryptoKey));
+ }
+
+ /* Save data encryption keys to the disk */
+ KmgrSaveCryptoKeys(LIVE_KMGR_DIR, bootstrap_keys_wrap);
+
+ explicit_bzero(bootstrap_keys_wrap, sizeof(bootstrap_keys_wrap));
+ pg_cipher_ctx_free(cluster_key_ctx);
+ }
+
+ /*
+ * We are either decrypting keys we copied from an old cluster, or
+ * decrypting keys we just wrote above --- either way, we decrypt
+ * them here and store them in a file-scoped variable for use in
+ * later encrypting during bootstrap mode.
+ */
+
+ /* Get the crypto keys from the file */
+ keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys);
+ Assert(nkeys == KMGR_MAX_INTERNAL_KEYS);
+
+ if (!kmgr_verify_cluster_key(cluster_key, keys_wrap, bootstrap_keys,
+ KMGR_MAX_INTERNAL_KEYS))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("supplied cluster key does not match expected cluster_key")));
+
+ /* bzero keys on exit */
+ on_proc_exit(bzeroKmgrKeys, 0);
+
+ explicit_bzero(cluster_key_hex, cluster_key_hex_len);
+ explicit_bzero(cluster_key, KMGR_CLUSTER_KEY_LEN);
+}
+
+/* Report shared-memory space needed by KmgrShmem */
+Size
+KmgrShmemSize(void)
+{
+ if (!file_encryption_keylen)
+ return 0;
+
+ return MAXALIGN(sizeof(KmgrShmemData));
+}
+
+/* Allocate and initialize key manager memory */
+void
+KmgrShmemInit(void)
+{
+ bool found;
+
+ if (!file_encryption_keylen)
+ return;
+
+ KmgrShmem = (KmgrShmemData *) ShmemInitStruct("File encryption key manager",
+ KmgrShmemSize(), &found);
+
+ on_shmem_exit(bzeroKmgrKeys, 0);
+}
+
+/*
+ * Get cluster key and verify it, then get the data encryption keys.
+ * This function is called by postmaster at startup time.
+ */
+void
+InitializeKmgr(void)
+{
+ CryptoKey *keys_wrap;
+ int nkeys;
+ char cluster_key_hex[ALLOC_KMGR_CLUSTER_KEY_LEN];
+ int cluster_key_hex_len;
+ struct stat buffer;
+ char live_path[MAXPGPATH];
+ unsigned char cluster_key[KMGR_CLUSTER_KEY_LEN];
+
+ if (!file_encryption_keylen)
+ return;
+
+ elog(DEBUG1, "starting up cluster file encryption manager");
+
+ if (stat(KMGR_DIR, &buffer) != 0 || !S_ISDIR(buffer.st_mode))
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ (errmsg("cluster file encryption directory %s is missing", KMGR_DIR))));
+
+ if (stat(KMGR_DIR_PID, &buffer) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ (errmsg("cluster had a pg_alterckey failure that needs repair or pg_alterckey is running"),
+ errhint("Run pg_alterckey --repair or wait for it to complete."))));
+
+ /*
+ * We want OLD deleted since it allows access to the data encryption
+ * keys using the old cluster key. If NEW exists, it means either
+ * NEW is partly written, or NEW wasn't renamed to LIVE --- in either
+ * case, it needs to be repaired.
+ */
+ if (stat(OLD_KMGR_DIR, &buffer) == 0 || stat(NEW_KMGR_DIR, &buffer) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ (errmsg("cluster had a pg_alterckey failure that needs repair"),
+ errhint("Run pg_alterckey --repair."))));
+
+ /* If OLD, NEW, and LIVE do not exist, there is a serious problem. */
+ if (stat(LIVE_KMGR_DIR, &buffer) != 0 || !S_ISDIR(buffer.st_mode))
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ (errmsg("cluster has no data encryption keys"))));
+
+ /* Get cluster key */
+ snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR);
+ cluster_key_hex_len = kmgr_run_cluster_key_command(cluster_key_command,
+ cluster_key_hex,
+ ALLOC_KMGR_CLUSTER_KEY_LEN,
+ live_path);
+
+ if (hex_decode(cluster_key_hex, cluster_key_hex_len, (char*) cluster_key) !=
+ KMGR_CLUSTER_KEY_LEN)
+ ereport(ERROR,
+ (errmsg("cluster key must be %d hexadecimal characters",
+ KMGR_CLUSTER_KEY_LEN * 2)));
+
+ /* Get the crypto keys from the file */
+ keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys);
+ Assert(nkeys == KMGR_MAX_INTERNAL_KEYS);
+
+ /*
+ * Verify cluster key and prepare a data encryption key in plaintext in shared memory.
+ */
+ if (!kmgr_verify_cluster_key(cluster_key, keys_wrap, KmgrShmem->intlKeys,
+ KMGR_MAX_INTERNAL_KEYS))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("supplied cluster key does not match expected cluster key")));
+
+ explicit_bzero(cluster_key_hex, cluster_key_hex_len);
+ explicit_bzero(cluster_key, KMGR_CLUSTER_KEY_LEN);
+}
+
+static void
+bzeroKmgrKeys(int status, Datum arg)
+{
+ if (IsBootstrapProcessingMode())
+ explicit_bzero(bootstrap_keys, sizeof(bootstrap_keys));
+ else
+ explicit_bzero(KmgrShmem->intlKeys, sizeof(KmgrShmem->intlKeys));
+}
+
+const CryptoKey *
+KmgrGetKey(int id)
+{
+ Assert(id < KMGR_MAX_INTERNAL_KEYS);
+
+ return (const CryptoKey *) (IsBootstrapProcessingMode() ?
+ &(bootstrap_keys[id]) : &(KmgrShmem->intlKeys[id]));
+}
+
+/* Generate an empty CryptoKey */
+static CryptoKey *
+generate_crypto_key(int len)
+{
+ CryptoKey *newkey;
+
+ Assert(len <= KMGR_MAX_KEY_LEN);
+ newkey = (CryptoKey *) palloc0(sizeof(CryptoKey));
+
+ /* We store the key as length + key into 'encrypted_key' */
+ memcpy(newkey->encrypted_key, &len, sizeof(len));
+
+ if (!pg_strong_random(newkey->encrypted_key + sizeof(len), len))
+ elog(ERROR, "failed to generate new file encryption key");
+
+ return newkey;
+}
+
+/*
+ * Save the given file encryption keys to the disk.
+ */
+static void
+KmgrSaveCryptoKeys(const char *dir, CryptoKey *keys)
+{
+ elog(DEBUG2, "saving all cryptographic keys");
+
+ for (int i = 0; i < KMGR_MAX_INTERNAL_KEYS; i++)
+ {
+ int fd;
+ char path[MAXPGPATH];
+
+ CryptoKeyFilePath(path, dir, i);
+
+ if ((fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY)) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\": %m",
+ path)));
+
+ errno = 0;
+ pgstat_report_wait_start(WAIT_EVENT_KEY_FILE_WRITE);
+ if (write(fd, &(keys[i]), sizeof(CryptoKey)) != sizeof(CryptoKey))
+ {
+ /* if write didn't set errno, assume problem is no disk space */
+ if (errno == 0)
+ errno = ENOSPC;
+
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write file \"%s\": %m",
+ path)));
+ }
+ pgstat_report_wait_end();
+
+ pgstat_report_wait_start(WAIT_EVENT_KEY_FILE_SYNC);
+ if (pg_fsync(fd) != 0)
+ ereport(PANIC,
+ (errcode_for_file_access(),
+ errmsg("could not fsync file \"%s\": %m",
+ path)));
+ pgstat_report_wait_end();
+
+ if (close(fd) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close file \"%s\": %m",
+ path)));
+ }
+}
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index b6e5128832..19aa502614 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -324,6 +324,7 @@ help(const char *progname)
#endif
printf(_(" -N MAX-CONNECT maximum number of allowed connections\n"));
printf(_(" -p PORT port number to listen on\n"));
+ printf(_(" -R fd prompt for the cluster key\n"));
printf(_(" -s show statistics after each query\n"));
printf(_(" -S WORK-MEM set amount of memory for sorts (in kB)\n"));
printf(_(" -V, --version output version information, then exit\n"));
@@ -351,7 +352,9 @@ help(const char *progname)
printf(_("\nOptions for bootstrapping mode:\n"));
printf(_(" --boot selects bootstrapping mode (must be first argument)\n"));
printf(_(" DBNAME database name (mandatory argument in bootstrapping mode)\n"));
+ printf(_(" -K LEN enable cluster file encryption with specified key length\n"));
printf(_(" -r FILENAME send stdout and stderr to given file\n"));
+ printf(_(" -u DATADIR copy encryption keys from datadir\n"));
printf(_(" -x NUM internal use\n"));
printf(_("\nPlease read the documentation for the complete list of run-time\n"
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index d87d9d06ee..71f2b90ca8 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4152,6 +4152,15 @@ pgstat_get_wait_io(WaitEventIO w)
case WAIT_EVENT_DSM_FILL_ZERO_WRITE:
event_name = "DSMFillZeroWrite";
break;
+ case WAIT_EVENT_KEY_FILE_READ:
+ event_name = "KeyFileRead";
+ break;
+ case WAIT_EVENT_KEY_FILE_WRITE:
+ event_name = "KeyFileWrite";
+ break;
+ case WAIT_EVENT_KEY_FILE_SYNC:
+ event_name = "KeyFileSync";
+ break;
case WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ:
event_name = "LockFileAddToDataDirRead";
break;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index fff4227e0b..bf883184b1 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/kmgr.h"
#include "lib/ilist.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
@@ -231,6 +232,7 @@ static int SendStop = false;
/* still more option variables */
bool EnableSSL = false;
+int terminal_fd = -1;
int PreAuthDelay = 0;
int AuthenticationTimeout = 60;
@@ -687,7 +689,7 @@ PostmasterMain(int argc, char *argv[])
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
- while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:W:-:")) != -1)
+ while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:R:S:sTt:W:-:")) != -1)
{
switch (opt)
{
@@ -778,6 +780,10 @@ PostmasterMain(int argc, char *argv[])
/* only used by single-user backend */
break;
+ case 'R':
+ terminal_fd = atoi(optarg);
+ break;
+
case 'S':
SetConfigOption("work_mem", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
@@ -1326,6 +1332,11 @@ PostmasterMain(int argc, char *argv[])
*/
RemovePgTempFiles();
+ InitializeKmgr();
+
+ if (terminal_fd != -1)
+ close(terminal_fd);
+
/*
* Initialize stats collection subsystem (this does NOT start the
* collector process!)
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 1d8d1742a7..c9fc2f4b4e 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -18,6 +18,7 @@
#include "access/xlog_internal.h" /* for pg_start/stop_backup */
#include "catalog/pg_type.h"
+#include "common/kmgr_utils.h"
#include "common/file_perm.h"
#include "commands/progress.h"
#include "lib/stringinfo.h"
@@ -152,6 +153,10 @@ struct exclude_list_item
*/
static const char *const excludeDirContents[] =
{
+ /* Skip temporary crypto key directories */
+ NEW_KMGR_DIR,
+ OLD_KMGR_DIR,
+
/*
* Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped even
* when stats_temp_directory is set because PGSS_TEXT_FILE is always
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 96c2aaabbd..64fe10ca2a 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -23,6 +23,7 @@
#include "access/syncscan.h"
#include "access/twophase.h"
#include "commands/async.h"
+#include "crypto/kmgr.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
@@ -149,6 +150,7 @@ CreateSharedMemoryAndSemaphores(void)
size = add_size(size, BTreeShmemSize());
size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize());
+ size = add_size(size, KmgrShmemSize());
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
#endif
@@ -267,6 +269,7 @@ CreateSharedMemoryAndSemaphores(void)
BTreeShmemInit();
SyncScanShmemInit();
AsyncShmemInit();
+ KmgrShmemInit();
#ifdef EXEC_BACKEND
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 774292fd94..a44805d5c3 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -53,3 +53,4 @@ XactTruncationLock 44
# 45 was XactTruncationLock until removal of BackendRandomLock
WrapLimitsVacuumLock 46
NotifyQueueTailLock 47
+KmgrFileLock 48
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d35c5020ea..81e64616d4 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/kmgr.h"
#include "executor/spi.h"
#include "jit/jit.h"
#include "libpq/libpq.h"
@@ -3578,7 +3579,7 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
* postmaster/postmaster.c (the option sets should not conflict) and with
* the common help() function in main/main.c.
*/
- while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:v:W:-:")) != -1)
+ while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:R:S:sTt:v:W:-:")) != -1)
{
switch (flag)
{
@@ -3670,6 +3671,16 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
strlcpy(OutputFileName, optarg, MAXPGPATH);
break;
+ case 'R':
+ terminal_fd = atoi(optarg);
+ if (terminal_fd == -1)
+ /*
+ * Allow file descriptor closing to be bypassed via -1.
+ * We just dup sterr. This is useful for single-user mode.
+ */
+ terminal_fd = dup(2);
+ break;
+
case 'S':
SetConfigOption("work_mem", optarg, ctx, gucsource);
break;
@@ -3922,6 +3933,18 @@ PostgresMain(int argc, char *argv[],
BaseInit();
/*
+ * Initialize kmgr for cluster encryption. Since kmgr needs to attach to
+ * shared memory the initialization must be called after BaseInit().
+ */
+ if (!IsUnderPostmaster)
+ {
+ InitializeKmgr();
+
+ if (terminal_fd != -1)
+ close(terminal_fd);
+ }
+
+ /*
* Create a per-backend PGPROC struct in shared memory, except in the
* EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
* this before we can use LWLocks (and in the EXEC_BACKEND case we already
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 878fcc2236..bbaf037bc6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -47,6 +47,7 @@
#include "commands/vacuum.h"
#include "commands/variable.h"
#include "common/string.h"
+#include "crypto/kmgr.h"
#include "funcapi.h"
#include "jit/jit.h"
#include "libpq/auth.h"
@@ -745,6 +746,8 @@ const char *const config_group_names[] =
gettext_noop("Statistics / Monitoring"),
/* STATS_COLLECTOR */
gettext_noop("Statistics / Query and Index Statistics Collector"),
+ /* ENCRYPTION */
+ gettext_noop("Encryption"),
/* AUTOVACUUM */
gettext_noop("Autovacuum"),
/* CLIENT_CONN */
@@ -3389,6 +3392,17 @@ static struct config_int ConfigureNamesInt[] =
check_huge_page_size, NULL, NULL
},
+ {
+ {"file_encryption_keylen", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows the bit length of the file encryption key."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &file_encryption_keylen,
+ 0, 0, 256,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
@@ -4384,6 +4398,16 @@ static struct config_string ConfigureNamesString[] =
},
{
+ {"cluster_key_command", PGC_SIGHUP, ENCRYPTION,
+ gettext_noop("Command to obtain cluster key for cluster file encryption."),
+ NULL
+ },
+ &cluster_key_command,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
{"application_name", PGC_USERSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index d50d87a602..6fcab31172 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -263,8 +263,8 @@ pg_control_recovery(PG_FUNCTION_ARGS)
Datum
pg_control_init(PG_FUNCTION_ARGS)
{
- Datum values[11];
- bool nulls[11];
+ Datum values[12];
+ bool nulls[12];
TupleDesc tupdesc;
HeapTuple htup;
ControlFileData *ControlFile;
@@ -274,7 +274,7 @@ pg_control_init(PG_FUNCTION_ARGS)
* Construct a tuple descriptor for the result row. This must match this
* function's pg_proc entry!
*/
- tupdesc = CreateTemplateTupleDesc(11);
+ tupdesc = CreateTemplateTupleDesc(12);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
@@ -297,6 +297,8 @@ pg_control_init(PG_FUNCTION_ARGS)
BOOLOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 11, "data_page_checksum_version",
INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 12, "file_encryption_keylen",
+ INT4OID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
/* read the control file */
@@ -338,6 +340,9 @@ pg_control_init(PG_FUNCTION_ARGS)
values[10] = Int32GetDatum(ControlFile->data_checksum_version);
nulls[10] = false;
+ values[11] = Int32GetDatum(ControlFile->file_encryption_keylen);
+ nulls[11] = false;
+
htup = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index b7fb2ec1fe..f19cfe5f6a 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -632,6 +632,11 @@
# autovacuum, -1 means use
# vacuum_cost_limit
+#------------------------------------------------------------------------------
+# ENCRYPTION
+#------------------------------------------------------------------------------
+
+#cluster_key_command = ''
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS