diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 666900f5d0f..6e9f7f466df 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2649,6 +2649,16 @@ openssl x509 -req -in server.csr -text -days 365 \
off.
+
+ On Windows, all connections will
+ perform a configuration reload due to the different process model of that
+ platform, so SSL_passphrase_cmd_reload must be
+ set to on for every entry which has an
+ SSL_passphrase_cmd. If an entry fails to load
+ at backend start, SSL connections for the hosts of that entry will fail,
+ while the other entries remain usable.
+
+
hostname can be either the literal
hostname for the connection, /no_sni/, or *.
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 11f5a483b36..fbd4f1173bc 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -167,6 +167,7 @@ be_tls_init(bool isServerStart)
int ssl_ver_max = -1;
host_cache_hash *host_cache = NULL;
bool hasWarned = false;
+ int nloaded = 0;
/*
* Since we don't know which host we're using until the ClientHello is
@@ -331,10 +332,20 @@ be_tls_init(bool isServerStart)
* Create the SSL context only once the entry has been accepted
* and added to new_hosts, as the cleanup callback can only free
* contexts of entries it can reach from there.
+ *
+ * An EXEC_BACKEND child has no previous configuration to fall
+ * back on, so rather than leaving the backend without SSL for all
+ * hosts we keep a failed entry without a context. Connections
+ * selecting it will fail the handshake.
*/
- if (!init_host_context(host, isServerStart, &hasWarned))
+ if (init_host_context(host, isServerStart, &hasWarned))
+ nloaded++;
+ else if (!IsUnderPostmaster)
goto error;
}
+
+ if (nloaded == 0)
+ goto error;
}
/*
@@ -2098,6 +2109,17 @@ sni_clienthello_cb(SSL *ssl, int *al, void *arg)
}
found:
+ if (install_config->ssl_ctx == NULL)
+ {
+ *al = SSL_AD_INTERNAL_ERROR;
+ ereport(COMMERROR,
+ errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("SSL configuration for host could not be loaded, terminating connection"),
+ errcontext("line %d of configuration file \"%s\"",
+ install_config->linenumber, install_config->sourcefile));
+ return SSL_CLIENT_HELLO_ERROR;
+ }
+
if (!ssl_update_ssl(ssl, install_config))
{
*al = SSL_AD_INTERNAL_ERROR;
diff --git a/src/test/ssl/t/004_sni.pl b/src/test/ssl/t/004_sni.pl
index 635e637bdd3..d02bc2d6ad2 100644
--- a/src/test/ssl/t/004_sni.pl
+++ b/src/test/ssl/t/004_sni.pl
@@ -504,6 +504,38 @@ SKIP:
'log reload failure due to passphrase command reloading');
}
+# A host whose key cannot be loaded in an EXEC_BACKEND child must only affect
+# connections to that host.
+ok(unlink($node->data_dir . '/pg_hosts.conf'));
+$node->append_conf('pg_hosts.conf',
+ 'localhost server-cn-only.crt server-cn-only.key');
+$node->append_conf('pg_hosts.conf',
+ 'example.org server-cn-only.crt server-password.key root+client_ca.crt "echo secret1" off'
+);
+$node->restart;
+$node_loglocation = -s $node->logfile;
+
+$node->connect_ok(
+ "$connstr sslrootcert=ssl/root+server_ca.crt sslmode=require host=localhost",
+ "pg_hosts.conf: connect to host next to a passphrase host without reload"
+);
+
+if ($windows_os || $exec_backend =~ /on/)
+{
+ $node->connect_fails(
+ "$connstr sslrootcert=ssl/root+server_ca.crt sslmode=require host=example.org",
+ "pg_hosts.conf: connect to passphrase host without reload fails",
+ expected_stderr => qr/internal error/);
+ $node->wait_for_log(qr/SSL configuration for host could not be loaded/,
+ $node_loglocation);
+}
+else
+{
+ $node->connect_ok(
+ "$connstr sslrootcert=ssl/root+server_ca.crt sslmode=require host=example.org",
+ "pg_hosts.conf: connect to passphrase host without reload");
+}
+
# Configure with only non-SNI connections allowed
ok(unlink($node->data_dir . '/pg_hosts.conf'));
$node->append_conf('pg_hosts.conf',