Skip to content

Commit 4a7e964

Browse files
SSL TAP test backend library independence refactoring
The SSL TAP tests were tightly coupled to the OpenSSL implementation, making it hard to add support for additional SSL/TLS backends. This refactoring makes the test avoid depending on specific implementations The SSLServer Perl module is renamed SSL::Server, which in turn use SSL::Backend::X where X is the backend pointed to by with_ssl. Each backend will implement its own module responsible for setting up keys, certs and to resolve sslkey values to their implementation specific value (file paths or vault nicknames etc). Further, switch_server_cert now takes a set of named parameters rather than a fixed set which used defaults. The modules also come with POD documentation. There are a few testcases which still use OpenSSL specifics, but it's not entirely clear how to abstract those until we have another library implemented. Original patch by me, with lots of rework by Andrew Dunstan to turn it into better Perl. Discussion: https://postgr.es/m/AA18A362-CA65-4F9A-AF61-76AE318FE97C@yesql.se
1 parent e07d4dd commit 4a7e964

File tree

6 files changed

+664
-322
lines changed

6 files changed

+664
-322
lines changed

src/test/ssl/t/001_ssltests.pl

+58-85
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,25 @@
88
use PostgreSQL::Test::Utils;
99
use Test::More;
1010

11-
use File::Copy;
12-
1311
use FindBin;
1412
use lib $FindBin::RealBin;
1513

16-
use SSLServer;
14+
use SSL::Server;
1715

1816
if ($ENV{with_ssl} ne 'openssl')
1917
{
2018
plan skip_all => 'OpenSSL not supported by this build';
2119
}
2220

21+
my $ssl_server = SSL::Server->new();
22+
sub sslkey
23+
{
24+
return $ssl_server->sslkey(@_);
25+
}
26+
sub switch_server_cert
27+
{
28+
$ssl_server->switch_server_cert(@_);
29+
}
2330
#### Some configuration
2431

2532
# This is the hostname used to connect to the server. This cannot be a
@@ -32,39 +39,6 @@
3239
# Allocation of base connection string shared among multiple tests.
3340
my $common_connstr;
3441

35-
# The client's private key must not be world-readable, so take a copy
36-
# of the key stored in the code tree and update its permissions.
37-
#
38-
# This changes to using keys stored in a temporary path for the rest of
39-
# the tests. To get the full path for inclusion in connection strings, the
40-
# %key hash can be interrogated.
41-
my $cert_tempdir = PostgreSQL::Test::Utils::tempdir();
42-
my %key;
43-
my @keys = (
44-
"client.key", "client-revoked.key",
45-
"client-der.key", "client-encrypted-pem.key",
46-
"client-encrypted-der.key", "client-dn.key");
47-
foreach my $keyfile (@keys)
48-
{
49-
copy("ssl/$keyfile", "$cert_tempdir/$keyfile")
50-
or die
51-
"couldn't copy ssl/$keyfile to $cert_tempdir/$keyfile for permissions change: $!";
52-
chmod 0600, "$cert_tempdir/$keyfile"
53-
or die "failed to change permissions on $cert_tempdir/$keyfile: $!";
54-
$key{$keyfile} = "$cert_tempdir/$keyfile";
55-
$key{$keyfile} =~ s!\\!/!g if $PostgreSQL::Test::Utils::windows_os;
56-
}
57-
58-
# Also make a copy of that explicitly world-readable. We can't
59-
# necessarily rely on the file in the source tree having those
60-
# permissions.
61-
copy("ssl/client.key", "$cert_tempdir/client_wrongperms.key")
62-
or die
63-
"couldn't copy ssl/client_key to $cert_tempdir/client_wrongperms.key for permission change: $!";
64-
chmod 0644, "$cert_tempdir/client_wrongperms.key"
65-
or die "failed to change permissions on $cert_tempdir/client_wrongperms.key: $!";
66-
$key{'client_wrongperms.key'} = "$cert_tempdir/client_wrongperms.key";
67-
$key{'client_wrongperms.key'} =~ s!\\!/!g if $PostgreSQL::Test::Utils::windows_os;
6842
#### Set up the server.
6943

7044
note "setting up data directory";
@@ -79,31 +53,31 @@
7953

8054
# Run this before we lock down access below.
8155
my $result = $node->safe_psql('postgres', "SHOW ssl_library");
82-
is($result, 'OpenSSL', 'ssl_library parameter');
56+
is($result, $ssl_server->ssl_library(), 'ssl_library parameter');
8357

84-
configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR,
85-
'trust');
58+
$ssl_server->configure_test_server_for_ssl($node, $SERVERHOSTADDR,
59+
$SERVERHOSTCIDR, 'trust');
8660

8761
note "testing password-protected keys";
8862

