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:
parent
facad31474
commit
3187ef7c46
@ -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>
|
||||
|
@ -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>
|
@ -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">
|
||||
|
@ -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.
|
||||
|
@ -171,7 +171,6 @@ break is not needed in a wider output rendering.
|
||||
&wal;
|
||||
&logical-replication;
|
||||
&jit;
|
||||
&database-encryption;
|
||||
®ress;
|
||||
|
||||
</part>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -240,7 +240,6 @@
|
||||
</para>
|
||||
</partintro>
|
||||
|
||||
&pgalterckey;
|
||||
&clusterdb;
|
||||
&createdb;
|
||||
&createuser;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)));
|
||||
}
|
||||
}
|
@ -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
|
@ -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++;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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!)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -53,4 +53,3 @@ XactTruncationLock 44
|
||||
# 45 was XactTruncationLock until removal of BackendRandomLock
|
||||
WrapLimitsVacuumLock 46
|
||||
NotifyQueueTailLock 47
|
||||
KmgrFileLock 48
|
||||
|
@ -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
|
||||
|
@ -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."),
|
||||
|
@ -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));
|
||||
|
@ -632,11 +632,6 @@
|
||||
# autovacuum, -1 means use
|
||||
# vacuum_cost_limit
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# ENCRYPTION
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#cluster_key_command = ''
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# CLIENT CONNECTION DEFAULTS
|
||||
|
@ -16,7 +16,6 @@ include $(top_builddir)/src/Makefile.global
|
||||
SUBDIRS = \
|
||||
initdb \
|
||||
pg_archivecleanup \
|
||||
pg_alterckey \
|
||||
pg_basebackup \
|
||||
pg_checksums \
|
||||
pg_config \
|
||||
|
@ -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();
|
||||
|
||||
|
1
src/bin/pg_alterckey/.gitignore
vendored
1
src/bin/pg_alterckey/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/pg_alterckey
|
@ -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
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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().
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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;
|
||||
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -89,7 +89,6 @@ enum config_group
|
||||
STATS,
|
||||
STATS_MONITORING,
|
||||
STATS_COLLECTOR,
|
||||
ENCRYPTION,
|
||||
AUTOVACUUM,
|
||||
CLIENT_CONN,
|
||||
CLIENT_CONN_STATEMENT,
|
||||
|
@ -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');
|
||||
|
Loading…
x
Reference in New Issue
Block a user