Revert "Add key management system" (978f869b99) & later commits

The patch needs test cases, reorganization, and cfbot testing.
Technically reverts commits 5c31afc49d..e35b2bad1a (exclusive/inclusive)
and 08db7c63f3..ccbe34139b.

Reported-by: Tom Lane, Michael Paquier

Discussion: https://postgr.es/m/E1ktAAG-0002V2-VB@gemulon.postgresql.org
This commit is contained in:
Bruce Momjian 2020-12-27 21:37:42 -05:00
parent facad31474
commit 3187ef7c46
62 changed files with 52 additions and 3370 deletions

View File

@ -1452,18 +1452,18 @@ include_dir 'conf.d'
mechanism is used.
</para>
<para>
The command must print the passphrase to the standard output
and exit with code 0. It can prompt from the terminal if
<option>--authprompt</option> is used. In the parameter value,
<literal>%R</literal> represents the file descriptor number opened
to the terminal that started the server. A file descriptor is only
available if enabled at server start. If <literal>%R</literal>
is used and no file descriptor is available, the server will not
start. Value <literal>%p</literal> is replaced by a pre-defined
prompt string. (Write <literal>%%</literal> for a literal
<literal>%</literal>.) Note that the prompt string will probably
contain whitespace, so be sure to quote its use adequately.
Newlines are stripped from the end of the output if present.
The command must print the passphrase to the standard output and exit
with code 0. In the parameter value, <literal>%p</literal> is
replaced by a prompt string. (Write <literal>%%</literal> for a
literal <literal>%</literal>.) Note that the prompt string will
probably contain whitespace, so be sure to quote adequately. A single
newline is stripped from the end of the output if present.
</para>
<para>
The command does not actually have to prompt the user for a
passphrase. It can read it from a file, obtain it from a keychain
facility, or similar. It is up to the user to make sure the chosen
mechanism is adequately secure.
</para>
<para>
This parameter can only be set in the <filename>postgresql.conf</filename>
@ -1486,12 +1486,10 @@ include_dir 'conf.d'
parameter is off (the default), then
<varname>ssl_passphrase_command</varname> will be ignored during a
reload and the SSL configuration will not be reloaded if a passphrase
is needed. This setting is appropriate for a command that requires a
terminal for prompting, which will likely not be available when the server is
running. (<option>--authprompt</option> closes the terminal file
descriptor soon after server start.) Setting this parameter on
might be appropriate, for example, if the passphrase is obtained
from a file.
is needed. That setting is appropriate for a command that requires a
TTY for prompting, which might not be available when the server is
running. Setting this parameter to on might be appropriate if the
passphrase is obtained from a file, for example.
</para>
<para>
This parameter can only be set in the <filename>postgresql.conf</filename>
@ -7818,52 +7816,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</variablelist>
</sect1>
<sect1 id="runtime-config-encryption">
<title>Cluster File Encryption</title>
<variablelist>
<varlistentry id="guc-cluster-key-command" xreflabel="cluster_key_command">
<term><varname>cluster_key_command</varname> (<type>string</type>)
<indexterm>
<primary><varname>cluster_key_command</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
This option specifies an external command to obtain the cluster-level
key for cluster file encryption during server initialization and
server start.
</para>
<para>
The command must print the cluster key to the standard output as
64 hexadecimal characters, and exit with code 0. The command
can prompt for the passphrase or PIN from the terminal if
<option>--authprompt</option> is used. In the parameter value,
<literal>%R</literal> represents the file descriptor number opened
to the terminal that started the server. A file descriptor is only
available if enabled at server start. If <literal>%R</literal>
is used and no file descriptor is available, the server will not
start. Value <literal>%p</literal> is replaced by a pre-defined
prompt string. Value <literal>%d</literal> is replaced by the
directory containing the keys; this is useful if the command
must create files with the keys, e.g., to store a cluster-level
key encryped by a key stored in a hardware security module.
(Write <literal>%%</literal> for a literal <literal>%</literal>.)
Note that the prompt string will probably contain whitespace,
so be sure to quote its use adequately. Newlines are stripped
from the end of the output if present.
</para>
<para>
This parameter can only be set by
<application>initdb</application>, in the
<filename>postgresql.conf</filename> file, or on the server
command line.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 id="runtime-config-client">
<title>Client Connection Defaults</title>
@ -9685,22 +9637,6 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem>
</varlistentry>
<varlistentry id="guc-file-encryption-keylen" xreflabel="file_encryption_keylen">
<term><varname>file_encryption_keylen</varname> (<type>boolean</type>)
<indexterm>
<primary>Cluster file encryption key length</primary>
</indexterm>
</term>
<listitem>
<para>
Reports the bit length of the cluster file
encryption key, or zero if disabled. See <xref
linkend="app-initdb-cluster-key-command"/> for more
information.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-data-directory-mode" xreflabel="data_directory_mode">
<term><varname>data_directory_mode</varname> (<type>integer</type>)
<indexterm>

View File

@ -1,97 +0,0 @@
<!-- doc/src/sgml/database-encryption.sgml -->
<chapter id="database-file-encryption">
<title>Cluster File Encryption</title>
<indexterm zone="database-file-encryption">
<primary>Cluster File Encryption</primary>
</indexterm>
<para>
The purpose of cluster file encryption is to prevent users with read
access to the directories used to store database files and write-ahead
log from being able to access the data stored in those files.
For example, when using cluster file encryption, users who have read
access to the cluster directories for backup purposes will not be able
to decrypt the data stored in these files.
</para>
<para>
Cluster file encryption uses two levels of encryption. The first level
is data encryption keys, specifically keys zero and one. Key zero is
the key used to encrypt database heap and index files which are stored in
the file system, plus temporary files created during database operation.
Key one is used to encrypt write-ahead log (WAL) files. Two different
keys are used so that primary and standby servers can use different zero
(heap/index/temp) keys, but the same one (WAL) key, so that these keys
can eventually be rotated by switching the primary to the standby
and then changing the WAL key.
</para>
<para>
The second level of encryption is a key used to encrypt first-level
keys. This type of key is often referred to as a Key Encryption Key
(<acronym>KEK</acronym>). This key is <emphasis>not</emphasis> stored
in the file system, but provided at <command>initdb</command> time and
each time the server is started. This key prevents anyone with access
to the database directories from decrypting the data because they do
not know the second-level key which encrypted the first-level keys
which encrypted the database cluster files.
</para>
<sect1 id="encryption-file-encryption">
<title>Initialization</title>
<para>
Cluster file encryption is enabled when
<productname>PostgreSQL</productname> is built
with <literal>--with-openssl</literal> and <xref
linkend="app-initdb-cluster-key-command"/> is specified
during <command>initdb</command>. The cluster key
provided by the <option>--cluster-key-command</option>
option during <command>initdb</command> and the one generated
by <xref linkend="guc-cluster-key-command"/> in the
<filename>postgresql.conf</filename> must match for the database
cluster to start. Note that the cluster key command
passed to <command>initdb</command> must return a key of
64 hexadecimal characters. For example.
<programlisting>
initdb -D dbname --cluster-key-command='ckey_passphrase.sh'
</programlisting>
</para>
</sect1>
<sect1 id="key-encryption-key">
<title>Internals</title>
<para>
During the <command>initdb</command> process, if
<option>--cluster-key-command</option> is specified, two data-level
encryption keys are created. These two keys are then encrypted with
the key encryption key (KEK) supplied by the cluster key command before
being stored in the database directory. The key or passphrase that
derives the key must be supplied from the terminal or stored in a
trusted key store, such as key vault software, hardware security module.
</para>
<para>
If the <productname>PostgreSQL</productname> server has
been initialized to require a cluster key, each time the
server starts the <filename>postgresql.conf</filename>
<varname>cluster_key_command</varname> command will be executed
and the cluster key retrieved. The data encryption keys in the
<filename>pg_cryptokeys</filename> directory will then be decrypted
using the supplied key and integrity-checked to ensure it
matches the initdb-supplied key. If this check fails, the
server will refuse to start.
</para>
<para>
The data encryption keys are randomly generated and are 128, 192,
or 256-bits in length. They are encrypted by the key encryption key
(KEK) using Advanced Encryption Standard (<acronym>AES256</acronym>)
encryption in Galois/Counter Mode (<acronym>GCM</acronym>), which also
provides KEK authentication.
</para>
</sect1>
</chapter>

View File

@ -49,7 +49,6 @@
<!ENTITY wal SYSTEM "wal.sgml">
<!ENTITY logical-replication SYSTEM "logical-replication.sgml">
<!ENTITY jit SYSTEM "jit.sgml">
<!ENTITY database-encryption SYSTEM "database-encryption.sgml">
<!-- programmer's guide -->
<!ENTITY bgworker SYSTEM "bgworker.sgml">

View File

@ -976,9 +976,8 @@ build-postgresql:
<listitem>
<para>
Build with support for <acronym>SSL</acronym> (encrypted)
connections and cluster file encryption. This requires the
<productname>OpenSSL</productname> package to be installed.
<filename>configure</filename> will check
connections. This requires the <productname>OpenSSL</productname>
package to be installed. <filename>configure</filename> will check
for the required header files and libraries to make sure that
your <productname>OpenSSL</productname> installation is sufficient
before proceeding.

View File

@ -171,7 +171,6 @@ break is not needed in a wider output rendering.
&wal;
&logical-replication;
&jit;
&database-encryption;
&regress;
</part>

View File

@ -189,7 +189,6 @@ Complete list of usable sgml source files in this directory.
<!ENTITY values SYSTEM "values.sgml">
<!-- applications and utilities -->
<!ENTITY pgalterckey SYSTEM "pg_alterckey.sgml">
<!ENTITY clusterdb SYSTEM "clusterdb.sgml">
<!ENTITY createdb SYSTEM "createdb.sgml">
<!ENTITY createuser SYSTEM "createuser.sgml">
@ -216,7 +215,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY pgtestfsync SYSTEM "pgtestfsync.sgml">
<!ENTITY pgtesttiming SYSTEM "pgtesttiming.sgml">
<!ENTITY pgupgrade SYSTEM "pgupgrade.sgml">
<!ENTITY pgwaldump SYSTEM "pg_waldump.sgml">
<!ENTITY pgwaldump SYSTEM "pg_waldump.sgml">
<!ENTITY postgres SYSTEM "postgres-ref.sgml">
<!ENTITY postmaster SYSTEM "postmaster.sgml">
<!ENTITY psqlRef SYSTEM "psql-ref.sgml">

View File

@ -163,17 +163,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry id="app-initdb-cluster-key-command" xreflabel="cluster key command">
<term><option>--cluster-key-command=<replaceable class="parameter">command</replaceable></option></term>
<listitem>
<para>
This option specifies an external command to obtain the cluster-level
key for cluster file encryption during server initialization and
server start; see <xref linkend="guc-cluster-key-command"/> for details.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
<term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
@ -234,18 +223,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry id="app-initdb-file-encryption-keylen"
xreflabel="file encryption">
<term><option>-K <replaceable class="parameter">length</replaceable></option></term>
<term><option>--file-encryption-keylen=<replaceable class="parameter">length</replaceable></option></term>
<listitem>
<para>
Specifies the number of bits for the file encryption keys. The
default is 128 bits.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--locale=<replaceable>locale</replaceable></option></term>
<listitem>
@ -308,17 +285,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-R</option></term>
<term><option>--authprompt</option></term>
<listitem>
<para>
Allows the <option>--cluster-key-command</option> command
to prompt for a passphrase or PIN.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-S</option></term>
<term><option>--sync-only</option></term>
@ -341,18 +307,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-u <replaceable>datadir</replaceable></option></term>
<term><option>--copy-encryption-keys=<replaceable>datadir</replaceable></option></term>
<listitem>
<para>
Copies cluster file encryption keys from another cluster; required
when using <application>pg_upgrade</application> on a cluster
with cluster file encryption enabled.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-U <replaceable class="parameter">username</replaceable></option></term>
<term><option>--username=<replaceable class="parameter">username</replaceable></option></term>

View File

