commit a4d9cbf4d1228dcc17f2961b7811321a50e74617 Author: Jacob Champion 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 The CA certificate file to use for the C GUC. If omitted it will -default to 'root+client_ca.crt'. +default to 'root+client_ca.crt'. If empty, no C configuration +parameter will be set. =item certfile => B @@ -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};