summaryrefslogtreecommitdiff
path: root/src/backend/libpq
diff options
context:
space:
mode:
authorPeter Eisentraut2018-02-26 18:28:38 +0000
committerPeter Eisentraut2018-03-17 12:28:51 +0000
commit8a3d9425290ff5f6434990349886afae9e1c6008 (patch)
tree47dd8b09381f019eba35cd060d8af8f93af44c01 /src/backend/libpq
parent7a50bb690b4837d29e715293c156cff2fc72885c (diff)
Add ssl_passphrase_command setting
This allows specifying an external command for prompting for or otherwise obtaining passphrases for SSL key files. This is useful because in many cases there is no TTY easily available during service startup. Also add a setting ssl_passphrase_command_supports_reload, which allows supporting SSL configuration reload even if SSL files need passphrases. Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Diffstat (limited to 'src/backend/libpq')
-rw-r--r--src/backend/libpq/Makefile2
-rw-r--r--src/backend/libpq/be-secure-common.c120
-rw-r--r--src/backend/libpq/be-secure-openssl.c58
-rw-r--r--src/backend/libpq/be-secure.c2
4 files changed, 167 insertions, 15 deletions
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 7fa2b027433..3dbec23e30a 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
# be-fsstubs is here for historical reasons, probably belongs elsewhere
-OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ifaddr.o pqcomm.o \
+OBJS = be-fsstubs.o be-secure.o be-secure-common.o auth.o crypt.o hba.o ifaddr.o pqcomm.o \
pqformat.o pqmq.o pqsignal.o auth-scram.o
ifeq ($(with_openssl),yes)
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
new file mode 100644
index 00000000000..d1740967f19
--- /dev/null
+++ b/src/backend/libpq/be-secure-common.c
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-common.c
+ *
+ * common implementation-independent SSL support code
+ *
+ * While be-secure.c contains the interfaces that the rest of the
+ * communications code calls, this file contains support routines that are
+ * used by the library-specific implementations such as be-secure-openssl.c.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/libpq/be-secure-common.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "storage/fd.h"
+
+/*
+ * Run ssl_passphrase_command
+ *
+ * prompt will be substituted for %p. is_server_start determines the loglevel
+ * of error messages.
+ *
+ * The result will be put in buffer buf, which is of size size. The return
+ * value is the length of the actual result.
+ */
+int
+run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
+{
+ int loglevel = is_server_start ? ERROR : LOG;
+ StringInfoData command;
+ char *p;
+ FILE *fh;
+ int pclose_rc;
+ size_t len = 0;
+
+ Assert(prompt);
+ Assert(size > 0);
+ buf[0] = '\0';
+
+ initStringInfo(&command);
+
+ for (p = ssl_passphrase_command; *p; p++)
+ {
+ if (p[0] == '%')
+ {
+ switch (p[1])
+ {
+ case 'p':
+ appendStringInfoString(&command, prompt);
+ p++;
+ break;
+ case '%':
+ appendStringInfoChar(&command, '%');
+ p++;
+ break;
+ default:
+ appendStringInfoChar(&command, p[0]);
+ }
+ }
+ else
+ appendStringInfoChar(&command, p[0]);
+ }
+
+ fh = OpenPipeStream(command.data, "r");
+ if (fh == NULL)
+ {
+ ereport(loglevel,
+ (errcode_for_file_access(),
+ errmsg("could not execute command \"%s\": %m",
+ command.data)));
+ goto error;
+ }
+
+ if (!fgets(buf, size, fh))
+ {
+ if (ferror(fh))
+ {
+ ereport(loglevel,
+ (errcode_for_file_access(),
+ errmsg("could not read from command \"%s\": %m",
+ command.data)));
+ goto error;
+ }
+ }
+
+ pclose_rc = ClosePipeStream(fh);
+ if (pclose_rc == -1)
+ {
+ ereport(loglevel,
+ (errcode_for_file_access(),
+ errmsg("could not close pipe to external command: %m")));
+ goto error;
+ }
+ else if (pclose_rc != 0)
+ {
+ ereport(loglevel,
+ (errcode_for_file_access(),
+ errmsg("command \"%s\" failed",
+ command.data),
+ errdetail_internal("%s", wait_result_to_str(pclose_rc))));
+ goto error;
+ }
+
+ /* strip trailing newline */
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len-- -1] = '\0';
+
+error:
+ pfree(command.data);
+ return len;
+}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 567cf7d4550..75ee5456cc1 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -52,7 +52,8 @@ static int my_SSL_set_fd(Port *port, int fd);
static DH *load_dh_file(char *filename, bool isServerStart);
static DH *load_dh_buffer(const char *, size_t);
-static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static int ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static int dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
static bool initialize_dh(SSL_CTX *context, bool isServerStart);
@@ -63,7 +64,8 @@ static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
static bool SSL_initialized = false;
-static bool ssl_passwd_cb_called = false;
+static bool dummy_ssl_passwd_cb_called = false;
+static bool ssl_is_server_start;
/* ------------------------------------------------------------ */
@@ -111,14 +113,28 @@ be_tls_init(bool isServerStart)
SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
/*
- * If reloading, override OpenSSL's default handling of
- * passphrase-protected files, because we don't want to prompt for a
- * passphrase in an already-running server. (Not that the default
- * handling is very desirable during server start either, but some people
- * insist we need to keep it.)
+ * Set password callback
*/
- if (!isServerStart)
- SSL_CTX_set_default_passwd_cb(context, ssl_passwd_cb);
+ if (isServerStart)
+ {
+ if (ssl_passphrase_command[0])
+ SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
+ }
+ else
+ {
+ if (ssl_passphrase_command[0] && ssl_passphrase_command_supports_reload)
+ SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
+ else
+ /*
+ * If reloading and no external command is configured, override
+ * OpenSSL's default handling of passphrase-protected files,
+ * because we don't want to prompt for a passphrase in an
+ * already-running server.
+ */
+ SSL_CTX_set_default_passwd_cb(context, dummy_ssl_passwd_cb);
+ }
+ /* used by the callback */
+ ssl_is_server_start = isServerStart;
/*
* Load and verify server's certificate and private key
@@ -138,13 +154,13 @@ be_tls_init(bool isServerStart)
/*
* OK, try to load the private key file.
*/
- ssl_passwd_cb_called = false;
+ dummy_ssl_passwd_cb_called = false;
if (SSL_CTX_use_PrivateKey_file(context,
ssl_key_file,
SSL_FILETYPE_PEM) != 1)
{
- if (ssl_passwd_cb_called)
+ if (dummy_ssl_passwd_cb_called)
ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
@@ -839,7 +855,21 @@ load_dh_buffer(const char *buffer, size_t len)
}
/*
- * Passphrase collection callback
+ * Passphrase collection callback using ssl_passphrase_command
+ */
+static int
+ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ /* same prompt as OpenSSL uses internally */
+ const char *prompt = "Enter PEM pass phrase:";
+
+ Assert(rwflag == 0);
+
+ return run_ssl_passphrase_command(prompt, ssl_is_server_start, buf, size);
+}
+
+/*
+ * Dummy passphrase callback
*
* If OpenSSL is told to use a passphrase-protected server key, by default
* it will issue a prompt on /dev/tty and try to read a key from there.
@@ -848,10 +878,10 @@ load_dh_buffer(const char *buffer, size_t len)
* function that just returns an empty passphrase, guaranteeing failure.
*/
static int
-ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
{
/* Set flag to change the error message we'll report */
- ssl_passwd_cb_called = true;
+ dummy_ssl_passwd_cb_called = true;
/* And return empty string */
Assert(size > 0);
buf[0] = '\0';
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 76c0a9e39b5..fb1f6b5bbe7 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -45,6 +45,8 @@ char *ssl_key_file;
char *ssl_ca_file;
char *ssl_crl_file;
char *ssl_dh_params_file;
+char *ssl_passphrase_command;
+bool ssl_passphrase_command_supports_reload;
#ifdef USE_SSL
bool ssl_loaded_verify_locations = false;