@ -1,197 +0,0 @@
<!--
doc/src/sgml/ref/pg_alterckey.sgml
PostgreSQL documentation
-->
<refentry id="app-pg_alterckey">
<indexterm zone="app-pg_alterckey">
<primary>pg_alterckey</primary>
</indexterm>
<refmeta>
<refentrytitle><application>pg_alterckey</application></refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo>Application</refmiscinfo>
</refmeta>
<refnamediv>
<refname>pg_alterckey</refname>
<refpurpose>alter the <productname>PostgreSQL</productname> cluster key</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>pg_alterckey</command>
<group choice="plain">
<arg choice="plain"><option>-R</option></arg>
<arg choice="plain"><option>--authprompt</option></arg>
</group>
<arg choice="plain">
<replaceable class="parameter">old_cluster_key_command</replaceable>
<replaceable class="parameter">new_cluster_key_command</replaceable>
</arg>
<arg choice="opt">
<group choice="plain">
<arg choice="plain"><option>-D</option></arg>
<arg choice="plain"><option>--pgdata</option></arg>
</group>
<replaceable class="parameter">datadir</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>pg_alterckey</command>
<group choice="opt">
<arg choice="plain"><option>-R</option></arg>
<arg choice="plain"><option>--authprompt</option></arg>
</group>
<group choice="plain">
<arg choice="plain"><option>-r</option></arg>
<arg choice="plain"><option>--repair</option></arg>
</group>
<arg choice="opt">
<group choice="opt">
<arg choice="plain"><option>-D</option></arg>
<arg choice="plain"><option>--pgdata</option></arg>
</group>
<replaceable class="parameter">datadir</replaceable>
</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="r1-app-pg_alterckey-1">
<title>Description</title>
<para>
<command>pg_alterckey</command> alters the cluster key used
for cluster file encryption. The cluster key is initially set
during <xref linkend="app-initdb"/>. The command can be run while the
server is running or stopped. The new password must be used the next
time the server is started.
</para>
<para>
Technically, <command>pg_alterckey</command> changes the key
encryption key (<acronym>KEK</acronym>) which encrypts the data
encryption keys; it does not change the data encryption keys. It does
this by decrypting each data encryption key using the <replaceable
class="parameter">old_cluster_key_command</replaceable>,
re-encrypting it using the <replaceable
class="parameter">new_cluster_key_command</replaceable>, and
then writes the result back to the cluster directory.
</para>
<para>
See the <xref linkend="app-initdb"/> documentation for how to define
the old and new passphrase commands. You can use different executables
for these commands, or you can use the same executable with different
arguments to specify retrieval of the old or new key.
</para>
<para>
When started, <command>pg_alterckey</command> repairs any files that
remain from previous <command>pg_alterckey</command> failures before
altering the cluster key. To perform only the repair task,
use the <option>--repair</option> option. The server will not start
if repair is needed, though a running server is unaffected by an
unrepaired cluster key configuration.
</para>
<para>
You can specify the data directory on the command line, or use
the environment variable <envar>PGDATA</envar>.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>
<variablelist>
<varlistentry>
<term><option>-R</option></term>
<term><option>--authprompt</option></term>
<listitem>
<para>
Allows the <option>old_cluster_key_command</option> and
<option>new_cluster_key_command</option> commands
to prompt for a passphrase or PIN.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Other options:
<variablelist>
<varlistentry>
<term><option>-V</option></term>
<term><option>--version</option></term>
<listitem>
<para>
Print the <application>pg_alterckey</application> version and exit.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-?</option></term>
<term><option>--help</option></term>
<listitem>
<para>
Show help about <application>pg_alterckey</application> command line
arguments, and exit.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1>
<title>Environment</title>
<variablelist>
<varlistentry>
<term><envar>PGDATA</envar></term>
<listitem>
<para>
Default data directory location
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><envar>PG_COLOR</envar></term>
<listitem>
<para>
Specifies whether to use color in diagnostic messages. Possible values
are <literal>always</literal>, <literal>auto</literal> and
<literal>never</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="app-initdb"/></member>
</simplelist>
</refsect1>
</refentry>

View File

@ -38,7 +38,6 @@ PostgreSQL documentation
<arg choice="opt"><option>-s</option></arg>
<arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
<arg choice="opt"><option>-p</option> <replaceable>path</replaceable></arg>
<arg choice="opt"><option>-R</option></arg>
<arg choice="opt"><option>-c</option></arg>
</cmdsynopsis>
@ -73,7 +72,6 @@ PostgreSQL documentation
<arg choice="opt"><option>-t</option> <replaceable>seconds</replaceable></arg>
<arg choice="opt"><option>-s</option></arg>
<arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
<arg choice="opt"><option>-R</option></arg>
<arg choice="opt"><option>-c</option></arg>
</cmdsynopsis>
@ -375,18 +373,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-R</option></term>
<term><option>--authprompt</option></term>
<listitem>
<para>
Allows <option>ssl_passphrase_command</option> or
<option>cluster_key_command</option> to prompt for a passphrase
or PIN.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option></term>
<term><option>--silent</option></term>

View File

@ -167,15 +167,6 @@ PostgreSQL documentation
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-R</option></term>
<term><option>--authprompt</option></term>
<listitem><para>allows <option>ssl_passphrase_command</option> or
<option>cluster_key_command</option> to prompt for a passphrase
or PIN.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option> <replaceable>dir</replaceable></term>
<term><option>--socketdir=</option><replaceable>dir</replaceable></term>
@ -318,9 +309,7 @@ make prefix=/usr/local/pgsql.new install
Again, use compatible <command>initdb</command>
flags that match the old cluster. Many
prebuilt installers do this step automatically. There is no need to
start the new cluster. If upgrading a cluster that uses
cluster file encryption, the <command>initdb</command> option
<option>--copy-encryption-keys</option> must be specified.
start the new cluster.
</para>
</step>
@ -849,13 +838,6 @@ psql --username=postgres --file=script.sql postgres
is down.
</para>
<para>
If the old cluster uses file encryption, the new cluster must use
the same keys, so <command>pg_upgrade</command> copies them to the
new cluster. It is necessary to initialize the new cluster with
the same <varname>cluster_key_command</varname> and the same
file encryption key length.
</para>
</refsect1>
<refsect1>

View File

@ -297,19 +297,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-R <replaceable class="parameter">file-descriptor</replaceable></option></term>
<listitem>
<para>
Makes <command>postgres</command> prompt for a passphrase or PIN
from the specified open numeric file descriptor. The descriptor
is closed after the key is read. The file descriptor number
<literal>-1</literal> duplicates standard error for the terminal;
this is useful for single-user mode.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option></term>
<listitem>

View File

@ -240,7 +240,6 @@
</para>
</partintro>
&pgalterckey;
&clusterdb;
&createdb;
&createuser;

View File

@ -77,11 +77,6 @@ Item
<entry>Subdirectory containing transaction commit timestamp data</entry>
</row>
<row>
<entry><filename>pg_cryptokeys</filename></entry>
<entry>Subdirectory containing file encryption keys</entry>
</row>
<row>
<entry><filename>pg_dynshmem</filename></entry>
<entry>Subdirectory containing files used by the dynamic shared memory

View File

