On Mon, Dec 14, 2020 at 11:16:18PM -0500, Bruce Momjian wrote:
> > 1. Previously, we added a variable bootstrap_keys_wrap that is used for
> > encryption during initdb. However, since we save the "wrapped" key, we need 
> > to
> > use a global KEK that can be accessed in boot mode to unwrap it before 
> > use... I
> > don't know if that's good. To make it simple, I modified the
> > bootstrap_keys_wrap to store the "unwrapped" key so that the encryption
> > function can get it correctly. (The variable name should be changed
> > accordingly).
> 
> I see what you are saying.  We store the wrapped in bootstrap mode, but
> the unwrapped in normal mode.  There is also the case of when we copy
> the keys from an old cluster.  I will work on a patch tomorrow and
> report back here.

I had not considered that we need the date keys available in bootstrap
mode, even if we copied them from another cluster during pg_upgrade.  I
have updated the diff URLs and attaching a patch showing the changes I
made. Basically, I had to separate BootStrapKmgr() into sections:

1.  copy or create an empty live key directory
2.  get the pass phrase
3.  populate the live key directory if we didn't copy it
4.  decrypt they keys into a file-scoped variable

Thanks for showing me this missing feature.

-- 
  Bruce Momjian  <br...@momjian.us>        https://momjian.us
  EnterpriseDB                             https://enterprisedb.com

  The usefulness of a cup is in its emptiness, Bruce Lee

diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c
new file mode 100644
index 9143e72..24c5d7f
*** a/src/backend/crypto/kmgr.c
--- b/src/backend/crypto/kmgr.c
*************** static KmgrShmemData *KmgrShmem;
*** 50,56 ****
  char   *cluster_passphrase_command = NULL;
  int		file_encryption_keylen = 0;
  
! CryptoKey	bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS];
  
  extern char *bootstrap_old_key_datadir;
  extern int	bootstrap_file_encryption_keylen;
--- 50,56 ----
  char   *cluster_passphrase_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 CryptoKey *generate_crypto_key(in
*** 65,74 ****
  void
  BootStrapKmgr(void)
  {
! 	PgKeyWrapCtx	*ctx;
  	char		passphrase[KMGR_MAX_PASSPHRASE_LEN];
- 	uint8		KEK_enc[KMGR_ENC_KEY_LEN];
- 	uint8		KEK_hmac[KMGR_MAC_KEY_LEN];
  	int			passlen;
  
  #ifndef USE_OPENSSL
--- 65,74 ----
  void
  BootStrapKmgr(void)
  {
! 	char		live_path[MAXPGPATH];
! 	CryptoKey	*keys_wrap;
! 	int			nkeys;
  	char		passphrase[KMGR_MAX_PASSPHRASE_LEN];
  	int			passlen;
  
  #ifndef USE_OPENSSL
*************** BootStrapKmgr(void)
*** 78,83 ****
--- 78,85 ----
  			  errhint("Compile with --with-openssl to use cluster encryption."))));
  #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)
  	{
*************** BootStrapKmgr(void)
*** 87,122 ****
  				 bootstrap_old_key_datadir, LIVE_KMGR_DIR);
  		copydir(old_key_dir, LIVE_KMGR_DIR, true);
  	}
! 	/* generate new cluster file encryption keys */
  	else
  	{
! 		char live_path[MAXPGPATH];
! 
! 		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)));
  
- 		memset(bootstrap_keys_wrap, 0, sizeof(bootstrap_keys_wrap));
- 		/* bzero keys on exit */
- 		on_proc_exit(bzeroKmgrKeys, 0);
- 	
- 		/* Get key encryption key from the passphrase command */
- 		snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR);
- 		passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
- 													  passphrase, KMGR_MAX_PASSPHRASE_LEN,
- 													  live_path);
- 		if (passlen < KMGR_MIN_PASSPHRASE_LEN)
- 			ereport(ERROR,
- 					(errmsg("passphrase must be at least %d bytes",
- 							KMGR_MIN_PASSPHRASE_LEN)));
- 	
  		/* Get key encryption key and HMAC key from passphrase */
  		kmgr_derive_keys(passphrase, passlen, KEK_enc, KEK_hmac);
  
- 		explicit_bzero(passphrase, passlen);
- 	
  		/* Create temporary key wrap context */
  		ctx = pg_create_keywrap_ctx(KEK_enc, KEK_hmac);
  		if (!ctx)
--- 89,128 ----
  				 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 passphrase command.  The passphrase