89-
open my $sslconf, '>', $node->data_dir . "/sslconfig.conf";
90-
print $sslconf "ssl=on\n";
91-
print $sslconf "ssl_cert_file='server-cn-only.crt'\n";
92-
print $sslconf "ssl_key_file='server-password.key'\n";
93-
print $sslconf "ssl_passphrase_command='echo wrongpassword'\n";
94-
close $sslconf;
63+
switch_server_cert($node,
64+
certfile => 'server-cn-only',
65+
cafile => 'root+client_ca',
66+
keyfile => 'server-password',
67+
passphrase_cmd => 'echo wrongpassword',
68+
restart => 'no' );
9569

9670
command_fails(
9771
[ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
9872
'restart fails with password-protected key file with wrong password');
9973
$node->_update_pid(0);
10074

101-
open $sslconf, '>', $node->data_dir . "/sslconfig.conf";
102-
print $sslconf "ssl=on\n";
103-
print $sslconf "ssl_cert_file='server-cn-only.crt'\n";
104-
print $sslconf "ssl_key_file='server-password.key'\n";
105-
print $sslconf "ssl_passphrase_command='echo secret1'\n";
106-
close $sslconf;
75+
switch_server_cert($node,
76+
certfile => 'server-cn-only',
77+
cafile => 'root+client_ca',
78+
keyfile => 'server-password',
79+
passphrase_cmd => 'echo secret1',
80+
restart => 'no');
10781

10882
command_ok(
10983
[ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
@@ -136,7 +110,7 @@
136110

137111
note "running client tests";
138112

139-
switch_server_cert($node, 'server-cn-only');
113+
switch_server_cert($node, certfile => 'server-cn-only');
140114

141115
# Set of default settings for SSL parameters in connection string. This
142116
# makes the tests protected against any defaults the environment may have
@@ -256,7 +230,7 @@
256230
);
257231

258232
# Test Subject Alternative Names.
259-
switch_server_cert($node, 'server-multiple-alt-names');
233+
switch_server_cert($node, certfile => 'server-multiple-alt-names');
260234

261235
$common_connstr =
262236
"$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
@@ -285,7 +259,7 @@
285259

286260
# Test certificate with a single Subject Alternative Name. (this gives a
287261
# slightly different error message, that's all)
288-
switch_server_cert($node, 'server-single-alt-name');
262+
switch_server_cert($node, certfile => 'server-single-alt-name');
289263

290264
$common_connstr =
291265
"$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
@@ -309,7 +283,7 @@
309283

310284
# Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN
311285
# should be ignored when the certificate has both.
312-
switch_server_cert($node, 'server-cn-and-alt-names');
286+
switch_server_cert($node, certfile => 'server-cn-and-alt-names');
313287

314288
$common_connstr =
315289
"$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
@@ -327,7 +301,7 @@
327301

328302
# Finally, test a server certificate that has no CN or SANs. Of course, that's
329303
# not a very sensible certificate, but libpq should handle it gracefully.
330-
switch_server_cert($node, 'server-no-names');
304+
switch_server_cert($node, certfile => 'server-no-names');
331305
$common_connstr =
332306
"$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR";
333307

@@ -342,7 +316,7 @@
342316
qr/could not get server's host name from server certificate/);
343317

344318
# Test that the CRL works
345-
switch_server_cert($node, 'server-revoked');
319+
switch_server_cert($node, certfile => 'server-revoked');
346320

347321
$common_connstr =
348322
"$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
@@ -410,42 +384,42 @@
410384

411385
# correct client cert in unencrypted PEM
412386
$node->connect_ok(
413-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client.key'}",
387+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client.key'),
414388
"certificate authorization succeeds with correct client cert in PEM format"
415389
);
416390

417391
# correct client cert in unencrypted DER
418392
$node->connect_ok(
419-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client-der.key'}",
393+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client-der.key'),
420394
"certificate authorization succeeds with correct client cert in DER format"
421395
);
422396

423397
# correct client cert in encrypted PEM
424398
$node->connect_ok(
425-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client-encrypted-pem.key'} sslpassword='dUmmyP^#+'",
399+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client-encrypted-pem.key') . " sslpassword='dUmmyP^#+'",
426400
"certificate authorization succeeds with correct client cert in encrypted PEM format"
427401
);
428402

429403
# correct client cert in encrypted DER
430404
$node->connect_ok(
431-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client-encrypted-der.key'} sslpassword='dUmmyP^#+'",
405+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client-encrypted-der.key') . " sslpassword='dUmmyP^#+'",
432406
"certificate authorization succeeds with correct client cert in encrypted DER format"
433407
);
434408

435409
# correct client cert in encrypted PEM with wrong password
436410
$node->connect_fails(
437-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client-encrypted-pem.key'} sslpassword='wrong'",
411+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client-encrypted-pem.key') . " sslpassword='wrong'",
438412
"certificate authorization fails with correct client cert and wrong password in encrypted PEM format",
439413
expected_stderr =>
440-
qr!\Qprivate key file "$key{'client-encrypted-pem.key'}": bad decrypt\E!
414+
qr!private key file \".*client-encrypted-pem\.key\": bad decrypt!,
441415
);
442416

443417

444418
# correct client cert using whole DN
445419
my $dn_connstr = "$common_connstr dbname=certdb_dn";
446420

447421
$node->connect_ok(
448-
"$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt sslkey=$key{'client-dn.key'}",
422+
"$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt " . sslkey('client-dn.key'),
449423
"certificate authorization succeeds with DN mapping",
450424
log_like => [
451425
qr/connection authenticated: identity="CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG" method=cert/
@@ -455,14 +429,14 @@
455429
$dn_connstr = "$common_connstr dbname=certdb_dn_re";
456430

457431
$node->connect_ok(
458-
"$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt sslkey=$key{'client-dn.key'}",
432+
"$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt " . sslkey('client-dn.key'),
459433
"certificate authorization succeeds with DN regex mapping");
460434

461435
# same thing but using explicit CN
462436
$dn_connstr = "$common_connstr dbname=certdb_cn";
463437

464438
$node->connect_ok(
465-
"$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt sslkey=$key{'client-dn.key'}",
439+
"$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt " . sslkey('client-dn.key'),
466440
"certificate authorization succeeds with CN mapping",
467441
# the full DN should still be used as the authenticated identity
468442
log_like => [
@@ -480,18 +454,18 @@
480454

481455
# correct client cert in encrypted PEM with empty password
482456
$node->connect_fails(
483-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client-encrypted-pem.key'} sslpassword=''",
457+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client-encrypted-pem.key') . " sslpassword=''",
484458
"certificate authorization fails with correct client cert and empty password in encrypted PEM format",
485459
expected_stderr =>
486-
qr!\Qprivate key file "$key{'client-encrypted-pem.key'}": processing error\E!
460+
qr!private key file \".*client-encrypted-pem\.key\": processing error!
487461
);
488462

489463
# correct client cert in encrypted PEM with no password
490464
$node->connect_fails(
491-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client-encrypted-pem.key'}",
465+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client-encrypted-pem.key'),
492466
"certificate authorization fails with correct client cert and no password in encrypted PEM format",
493467
expected_stderr =>
494-
qr!\Qprivate key file "$key{'client-encrypted-pem.key'}": processing error\E!
468+
qr!private key file \".*client-encrypted-pem\.key\": processing error!
495469
);
496470

497471
}
@@ -534,12 +508,12 @@
534508
'-P',
535509
'null=_null_',
536510
'-d',
537-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client.key'}",
511+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client.key'),
538512
'-c',
539513
"SELECT * FROM pg_stat_ssl WHERE pid = pg_backend_pid()"
540514
],
541515
qr{^pid,ssl,version,cipher,bits,client_dn,client_serial,issuer_dn\r?\n
542-
^\d+,t,TLSv[\d.]+,[\w-]+,\d+,/CN=ssltestuser,$serialno,\Q/CN=Test CA for PostgreSQL SSL regression test client certs\E\r?$}mx,
516+
^\d+,t,TLSv[\d.]+,[\w-]+,\d+,/?CN=ssltestuser,$serialno,/?\QCN=Test CA for PostgreSQL SSL regression test client certs\E\r?$}mx,
543517
'pg_stat_ssl with client certificate');
544518

545519
# client key with wrong permissions
@@ -548,16 +522,16 @@
548522
skip "Permissions check not enforced on Windows", 2 if ($windows_os);
549523

550524
$node->connect_fails(
551-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client_wrongperms.key'}",
525+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client_wrongperms.key'),
552526
"certificate authorization fails because of file permissions",
553527
expected_stderr =>
554-
qr!\Qprivate key file "$key{'client_wrongperms.key'}" has group or world access\E!
528+
qr!private key file \".*client_wrongperms\.key\" has group or world access!
555529
);
556530
}
557531

558532
# client cert belonging to another user
559533
$node->connect_fails(
560-
"$common_connstr user=anotheruser sslcert=ssl/client.crt sslkey=$key{'client.key'}",
534+
"$common_connstr user=anotheruser sslcert=ssl/client.crt " . sslkey('client.key'),
561535
"certificate authorization fails with client cert belonging to another user",
562536
expected_stderr =>
563537
qr/certificate authentication failed for user "anotheruser"/,
@@ -567,7 +541,7 @@
567541

568542
# revoked client cert
569543
$node->connect_fails(
570-
"$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=$key{'client-revoked.key'}",
544+
"$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt " . sslkey('client-revoked.key'),
571545
"certificate authorization fails with revoked client cert",
572546
expected_stderr => qr/SSL error: sslv3 alert certificate revoked/,
573547
# revoked certificates should not authenticate the user
@@ -580,31 +554,31 @@
580554
"$default_ssl_connstr sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=verifydb hostaddr=$SERVERHOSTADDR host=localhost";
581555

582556
$node->connect_ok(
583-
"$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=$key{'client.key'}",
557+
"$common_connstr user=ssltestuser sslcert=ssl/client.crt " . sslkey('client.key'),
584558
"auth_option clientcert=verify-full succeeds with matching username and Common Name",
585559
# verify-full does not provide authentication
586560
log_unlike => [qr/connection authenticated:/],);
587561

588562
$node->connect_fails(
589-
"$common_connstr user=anotheruser sslcert=ssl/client.crt sslkey=$key{'client.key'}",
563+
"$common_connstr user=anotheruser sslcert=ssl/client.crt " . sslkey('client.key'),
590564
"auth_option clientcert=verify-full fails with mismatching username and Common Name",
591565
expected_stderr =>
592566
qr/FATAL: .* "trust" authentication failed for user "anotheruser"/,
593567
# verify-full does not provide authentication
594568
log_unlike => [qr/connection authenticated:/],);
595569

596-
# Check that connecting with auth-optionverify-ca in pg_hba :
570+
# Check that connecting with auth-option verify-ca in pg_hba :
597571
# works, when username doesn't match Common Name
598572
$node->connect_ok(
599-
"$common_connstr user=yetanotheruser sslcert=ssl/client.crt sslkey=$key{'client.key'}",
573+
"$common_connstr user=yetanotheruser sslcert=ssl/client.crt " . sslkey('client.key'),
600574
"auth_option clientcert=verify-ca succeeds with mismatching username and Common Name",
601575
# verify-full does not provide authentication
602576
log_unlike => [qr/connection authenticated:/],);
603577

604578
# intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file
605-
switch_server_cert($node, 'server-cn-only', 'root_ca');
579+
switch_server_cert($node, certfile => 'server-cn-only', cafile => 'root_ca');
606580
$common_connstr =
607-
"$default_ssl_connstr user=ssltestuser dbname=certdb sslkey=$key{'client.key'} sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR host=localhost";
581+
"$default_ssl_connstr user=ssltestuser dbname=certdb " . sslkey('client.key') . " sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR host=localhost";
608582

609583
$node->connect_ok(
610584
"$common_connstr sslmode=require sslcert=ssl/client+client_ca.crt",
@@ -615,12 +589,11 @@
615589
expected_stderr => qr/SSL error: tlsv1 alert unknown ca/);
616590

617591
# test server-side CRL directory
618-
switch_server_cert($node, 'server-cn-only', undef, undef,
619-
'root+client-crldir');
592+
switch_server_cert($node, certfile => 'server-cn-only', crldir => 'root+client-crldir');
620593

621594
# revoked client cert
622595
$node->connect_fails(
623-
"$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=$key{'client-revoked.key'}",
596+
"$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt " . sslkey('client-revoked.key'),
624597
"certificate authorization fails with revoked client cert with server-side CRL directory",
625598
expected_stderr => qr/SSL error: sslv3 alert certificate revoked/);
626599

src/test/ssl/t/002_scram.pl

+14-3
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,24 @@
1414
use FindBin;
1515
use lib $FindBin::RealBin;
1616

17-
use SSLServer;
17+
use SSL::Server;
1818

1919
if ($ENV{with_ssl} ne 'openssl')
2020
{
2121
plan skip_all => 'OpenSSL not supported by this build';
2222
}
2323

24+
my $ssl_server = SSL::Server->new();
25+
sub sslkey
26+
{
27+
return $ssl_server->sslkey(@_);
28+
}
29+
sub switch_server_cert
30+
{
31+
$ssl_server->switch_server_cert(@_);
32+
}
33+
34+
2435
# This is the hostname used to connect to the server.
2536
my $SERVERHOSTADDR = '127.0.0.1';
2637
# This is the pattern to use in pg_hba.conf to match incoming connections.
@@ -46,9 +57,9 @@
4657
$node->start;
4758

4859
# Configure server for SSL connections, with password handling.
49-
configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR,
60+
$ssl_server->configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR,
5061
"scram-sha-256", 'password' => "pass", 'password_enc' => "scram-sha-256");
51-
switch_server_cert($node, 'server-cn-only');
62+
switch_server_cert($node, certfile => 'server-cn-only');
5263
$ENV{PGPASSWORD} = "pass";
5364
$common_connstr =
5465
"dbname=trustdb sslmode=require sslcert=invalid sslrootcert=invalid hostaddr=$SERVERHOSTADDR host=localhost";

0 commit comments

Comments
 (0)