@ -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 crypto
jit
include $(srcdir)/common.mk
@ -212,12 +212,6 @@ endif
$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample '$(DESTDIR)$(datadir)/pg_hba.conf.sample'
$(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample '$(DESTDIR)$(datadir)/pg_ident.conf.sample'
$(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample '$(DESTDIR)$(datadir)/postgresql.conf.sample'
$(INSTALL_DATA) $(srcdir)/crypto/ckey_aws.sh.sample '$(DESTDIR)$(datadir)/auth_commands/ckey_aws.sh.sample'
$(INSTALL_DATA) $(srcdir)/crypto/ckey_direct.sh.sample '$(DESTDIR)$(datadir)/auth_commands/ckey_direct.sh.sample'
$(INSTALL_DATA) $(srcdir)/crypto/ckey_passphrase.sh.sample '$(DESTDIR)$(datadir)/auth_commands/ckey_passphrase.sh.sample'
$(INSTALL_DATA) $(srcdir)/crypto/ckey_piv_nopin.sh.sample '$(DESTDIR)$(datadir)/auth_commands/ckey_piv_nopin.sh.sample'
$(INSTALL_DATA) $(srcdir)/crypto/ckey_piv_pin.sh.sample '$(DESTDIR)$(datadir)/auth_commands/ckey_piv_pin.sh.sample'
$(INSTALL_DATA) $(srcdir)/crypto/ssl_passphrase.sh.sample '$(DESTDIR)$(datadir)/auth_commands/ssl_passphrase.sh.sample'
ifeq ($(with_llvm), yes)
install-bin: install-postgres-bitcode
@ -243,7 +237,6 @@ endif
installdirs:
$(MKDIR_P) '$(DESTDIR)$(bindir)' '$(DESTDIR)$(datadir)'
$(MKDIR_P) '$(DESTDIR)$(datadir)' '$(DESTDIR)$(datadir)/auth_commands'
ifeq ($(PORTNAME), cygwin)
ifeq ($(MAKE_DLL), true)
$(MKDIR_P) '$(DESTDIR)$(libdir)'
@ -283,13 +276,7 @@ endif
$(MAKE) -C utils uninstall-data
rm -f '$(DESTDIR)$(datadir)/pg_hba.conf.sample' \
'$(DESTDIR)$(datadir)/pg_ident.conf.sample' \
'$(DESTDIR)$(datadir)/postgresql.conf.sample' \
'$(DESTDIR)$(datadir)/auth_commands/ckey_aws.sh.sample' \
'$(DESTDIR)$(datadir)/auth_commands/ckey_direct.sh.sample' \
'$(DESTDIR)$(datadir)/auth_commands/ckey_passphrase.sh.sample' \
'$(DESTDIR)$(datadir)/auth_commands/ckey_piv_nopin.sh.sample' \
'$(DESTDIR)$(datadir)/auth_commands/ckey_piv_pin.sh.sample' \
'$(DESTDIR)$(datadir)/auth_commands/ssl_passphrase.sh.sample'
'$(DESTDIR)$(datadir)/postgresql.conf.sample'
ifeq ($(with_llvm), yes)
$(call uninstall_llvm_module,postgres)
endif

View File

@ -44,13 +44,11 @@
#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"
@ -83,7 +81,6 @@
#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"
@ -4621,7 +4618,6 @@ 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
@ -4721,7 +4717,6 @@ ReadControlFile(void)
pg_crc32c crc;
int fd;
static char wal_segsz_str[20];
static char file_encryption_keylen_str[20];
int r;
/*
@ -4910,12 +4905,6 @@ 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);
}
/*
@ -5365,16 +5354,6 @@ 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();

View File

@ -28,14 +28,12 @@
#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"
@ -53,8 +51,6 @@
#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);
@ -228,7 +224,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:FkK:r:R:u:x:X:-:")) != -1)
while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:X:-:")) != -1)
{
switch (flag)
{
@ -257,18 +253,9 @@ 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;
@ -325,12 +312,6 @@ 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:

View File

@ -1,18 +0,0 @@
#-------------------------------------------------------------------------
#
# 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

View File

@ -1,50 +0,0 @@
#!/bin/sh
# This uses the AWS Secrets Manager using the AWS CLI and OpenSSL.
[ "$#" -ne 1 ] && echo "cluster_key_command usage: $0 \"%d\"" 1>&2 && exit 1
# No need for %R or -R since we are not prompting
DIR="$1"
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
# File containing the id of the AWS secret
AWS_ID_FILE="$DIR/aws-secret.id"
# ----------------------------------------------------------------------
# Create an AWS Secrets Manager secret?
if [ ! -e "$AWS_ID_FILE" ]
then # The 'postgres' operating system user must have permission to
# access the AWS CLI
# The epoch-time/directory/hostname combination is unique
HASH=$(echo -n "$(date '+%s')$DIR$(hostname)" | sha1sum | cut -d' ' -f1)
AWS_SECRET_ID="Postgres-cluster-key-$HASH"
# Use stdin to avoid passing the secret on the command line
openssl rand -hex 32 |
aws secretsmanager create-secret \
--name "$AWS_SECRET_ID" \
--description 'Used for Postgres cluster file encryption' \
--secret-string 'file:///dev/stdin' \
--output text > /dev/null
if [ "$?" -ne 0 ]
then echo 'cluster key generation failed' 1>&2
exit 1
fi
echo "$AWS_SECRET_ID" > "$AWS_ID_FILE"
fi
if ! aws secretsmanager get-secret-value \
--secret-id "$(cat "$AWS_ID_FILE")" \
--output text
then echo 'cluster key retrieval failed' 1>&2
exit 1
fi | awk -F'\t' 'NR == 1 {print $4}'
exit 0

View File

@ -1,37 +0,0 @@
#!/bin/sh
# This uses a key supplied by the user
# If OpenSSL is installed, you can generate a pseudo-random key by running:
# openssl rand -hex 32
# To get a true random key, run:
# wget -q -O - 'https://www.random.org/cgi-bin/randbyte?nbytes=32&format=h' | tr -d ' \n'; echo
[ "$#" -lt 1 ] && echo "cluster_key_command usage: $0 %R [%p]" 1>&2 && exit 1
# Supports environment variable PROMPT
FD="$1"
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
[ "$2" ] && PROMPT="$2"
# ----------------------------------------------------------------------
[ ! "$PROMPT" ] && PROMPT='Enter cluster key as 64 hexadecimal characters: '
stty -echo <&"$FD"
echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
read KEY <&"$FD"
stty echo <&"$FD"
if [ "$(expr "$KEY" : '[0-9a-fA-F]*$')" -ne 64 ]
then echo 'invalid; must be 64 hexadecimal characters' 1>&2
exit 1
fi
echo "$KEY"
exit 0

View File

@ -1,33 +0,0 @@
#!/bin/sh
# This uses a passphrase supplied by the user.
[ "$#" -lt 1 ] && echo "cluster_key_command usage: $0 %R [\"%p\"]" 1>&2 && exit 1
FD="$1"
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
# Supports environment variable PROMPT
[ "$2" ] && PROMPT="$2"
# ----------------------------------------------------------------------
[ ! "$PROMPT" ] && PROMPT='Enter cluster passphrase: '
stty -echo <&"$FD"
echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
read PASS <&"$FD"
stty echo <&"$FD"
if [ ! "$PASS" ]
then echo 'invalid: empty passphrase' 1>&2
exit 1
fi
echo "$PASS" | sha256sum | cut -d' ' -f1
exit 0

View File

@ -1,63 +0,0 @@
#!/bin/sh
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
# It uses a PIN stored in a file.
# It uses OpenSSL with PKCS11 enabled via OpenSC.
[ "$#" -ne 1 ] && echo "cluster_key_command usage: $0 \"%d\"" 1>&2 && exit 1
# Supports environment variable PIV_PIN_FILE
# No need for %R or -R since we are not prompting for a PIN
DIR="$1"
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
# Set these here or pass in as environment variables.
# File that stores the PIN to unlock the PIV
#PIV_PIN_FILE=''
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
PIV_SLOT='0:3'
# File containing the cluster key encrypted with the PIV_SLOT's public key
KEY_FILE="$DIR/pivpass.key"
# ----------------------------------------------------------------------
[ ! "$PIV_PIN_FILE" ] && echo 'PIV_PIN_FILE undefined' 1>&2 && exit 1
[ ! -e "$PIV_PIN_FILE" ] && echo "$PIV_PIN_FILE does not exist" 1>&2 && exit 1
[ -d "$PIV_PIN_FILE" ] && echo "$PIV_PIN_FILE is a directory" 1>&2 && exit 1
[ ! "$KEY_FILE" ] && echo 'KEY_FILE undefined' 1>&2 && exit 1
[ -d "$KEY_FILE" ] && echo "$KEY_FILE is a directory" 1>&2 && exit 1
# Create a cluster key encrypted with the PIV_SLOT's public key?
if [ ! -e "$KEY_FILE" ]
then # The 'postgres' operating system user must have permission to
# access the PIV device.
openssl rand -hex 32 |
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
-inkey "$PIV_SLOT" -passin file:"$PIV_PIN_FILE" -out "$KEY_FILE"
then echo 'cluster key generation failed' 1>&2
exit 1
fi
# Warn the user to save the cluster key in a safe place
cat 1>&2 <<END
WARNING: The PIV device can be locked and require a reset if too many PIN
attempts fail. It is recommended to run this command manually and save
the cluster key in a secure location for possible recovery.
END
fi
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
-inkey "$PIV_SLOT" -passin file:"$PIV_PIN_FILE" -in "$KEY_FILE"
then echo 'cluster key decryption failed' 1>&2
exit 1
fi
exit 0

View File

@ -1,76 +0,0 @@
#!/bin/sh
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
# It requires a user-entered PIN.
# It uses OpenSSL with PKCS11 enabled via OpenSC.
[ "$#" -lt 2 ] && echo "cluster_key_command usage: $0 \"%d\" %R [\"%p\"]" 1>&2 && exit 1
# Supports environment variable PROMPT
DIR="$1"
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
FD="$2"
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
[ "$3" ] && PROMPT="$3"
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
PIV_SLOT='0:3'
# File containing the cluster key encrypted with the PIV_SLOT's public key
KEY_FILE="$DIR/pivpass.key"
# ----------------------------------------------------------------------
[ ! "$PROMPT" ] && PROMPT='Enter PIV PIN: '
stty -echo <&"$FD"
# Create a cluster key encrypted with the PIV_SLOT's public key?
if [ ! -e "$KEY_FILE" ]
then echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
# The 'postgres' operating system user must have permission to
# access the PIV device.
openssl rand -hex 32 |
# 'engine "pkcs11" set.' message confuses prompting
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
-inkey "$PIV_SLOT" -passin fd:"$FD" -out "$KEY_FILE" 2>&1
then stty echo <&"$FD"
echo 'cluster key generation failed' 1>&2
exit 1
fi | grep -v 'engine "pkcs11" set\.'
echo 1>&"$FD"
# Warn the user to save the cluster key in a safe place
cat 1>&"$FD" <<END
WARNING: The PIV can be locked and require a reset if too many PIN
attempts fail. It is recommended to run this command manually and save
the cluster key in a secure location for possible recovery.
END
fi
echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
-inkey "$PIV_SLOT" -passin fd:"$FD" -in "$KEY_FILE" 2>&1
then stty echo <&"$FD"
echo 'cluster key retrieval failed' 1>&2
exit 1
fi | grep -v 'engine "pkcs11" set\.'
echo 1>&"$FD"
stty echo <&"$FD"
exit 0

View File

@ -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)));
}
}

View File

@ -1,33 +0,0 @@
#!/bin/sh
# This uses a passphrase supplied by the user.
[ "$#" -lt 1 ] && echo "ssl_passphrase_command usage: $0 %R [\"%p\"]" 1>&2 && exit 1
FD="$1"
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
# Supports environment variable PROMPT
[ "$2" ] && PROMPT="$2"
# ----------------------------------------------------------------------
[ ! "$PROMPT" ] && PROMPT='Enter cluster passphrase: '
stty -echo <&"$FD"
echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
read PASS <&"$FD"
stty echo <&"$FD"
if [ ! "$PASS" ]
then echo 'invalid: empty passphrase' 1>&2
exit 1
fi
echo "$PASS"
exit 0

View File

@ -22,7 +22,6 @@
#include <sys/stat.h>
#include <unistd.h>
#include "postmaster/postmaster.h"
#include "common/string.h"
#include "libpq/libpq.h"
#include "storage/fd.h"
@ -62,19 +61,6 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
appendStringInfoString(&command, prompt);
p++;
break;
case 'R':
{
char fd_str[20];
if (terminal_fd == -1)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("ssl_passphrase_command referenced %%R, but -R not specified")));
p++;
snprintf(fd_str, sizeof(fd_str), "%d", terminal_fd);
appendStringInfoString(&command, fd_str);
break;
}
case '%':
appendStringInfoChar(&command, '%');
p++;

View File

@ -324,7 +324,6 @@ 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"));
@ -352,9 +351,7 @@ 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"

View File

@ -4152,15 +4152,6 @@ 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;

View File

@ -100,7 +100,6 @@
#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"
@ -232,7 +231,6 @@ static int SendStop = false;
/* still more option variables */
bool EnableSSL = false;
int terminal_fd = -1;
int PreAuthDelay = 0;
int AuthenticationTimeout = 60;
@ -689,7 +687,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:R:S:sTt:W:-:")) != -1)
while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
@ -780,10 +778,6 @@ 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;
@ -1332,11 +1326,6 @@ 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!)

View File

@ -18,7 +18,6 @@
#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"
@ -153,10 +152,6 @@ 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

View File

@ -23,7 +23,6 @@
#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"
@ -150,7 +149,6 @@ 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
@ -269,7 +267,6 @@ CreateSharedMemoryAndSemaphores(void)
BTreeShmemInit();
SyncScanShmemInit();
AsyncShmemInit();
KmgrShmemInit();
#ifdef EXEC_BACKEND

View File

@ -53,4 +53,3 @@ XactTruncationLock 44
# 45 was XactTruncationLock until removal of BackendRandomLock
WrapLimitsVacuumLock 46
NotifyQueueTailLock 47
KmgrFileLock 48

View File

@ -42,7 +42,6 @@
#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"
@ -3579,7 +3578,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:R:S:sTt:v:W:-:")) != -1)
while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:v:W:-:")) != -1)
{
switch (flag)
{
@ -3671,16 +3670,6 @@ 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;
@ -3932,18 +3921,6 @@ PostgresMain(int argc, char *argv[],
/* Early initialization */
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

View File

@ -47,7 +47,6 @@
#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"
@ -746,8 +745,6 @@ 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 */
@ -3392,17 +3389,6 @@ 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
@ -4397,16 +4383,6 @@ static struct config_string ConfigureNamesString[] =
NULL, NULL, NULL
},
{
{"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."),

View File

@ -263,8 +263,8 @@ pg_control_recovery(PG_FUNCTION_ARGS)
Datum
pg_control_init(PG_FUNCTION_ARGS)
{
Datum values[12];
bool nulls[12];
Datum values[11];
bool nulls[11];
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(12);
tupdesc = CreateTemplateTupleDesc(11);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
@ -297,8 +297,6 @@ 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 */
@ -340,9 +338,6 @@ 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));

View File

@ -632,11 +632,6 @@
# autovacuum, -1 means use
# vacuum_cost_limit
#------------------------------------------------------------------------------
# ENCRYPTION
#------------------------------------------------------------------------------
#cluster_key_command = ''
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS

View File

@ -16,7 +16,6 @@ include $(top_builddir)/src/Makefile.global
SUBDIRS = \
initdb \
pg_archivecleanup \
pg_alterckey \
pg_basebackup \
pg_checksums \
pg_config \

View File

@ -141,16 +141,11 @@ static bool debug = false;
static bool noclean = false;
static bool do_sync = true;
static bool sync_only = false;
static bool pass_terminal_fd = false;
static char *term_fd_opt = NULL;
static int file_encryption_keylen = 0;
static bool show_setting = false;
static bool data_checksums = false;
static char *xlog_dir = NULL;
static char *str_wal_segment_size_mb = NULL;
static int wal_segment_size_mb;
static char *cluster_key_cmd = NULL;
static char *old_key_datadir = NULL;
/* internal vars */
@ -208,7 +203,6 @@ static const char *const subdirs[] = {
"global",
"pg_wal/archive_status",
"pg_commit_ts",
"pg_cryptokeys",
"pg_dynshmem",
"pg_notify",
"pg_serial",
@ -960,13 +954,12 @@ test_config_settings(void)
test_buffs = MIN_BUFS_FOR_CONNS(test_conns);
snprintf(cmd, sizeof(cmd),
"\"%s\" --boot -x0 %s %s "
"\"%s\" --boot -x0 %s "
"-c max_connections=%d "
"-c shared_buffers=%d "
"-c dynamic_shared_memory_type=%s "
"< \"%s\" > \"%s\" 2>&1",
backend_exec, boot_options,
term_fd_opt ? term_fd_opt : "",
test_conns, test_buffs,
dynamic_shared_memory_type,
DEVNULL, DEVNULL);
@ -997,13 +990,12 @@ test_config_settings(void)
}
snprintf(cmd, sizeof(cmd),
"\"%s\" --boot -x0 %s %s "
"\"%s\" --boot -x0 %s "
"-c max_connections=%d "
"-c shared_buffers=%d "
"-c dynamic_shared_memory_type=%s "
"< \"%s\" > \"%s\" 2>&1",
backend_exec, boot_options,
term_fd_opt ? term_fd_opt : "",
n_connections, test_buffs,
dynamic_shared_memory_type,
DEVNULL, DEVNULL);
@ -1193,13 +1185,6 @@ setup_config(void)
"password_encryption = md5");
}
if (cluster_key_cmd)
{
snprintf(repltok, sizeof(repltok), "cluster_key_command = '%s'",
escape_quotes(cluster_key_cmd));
conflines = replace_token(conflines, "#cluster_key_command = ''", repltok);
}
/*
* If group access has been enabled for the cluster then it makes sense to
* ensure that the log files also allow group access. Otherwise a backup
@ -1409,22 +1394,13 @@ bootstrap_template1(void)
/* Also ensure backend isn't confused by this environment var: */
unsetenv("PGCLIENTENCODING");
if (file_encryption_keylen != 0)
sprintf(buf, "%d", file_encryption_keylen);
else
buf[0] = '\0';
snprintf(cmd, sizeof(cmd),
"\"%s\" --boot -x1 -X %u %s %s %s %s %s %s %s %s",
"\"%s\" --boot -x1 -X %u %s %s %s",
backend_exec,
wal_segment_size_mb * (1024 * 1024),
data_checksums ? "-k" : "",
cluster_key_cmd ? "-K" : "", buf,
old_key_datadir ? "-u" : "",
old_key_datadir ? old_key_datadir : "",
boot_options,
debug ? "-d 5" : "",
term_fd_opt ? term_fd_opt : "");
debug ? "-d 5" : "");
PG_CMD_OPEN;
@ -2305,29 +2281,21 @@ usage(const char *progname)
" set default locale in the respective category for\n"
" new databases (default taken from environment)\n"));
printf(_(" --no-locale equivalent to --locale=C\n"));
printf(_(" --pwfile=FILE read the new superuser password from file\n"));
printf(_(" --pwfile=FILE read password for the new superuser from file\n"));
printf(_(" -T, --text-search-config=CFG\n"
" default text search configuration\n"));
printf(_(" -U, --username=NAME database superuser name\n"));
printf(_(" -W, --pwprompt prompt for the new superuser password\n"));
printf(_(" -W, --pwprompt prompt for a password for the new superuser\n"));
printf(_(" -X, --waldir=WALDIR location for the write-ahead log directory\n"));
printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n"));
printf(_("\nLess commonly used options:\n"));
printf(_(" -c --cluster-key-command=COMMAND\n"
" enable cluster file encryption and set command\n"
" to obtain the cluster key\n"));
printf(_(" -d, --debug generate lots of debugging output\n"));
printf(_(" -k, --data-checksums use data page checksums\n"));
printf(_(" -K, --file-encryption-keylen=LENGTH\n"
" bit length of the file encryption key\n"));
printf(_(" -L DIRECTORY where to find the input files\n"));
printf(_(" -n, --no-clean do not clean up after errors\n"));
printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" -R, --authprompt prompt for a passphrase or PIN\n"));
printf(_(" -s, --show show internal settings\n"));
printf(_(" -S, --sync-only only sync data directory\n"));
printf(_(" -u, --copy-encryption-keys=DATADIR\n"
" copy the file encryption key from another cluster\n"));
printf(_("\nOther options:\n"));
printf(_(" -V, --version output version information, then exit\n"));
printf(_(" -?, --help show this help, then exit\n"));
@ -2892,23 +2860,6 @@ initialize_data_directory(void)
/* Top level PG_VERSION is checked by bootstrapper, so make it first */
write_version_file(NULL);
if (pass_terminal_fd)
{
#ifndef WIN32
int terminal_fd = open("/dev/tty", O_RDWR, 0);
#else
int terminal_fd = open("CONOUT$", O_RDWR, 0);
#endif
if (terminal_fd < 0)
{
pg_log_error(_("%s: could not open terminal: %s\n"),
progname, strerror(errno));
exit(1);
}
term_fd_opt = psprintf("-R %d", terminal_fd);
}
/* Select suitable configuration settings */
set_null_conf();
test_config_settings();
@ -2932,9 +2883,8 @@ initialize_data_directory(void)
fflush(stdout);
snprintf(cmd, sizeof(cmd),
"\"%s\" %s %s template1 >%s",
"\"%s\" %s template1 >%s",
backend_exec, backend_options,
term_fd_opt ? term_fd_opt : "",
DEVNULL);
PG_CMD_OPEN;
@ -3007,11 +2957,7 @@ main(int argc, char *argv[])
{"waldir", required_argument, NULL, 'X'},
{"wal-segsize", required_argument, NULL, 12},
{"data-checksums", no_argument, NULL, 'k'},
{"authprompt", no_argument, NULL, 'R'},
{"file-encryption-keylen", required_argument, NULL, 'K'},
{"allow-group-access", no_argument, NULL, 'g'},
{"cluster-key-command", required_argument, NULL, 'c'},
{"copy-encryption-keys", required_argument, NULL, 'u'},
{NULL, 0, NULL, 0}
};
@ -3053,7 +2999,7 @@ main(int argc, char *argv[])
/* process command-line options */
while ((c = getopt_long(argc, argv, "A:c:dD:E:gkK:L:nNRsST:u:U:WX:", long_options, &option_index)) != -1)
while ((c = getopt_long(argc, argv, "A:dD:E:gkL:nNsST:U:WX:", long_options, &option_index)) != -1)
{
switch (c)
{
@ -3099,12 +3045,6 @@ main(int argc, char *argv[])
case 'N':
do_sync = false;
break;
case 'R':
pass_terminal_fd = true;
break;
case 'K':
file_encryption_keylen = atoi(optarg);
break;
case 'S':
sync_only = true;
break;
@ -3141,12 +3081,6 @@ main(int argc, char *argv[])
case 9:
pwfilename = pg_strdup(optarg);
break;
case 'c':
cluster_key_cmd = pg_strdup(optarg);
break;
case 'u':
old_key_datadir = pg_strdup(optarg);
break;
case 's':
show_setting = true;
break;
@ -3217,37 +3151,6 @@ main(int argc, char *argv[])
exit(1);
}
#ifndef USE_OPENSSL
if (cluster_key_cmd)
{
pg_log_error("cluster file encryption is not supported because OpenSSL is not supported by this build");
exit(1);
}
#endif
if (old_key_datadir != NULL && cluster_key_cmd == NULL)
{
pg_log_error("copying encryption keys requires the cluster key command to be specified");
exit(1);
}
if (file_encryption_keylen != 0 && cluster_key_cmd == NULL)
{
pg_log_error("a non-zero file encryption key length requires the cluster key command to be specified");
exit(1);
}
if (file_encryption_keylen != 0 && file_encryption_keylen != 128 &&
file_encryption_keylen != 192 && file_encryption_keylen != 256)
{
pg_log_error("invalid file encrypt key length; supported values are 0 (disabled), 128, 192, and 256");
exit(1);
}
/* set the default */
if (file_encryption_keylen == 0 && cluster_key_cmd != NULL)
file_encryption_keylen = 128;
check_authmethod_unspecified(&authmethodlocal);
check_authmethod_unspecified(&authmethodhost);
@ -3315,11 +3218,6 @@ main(int argc, char *argv[])
else
printf(_("Data page checksums are disabled.\n"));
if (cluster_key_cmd)
printf(_("Cluster file encryption is enabled.\n"));
else
printf(_("Cluster file encryption is disabled.\n"));
if (pwprompt || pwfilename)
get_su_pwd();

View File

@ -1 +0,0 @@
/pg_alterckey

View File

@ -1,38 +0,0 @@
#-------------------------------------------------------------------------
#
# Makefile for src/bin/pg_alterckey
#
# Copyright (c) 1998-2020, PostgreSQL Global Development Group
#
# src/bin/pg_alterckey/Makefile
#
#-------------------------------------------------------------------------
PGFILEDESC = "pg_alterckey - alter the cluster key"
PGAPPICON=win32
subdir = src/bin/pg_alterckey
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = \
$(WIN32RES) \
pg_alterckey.o
all: pg_alterckey
pg_alterckey: $(OBJS) | submake-libpgport
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
install: all installdirs
$(INSTALL_PROGRAM) pg_alterckey$(X) '$(DESTDIR)$(bindir)/pg_alterckey$(X)'
installdirs:
$(MKDIR_P) '$(DESTDIR)$(bindir)'
uninstall:
rm -f '$(DESTDIR)$(bindir)/pg_alterckey$(X)'
clean distclean maintainer-clean:
rm -f pg_alterckey$(X) $(OBJS)
rm -rf tmp_check

View File

@ -1,694 +0,0 @@
/*-------------------------------------------------------------------------
*
* pg_alterckey.c
* A utility to change the cluster key (key encryption key, KEK)
* used for cluster file encryption.
*
* The theory of operation is fairly simple:
* 1. Create lock file
* 2. Retrieve current and new cluster key using the supplied
* commands.
* 3. Revert any failed alter operation.
* 4. Create a temporary directory in PGDATA
* 5. For each data encryption key in the pg_cryptokeys directory,
* decrypt it with the old cluster key and re-encrypt it
* with the new cluster key.
* 6. Make the temporary directory the new pg_cryptokeys directory.
* 7. Remove lock file
*
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/bin/pg_alterckey/pg_alterckey.c
*
*-------------------------------------------------------------------------
*/
#define FRONTEND 1
#include "postgres_fe.h"
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/hex_decode.h"
#include "common/restricted_token.h"
#include "crypto/kmgr.h"
#include "common/logging.h"
#include "getopt_long.h"
#include "pg_getopt.h"
typedef enum {
SUCCESS_EXIT = 0,
ERROR_EXIT,
RMDIR_EXIT,
REPAIR_EXIT
} exit_action;
static int lock_fd = -1;
static bool pass_terminal_fd = false;
int terminal_fd = -1;
static bool repair_mode = false;
static char *old_cluster_key_cmd = NULL,
*new_cluster_key_cmd = NULL;
static char old_cluster_key[KMGR_CLUSTER_KEY_LEN],
new_cluster_key[KMGR_CLUSTER_KEY_LEN];
static CryptoKey in_key, data_key, out_key;
static char top_path[MAXPGPATH], pid_path[MAXPGPATH], live_path[MAXPGPATH],
new_path[MAXPGPATH], old_path[MAXPGPATH];
static char *DataDir = NULL;
static const char *progname;
static void create_lockfile(void);
static void recover_failure(void);
static void retrieve_cluster_keys(void);
static void bzero_keys_and_exit(exit_action action);
static void reencrypt_data_keys(void);
static void install_new_keys(void);
static void
usage(const char *progname)
{
printf(_("%s changes the cluster key of a PostgreSQL database cluster.\n\n"), progname);
printf(_("Usage:\n"));
printf(_(" %s [OPTION] old_cluster_key_command new_cluster_key_command [DATADIR]\n"), progname);
printf(_(" %s [repair_option] [DATADIR]\n"), progname);
printf(_("\nOptions:\n"));
printf(_(" -R, --authprompt prompt for a passphrase or PIN\n"));
printf(_(" [-D, --pgdata=]DATADIR data directory\n"));
printf(_(" -V, --version output version information, then exit\n"));
printf(_(" -?, --help show this help, then exit\n"));
printf(_("\nRepair options:\n"));
printf(_(" -r, --repair repair previous failure\n"));
printf(_("\nIf no data directory (DATADIR) is specified, "
"the environment variable PGDATA\nis used.\n\n"));
printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
}
int
main(int argc, char *argv[])
{
static struct option long_options1[] = {
{"authprompt", required_argument, NULL, 'R'},
{"repair", required_argument, NULL, 'r'},
{NULL, 0, NULL, 0}
};
static struct option long_options2[] = {
{"pgdata", required_argument, NULL, 'D'},
{NULL, 0, NULL, 0}
};
int c;
pg_logging_init(argv[0]);
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_alterckey"));
progname = get_progname(argv[0]);
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
usage(progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
puts("pg_alterckey (PostgreSQL) " PG_VERSION);
exit(0);
}
}
/* check for -r/-R */
while ((c = getopt_long(argc, argv, "rR", long_options1, NULL)) != -1)
{
switch (c)
{
case 'r':
repair_mode = true;
break;
case 'R':
pass_terminal_fd = true;
break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
}
if (!repair_mode)
{
/* get cluster key commands */
if (optind < argc)
old_cluster_key_cmd = argv[optind++];
else
{
pg_log_error("missing old_cluster_key_command");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
exit(1);
}
if (optind < argc)
new_cluster_key_cmd = argv[optind++];
else
{
pg_log_error("missing new_cluster_key_command");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
exit(1);
}
}
/* check for datadir */
argc -= optind;
argv += optind;
while ((c = getopt_long(argc, argv, "D:", long_options2, NULL)) != -1)
{
switch (c)
{
case 'D':
DataDir = optarg;
break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
}
if (DataDir == NULL)
{
if (optind < argc)
DataDir = argv[optind++];
else
DataDir = getenv("PGDATA");
}
/*
* Disallow running as root because we create directories in PGDATA
*/
#ifndef WIN32
if (geteuid() == 0)
{
pg_log_error("%s: cannot be run as root\n"
"Please log in (using, e.g., \"su\") as the "
"(unprivileged) user that will\n"
"own the server process.\n",
progname);
exit(1);
}
#endif
get_restricted_token();
/* Set mask based on PGDATA permissions */
if (!GetDataDirectoryCreatePerm(DataDir))
{
pg_log_error("could not read permissions of directory \"%s\": %m",
DataDir);
exit(1);
}
umask(pg_mode_mask);
snprintf(top_path, sizeof(top_path), "%s/%s", DataDir, KMGR_DIR);
snprintf(pid_path, sizeof(pid_path), "%s/%s", DataDir, KMGR_DIR_PID);
snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR);
snprintf(new_path, sizeof(new_path), "%s/%s", DataDir, NEW_KMGR_DIR);
snprintf(old_path, sizeof(old_path), "%s/%s", DataDir, OLD_KMGR_DIR);
/* Complain if any arguments remain */
if (optind < argc)
{
pg_log_error("too many command-line arguments (first is \"%s\")",
argv[optind]);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
exit(1);
}
if (DataDir == NULL)
{
pg_log_error("no data directory specified");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
create_lockfile();
recover_failure();
if (!repair_mode)
{
retrieve_cluster_keys();
reencrypt_data_keys();
install_new_keys();
}
#ifndef WIN32
/* remove file system reference to file */
if (unlink(pid_path) < 0)
{
pg_log_error("could not delete lock file \"%s\": %m", KMGR_DIR_PID);
exit(1);
}
#endif
close (lock_fd);
bzero_keys_and_exit(SUCCESS_EXIT);
}
/* This prevents almost all cases of concurrent access */
void
create_lockfile(void)
{
struct stat buffer;
char lock_pid_str[20];
if (stat(top_path, &buffer) != 0 || !S_ISDIR(buffer.st_mode))
{
pg_log_error("cluster file encryption directory \"%s\" is missing; is it enabled?", KMGR_DIR_PID);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
/* Does a lockfile exist? */
if ((lock_fd = open(pid_path, O_RDONLY, 0)) != -1)
{
int lock_pid;
int len;
/* read the PID */
if ((len = read(lock_fd, lock_pid_str, sizeof(lock_pid_str) - 1)) == 0)
{
pg_log_error("cannot read pid from lock file \"%s\": %m", KMGR_DIR_PID);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
lock_pid_str[len] = '\0';
if ((lock_pid = atoi(lock_pid_str)) == 0)
{
pg_log_error("invalid pid in lock file \"%s\": %m", KMGR_DIR_PID);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
/* Is the PID running? */
if (kill(lock_pid, 0) == 0)
{
pg_log_error("active process %d currently holds a lock on this operation, recorded in \"%s\"",
lock_pid, KMGR_DIR_PID);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
close(lock_fd);
if (repair_mode)
printf("old lock file removed\n");
/*
* pid is no longer running, so remove the lock file.
* This is not 100% safe from concurrent access, e.g.:
*
* process 1 exits and leaves stale lock file
* process 2 checks stale lock file of process 1
* process 3 checks stale lock file of process 1
* process 2 remove the lock file of process 1
* process 4 creates a lock file
* process 3 remove the lock file of process 4
* process 5 creates a lock file
*
* The sleep(2) helps with this since it reduces the likelihood
* a process that did an unlock will interfere with another unlock
* process. We could ask users to remove the lock, but that seems
* even more error-prone, especially since this might happen
* on server start. Many PG tools seem to have problems with
* concurrent access.
*/
unlink(pid_path);
/* Sleep to reduce the likelihood of concurrent unlink */
pg_usleep(2000000L); /* 2 seconds */
}
/* Create our own lockfile? */
#ifndef WIN32
lock_fd = open(pid_path, O_RDWR | O_CREAT | O_EXCL, pg_file_create_mode);
#else
/* delete on close */
lock_fd = open(pid_path, O_RDWR | O_CREAT | O_EXCL | O_TEMPORARY,
pg_file_create_mode);
#endif
if (lock_fd == -1)
{
if (errno == EEXIST)
pg_log_error("an active process currently holds a lock on this operation, recorded in \"%s\"",
KMGR_DIR_PID);
else
pg_log_error("unable to create lock file \"%s\": %m", KMGR_DIR_PID);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
snprintf(lock_pid_str, sizeof(lock_pid_str), "%d\n", getpid());
if (write(lock_fd, lock_pid_str, strlen(lock_pid_str)) != strlen(lock_pid_str))
{
pg_log_error("could not write pid to lock file \"%s\": %m", KMGR_DIR_PID);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
}
/*
* recover_failure
*
* A previous pg_alterckey might have failed, so it might need recovery.
* The normal operation is:
* 1. reencrypt LIVE_KMGR_DIR -> NEW_KMGR_DIR
* 2. rename KMGR_DIR -> OLD_KMGR_DIR
* 3. rename NEW_KMGR_DIR -> LIVE_KMGR_DIR
* remove OLD_KMGR_DIR
*
* There are eight possible directory configurations:
*
* LIVE_KMGR_DIR NEW_KMGR_DIR OLD_KMGR_DIR
*
* Normal:
* 0. normal X
* 1. remove new X X
* 2. install new X X
* 3. remove old X X
*
* Abnormal:
* fatal
* restore old X
* install new X
* remove old and new X X X
*
* We don't handle the abnormal cases, just report an error.
*/
static void
recover_failure(void)
{
struct stat buffer;
bool is_live, is_new, is_old;
is_live = !stat(live_path, &buffer);
is_new = !stat(new_path, &buffer);
is_old = !stat(old_path, &buffer);
/* normal #0 */
if (is_live && !is_new && !is_old)
{
if (repair_mode)
printf("repair unnecessary\n");
return;
}
/* remove new #1 */
else if (is_live && is_new && !is_old)
{
if (!rmtree(new_path, true))
{
pg_log_error("unable to remove new directory \"%s\": %m", NEW_KMGR_DIR);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
printf(_("removed files created during previously aborted alter operation\n"));
return;
}
/* install new #2 */
else if (!is_live && is_new && is_old)
{
if (rename(new_path, live_path) != 0)
{
pg_log_error("unable to rename directory \"%s\" to \"%s\": %m",
NEW_KMGR_DIR, LIVE_KMGR_DIR);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
printf(_("Installed new cluster password supplied in previous alter operation\n"));
return;
}
/* remove old #3 */
else if (is_live && !is_new && is_old)
{
if (!rmtree(old_path, true))
{
pg_log_error("unable to remove old directory \"%s\": %m", OLD_KMGR_DIR);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
printf(_("Removed old files invalidated during previous alter operation\n"));
return;
}
else
{
pg_log_error("cluster file encryption directory \"%s\" is in an abnormal state and cannot be processed",
KMGR_DIR);
fprintf(stderr, _("Exiting with no changes made.\n"));
exit(1);
}
}
/* Retrieve old and new cluster keys */
void
retrieve_cluster_keys()
{
int cluster_key_len;
char cluster_key_hex[ALLOC_KMGR_CLUSTER_KEY_LEN];
/*
* If we have been asked to pass an open file descriptor to the user
* terminal to the commands, set one up.
*/
if (pass_terminal_fd)
{
#ifndef WIN32
terminal_fd = open("/dev/tty", O_RDWR, 0);
#else
terminal_fd = open("CONOUT$", O_RDWR, 0);
#endif
if (terminal_fd < 0)
{
pg_log_error(_("%s: could not open terminal: %s\n"),
progname, strerror(errno));
exit(1);
}
}
/* Get old key encryption key from the cluster key command */
cluster_key_len = kmgr_run_cluster_key_command(old_cluster_key_cmd,
(char *) cluster_key_hex,
ALLOC_KMGR_CLUSTER_KEY_LEN,
live_path);
if (hex_decode(cluster_key_hex, cluster_key_len, (char *) old_cluster_key) !=
KMGR_CLUSTER_KEY_LEN)
{
pg_log_error("cluster key must be at %d hex bytes", KMGR_CLUSTER_KEY_LEN);
bzero_keys_and_exit(ERROR_EXIT);
}
/*
* Create new key directory here in case the new cluster key command needs it
* to exist.
*/
if (mkdir(new_path, pg_dir_create_mode) != 0)
{
pg_log_error("unable to create new cluster key directory \"%s\": %m", NEW_KMGR_DIR);
bzero_keys_and_exit(ERROR_EXIT);
}
/* Get new key */
cluster_key_len = kmgr_run_cluster_key_command(new_cluster_key_cmd,
(char *) cluster_key_hex,
ALLOC_KMGR_CLUSTER_KEY_LEN,
live_path);
if (hex_decode(cluster_key_hex, cluster_key_len, (char *) new_cluster_key) !=
KMGR_CLUSTER_KEY_LEN)
{
pg_log_error("cluster key must be at %d hex bytes", KMGR_CLUSTER_KEY_LEN);
bzero_keys_and_exit(ERROR_EXIT);
}
if (pass_terminal_fd)
close(terminal_fd);
/* output newline */
puts("");
if (strcmp(old_cluster_key, new_cluster_key) == 0)
{
pg_log_error("cluster keys are identical, exiting\n");
bzero_keys_and_exit(RMDIR_EXIT);
}
}
/* Decrypt old keys encrypted with old pass phrase and reencrypt with new one */
void
reencrypt_data_keys(void)
{
DIR *dir;
struct dirent *de;
PgCipherCtx *old_ctx, *new_ctx;
if ((dir = opendir(live_path)) == NULL)
{
pg_log_error("unable to open live cluster key directory \"%s\": %m", LIVE_KMGR_DIR);
bzero_keys_and_exit(RMDIR_EXIT);
}
old_ctx = pg_cipher_ctx_create(PG_CIPHER_AES_GCM,
(unsigned char *)old_cluster_key,
KMGR_CLUSTER_KEY_LEN, true);
if (!old_ctx)
pg_log_error("could not initialize encryption context");
new_ctx = pg_cipher_ctx_create(PG_CIPHER_AES_GCM,
(unsigned char *)new_cluster_key,
KMGR_CLUSTER_KEY_LEN, true);
if (!new_ctx)
pg_log_error("could not initialize encryption context");
while ((de = readdir(dir)) != NULL)
{
/*
* We copy only the numeric files/keys, since there might be encrypted
* cluster key files in the old directory that only match the old key.
*/
if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
{
char src_path[MAXPGPATH], dst_path[MAXPGPATH];
int src_fd, dst_fd;
int len;
uint32 id = strtoul(de->d_name, NULL, 10);
CryptoKeyFilePath(src_path, live_path, id);
CryptoKeyFilePath(dst_path, new_path, id);
if ((src_fd = open(src_path, O_RDONLY | PG_BINARY, 0)) < 0)
{
pg_log_error("could not open file \"%s\": %m", src_path);
bzero_keys_and_exit(RMDIR_EXIT);
}
if ((dst_fd = open(dst_path, O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
pg_file_create_mode)) < 0)
{
pg_log_error("could not open file \"%s\": %m", dst_path);
bzero_keys_and_exit(RMDIR_EXIT);
}
/* Read the source key */
len = read(src_fd, &in_key, sizeof(CryptoKey));
if (len != sizeof(CryptoKey))
{
if (len < 0)
pg_log_error("could read file \"%s\": %m", src_path);
else
pg_log_error("could read file \"%s\": read %d of %zu",
src_path, len, sizeof(CryptoKey));
bzero_keys_and_exit(RMDIR_EXIT);
}
/* decrypt with old key */
if (!kmgr_unwrap_key(old_ctx, &in_key, &data_key))
{
pg_log_error("incorrect old key specified");
bzero_keys_and_exit(RMDIR_EXIT);
}
/* encrypt with new key */
if (!kmgr_wrap_key(new_ctx, &data_key, &out_key))
{
pg_log_error("could not encrypt new key");
bzero_keys_and_exit(RMDIR_EXIT);
}
/* Write to the dest key */
len = write(dst_fd, &out_key, sizeof(CryptoKey));
if (len != sizeof(CryptoKey))
{
pg_log_error("could not write fie \"%s\"", dst_path);
bzero_keys_and_exit(RMDIR_EXIT);
}
close(src_fd);
close(dst_fd);
}
}
/* The cluster key is correct, free the cipher context */
pg_cipher_ctx_free(old_ctx);
pg_cipher_ctx_free(new_ctx);
closedir(dir);
}
void
install_new_keys(void)
{
/* add fsyncs? XXX */
if (rename(live_path, old_path) != 0)
{
pg_log_error("unable to rename directory \"%s\" to \"%s\": %m",
LIVE_KMGR_DIR, OLD_KMGR_DIR);
bzero_keys_and_exit(RMDIR_EXIT);
}
if (rename(new_path, live_path) != 0)
{
pg_log_error("unable to rename directory \"%s\" to \"%s\": %m",
NEW_KMGR_DIR, LIVE_KMGR_DIR);
bzero_keys_and_exit(REPAIR_EXIT);
}
if (!rmtree(old_path, true))
{
pg_log_error("unable to remove old directory \"%s\": %m", OLD_KMGR_DIR);
bzero_keys_and_exit(REPAIR_EXIT);
}
}
void
bzero_keys_and_exit(exit_action action)
{
explicit_bzero(old_cluster_key, sizeof(old_cluster_key));
explicit_bzero(new_cluster_key, sizeof(new_cluster_key));
explicit_bzero(&in_key, sizeof(in_key));
explicit_bzero(&data_key, sizeof(data_key));
explicit_bzero(&out_key, sizeof(out_key));
if (action == RMDIR_EXIT)
{
if (!rmtree(new_path, true))
pg_log_error("unable to remove new directory \"%s\": %m", NEW_KMGR_DIR);
printf("Re-running pg_alterckey to repair might be needed before the next server start\n");
exit(1);
}
else if (action == REPAIR_EXIT)
{
unlink(pid_path);
printf("Re-running pg_alterckey to repair might be needed before the next server start\n");
}
/* return 0 or 1 */
exit(action != SUCCESS_EXIT);
}

View File

@ -25,7 +25,6 @@
#include "access/xlog_internal.h"
#include "catalog/pg_control.h"
#include "common/controldata_utils.h"
#include "common/kmgr_utils.h"
#include "common/logging.h"
#include "getopt_long.h"
#include "pg_getopt.h"
@ -335,7 +334,5 @@ main(int argc, char *argv[])
ControlFile->data_checksum_version);
printf(_("Mock authentication nonce: %s\n"),
mock_auth_nonce_str);
printf(_("File encryption key length: %d\n"),
ControlFile->file_encryption_keylen);
return 0;
}

View File

@ -79,7 +79,6 @@ typedef enum
static bool do_wait = true;
static int wait_seconds = DEFAULT_WAIT;
static bool wait_seconds_arg = false;
static bool pass_terminal_fd = false;
static bool silent_mode = false;
static ShutdownMode shutdown_mode = FAST_MODE;
static int sig = SIGINT; /* default */
@ -443,7 +442,7 @@ free_readfile(char **optlines)
static pgpid_t
start_postmaster(void)
{
char cmd[MAXPGPATH], *term_fd_opt = NULL;
char cmd[MAXPGPATH];
#ifndef WIN32
pgpid_t pm_pid;
@ -468,19 +467,6 @@ start_postmaster(void)
/* fork succeeded, in child */
if (pass_terminal_fd)
{
int terminal_fd = open("/dev/tty", O_RDWR, 0);
if (terminal_fd < 0)
{
write_stderr(_("%s: could not open terminal: %s\n"),
progname, strerror(errno));
exit(1);
}
term_fd_opt = psprintf(" -R %d", terminal_fd);
}
/*
* If possible, detach the postmaster process from the launching process
* group and make it a group leader, so that it doesn't get signaled along
@ -501,14 +487,12 @@ start_postmaster(void)
* has the same PID as the current child process.
*/
if (log_file != NULL)
snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s%s < \"%s\" >> \"%s\" 2>&1",
snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s < \"%s\" >> \"%s\" 2>&1",
exec_path, pgdata_opt, post_opts,
term_fd_opt ? term_fd_opt : "",
DEVNULL, log_file);
else
snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s%s < \"%s\" 2>&1",
exec_path, pgdata_opt, post_opts,
term_fd_opt ? term_fd_opt : "", DEVNULL);
snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s < \"%s\" 2>&1",
exec_path, pgdata_opt, post_opts, DEVNULL);
(void) execl("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
@ -529,21 +513,6 @@ start_postmaster(void)
PROCESS_INFORMATION pi;
const char *comspec;
if (pass_terminal_fd)
{
/* Hopefully we can read and write CONOUT, see simple_prompt() XXX */
/* Do CreateRestrictedProcess() children even inherit open file descriptors? XXX */
int terminal_fd = open("CONOUT$", O_RDWR, 0);
if (terminal_fd < 0)
{
write_stderr(_("%s: could not open terminal: %s\n"),
progname, strerror(errno));
exit(1);
}
term_fd_opt = psprintf(" -R %d", terminal_fd);
}
/* Find CMD.EXE location using COMSPEC, if it's set */
comspec = getenv("COMSPEC");
if (comspec == NULL)
@ -584,14 +553,12 @@ start_postmaster(void)
else
close(fd);
snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s%s < \"%s\" >> \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts,
term_fd_opt ? term_fd_opt : "", DEVNULL, log_file);
snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
}
else
snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s%s < \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts,
term_fd_opt ? term_fd_opt : "", DEVNULL);
snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts, DEVNULL);
if (!CreateRestrictedProcess(cmd, &pi, false))
{
@ -722,8 +689,7 @@ wait_for_postmaster(pgpid_t pm_pid, bool do_checkpoint)
}
else
#endif
if (!pass_terminal_fd)
print_msg(".");
print_msg(".");
}
pg_usleep(USEC_PER_SEC / WAITS_PER_SEC);
@ -2100,7 +2066,6 @@ do_help(void)
printf(_(" -o, --options=OPTIONS command line options to pass to postgres\n"
" (PostgreSQL server executable) or initdb\n"));
printf(_(" -p PATH-TO-POSTGRES normally not necessary\n"));
printf(_(" -R, --authprompt prompt for a paasphrase or PIN\n"));
printf(_("\nOptions for stop or restart:\n"));
printf(_(" -m, --mode=MODE MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
@ -2295,7 +2260,6 @@ main(int argc, char **argv)
{"mode", required_argument, NULL, 'm'},
{"pgdata", required_argument, NULL, 'D'},
{"options", required_argument, NULL, 'o'},
{"authprompt", no_argument, NULL, 'R'},
{"silent", no_argument, NULL, 's'},
{"timeout", required_argument, NULL, 't'},
{"core-files", no_argument, NULL, 'c'},
@ -2368,7 +2332,7 @@ main(int argc, char **argv)
/* process command-line options */
while (optind < argc)
{
while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:RsS:t:U:wW",
while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW",
long_options, &option_index)) != -1)
{
switch (c)
@ -2421,9 +2385,6 @@ main(int argc, char **argv)
case 'P':
register_password = pg_strdup(optarg);
break;
case 'R':
pass_terminal_fd = true;
break;
case 's':
silent_mode = true;
break;

View File

@ -804,8 +804,6 @@ PrintControlValues(bool guessed)
(ControlFile.float8ByVal ? _("by value") : _("by reference")));
printf(_("Data page checksum version: %u\n"),
ControlFile.data_checksum_version);
printf(_("File encryption key length: %d\n"),
ControlFile.file_encryption_keylen);
}

View File

@ -28,7 +28,6 @@
#include "catalog/pg_tablespace_d.h"
#include "common/hashfn.h"
#include "common/kmgr_utils.h"
#include "common/string.h"
#include "datapagemap.h"
#include "filemap.h"
@ -108,13 +107,6 @@ static const char *excludeDirContents[] =
/* Contents removed on startup, see AsyncShmemInit(). */
"pg_notify",
/*
* Skip cryptographic keys. It's generally not a good idea to copy the
* cryptographic keys from source database because these might use
* different cluster key.
*/
KMGR_DIR,
/*
* Old contents are loaded for possible debugging but are not required for
* normal operation, see SerialInit().

View File

@ -10,7 +10,6 @@
#include "postgres_fe.h"
#include "catalog/pg_authid_d.h"
#include "common/kmgr_utils.h"
#include "fe_utils/string_utils.h"
#include "mb/pg_wchar.h"
#include "pg_upgrade.h"
@ -28,7 +27,6 @@ static void check_for_tables_with_oids(ClusterInfo *cluster);
static void check_for_reg_data_type_usage(ClusterInfo *cluster);
static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
static void check_for_pg_role_prefix(ClusterInfo *cluster);
static void check_for_cluster_key_failure(ClusterInfo *cluster);
static void check_for_new_tablespace_dir(ClusterInfo *new_cluster);
static char *get_canonical_locale_name(int category, const char *locale);
@ -141,9 +139,6 @@ check_and_dump_old_cluster(bool live_check)
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
check_for_pg_role_prefix(&old_cluster);
if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1400)
check_for_cluster_key_failure(&old_cluster);
if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
check_for_jsonb_9_4_usage(&old_cluster);
@ -178,9 +173,6 @@ check_new_cluster(void)
check_loadable_libraries();
if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1400)
check_for_cluster_key_failure(&new_cluster);
switch (user_opts.transfer_mode)
{
case TRANSFER_MODE_CLONE:
@ -1277,32 +1269,6 @@ check_for_pg_role_prefix(ClusterInfo *cluster)
}
/*
* check_for_cluster_key_failure()
*
* Make sure there was no unrepaired pg_alterckey failure
*/
static void
check_for_cluster_key_failure(ClusterInfo *cluster)
{
struct stat buffer;
if (stat (KMGR_DIR_PID, &buffer) == 0)
{
if (cluster == &old_cluster)
pg_fatal("The source cluster had a pg_alterckey failure that needs repair or\n"
"pg_alterckey is running. Run pg_alterckey --repair or wait for it\n"
"to complete.\n");
else
pg_fatal("The target cluster had a pg_alterckey failure that needs repair or\n"
"pg_alterckey is running. Run pg_alterckey --repair or wait for it\n"
"to complete.\n");
}
check_ok();
}
/*
* get_canonical_locale_name
*

View File

@ -9,16 +9,10 @@
#include "postgres_fe.h"
#include <dirent.h>
#include <ctype.h>
#include "pg_upgrade.h"
#include "access/xlog_internal.h"
#include "common/controldata_utils.h"
#include "common/file_utils.h"
#include "common/kmgr_utils.h"
/*
* get_control_data()
*
@ -65,7 +59,6 @@ get_control_data(ClusterInfo *cluster, bool live_check)
bool got_date_is_int = false;
bool got_data_checksum_version = false;
bool got_cluster_state = false;
int got_file_encryption_keylen = 0;
char *lc_collate = NULL;
char *lc_ctype = NULL;
char *lc_monetary = NULL;
@ -209,13 +202,6 @@ get_control_data(ClusterInfo *cluster, bool live_check)
got_data_checksum_version = true;
}
/* Only in <= 14 */
if (GET_MAJOR_VERSION(cluster->major_version) <= 1400)
{
cluster->controldata.file_encryption_keylen = 0;
got_file_encryption_keylen = true;
}
/* we have the result of cmd in "output". so parse it line by line now */
while (fgets(bufin, sizeof(bufin), output))
{
@ -499,18 +485,6 @@ get_control_data(ClusterInfo *cluster, bool live_check)
cluster->controldata.data_checksum_version = str2uint(p);
got_data_checksum_version = true;
}
else if ((p = strstr(bufin, "File encryption key length:")) != NULL)
{
p = strchr(p, ':');
if (p == NULL || strlen(p) <= 1)
pg_fatal("%d: controldata retrieval problem\n", __LINE__);
p++; /* remove ':' char */
/* used later for contrib check */
cluster->controldata.file_encryption_keylen = atoi(p);
got_file_encryption_keylen = true;
}
}
pclose(output);
@ -565,8 +539,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
!got_index || !got_toast ||
(!got_large_object &&
cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
!got_date_is_int || !got_data_checksum_version ||
!got_file_encryption_keylen)
!got_date_is_int || !got_data_checksum_version)
{
if (cluster == &old_cluster)
pg_log(PG_REPORT,
@ -632,10 +605,6 @@ get_control_data(ClusterInfo *cluster, bool live_check)
if (!got_data_checksum_version)
pg_log(PG_REPORT, " data checksum version\n");
/* value added in Postgres 14 */
if (!got_file_encryption_keylen)
pg_log(PG_REPORT, " file encryption key length\n");
pg_fatal("Cannot continue without required control information, terminating\n");
}
}
@ -700,15 +669,6 @@ check_control_data(ControlData *oldctrl,
pg_fatal("old cluster uses data checksums but the new one does not\n");
else if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
pg_fatal("old and new cluster pg_controldata checksum versions do not match\n");
/*
* We cannot upgrade if the old cluster file encryption key length
* doesn't match the new one.
*/
if (oldctrl->file_encryption_keylen != newctrl->file_encryption_keylen)
pg_fatal("old and new clusters use different file encryption key lengths or\n"
"one cluster uses encryption and the other does not");
}

