diff options
Diffstat (limited to 'src/backend/crypto/kmgr.c')
-rw-r--r-- | src/backend/crypto/kmgr.c | 372 |
1 files changed, 0 insertions, 372 deletions
diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c deleted file mode 100644 index 4e701e02756..00000000000 --- a/src/backend/crypto/kmgr.c +++ /dev/null @@ -1,372 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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))); - } -} |