@ -2,7 +2,7 @@ use strict;
use warnings ;
use PostgresNode ;
use TestLib ;
use Test::More tests = > 40 ;
use Test::More tests = > 62 ;
use ServerSetup ;
use File::Copy ;
@ -20,6 +20,14 @@ my $common_connstr;
# of the key stored in the code tree and update its permissions.
copy ( "ssl/client.key" , "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.
@ -48,6 +56,7 @@ $common_connstr =
# The server should not accept non-SSL connections.
test_connect_fails ( $ common_connstr , "sslmode=disable" ,
qr/\Qno pg_hba.conf entry\E/ ,
"server doesn't accept non-SSL connections" ) ;
# 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" ,
"connect without server root cert sslmode=require" ) ;
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" ) ;
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" ) ;
# 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.)
test_connect_fails ( $ common_connstr ,
"sslrootcert=ssl/client_ca.crt sslmode=require" ,
qr/SSL error/ ,
"connect with wrong server root cert sslmode=require" ) ;
test_connect_fails ( $ common_connstr ,
"sslrootcert=ssl/client_ca.crt sslmode=verify-ca" ,
qr/SSL error/ ,
"connect with wrong server root cert sslmode=verify-ca" ) ;
test_connect_fails ( $ common_connstr ,
"sslrootcert=ssl/client_ca.crt sslmode=verify-full" ,
qr/SSL error/ ,
"connect with wrong server root cert sslmode=verify-full" ) ;
# Try with just the server CA's cert. This fails because the root file
# must contain the whole chain up to the root CA.
test_connect_fails ( $ common_connstr ,
"sslrootcert=ssl/server_ca.crt sslmode=verify-ca" ,
qr/SSL error/ ,
"connect with server CA cert, without root CA" ) ;
# 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
test_connect_fails ( $ common_connstr ,
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl" ,
qr/SSL error/ ,
"CRL belonging to a different CA" ) ;
# 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" ,
"mismatch between host name and server certificate sslmode=verify-ca" ) ;
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" ) ;
# Test Subject Alternative 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" ) ;
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" ) ;
test_connect_fails ( $ common_connstr ,
"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" ) ;
# 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" ) ;
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" ) ;
test_connect_fails ( $ common_connstr ,
"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" ) ;
# 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" ,
"certificate with both a CN and SANs 2" ) ;
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" ) ;
# 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" ) ;
test_connect_fails ( $ common_connstr ,
"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" ) ;
# Test that the CRL works
@ -201,6 +223,7 @@ test_connect_ok($common_connstr,
"connects without client-side CRL" ) ;
test_connect_fails ( $ common_connstr ,
"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl" ,
qr/SSL error/ ,
"does not connect with client-side CRL" ) ;
### Part 2. Server-side tests.
@ -215,6 +238,7 @@ $common_connstr =
# no client cert
test_connect_fails ( $ common_connstr ,
"user=ssltestuser sslcert=invalid" ,
qr/connection requires a valid client certificate/ ,
"certificate authorization fails without 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" ,
"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
test_connect_fails ( $ common_connstr ,
"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" ) ;
# revoked client cert
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" ) ;
# 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" ,
"intermediate client certificate is provided by client" ) ;
test_connect_fails ( $ common_connstr , "sslmode=require sslcert=ssl/client.crt" ,
qr/SSL error/ ,
"intermediate client certificate is missing" ) ;
# clean up
unlink "ssl/client_tmp.key" ;
unlink ( "ssl/client_tmp.key" ,
"ssl/client_wrongperms_tmp.key" ,
"ssl/client-revoked_tmp.key" ) ;