View File

@ -11,7 +11,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef HAVE_COPYFILE_H
#include <copyfile.h>
#endif
@ -22,7 +21,6 @@
#include "access/visibilitymap.h"
#include "common/file_perm.h"
#include "common/file_utils.h"
#include "pg_upgrade.h"
#include "storage/bufpage.h"
#include "storage/checksum.h"

View File

@ -52,7 +52,6 @@ parseCommandLine(int argc, char *argv[])
{"check", no_argument, NULL, 'c'},
{"link", no_argument, NULL, 'k'},
{"retain", no_argument, NULL, 'r'},
{"authprompt", no_argument, NULL, 'R'},
{"jobs", required_argument, NULL, 'j'},
{"socketdir", required_argument, NULL, 's'},
{"verbose", no_argument, NULL, 'v'},
@ -103,7 +102,7 @@ parseCommandLine(int argc, char *argv[])
if (os_user_effective_id == 0)
pg_fatal("%s: cannot be run as root\n", os_info.progname);
while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rRs:U:v",
while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v",
long_options, &optindex)) != -1)
{
switch (option)
@ -181,10 +180,6 @@ parseCommandLine(int argc, char *argv[])
log_opts.retain = true;
break;
case 'R':
user_opts.pass_terminal_fd = true;
break;
case 's':
user_opts.socketdir = pg_strdup(optarg);
break;

View File

@ -11,7 +11,6 @@
#include <sys/time.h>
#include "libpq-fe.h"
#include "common/kmgr_utils.h"
/* Use port in the private/dynamic port number range */
#define DEF_PGUPORT 50432
@ -220,7 +219,6 @@ typedef struct
bool date_is_int;
bool float8_pass_by_value;
bool data_checksum_version;
int file_encryption_keylen;
} ControlData;
/*
@ -295,7 +293,6 @@ typedef struct
int jobs; /* number of processes/threads to use */
char *socketdir; /* directory to use for Unix sockets */
bool ind_coll_unknown; /* mark unknown index collation versions */
bool pass_terminal_fd; /* pass -R to pg_ctl? */
} UserOpts;
typedef struct

