]> granicus.if.org Git - postgresql/blob - src/test/ssl/t/001_ssltests.pl
Refactor Perl test code
[postgresql] / src / test / ssl / t / 001_ssltests.pl
1 use strict;
2 use warnings;
3 use PostgresNode;
4 use TestLib;
5 use TestLib;
6 use Test::More tests => 38;
7 use ServerSetup;
8 use File::Copy;
9
10 # Like TestLib.pm, we use IPC::Run
11 BEGIN
12 {
13         eval {
14                 require IPC::Run;
15                 import IPC::Run qw(run start);
16                 1;
17         } or do
18         {
19                 plan skip_all => "IPC::Run not available";
20           }
21 }
22
23 #### Some configuration
24
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';
29
30 # Define a couple of helper functions to test connecting to the server.
31
32 my $common_connstr;
33
34 sub run_test_psql
35 {
36         my $connstr   = $_[0];
37         my $logstring = $_[1];
38
39         my $cmd = [
40                 'psql', '-A', '-t', '-c', "SELECT 'connected with $connstr'",
41                 '-d', "$connstr" ];
42
43         my $result = run_log($cmd);
44         return $result;
45 }
46
47 #
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.
51 #
52 # The second argument is a hostname to connect to.
53 sub test_connect_ok
54 {
55         my $connstr = $_[0];
56
57         my $result =
58           run_test_psql("$common_connstr $connstr", "(should succeed)");
59         ok($result, $connstr);
60 }
61
62 sub test_connect_fails
63 {
64         my $connstr = $_[0];
65
66         my $result = run_test_psql("$common_connstr $connstr", "(should fail)");
67         ok(!$result, "$connstr (should fail)");
68 }
69
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
72 # a checkout.
73 chmod 0600, "ssl/client.key";
74
75 #### Part 0. Set up the server.
76
77 diag "setting up data directory...";
78 my $node = get_new_node();
79 $node->init;
80
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;
85 $node->start;
86 configure_test_server_for_ssl($node, $SERVERHOSTADDR);
87 switch_server_cert($node, 'server-cn-only');
88
89 ### Part 1. Run client-side tests.
90 ###
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.
94
95 diag "running client tests...";
96
97 $common_connstr =
98 "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
99
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");
103
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");
110
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");
117
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");
122
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");
128
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");
133
134 diag "testing sslcrl option with a non-revoked cert";
135
136 # Invalid CRL filename is the same as no CRL, succeeds
137 test_connect_ok(
138         "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid");
139
140 # A CRL belonging to a different CA is not accepted, fails
141 test_connect_fails(
142 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl");
143
144 # With the correct CRL, succeeds (this cert is not revoked)
145 test_connect_ok(
146 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl"
147 );
148
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";
152 $common_connstr =
153 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
154
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");
158
159 # Test Subject Alternative Names.
160 switch_server_cert($node, 'server-multiple-alt-names');
161
162 diag "test hostname matching with X509 Subject Alternative Names";
163 $common_connstr =
164 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
165
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");
169
170 test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
171 test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
172
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');
176
177 diag "test hostname matching with a single X509 Subject Alternative Name";
178 $common_connstr =
179 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
180
181 test_connect_ok("host=single.alt-name.pg-ssltest.test");
182
183 test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
184 test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
185
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');
189
190 diag "test certificate with both a CN and SANs";
191 $common_connstr =
192 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
193
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");
197
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');
201 $common_connstr =
202 "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR";
203
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");
206
207 # Test that the CRL works
208 diag "Testing client-side CRL";
209 switch_server_cert($node, 'server-revoked');
210
211 $common_connstr =
212 "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
213
214 # Without the CRL, succeeds. With it, fails.
215 test_connect_ok("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca");
216 test_connect_fails(
217 "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl"
218 );
219
220 ### Part 2. Server-side tests.
221 ###
222 ### Test certificate authorization.
223
224 diag "Testing certificate authorization...";
225 $common_connstr =
226 "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR";
227
228 # no client cert
229 test_connect_fails("user=ssltestuser sslcert=invalid");
230
231 # correct client cert
232 test_connect_ok(
233         "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client.key");
234
235 # client cert belonging to another user
236 test_connect_fails(
237         "user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client.key");
238
239 # revoked client cert
240 test_connect_fails(
241 "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked.key"
242 );