6 use Test::More tests => 38;
10 # Like TestLib.pm, we use IPC::Run
15 import IPC::Run qw(run start);
19 plan skip_all => "IPC::Run not available";
23 #### Some configuration
25 # This is the hostname used to connect to the server. This cannot be a
26 # hostname, because the server certificate is always for the domain
27 # postgresql-ssl-regression.test.
28 my $SERVERHOSTADDR = '127.0.0.1';
30 # Define a couple of helper functions to test connecting to the server.
37 my $logstring = $_[1];
40 'psql', '-A', '-t', '-c', "SELECT 'connected with $connstr'",
43 my $result = run_log($cmd);
48 # The first argument is a (part of a) connection string, and it's also printed
49 # out as the test case name. It is appended to $common_connstr global variable,
50 # which also contains a libpq connection string.
52 # The second argument is a hostname to connect to.
58 run_test_psql("$common_connstr $connstr", "(should succeed)");
59 ok($result, $connstr);
62 sub test_connect_fails
66 my $result = run_test_psql("$common_connstr $connstr", "(should fail)");
67 ok(!$result, "$connstr (should fail)");
70 # The client's private key must not be world-readable. Git doesn't track
71 # permissions (except for the executable bit), so they might be wrong after
73 chmod 0600, "ssl/client.key";
75 #### Part 0. Set up the server.
77 diag "setting up data directory...";
78 my $node = get_new_node();
81 # PGHOST is enforced here to set up the node, subsequent connections
82 # will use a dedicated connection string.
83 $ENV{PGHOST} = $node->host;
84 $ENV{PGPORT} = $node->port;
86 configure_test_server_for_ssl($node, $SERVERHOSTADDR);
87 switch_server_cert($node, 'server-cn-only');
89 ### Part 1. Run client-side tests.
91 ### Test that libpq accepts/rejects the connection correctly, depending
92 ### on sslmode and whether the server's certificate looks correct. No
93 ### client certificate is used in these tests.
95 diag "running client tests...";
98 "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
100 # The server should not accept non-SSL connections
101 diag "test that the server doesn't accept non-SSL connections";
102 test_connect_fails("sslmode=disable");
104 # Try without a root cert. In sslmode=require, this should work. In verify-ca
105 # or verify-full mode it should fail
106 diag "connect without server root cert";
107 test_connect_ok("sslrootcert=invalid sslmode=require");
108 test_connect_fails("sslrootcert=invalid sslmode=verify-ca");
109 test_connect_fails("sslrootcert=invalid sslmode=verify-full");
111 # Try with wrong root cert, should fail. (we're using the client CA as the
112 # root, but the server's key is signed by the server CA)
113 diag "connect without wrong server root cert";
114 test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=require");
115 test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=verify-ca");
116 test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=verify-full");
118 # Try with just the server CA's cert. This fails because the root file
119 # must contain the whole chain up to the root CA.
120 diag "connect with server CA cert, without root CA";
121 test_connect_fails("sslrootcert=ssl/server_ca.crt sslmode=verify-ca");
123 # And finally, with the correct root cert.
124 diag "connect with correct server CA cert file";
125 test_connect_ok("sslrootcert=ssl/root+server_ca.crt sslmode=require");
126 test_connect_ok("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca");
127 test_connect_ok("sslrootcert=ssl/root+server_ca.crt sslmode=verify-full");
129 # Test with cert root file that contains two certificates. The client should
130 # be able to pick the right one, regardless of the order in the file.
131 test_connect_ok("sslrootcert=ssl/both-cas-1.crt sslmode=verify-ca");
132 test_connect_ok("sslrootcert=ssl/both-cas-2.crt sslmode=verify-ca");
134 diag "testing sslcrl option with a non-revoked cert";
136 # Invalid CRL filename is the same as no CRL, succeeds
138 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid");
140 # A CRL belonging to a different CA is not accepted, fails
142 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl");
144 # With the correct CRL, succeeds (this cert is not revoked)
146 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl"
149 # Check that connecting with verify-full fails, when the hostname doesn't
150 # match the hostname in the server's certificate.
151 diag "test mismatch between hostname and server certificate";
153 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
155 test_connect_ok("sslmode=require host=wronghost.test");
156 test_connect_ok("sslmode=verify-ca host=wronghost.test");
157 test_connect_fails("sslmode=verify-full host=wronghost.test");
159 # Test Subject Alternative Names.
160 switch_server_cert($node, 'server-multiple-alt-names');
162 diag "test hostname matching with X509 Subject Alternative Names";
164 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
166 test_connect_ok("host=dns1.alt-name.pg-ssltest.test");
167 test_connect_ok("host=dns2.alt-name.pg-ssltest.test");
168 test_connect_ok("host=foo.wildcard.pg-ssltest.test");
170 test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
171 test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
173 # Test certificate with a single Subject Alternative Name. (this gives a
174 # slightly different error message, that's all)
175 switch_server_cert($node, 'server-single-alt-name');
177 diag "test hostname matching with a single X509 Subject Alternative Name";
179 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
181 test_connect_ok("host=single.alt-name.pg-ssltest.test");
183 test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
184 test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
186 # Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN
187 # should be ignored when the certificate has both.
188 switch_server_cert($node, 'server-cn-and-alt-names');
190 diag "test certificate with both a CN and SANs";
192 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
194 test_connect_ok("host=dns1.alt-name.pg-ssltest.test");
195 test_connect_ok("host=dns2.alt-name.pg-ssltest.test");
196 test_connect_fails("host=common-name.pg-ssltest.test");
198 # Finally, test a server certificate that has no CN or SANs. Of course, that's
199 # not a very sensible certificate, but libpq should handle it gracefully.
200 switch_server_cert($node, 'server-no-names');
202 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR";
204 test_connect_ok("sslmode=verify-ca host=common-name.pg-ssltest.test");
205 test_connect_fails("sslmode=verify-full host=common-name.pg-ssltest.test");
207 # Test that the CRL works
208 diag "Testing client-side CRL";
209 switch_server_cert($node, 'server-revoked');
212 "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
214 # Without the CRL, succeeds. With it, fails.
215 test_connect_ok("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca");
217 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl"
220 ### Part 2. Server-side tests.
222 ### Test certificate authorization.
224 diag "Testing certificate authorization...";
226 "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR";
229 test_connect_fails("user=ssltestuser sslcert=invalid");
231 # correct client cert
233 "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client.key");
235 # client cert belonging to another user
237 "user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client.key");
239 # revoked client cert
241 "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked.key"