+ 	 * command might want to check for the existance of files in the
+ 	 * live directory, so run this _after_ copying the directory in place.
+ 	 */
+ 	passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+ 												  passphrase, KMGR_MAX_PASSPHRASE_LEN,
+ 												  live_path);
+ 	if (passlen < KMGR_MIN_PASSPHRASE_LEN)
+ 		ereport(ERROR,
+ 				(errmsg("passphrase must be at least %d bytes",
+ 						KMGR_MIN_PASSPHRASE_LEN)));
+ 
+ 	/* generate new cluster file encryption keys */
+ 	if (bootstrap_old_key_datadir == NULL)
+ 	{
+ 		CryptoKey	bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS];
+ 		PgKeyWrapCtx	*ctx;
+ 		uint8		KEK_enc[KMGR_ENC_KEY_LEN];
+ 		uint8		KEK_hmac[KMGR_MAC_KEY_LEN];
  
  		/* Get key encryption key and HMAC key from passphrase */
  		kmgr_derive_keys(passphrase, passlen, KEK_enc, KEK_hmac);
  
  		/* Create temporary key wrap context */
  		ctx = pg_create_keywrap_ctx(KEK_enc, KEK_hmac);
  		if (!ctx)
*************** BootStrapKmgr(void)
*** 142,149 ****
--- 148,177 ----
  		/* Save data encryption keys to the disk */
  		KmgrSaveCryptoKeys(LIVE_KMGR_DIR, bootstrap_keys_wrap);
  
+ 		explicit_bzero(bootstrap_keys_wrap, sizeof(bootstrap_keys_wrap));
  		pg_free_keywrap_ctx(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_passphrase(passphrase, passlen, keys_wrap, bootstrap_keys,
+ 								KMGR_MAX_INTERNAL_KEYS))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("cluster passphrase does not match expected passphrase")));
+ 
+ 	/* bzero keys on exit */
+ 	on_proc_exit(bzeroKmgrKeys, 0);
+ 
+ 	explicit_bzero(passphrase, passlen);
  }
  
  /* Report shared-memory space needed by KmgrShmem */
*************** void
*** 179,187 ****
  InitializeKmgr(void)
  {
  	CryptoKey	*keys_wrap;
  	char		passphrase[KMGR_MAX_PASSPHRASE_LEN];
  	int			passlen;
- 	int			nkeys;
  	struct stat buffer;
  	char live_path[MAXPGPATH];
  
--- 207,215 ----
  InitializeKmgr(void)
  {
  	CryptoKey	*keys_wrap;
+ 	int			nkeys;
  	char		passphrase[KMGR_MAX_PASSPHRASE_LEN];
  	int			passlen;
  	struct stat buffer;
  	char live_path[MAXPGPATH];
  
*************** InitializeKmgr(void)
*** 219,234 ****
  				(errcode(ERRCODE_INTERNAL_ERROR),
  				 (errmsg("cluster has no data encryption keys"))));
  
- 	/* Get the crypto keys from the file */
- 	keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys);
- 	Assert(nkeys == KMGR_MAX_INTERNAL_KEYS);
- 
  	/* Get cluster passphrase */
  	snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR);
  	passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
  												  passphrase, KMGR_MAX_PASSPHRASE_LEN,
  												  live_path);
  
  	/*
  	 * Verify passphrase and prepare a data encryption key in plaintext in shared memory.
  	 */
--- 247,262 ----
  				(errcode(ERRCODE_INTERNAL_ERROR),
  				 (errmsg("cluster has no data encryption keys"))));
  
  	/* Get cluster passphrase */
  	snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR);
  	passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
  												  passphrase, KMGR_MAX_PASSPHRASE_LEN,
  												  live_path);
  
+ 	/* Get the crypto keys from the file */
+ 	keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys);
+ 	Assert(nkeys == KMGR_MAX_INTERNAL_KEYS);
+ 
  	/*
  	 * Verify passphrase and prepare a data encryption key in plaintext in shared memory.
  	 */
*************** static void
*** 245,251 ****
  bzeroKmgrKeys(int status, Datum arg)
  {
  	if (IsBootstrapProcessingMode())
! 		explicit_bzero(bootstrap_keys_wrap, sizeof(bootstrap_keys_wrap));
  	else
  		explicit_bzero(KmgrShmem->intlKeys, sizeof(KmgrShmem->intlKeys));
  }
--- 273,279 ----
  bzeroKmgrKeys(int status, Datum arg)
  {
  	if (IsBootstrapProcessingMode())
! 		explicit_bzero(bootstrap_keys, sizeof(bootstrap_keys));
  	else
  		explicit_bzero(KmgrShmem->intlKeys, sizeof(KmgrShmem->intlKeys));
  }
*************** KmgrGetKey(int id)
*** 256,262 ****
  	Assert(id < KMGR_MAX_INTERNAL_KEYS);
  
  	return (const CryptoKey *) (IsBootstrapProcessingMode() ?
! 			&(bootstrap_keys_wrap[id]) : &(KmgrShmem->intlKeys[id]));
  }
  
  /* Generate an empty CryptoKey */
--- 284,290 ----
  	Assert(id < KMGR_MAX_INTERNAL_KEYS);
  
  	return (const CryptoKey *) (IsBootstrapProcessingMode() ?
! 			&(bootstrap_keys[id]) : &(KmgrShmem->intlKeys[id]));
  }
  
  /* Generate an empty CryptoKey */

Reply via email to