tests.patch.txt

text/plain

Filename: tests.patch.txt
Type: text/plain
Part: 0
Message: Re: Serverside SNI support in libpq
commit a4d9cbf4d1228dcc17f2961b7811321a50e74617
Author: Jacob Champion <jacob.champion@enterprisedb.com>
Date:   Tue Mar 4 13:17:12 2025 -0800

    Tests

diff --git a/src/test/ssl/t/004_sni.pl b/src/test/ssl/t/004_sni.pl
index f0ce048273a..72e64c6c00d 100644
--- a/src/test/ssl/t/004_sni.pl
+++ b/src/test/ssl/t/004_sni.pl
@@ -33,6 +33,11 @@ if (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\bssl\b/)
 
 my $ssl_server = SSL::Server->new();
 
+sub sslkey
+{
+	return $ssl_server->sslkey(@_);
+}
+
 my $node = PostgreSQL::Test::Cluster->new('primary');
 $node->init;
 
@@ -161,4 +166,57 @@ $node->connect_fails(
 	"connect fails since the passphrase protected key cannot be reloaded",
 	expected_stderr => qr/tlsv1 unrecognized name/);
 
+# Test client CAs.
+
+$connstr =
+  "user=ssltestuser dbname=certdb hostaddr=$SERVERHOSTADDR sslmode=require sslsni=1";
+
+ok(unlink($node->data_dir . '/pg_hosts.conf'));
+# example.org has an unconfigured CA.
+$node->append_conf('pg_hosts.conf', 'example.org server-cn-only.crt server-cn-only.key ""');
+# example.com uses the client CA.
+$node->append_conf('pg_hosts.conf', 'example.com server-cn-only.crt server-cn-only.key root+client_ca.crt');
+# example.net uses the server CA (which is wrong).
+$node->append_conf('pg_hosts.conf', 'example.net server-cn-only.crt server-cn-only.key root+server_ca.crt');
+$node->reload;
+
+my @cases = ( "", "root+client_ca", "root+server_ca" );
+foreach my $default_ca (@cases)
+{
+	# The default CA should, ideally, not matter for the purposes of these
+	# tests, since we connect to the other hosts explicitly.
+	$ssl_server->switch_server_cert(
+		$node,
+		certfile => 'server-cn-only',
+		cafile => $default_ca);
+
+	# example.org is unconfigured and should fail.
+	$node->connect_fails(
+		"$connstr host=example.org sslcertmode=require sslcert=ssl/client.crt " . sslkey('client.key'),
+		"example.org, $default_ca: connect with sslcert, no client CA configured",
+		expected_stderr => qr/client certificates can only be checked if a root certificate store is available/);
+
+	# example.com is configured and should require a valid client cert.
+	$node->connect_fails(
+		"$connstr host=example.com sslcertmode=disable",
+		"example.com, $default_ca: connect fails if no client certificate sent",
+		expected_stderr => qr/connection requires a valid client certificate/);
+
+	$node->connect_ok(
+		"$connstr host=example.com sslcertmode=require sslcert=ssl/client.crt " . sslkey('client.key'),
+		"example.com, $default_ca: connect with sslcert, client certificate sent");
+
+	# example.net is configured and should require a client cert, but will
+	# always fail verification.
+	$node->connect_fails(
+		"$connstr host=example.net sslcertmode=disable",
+		"example.net, $default_ca: connect fails if no client certificate sent",
+		expected_stderr => qr/connection requires a valid client certificate/);
+
+	$node->connect_fails(
+		"$connstr host=example.net sslcertmode=require sslcert=ssl/client.crt " . sslkey('client.key'),
+		"example.net, $default_ca: connect with sslcert, client certificate sent",
+		expected_stderr => qr/unknown ca/);
+}
+
 done_testing();
diff --git a/src/test/ssl/t/SSL/Backend/OpenSSL.pm b/src/test/ssl/t/SSL/Backend/OpenSSL.pm
index e044318531f..bdcce84003e 100644
--- a/src/test/ssl/t/SSL/Backend/OpenSSL.pm
+++ b/src/test/ssl/t/SSL/Backend/OpenSSL.pm
@@ -71,6 +71,7 @@ sub init
 	chmod(0600, glob "$pgdata/server-*.key")
 	  or die "failed to change permissions on server keys: $!";
 	_copy_files("ssl/root+client_ca.crt", $pgdata);
+	_copy_files("ssl/root+server_ca.crt", $pgdata);
 	_copy_files("ssl/root_ca.crt", $pgdata);
 	_copy_files("ssl/root+client.crl", $pgdata);
 	mkdir("$pgdata/root+client-crldir")
@@ -145,7 +146,8 @@ following parameters are supported:
 =item cafile => B<value>
 
 The CA certificate file to use for the C<ssl_ca_file> GUC. If omitted it will
-default to 'root+client_ca.crt'.
+default to 'root+client_ca.crt'. If empty, no C<ssl_ca_file> configuration
+parameter will be set.
 
 =item certfile => B<value>
 
@@ -180,10 +182,11 @@ sub set_server_cert
 	  unless defined $params->{keyfile};
 
 	my $sslconf =
-		"ssl_ca_file='$params->{cafile}.crt'\n"
-	  . "ssl_cert_file='$params->{certfile}.crt'\n"
+		"ssl_cert_file='$params->{certfile}.crt'\n"
 	  . "ssl_key_file='$params->{keyfile}.key'\n"
 	  . "ssl_crl_file='$params->{crlfile}'\n";
+	$sslconf .= "ssl_ca_file='$params->{cafile}.crt'\n"
+	  if $params->{cafile} ne "";
 	$sslconf .= "ssl_crl_dir='$params->{crldir}'\n"
 	  if defined $params->{crldir};