View File

@ -244,9 +244,8 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
* vacuumdb --freeze actually freezes the tuples.
*/
snprintf(cmd, sizeof(cmd),
"\"%s/pg_ctl\" -w%s -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
cluster->bindir, user_opts.pass_terminal_fd ? " -R" : "",
SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
"\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
(cluster->controldata.cat_ver >=
BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" :
" -c autovacuum=off -c autovacuum_freeze_max_age=2000000000",

View File

@ -62,7 +62,6 @@ OBJS_COMMON = \
ip.o \
jsonapi.o \
keywords.o \
kmgr_utils.o \
kwlookup.o \
link-canary.o \
md5_common.o \
@ -83,12 +82,10 @@ OBJS_COMMON = \
ifeq ($(with_openssl),yes)
OBJS_COMMON += \
cipher_openssl.o \
protocol_openssl.o \
cryptohash_openssl.o
else
OBJS_COMMON += \
cipher.o \
cryptohash.o \
md5.o \
sha2.o

View File

@ -1,70 +0,0 @@
/*-------------------------------------------------------------------------
*
* cipher.c
* Shared frontend/backend for cryptographic functions
*
* Copyright (c) 2020, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/cipher.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include "common/cipher.h"
static void cipher_failure(void) pg_attribute_noreturn();
PgCipherCtx *
pg_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
{
cipher_failure();
return NULL; /* keep compiler quiet */
}
void
pg_cipher_ctx_free(PgCipherCtx *ctx)
{
cipher_failure();
}
bool
pg_cipher_encrypt(PgCipherCtx *ctx, const unsigned char *plaintext,
const int inlen, unsigned char *ciphertext, int *outlen,
const unsigned char *iv, const int ivlen,
unsigned char *outtag, const int taglen)
{
cipher_failure();
return false; /* keep compiler quiet */
}
bool
pg_cipher_decrypt(PgCipherCtx *ctx, const unsigned char *ciphertext,
const int inlen, unsigned char *plaintext, int *outlen,
const unsigned char *iv, const int ivlen,
unsigned char *intag, const int taglen)
{
cipher_failure();
return false; /* keep compiler quiet */
}
static void
cipher_failure(void)
{
#ifndef FRONTEND
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."))));
#else
fprintf(stderr, _("cluster file encryption is not supported because OpenSSL is not supported by this build"));
exit(1);
#endif
}

View File

@ -1,268 +0,0 @@
/*-------------------------------------------------------------------------
* cipher_openssl.c
* Cryptographic function using OpenSSL
*
* This contains the common low-level functions needed in both frontend and
* backend, for implement the database encryption.
*
* Portions Copyright (c) 2020, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/cipher_openssl.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include "common/cipher.h"
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
/*
* prototype for the EVP functions that return an algorithm, e.g.
* EVP_aes_128_gcm().
*/
typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void);
static ossl_EVP_cipher_func get_evp_aes_gcm(int klen);
static EVP_CIPHER_CTX *ossl_cipher_ctx_create(int cipher, uint8 *key, int klen,
bool enc);
/*
* Return a newly created cipher context. 'cipher' specifies cipher algorithm
* by identifer like PG_CIPHER_XXX.
*/
PgCipherCtx *
pg_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
{
PgCipherCtx *ctx = NULL;
if (cipher >= PG_MAX_CIPHER_ID)
return NULL;
ctx = ossl_cipher_ctx_create(cipher, key, klen, enc);
return ctx;
}
void
pg_cipher_ctx_free(PgCipherCtx *ctx)
{
EVP_CIPHER_CTX_free(ctx);
}
/*
* Encryption routine to encrypt data provided.
*
* ctx is the encryption context which must have been created previously.
*
* plaintext is the data we are going to encrypt
* inlen is the length of the data to encrypt
*
* ciphertext is the encrypted result
* outlen is the encrypted length
*
* iv is the IV to use.
* ivlen is the IV length to use.
*
* outtag is the resulting tag.
* taglen is the length of the tag.
*/
bool
pg_cipher_encrypt(PgCipherCtx *ctx,
const unsigned char *plaintext, const int inlen,
unsigned char *ciphertext, int *outlen,
const unsigned char *iv, const int ivlen,
unsigned char *outtag, const int taglen)
{
int len;
int enclen;
Assert(ctx != NULL);
/*
* Here we are setting the IV for the context which was passed
* in. Note that we signal to OpenSSL that we are configuring
* a new value for the context by passing in 'NULL' for the
* 2nd ('type') parameter.
*/
/* Set the IV length first */
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, ivlen, NULL))
return false;
/* Set the IV for this encryption. */
if (!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv))
return false;
/*
* This is the function which is actually performing the
* encryption for us.
*/
if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, inlen))
return false;
enclen = len;
/* Finalize the encryption, which could add more to output. */
if (!EVP_EncryptFinal_ex(ctx, ciphertext + enclen, &len))
return false;
*outlen = enclen + len;
/*
* Once all of the encryption has been completed we grab
* the tag.
*/
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, taglen, outtag))
return false;
return true;
}
/*
* Decryption routine
*
* ctx is the encryption context which must have been created previously.
*
* ciphertext is the data we are going to decrypt
* inlen is the length of the data to decrypt
*
* plaintext is the decrypted result
* outlen is the decrypted length
*
* iv is the IV to use.
* ivlen is the length of the IV.
*
* intag is the tag to use to verify.
* taglen is the length of the tag.
*/
bool
pg_cipher_decrypt(PgCipherCtx *ctx,
const unsigned char *ciphertext, const int inlen,
unsigned char *plaintext, int *outlen,
const unsigned char *iv, const int ivlen,
unsigned char *intag, const int taglen)
{
int declen;
int len;
/*
* Here we are setting the IV for the context which was passed
* in. Note that we signal to OpenSSL that we are configuring
* a new value for the context by passing in 'NULL' for the
* 2nd ('type') parameter.
*/
/* Set the IV length first */
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, ivlen, NULL))
return false;
/* Set the IV for this decryption. */
if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv))
return false;
/*
* This is the function which is actually performing the
* decryption for us.
*/
if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, inlen))
return false;
declen = len;
/* Set the expected tag value. */
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, taglen, intag))
return false;
/*
* Finalize the decryption, which could add more to output,
* this is also the step which checks the tag and we MUST
* fail if this indicates an invalid tag!
*/
if (!EVP_DecryptFinal_ex(ctx, plaintext + declen, &len))
return false;
*outlen = declen + len;
return true;
}
/*
* Returns the correct cipher functions for OpenSSL based
* on the key length requested.
*/
static ossl_EVP_cipher_func
get_evp_aes_gcm(int klen)
{
switch (klen)
{
case PG_AES128_KEY_LEN:
return EVP_aes_128_gcm;
case PG_AES192_KEY_LEN:
return EVP_aes_192_gcm;
case PG_AES256_KEY_LEN:
return EVP_aes_256_gcm;
default:
return NULL;
}
}
/*
* Initialize and return an EVP_CIPHER_CTX. Returns NULL if the given
* cipher algorithm is not supported or on failure.
*/
static EVP_CIPHER_CTX *
ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
{
EVP_CIPHER_CTX *ctx;
ossl_EVP_cipher_func func;
int ret;
ctx = EVP_CIPHER_CTX_new();
/*
* We currently only support AES GCM but others could be
* added in the future.
*/
switch (cipher)
{
case PG_CIPHER_AES_GCM:
func = get_evp_aes_gcm(klen);
if (!func)
goto failed;
break;
default:
goto failed;
}
/*
* We create the context here based on the cipher requested and the provided
* key. Note that the IV will be provided in the actual encryption call
* through another EVP_EncryptInit_ex call- this is fine as long as 'type'
* is passed in as NULL!
*/
if (enc)
ret = EVP_EncryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
else
ret = EVP_DecryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
if (!ret)
goto failed;
/* Set the key length based on the key length requested. */
if (!EVP_CIPHER_CTX_set_key_length(ctx, klen))
goto failed;
return ctx;
failed:
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

View File

@ -1,507 +0,0 @@
/*-------------------------------------------------------------------------
*
* kmgr_utils.c
* Shared frontend/backend for cluster file encryption
*
* Copyright (c) 2020, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/kmgr_utils.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <unistd.h>
#include <sys/stat.h>
#ifdef FRONTEND
#include "common/logging.h"
#endif
#include "common/cryptohash.h"
#include "common/file_perm.h"
#include "common/kmgr_utils.h"
#include "common/hex_decode.h"
#include "common/string.h"
#include "crypto/kmgr.h"
#include "lib/stringinfo.h"
#include "postmaster/postmaster.h"
#include "storage/fd.h"
#ifndef FRONTEND
#include "pgstat.h"
#include "storage/fd.h"
#endif
#define KMGR_PROMPT_MSG "Enter authentication needed to generate the cluster key: "
#ifdef FRONTEND
static FILE *open_pipe_stream(const char *command);
static int close_pipe_stream(FILE *file);
#endif
static void read_one_keyfile(const char *dataDir, uint32 id, CryptoKey *key_p);
/*
* Encrypt the given data. Return true and set encrypted data to 'out' if
* success. Otherwise return false. The caller must allocate sufficient space
* for cipher data calculated by using KmgrSizeOfCipherText(). Please note that
* this function modifies 'out' data even on failure case.
*/
bool
kmgr_wrap_key(PgCipherCtx *ctx, CryptoKey *in, CryptoKey *out)
{
int len, enclen;
unsigned char iv[sizeof(in->pgkey_id) + sizeof(in->counter)];
Assert(ctx && in && out);
/* Get the actual length of the key we are wrapping */
memcpy(&len, in->encrypted_key, sizeof(len));
/* Key ID remains the same */
out->pgkey_id = in->pgkey_id;
/* Increment the counter */
out->counter = in->counter + 1;
/* Construct the IV we are going to use, see kmgr_utils.h */
memcpy(iv, &out->pgkey_id, sizeof(out->pgkey_id));
memcpy(iv + sizeof(out->pgkey_id), &out->counter, sizeof(out->counter));
if (!pg_cipher_encrypt(ctx,
in->encrypted_key, /* Plaintext source, key length + key */
sizeof(in->encrypted_key), /* Full data length */
out->encrypted_key, /* Ciphertext result */
&enclen, /* Resulting length, must match input for us */
iv, /* Generated IV from above */
sizeof(iv), /* Length of the IV */
out->tag, /* Resulting tag */
sizeof(out->tag))) /* Length of our tag */
return false;
Assert(enclen == sizeof(in->encrypted_key));
return true;
}
/*
* Decrypt the given Data. Return true and set plain text data to `out` if
* success. Otherwise return false. The caller must allocate sufficient space
* for cipher data calculated by using KmgrSizeOfPlainText(). Please note that
* this function modifies 'out' data even on failure case.
*/
bool
kmgr_unwrap_key(PgCipherCtx *ctx, CryptoKey *in, CryptoKey *out)
{
int declen;
unsigned char iv[sizeof(in->pgkey_id) + sizeof(in->counter)];
Assert(ctx && in && out);
out->pgkey_id = in->pgkey_id;
out->counter = in->counter;
memcpy(out->tag, in->tag, sizeof(in->tag));
/* Construct the IV we are going to use, see kmgr_utils.h */
memcpy(iv, &out->pgkey_id, sizeof(out->pgkey_id));
memcpy(iv + sizeof(out->pgkey_id), &out->counter, sizeof(out->counter));
/* Decrypt encrypted data */
if (!pg_cipher_decrypt(ctx,
in->encrypted_key, /* Encrypted source */
sizeof(in->encrypted_key), /* Length of encrypted data */
out->encrypted_key, /* Plaintext result */
&declen, /* Length of plaintext */
iv, /* IV we constructed above */
sizeof(iv), /* Size of our IV */
in->tag, /* Tag which will be verified */
sizeof(in->tag))) /* Size of our tag */
return false;
Assert(declen == sizeof(in->encrypted_key));
return true;
}
/*
* Verify the correctness of the given cluster key by unwrapping the given keys.
* If the given cluster key is correct we set unwrapped keys to out_keys and return
* true. Otherwise return false. Please note that this function changes the
* contents of out_keys even on failure. Both in_keys and out_keys must be the
* same length, nkey.
*/
bool
kmgr_verify_cluster_key(unsigned char *cluster_key,
CryptoKey *in_keys, CryptoKey *out_keys, int nkeys)
{
PgCipherCtx *ctx;
/*
* Create decryption context with cluster KEK.
*/
ctx = pg_cipher_ctx_create(PG_CIPHER_AES_GCM, cluster_key,
KMGR_CLUSTER_KEY_LEN, false);
for (int i = 0; i < nkeys; i++)
{
if (!kmgr_unwrap_key(ctx, &(in_keys[i]), &(out_keys[i])))
{
/* The cluster key is not correct */
pg_cipher_ctx_free(ctx);
return false;
}
explicit_bzero(&(in_keys[i]), sizeof(in_keys[i]));
}
/* The cluster key is correct, free the cipher context */
pg_cipher_ctx_free(ctx);
return true;
}
/*
* Run cluster key command.
*
* prompt will be substituted for %p, file descriptor for %R
*
* The result will be put in buffer buf, which is of size size.
* The return value is the length of the actual result.
*/
int
kmgr_run_cluster_key_command(char *cluster_key_command, char *buf,
int size, char *dir)
{
StringInfoData command;
const char *sp;
FILE *fh;
int pclose_rc;
size_t len = 0;
buf[0] = '\0';
Assert(size > 0);
/*
* Build the command to be executed.
*/
initStringInfo(&command);
for (sp = cluster_key_command; *sp; sp++)
{
if (*sp == '%')
{
switch (sp[1])
{
case 'd':
{
char *nativePath;
sp++;
/*
* This needs to use a placeholder to not modify the
* input with the conversion done via
* make_native_path().
*/
nativePath = pstrdup(dir);
make_native_path(nativePath);
appendStringInfoString(&command, nativePath);
pfree(nativePath);
break;
}
case 'p':
sp++;
appendStringInfoString(&command, KMGR_PROMPT_MSG);
break;
case 'R':
{
char fd_str[20];
if (terminal_fd == -1)
{
#ifdef FRONTEND
pg_log_fatal("cluster key command referenced %%R, but --authprompt not specified");
exit(EXIT_FAILURE);
#else
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("cluster key command referenced %%R, but --authprompt not specified")));
#endif
}
sp++;
snprintf(fd_str, sizeof(fd_str), "%d", terminal_fd);
appendStringInfoString(&command, fd_str);
break;
}
case '%':
/* convert %% to a single % */
sp++;
appendStringInfoChar(&command, *sp);
break;
default:
/* otherwise treat the % as not special */
appendStringInfoChar(&command, *sp);
break;
}
}
else
{
appendStringInfoChar(&command, *sp);
}
}
#ifdef FRONTEND
fh = open_pipe_stream(command.data);
if (fh == NULL)
{
pg_log_fatal("could not execute command \"%s\": %m",
command.data);
exit(EXIT_FAILURE);
}
#else
fh = OpenPipeStream(command.data, "r");
if (fh == NULL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not execute command \"%s\": %m",
command.data)));
#endif
if (!fgets(buf, size, fh))
{
if (ferror(fh))
{
#ifdef FRONTEND
pg_log_fatal("could not read from command \"%s\": %m",
command.data);
exit(EXIT_FAILURE);
#else
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
command.data)));
#endif
}
}
#ifdef FRONTEND
pclose_rc = close_pipe_stream(fh);
#else
pclose_rc = ClosePipeStream(fh);
#endif
if (pclose_rc == -1)
{
#ifdef FRONTEND
pg_log_fatal("could not close pipe to external command: %m");
exit(EXIT_FAILURE);
#else
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close pipe to external command: %m")));
#endif
}
else if (pclose_rc != 0)
{
#ifdef FRONTEND
pg_log_fatal("command \"%s\" failed", command.data);
exit(EXIT_FAILURE);
#else
ereport(ERROR,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
command.data),
errdetail_internal("%s", wait_result_to_str(pclose_rc))));
#endif
}
/* strip trailing newline and carriage return */
len = pg_strip_crlf(buf);
pfree(command.data);
return len;
}
#ifdef FRONTEND
static FILE *
open_pipe_stream(const char *command)
{
FILE *res;
#ifdef WIN32
size_t cmdlen = strlen(command);
char *buf;
int save_errno;
buf = malloc(cmdlen + 2 + 1);
if (buf == NULL)
{
errno = ENOMEM;
return NULL;
}
buf[0] = '"';
memcpy(&buf[1], command, cmdlen);
buf[cmdlen + 1] = '"';
buf[cmdlen + 2] = '\0';
res = _popen(buf, "r");
save_errno = errno;
free(buf);
errno = save_errno;
#else
res = popen(command, "r");
#endif /* WIN32 */
return res;
}
static int
close_pipe_stream(FILE *file)
{
#ifdef WIN32
return _pclose(file);
#else
return pclose(file);
#endif /* WIN32 */
}
#endif /* FRONTEND */
CryptoKey *
kmgr_get_cryptokeys(const char *path, int *nkeys)
{
struct dirent *de;
DIR *dir;
CryptoKey *keys;
#ifndef FRONTEND
if ((dir = AllocateDir(path)) == NULL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open directory \"%s\": %m",
path)));
#else
if ((dir = opendir(path)) == NULL)
pg_log_fatal("could not open directory \"%s\": %m", path);
#endif
keys = (CryptoKey *) palloc0(sizeof(CryptoKey) * KMGR_MAX_INTERNAL_KEYS);
*nkeys = 0;
#ifndef FRONTEND
while ((de = ReadDir(dir, LIVE_KMGR_DIR)) != NULL)
#else
while ((de = readdir(dir)) != NULL)
#endif
{
if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
{
uint32 id = strtoul(de->d_name, NULL, 10);
if (id < 0 || id >= KMGR_MAX_INTERNAL_KEYS)
{
#ifndef FRONTEND
elog(ERROR, "invalid cryptographic key identifier %u", id);
#else
pg_log_fatal("invalid cryptographic key identifier %u", id);
#endif
}
if (*nkeys >= KMGR_MAX_INTERNAL_KEYS)
{
#ifndef FRONTEND
elog(ERROR, "too many cryptographic keys");
#else
pg_log_fatal("too many cryptographic keys");
#endif
}
read_one_keyfile(path, id, &(keys[id]));
(*nkeys)++;
}
}
#ifndef FRONTEND
FreeDir(dir);
#else
closedir(dir);
#endif
return keys;
}
static void
read_one_keyfile(const char *cryptoKeyDir, uint32 id, CryptoKey *key_p)
{
char path[MAXPGPATH];
int fd;
int r;
CryptoKeyFilePath(path, cryptoKeyDir, id);
#ifndef FRONTEND
if ((fd = OpenTransientFile(path, O_RDONLY | PG_BINARY)) == -1)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open file \"%s\" for reading: %m",
path)));
#else
if ((fd = open(path, O_RDONLY | PG_BINARY, 0)) == -1)
pg_log_fatal("could not open file \"%s\" for reading: %m",
path);
#endif
#ifndef FRONTEND
pgstat_report_wait_start(WAIT_EVENT_KEY_FILE_READ);
#endif
/* Get key bytes */
r = read(fd, key_p, sizeof(CryptoKey));
if (r != sizeof(CryptoKey))
{
if (r < 0)
{
#ifndef FRONTEND
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m", path)));
#else
pg_log_fatal("could not read file \"%s\": %m", path);
#endif
}
else
{
#ifndef FRONTEND
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("could not read file \"%s\": read %d of %zu",
path, r, sizeof(CryptoKey))));
#else
pg_log_fatal("could not read file \"%s\": read %d of %zu",
path, r, sizeof(CryptoKey));
#endif
}
}
#ifndef FRONTEND
pgstat_report_wait_end();
#endif
#ifndef FRONTEND
if (CloseTransientFile(fd) != 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close file \"%s\": %m",
path)));
#else
if (close(fd) != 0)
pg_log_fatal("could not close file \"%s\": %m", path);
#endif
}

