diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/Makefile | 2 | ||||
| -rw-r--r-- | src/backend/access/transam/xlog.c | 21 | ||||
| -rw-r--r-- | src/backend/bootstrap/bootstrap.c | 21 | ||||
| -rw-r--r-- | src/backend/crypto/Makefile | 18 | ||||
| -rw-r--r-- | src/backend/crypto/kmgr.c | 372 | ||||
| -rw-r--r-- | src/backend/main/main.c | 3 | ||||
| -rw-r--r-- | src/backend/postmaster/pgstat.c | 9 | ||||
| -rw-r--r-- | src/backend/postmaster/postmaster.c | 13 | ||||
| -rw-r--r-- | src/backend/replication/basebackup.c | 5 | ||||
| -rw-r--r-- | src/backend/storage/ipc/ipci.c | 3 | ||||
| -rw-r--r-- | src/backend/storage/lmgr/lwlocknames.txt | 1 | ||||
| -rw-r--r-- | src/backend/tcop/postgres.c | 25 | ||||
| -rw-r--r-- | src/backend/utils/misc/guc.c | 24 | ||||
| -rw-r--r-- | src/backend/utils/misc/pg_controldata.c | 11 | ||||
| -rw-r--r-- | src/backend/utils/misc/postgresql.conf.sample | 5 |
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 |
