]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
ssl: Add tests for client CA
authorDaniel Gustafsson <dgustafsson@postgresql.org>
Wed, 18 Mar 2026 11:36:53 +0000 (12:36 +0100)
committerDaniel Gustafsson <dgustafsson@postgresql.org>
Wed, 18 Mar 2026 11:36:53 +0000 (12:36 +0100)
These tests were originally written to test the SSL SNI patchset
but they have merit on their own since we lack coverage for these
scenarios in the non SNI case as well.

Author: Jacob Champion <jacob.champion@enterprisedb.com>
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/1C81CD0D-407E-44F9-833A-DD0331C202E5@yesql.se

src/test/ssl/t/001_ssltests.pl
src/test/ssl/t/SSL/Backend/OpenSSL.pm

index 2b9b3dfd663c9d43983aeee4e31ee0a897988279..963bfea8ed5b33a04a7f27eb25073a489e7152e1 100644 (file)
@@ -1004,4 +1004,60 @@ $node->connect_fails(
                qr{Failed certificate data \(unverified\): subject "/CN=\\xce\\x9f\\xce\\xb4\\xcf\\x85\\xcf\\x83\\xcf\\x83\\xce\\xad\\xce\\xb1\\xcf\\x82", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"},
        ]);
 
+SKIP:
+{
+       skip "sslmode require not supported in this build", 4
+         unless ($supports_sslcertmode_require);
+
+       # Test client CAs
+       my $connstr =
+         "user=ssltestuser dbname=certdb hostaddr=$SERVERHOSTADDR sslmode=require sslsni=1";
+
+       switch_server_cert($node, certfile => 'server-cn-only', cafile => '');
+       # example.org is unconfigured and should fail.
+       $node->connect_fails(
+               "$connstr host=example.org sslcertmode=require sslcert=ssl/client.crt"
+                 . sslkey('client.key'),
+               "host: 'example.org', 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 uses the client CA.
+       switch_server_cert(
+               $node,
+               certfile => 'server-cn-only',
+               cafile => 'root+client_ca');
+       # example.com is configured and should require a valid client cert.
+       $node->connect_fails(
+               "$connstr host=example.com sslcertmode=disable",
+               "host: 'example.com', ca: 'root+client_ca.crt': 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'),
+               "host: 'example.com', ca: 'root+client_ca.crt': connect with sslcert, client certificate sent"
+       );
+
+       # example.net uses the server CA (which is wrong).
+       switch_server_cert(
+               $node,
+               certfile => 'server-cn-only',
+               cafile => 'root+server_ca');
+       # example.net is configured and should require a client cert, but will
+       # always fail verification.
+       $node->connect_fails(
+               "$connstr host=example.net sslcertmode=disable",
+               "host: 'example.net', ca: 'root+server_ca.crt': 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'),
+               "host: 'example.net', ca: 'root+server_ca.crt': connect with sslcert, client certificate sent",
+               expected_stderr => qr/unknown ca/);
+}
+
 done_testing();
index 7ea05572a8da9fb50e4e07487d88d50cdfac4340..6060771c1a85907c2fddd4523f7cb12c56cfb503 100644 (file)
@@ -72,6 +72,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")
@@ -146,7 +147,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>
 
@@ -181,10 +183,18 @@ 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";
+       if ($params->{cafile} ne "")
+       {
+               $sslconf .= "ssl_ca_file='$params->{cafile}.crt'\n";
+       }
+       else
+       {
+               $sslconf .= "ssl_ca_file=''\n";
+       }
+
        $sslconf .= "ssl_crl_dir='$params->{crldir}'\n"
          if defined $params->{crldir};