View File

@ -22,7 +22,7 @@
/* Version identifier for this pg_control format */
#define PG_CONTROL_VERSION 1400
#define PG_CONTROL_VERSION 1300
/* Nonce key length, see below */
#define MOCK_AUTH_NONCE_LEN 32
@ -226,9 +226,6 @@ typedef struct ControlFileData
*/
char mock_authentication_nonce[MOCK_AUTH_NONCE_LEN];
/* File encryption key length. Zero if disabled. */
int file_encryption_keylen;
/* CRC of all above ... MUST BE LAST! */
pg_crc32c crc;
} ControlFileData;

View File

@ -1,62 +0,0 @@
/*-------------------------------------------------------------------------
*
* cipher.h
* Declarations for cryptographic functions
*
* Portions Copyright (c) 2020, PostgreSQL Global Development Group
*
* src/include/common/cipher.h
*
*-------------------------------------------------------------------------
*/
#ifndef PG_CIPHER_H
#define PG_CIPHER_H
#ifdef USE_OPENSSL
#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#endif
/*
* Supported symmetric encryption algorithm. These identifiers are passed
* to pg_cipher_ctx_create() function, and then actual encryption
* implementations need to initialize their context of the given encryption
* algorithm.
*/
#define PG_CIPHER_AES_GCM 0
#define PG_MAX_CIPHER_ID 1
/* AES128/192/256 various length definitions */
#define PG_AES128_KEY_LEN (128 / 8)
#define PG_AES192_KEY_LEN (192 / 8)
#define PG_AES256_KEY_LEN (256 / 8)
/*
* The encrypted data is a series of blocks of size. Initialization
* vector(IV) is the same size of cipher block.
*/
#define PG_AES_BLOCK_SIZE 16
#define PG_AES_IV_SIZE (PG_AES_BLOCK_SIZE)
#ifdef USE_OPENSSL
typedef EVP_CIPHER_CTX PgCipherCtx;
#else
typedef void PgCipherCtx;
#endif
extern PgCipherCtx *pg_cipher_ctx_create(int cipher, uint8 *key, int klen,
bool enc);
extern void pg_cipher_ctx_free(PgCipherCtx *ctx);
extern bool pg_cipher_encrypt(PgCipherCtx *ctx,
const unsigned char *plaintext, const int inlen,
unsigned char *ciphertext, int *outlen,
const unsigned char *iv, const int ivlen,
unsigned char *tag, const int taglen);
extern bool pg_cipher_decrypt(PgCipherCtx *ctx,
const unsigned char *ciphertext, const int inlen,
unsigned char *plaintext, int *outlen,
const unsigned char *iv, const int ivlen,
unsigned char *intag, const int taglen);
#endif /* PG_CIPHER_H */

