[PATCH] Reload SSL certificates on SIGHUP
Hi,
I have written a patch which makes it possible to change SSL
certificates (and other SSL parameters, including the CRL) without
restarting PostgreSQL. In fact this patch also makes it possible to turn
on or off ssl entirely without restart. It does so by initializing a new
SSL context when the postmaster receives a SIGHUP, and if the
initialization succeeded the old context is replaced by the new.
There was some previous discussion[1] on the mailing list about what the
proper context should be for the SSL parameters, but as far as I can
tell the discussion never reached a conclusion. I have changed the SSL
GUCs to PGC_SIGUP since I felt that was the closest to the truth, but it
is not a perfect fit (the backends wont reload the SSL context). Should
we add a new context for the SSL GUCs?
Notes
1.
/messages/by-id/CAAS3tyLJcv-m0CqfMrrxUjwa9_FKscKuAKT9_L41wNuJZywM2Q@mail.gmail.com
Andreas
Attachments:
reload-ssl-v01.patchtext/x-patch; name=reload-ssl-v01.patchDownload
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index f0774fe..7ea4810 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -87,7 +87,8 @@ static DH *load_dh_buffer(const char *, size_t);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static SSL_CTX *initialize_context(void);
+static bool initialize_ecdh(SSL_CTX *context);
static const char *SSLerrmessage(void);
static char *X509_NAME_to_cstring(X509_NAME *name);
@@ -96,6 +97,7 @@ static char *X509_NAME_to_cstring(X509_NAME *name);
static bool in_ssl_renegotiation = false;
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -170,168 +172,39 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
+int
be_tls_init(void)
{
- struct stat buf;
+ SSL_CTX *context;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
- {
+ if (!SSL_initialized) {
#if SSLEAY_VERSION_NUMBER >= 0x0907000L
OPENSSL_config(NULL);
#endif
SSL_library_init();
SSL_load_error_strings();
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage())));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage())));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- /*
- * Require no public access to key file.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("Permissions should be u=rw (0600) or less.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage())));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage())));
+ SSL_initialized = true;
}
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
+ if ((context = initialize_context()) != NULL)
{
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage())));
- }
-
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
- {
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage())));
- }
+ be_tls_destroy();
+ SSL_context = context;
+ /* Remember if we would have loaded the CA store */
+ ssl_loaded_verify_locations = !!ssl_ca_file[0];
+ return 0;
}
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
+ return -1;
+}
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
}
/*
@@ -990,27 +863,196 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+#define INIT_CONTEXT_ERROR(error) do { \
+ ereport(LOG, error); \
+ SSL_CTX_free(context); \
+ return NULL; \
+} while (0);
+
+static SSL_CTX *
+initialize_context(void)
+{
+ struct stat buf;
+
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+
+ SSL_CTX *context;
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not create SSL context: %s", SSLerrmessage())));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage())));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ /*
+ * Require no public access to key file.
+ *
+ * XXX temporarily suppress check when on Windows, because there may not
+ * be proper support for Unix-y file permissions. Need to think of a
+ * reasonable check to apply on Windows. (See also the data directory
+ * permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("Permissions should be u=rw (0600) or less.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage())));
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("check of private key failed: %s", SSLerrmessage())));
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ if (!initialize_ecdh(context))
+ {
+ SSL_CTX_free(context);
+ return NULL;
+ }
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")));
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage())));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage())));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
+ }
+
+ return context;
+}
+
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
EC_KEY *ecdh;
int nid;
nid = OBJ_sn2nid(SSLECDHCurve);
- if (!nid)
- ereport(FATAL,
+ if (!nid) {
+ ereport(LOG,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
- if (!ecdh)
- ereport(FATAL,
+ if (!ecdh) {
+ ereport(LOG,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 4a650cc..9b3b7b6 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -69,15 +69,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
secure_initialize(void)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init();
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index ee0b018..92c7349 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -238,6 +238,8 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -887,7 +889,12 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1839,7 +1846,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2391,6 +2398,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize() != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4612,7 +4635,12 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b3c9f14..bb2463f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -908,7 +908,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -917,7 +917,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3269,7 +3269,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3279,7 +3279,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3289,7 +3289,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3299,7 +3299,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3341,7 +3341,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3356,7 +3356,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 06dfc06..029a76e 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -78,16 +78,15 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
#ssl_renegotiation_limit = 512MB # amount of data between renegotiations
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = on
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 6171ef3..3b09efb 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -206,7 +206,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(void);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index c408e5b..21b6dc8 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -83,6 +83,7 @@ extern int (*pq_putmessage_hook) (char msgtype, const char *s, size_t len);
extern int (*pq_flush_hook) (void);
extern int secure_initialize(void);
+extern void secure_destroy(void);
extern bool secure_loaded_verify_locations(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
On 5/30/15 10:14 PM, Andreas Karlsson wrote:
I have written a patch which makes it possible to change SSL
certificates (and other SSL parameters, including the CRL) without
restarting PostgreSQL. In fact this patch also makes it possible to turn
on or off ssl entirely without restart. It does so by initializing a new
SSL context when the postmaster receives a SIGHUP, and if the
initialization succeeded the old context is replaced by the new.
I think this would be a useful feature, and the implementation looks
sound. But I don't like how the reload is organized. Reinitializing
the context in the sighup handler, aside from questions about how much
work one should do in a signal handler, would cause SSL reinitialization
for unrelated reloads. We have the GUC assign hook mechanism for
handling this sort of thing. The trick would be that when multiple
SSL-related settings change, you only want to do one reinitialization.
You could either have the different assign hooks communicate with each
other somehow, or have them set a "need SSL init" flag that is checked
somewhere else.
There was some previous discussion[1] on the mailing list about what the
proper context should be for the SSL parameters, but as far as I can
tell the discussion never reached a conclusion. I have changed the SSL
GUCs to PGC_SIGUP since I felt that was the closest to the truth, but it
is not a perfect fit (the backends wont reload the SSL context). Should
we add a new context for the SSL GUCs?
I think PGC_SIGHUP is fine for this.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 07/02/2015 06:13 PM, Peter Eisentraut wrote:
I think this would be a useful feature, and the implementation looks
sound. But I don't like how the reload is organized. Reinitializing
the context in the sighup handler, aside from questions about how much
work one should do in a signal handler, would cause SSL reinitialization
for unrelated reloads. We have the GUC assign hook mechanism for
handling this sort of thing. The trick would be that when multiple
SSL-related settings change, you only want to do one reinitialization.
You could either have the different assign hooks communicate with each
other somehow, or have them set a "need SSL init" flag that is checked
somewhere else.
It is not enough to just add a hook to the GUCs since I would guess most
users would expect the certificate to be reloaded if just the file has
been replaced and no GUC was changed. To support this we would need to
also check the mtimes of the SSL files, would that complexity really be
worth it?
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jul 22, 2015 at 9:52 AM, Andreas Karlsson <andreas@proxel.se> wrote:
On 07/02/2015 06:13 PM, Peter Eisentraut wrote:
I think this would be a useful feature, and the implementation looks
sound. But I don't like how the reload is organized. Reinitializing
the context in the sighup handler, aside from questions about how much
work one should do in a signal handler, would cause SSL reinitialization
for unrelated reloads. We have the GUC assign hook mechanism for
handling this sort of thing. The trick would be that when multiple
SSL-related settings change, you only want to do one reinitialization.
You could either have the different assign hooks communicate with each
other somehow, or have them set a "need SSL init" flag that is checked
somewhere else.It is not enough to just add a hook to the GUCs since I would guess most
users would expect the certificate to be reloaded if just the file has been
replaced and no GUC was changed.
Why? It seems to me that the assign hook gets called once per process
at reload for a SIGHUP parameter even if its value is not changed, no?
To support this we would need to also check
the mtimes of the SSL files, would that complexity really be worth it?
Or we could reload the SSL context unconditionally once per reload
loop. I am wondering how costly that may prove to be though.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 7/21/15 8:52 PM, Andreas Karlsson wrote:
It is not enough to just add a hook to the GUCs since I would guess most
users would expect the certificate to be reloaded if just the file has
been replaced and no GUC was changed. To support this we would need to
also check the mtimes of the SSL files, would that complexity really be
worth it?
Actually, I misread your patch. I thought you only wanted to reload the
SSL files when the GUC settings change, but of course we also want to
reload them when the files are changed.
I don't have a problem with rebuilding the SSL context on every reload
cycle. We already do a lot of extra reloading every time, so a bit more
shouldn't hurt. But I'm not so sure whether we should do that in the
SIGHUP handler. I don't know how we got into the situation of doing all
the file reloads directly in the handler, but at least we can control
that code. Making a bunch of calls into an external library is a
different thing, though. Can we find a way to do this differently?
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Peter Eisentraut <peter_e@gmx.net> writes:
I don't have a problem with rebuilding the SSL context on every reload
cycle. We already do a lot of extra reloading every time, so a bit more
shouldn't hurt. But I'm not so sure whether we should do that in the
SIGHUP handler. I don't know how we got into the situation of doing all
the file reloads directly in the handler, but at least we can control
that code. Making a bunch of calls into an external library is a
different thing, though. Can we find a way to do this differently?
Do we have an idea how expensive it is to load that data?
A brute-force answer is to not have the postmaster load it at all,
but to have new backends do so (if needed) during their connection
acceptance/authentication phase. I'm not sure how much that would
add to the SSL connection startup time though. It would also mean
that problems with the SSL config files would only be reported during
subsequent connection starts, not at SIGHUP time, and indeed that
SIGHUP is more or less meaningless for SSL file changes: the instant
you change a file, it's live for later connections. On the plus side,
it would make Windows and Unix behavior closer, since (I suppose)
we're reloading that stuff anyway in EXEC_BACKEND builds.
I'm not entirely sure your concern is valid, though. We have always had
the principle that almost everything of interest in the postmaster happens
in signal handler functions. We could possibly change things so that
reloading config files is done in the "main loop" of ServerLoop, but
if we did, it would have to execute with all signals blocked, which seems
like just about as much of a risk for third-party code as executing that
code in a signal handler is.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 07/23/2015 07:19 AM, Michael Paquier wrote:
On Wed, Jul 22, 2015 at 9:52 AM, Andreas Karlsson <andreas@proxel.se> wrote:
On 07/02/2015 06:13 PM, Peter Eisentraut wrote:
I think this would be a useful feature, and the implementation looks
sound. But I don't like how the reload is organized. Reinitializing
the context in the sighup handler, aside from questions about how much
work one should do in a signal handler, would cause SSL reinitialization
for unrelated reloads. We have the GUC assign hook mechanism for
handling this sort of thing. The trick would be that when multiple
SSL-related settings change, you only want to do one reinitialization.
You could either have the different assign hooks communicate with each
other somehow, or have them set a "need SSL init" flag that is checked
somewhere else.It is not enough to just add a hook to the GUCs since I would guess most
users would expect the certificate to be reloaded if just the file has been
replaced and no GUC was changed.Why? It seems to me that the assign hook gets called once per process
at reload for a SIGHUP parameter even if its value is not changed, no?
My bad, I tested it and you are correct. But I am not convinced moving
the SSL initialization to a GUC assign hook would make anything clearer.
It would not move any work out of the signal handler either since the
assign hooks are ran inside it, and the hook will be ran in all backends
which is not any interesting property for SSL initialization.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jul 29, 2015 at 10:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Peter Eisentraut <peter_e@gmx.net> writes:
I don't have a problem with rebuilding the SSL context on every reload
cycle. We already do a lot of extra reloading every time, so a bit more
shouldn't hurt. But I'm not so sure whether we should do that in the
SIGHUP handler. I don't know how we got into the situation of doing all
the file reloads directly in the handler, but at least we can control
that code. Making a bunch of calls into an external library is a
different thing, though. Can we find a way to do this differently?Do we have an idea how expensive it is to load that data?
There are no numbers on this thread. And honestly I would be curious
as well to see a run of pgbench with -C doing or similar to check how
long it takes to establish a connection. I would expect it to be
measurable though, but here I'm just hand-waving ;)
A brute-force answer is to not have the postmaster load it at all,
but to have new backends do so (if needed) during their connection
acceptance/authentication phase. I'm not sure how much that would
add to the SSL connection startup time though. It would also mean
that problems with the SSL config files would only be reported during
subsequent connection starts, not at SIGHUP time, and indeed that
SIGHUP is more or less meaningless for SSL file changes: the instant
you change a file, it's live for later connections. On the plus side,
it would make Windows and Unix behavior closer, since (I suppose)
we're reloading that stuff anyway in EXEC_BACKEND builds.
Indeed.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 07/29/2015 03:24 AM, Tom Lane wrote:
Peter Eisentraut <peter_e@gmx.net> writes:
I don't have a problem with rebuilding the SSL context on every reload
cycle. We already do a lot of extra reloading every time, so a bit more
shouldn't hurt. But I'm not so sure whether we should do that in the
SIGHUP handler. I don't know how we got into the situation of doing all
the file reloads directly in the handler, but at least we can control
that code. Making a bunch of calls into an external library is a
different thing, though. Can we find a way to do this differently?Do we have an idea how expensive it is to load that data?
A brute-force answer is to not have the postmaster load it at all,
but to have new backends do so (if needed) during their connection
acceptance/authentication phase. I'm not sure how much that would
add to the SSL connection startup time though. It would also mean
that problems with the SSL config files would only be reported during
subsequent connection starts, not at SIGHUP time, and indeed that
SIGHUP is more or less meaningless for SSL file changes: the instant
you change a file, it's live for later connections. On the plus side,
it would make Windows and Unix behavior closer, since (I suppose)
we're reloading that stuff anyway in EXEC_BACKEND builds.
I measured it taking ~0.3ms to build the new SSL context in a simple
benchmark (certificate + CA + small crl).
Personally I do not think moving this to connection start would be worth
it since reporting errors that late is not nice for people who have
misconfigured their database, and even if my benchmarks indicates it is
relatively cheap to reload SSL adding more work to connection
establishing is something I would want to avoid unless it gives us a
clear benefit.
I'm not entirely sure your concern is valid, though. We have always had
the principle that almost everything of interest in the postmaster happens
in signal handler functions. We could possibly change things so that
reloading config files is done in the "main loop" of ServerLoop, but
if we did, it would have to execute with all signals blocked, which seems
like just about as much of a risk for third-party code as executing that
code in a signal handler is.
Agreed, I am not sure what moving it to the main loop would gain us.
--
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
[ moving this discussion back to the patch thread ]
Andreas Karlsson <andreas@proxel.se> writes:
On 08/25/2015 09:39 AM, Michael Paquier wrote:
-- Reload SSL certificates on SIGHUP: returned with feedback? I think
that this patch needs more work to be in a commitable state.
Maybe I am being dense here, but I do not feel like I have gotten any
clear feedback which gives me a way forward with the patch. I do not
really see what more I can do here other than resubmit it to the next CF
which I feel would be poor etiquette by me.
I think we pretty much rejected Peter's concern about doing the work
in the SIGHUP handler. There's been some other discussion about
refactoring the postmaster to not do all its work in signal handlers,
but that is material for a different patch. Absent hard evidence that
reloading SSL config in the handler actually fails, I don't think we
should ask this patch to do a half-baked job of refactoring that.
However ... a concern I've got is that there's a difference between how
the Unix and Windows builds work, and this patch will move that from a
back-burner issue to a significant concern. Namely, that on Unix we load
the SSL data once and that's what you use, while on Windows (or any
EXEC_BACKEND build) what you're going to get is whatever is in the files
right now when a connection starts, whether it's good or bad. What this
patch does, unless I missed something, is to persuade the Unix ports to
implement "reload SSL data at SIGHUP", which is good; but the Windows
behavior stays where it is.
It didn't matter so much as long as changing the SSL config files wasn't
considered a supported operation; but if that is supported, then people
are going to complain.
Is it unreasonable of me to ask for the Windows behavior to be fixed at
the same time? I dunno. It's perhaps less broken than the Unix behavior,
but that doesn't make it desirable. OTOH it might be a significantly
larger patch, and I confess I'm not even too sure what we'd have to do.
So I think the way to move this forward is to investigate how to hold
the SSL config constant until SIGHUP in an EXEC_BACKEND build. If we
find out that that's unreasonably difficult, maybe we'll decide that
we can live without it; but I'd like to see the question investigated
rather than ignored.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Aug 26, 2015 at 10:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
[...]
So I think the way to move this forward is to investigate how to hold
the SSL config constant until SIGHUP in an EXEC_BACKEND build. If we
find out that that's unreasonably difficult, maybe we'll decide that
we can live without it; but I'd like to see the question investigated
rather than ignored.
You have a point here.
In EXEC_BACKEND, parameter updated via SIGHUP are only taken into
account by newly-started backends, right? Hence, a way to do what we
want is to actually copy the data needed to initialize the SSL context
into alternate file(s). When postmaster starts up, or when SIGHUP
shows up those alternate files are upserted by the postmaster.
be-secure-openssl.c needs also to be changed such as with EXEC_BACKEND
the context needs to be loaded from those alternate files. At quick
glance this seems doable.
For now I am moving the patch to the next CF, more investigation is
surely needed.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Aug 26, 2015 at 12:24 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Wed, Aug 26, 2015 at 10:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
[...]
So I think the way to move this forward is to investigate how to hold
the SSL config constant until SIGHUP in an EXEC_BACKEND build. If we
find out that that's unreasonably difficult, maybe we'll decide that
we can live without it; but I'd like to see the question investigated
rather than ignored.You have a point here.
In EXEC_BACKEND, parameter updated via SIGHUP are only taken into
account by newly-started backends, right?
Oops. I mistook with PGC_BACKEND here. Sorry for the noise.
Hence, a way to do what we
want is to actually copy the data needed to initialize the SSL context
into alternate file(s). When postmaster starts up, or when SIGHUP
shows up those alternate files are upserted by the postmaster.
be-secure-openssl.c needs also to be changed such as with EXEC_BACKEND
the context needs to be loaded from those alternate files. At quick
glance this seems doable.
Still, this idea would be to use a set of alternate files in global/
to set the context, basically something like
config_exec_ssl_cert_file, config_exec_ssl_key_file and
config_exec_ssl_ca_file. It does not seem to be necessary to
manipulate [read|write]_nondefault_variables() as the use of this
metadata should be made only when SSL context is initialized on
backend. Other thoughts welcome.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 08/26/2015 03:57 AM, Tom Lane wrote:
Is it unreasonable of me to ask for the Windows behavior to be fixed at
the same time? I dunno. It's perhaps less broken than the Unix behavior,
but that doesn't make it desirable. OTOH it might be a significantly
larger patch, and I confess I'm not even too sure what we'd have to do.So I think the way to move this forward is to investigate how to hold
the SSL config constant until SIGHUP in an EXEC_BACKEND build. If we
find out that that's unreasonably difficult, maybe we'll decide that
we can live without it; but I'd like to see the question investigated
rather than ignored.
I think this is a real concern and one that I will look into, to see if
it can be fixed with a reasonable amount of work.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2015-08-27 01:12:54 +0200, Andreas Karlsson wrote:
I think this is a real concern and one that I will look into, to see if it
can be fixed with a reasonable amount of work.
This patch has been in waiting-for-author for a month. Marking it as
returned-with-feedback.
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 08/26/2015 07:46 AM, Michael Paquier wrote:
On Wed, Aug 26, 2015 at 12:24 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:On Wed, Aug 26, 2015 at 10:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
[...]
So I think the way to move this forward is to investigate how to hold
the SSL config constant until SIGHUP in an EXEC_BACKEND build. If we
find out that that's unreasonably difficult, maybe we'll decide that
we can live without it; but I'd like to see the question investigated
rather than ignored.You have a point here.
In EXEC_BACKEND, parameter updated via SIGHUP are only taken into
account by newly-started backends, right?Oops. I mistook with PGC_BACKEND here. Sorry for the noise.
Hence, a way to do what we
want is to actually copy the data needed to initialize the SSL context
into alternate file(s). When postmaster starts up, or when SIGHUP
shows up those alternate files are upserted by the postmaster.
be-secure-openssl.c needs also to be changed such as with EXEC_BACKEND
the context needs to be loaded from those alternate files. At quick
glance this seems doable.Still, this idea would be to use a set of alternate files in global/
to set the context, basically something like
config_exec_ssl_cert_file, config_exec_ssl_key_file and
config_exec_ssl_ca_file. It does not seem to be necessary to
manipulate [read|write]_nondefault_variables() as the use of this
metadata should be made only when SSL context is initialized on
backend. Other thoughts welcome.
Sorry for dropping this patch, but now I have started looking at it again.
I started implementing your suggested solution, but realized that I do
not like copying of the private key file. The private key might have
been put by the DBA on another file system for security reasons and
having PostgreSQL copy potentially sensitive data to somewhere under
pg_data seems like a surprising behavior. Especially since this only
happens on some platforms.
I guess a possible solution would be to read the files into the
postmaster (where we already have the private key today) and have
OpenSSL read the keys from memory and re-implement something like
SSL_CTX_use_certificate_chain_file() in our code, and similar things for
the other functions which now take a path. This seems like a bit too
much work to burden this patch with (and not obviously something we
would want anyway) since the behavior is already different on Windows in
the current code.
Thoughts?
I have attached a rebased version of the original patch which applies on
current master.
Andreas
Attachments:
reload-ssl-v02.patchtext/x-diff; name=reload-ssl-v02.patchDownload
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index e9bc282..7dda4be 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,14 @@ static DH *load_dh_buffer(const char *, size_t);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static SSL_CTX *initialize_context(void);
+static bool initialize_ecdh(SSL_CTX *context);
static const char *SSLerrmessage(void);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -157,168 +159,39 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
+int
be_tls_init(void)
{
- struct stat buf;
+ SSL_CTX *context;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
- {
+ if (!SSL_initialized) {
#if SSLEAY_VERSION_NUMBER >= 0x0907000L
OPENSSL_config(NULL);
#endif
SSL_library_init();
SSL_load_error_strings();
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage())));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage())));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- /*
- * Require no public access to key file.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("Permissions should be u=rw (0600) or less.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage())));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage())));
+ SSL_initialized = true;
}
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
- {
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage())));
- }
-
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
+ if ((context = initialize_context()) != NULL)
{
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage())));
- }
+ be_tls_destroy();
+ SSL_context = context;
+ /* Remember if we would have loaded the CA store */
+ ssl_loaded_verify_locations = !!ssl_ca_file[0];
+ return 0;
}
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
+ return -1;
+}
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
}
/*
@@ -924,27 +797,196 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+#define INIT_CONTEXT_ERROR(error) do { \
+ ereport(LOG, error); \
+ SSL_CTX_free(context); \
+ return NULL; \
+} while (0);
+
+static SSL_CTX *
+initialize_context(void)
+{
+ struct stat buf;
+
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+
+ SSL_CTX *context;
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not create SSL context: %s", SSLerrmessage())));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage())));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ /*
+ * Require no public access to key file.
+ *
+ * XXX temporarily suppress check when on Windows, because there may not
+ * be proper support for Unix-y file permissions. Need to think of a
+ * reasonable check to apply on Windows. (See also the data directory
+ * permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("Permissions should be u=rw (0600) or less.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage())));
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("check of private key failed: %s", SSLerrmessage())));
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ if (!initialize_ecdh(context))
+ {
+ SSL_CTX_free(context);
+ return NULL;
+ }
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")));
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage())));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage())));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
+ }
+
+ return context;
+}
+
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
EC_KEY *ecdh;
int nid;
nid = OBJ_sn2nid(SSLECDHCurve);
- if (!nid)
- ereport(FATAL,
+ if (!nid) {
+ ereport(LOG,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
- if (!ecdh)
- ereport(FATAL,
+ if (!ecdh) {
+ ereport(LOG,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 2ddcf42..e23cd00 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -63,15 +63,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
secure_initialize(void)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init();
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3cba0e5..d644c60 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -237,6 +237,8 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -911,7 +913,12 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1940,7 +1947,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2493,6 +2500,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize() != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4690,7 +4713,12 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..4992606 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -903,7 +903,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -912,7 +912,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3303,7 +3303,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3313,7 +3313,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3323,7 +3323,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3333,7 +3333,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3375,7 +3375,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3390,7 +3390,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 029114f..a5b64d0 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -78,15 +78,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = on
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index caaa8b5..1c058dd 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -201,7 +201,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(void);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index efb2dac..1730067 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -84,6 +84,7 @@ extern int (*pq_putmessage_hook) (char msgtype, const char *s, size_t len);
extern int (*pq_flush_hook) (void);
extern int secure_initialize(void);
+extern void secure_destroy(void);
extern bool secure_loaded_verify_locations(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
On Mon, Nov 23, 2015 at 12:29 PM, Andreas Karlsson <andreas@proxel.se> wrote:
On 08/26/2015 07:46 AM, Michael Paquier wrote:
On Wed, Aug 26, 2015 at 12:24 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:On Wed, Aug 26, 2015 at 10:57 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
[...]
So I think the way to move this forward is to investigate how to hold
the SSL config constant until SIGHUP in an EXEC_BACKEND build. If we
find out that that's unreasonably difficult, maybe we'll decide that
we can live without it; but I'd like to see the question investigated
rather than ignored.You have a point here.
In EXEC_BACKEND, parameter updated via SIGHUP are only taken into
account by newly-started backends, right?Oops. I mistook with PGC_BACKEND here. Sorry for the noise.
Hence, a way to do what we
want is to actually copy the data needed to initialize the SSL context
into alternate file(s). When postmaster starts up, or when SIGHUP
shows up those alternate files are upserted by the postmaster.
be-secure-openssl.c needs also to be changed such as with EXEC_BACKEND
the context needs to be loaded from those alternate files. At quick
glance this seems doable.Still, this idea would be to use a set of alternate files in global/
to set the context, basically something like
config_exec_ssl_cert_file, config_exec_ssl_key_file and
config_exec_ssl_ca_file. It does not seem to be necessary to
manipulate [read|write]_nondefault_variables() as the use of this
metadata should be made only when SSL context is initialized on
backend. Other thoughts welcome.I started implementing your suggested solution, but realized that I do not
like copying of the private key file. The private key might have been put by
the DBA on another file system for security reasons and having PostgreSQL
copy potentially sensitive data to somewhere under pg_data seems like a
surprising behavior. Especially since this only happens on some platforms.
You are referring for example to Fedora/Ubuntu that use a symlink to
point to those SSL files, right? Yes this approach may be a security
concern in those cases... One idea may be that we actually encrypt
this data
I guess a possible solution would be to read the files into the postmaster
(where we already have the private key today) and have OpenSSL read the keys
from memory and re-implement something like
SSL_CTX_use_certificate_chain_file() in our code, and similar things for the
other functions which now take a path. This seems like a bit too much work
to burden this patch with (and not obviously something we would want anyway)
since the behavior is already different on Windows in the current code.Thoughts?
Reimplementing a twin of SSL_CTX_use_certificate_chain_file() would
have benefits in this case, but that's really something to avoid. I
may say something stupid here, but what if as you say we store the
information of the certificate into a dedicated shared memory block
when postmaster starts up, except that when we need to reload the keys
we dump them into a temporary file with tmpfile or similar and then
read it using SSL_CTX_use_certificate_chain_file(). For EXEC_BACKEND,
the shared memory block will be reattached at startup using
PGSharedMemoryReAttach() so it should have the data. For
non-EXEC_BACKEND, the child processes will just inherit the shmem
block with fork(). When SIGHUP is issued, all the processes
unconditionally dump the data into a per-process tmp file and then
reload it in the SSL context.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sun, Nov 22, 2015 at 7:29 PM, Andreas Karlsson <andreas@proxel.se> wrote:
Sorry for dropping this patch, but now I have started looking at it again.
Any chance of picking this up again soon, Andreas? I think it's an
important project. I would like to review it.
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 08/31/2016 11:34 PM, Peter Geoghegan wrote:
On Sun, Nov 22, 2015 at 7:29 PM, Andreas Karlsson <andreas@proxel.se> wrote:
Sorry for dropping this patch, but now I have started looking at it again.
Any chance of picking this up again soon, Andreas? I think it's an
important project. I would like to review it.
I do not really have any good ideas for how to fix it for Windows, but
if anyone would like to discuss solutions I am interested in working on
this patch again.
The alternatives as I see them now:
1) Serialize the certificates, key, and CRL and write them to the
backend_var temp file and then deserialize everything in the backends.
Sounds like you would need to write some code for every SSL library to
support the serialization and deserialization, which I am not a fan of
doing just for one platform since I worry about little used code paths.
Additionally this would mean that we write a copy of the private key to
potentially another file system than the one where the private key is
stored, this sounds like a bad idea from a security point of view.
2) Copy all the SSL related files into the data directory at SIGHUP,
before loading them. While this does not require any serialization of
certificates it still has the problem of writing private keys to disk.
3) Leave my patch as it is now. This means the postmaster will reload
certificates on SIGHUP while the backends will also load them when
spawning. This means windows will continue to work the same as before my
patch.
Is there any other way to pass the current set of loaded certificates
and keys from the postmaster to the backends on Windows? I guess you
could use a pipe, but if so we should probably send all data on this
pipe, not just the SSL stuff.
I am leaning towards doing (3) but I know I am biased since it is less
work and I do not care much for Windows.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sun, Sep 4, 2016 at 11:39 PM, Andreas Karlsson <andreas@proxel.se> wrote:
1) Serialize the certificates, key, and CRL and write them to the
backend_var temp file and then deserialize everything in the backends.Sounds like you would need to write some code for every SSL library to
support the serialization and deserialization, which I am not a fan of doing
just for one platform since I worry about little used code paths.
Additionally this would mean that we write a copy of the private key to
potentially another file system than the one where the private key is
stored, this sounds like a bad idea from a security point of view.
Yeah... This would result in something that is heavily SSL-dependent,
which would be an additional maintenance pain when trying to support
future OpenSSL versions.
2) Copy all the SSL related files into the data directory at SIGHUP, before
loading them. While this does not require any serialization of certificates
it still has the problem of writing private keys to disk.
You expressed enough concern about that upthread, copying private keys
into PGDATA is a security concern.
3) Leave my patch as it is now. This means the postmaster will reload
certificates on SIGHUP while the backends will also load them when spawning.
This means windows will continue to work the same as before my patch.Is there any other way to pass the current set of loaded certificates and
keys from the postmaster to the backends on Windows? I guess you could use a
pipe, but if so we should probably send all data on this pipe, not just the
SSL stuff.I am leaning towards doing (3) but I know I am biased since it is less work
and I do not care much for Windows.
Seriously... The benefit of this feature is clear for a lot of people.
And the implementation dedicated only to Windows would just result in
a grotty thing anyway. So I'd say that at this point we could just
push for 3) and facilitate the life of most with SSL configuration.
The behavior across platforms needs to be properly documented for
sure.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, 7 Sep 2016 17:09:17 +0900
Michael Paquier <michael.paquier@gmail.com> wrote:
On Sun, Sep 4, 2016 at 11:39 PM, Andreas Karlsson <andreas@proxel.se>
wrote:1) Serialize the certificates, key, and CRL and write them to the
backend_var temp file and then deserialize everything in the
backends.Sounds like you would need to write some code for every SSL library
to support the serialization and deserialization, which I am not a
fan of doing just for one platform since I worry about little used
code paths. Additionally this would mean that we write a copy of
the private key to potentially another file system than the one
where the private key is stored, this sounds like a bad idea from a
security point of view.Yeah... This would result in something that is heavily SSL-dependent,
which would be an additional maintenance pain when trying to support
future OpenSSL versions.
OpenSSL has documented API for serializing/deserializing each and every
cryptographic format it supports. And this API quite unlikely to change
in future OpenSSL versions. Moreover, LibreSSL is compatible with this
API as far as I know.
Really, Apache does simular thing for ages - it starts as root, loads
certificates and keys, serializes them in memory, then forks and drops
privileges, and then uses these keys and certificates.
There are two problems with this approach
1. You have to carefully design data structures to store serialized
keys. Apache made a mistake there and didn't allow for future invention
of new public key algorithms. So, in 2008 I had problems adding
russian GOST cryptography there.
2. You keys and certificates might not be stored in the filesystem at
all. They can live in some hardware cryptography module, which don't
let private keys out, just provide some handle.
(OpenSSL supports it via lodable engine modules, and Postgres allows to
use engine modules since 8.2, so people can use it with postgres).
Really OpenSSL/LibreSSL provide useful abstraction of memory BIO (Where
BIO stands for basic input/output) which can be used to store
serialized cryptographic data. And serializing/deserializing API is
designed to work with BIO anyway.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I have attached a version of the patch rebased on top of the OpenSSL 1.1
changes.
Andreas
Attachments:
reload-ssl-v03.patchtext/x-patch; name=reload-ssl-v03.patchDownload
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 668f217..a1b582f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,14 @@ static DH *generate_dh_parameters(int prime_len, int generator);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static SSL_CTX *initialize_context(void);
+static bool initialize_ecdh(SSL_CTX *context);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -157,14 +159,12 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
+int
be_tls_init(void)
{
- struct stat buf;
+ SSL_CTX *context;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
+ if (!SSL_initialized)
{
#ifdef HAVE_OPENSSL_INIT_SSL
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,177 +173,28 @@ be_tls_init(void)
SSL_library_init();
SSL_load_error_strings();
#endif
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage(ERR_get_error()))));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage(ERR_get_error()))));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- if (!S_ISREG(buf.st_mode))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" is not a regular file",
- ssl_key_file)));
-
- /*
- * Refuse to load files owned by users other than us or root.
- *
- * XXX surely we can check this on Windows somehow, too.
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (buf.st_uid != geteuid() && buf.st_uid != 0)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" must be owned by the database user or root",
- ssl_key_file)));
-#endif
-
- /*
- * Require no public access to key file. If the file is owned by us,
- * require mode 0600 or less. If owned by root, require 0640 or less
- * to allow read access through our gid, or a supplementary gid that
- * allows to read system-wide certificates.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
- (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage(ERR_get_error()))));
- }
-
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
- {
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ SSL_initialized = true;
}
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
+ if ((context = initialize_context()) != NULL)
{
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage(ERR_get_error()))));
- }
+ be_tls_destroy();
+ SSL_context = context;
+ /* Set flag to remember if CA store is successfully loaded */
+ ssl_loaded_verify_locations = !!ssl_ca_file[0];
+ return 0;
}
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
+ return -1;
+}
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
}
/*
@@ -1034,27 +885,219 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+#define INIT_CONTEXT_ERROR(error) do { \
+ ereport(LOG, error); \
+ SSL_CTX_free(context); \
+ return NULL; \
+} while (0);
+
+static SSL_CTX *
+initialize_context(void)
+{
+ struct stat buf;
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+ SSL_CTX *context;
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ if (!S_ISREG(buf.st_mode))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" is not a regular file",
+ ssl_key_file)));
+
+ /*
+ * Refuse to load files owned by users other than us or root.
+ *
+ * XXX surely we can check this on Windows somehow, too.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (buf.st_uid != geteuid() && buf.st_uid != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" must be owned by the database user or root",
+ ssl_key_file)));
+#endif
+
+ /*
+ * Require no public access to key file. If the file is owned by us,
+ * require mode 0600 or less. If owned by root, require 0640 or less
+ * to allow read access through our gid, or a supplementary gid that
+ * allows to read system-wide certificates.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+ (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ if (!initialize_ecdh(context))
+ {
+ SSL_CTX_free(context);
+ return NULL;
+ }
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")))
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
+ }
+
+ return context;
+}
+
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
int nid;
nid = OBJ_sn2nid(SSLECDHCurve);
- if (!nid)
- ereport(FATAL,
+ if (!nid) {
+ ereport(LOG,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
- if (!ecdh)
- ereport(FATAL,
+ if (!ecdh) {
+ ereport(LOG,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index b267507..7d7faa6 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -64,15 +64,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
secure_initialize(void)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init();
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74..c997581 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -241,6 +241,8 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -929,7 +931,12 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1958,7 +1965,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2511,6 +2518,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize() != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4734,7 +4757,12 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 65660c1..72f7d0e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -935,7 +935,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -944,7 +944,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3437,7 +3437,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3447,7 +3447,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3457,7 +3457,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3467,7 +3467,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3509,7 +3509,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3524,7 +3524,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 7c2daa5..f71218d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,15 +76,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = md5 # md5 or plain
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b91eca5..9d835e3 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -200,7 +200,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(void);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 18052cb..1e6e959 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -85,6 +85,7 @@ extern int (*pq_putmessage_hook) (char msgtype, const char *s, size_t len);
extern int (*pq_flush_hook) (void);
extern int secure_initialize(void);
+extern void secure_destroy(void);
extern bool secure_loaded_verify_locations(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
Hi,
this is a first review of this patch.
As a feature, I think this functionality is very welcome. Having to
schedule a downtime in order to enable SSL or change the SSL certificate
is a nuisance and it might make admins think twice, reducing security.
The patch applies cleanly (modulo fuzz in the last hunk, but see below)
to master as of 00a86856, it compiles cleanly when using --with-openssl
and without that option and passes make check for both cases.
It does not update the documentation, I think at least section 18.9
"Secure TCP/IP Connections with SSL" needs updating as it says: "The
files server.key, server.crt, root.crt, and root.crl (or their
configured alternative names) are only examined during server start; so
you must restart the server for changes in them to take effect".
I (so far) lightly tested the functionality, and I could not find any
serious logic issues up till now. Changing or replacing the server
certificate with an expired one would make psql no longer connect with
sslmode=require.
However see below for segfaults during testing.
Some code comments on the patch:
On Mon, Oct 31, 2016 at 10:40:18AM +0100, Andreas Karlsson wrote:
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 668f217..a1b582f 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -77,12 +77,14 @@ static DH *generate_dh_parameters(int prime_len, int generator);
[...]
+ if ((context = initialize_context()) != NULL)
{
[...]
+ be_tls_destroy();
I am getting segfaults on reloading the configuration for this call.
|Program received signal SIGSEGV, Segmentation fault.
|X509_VERIFY_PARAM_free (param=0x21) at x509_vpm.c:105
|105 x509_vpm.c: Datei oder Verzeichnis nicht gefunden.
|(gdb) bt full
|#0 X509_VERIFY_PARAM_free (param=0x21) at x509_vpm.c:105
|No locals.
|#1 0x00007f3b59745a09 in SSL_CTX_free (a=0x21) at ssl_lib.c:1955
| i = -1
|#2 0x00000000005ee314 in be_tls_destroy () at be-secure-openssl.c:197
|No locals.
|#3 be_tls_init () at be-secure-openssl.c:181
|No locals.
|#4 0x00000000005e3f55 in secure_initialize () at be-secure.c:74
|No locals.
|#5 0x000000000065f377 in SIGHUP_handler (postgres_signal_arg=<optimized out>) at postmaster.c:2524
| save_errno = 4
| __func__ = "SIGHUP_handler"
|#6 <signal handler called>
|No locals.
|#7 0x00007f3b58938873 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:81
|No locals.
|#8 0x000000000046e027 in ServerLoop () at postmaster.c:1672
| timeout = {tv_sec = 54, tv_usec = 568533}
| rmask = {fds_bits = {56, 0 <repeats 15 times>}}
| selres = <optimized out>
| now = <optimized out>
| readmask = {fds_bits = {56, 0 <repeats 15 times>}}
| last_lockfile_recheck_time = 1478169442
| last_touch_time = 1478169442
| __func__ = "ServerLoop"
|#9 0x000000000066263e in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x2405df0)
| at postmaster.c:1316
| opt = <optimized out>
| status = <optimized out>
| userDoption = <optimized out>
| listen_addr_saved = <optimized out>
| i = <optimized out>
| output_config_variable = <optimized out>
| __func__ = "PostmasterMain"
|#10 0x000000000046f6b7 in main (argc=3, argv=0x2405df0) at main.c:228
|No locals.
What I'm doing is:
|postgres=# SHOW ssl;
| ssl
|-----
| on
|(1 row)
|
|postgres=# ALTER SYSTEM SET ssl = off;
|ALTER SYSTEM
|postgres=# SELECT pg_reload_conf();
| pg_reload_conf
|----------------
| t
|(1 row)
|
|postgres=# SHOW ssl;
| ssl
|-----
| off
|(1 row)
|
|postgres=# ALTER SYSTEM SET ssl = on;
|ALTER SYSTEM
|postgres=# SELECT pg_reload_conf();
| pg_reload_conf
|----------------
| t
|(1 row)
|
|postgres=# SHOW ssl;
|FATAL: terminating connection due to unexpected postmaster exit
|server closed the connection unexpectedly
| This probably means the server terminated abnormally
| before or while processing the request.
|The connection to the server was lost. Attempting reset: Failed.
|!>
The other one I got:
|Program received signal SIGSEGV, Segmentation fault.
|sk_pop_free (st=0x100007f00000002, func=0x7fdbac018ed0 <ASN1_OBJECT_free>) at stack.c:291
|291 stack.c: Datei oder Verzeichnis nicht gefunden.
|(gdb) bt full
|#0 sk_pop_free (st=0x100007f00000002, func=0x7fdbac018ed0 <ASN1_OBJECT_free>) at stack.c:291
| i = <optimized out>
|#1 0x00007fdbac04324a in x509_verify_param_zero (param=0x10d4b40) at x509_vpm.c:85
|No locals.
|#2 X509_VERIFY_PARAM_free (param=0x10d4b40) at x509_vpm.c:105
|No locals.
|#3 0x00007fdbac33ca09 in SSL_CTX_free (a=0x100007f00000002) at ssl_lib.c:1955
| i = -1
|#4 0x00000000005ee84c in be_tls_destroy () at be-secure-openssl.c:197
|No locals.
|#5 0x00000000005e3f65 in secure_destroy () at be-secure.c:87
|No locals.
|#6 0x000000000065f395 in SIGHUP_handler (postgres_signal_arg=<optimized out>) at postmaster.c:2532
| save_errno = 4
| __func__ = "SIGHUP_handler"
|#7 <signal handler called>
|No locals.
|#8 0x00007fdbab52f873 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:81
|No locals.
|#9 0x000000000046e027 in ServerLoop () at postmaster.c:1672
| timeout = {tv_sec = 54, tv_usec = 411921}
| rmask = {fds_bits = {56, 0 <repeats 15 times>}}
| selres = <optimized out>
| now = <optimized out>
| readmask = {fds_bits = {56, 0 <repeats 15 times>}}
| last_lockfile_recheck_time = 1478182425
| last_touch_time = 1478182425
| __func__ = "ServerLoop"
|#10 0x000000000066263e in PostmasterMain (argc=argc@entry=4, argv=argv@entry=0x108de00)
| at postmaster.c:1316
| opt = <optimized out>
| status = <optimized out>
| userDoption = <optimized out>
| listen_addr_saved = <optimized out>
| i = <optimized out>
| output_config_variable = <optimized out>
| __func__ = "PostmasterMain"
|#11 0x000000000046f6b7 in main (argc=4, argv=0x108de00) at main.c:228
|No locals.
I ran pg_reload_conf() twice after setting ssl to off here:
|postgres=# SHOW ssl;
| ssl
|-----
| on
|(1 row)
|
|postgres=# ALTER SYSTEM SET ssl = off;
|ALTER SYSTEM
|postgres=# SELECT pg_reload_conf();
| pg_reload_conf
|----------------
| t
|(1 row)
|
|postgres=# SHOW ssl;
| ssl
|-----
| off
|(1 row)
|
|postgres=# SELECT pg_reload_conf();
| pg_reload_conf
|----------------
| t
|(1 row)
|
|postgres=# SHOW ssl;
|FATAL: terminating connection due to unexpected postmaster exit
|server closed the connection unexpectedly
| This probably means the server terminated abnormally
| before or while processing the request.
|The connection to the server was lost. Attempting reset: Failed.
I set a breakpoint in be_tls_destroy() and dumped SSL_context in gdb for
both pg_reload_conf() calls and the differences are:
-$17 = {method = 0x7f4faf6bce80 <SSLv23_method_data.16018>, cipher_list = 0x298d3a0,
- cipher_list_by_id = 0x298f140, cert_store = 0x298c4c0, sessions = 0x298c370,
+$18 = {method = 0x7f4fae955b28 <main_arena+1288>, cipher_list = 0x7f4fae955b28 <main_arena+1288>,
+ cipher_list_by_id = 0x298bf40, cert_store = 0x298bf40, sessions = 0x298c370,
[...]
- sess_timeout = 0, sess_cache_full = 0, sess_hit = 0, sess_cb_hit = 0}, references = 1,
+ sess_timeout = 0, sess_cache_full = 0, sess_hit = 0, sess_cb_hit = 0}, references = 0,
[...]
- comp_methods = 0x2969920, info_callback = 0x0, client_CA = 0x298cb80, options = 56098820, mode = 2,
+ comp_methods = 0x0, info_callback = 0x0, client_CA = 0x298cb80, options = 56098820, mode = 2,
Not sure whether this might be an issue with my openssl library (this is
on Debian stable).
+ SSL_context = context; + /* Set flag to remember if CA store is successfully loaded */ + ssl_loaded_verify_locations = !!ssl_ca_file[0];
I am not sure this '!!' operator is according to project policy, it
seems to be not used elsewhere in the codebase except for imported or
auto-generated code. At least there should be a comment here, methinks.
[...]
+ + /* set up the allowed cipher list */ + if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1) + INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")))
Missing semicolon at the end of the line.
[...]
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
int nid;nid = OBJ_sn2nid(SSLECDHCurve); - if (!nid) - ereport(FATAL, + if (!nid) { + ereport(LOG,
Opening braces should be on the next line.
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve))); + return false; + }ecdh = EC_KEY_new_by_curve_name(nid); - if (!ecdh) - ereport(FATAL, + if (!ecdh) { + ereport(LOG,
Same.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 24add74..c997581 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -241,6 +241,8 @@ bool enable_bonjour = false; char *bonjour_name; bool restart_after_crash = true;+static bool LoadedSSL = false;
+
All the delarations above this one are global variables for GUCs (see
postmaster.h, lines 16-31). So ISTM this static variable declaration
should be introduced by a comment in order to logically seperate it from
the previous ones, like the following static variables are:
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
[...]
--- a/src/include/libpq/libpq.h +++ b/src/include/libpq/libpq.h @@ -85,6 +85,7 @@ extern int (*pq_putmessage_hook) (char msgtype, const char *s, size_t len); extern int (*pq_flush_hook) (void);extern int secure_initialize(void);
+extern void secure_destroy(void);
extern bool secure_loaded_verify_locations(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
This hunk baffled me at first because two lines below your added
secure_destroy() declaration, the same line is already present. I did
some digging and it turns out we had a secure_destroy() in the ancient
past, but its implementation got removed in 2008 in 4e8162865 as there
were no (more) users of it, however, the declaration was kept on until
now.
So this hunk should be removed I guess.
Michael
--
Michael Banck
Projektleiter / Senior Berater
Tel.: +49 2166 9901-171
Fax: +49 2166 9901-100
Email: michael.banck@credativ.de
credativ GmbH, HRB M�nchengladbach 12080
USt-ID-Nummer: DE204566209
Trompeterallee 108, 41189 M�nchengladbach
Gesch�ftsf�hrung: Dr. Michael Meskes, J�rg Folz, Sascha Heuer
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Thanks for the review! I have fixed all your feedback in the attached patch.
On 11/03/2016 04:24 PM, Michael Banck wrote:
It does not update the documentation, I think at least section 18.9
"Secure TCP/IP Connections with SSL" needs updating as it says: "The
files server.key, server.crt, root.crt, and root.crl (or their
configured alternative names) are only examined during server start; so
you must restart the server for changes in them to take effect".
Changed this.
However see below for segfaults during testing.
Fixed, this was due to me not setting SSL_Context to NULL after freeing it.
[...]
I am not sure this '!!' operator is according to project policy, it
seems to be not used elsewhere in the codebase except for imported or
auto-generated code. At least there should be a comment here, methinks.
I changed the code to compare with '\0' instead.
[...]
Missing semicolon at the end of the line.
[...]
Opening braces should be on the next line.
[...]
Same.
Fixed.
[...]
All the delarations above this one are global variables for GUCs (see
postmaster.h, lines 16-31). So ISTM this static variable declaration
should be introduced by a comment in order to logically seperate it from
the previous ones, like the following static variables are:
Fixed.
[...]
This hunk baffled me at first because two lines below your added
secure_destroy() declaration, the same line is already present. I did
some digging and it turns out we had a secure_destroy() in the ancient
past, but its implementation got removed in 2008 in 4e8162865 as there
were no (more) users of it, however, the declaration was kept on until
now.So this hunk should be removed I guess.
Removed.
Andreas
Attachments:
reload-ssl-v04.patchtext/x-patch; name=reload-ssl-v04.patchDownload
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 787cfce..5e78d81 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2288,8 +2288,8 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
The files <filename>server.key</>, <filename>server.crt</>,
<filename>root.crt</filename>, and <filename>root.crl</filename>
(or their configured alternative names)
- are only examined during server start; so you must restart
- the server for changes in them to take effect.
+ are examined when reloading the configuration, or when spawning the backend
+ process on <systemitem class="osname">Windows</> systems.
</para>
</sect2>
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 668f217..8449a53 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,14 @@ static DH *generate_dh_parameters(int prime_len, int generator);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static SSL_CTX *initialize_context(void);
+static bool initialize_ecdh(SSL_CTX *context);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -157,14 +159,12 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
+int
be_tls_init(void)
{
- struct stat buf;
+ SSL_CTX *context;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
+ if (!SSL_initialized)
{
#ifdef HAVE_OPENSSL_INIT_SSL
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,177 +173,29 @@ be_tls_init(void)
SSL_library_init();
SSL_load_error_strings();
#endif
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage(ERR_get_error()))));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage(ERR_get_error()))));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- if (!S_ISREG(buf.st_mode))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" is not a regular file",
- ssl_key_file)));
-
- /*
- * Refuse to load files owned by users other than us or root.
- *
- * XXX surely we can check this on Windows somehow, too.
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (buf.st_uid != geteuid() && buf.st_uid != 0)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" must be owned by the database user or root",
- ssl_key_file)));
-#endif
-
- /*
- * Require no public access to key file. If the file is owned by us,
- * require mode 0600 or less. If owned by root, require 0640 or less
- * to allow read access through our gid, or a supplementary gid that
- * allows to read system-wide certificates.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
- (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage(ERR_get_error()))));
+ SSL_initialized = true;
}
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
+ if ((context = initialize_context()) != NULL)
{
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage(ERR_get_error()))));
- }
-
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
- {
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage(ERR_get_error()))));
- }
+ SSL_CTX_free(SSL_context);
+ SSL_context = context;
+ /* Set flag to remember if CA store is successfully loaded */
+ ssl_loaded_verify_locations = ssl_ca_file[0] != '\0';
+ return 0;
}
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
+ return -1;
+}
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
}
/*
@@ -1034,8 +886,194 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+#define INIT_CONTEXT_ERROR(error) do { \
+ ereport(LOG, error); \
+ SSL_CTX_free(context); \
+ return NULL; \
+} while (0)
+
+static SSL_CTX *
+initialize_context(void)
+{
+ struct stat buf;
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+ SSL_CTX *context;
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ if (!S_ISREG(buf.st_mode))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" is not a regular file",
+ ssl_key_file)));
+
+ /*
+ * Refuse to load files owned by users other than us or root.
+ *
+ * XXX surely we can check this on Windows somehow, too.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (buf.st_uid != geteuid() && buf.st_uid != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" must be owned by the database user or root",
+ ssl_key_file)));
+#endif
+
+ /*
+ * Require no public access to key file. If the file is owned by us,
+ * require mode 0600 or less. If owned by root, require 0640 or less
+ * to allow read access through our gid, or a supplementary gid that
+ * allows to read system-wide certificates.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+ (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ if (!initialize_ecdh(context))
+ {
+ SSL_CTX_free(context);
+ return NULL;
+ }
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")));
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
+ }
+
+ return context;
+}
+
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
@@ -1043,18 +1081,26 @@ initialize_ecdh(void)
nid = OBJ_sn2nid(SSLECDHCurve);
if (!nid)
- ereport(FATAL,
+ {
+ ereport(LOG,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh)
- ereport(FATAL,
+ {
+ ereport(LOG,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index b267507..7d7faa6 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -64,15 +64,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
secure_initialize(void)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init();
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74..aeb0fe6 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -241,6 +241,9 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+/* Set when and if SSL has been initialized properly */
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -929,7 +932,12 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1958,7 +1966,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2511,6 +2519,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize() != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4734,7 +4758,12 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 65660c1..72f7d0e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -935,7 +935,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -944,7 +944,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3437,7 +3437,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3447,7 +3447,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3457,7 +3457,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3467,7 +3467,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3509,7 +3509,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3524,7 +3524,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 7c2daa5..f71218d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,15 +76,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = md5 # md5 or plain
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b91eca5..9d835e3 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -200,7 +200,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(void);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
Hi,
On Mon, Nov 07, 2016 at 02:36:00AM +0100, Andreas Karlsson wrote:
Thanks for the review! I have fixed all your feedback in the attached
patch.
Thanks! I couldn't find furhter faults in my testing. I guess the
question what to do about this on Windows is possibly still open, but as
I am not familiar with the Windows port at all I've marked it Ready for
Committer for now.
Thanks,
Michael
--
Michael Banck
Projektleiter / Senior Berater
Tel.: +49 2166 9901-171
Fax: +49 2166 9901-100
Email: michael.banck@credativ.de
credativ GmbH, HRB M�nchengladbach 12080
USt-ID-Nummer: DE204566209
Trompeterallee 108, 41189 M�nchengladbach
Gesch�ftsf�hrung: Dr. Michael Meskes, J�rg Folz, Sascha Heuer
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Nov 8, 2016 at 9:22 PM, Michael Banck <michael.banck@credativ.de> wrote:
Thanks! I couldn't find furhter faults in my testing. I guess the
question what to do about this on Windows is possibly still open, but as
I am not familiar with the Windows port at all I've marked it Ready for
Committer for now.
The conclusion on Windows per upthread is "do nothing", because there
is no way to get a good balance between usability and security. To be
honest, I am fine with that.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/08/2016 01:22 PM, Michael Banck wrote:
Thanks! I couldn't find furhter faults in my testing. I guess the
question what to do about this on Windows is possibly still open, but as
I am not familiar with the Windows port at all I've marked it Ready for
Committer for now.
Thanks again for the review!
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Nov 9, 2016 at 3:48 AM, Andreas Karlsson <andreas@proxel.se> wrote:
On 11/08/2016 01:22 PM, Michael Banck wrote:
Thanks! I couldn't find furhter faults in my testing. I guess the
question what to do about this on Windows is possibly still open, but as
I am not familiar with the Windows port at all I've marked it Ready for
Committer for now.Thanks again for the review!
It seems to me that this patch is missing something... To begin with,
src/test/ssl/ServerSetup.pm should be patched so as the new SSL
configuration is reloaded after pg_ctl reload, and not after an
instance restart. That's straight-forward:
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -96,7 +96,7 @@ sub configure_test_server_for_ssl
close HBA;
}
-# Change the configuration to use given server cert file, and restart
+# Change the configuration to use given server cert file, and reload
# the server so that the configuration takes effect.
sub switch_server_cert
{
@@ -115,6 +115,6 @@ sub switch_server_cert
print SSLCONF "ssl_crl_file='root+client.crl'\n";
close SSLCONF;
- # Stop and restart server to reload the new config.
- $node->restart;
+ # Reload the new configuration set.
+ $node->reload;
}
Once I did that, half of the tests are failing. And I would have
expected all of them to work properly.
--
Michael
Attachments:
reload-ssl-tests.patchtext/x-diff; charset=US-ASCII; name=reload-ssl-tests.patchDownload
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index d312880..37c48d9 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -96,7 +96,7 @@ sub configure_test_server_for_ssl
close HBA;
}
-# Change the configuration to use given server cert file, and restart
+# Change the configuration to use given server cert file, and reload
# the server so that the configuration takes effect.
sub switch_server_cert
{
@@ -115,6 +115,6 @@ sub switch_server_cert
print SSLCONF "ssl_crl_file='root+client.crl'\n";
close SSLCONF;
- # Stop and restart server to reload the new config.
- $node->restart;
+ # Reload the new configuration set.
+ $node->reload;
}
On 11/09/2016 06:54 AM, Michael Paquier wrote:
It seems to me that this patch is missing something... To begin with, src/test/ssl/ServerSetup.pm should be patched so as the new SSL configuration is reloaded after pg_ctl reload, and not after an instance restart. That's straight-forward: --- a/src/test/ssl/ServerSetup.pm +++ b/src/test/ssl/ServerSetup.pm @@ -96,7 +96,7 @@ sub configure_test_server_for_ssl close HBA; }-# Change the configuration to use given server cert file, and restart +# Change the configuration to use given server cert file, and reload # the server so that the configuration takes effect. sub switch_server_cert { @@ -115,6 +115,6 @@ sub switch_server_cert print SSLCONF "ssl_crl_file='root+client.crl'\n"; close SSLCONF;- # Stop and restart server to reload the new config. - $node->restart; + # Reload the new configuration set. + $node->reload; }Once I did that, half of the tests are failing. And I would have
expected all of them to work properly.
Those tests fail due to that listen_addresses cannot be changed on
reload so none of the test cases can even connect to the database. When
I hacked ServerSetup.pm to set the correct listen_address before
starting all tests pass.
It is a bit annoying that if pg_hba.conf contains hostssl then postgres
will refuse to start. Maybe this is something we should also fix in this
patch since now when we can enable SSL after starting it becomes more
useful to not bail on hostssl. What do you think?
I will look into writing a cleaner patch for ServerSetup.pm some time
later this week.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Nov 9, 2016 at 7:46 PM, Andreas Karlsson <andreas@proxel.se> wrote:
Those tests fail due to that listen_addresses cannot be changed on reload so
none of the test cases can even connect to the database. When I hacked
ServerSetup.pm to set the correct listen_address before starting all tests
pass.
Hm... listen_addresses remain constant at 127.0.0.1 and setting up
listen_addresses = '*' does not work either.. Perhaps I am missing
something?
It is a bit annoying that if pg_hba.conf contains hostssl then postgres will
refuse to start. Maybe this is something we should also fix in this patch
since now when we can enable SSL after starting it becomes more useful to
not bail on hostssl. What do you think?
I forgot that... There is the same problem today when updating
postgresql.conf and restarting the server if there is an hostssl
entry. Do you have in mind to relax things? It seems to be that the
safest bet is to not reload parameters if ssl is switched from on to
off and if pg_hba.conf has a hostssl entry, right? That complicates
the code though.
I will look into writing a cleaner patch for ServerSetup.pm some time later
this week.
Thanks. Making the restart/reload OS-dependent will be necessary.
src/test/ssl can run on Windows.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/10/2016 07:16 AM, Michael Paquier wrote:
On Wed, Nov 9, 2016 at 7:46 PM, Andreas Karlsson <andreas@proxel.se> wrote:
Those tests fail due to that listen_addresses cannot be changed on reload so
none of the test cases can even connect to the database. When I hacked
ServerSetup.pm to set the correct listen_address before starting all tests
pass.Hm... listen_addresses remain constant at 127.0.0.1 and setting up
listen_addresses = '*' does not work either.. Perhaps I am missing
something?
When PostgreSQL is started in the tests it by default only listens to a
unix socket (except on Windows). It is the call to the restart function
in the SSL tests which allows PostgreSQL to receive TCP connections.
Fixing this in the SSL tests will require some refactoring.
It is a bit annoying that if pg_hba.conf contains hostssl then postgres will
refuse to start. Maybe this is something we should also fix in this patch
since now when we can enable SSL after starting it becomes more useful to
not bail on hostssl. What do you think?I forgot that... There is the same problem today when updating
postgresql.conf and restarting the server if there is an hostssl
entry. Do you have in mind to relax things? It seems to be that the
safest bet is to not reload parameters if ssl is switched from on to
off and if pg_hba.conf has a hostssl entry, right? That complicates
the code though.
I personally think that it would be cleaner and easier to understand if
we just do not fail on hostssl lines just because SSL is disabled. A
warning should be enough. But I do not have any strong opinions here,
and would be fine with leaving the code as-is.
I will look into writing a cleaner patch for ServerSetup.pm some time later
this week.Thanks. Making the restart/reload OS-dependent will be necessary.
src/test/ssl can run on Windows.
I do not think that this will be an issue with the current design, but I
do not have access to a Windows machine for testing.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
Here is a new version of the patch with the only differences;
1) The SSL tests have been changed to use reload rather than restart
2) Rebased on master
Please take a look.
Andreas
Attachments:
reload-ssl-v05.patchtext/x-patch; name=reload-ssl-v05.patchDownload
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 787cfce..5e78d81 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2288,8 +2288,8 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
The files <filename>server.key</>, <filename>server.crt</>,
<filename>root.crt</filename>, and <filename>root.crl</filename>
(or their configured alternative names)
- are only examined during server start; so you must restart
- the server for changes in them to take effect.
+ are examined when reloading the configuration, or when spawning the backend
+ process on <systemitem class="osname">Windows</> systems.
</para>
</sect2>
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 668f217..8449a53 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,14 @@ static DH *generate_dh_parameters(int prime_len, int generator);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static SSL_CTX *initialize_context(void);
+static bool initialize_ecdh(SSL_CTX *context);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -157,14 +159,12 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
+int
be_tls_init(void)
{
- struct stat buf;
+ SSL_CTX *context;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
+ if (!SSL_initialized)
{
#ifdef HAVE_OPENSSL_INIT_SSL
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,177 +173,29 @@ be_tls_init(void)
SSL_library_init();
SSL_load_error_strings();
#endif
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage(ERR_get_error()))));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage(ERR_get_error()))));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- if (!S_ISREG(buf.st_mode))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" is not a regular file",
- ssl_key_file)));
-
- /*
- * Refuse to load files owned by users other than us or root.
- *
- * XXX surely we can check this on Windows somehow, too.
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (buf.st_uid != geteuid() && buf.st_uid != 0)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" must be owned by the database user or root",
- ssl_key_file)));
-#endif
-
- /*
- * Require no public access to key file. If the file is owned by us,
- * require mode 0600 or less. If owned by root, require 0640 or less
- * to allow read access through our gid, or a supplementary gid that
- * allows to read system-wide certificates.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
- (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage(ERR_get_error()))));
+ SSL_initialized = true;
}
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
+ if ((context = initialize_context()) != NULL)
{
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage(ERR_get_error()))));
- }
-
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
- {
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage(ERR_get_error()))));
- }
+ SSL_CTX_free(SSL_context);
+ SSL_context = context;
+ /* Set flag to remember if CA store is successfully loaded */
+ ssl_loaded_verify_locations = ssl_ca_file[0] != '\0';
+ return 0;
}
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
+ return -1;
+}
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
}
/*
@@ -1034,8 +886,194 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+#define INIT_CONTEXT_ERROR(error) do { \
+ ereport(LOG, error); \
+ SSL_CTX_free(context); \
+ return NULL; \
+} while (0)
+
+static SSL_CTX *
+initialize_context(void)
+{
+ struct stat buf;
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+ SSL_CTX *context;
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ if (!S_ISREG(buf.st_mode))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" is not a regular file",
+ ssl_key_file)));
+
+ /*
+ * Refuse to load files owned by users other than us or root.
+ *
+ * XXX surely we can check this on Windows somehow, too.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (buf.st_uid != geteuid() && buf.st_uid != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" must be owned by the database user or root",
+ ssl_key_file)));
+#endif
+
+ /*
+ * Require no public access to key file. If the file is owned by us,
+ * require mode 0600 or less. If owned by root, require 0640 or less
+ * to allow read access through our gid, or a supplementary gid that
+ * allows to read system-wide certificates.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+ (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ if (!initialize_ecdh(context))
+ {
+ SSL_CTX_free(context);
+ return NULL;
+ }
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")));
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
+ }
+
+ return context;
+}
+
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
@@ -1043,18 +1081,26 @@ initialize_ecdh(void)
nid = OBJ_sn2nid(SSLECDHCurve);
if (!nid)
- ereport(FATAL,
+ {
+ ereport(LOG,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh)
- ereport(FATAL,
+ {
+ ereport(LOG,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index b267507..7d7faa6 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -64,15 +64,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
secure_initialize(void)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init();
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74..aeb0fe6 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -241,6 +241,9 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+/* Set when and if SSL has been initialized properly */
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -929,7 +932,12 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1958,7 +1966,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2511,6 +2519,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize() != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4734,7 +4758,12 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 3c695c1..4c02e62 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -935,7 +935,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -944,7 +944,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3437,7 +3437,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3447,7 +3447,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3457,7 +3457,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3467,7 +3467,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3509,7 +3509,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3524,7 +3524,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 7c2daa5..f71218d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,15 +76,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = md5 # md5 or plain
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b91eca5..9d835e3 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -200,7 +200,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(void);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index d312880..97257bc 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -70,7 +70,11 @@ sub configure_test_server_for_ssl
close CONF;
-# Copy all server certificates and keys, and client root cert, to the data dir
+ # ssl configuration will be placed here
+ open SSLCONF, ">$pgdata/sslconfig.conf";
+ close SSLCONF;
+
+ # Copy all server certificates and keys, and client root cert, to the data dir
copy_files("ssl/server-*.crt", $pgdata);
copy_files("ssl/server-*.key", $pgdata);
chmod(0600, glob "$pgdata/server-*.key") or die $!;
@@ -78,22 +82,11 @@ sub configure_test_server_for_ssl
copy_files("ssl/root_ca.crt", $pgdata);
copy_files("ssl/root+client.crl", $pgdata);
- # Only accept SSL connections from localhost. Our tests don't depend on this
- # but seems best to keep it as narrow as possible for security reasons.
- #
- # When connecting to certdb, also check the client certificate.
- open HBA, ">$pgdata/pg_hba.conf";
- print HBA
-"# TYPE DATABASE USER ADDRESS METHOD\n";
- print HBA
-"hostssl trustdb ssltestuser $serverhost/32 trust\n";
- print HBA
-"hostssl trustdb ssltestuser ::1/128 trust\n";
- print HBA
-"hostssl certdb ssltestuser $serverhost/32 cert\n";
- print HBA
-"hostssl certdb ssltestuser ::1/128 cert\n";
- close HBA;
+ # Stop and restart server to load new listen_addresses.
+ $node->restart;
+
+ # Change pg_hba after restart because hostssl requires ssl=on
+ configure_hba_for_ssl($node, $serverhost);
}
# Change the configuration to use given server cert file, and restart
@@ -105,7 +98,7 @@ sub switch_server_cert
my $cafile = $_[2] || "root+client_ca";
my $pgdata = $node->data_dir;
- diag "Restarting server with certfile \"$certfile\" and cafile \"$cafile\"...";
+ diag "Reloading server with certfile \"$certfile\" and cafile \"$cafile\"...";
open SSLCONF, ">$pgdata/sslconfig.conf";
print SSLCONF "ssl=on\n";
@@ -115,6 +108,29 @@ sub switch_server_cert
print SSLCONF "ssl_crl_file='root+client.crl'\n";
close SSLCONF;
- # Stop and restart server to reload the new config.
- $node->restart;
+ $node->reload;
+}
+
+sub configure_hba_for_ssl
+{
+ my $node = $_[0];
+ my $serverhost = $_[1];
+ my $pgdata = $node->data_dir;
+
+ # Only accept SSL connections from localhost. Our tests don't depend on this
+ # but seems best to keep it as narrow as possible for security reasons.
+ #
+ # When connecting to certdb, also check the client certificate.
+ open HBA, ">$pgdata/pg_hba.conf";
+ print HBA
+"# TYPE DATABASE USER ADDRESS METHOD\n";
+ print HBA
+"hostssl trustdb ssltestuser $serverhost/32 trust\n";
+ print HBA
+"hostssl trustdb ssltestuser ::1/128 trust\n";
+ print HBA
+"hostssl certdb ssltestuser $serverhost/32 cert\n";
+ print HBA
+"hostssl certdb ssltestuser ::1/128 cert\n";
+ close HBA;
}
On 11/11/2016 07:40 PM, Andreas Karlsson wrote:
Hi,
Here is a new version of the patch with the only differences;
1) The SSL tests have been changed to use reload rather than restart
2) Rebased on master
And here with a fix to a comment.
Andreas
Attachments:
reload-ssl-v06.patchtext/x-patch; name=reload-ssl-v06.patchDownload
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 787cfce..5e78d81 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2288,8 +2288,8 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
The files <filename>server.key</>, <filename>server.crt</>,
<filename>root.crt</filename>, and <filename>root.crl</filename>
(or their configured alternative names)
- are only examined during server start; so you must restart
- the server for changes in them to take effect.
+ are examined when reloading the configuration, or when spawning the backend
+ process on <systemitem class="osname">Windows</> systems.
</para>
</sect2>
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 668f217..8449a53 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,14 @@ static DH *generate_dh_parameters(int prime_len, int generator);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static SSL_CTX *initialize_context(void);
+static bool initialize_ecdh(SSL_CTX *context);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -157,14 +159,12 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
+int
be_tls_init(void)
{
- struct stat buf;
+ SSL_CTX *context;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
+ if (!SSL_initialized)
{
#ifdef HAVE_OPENSSL_INIT_SSL
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,177 +173,29 @@ be_tls_init(void)
SSL_library_init();
SSL_load_error_strings();
#endif
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage(ERR_get_error()))));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage(ERR_get_error()))));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- if (!S_ISREG(buf.st_mode))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" is not a regular file",
- ssl_key_file)));
-
- /*
- * Refuse to load files owned by users other than us or root.
- *
- * XXX surely we can check this on Windows somehow, too.
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (buf.st_uid != geteuid() && buf.st_uid != 0)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" must be owned by the database user or root",
- ssl_key_file)));
-#endif
-
- /*
- * Require no public access to key file. If the file is owned by us,
- * require mode 0600 or less. If owned by root, require 0640 or less
- * to allow read access through our gid, or a supplementary gid that
- * allows to read system-wide certificates.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
- (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage(ERR_get_error()))));
+ SSL_initialized = true;
}
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
+ if ((context = initialize_context()) != NULL)
{
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage(ERR_get_error()))));
- }
-
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
- {
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage(ERR_get_error()))));
- }
+ SSL_CTX_free(SSL_context);
+ SSL_context = context;
+ /* Set flag to remember if CA store is successfully loaded */
+ ssl_loaded_verify_locations = ssl_ca_file[0] != '\0';
+ return 0;
}
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
+ return -1;
+}
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
}
/*
@@ -1034,8 +886,194 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+#define INIT_CONTEXT_ERROR(error) do { \
+ ereport(LOG, error); \
+ SSL_CTX_free(context); \
+ return NULL; \
+} while (0)
+
+static SSL_CTX *
+initialize_context(void)
+{
+ struct stat buf;
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+ SSL_CTX *context;
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ if (!S_ISREG(buf.st_mode))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" is not a regular file",
+ ssl_key_file)));
+
+ /*
+ * Refuse to load files owned by users other than us or root.
+ *
+ * XXX surely we can check this on Windows somehow, too.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (buf.st_uid != geteuid() && buf.st_uid != 0)
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" must be owned by the database user or root",
+ ssl_key_file)));
+#endif
+
+ /*
+ * Require no public access to key file. If the file is owned by us,
+ * require mode 0600 or less. If owned by root, require 0640 or less
+ * to allow read access through our gid, or a supplementary gid that
+ * allows to read system-wide certificates.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+ (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+ INIT_CONTEXT_ERROR(
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ INIT_CONTEXT_ERROR(
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage(ERR_get_error()))));
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ if (!initialize_ecdh(context))
+ {
+ SSL_CTX_free(context);
+ return NULL;
+ }
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ INIT_CONTEXT_ERROR((errmsg("could not set the cipher list (no valid ciphers available)")));
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ INIT_CONTEXT_ERROR(
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
+ }
+
+ return context;
+}
+
+static bool
+initialize_ecdh(SSL_CTX *context)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
@@ -1043,18 +1081,26 @@ initialize_ecdh(void)
nid = OBJ_sn2nid(SSLECDHCurve);
if (!nid)
- ereport(FATAL,
+ {
+ ereport(LOG,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh)
- ereport(FATAL,
+ {
+ ereport(LOG,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index b267507..7d7faa6 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -64,15 +64,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
secure_initialize(void)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init();
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74..aeb0fe6 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -241,6 +241,9 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+/* Set when and if SSL has been initialized properly */
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -929,7 +932,12 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1958,7 +1966,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2511,6 +2519,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize() != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4734,7 +4758,12 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 3c695c1..4c02e62 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -935,7 +935,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -944,7 +944,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3437,7 +3437,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3447,7 +3447,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3457,7 +3457,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3467,7 +3467,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3509,7 +3509,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3524,7 +3524,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 7c2daa5..f71218d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,15 +76,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = md5 # md5 or plain
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b91eca5..9d835e3 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -200,7 +200,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(void);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index d312880..20eaf76 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -70,7 +70,11 @@ sub configure_test_server_for_ssl
close CONF;
-# Copy all server certificates and keys, and client root cert, to the data dir
+ # ssl configuration will be placed here
+ open SSLCONF, ">$pgdata/sslconfig.conf";
+ close SSLCONF;
+
+ # Copy all server certificates and keys, and client root cert, to the data dir
copy_files("ssl/server-*.crt", $pgdata);
copy_files("ssl/server-*.key", $pgdata);
chmod(0600, glob "$pgdata/server-*.key") or die $!;
@@ -78,25 +82,14 @@ sub configure_test_server_for_ssl
copy_files("ssl/root_ca.crt", $pgdata);
copy_files("ssl/root+client.crl", $pgdata);
- # Only accept SSL connections from localhost. Our tests don't depend on this
- # but seems best to keep it as narrow as possible for security reasons.
- #
- # When connecting to certdb, also check the client certificate.
- open HBA, ">$pgdata/pg_hba.conf";
- print HBA
-"# TYPE DATABASE USER ADDRESS METHOD\n";
- print HBA
-"hostssl trustdb ssltestuser $serverhost/32 trust\n";
- print HBA
-"hostssl trustdb ssltestuser ::1/128 trust\n";
- print HBA
-"hostssl certdb ssltestuser $serverhost/32 cert\n";
- print HBA
-"hostssl certdb ssltestuser ::1/128 cert\n";
- close HBA;
+ # Stop and restart server to load new listen_addresses.
+ $node->restart;
+
+ # Change pg_hba after restart because hostssl requires ssl=on
+ configure_hba_for_ssl($node, $serverhost);
}
-# Change the configuration to use given server cert file, and restart
+# Change the configuration to use given server cert file, and reload
# the server so that the configuration takes effect.
sub switch_server_cert
{
@@ -105,7 +98,7 @@ sub switch_server_cert
my $cafile = $_[2] || "root+client_ca";
my $pgdata = $node->data_dir;
- diag "Restarting server with certfile \"$certfile\" and cafile \"$cafile\"...";
+ diag "Reloading server with certfile \"$certfile\" and cafile \"$cafile\"...";
open SSLCONF, ">$pgdata/sslconfig.conf";
print SSLCONF "ssl=on\n";
@@ -115,6 +108,29 @@ sub switch_server_cert
print SSLCONF "ssl_crl_file='root+client.crl'\n";
close SSLCONF;
- # Stop and restart server to reload the new config.
- $node->restart;
+ $node->reload;
+}
+
+sub configure_hba_for_ssl
+{
+ my $node = $_[0];
+ my $serverhost = $_[1];
+ my $pgdata = $node->data_dir;
+
+ # Only accept SSL connections from localhost. Our tests don't depend on this
+ # but seems best to keep it as narrow as possible for security reasons.
+ #
+ # When connecting to certdb, also check the client certificate.
+ open HBA, ">$pgdata/pg_hba.conf";
+ print HBA
+"# TYPE DATABASE USER ADDRESS METHOD\n";
+ print HBA
+"hostssl trustdb ssltestuser $serverhost/32 trust\n";
+ print HBA
+"hostssl trustdb ssltestuser ::1/128 trust\n";
+ print HBA
+"hostssl certdb ssltestuser $serverhost/32 cert\n";
+ print HBA
+"hostssl certdb ssltestuser ::1/128 cert\n";
+ close HBA;
}
Hi,
On Fri, Nov 11, 2016 at 07:42:05PM +0100, Andreas Karlsson wrote:
On 11/11/2016 07:40 PM, Andreas Karlsson wrote:
Here is a new version of the patch with the only differences;
1) The SSL tests have been changed to use reload rather than restart
2) Rebased on master
And here with a fix to a comment.
Michael, as you brought up the issues with the SSL tests, do you plan to
review the latest version (and add yourself as reviewer)?
Michael
--
Michael Banck
Projektleiter / Senior Berater
Tel: +49 2166 9901-171
Fax: +49 2166 9901-100
Email: michael.banck@credativ.de
credativ GmbH, HRB M�nchengladbach 12080
USt-ID-Nummer: DE204566209
Trompeterallee 108, 41189 M�nchengladbach
Gesch�ftsf�hrung: Dr. Michael Meskes, J�rg Folz, Sascha Heuer
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Nov 23, 2016 at 5:48 AM, Michael Banck
<michael.banck@credativ.de> wrote:
On Fri, Nov 11, 2016 at 07:42:05PM +0100, Andreas Karlsson wrote:
On 11/11/2016 07:40 PM, Andreas Karlsson wrote:
Here is a new version of the patch with the only differences;
1) The SSL tests have been changed to use reload rather than restart
2) Rebased on master
And here with a fix to a comment.
Michael, as you brought up the issues with the SSL tests, do you plan to
review the latest version (and add yourself as reviewer)?
Yes. I need two days.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sat, Nov 12, 2016 at 3:42 AM, Andreas Karlsson <andreas@proxel.se> wrote:
On 11/11/2016 07:40 PM, Andreas Karlsson wrote:
Here is a new version of the patch with the only differences;
1) The SSL tests have been changed to use reload rather than restart
Did you check if the tests pass? I am getting a couple of failures
like this one:
psql: server certificate for "common-name.pg-ssltest.test" does not
match host name "127.0.0.1"
not ok 11 - sslrootcert=ssl/root+server_ca.crt sslmode=verify-full
Attached are the logs of the run I did, and the same behavior shows
for macOS and Linux. The shape of the tests look correct to me after
review. Still, seeing failing tests with sslmode=verify-full is a
problem that needs to be addressed. This may be pointing to an
incorrect CA load handling, though I could not spot a problem when
going through the code.
2) Rebased on master
And here with a fix to a comment.
config.sgml needs an update as it still mentions that SSL parameter
require a restart when updated.
I have done a couple of tests on Linux, switching ssl mode between on
and off and testing connection attempts with sslmode. Things are
proving to work as I would expect them to be, so basically that's
nice:
- switching to off with sslmode=require triggers an error:
psql: server does not support SSL, but SSL was required
- switching to on with sslmode=require connects with SSL.
- switching to off with sslmode=prefer connects without SSL.
- switching to on with sslmode=prefer connects with SSL.
I have done as well a couple of tests with Windows, where switching
ssl between on and off is proving to work properly for each new
connection. There is no surprise here, and that's as documented in the
patch.
--
Michael
On 11/24/2016 08:46 AM, Michael Paquier wrote:
On Sat, Nov 12, 2016 at 3:42 AM, Andreas Karlsson <andreas@proxel.se> wrote:
On 11/11/2016 07:40 PM, Andreas Karlsson wrote:
Here is a new version of the patch with the only differences;
1) The SSL tests have been changed to use reload rather than restart
Did you check if the tests pass? I am getting a couple of failures
like this one:
psql: server certificate for "common-name.pg-ssltest.test" does not
match host name "127.0.0.1"
not ok 11 - sslrootcert=ssl/root+server_ca.crt sslmode=verify-full
Attached are the logs of the run I did, and the same behavior shows
for macOS and Linux. The shape of the tests look correct to me after
review. Still, seeing failing tests with sslmode=verify-full is a
problem that needs to be addressed. This may be pointing to an
incorrect CA load handling, though I could not spot a problem when
going through the code.
Thanks for finding this. I will look at this more once I get home, but
the tests do not fail on my computer. I wonder what I do differently.
What versions of Perl and OpenSSL do you run and how did you run the
tests when the failed? I ran the tests by running "make check" inside
"src/test/ssl".
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/24/2016 02:49 PM, Andreas Karlsson wrote:
Thanks for finding this. I will look at this more once I get home, but
the tests do not fail on my computer. I wonder what I do differently.What versions of Perl and OpenSSL do you run and how did you run the
tests when the failed? I ran the tests by running "make check" inside
"src/test/ssl".
Never mind, I had not fetched the latest master. Once I had done so
these tests were both broken in my rebased branch and in the master
branch without any modifications. I guess I will have to bisect this
once I get home.
Inof for myself or anyone else who feels like bisecting this: the last
known good commit is 67dc4ccbb2e1c27da823eced66d9217a5652cbb0.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Nov 24, 2016 at 11:15 PM, Andreas Karlsson <andreas@proxel.se> wrote:
Never mind, I had not fetched the latest master. Once I had done so these
tests were both broken in my rebased branch and in the master branch without
any modifications. I guess I will have to bisect this once I get home.Inof for myself or anyone else who feels like bisecting this: the last known
good commit is 67dc4ccbb2e1c27da823eced66d9217a5652cbb0.
Actually, it is a bit stupid of me to not move on with this patch as
this is backend-only, and the commit causing the regression failures
is 9a1d0af which is a frontend-only changes. So I have done a review
of this patch after reverting 9a1d0af, and things are working honestly
well.
Looking at the latest patch at code-level, there is some refactoring
to introduce initialize_context(). But it is actually not necessary
(perhaps this is the remnant of a past version?) as be_tls_init() is
its only caller. This patch makes hard to look at the diffs, and it
makes future back-patching more complicated, so I would suggest
simplifying the patch as much as possible. You could use for example a
goto block for the error code path to free the context with
SSL_CTX_free(), and set up ssl_loaded_verify_locations once the CA is
loaded. The same applies to initialize_ecdh().
+ if (secure_initialize() != 0)
+ ereport(FATAL,
+ (errmsg("could not load ssl context")));
+ LoadedSSL = true;
In case of a failure, a LOG message would have been already generated,
so this duplicates the information. Worse, if log_min_messages is set
to a level higher than LOG, users *lose* information on what has
happened. I think that secure_initialize() should have an argument to
define elevel and that this routine should be in charge of generating
an error message. Having it return a status code is necessary, but you
could cast secure_initialize() with (void) to show that we don't care
about the status code in this case. There is no need to care about
freeing the new context when the FATAL code path is used as process
would just shut down.
config.sgml needs to be updated to reflect that the SSL parameters are
updated at server reload (mentioned already upthread, just
re-mentioning it to group all the comments in one place).
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Nov 28, 2016 at 2:01 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Thu, Nov 24, 2016 at 11:15 PM, Andreas Karlsson <andreas@proxel.se> wrote:
Never mind, I had not fetched the latest master. Once I had done so these
tests were both broken in my rebased branch and in the master branch without
any modifications. I guess I will have to bisect this once I get home.Inof for myself or anyone else who feels like bisecting this: the last known
good commit is 67dc4ccbb2e1c27da823eced66d9217a5652cbb0.Actually, it is a bit stupid of me to not move on with this patch as
this is backend-only, and the commit causing the regression failures
is 9a1d0af which is a frontend-only changes. So I have done a review
of this patch after reverting 9a1d0af, and things are working honestly
well.Looking at the latest patch at code-level, there is some refactoring
to introduce initialize_context(). But it is actually not necessary
(perhaps this is the remnant of a past version?) as be_tls_init() is
its only caller. This patch makes hard to look at the diffs, and it
makes future back-patching more complicated, so I would suggest
simplifying the patch as much as possible. You could use for example a
goto block for the error code path to free the context with
SSL_CTX_free(), and set up ssl_loaded_verify_locations once the CA is
loaded. The same applies to initialize_ecdh().+ if (secure_initialize() != 0) + ereport(FATAL, + (errmsg("could not load ssl context"))); + LoadedSSL = true; In case of a failure, a LOG message would have been already generated, so this duplicates the information. Worse, if log_min_messages is set to a level higher than LOG, users *lose* information on what has happened. I think that secure_initialize() should have an argument to define elevel and that this routine should be in charge of generating an error message. Having it return a status code is necessary, but you could cast secure_initialize() with (void) to show that we don't care about the status code in this case. There is no need to care about freeing the new context when the FATAL code path is used as process would just shut down.config.sgml needs to be updated to reflect that the SSL parameters are
updated at server reload (mentioned already upthread, just
re-mentioning it to group all the comments in one place).
As this patch could be really simplified this way, I am marking is as
returned with feedback.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/30/2016 06:52 AM, Michael Paquier wrote:
On Mon, Nov 28, 2016 at 2:01 PM, Michael Paquier
Looking at the latest patch at code-level, there is some refactoring
to introduce initialize_context(). But it is actually not necessary
(perhaps this is the remnant of a past version?) as be_tls_init() is
its only caller. This patch makes hard to look at the diffs, and it
makes future back-patching more complicated, so I would suggest
simplifying the patch as much as possible. You could use for example a
goto block for the error code path to free the context with
SSL_CTX_free(), and set up ssl_loaded_verify_locations once the CA is
loaded. The same applies to initialize_ecdh().+ if (secure_initialize() != 0) + ereport(FATAL, + (errmsg("could not load ssl context"))); + LoadedSSL = true; In case of a failure, a LOG message would have been already generated, so this duplicates the information. Worse, if log_min_messages is set to a level higher than LOG, users *lose* information on what has happened. I think that secure_initialize() should have an argument to define elevel and that this routine should be in charge of generating an error message. Having it return a status code is necessary, but you could cast secure_initialize() with (void) to show that we don't care about the status code in this case. There is no need to care about freeing the new context when the FATAL code path is used as process would just shut down.
Thanks, this is a really good suggestion which made the diff much
cleaner. I removed my new macro too now since I felt it mostly made the
code more cryptic just to gain a few lines of code.
config.sgml needs to be updated to reflect that the SSL parameters are
updated at server reload (mentioned already upthread, just
re-mentioning it to group all the comments in one place).
Thanks, fixed this.
As this patch could be really simplified this way, I am marking is as
returned with feedback.
I have attached a new version. The commitfest should technically have
been closed by now, so do what you like with the status. I can always
submit the patch to the next commitfest.
Andreas
Attachments:
reload-ssl-v07.patchtext/x-patch; name=reload-ssl-v07.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b1c5289..5f80930 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -959,9 +959,8 @@ include_dir 'conf.d'
<para>
Enables <acronym>SSL</> connections. Please read
<xref linkend="ssl-tcp"> before using this. The default
- is <literal>off</>. This parameter can only be set at server
- start. <acronym>SSL</> communication is only possible with
- TCP/IP connections.
+ is <literal>off</>. <acronym>SSL</> communication is only
+ possible with TCP/IP connections.
</para>
</listitem>
</varlistentry>
@@ -979,7 +978,7 @@ include_dir 'conf.d'
and client certificate verification is not performed. (In previous
releases of PostgreSQL, the name of this file was hard-coded
as <filename>root.crt</filename>.) Relative paths are relative to the
- data directory. This parameter can only be set at server start.
+ data directory.
</para>
</listitem>
</varlistentry>
@@ -994,8 +993,7 @@ include_dir 'conf.d'
<para>
Specifies the name of the file containing the SSL server certificate.
The default is <filename>server.crt</filename>. Relative paths are
- relative to the data directory. This parameter can only be set at
- server start.
+ relative to the data directory.
</para>
</listitem>
</varlistentry>
@@ -1012,8 +1010,7 @@ include_dir 'conf.d'
revocation list (CRL). The default is empty, meaning no CRL file is
loaded. (In previous releases of PostgreSQL, the name of this file was
hard-coded as <filename>root.crl</filename>.) Relative paths are
- relative to the data directory. This parameter can only be set at
- server start.
+ relative to the data directory.
</para>
</listitem>
</varlistentry>
@@ -1028,8 +1025,7 @@ include_dir 'conf.d'
<para>
Specifies the name of the file containing the SSL server private key.
The default is <filename>server.key</filename>. Relative paths are
- relative to the data directory. This parameter can only be set at
- server start.
+ relative to the data directory.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 787cfce..5e78d81 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2288,8 +2288,8 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
The files <filename>server.key</>, <filename>server.crt</>,
<filename>root.crt</filename>, and <filename>root.crl</filename>
(or their configured alternative names)
- are only examined during server start; so you must restart
- the server for changes in them to take effect.
+ are examined when reloading the configuration, or when spawning the backend
+ process on <systemitem class="osname">Windows</> systems.
</para>
</sect2>
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 668f217..ebd00de 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,13 @@ static DH *generate_dh_parameters(int prime_len, int generator);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static bool initialize_ecdh(SSL_CTX *context, bool failOnError);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -154,17 +155,19 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/* Public interface */
/* ------------------------------------------------------------ */
+
/*
* Initialize global SSL context.
*/
-void
-be_tls_init(void)
+int
+be_tls_init(bool failOnError)
{
struct stat buf;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+ SSL_CTX *context;
- if (!SSL_context)
+ if (!SSL_initialized)
{
#ifdef HAVE_OPENSSL_INIT_SSL
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,121 +176,153 @@ be_tls_init(void)
SSL_library_init();
SSL_load_error_strings();
#endif
+ SSL_initialized = true;
+ }
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage(ERR_get_error()))));
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage(ERR_get_error()))));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- if (!S_ISREG(buf.st_mode))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" is not a regular file",
- ssl_key_file)));
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
- /*
- * Refuse to load files owned by users other than us or root.
- *
- * XXX surely we can check this on Windows somehow, too.
- */
+ if (stat(ssl_key_file, &buf) != 0)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+ goto error;
+ }
+
+ if (!S_ISREG(buf.st_mode))
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" is not a regular file",
+ ssl_key_file)));
+ goto error;
+ }
+
+ /*
+ * Refuse to load files owned by users other than us or root.
+ *
+ * XXX surely we can check this on Windows somehow, too.
+ */
#if !defined(WIN32) && !defined(__CYGWIN__)
- if (buf.st_uid != geteuid() && buf.st_uid != 0)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" must be owned by the database user or root",
- ssl_key_file)));
+ if (buf.st_uid != geteuid() && buf.st_uid != 0)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" must be owned by the database user or root",
+ ssl_key_file)));
+ goto error;
+ }
#endif
- /*
- * Require no public access to key file. If the file is owned by us,
- * require mode 0600 or less. If owned by root, require 0640 or less
- * to allow read access through our gid, or a supplementary gid that
- * allows to read system-wide certificates.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
+ /*
+ * Require no public access to key file. If the file is owned by us,
+ * require mode 0600 or less. If owned by root, require 0640 or less
+ * to allow read access through our gid, or a supplementary gid that
+ * allows to read system-wide certificates.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
#if !defined(WIN32) && !defined(__CYGWIN__)
- if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
- (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+ if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+ (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+ goto error;
+ }
#endif
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage(ERR_get_error()))));
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage(ERR_get_error()))));
+ goto error;
}
/* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
SSL_OP_SINGLE_DH_USE |
SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
/* set up ephemeral ECDH keys */
- initialize_ecdh();
+ if (!initialize_ecdh(context, failOnError))
+ goto error;
/* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("could not set the cipher list (no valid ciphers available)")));
+ goto error;
+ }
/* Let server choose order */
if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
/*
* Load CA store, so we can verify client certificates if needed.
*/
if (ssl_ca_file[0])
{
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
(root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("could not load root certificate file \"%s\": %s",
ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
}
/*----------
@@ -297,7 +332,7 @@ be_tls_init(void)
*/
if (ssl_crl_file[0])
{
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
if (cvstore)
{
@@ -316,9 +351,12 @@ be_tls_init(void)
#endif
}
else
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("could not load SSL certificate revocation list file \"%s\": %s",
ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
}
}
@@ -329,7 +367,7 @@ be_tls_init(void)
* presented. We might fail such connections later, depending on what
* we find in pg_hba.conf.
*/
- SSL_CTX_set_verify(SSL_context,
+ SSL_CTX_set_verify(context,
(SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE),
verify_cb);
@@ -342,8 +380,31 @@ be_tls_init(void)
* CertificateRequests. This lets a client with a keystore select the
* appropriate client certificate to send to us.
*/
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
}
+ else
+ ssl_loaded_verify_locations = false;
+
+
+ SSL_CTX_free(SSL_context);
+ SSL_context = context;
+
+ return 0;
+
+error:
+ SSL_CTX_free(context); \
+
+ return -1;
+}
+
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
}
/*
@@ -1034,8 +1095,8 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+static bool
+initialize_ecdh(SSL_CTX *context, bool failOnError)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
@@ -1043,18 +1104,26 @@ initialize_ecdh(void)
nid = OBJ_sn2nid(SSLECDHCurve);
if (!nid)
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh)
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index b267507..bd45da4 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -64,15 +64,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
-secure_initialize(void)
+secure_initialize(bool failOnError)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init(failOnError);
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74..78b7c39 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -241,6 +241,9 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+/* Set when and if SSL has been initialized properly */
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -929,7 +932,10 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ (void) secure_initialize(true);
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1958,7 +1964,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2511,6 +2517,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize(false) != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4734,7 +4756,10 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ (void) secure_initialize(true);
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 28ebcb6..8ae99cc 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -935,7 +935,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -944,7 +944,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3435,7 +3435,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3445,7 +3445,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3455,7 +3455,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3465,7 +3465,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3507,7 +3507,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3522,7 +3522,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 0df1538..bd7308b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,15 +76,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = md5 # md5 or plain
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b91eca5..26cd60b 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -200,7 +200,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(bool failOnError);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 5fac817..66ceb2b 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -81,7 +81,7 @@ extern char *ssl_key_file;
extern char *ssl_ca_file;
extern char *ssl_crl_file;
-extern int secure_initialize(void);
+extern int secure_initialize(bool failOnError);
extern bool secure_loaded_verify_locations(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index d312880..20eaf76 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -70,7 +70,11 @@ sub configure_test_server_for_ssl
close CONF;
-# Copy all server certificates and keys, and client root cert, to the data dir
+ # ssl configuration will be placed here
+ open SSLCONF, ">$pgdata/sslconfig.conf";
+ close SSLCONF;
+
+ # Copy all server certificates and keys, and client root cert, to the data dir
copy_files("ssl/server-*.crt", $pgdata);
copy_files("ssl/server-*.key", $pgdata);
chmod(0600, glob "$pgdata/server-*.key") or die $!;
@@ -78,25 +82,14 @@ sub configure_test_server_for_ssl
copy_files("ssl/root_ca.crt", $pgdata);
copy_files("ssl/root+client.crl", $pgdata);
- # Only accept SSL connections from localhost. Our tests don't depend on this
- # but seems best to keep it as narrow as possible for security reasons.
- #
- # When connecting to certdb, also check the client certificate.
- open HBA, ">$pgdata/pg_hba.conf";
- print HBA
-"# TYPE DATABASE USER ADDRESS METHOD\n";
- print HBA
-"hostssl trustdb ssltestuser $serverhost/32 trust\n";
- print HBA
-"hostssl trustdb ssltestuser ::1/128 trust\n";
- print HBA
-"hostssl certdb ssltestuser $serverhost/32 cert\n";
- print HBA
-"hostssl certdb ssltestuser ::1/128 cert\n";
- close HBA;
+ # Stop and restart server to load new listen_addresses.
+ $node->restart;
+
+ # Change pg_hba after restart because hostssl requires ssl=on
+ configure_hba_for_ssl($node, $serverhost);
}
-# Change the configuration to use given server cert file, and restart
+# Change the configuration to use given server cert file, and reload
# the server so that the configuration takes effect.
sub switch_server_cert
{
@@ -105,7 +98,7 @@ sub switch_server_cert
my $cafile = $_[2] || "root+client_ca";
my $pgdata = $node->data_dir;
- diag "Restarting server with certfile \"$certfile\" and cafile \"$cafile\"...";
+ diag "Reloading server with certfile \"$certfile\" and cafile \"$cafile\"...";
open SSLCONF, ">$pgdata/sslconfig.conf";
print SSLCONF "ssl=on\n";
@@ -115,6 +108,29 @@ sub switch_server_cert
print SSLCONF "ssl_crl_file='root+client.crl'\n";
close SSLCONF;
- # Stop and restart server to reload the new config.
- $node->restart;
+ $node->reload;
+}
+
+sub configure_hba_for_ssl
+{
+ my $node = $_[0];
+ my $serverhost = $_[1];
+ my $pgdata = $node->data_dir;
+
+ # Only accept SSL connections from localhost. Our tests don't depend on this
+ # but seems best to keep it as narrow as possible for security reasons.
+ #
+ # When connecting to certdb, also check the client certificate.
+ open HBA, ">$pgdata/pg_hba.conf";
+ print HBA
+"# TYPE DATABASE USER ADDRESS METHOD\n";
+ print HBA
+"hostssl trustdb ssltestuser $serverhost/32 trust\n";
+ print HBA
+"hostssl trustdb ssltestuser ::1/128 trust\n";
+ print HBA
+"hostssl certdb ssltestuser $serverhost/32 cert\n";
+ print HBA
+"hostssl certdb ssltestuser ::1/128 cert\n";
+ close HBA;
}
On Fri, Dec 2, 2016 at 11:26 AM, Andreas Karlsson <andreas@proxel.se> wrote:
I have attached a new version. The commitfest should technically have been
closed by now, so do what you like with the status. I can always submit the
patch to the next commitfest.
I have just moved it to the next CF. Will look at it in couple of
days, that's more or less at the top of my TODO list.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Dec 2, 2016 at 1:02 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Fri, Dec 2, 2016 at 11:26 AM, Andreas Karlsson <andreas@proxel.se> wrote:
I have attached a new version. The commitfest should technically have been
closed by now, so do what you like with the status. I can always submit the
patch to the next commitfest.I have just moved it to the next CF. Will look at it in couple of
days, that's more or less at the top of my TODO list.
Thanks for the new version, it is far easier to check that there is no
regression with the new code.
/* Public interface */
/* ------------------------------------------------------------ */
+
/*
Useless noise.
+void
+be_tls_destroy(void)
+{
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
}
Perhaps we had better leave immediately if SSL_context is already
NULL? I have tested the error code paths by enforcing manually an
error and I could not see any failures, still I am wondering if
calling SSL_CTX_free(NULL) would be safe, particularly if there is a
callback added in the future.
+ if (secure_initialize(false) != 0)
+ ereport(WARNING,
+ (errmsg("ssl context not reloaded")));
SSL should be upper-case here.
One last thing that I think is missing in this patch is for users the
possibility to check via SQL if the SSL context is actually loaded or
not. As the context is reloaded after all the new values are
available, with the current patch users may see that ssl is set to on
but no context is loaded. So why not adding for example a read-only
parameter that maps with SSLLoaded?
Once those issues are addressed, I think that this will be ready for committer.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 12/04/2016 02:12 PM, Michael Paquier wrote:
One last thing that I think is missing in this patch is for users the
possibility to check via SQL if the SSL context is actually loaded or
not. As the context is reloaded after all the new values are
available, with the current patch users may see that ssl is set to on
but no context is loaded. So why not adding for example a read-only
parameter that maps with SSLLoaded?
The other three issues are easy to fix, but this one is a bit trickier.
Do you mean that we should add another GUC here which is read-only? Does
this have a precedent in the code?
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sun, Dec 4, 2016 at 11:11 PM, Andreas Karlsson <andreas@proxel.se> wrote:
On 12/04/2016 02:12 PM, Michael Paquier wrote:
One last thing that I think is missing in this patch is for users the
possibility to check via SQL if the SSL context is actually loaded or
not. As the context is reloaded after all the new values are
available, with the current patch users may see that ssl is set to on
but no context is loaded. So why not adding for example a read-only
parameter that maps with SSLLoaded?The other three issues are easy to fix, but this one is a bit trickier. Do
you mean that we should add another GUC here which is read-only?
Yes, that's what I meant. It is hard to track if the reloading has
been effective or not.
Does this have a precedent in the code?
data_checksums in guc.c is an example, it is marked with
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE and its value is updated with
SetConfigOption() when the control file is read. The idea would be to
do something like that with LoadedSSL.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 12/04/2016 03:20 PM, Michael Paquier wrote:
On Sun, Dec 4, 2016 at 11:11 PM, Andreas Karlsson <andreas@proxel.se> wrote:
On 12/04/2016 02:12 PM, Michael Paquier wrote:
One last thing that I think is missing in this patch is for users the
possibility to check via SQL if the SSL context is actually loaded or
not. As the context is reloaded after all the new values are
available, with the current patch users may see that ssl is set to on
but no context is loaded. So why not adding for example a read-only
parameter that maps with SSLLoaded?The other three issues are easy to fix, but this one is a bit trickier. Do
you mean that we should add another GUC here which is read-only?Yes, that's what I meant. It is hard to track if the reloading has
been effective or not.Does this have a precedent in the code?
data_checksums in guc.c is an example, it is marked with
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE and its value is updated with
SetConfigOption() when the control file is read. The idea would be to
do something like that with LoadedSSL.
Thanks. I will be quite busy the upcoming couple of weeks so there will
be a while until I can sit down with this. Feel free to contribute to
the patch.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Dec 5, 2016 at 9:50 AM, Andreas Karlsson <andreas@proxel.se> wrote:
On 12/04/2016 03:20 PM, Michael Paquier wrote:
On Sun, Dec 4, 2016 at 11:11 PM, Andreas Karlsson <andreas@proxel.se>
wrote:On 12/04/2016 02:12 PM, Michael Paquier wrote:
Does this have a precedent in the code?data_checksums in guc.c is an example, it is marked with
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE and its value is updated with
SetConfigOption() when the control file is read. The idea would be to
do something like that with LoadedSSL.
OK, here is attached what I had in mind as reload-ssl-v08-02.patch for
reference. This applies on top of the main patch
reload-ssl-v08-01.patch that is the same version as v7 with the issues
I reported previously as addressed. LoadedSSL is mapped with a
read-only GUC parameter that new sessions can query after connecting.
The only use case where that would be useful would be when using
sslmode=prefer to check whether the SSL context is loaded even if ssl
has been switched from off to on. But let's be honest, pg_stat_ssl
reports already this kind of information, making this patch at the end
useless because LoadedSSL does not change for an already-spawned
backend.
Thanks. I will be quite busy the upcoming couple of weeks so there will be a
while until I can sit down with this. Feel free to contribute to the patch.
I am switching the patch as ready for committer. I have no more
comments about this patch.
Note to committer-san: please look only at v08-01.
--
Michael
Attachments:
reload-ssl-v08-01.patchapplication/x-download; name=reload-ssl-v08-01.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index fdf8b3e9c7..23c77ab050 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -959,9 +959,8 @@ include_dir 'conf.d'
<para>
Enables <acronym>SSL</> connections. Please read
<xref linkend="ssl-tcp"> before using this. The default
- is <literal>off</>. This parameter can only be set at server
- start. <acronym>SSL</> communication is only possible with
- TCP/IP connections.
+ is <literal>off</>. <acronym>SSL</> communication is only
+ possible with TCP/IP connections.
</para>
</listitem>
</varlistentry>
@@ -979,7 +978,7 @@ include_dir 'conf.d'
and client certificate verification is not performed. (In previous
releases of PostgreSQL, the name of this file was hard-coded
as <filename>root.crt</filename>.) Relative paths are relative to the
- data directory. This parameter can only be set at server start.
+ data directory.
</para>
</listitem>
</varlistentry>
@@ -994,8 +993,7 @@ include_dir 'conf.d'
<para>
Specifies the name of the file containing the SSL server certificate.
The default is <filename>server.crt</filename>. Relative paths are
- relative to the data directory. This parameter can only be set at
- server start.
+ relative to the data directory.
</para>
</listitem>
</varlistentry>
@@ -1012,8 +1010,7 @@ include_dir 'conf.d'
revocation list (CRL). The default is empty, meaning no CRL file is
loaded. (In previous releases of PostgreSQL, the name of this file was
hard-coded as <filename>root.crl</filename>.) Relative paths are
- relative to the data directory. This parameter can only be set at
- server start.
+ relative to the data directory.
</para>
</listitem>
</varlistentry>
@@ -1028,8 +1025,7 @@ include_dir 'conf.d'
<para>
Specifies the name of the file containing the SSL server private key.
The default is <filename>server.key</filename>. Relative paths are
- relative to the data directory. This parameter can only be set at
- server start.
+ relative to the data directory.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 787cfce987..5e78d81607 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2288,8 +2288,8 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
The files <filename>server.key</>, <filename>server.crt</>,
<filename>root.crt</filename>, and <filename>root.crl</filename>
(or their configured alternative names)
- are only examined during server start; so you must restart
- the server for changes in them to take effect.
+ are examined when reloading the configuration, or when spawning the backend
+ process on <systemitem class="osname">Windows</> systems.
</para>
</sect2>
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 668f217bba..e1d33a9e48 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -77,12 +77,13 @@ static DH *generate_dh_parameters(int prime_len, int generator);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_ecdh(void);
+static bool initialize_ecdh(SSL_CTX *context, bool failOnError);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
+static bool SSL_initialized = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
@@ -157,14 +158,15 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
/*
* Initialize global SSL context.
*/
-void
-be_tls_init(void)
+int
+be_tls_init(bool failOnError)
{
struct stat buf;
- STACK_OF(X509_NAME) *root_cert_list = NULL;
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+ SSL_CTX *context;
- if (!SSL_context)
+ if (!SSL_initialized)
{
#ifdef HAVE_OPENSSL_INIT_SSL
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
@@ -173,121 +175,153 @@ be_tls_init(void)
SSL_library_init();
SSL_load_error_strings();
#endif
+ SSL_initialized = true;
+ }
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage(ERR_get_error()))));
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we don't
+ * actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ context = SSL_CTX_new(SSLv23_method());
+ if (!context)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage(ERR_get_error()))));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- if (!S_ISREG(buf.st_mode))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" is not a regular file",
- ssl_key_file)));
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
- /*
- * Refuse to load files owned by users other than us or root.
- *
- * XXX surely we can check this on Windows somehow, too.
- */
+ if (stat(ssl_key_file, &buf) != 0)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+ goto error;
+ }
+
+ if (!S_ISREG(buf.st_mode))
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" is not a regular file",
+ ssl_key_file)));
+ goto error;
+ }
+
+ /*
+ * Refuse to load files owned by users other than us or root.
+ *
+ * XXX surely we can check this on Windows somehow, too.
+ */
#if !defined(WIN32) && !defined(__CYGWIN__)
- if (buf.st_uid != geteuid() && buf.st_uid != 0)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" must be owned by the database user or root",
- ssl_key_file)));
+ if (buf.st_uid != geteuid() && buf.st_uid != 0)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" must be owned by the database user or root",
+ ssl_key_file)));
+ goto error;
+ }
#endif
- /*
- * Require no public access to key file. If the file is owned by us,
- * require mode 0600 or less. If owned by root, require 0640 or less
- * to allow read access through our gid, or a supplementary gid that
- * allows to read system-wide certificates.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
+ /*
+ * Require no public access to key file. If the file is owned by us,
+ * require mode 0600 or less. If owned by root, require 0640 or less
+ * to allow read access through our gid, or a supplementary gid that
+ * allows to read system-wide certificates.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
#if !defined(WIN32) && !defined(__CYGWIN__)
- if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
- (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+ if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+ (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+ goto error;
+ }
#endif
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage(ERR_get_error()))));
+ if (SSL_CTX_use_PrivateKey_file(context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
+
+ if (SSL_CTX_check_private_key(context) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage(ERR_get_error()))));
+ goto error;
}
/* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
+ SSL_CTX_set_tmp_dh_callback(context, tmp_dh_cb);
+ SSL_CTX_set_options(context,
SSL_OP_SINGLE_DH_USE |
SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
/* set up ephemeral ECDH keys */
- initialize_ecdh();
+ if (!initialize_ecdh(context, failOnError))
+ goto error;
/* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
+ if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+ {
+ ereport(failOnError ? FATAL : WARNING,
+ (errmsg("could not set the cipher list (no valid ciphers available)")));
+ goto error;
+ }
/* Let server choose order */
if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
/*
* Load CA store, so we can verify client certificates if needed.
*/
if (ssl_ca_file[0])
{
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
+ if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
(root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("could not load root certificate file \"%s\": %s",
ssl_ca_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
}
/*----------
@@ -297,7 +331,7 @@ be_tls_init(void)
*/
if (ssl_crl_file[0])
{
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(context);
if (cvstore)
{
@@ -316,9 +350,12 @@ be_tls_init(void)
#endif
}
else
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("could not load SSL certificate revocation list file \"%s\": %s",
ssl_crl_file, SSLerrmessage(ERR_get_error()))));
+ goto error;
+ }
}
}
@@ -329,7 +366,7 @@ be_tls_init(void)
* presented. We might fail such connections later, depending on what
* we find in pg_hba.conf.
*/
- SSL_CTX_set_verify(SSL_context,
+ SSL_CTX_set_verify(context,
(SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE),
verify_cb);
@@ -342,8 +379,34 @@ be_tls_init(void)
* CertificateRequests. This lets a client with a keystore select the
* appropriate client certificate to send to us.
*/
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
+ SSL_CTX_set_client_CA_list(context, root_cert_list);
}
+ else
+ ssl_loaded_verify_locations = false;
+
+
+ SSL_CTX_free(SSL_context);
+ SSL_context = context;
+
+ return 0;
+
+error:
+ SSL_CTX_free(context); \
+
+ return -1;
+}
+
+/*
+ * Destroy global SSL context.
+ */
+void
+be_tls_destroy(void)
+{
+ if (SSL_context == NULL)
+ return;
+
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
}
/*
@@ -1034,8 +1097,8 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-static void
-initialize_ecdh(void)
+static bool
+initialize_ecdh(SSL_CTX *context, bool failOnError)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
@@ -1043,18 +1106,26 @@ initialize_ecdh(void)
nid = OBJ_sn2nid(SSLECDHCurve);
if (!nid)
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+ return false;
+ }
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh)
- ereport(FATAL,
+ {
+ ereport(failOnError ? FATAL : WARNING,
(errmsg("ECDH: could not create key")));
+ return false;
+ }
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(context, ecdh);
EC_KEY_free(ecdh);
#endif
+
+ return true;
}
/*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index b267507de9..bd45da4a3e 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -64,15 +64,28 @@ bool SSLPreferServerCiphers;
/*
* Initialize global context
+ *
+ * Returns 0 if OK, -1 on failure.
*/
int
-secure_initialize(void)
+secure_initialize(bool failOnError)
{
#ifdef USE_SSL
- be_tls_init();
+ return be_tls_init(failOnError);
+#else
+ return 0;
#endif
+}
- return 0;
+/*
+ * Destroy global context
+ */
+void
+secure_destroy(void)
+{
+#ifdef USE_SSL
+ be_tls_destroy();
+#endif
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74512..943796ac7f 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -241,6 +241,9 @@ bool enable_bonjour = false;
char *bonjour_name;
bool restart_after_crash = true;
+/* Set when and if SSL has been initialized properly */
+static bool LoadedSSL = false;
+
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
BgWriterPID = 0,
@@ -929,7 +932,10 @@ PostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ (void) secure_initialize(true);
+ LoadedSSL = true;
+ }
#endif
/*
@@ -1958,7 +1964,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
- if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -2511,6 +2517,22 @@ SIGHUP_handler(SIGNAL_ARGS)
ereport(WARNING,
(errmsg("pg_ident.conf not reloaded")));
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize(false) != 0)
+ ereport(WARNING,
+ (errmsg("SSL context not reloaded")));
+ else
+ LoadedSSL = true;
+ }
+ else
+ {
+ secure_destroy();
+ LoadedSSL = false;
+ }
+#endif
+
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
@@ -4734,7 +4756,10 @@ SubPostmasterMain(int argc, char *argv[])
*/
#ifdef USE_SSL
if (EnableSSL)
- secure_initialize();
+ {
+ (void) secure_initialize(true);
+ LoadedSSL = true;
+ }
#endif
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4e49d5b79c..2478f0afac 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -935,7 +935,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Enables SSL connections."),
NULL
},
@@ -944,7 +944,7 @@ static struct config_bool ConfigureNamesBool[] =
check_ssl, NULL, NULL
},
{
- {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Give priority to server ciphersuite order."),
NULL
},
@@ -3445,7 +3445,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server certificate file."),
NULL
},
@@ -3455,7 +3455,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL server private key file."),
NULL
},
@@ -3465,7 +3465,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate authority file."),
NULL
},
@@ -3475,7 +3475,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Location of the SSL certificate revocation list file."),
NULL
},
@@ -3517,7 +3517,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
GUC_SUPERUSER_ONLY
@@ -3532,7 +3532,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the curve to use for ECDH."),
NULL,
GUC_SUPERUSER_ONLY
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 1f490c7de4..3199b9386a 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,15 +76,14 @@
# - Security and Authentication -
#authentication_timeout = 1min # 1s-600s
-#ssl = off # (change requires restart)
+#ssl = off
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
- # (change requires restart)
-#ssl_prefer_server_ciphers = on # (change requires restart)
-#ssl_ecdh_curve = 'prime256v1' # (change requires restart)
-#ssl_cert_file = 'server.crt' # (change requires restart)
-#ssl_key_file = 'server.key' # (change requires restart)
-#ssl_ca_file = '' # (change requires restart)
-#ssl_crl_file = '' # (change requires restart)
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+#ssl_cert_file = 'server.crt'
+#ssl_key_file = 'server.key'
+#ssl_ca_file = ''
+#ssl_crl_file = ''
#password_encryption = md5 # md5 or plain
#db_user_namespace = off
#row_security = on
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b91eca5b2c..26cd60b39f 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -200,7 +200,8 @@ typedef struct Port
* These functions are implemented by the glue code specific to each
* SSL implementation (e.g. be-secure-openssl.c)
*/
-extern void be_tls_init(void);
+extern int be_tls_init(bool failOnError);
+extern void be_tls_destroy(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 5fac8171ed..66ceb2b4a0 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -81,7 +81,7 @@ extern char *ssl_key_file;
extern char *ssl_ca_file;
extern char *ssl_crl_file;
-extern int secure_initialize(void);
+extern int secure_initialize(bool failOnError);
extern bool secure_loaded_verify_locations(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index d312880f8b..20eaf76bff 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -70,7 +70,11 @@ sub configure_test_server_for_ssl
close CONF;
-# Copy all server certificates and keys, and client root cert, to the data dir
+ # ssl configuration will be placed here
+ open SSLCONF, ">$pgdata/sslconfig.conf";
+ close SSLCONF;
+
+ # Copy all server certificates and keys, and client root cert, to the data dir
copy_files("ssl/server-*.crt", $pgdata);
copy_files("ssl/server-*.key", $pgdata);
chmod(0600, glob "$pgdata/server-*.key") or die $!;
@@ -78,25 +82,14 @@ sub configure_test_server_for_ssl
copy_files("ssl/root_ca.crt", $pgdata);
copy_files("ssl/root+client.crl", $pgdata);
- # Only accept SSL connections from localhost. Our tests don't depend on this
- # but seems best to keep it as narrow as possible for security reasons.
- #
- # When connecting to certdb, also check the client certificate.
- open HBA, ">$pgdata/pg_hba.conf";
- print HBA
-"# TYPE DATABASE USER ADDRESS METHOD\n";
- print HBA
-"hostssl trustdb ssltestuser $serverhost/32 trust\n";
- print HBA
-"hostssl trustdb ssltestuser ::1/128 trust\n";
- print HBA
-"hostssl certdb ssltestuser $serverhost/32 cert\n";
- print HBA
-"hostssl certdb ssltestuser ::1/128 cert\n";
- close HBA;
+ # Stop and restart server to load new listen_addresses.
+ $node->restart;
+
+ # Change pg_hba after restart because hostssl requires ssl=on
+ configure_hba_for_ssl($node, $serverhost);
}
-# Change the configuration to use given server cert file, and restart
+# Change the configuration to use given server cert file, and reload
# the server so that the configuration takes effect.
sub switch_server_cert
{
@@ -105,7 +98,7 @@ sub switch_server_cert
my $cafile = $_[2] || "root+client_ca";
my $pgdata = $node->data_dir;
- diag "Restarting server with certfile \"$certfile\" and cafile \"$cafile\"...";
+ diag "Reloading server with certfile \"$certfile\" and cafile \"$cafile\"...";
open SSLCONF, ">$pgdata/sslconfig.conf";
print SSLCONF "ssl=on\n";
@@ -115,6 +108,29 @@ sub switch_server_cert
print SSLCONF "ssl_crl_file='root+client.crl'\n";
close SSLCONF;
- # Stop and restart server to reload the new config.
- $node->restart;
+ $node->reload;
+}
+
+sub configure_hba_for_ssl
+{
+ my $node = $_[0];
+ my $serverhost = $_[1];
+ my $pgdata = $node->data_dir;
+
+ # Only accept SSL connections from localhost. Our tests don't depend on this
+ # but seems best to keep it as narrow as possible for security reasons.
+ #
+ # When connecting to certdb, also check the client certificate.
+ open HBA, ">$pgdata/pg_hba.conf";
+ print HBA
+"# TYPE DATABASE USER ADDRESS METHOD\n";
+ print HBA
+"hostssl trustdb ssltestuser $serverhost/32 trust\n";
+ print HBA
+"hostssl trustdb ssltestuser ::1/128 trust\n";
+ print HBA
+"hostssl certdb ssltestuser $serverhost/32 cert\n";
+ print HBA
+"hostssl certdb ssltestuser ::1/128 cert\n";
+ close HBA;
}
reload-ssl-v08-02.patchapplication/x-download; name=reload-ssl-v08-02.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 23c77ab050..9b6aa7fe74 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -965,6 +965,19 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
+ <varlistentry id="guc-ssl-loaded" xreflabel="ssl_loaded">
+ <term><varname>ssl_loaded</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>ssl_loaded</> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Reports whether the SSL context is loaded for this cluster or not.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-ssl-ca-file" xreflabel="ssl_ca_file">
<term><varname>ssl_ca_file</varname> (<type>string</type>)
<indexterm>
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 943796ac7f..e1f5dad948 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -936,6 +936,14 @@ PostmasterMain(int argc, char *argv[])
(void) secure_initialize(true);
LoadedSSL = true;
}
+
+ /*
+ * Make LoadedSSL value visible as a GUC variable, too. This allows
+ * other backends to track the activity of this parameter after being
+ * spawned.
+ */
+ SetConfigOption("ssl_loaded", LoadedSSL ? "yes" : "no",
+ PGC_INTERNAL, PGC_S_OVERRIDE);
#endif
/*
@@ -2531,6 +2539,14 @@ SIGHUP_handler(SIGNAL_ARGS)
secure_destroy();
LoadedSSL = false;
}
+
+ /*
+ * Make LoadedSSL value visible as a GUC variable, too. This allows
+ * other backends to track the activity of this parameter after being
+ * spawned.
+ */
+ SetConfigOption("ssl_loaded", LoadedSSL ? "yes" : "no",
+ PGC_INTERNAL, PGC_S_OVERRIDE);
#endif
#ifdef EXEC_BACKEND
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2478f0afac..7e973c90f7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -509,6 +509,7 @@ static int block_size;
static int segment_size;
static int wal_block_size;
static bool data_checksums;
+static bool ssl_loaded;
static int wal_segment_size;
static bool integer_datetimes;
static bool assert_enabled;
@@ -1644,6 +1645,17 @@ static struct config_bool ConfigureNamesBool[] =
},
{
+ {"ssl_loaded", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows whether SSL context is loaded for this cluster or not."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &ssl_loaded,
+ false,
+ NULL, NULL, NULL
+ },
+
+ {
{"syslog_sequence_numbers", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Add sequence number to syslog messages to avoid duplicate suppression."),
NULL
On 12/5/16 12:17 AM, Michael Paquier wrote:
OK, here is attached what I had in mind as reload-ssl-v08-02.patch for
reference. This applies on top of the main patch
reload-ssl-v08-01.patch that is the same version as v7 with the issues
I reported previously as addressed. LoadedSSL is mapped with a
read-only GUC parameter that new sessions can query after connecting.
The only use case where that would be useful would be when using
sslmode=prefer to check whether the SSL context is loaded even if ssl
has been switched from off to on. But let's be honest, pg_stat_ssl
reports already this kind of information, making this patch at the end
useless because LoadedSSL does not change for an already-spawned
backend.
Yeah, it seems that if you want to know whether you are using SSL, then
we already have that. I don't see the need for this new read-only setting.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
Yeah, it seems that if you want to know whether you are using SSL, then
we already have that. I don't see the need for this new read-only setting.
I concur --- there might be use for more reporting about SSL status, but
that patch doesn't seem like quite the right thing.
I've pushed the main patch with some small adjustments; one notable one
that I made the EXEC_BACKEND reload path work like SIGHUP reload (press on
without SSL if fail) not server start (abort if fail). As it stood, if
you fat-fingered a change to your SSL files on Windows, the postmaster
would keep running but all backends would die instantly, whether or not
an SSL connection was being requested. That didn't seem helpful.
I also went ahead and downgraded the "hostssl record with SSL turned off"
case to a warning.
Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
Our documentation suggests that if you have one, the server will demand
the passphrase just once at server start and then all is good. I'm not
sure if that's at all practical in modern usage, but in any case it's
not going to be reasonable to put a passphrase in again at every SIGHUP.
On Windows things are even worse; you'd have to give the passphrase again
to every spawned backend. (But that was true already.)
I can think of at least three things we might do about this:
1. Leave the code as it stands, and change the documentation to state
that you cannot use a passphrase-protected server key, period.
2. Add a password callback function that would supply the passphrase
when needed. The question is, where would it get that from? It'd
be straightforward to supply it from a string GUC, but from a security
POV it seems pretty silly to have the password in postgresql.conf.
3. Add a password callback function that just returns an empty string,
thereby ensuring a clean failure if the server tries to load a
passphrase-protected key. We'd still need to change the documentation
but at least the behavior would be reasonably clean.
I'm kinda leaning to the last choice; I don't want to leave things as they
are, but actually making password-protected keys work in a useful way
seems like a lot more effort than is justified.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Jan 3, 2017 at 4:02 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
Yeah, it seems that if you want to know whether you are using SSL, then
we already have that. I don't see the need for this new read-onlysetting.
I concur --- there might be use for more reporting about SSL status, but
that patch doesn't seem like quite the right thing.I've pushed the main patch with some small adjustments; one notable one
that I made the EXEC_BACKEND reload path work like SIGHUP reload (press on
without SSL if fail) not server start (abort if fail). As it stood, if
you fat-fingered a change to your SSL files on Windows, the postmaster
would keep running but all backends would die instantly, whether or not
an SSL connection was being requested. That didn't seem helpful.I also went ahead and downgraded the "hostssl record with SSL turned off"
case to a warning.Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
Our documentation suggests that if you have one, the server will demand
the passphrase just once at server start and then all is good. I'm not
sure if that's at all practical in modern usage, but in any case it's
not going to be reasonable to put a passphrase in again at every SIGHUP.
On Windows things are even worse; you'd have to give the passphrase again
to every spawned backend. (But that was true already.)I can think of at least three things we might do about this:
1. Leave the code as it stands, and change the documentation to state
that you cannot use a passphrase-protected server key, period.
2. Add a password callback function that would supply the passphrase
when needed. The question is, where would it get that from? It'd
be straightforward to supply it from a string GUC, but from a security
POV it seems pretty silly to have the password in postgresql.conf.
Yeah, that seems like a really bad idea. If you want to do that, then you
might as well just remove the passphrase from the key.
3. Add a password callback function that just returns an empty string,
thereby ensuring a clean failure if the server tries to load a
passphrase-protected key. We'd still need to change the documentation
but at least the behavior would be reasonably clean.I'm kinda leaning to the last choice; I don't want to leave things as they
are, but actually making password-protected keys work in a useful way
seems like a lot more effort than is justified.
The effort to deal with it may well be justified. IIRC, Greg Stark had some
ideas of what he wanted to do with encryption keys (or maybe that was
either somebody else or some other keys?), which also included getting the
private key out of the general postmaster address space to protect it. But
it *is* a bigger option, and in particular it is well out of scope of this
patch.
Of the three options I agree 3 is probably the best.
Another option would be to use a callback to get the key value the first
time, and then cache it so we can re-use it. That means we can make it work
if the new key has the same password, but it also means we need to take
care of protecting that passphrase. But I'm not sure that's any worse than
the fact that we keep the private key around unlocked today?
That said, they passphrase should only be required if the key changes, not
if the certificate changes, I believe. Do we take advantage of this today
(sorry, haven't looked at the code)? Because the most common operation is
probably the renewal of a certificate, which does not change the key, for
example...
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
On 1/2/17 10:02 PM, Tom Lane wrote:
Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
I don't have experience with this in practice, but my hunch would be
that you can continue to use passphrases as before, but the new reload
functionality is effectively not supported. That is, if you use
passphrases and make a key change, you need to do a full restart.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
On 1/2/17 10:02 PM, Tom Lane wrote:
Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
I don't have experience with this in practice, but my hunch would be
that you can continue to use passphrases as before, but the new reload
functionality is effectively not supported. That is, if you use
passphrases and make a key change, you need to do a full restart.
Um, no, that's not the situation. Since we reload the SSL_CTX each
time we get SIGHUP, and no information is carried forward from the
old SSL_CTX, a passphrase-protected server key causes the postmaster
to freeze at SIGHUP until somebody types a new passphrase on its
/dev/tty. (I've just verified this: it stops accepting connections
or indeed doing anything at all.) In the likely event that /dev/tty
no longer opens successfully, it would report failure of the SSL
reload and stop accepting new SSL connections. Even if you thought
the previous behavior was useful, this completely isn't.
If there were a way to carry forward the decrypted key from the old
SSL_CTX to the new one, we could perhaps preserve the old behavior.
But I don't know of one, and even if there is one, it's likely very
OpenSSL-specific --- do we want to assume that every TLS implementation
will support that?
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Magnus Hagander <magnus@hagander.net> writes:
On Tue, Jan 3, 2017 at 4:02 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
...
2. Add a password callback function that would supply the passphrase
when needed. The question is, where would it get that from? It'd
be straightforward to supply it from a string GUC, but from a security
POV it seems pretty silly to have the password in postgresql.conf.
Yeah, that seems like a really bad idea. If you want to do that, then you
might as well just remove the passphrase from the key.
Agreed. It's difficult to imagine a situation in which postgresql.conf
could be considered more secure than server.key, and usually it'd be less
so.
3. Add a password callback function that just returns an empty string,
thereby ensuring a clean failure if the server tries to load a
passphrase-protected key. We'd still need to change the documentation
but at least the behavior would be reasonably clean.
Another option would be to use a callback to get the key value the first
time, and then cache it so we can re-use it. That means we can make it work
if the new key has the same password, but it also means we need to take
care of protecting that passphrase. But I'm not sure that's any worse than
the fact that we keep the private key around unlocked today?
Yeah, I'm not terribly fussed about having the passphrase sitting in
postmaster memory. But the above is work I don't care to do ATM.
I think probably the right thing for now is to install a do-nothing
callback, so that at least we don't have the issue of the postmaster
freezing at SIGHUP. If someone feels like trying to revive support
of passphrase-protected server keys, that would be a perfectly fine
base to build on; they'd need a callback there anyway.
That said, they passphrase should only be required if the key changes, not
if the certificate changes, I believe. Do we take advantage of this today
(sorry, haven't looked at the code)? Because the most common operation is
probably the renewal of a certificate, which does not change the key, for
example...
As I just explained to Peter, we don't have any way to exploit that given
the design of loading a whole new SSL_CTX.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Jan 3, 2017 at 4:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Magnus Hagander <magnus@hagander.net> writes:
On Tue, Jan 3, 2017 at 4:02 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
...
2. Add a password callback function that would supply the passphrase
when needed. The question is, where would it get that from? It'd
be straightforward to supply it from a string GUC, but from a security
POV it seems pretty silly to have the password in postgresql.conf.Yeah, that seems like a really bad idea. If you want to do that, then you
might as well just remove the passphrase from the key.Agreed. It's difficult to imagine a situation in which postgresql.conf
could be considered more secure than server.key, and usually it'd be less
so.3. Add a password callback function that just returns an empty string,
thereby ensuring a clean failure if the server tries to load a
passphrase-protected key. We'd still need to change the documentation
but at least the behavior would be reasonably clean.Another option would be to use a callback to get the key value the first
time, and then cache it so we can re-use it. That means we can make itwork
if the new key has the same password, but it also means we need to take
care of protecting that passphrase. But I'm not sure that's any worsethan
the fact that we keep the private key around unlocked today?
Yeah, I'm not terribly fussed about having the passphrase sitting in
postmaster memory. But the above is work I don't care to do ATM.I think probably the right thing for now is to install a do-nothing
callback, so that at least we don't have the issue of the postmaster
freezing at SIGHUP. If someone feels like trying to revive support
of passphrase-protected server keys, that would be a perfectly fine
base to build on; they'd need a callback there anyway.
Does it still support passphrase protected ones on startup, or did that get
thrown out with the bathwater? I think that's definitely a separate thing
and there are a nontrivial number of people who would be interested in a
setup where they can use a passphrase to protect it initially, just not
reload it.
That said, they passphrase should only be required if the key changes,
not
if the certificate changes, I believe. Do we take advantage of this today
(sorry, haven't looked at the code)? Because the most common operation is
probably the renewal of a certificate, which does not change the key, for
example...As I just explained to Peter, we don't have any way to exploit that given
the design of loading a whole new SSL_CTX.
Bummer.
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
* Magnus Hagander (magnus@hagander.net) wrote:
On Tue, Jan 3, 2017 at 4:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Magnus Hagander <magnus@hagander.net> writes:
On Tue, Jan 3, 2017 at 4:02 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Before we leave this area, though, there is a loose end that requires
more thought. That is, what about passphrase-protected server keys?
...
2. Add a password callback function that would supply the passphrase
when needed. The question is, where would it get that from? It'd
be straightforward to supply it from a string GUC, but from a security
POV it seems pretty silly to have the password in postgresql.conf.Yeah, that seems like a really bad idea. If you want to do that, then you
might as well just remove the passphrase from the key.Agreed. It's difficult to imagine a situation in which postgresql.conf
could be considered more secure than server.key, and usually it'd be less
so.3. Add a password callback function that just returns an empty string,
thereby ensuring a clean failure if the server tries to load a
passphrase-protected key. We'd still need to change the documentation
but at least the behavior would be reasonably clean.Another option would be to use a callback to get the key value the first
time, and then cache it so we can re-use it. That means we can make itwork
if the new key has the same password, but it also means we need to take
care of protecting that passphrase. But I'm not sure that's any worsethan
the fact that we keep the private key around unlocked today?
Yeah, I'm not terribly fussed about having the passphrase sitting in
postmaster memory. But the above is work I don't care to do ATM.I think probably the right thing for now is to install a do-nothing
callback, so that at least we don't have the issue of the postmaster
freezing at SIGHUP. If someone feels like trying to revive support
of passphrase-protected server keys, that would be a perfectly fine
base to build on; they'd need a callback there anyway.Does it still support passphrase protected ones on startup, or did that get
thrown out with the bathwater? I think that's definitely a separate thing
and there are a nontrivial number of people who would be interested in a
setup where they can use a passphrase to protect it initially, just not
reload it.
+1
Thanks!
Stephen
Magnus Hagander <magnus@hagander.net> writes:
On Tue, Jan 3, 2017 at 4:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I think probably the right thing for now is to install a do-nothing
callback, so that at least we don't have the issue of the postmaster
freezing at SIGHUP. If someone feels like trying to revive support
of passphrase-protected server keys, that would be a perfectly fine
base to build on; they'd need a callback there anyway.
Does it still support passphrase protected ones on startup, or did that get
thrown out with the bathwater?
It does not; what would be the point, if the key would be lost at SIGHUP?
I think that's definitely a separate thing
and there are a nontrivial number of people who would be interested in a
setup where they can use a passphrase to protect it initially, just not
reload it.
If any of those number of people want to step up and design/implement
a non-broken solution for passphrases, that'd be fine with me. But
I would want to see something that's actually a credible solution,
allowing the postmaster to be started as a normal daemon. And working
on Windows.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jan 4, 2017 at 3:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Magnus Hagander <magnus@hagander.net> writes:
On Tue, Jan 3, 2017 at 4:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I think probably the right thing for now is to install a do-nothing
callback, so that at least we don't have the issue of the postmaster
freezing at SIGHUP. If someone feels like trying to revive support
of passphrase-protected server keys, that would be a perfectly fine
base to build on; they'd need a callback there anyway.Does it still support passphrase protected ones on startup, or did that
get
thrown out with the bathwater?
It does not; what would be the point, if the key would be lost at SIGHUP?
If we lost it, yes. But we could keep the old key around if it hasn't
changed, thus behave just like we did in <= 9.6.
I think that's definitely a separate thing
and there are a nontrivial number of people who would be interested in a
setup where they can use a passphrase to protect it initially, just not
reload it.If any of those number of people want to step up and design/implement
a non-broken solution for passphrases, that'd be fine with me. But
I would want to see something that's actually a credible solution,
allowing the postmaster to be started as a normal daemon. And working
on Windows.
Well, for all those people 9.6 worked significantly better... Because they
could reload *other* config parameters without failure.
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
* Magnus Hagander (magnus@hagander.net) wrote:
On Wed, Jan 4, 2017 at 3:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Magnus Hagander <magnus@hagander.net> writes:
On Tue, Jan 3, 2017 at 4:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I think probably the right thing for now is to install a do-nothing
callback, so that at least we don't have the issue of the postmaster
freezing at SIGHUP. If someone feels like trying to revive support
of passphrase-protected server keys, that would be a perfectly fine
base to build on; they'd need a callback there anyway.Does it still support passphrase protected ones on startup, or did that
get
thrown out with the bathwater?
It does not; what would be the point, if the key would be lost at SIGHUP?
If we lost it, yes. But we could keep the old key around if it hasn't
changed, thus behave just like we did in <= 9.6.
Either that, or throw the same warning that we can't reload the SSL
bits if we had to ask for a passphrase at startup, again like how it
worked in 9.6.
I think that's definitely a separate thing
and there are a nontrivial number of people who would be interested in a
setup where they can use a passphrase to protect it initially, just not
reload it.If any of those number of people want to step up and design/implement
a non-broken solution for passphrases, that'd be fine with me. But
I would want to see something that's actually a credible solution,
allowing the postmaster to be started as a normal daemon. And working
on Windows.Well, for all those people 9.6 worked significantly better... Because they
could reload *other* config parameters without failure.
Indeed, this is important functionality that people are using. I don't
agree with simply removing it because we want to make these options able
to be changed on a reload, that's not a good trade-off.
Thanks!
Stephen
On 01/04/2017 03:48 PM, Magnus Hagander wrote:
On Wed, Jan 4, 2017 at 3:47 PM, Tom Lane <tgl@sss.pgh.pa.us
It does not; what would be the point, if the key would be lost at
SIGHUP?If we lost it, yes. But we could keep the old key around if it hasn't
changed, thus behave just like we did in <= 9.6.
That means storing the pass phrase in the memory of the postmaster,
which does not sound like a terribly good idea to me, but I have never
used keys with pass phrases for daemons so it might be a common solution
which is acceptable by many.
If any of those number of people want to step up and design/implement
a non-broken solution for passphrases, that'd be fine with me. But
I would want to see something that's actually a credible solution,
allowing the postmaster to be started as a normal daemon. And working
on Windows.Well, for all those people 9.6 worked significantly better... Because
they could reload *other* config parameters without failure.
A possible solution might be to only add the error throwing hook when
loading certificates during SIGHUP (and at Windows) and to work as
before on startup. Would that be an acceptable solution? I could write a
patch for this if people are interested.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* Andreas Karlsson (andreas@proxel.se) wrote:
On 01/04/2017 03:48 PM, Magnus Hagander wrote:
On Wed, Jan 4, 2017 at 3:47 PM, Tom Lane <tgl@sss.pgh.pa.us
It does not; what would be the point, if the key would be lost at
SIGHUP?If we lost it, yes. But we could keep the old key around if it hasn't
changed, thus behave just like we did in <= 9.6.That means storing the pass phrase in the memory of the postmaster,
which does not sound like a terribly good idea to me, but I have
never used keys with pass phrases for daemons so it might be a
common solution which is acceptable by many.
I'm not sure I see that as a big deal- if you can access the
postmaster's memory then you can just pull out the actual private key
itself, no? There's a bigger issue here though, which has to do with
what happens if the user actually does change the passphrase on the key
and we then reload, what do we do then?
If any of those number of people want to step up and design/implement
a non-broken solution for passphrases, that'd be fine with me. But
I would want to see something that's actually a credible solution,
allowing the postmaster to be started as a normal daemon. And working
on Windows.Well, for all those people 9.6 worked significantly better... Because
they could reload *other* config parameters without failure.A possible solution might be to only add the error throwing hook
when loading certificates during SIGHUP (and at Windows) and to work
as before on startup. Would that be an acceptable solution? I could
write a patch for this if people are interested.
I'm not sure I see how that's a solution..? Wouldn't that mean that a
SIGHUP with an encrypted key would result in a failure?
The solution, at least in my view, seems to be to say "sorry, we can't
reload the SSL stuff if you used a passphrase to unlock the key on
startup, you will have to perform a restart if you want the SSL bits to
be changed."
No, I've not looked at what that would require in terms of code.
Thanks!
Stephen
Stephen Frost <sfrost@snowman.net> writes:
Indeed, this is important functionality that people are using.
Who exactly are these people, and why haven't they complained about how
crappy the support is now? I'm *completely* unconvinced by the argument
that the way it has worked in the past is an important feature that we
have to preserve. It's an accident that it worked at all, and as far as
I can tell it didn't work very well. Have you tried it? On my machine,
it could not accept a passphrase at all unless I didn't detach the
postmaster from the terminal, which is entirely silly as a production
solution.
In short, I reject the above argument 100%. I'm all for inventing
a solution in which passphrases work usefully, but don't tell me
that what we had was such a solution.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 01/04/2017 04:14 PM, Stephen Frost wrote:
* Andreas Karlsson (andreas@proxel.se) wrote:
A possible solution might be to only add the error throwing hook
when loading certificates during SIGHUP (and at Windows) and to work
as before on startup. Would that be an acceptable solution? I could
write a patch for this if people are interested.I'm not sure I see how that's a solution..? Wouldn't that mean that a
SIGHUP with an encrypted key would result in a failure?The solution, at least in my view, seems to be to say "sorry, we can't
reload the SSL stuff if you used a passphrase to unlock the key on
startup, you will have to perform a restart if you want the SSL bits to
be changed."
Sorry, I was very unclear. I meant refusing the reload the SSL context
if there is a pass phrase, but that the rest of the config will be
reloaded just fine. This will lead to some log spam on every SIGHUP for
people with a pass phrase but should otherwise work as before.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* Andreas Karlsson (andreas@proxel.se) wrote:
On 01/04/2017 04:14 PM, Stephen Frost wrote:
* Andreas Karlsson (andreas@proxel.se) wrote:
A possible solution might be to only add the error throwing hook
when loading certificates during SIGHUP (and at Windows) and to work
as before on startup. Would that be an acceptable solution? I could
write a patch for this if people are interested.I'm not sure I see how that's a solution..? Wouldn't that mean that a
SIGHUP with an encrypted key would result in a failure?The solution, at least in my view, seems to be to say "sorry, we can't
reload the SSL stuff if you used a passphrase to unlock the key on
startup, you will have to perform a restart if you want the SSL bits to
be changed."Sorry, I was very unclear. I meant refusing the reload the SSL
context if there is a pass phrase, but that the rest of the config
will be reloaded just fine. This will lead to some log spam on every
SIGHUP for people with a pass phrase but should otherwise work as
before.
Right, that sounds like it'd work for me, at least.
Thanks!
Stephen
Andreas Karlsson <andreas@proxel.se> writes:
Sorry, I was very unclear. I meant refusing the reload the SSL context
if there is a pass phrase, but that the rest of the config will be
reloaded just fine. This will lead to some log spam on every SIGHUP for
people with a pass phrase but should otherwise work as before.
How will you know whether there's a pass phrase?
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 1/4/17 10:26 AM, Tom Lane wrote:
Andreas Karlsson <andreas@proxel.se> writes:
Sorry, I was very unclear. I meant refusing the reload the SSL context
if there is a pass phrase, but that the rest of the config will be
reloaded just fine. This will lead to some log spam on every SIGHUP for
people with a pass phrase but should otherwise work as before.How will you know whether there's a pass phrase?
One could register a password callback that remembers whether it was called.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
On 1/4/17 10:26 AM, Tom Lane wrote:
How will you know whether there's a pass phrase?
One could register a password callback that remembers whether it was called.
Hmm ... actually, we don't even need to work that hard. If we simply
use the callback that's there now, but only during reloads not server
start, then we get the desired behavior. Reloads will fail because
the wrong passphrase was returned by the callback, and we'll keep the
current SSL state. It would probably be worth tweaking things to minimize
the amount of log spam that you get from that; but it would work, for
values of "work" similar to what was there before.
I still maintain that the existing solution for passphrases is useless,
but in the interest of removing objections to the current patch, I'll
go make that happen.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 1/4/17 10:57 AM, Tom Lane wrote:
I still maintain that the existing solution for passphrases is useless,
but in the interest of removing objections to the current patch, I'll
go make that happen.
Sounds good.
Looking around briefly (e.g., Apache, nginx), the standard approach
appears to be a configuration setting that gets the password from an
external program or file. (Although the default still appears to be to
get from tty.)
systemd has support for getting passwords to services without tty.
So if someone is interested, there is some room for enhancement here.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* Peter Eisentraut (peter.eisentraut@2ndquadrant.com) wrote:
On 1/4/17 10:57 AM, Tom Lane wrote:
I still maintain that the existing solution for passphrases is useless,
but in the interest of removing objections to the current patch, I'll
go make that happen.Sounds good.
Agreed, thanks.
Looking around briefly (e.g., Apache, nginx), the standard approach
appears to be a configuration setting that gets the password from an
external program or file. (Although the default still appears to be to
get from tty.)
Right, the MIT Kerberos daemon will definitely prompt for the passphrase
for the master key on the terminal also. They might also have a way to
get it from a program now, not sure, it's been a while, but it was a
requirement from NIST 800-53 to not have unencrypted keys on the
filesystem and I had to address that for the MIT Kerberos master key and
the private keys for various SSL-using services.
systemd has support for getting passwords to services without tty.
Oh, that's interesting, I wasn't aware of that.
So if someone is interested, there is some room for enhancement here.
Agreed.
Thanks!
Stephen
On Wed, Jan 4, 2017 at 11:49 AM, Stephen Frost <sfrost@snowman.net> wrote:
systemd has support for getting passwords to services without tty.
Oh, that's interesting, I wasn't aware of that.
So if someone is interested, there is some room for enhancement here.
Agreed.
The first thing that pops into my head is that we could add a GUC
ssl_cert_passphrase_command whose contents get executed as a shell
command when we need a passphrase; that program is expected to emit
the passphrase and nothing else on standard output and then exit(0).
Blah blah logging blah blah failure handling. That's not trivial to
implement if you want the postmaster to still be responsive while the
command is running, but I think it could be done. (I'm not
volunteering.)
Of course, if there's some sort of commonly-used library out there for
this sort of thing where we can just link against it and call whatever
APIs it exposes, that might be a better alternative, or something to
support in addition, but I don't really know whether there's any
standardization in this area.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
Of course, if there's some sort of commonly-used library out there for
this sort of thing where we can just link against it and call whatever
APIs it exposes, that might be a better alternative, or something to
support in addition, but I don't really know whether there's any
standardization in this area.
I was wondering if we could make use of ssh-agent. But it seems to want
to hold the private key itself, so that you have to communicate with it
every time you need an operation done with the key. I'm not sure what the
performance of that is like, and I am sure that the code would look a
whole lot different from the path where we hold the key locally. It might
be workable if OpenSSL already incorporates library routines for talking
to ssh-agent, but I haven't looked.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers