|
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