View File

@ -1,98 +0,0 @@
/*-------------------------------------------------------------------------
*
* kmgr_utils.h
* Declarations for utility function for file encryption key
*
* Portions Copyright (c) 2020, PostgreSQL Global Development Group
*
* src/include/common/kmgr_utils.h
*
*-------------------------------------------------------------------------
*/
#ifndef KMGR_UTILS_H
#define KMGR_UTILS_H
#include "common/cipher.h"
/* Current version number */
#define KMGR_VERSION 1
/*
* Directories where cluster file encryption keys reside within PGDATA.
*/
#define KMGR_DIR "pg_cryptokeys"
#define KMGR_DIR_PID KMGR_DIR"/pg_alterckey.pid"
#define LIVE_KMGR_DIR KMGR_DIR"/live"
/* used during cluster key rotation */
#define NEW_KMGR_DIR KMGR_DIR"/new"
#define OLD_KMGR_DIR KMGR_DIR"/old"
/* CryptoKey file name is keys id */
#define CryptoKeyFilePath(path, dir, id) \
snprintf((path), MAXPGPATH, "%s/%d", (dir), (id))
/*
* Identifiers of internal keys.
*/
#define KMGR_KEY_ID_REL 0
#define KMGR_KEY_ID_WAL 1
#define KMGR_MAX_INTERNAL_KEYS 2
/* We always, today, use a 256-bit AES key. */
#define KMGR_CLUSTER_KEY_LEN PG_AES256_KEY_LEN
/* double for hex format, plus some for spaces, \r,\n, and null byte */
#define ALLOC_KMGR_CLUSTER_KEY_LEN (KMGR_CLUSTER_KEY_LEN * 2 + 10 + 2 + 1)
/* Maximum length of key the key manager can store */
#define KMGR_MAX_KEY_LEN 256
#define KMGR_MAX_KEY_LEN_BYTES KMGR_MAX_KEY_LEN / 8
#define KMGR_MAX_WRAPPED_KEY_LEN KmgrSizeOfCipherText(KMGR_MAX_KEY_LEN)
/*
* Cryptographic key data structure.
*
* This is the structure we use to write out the encrypted keys.
*
* pgkey_id is the identifier for this key (should be same as the
* file name and be one of KMGR_KEY_ID_* from above). This is what
* we consider our 'context' or 'fixed' portion of the deterministic
* IV we create.
*
* counter is updated each time we use the cluster KEK to encrypt a
* new key. This is our the 'invocation' field of the deterministic
* IV we create.
*
* Absolutely essential when using GCM (or CTR) is that the IV is unique,
* for a given key, but a deterministic IV such as this is perfectly
* acceptable and encouraged. If (and only if!) the KEK is changed to a
* new key, then we can re-initialize the counter.
*
* Detailed discussion of deterministic IV creation can be found here:
*
* https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
*
* tag is the GCM tag which is produced and must be validated in order
* to be able to trust the results of our decryption.
*
* encrypted_key is the encrypted key length (as an int) + encrypted key.
*/
typedef struct CryptoKey
{
uint64 pgkey_id; /* Upper half of IV */
uint64 counter; /* Lower half of IV */
unsigned char tag[16]; /* GCM tag */
unsigned char encrypted_key[sizeof(int) + KMGR_MAX_KEY_LEN_BYTES];
} CryptoKey;
extern bool kmgr_wrap_key(PgCipherCtx *ctx, CryptoKey *in, CryptoKey *out);
extern bool kmgr_unwrap_key(PgCipherCtx *ctx, CryptoKey *in, CryptoKey *out);
extern bool kmgr_verify_cluster_key(unsigned char *cluster_key,
CryptoKey *in_keys, CryptoKey *out_keys,
int nkey);
extern int kmgr_run_cluster_key_command(char *cluster_key_command,
char *buf, int size, char *dir);
extern CryptoKey *kmgr_get_cryptokeys(const char *path, int *nkeys);
#endif /* KMGR_UTILS_H */

