|
8 | 8 | use PostgreSQL::Test::Utils;
|
9 | 9 | use Test::More;
|
10 | 10 |
|
11 |
| -use File::Copy; |
12 |
| - |
13 | 11 | use FindBin;
|
14 | 12 | use lib $FindBin::RealBin;
|
15 | 13 |
|
16 |
| -use SSLServer; |
| 14 | +use SSL::Server; |
17 | 15 |
|
18 | 16 | if ($ENV{with_ssl} ne 'openssl')
|
19 | 17 | {
|
20 | 18 | plan skip_all => 'OpenSSL not supported by this build';
|
21 | 19 | }
|
22 | 20 |
|
| 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 | +} |
23 | 30 | #### Some configuration
|
24 | 31 |
|
25 | 32 | # This is the hostname used to connect to the server. This cannot be a
|
|
32 | 39 | # Allocation of base connection string shared among multiple tests.
|
33 | 40 | my $common_connstr;
|
34 | 41 |
|
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; |
68 | 42 | #### Set up the server.
|
69 | 43 |
|
70 | 44 | note "setting up data directory";
|
|
79 | 53 |
|
80 | 54 | # Run this before we lock down access below.
|
81 | 55 | 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'); |
83 | 57 |
|
84 |
| -configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR, |
85 |
| - 'trust'); |
| 58 | +$ssl_server->configure_test_server_for_ssl($node, $SERVERHOSTADDR, |
| 59 | + $SERVERHOSTCIDR, 'trust'); |
86 | 60 |
|
87 | 61 | note "testing password-protected keys";
|
88 | 62 |
|
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' ); |
95 | 69 |
|
96 | 70 | command_fails(
|
97 | 71 | [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
|
98 | 72 | 'restart fails with password-protected key file with wrong password');
|
99 | 73 | $node->_update_pid(0);
|
100 | 74 |
|
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'); |
107 | 81 |
|
108 | 82 | command_ok(
|
109 | 83 | [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
|
|
136 | 110 |
|
137 | 111 | note "running client tests";
|
138 | 112 |
|
139 |
| -switch_server_cert($node, 'server-cn-only'); |
| 113 | +switch_server_cert($node, certfile => 'server-cn-only'); |
140 | 114 |
|
141 | 115 | # Set of default settings for SSL parameters in connection string. This
|
142 | 116 | # makes the tests protected against any defaults the environment may have
|
|
256 | 230 | );
|
257 | 231 |
|
258 | 232 | # Test Subject Alternative Names.
|
259 |
| -switch_server_cert($node, 'server-multiple-alt-names'); |
| 233 | +switch_server_cert($node, certfile => 'server-multiple-alt-names'); |
260 | 234 |
|
261 | 235 | $common_connstr =
|
262 | 236 | "$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
|
|
285 | 259 |
|
286 | 260 | # Test certificate with a single Subject Alternative Name. (this gives a
|
287 | 261 | # 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'); |
289 | 263 |
|
290 | 264 | $common_connstr =
|
291 | 265 | "$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
|
|
309 | 283 |
|
310 | 284 | # Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN
|
311 | 285 | # 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'); |
313 | 287 |
|
314 | 288 | $common_connstr =
|
315 | 289 | "$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
|
|
327 | 301 |
|
328 | 302 | # Finally, test a server certificate that has no CN or SANs. Of course, that's
|
329 | 303 | # 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'); |
331 | 305 | $common_connstr =
|
332 | 306 | "$default_ssl_connstr user=ssltestuser dbname=trustdb sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR";
|
333 | 307 |
|
|
342 | 316 | qr/could not get server's host name from server certificate/);
|
343 | 317 |
|
344 | 318 | # Test that the CRL works
|
345 |
| -switch_server_cert($node, 'server-revoked'); |
| 319 | +switch_server_cert($node, certfile => 'server-revoked'); |
346 | 320 |
|
347 | 321 | $common_connstr =
|
348 | 322 | "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
|
|
410 | 384 |
|
411 | 385 | # correct client cert in unencrypted PEM
|
412 | 386 | $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'), |
414 | 388 | "certificate authorization succeeds with correct client cert in PEM format"
|
415 | 389 | );
|
416 | 390 |
|
417 | 391 | # correct client cert in unencrypted DER
|
418 | 392 | $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'), |
420 | 394 | "certificate authorization succeeds with correct client cert in DER format"
|
421 | 395 | );
|
422 | 396 |
|
423 | 397 | # correct client cert in encrypted PEM
|
424 | 398 | $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^#+'", |
426 | 400 | "certificate authorization succeeds with correct client cert in encrypted PEM format"
|
427 | 401 | );
|
428 | 402 |
|
429 | 403 | # correct client cert in encrypted DER
|
430 | 404 | $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^#+'", |
432 | 406 | "certificate authorization succeeds with correct client cert in encrypted DER format"
|
433 | 407 | );
|
434 | 408 |
|
435 | 409 | # correct client cert in encrypted PEM with wrong password
|
436 | 410 | $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'", |
438 | 412 | "certificate authorization fails with correct client cert and wrong password in encrypted PEM format",
|
439 | 413 | 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!, |
441 | 415 | );
|
442 | 416 |
|
443 | 417 |
|
444 | 418 | # correct client cert using whole DN
|
445 | 419 | my $dn_connstr = "$common_connstr dbname=certdb_dn";
|
446 | 420 |
|
447 | 421 | $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'), |
449 | 423 | "certificate authorization succeeds with DN mapping",
|
450 | 424 | log_like => [
|
451 | 425 | qr/connection authenticated: identity="CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG" method=cert/
|
|
455 | 429 | $dn_connstr = "$common_connstr dbname=certdb_dn_re";
|
456 | 430 |
|
457 | 431 | $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'), |
459 | 433 | "certificate authorization succeeds with DN regex mapping");
|
460 | 434 |
|
461 | 435 | # same thing but using explicit CN
|
462 | 436 | $dn_connstr = "$common_connstr dbname=certdb_cn";
|
463 | 437 |
|
464 | 438 | $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'), |
466 | 440 | "certificate authorization succeeds with CN mapping",
|
467 | 441 | # the full DN should still be used as the authenticated identity
|
468 | 442 | log_like => [
|
|
480 | 454 |
|
481 | 455 | # correct client cert in encrypted PEM with empty password
|
482 | 456 | $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=''", |
484 | 458 | "certificate authorization fails with correct client cert and empty password in encrypted PEM format",
|
485 | 459 | 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! |
487 | 461 | );
|
488 | 462 |
|
489 | 463 | # correct client cert in encrypted PEM with no password
|
490 | 464 | $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'), |
492 | 466 | "certificate authorization fails with correct client cert and no password in encrypted PEM format",
|
493 | 467 | 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! |
495 | 469 | );
|
496 | 470 |
|
497 | 471 | }
|
|
534 | 508 | '-P',
|
535 | 509 | 'null=_null_',
|
536 | 510 | '-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'), |
538 | 512 | '-c',
|
539 | 513 | "SELECT * FROM pg_stat_ssl WHERE pid = pg_backend_pid()"
|
540 | 514 | ],
|
541 | 515 | 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, |
543 | 517 | 'pg_stat_ssl with client certificate');
|
544 | 518 |
|
545 | 519 | # client key with wrong permissions
|
|
548 | 522 | skip "Permissions check not enforced on Windows", 2 if ($windows_os);
|
549 | 523 |
|
550 | 524 | $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'), |
552 | 526 | "certificate authorization fails because of file permissions",
|
553 | 527 | 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! |
555 | 529 | );
|
556 | 530 | }
|
557 | 531 |
|
558 | 532 | # client cert belonging to another user
|
559 | 533 | $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'), |
561 | 535 | "certificate authorization fails with client cert belonging to another user",
|
562 | 536 | expected_stderr =>
|
563 | 537 | qr/certificate authentication failed for user "anotheruser"/,
|
|
567 | 541 |
|
568 | 542 | # revoked client cert
|
569 | 543 | $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'), |
571 | 545 | "certificate authorization fails with revoked client cert",
|
572 | 546 | expected_stderr => qr/SSL error: sslv3 alert certificate revoked/,
|
573 | 547 | # revoked certificates should not authenticate the user
|
|
580 | 554 | "$default_ssl_connstr sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=verifydb hostaddr=$SERVERHOSTADDR host=localhost";
|
581 | 555 |
|
582 | 556 | $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'), |
584 | 558 | "auth_option clientcert=verify-full succeeds with matching username and Common Name",
|
585 | 559 | # verify-full does not provide authentication
|
586 | 560 | log_unlike => [qr/connection authenticated:/],);
|
587 | 561 |
|
588 | 562 | $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'), |
590 | 564 | "auth_option clientcert=verify-full fails with mismatching username and Common Name",
|
591 | 565 | expected_stderr =>
|
592 | 566 | qr/FATAL: .* "trust" authentication failed for user "anotheruser"/,
|
593 | 567 | # verify-full does not provide authentication
|
594 | 568 | log_unlike => [qr/connection authenticated:/],);
|
595 | 569 |
|
596 |
| -# Check that connecting with auth-optionverify-ca in pg_hba : |
| 570 | +# Check that connecting with auth-option verify-ca in pg_hba : |
597 | 571 | # works, when username doesn't match Common Name
|
598 | 572 | $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'), |
600 | 574 | "auth_option clientcert=verify-ca succeeds with mismatching username and Common Name",
|
601 | 575 | # verify-full does not provide authentication
|
602 | 576 | log_unlike => [qr/connection authenticated:/],);
|
603 | 577 |
|
604 | 578 | # 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'); |
606 | 580 | $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"; |
608 | 582 |
|
609 | 583 | $node->connect_ok(
|
610 | 584 | "$common_connstr sslmode=require sslcert=ssl/client+client_ca.crt",
|
|
615 | 589 | expected_stderr => qr/SSL error: tlsv1 alert unknown ca/);
|
616 | 590 |
|
617 | 591 | # 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'); |
620 | 593 |
|
621 | 594 | # revoked client cert
|
622 | 595 | $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'), |
624 | 597 | "certificate authorization fails with revoked client cert with server-side CRL directory",
|
625 | 598 | expected_stderr => qr/SSL error: sslv3 alert certificate revoked/);
|
626 | 599 |
|
|
0 commit comments