Check error messages in SSL tests

In tests that check whether a connection fails, also check the error
message.  That makes sure that the connection was rejected for the right
reason.

This discovered that two tests had their connection failing for the
wrong reason.  One test failed because pg_hba.conf was not set up to
allow that user, one test failed because the client key file did not
have the right permissions.  Fix those tests and add a new one that is
really supposed to check the file permission issue.

Reviewed-by: Michael Paquier <michael@paquier.xyz>
This commit is contained in:
Peter Eisentraut 2018-02-23 13:54:45 -05:00
parent bc1adc651b
commit 081bfc19b3
4 changed files with 59 additions and 32 deletions

View File

@ -27,7 +27,6 @@ use Test::More;
use Exporter 'import'; use Exporter 'import';
our @EXPORT = qw( our @EXPORT = qw(
configure_test_server_for_ssl configure_test_server_for_ssl
run_test_psql
switch_server_cert switch_server_cert
test_connect_fails test_connect_fails
test_connect_ok test_connect_ok
@ -35,37 +34,28 @@ our @EXPORT = qw(
# Define a couple of helper functions to test connecting to the server. # Define a couple of helper functions to test connecting to the server.
# Attempt connection to server with given connection string.
sub run_test_psql
{
my $connstr = $_[0];
my $cmd = [
'psql', '-X', '-A', '-t', '-c', "SELECT \$\$connected with $connstr\$\$",
'-d', "$connstr" ];
my $result = run_log($cmd);
return $result;
}
# The first argument is a base connection string to use for connection. # The first argument is a base connection string to use for connection.
# The second argument is a complementary connection string. # The second argument is a complementary connection string.
sub test_connect_ok sub test_connect_ok
{ {
my $common_connstr = $_[0]; my ($common_connstr, $connstr, $test_name) = @_;
my $connstr = $_[1];
my $test_name = $_[2];
ok(run_test_psql("$common_connstr $connstr"), $test_name); my $cmd = [
'psql', '-X', '-A', '-t', '-c', "SELECT \$\$connected with $connstr\$\$",
'-d', "$common_connstr $connstr" ];
command_ok($cmd, $test_name);
} }
sub test_connect_fails sub test_connect_fails
{ {
my $common_connstr = $_[0]; my ($common_connstr, $connstr, $expected_stderr, $test_name) = @_;
my $connstr = $_[1];
my $test_name = $_[2];
ok(!run_test_psql("$common_connstr $connstr"), $test_name); my $cmd = [
'psql', '-X', '-A', '-t', '-c', "SELECT \$\$connected with $connstr\$\$",
'-d', "$common_connstr $connstr" ];
command_fails_like($cmd, $expected_stderr, $test_name);
} }
# Copy a set of files, taking into account wildcards # Copy a set of files, taking into account wildcards
@ -169,12 +159,12 @@ sub configure_hba_for_ssl
print $hba print $hba
"# TYPE DATABASE USER ADDRESS METHOD\n"; "# TYPE DATABASE USER ADDRESS METHOD\n";
print $hba print $hba
"hostssl trustdb ssltestuser $serverhost/32 $authmethod\n"; "hostssl trustdb all $serverhost/32 $authmethod\n";
print $hba print $hba
"hostssl trustdb ssltestuser ::1/128 $authmethod\n"; "hostssl trustdb all ::1/128 $authmethod\n";
print $hba print $hba
"hostssl certdb ssltestuser $serverhost/32 cert\n"; "hostssl certdb all $serverhost/32 cert\n";
print $hba print $hba
"hostssl certdb ssltestuser ::1/128 cert\n"; "hostssl certdb all ::1/128 cert\n";
close $hba; close $hba;
} }

View File

@ -1,3 +1,3 @@
/*.old /*.old
/new_certs_dir/ /new_certs_dir/
/client_tmp.key /client*_tmp.key

View File

@ -2,7 +2,7 @@ use strict;
use warnings; use warnings;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 40; use Test::More tests => 62;
use ServerSetup; use ServerSetup;
use File::Copy; use File::Copy;
@ -20,6 +20,14 @@ my $common_connstr;
# of the key stored in the code tree and update its permissions. # of the key stored in the code tree and update its permissions.
copy("ssl/client.key", "ssl/client_tmp.key"); copy("ssl/client.key", "ssl/client_tmp.key");
chmod 0600, "ssl/client_tmp.key"; chmod 0600, "ssl/client_tmp.key";
copy("ssl/client-revoked.key", "ssl/client-revoked_tmp.key");
chmod 0600, "ssl/client-revoked_tmp.key";
# Also make a copy of that explicitly world-readable. We can't
# necessarily rely on the file in the source tree having those
# permissions.
copy("ssl/client.key", "ssl/client_wrongperms_tmp.key");
chmod 0644, "ssl/client_wrongperms_tmp.key";
#### Part 0. Set up the server. #### Part 0. Set up the server.
@ -48,6 +56,7 @@ $common_connstr =
# The server should not accept non-SSL connections. # The server should not accept non-SSL connections.
test_connect_fails($common_connstr, "sslmode=disable", test_connect_fails($common_connstr, "sslmode=disable",
qr/\Qno pg_hba.conf entry\E/,
"server doesn't accept non-SSL connections"); "server doesn't accept non-SSL connections");
# Try without a root cert. In sslmode=require, this should work. In verify-ca # Try without a root cert. In sslmode=require, this should work. In verify-ca
@ -55,26 +64,32 @@ test_connect_fails($common_connstr, "sslmode=disable",
test_connect_ok($common_connstr, "sslrootcert=invalid sslmode=require", test_connect_ok($common_connstr, "sslrootcert=invalid sslmode=require",
"connect without server root cert sslmode=require"); "connect without server root cert sslmode=require");
test_connect_fails($common_connstr, "sslrootcert=invalid sslmode=verify-ca", test_connect_fails($common_connstr, "sslrootcert=invalid sslmode=verify-ca",
qr/root certificate file "invalid" does not exist/,
"connect without server root cert sslmode=verify-ca"); "connect without server root cert sslmode=verify-ca");
test_connect_fails($common_connstr, "sslrootcert=invalid sslmode=verify-full", test_connect_fails($common_connstr, "sslrootcert=invalid sslmode=verify-full",
qr/root certificate file "invalid" does not exist/,
"connect without server root cert sslmode=verify-full"); "connect without server root cert sslmode=verify-full");
# Try with wrong root cert, should fail. (We're using the client CA as the # Try with wrong root cert, should fail. (We're using the client CA as the
# root, but the server's key is signed by the server CA.) # root, but the server's key is signed by the server CA.)
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslrootcert=ssl/client_ca.crt sslmode=require", "sslrootcert=ssl/client_ca.crt sslmode=require",
qr/SSL error/,
"connect with wrong server root cert sslmode=require"); "connect with wrong server root cert sslmode=require");
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslrootcert=ssl/client_ca.crt sslmode=verify-ca", "sslrootcert=ssl/client_ca.crt sslmode=verify-ca",
qr/SSL error/,
"connect with wrong server root cert sslmode=verify-ca"); "connect with wrong server root cert sslmode=verify-ca");
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslrootcert=ssl/client_ca.crt sslmode=verify-full", "sslrootcert=ssl/client_ca.crt sslmode=verify-full",
qr/SSL error/,
"connect with wrong server root cert sslmode=verify-full"); "connect with wrong server root cert sslmode=verify-full");
# Try with just the server CA's cert. This fails because the root file # Try with just the server CA's cert. This fails because the root file
# must contain the whole chain up to the root CA. # must contain the whole chain up to the root CA.
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslrootcert=ssl/server_ca.crt sslmode=verify-ca", "sslrootcert=ssl/server_ca.crt sslmode=verify-ca",
qr/SSL error/,
"connect with server CA cert, without root CA"); "connect with server CA cert, without root CA");
# And finally, with the correct root cert. # And finally, with the correct root cert.
@ -107,6 +122,7 @@ test_connect_ok($common_connstr,
# A CRL belonging to a different CA is not accepted, fails # A CRL belonging to a different CA is not accepted, fails
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl", "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
qr/SSL error/,
"CRL belonging to a different CA"); "CRL belonging to a different CA");
# With the correct CRL, succeeds (this cert is not revoked) # With the correct CRL, succeeds (this cert is not revoked)
@ -124,9 +140,9 @@ test_connect_ok($common_connstr, "sslmode=require host=wronghost.test",
test_connect_ok($common_connstr, "sslmode=verify-ca host=wronghost.test", test_connect_ok($common_connstr, "sslmode=verify-ca host=wronghost.test",
"mismatch between host name and server certificate sslmode=verify-ca"); "mismatch between host name and server certificate sslmode=verify-ca");
test_connect_fails($common_connstr, "sslmode=verify-full host=wronghost.test", test_connect_fails($common_connstr, "sslmode=verify-full host=wronghost.test",
qr/\Qserver certificate for "common-name.pg-ssltest.test" does not match host name "wronghost.test"\E/,
"mismatch between host name and server certificate sslmode=verify-full"); "mismatch between host name and server certificate sslmode=verify-full");
# Test Subject Alternative Names. # Test Subject Alternative Names.
switch_server_cert($node, 'server-multiple-alt-names'); switch_server_cert($node, 'server-multiple-alt-names');
@ -141,9 +157,11 @@ test_connect_ok($common_connstr, "host=foo.wildcard.pg-ssltest.test",
"host name matching with X.509 Subject Alternative Names wildcard"); "host name matching with X.509 Subject Alternative Names wildcard");
test_connect_fails($common_connstr, "host=wronghost.alt-name.pg-ssltest.test", test_connect_fails($common_connstr, "host=wronghost.alt-name.pg-ssltest.test",
qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "wronghost.alt-name.pg-ssltest.test"\E/,
"host name not matching with X.509 Subject Alternative Names"); "host name not matching with X.509 Subject Alternative Names");
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"host=deep.subdomain.wildcard.pg-ssltest.test", "host=deep.subdomain.wildcard.pg-ssltest.test",
qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/,
"host name not matching with X.509 Subject Alternative Names wildcard"); "host name not matching with X.509 Subject Alternative Names wildcard");
# Test certificate with a single Subject Alternative Name. (this gives a # Test certificate with a single Subject Alternative Name. (this gives a
@ -157,9 +175,11 @@ test_connect_ok($common_connstr, "host=single.alt-name.pg-ssltest.test",
"host name matching with a single X.509 Subject Alternative Name"); "host name matching with a single X.509 Subject Alternative Name");
test_connect_fails($common_connstr, "host=wronghost.alt-name.pg-ssltest.test", test_connect_fails($common_connstr, "host=wronghost.alt-name.pg-ssltest.test",
qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "wronghost.alt-name.pg-ssltest.test"\E/,
"host name not matching with a single X.509 Subject Alternative Name"); "host name not matching with a single X.509 Subject Alternative Name");
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"host=deep.subdomain.wildcard.pg-ssltest.test", "host=deep.subdomain.wildcard.pg-ssltest.test",
qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/,
"host name not matching with a single X.509 Subject Alternative Name wildcard"); "host name not matching with a single X.509 Subject Alternative Name wildcard");
# Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN # Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN
@ -174,6 +194,7 @@ test_connect_ok($common_connstr, "host=dns1.alt-name.pg-ssltest.test",
test_connect_ok($common_connstr, "host=dns2.alt-name.pg-ssltest.test", test_connect_ok($common_connstr, "host=dns2.alt-name.pg-ssltest.test",
"certificate with both a CN and SANs 2"); "certificate with both a CN and SANs 2");
test_connect_fails($common_connstr, "host=common-name.pg-ssltest.test", test_connect_fails($common_connstr, "host=common-name.pg-ssltest.test",
qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 1 other name) does not match host name "common-name.pg-ssltest.test"\E/,
"certificate with both a CN and SANs ignores CN"); "certificate with both a CN and SANs ignores CN");
# Finally, test a server certificate that has no CN or SANs. Of course, that's # Finally, test a server certificate that has no CN or SANs. Of course, that's
@ -187,6 +208,7 @@ test_connect_ok($common_connstr,
"server certificate without CN or SANs sslmode=verify-ca"); "server certificate without CN or SANs sslmode=verify-ca");
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslmode=verify-full host=common-name.pg-ssltest.test", "sslmode=verify-full host=common-name.pg-ssltest.test",
qr/could not get server's host name from server certificate/,
"server certificate without CN or SANs sslmode=verify-full"); "server certificate without CN or SANs sslmode=verify-full");
# Test that the CRL works # Test that the CRL works
@ -201,6 +223,7 @@ test_connect_ok($common_connstr,
"connects without client-side CRL"); "connects without client-side CRL");
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl", "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
qr/SSL error/,
"does not connect with client-side CRL"); "does not connect with client-side CRL");
### Part 2. Server-side tests. ### Part 2. Server-side tests.
@ -215,6 +238,7 @@ $common_connstr =
# no client cert # no client cert
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"user=ssltestuser sslcert=invalid", "user=ssltestuser sslcert=invalid",
qr/connection requires a valid client certificate/,
"certificate authorization fails without client cert"); "certificate authorization fails without client cert");
# correct client cert # correct client cert
@ -222,14 +246,22 @@ test_connect_ok($common_connstr,
"user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key", "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key",
"certificate authorization succeeds with correct client cert"); "certificate authorization succeeds with correct client cert");
# client key with wrong permissions
test_connect_fails($common_connstr,
"user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_wrongperms_tmp.key",
qr!\Qprivate key file "ssl/client_wrongperms_tmp.key" has group or world access\E!,
"certificate authorization fails because of file permissions");
# client cert belonging to another user # client cert belonging to another user
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key", "user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key",
qr/certificate authentication failed for user "anotheruser"/,
"certificate authorization fails with client cert belonging to another user"); "certificate authorization fails with client cert belonging to another user");
# revoked client cert # revoked client cert
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked.key", "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked_tmp.key",
qr/SSL error/,
"certificate authorization fails with revoked client cert"); "certificate authorization fails with revoked client cert");
# intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file # intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file
@ -241,7 +273,10 @@ test_connect_ok($common_connstr,
"sslmode=require sslcert=ssl/client+client_ca.crt", "sslmode=require sslcert=ssl/client+client_ca.crt",
"intermediate client certificate is provided by client"); "intermediate client certificate is provided by client");
test_connect_fails($common_connstr, "sslmode=require sslcert=ssl/client.crt", test_connect_fails($common_connstr, "sslmode=require sslcert=ssl/client.crt",
qr/SSL error/,
"intermediate client certificate is missing"); "intermediate client certificate is missing");
# clean up # clean up
unlink "ssl/client_tmp.key"; unlink("ssl/client_tmp.key",
"ssl/client_wrongperms_tmp.key",
"ssl/client-revoked_tmp.key");

View File

@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 5; use Test::More tests => 6;
use ServerSetup; use ServerSetup;
use File::Copy; use File::Copy;
@ -59,8 +59,10 @@ else
{ {
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"scram_channel_binding=tls-server-end-point", "scram_channel_binding=tls-server-end-point",
qr/unsupported SCRAM channel-binding type/,
"SCRAM authentication with tls-server-end-point as channel binding"); "SCRAM authentication with tls-server-end-point as channel binding");
} }
test_connect_fails($common_connstr, test_connect_fails($common_connstr,
"scram_channel_binding=not-exists", "scram_channel_binding=not-exists",
qr/unsupported SCRAM channel-binding type/,
"SCRAM authentication with invalid channel binding"); "SCRAM authentication with invalid channel binding");