View File

@ -1,29 +0,0 @@
/*-------------------------------------------------------------------------
*
* kmgr.h
*
* Portions Copyright (c) 2020, PostgreSQL Global Development Group
*
* src/include/crypto/kmgr.h
*
*-------------------------------------------------------------------------
*/
#ifndef KMGR_H
#define KMGR_H
#include "common/cipher.h"
#include "common/kmgr_utils.h"
#include "storage/relfilenode.h"
#include "storage/bufpage.h"
/* GUC parameters */
extern int file_encryption_keylen;
extern char *cluster_key_command;
extern Size KmgrShmemSize(void);
extern void KmgrShmemInit(void);
extern void BootStrapKmgr(void);
extern void InitializeKmgr(void);
extern const CryptoKey *KmgrGetKey(int id);
#endif /* KMGR_H */

View File

@ -1010,9 +1010,6 @@ typedef enum
WAIT_EVENT_DATA_FILE_TRUNCATE,
WAIT_EVENT_DATA_FILE_WRITE,
WAIT_EVENT_DSM_FILL_ZERO_WRITE,
WAIT_EVENT_KEY_FILE_READ,
WAIT_EVENT_KEY_FILE_WRITE,
WAIT_EVENT_KEY_FILE_SYNC,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_SYNC,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_WRITE,

View File

@ -30,8 +30,6 @@ extern bool enable_bonjour;
extern char *bonjour_name;
extern bool restart_after_crash;
extern int terminal_fd;
#ifdef WIN32
extern HANDLE PostmasterHandle;
#else

View File

@ -89,7 +89,6 @@ enum config_group
STATS,
STATS_MONITORING,
STATS_COLLECTOR,
ENCRYPTION,
AUTOVACUUM,
CLIENT_CONN,
CLIENT_CONN_STATEMENT,

View File

@ -122,20 +122,18 @@ sub mkvcbuild
archive.c base64.c checksum_helper.c
config_info.c controldata_utils.c d2s.c encnames.c exec.c
f2s.c file_perm.c file_utils.c hashfn.c hex_decode.c ip.c jsonapi.c
keywords.c kmgr_utils.c kwlookup.c link-canary.c md5_common.c
keywords.c kwlookup.c link-canary.c md5_common.c
pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
wait_error.c wchar.c);
if ($solution->{options}->{openssl})
{
push(@pgcommonallfiles, 'cipher_openssl.c');
push(@pgcommonallfiles, 'cryptohash_openssl.c');
push(@pgcommonallfiles, 'protocol_openssl.c');
}
else
{
push(@pgcommonallfiles, 'cipher.c');
push(@pgcommonallfiles, 'cryptohash.c');
push(@pgcommonallfiles, 'md5.c');
push(@pgcommonallfiles, 'sha2.c');