From ae81776a23f78babc9707e22f95dea15aa2dbcd2 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Tue, 30 Nov 2021 11:19:59 +0100 Subject: [PATCH] Add TAP tests for contrib/sslinfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds rudimentary coverage of the sslinfo extension into the SSL test harness. The output is validated by comparing with pg_stat_ssl to provide some level of test stability should the underlying certs be slightly altered. A new cert is added to provide an extension to test against. Reviewed-by: Tom Lane Reviewed-by: Andrew Dunstan Reviewed-by: Dagfinn Ilmari Mannsåker Discussion: https://postgr.es/m/E23F9811-0C77-45DA-912F-D809AB140741@yesql.se --- src/test/ssl/Makefile | 2 + src/test/ssl/README | 2 + src/test/ssl/conf/client_ext.config | 16 ++++ src/test/ssl/ssl/client_ext.crt | 21 +++++ src/test/ssl/ssl/client_ext.key | 28 ++++++ src/test/ssl/sslfiles.mk | 2 +- src/test/ssl/t/003_sslinfo.pl | 134 ++++++++++++++++++++++++++++ 7 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/test/ssl/conf/client_ext.config create mode 100644 src/test/ssl/ssl/client_ext.crt create mode 100644 src/test/ssl/ssl/client_ext.key create mode 100644 src/test/ssl/t/003_sslinfo.pl diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile index cb24e31c6e..b749383348 100644 --- a/src/test/ssl/Makefile +++ b/src/test/ssl/Makefile @@ -9,6 +9,8 @@ # #------------------------------------------------------------------------- +EXTRA_INSTALL = contrib/sslinfo + subdir = src/test/ssl top_builddir = ../../.. include $(top_builddir)/src/Makefile.global diff --git a/src/test/ssl/README b/src/test/ssl/README index 9e2cedc0dd..7e60700652 100644 --- a/src/test/ssl/README +++ b/src/test/ssl/README @@ -16,6 +16,8 @@ Running the tests ================= NOTE: You must have given the --enable-tap-tests argument to configure. +Also, to use "make installcheck", you must have built and installed +contrib/sslinfo in addition to the core code. Run make check diff --git a/src/test/ssl/conf/client_ext.config b/src/test/ssl/conf/client_ext.config new file mode 100644 index 0000000000..c2dbfef9a8 --- /dev/null +++ b/src/test/ssl/conf/client_ext.config @@ -0,0 +1,16 @@ +# An OpenSSL format CSR config file for creating a client certificate. +# +# The certificate is for user "ssltestuser" and intends to test client +# certificate with extensions. + +[ req ] +distinguished_name = req_distinguished_name +req_extensions = client_ext +prompt = no + +[ req_distinguished_name ] +CN = ssltestuser + +[ client_ext ] +basicConstraints = critical,CA:false +extendedKeyUsage = clientAuth diff --git a/src/test/ssl/ssl/client_ext.crt b/src/test/ssl/ssl/client_ext.crt new file mode 100644 index 0000000000..9874ce49b9 --- /dev/null +++ b/src/test/ssl/ssl/client_ext.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIIICEREAQyQQAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNs +aWVudCBjZXJ0czAeFw0yMTExMTAwMzMyNDFaFw00OTAzMjgwMzMyNDFaMBYxFDAS +BgNVBAMMC3NzbHRlc3R1c2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArCHikkEQLFITbn3ZfO8X2RW3fELeaImgy8W4Pkkc4LxdHCWjdCML/vtE/ZVu +Op74qrQQWT0HKXFVUiZLbjAgV2PONS6VFHhc3sTFxuTaBnVdY+K98hoFnXskINt/ +wgwUhRcRZuKPcZvEHiqF6e3g3lQa99l1nVKPGPLOCvVhSgoV0Gwgxok0t7s25BCV +ZmpMAwSTxpeviLF0e2MsttuyClQ4nuD92EHZX3BuG0WNPLxiwikV96uMffpMRGsx +uiAHzD5ykYM7/b3eU0bjfi0J0qcfTSeytqFuRCNEukJpmtUmyYGqsFJ7HN7ejCY7 +ObAlBn8h+4bgwBRaeZDZLTMaYQIDAQABo4GgMIGdMAwGA1UdEwEB/wQCMAAwEwYD +VR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0OBBYEFPPv1n7k1Vd9BBC4eoGWPZwVz2Lx +MFkGA1UdIwRSMFChRKRCMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBmb3IgUG9z +dGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlggggIQMDFBIHATANBgkq +hkiG9w0BAQsFAAOCAQEAtqIeTmUhtHyCt5k2yx88F0dKshYq4Z+LQI+agyZ1fRE6 +Ux5p+SBGbzvc+NcUvc7yGG6w2G/nTVnGwSHN9NtQa2T2XbHJysJ/dwCfmRsachKz +4kCp0zAHEDrEmZua0sy5BLwwVCk5WNBR0lZ35WmIEuRA+5G/2lCywtrb9W4YnbAM +nH7BtZE8qPbK4OicB40I2NXz6KhG3755oKN03VC1IaX9JFQxf37ac7jVK5bsjfaF +0xCAeuDN6wDiVHZj6q1GhhmNLzaF5zmU2e/cI1nTI5tfGKnygavlZIz2VvAlcypt +YZdMDy69VbTWUa57UPCspghgvm5M2/Hjmz50CXGMvw== +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/client_ext.key b/src/test/ssl/ssl/client_ext.key new file mode 100644 index 0000000000..04e5930638 --- /dev/null +++ b/src/test/ssl/ssl/client_ext.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsIeKSQRAsUhNu +fdl87xfZFbd8Qt5oiaDLxbg+SRzgvF0cJaN0Iwv++0T9lW46nviqtBBZPQcpcVVS +JktuMCBXY841LpUUeFzexMXG5NoGdV1j4r3yGgWdeyQg23/CDBSFFxFm4o9xm8Qe +KoXp7eDeVBr32XWdUo8Y8s4K9WFKChXQbCDGiTS3uzbkEJVmakwDBJPGl6+IsXR7 +Yyy227IKVDie4P3YQdlfcG4bRY08vGLCKRX3q4x9+kxEazG6IAfMPnKRgzv9vd5T +RuN+LQnSpx9NJ7K2oW5EI0S6Qmma1SbJgaqwUnsc3t6MJjs5sCUGfyH7huDAFFp5 +kNktMxphAgMBAAECggEAQlVWkmUHXgUNHvXZo8chyhMP4A+G1QNAl3Zs73fObJ66 +RPgOOtmsrEjZh92XmnibvHDiofkeMu7NYfiG9gIO3I6GL0Fxyu8tXt22l9SmXnnJ +EQ6Wg19azZrgS9c6ryVnnPhMSPlDLRVJaRSbAZCdqSABOoUvSX7AzWz4UQnJwbVp +c9Le7DbXcD4IIhi+D2o6k46oGTm+P8kEAbw73tN7NmxBudwMhvGup3HlDNypbwPJ +0aWR+nxZbaAVnmYiENX7L68R9rweqDES8AgV030L4YF022C8TAuBLeCjuEQucdp4 ++ZcNUzAF2G1NN/VUpjBKK08+Pu0C0vV+fDrKWK+QnwKBgQC74THLylX/+7TJC24U +LXu/z5BjkejUr4GLHTZG9edGgaoSiKikXdseCI/RiDVXvtQ7kstFYflOZ+XGuc4l +GVAN52uRqg7uXw0R8F8bKpal08j4Rhe4rXKvH5h9hSeozOlxq7jrQ2xk96Guu3k7 +ujqkkVoPX+dnwUVN6elWrMIUpwKBgQDqiwqaKk7Pmkqc5et4WKvKFLKYuTU/qOO6 +fVEqGlgbLGNf+DVgKcTl5AVyhqtedh1hin0ij/dDHoYOmynmbe/zguSxF7kYUxdJ +STwWpQt/ccaWMfqgrjxXpWsPc1fRWgmACAaum04GXmBeZ4z0rVT4blwAVddgoLL8 +q4lrSNbRtwKBgQClv4jnyaxPNecLCmtln66xzFMMlJe8ssztRqswtRYA7Ll2ultV +DnwVpeYDK1AsBe1EVT/BCSshEaXzyM3lisxGR+htTIL5pp9oORAeblcTGqEM7wFU +aqhneM9VxRf04jn8j0uHOicxeAmKllfg6m1768NxFuGWdjpG/1pcnfJmtwKBgAF8 +Nen6AJvB710E+7O8ZAIYlXTwH00y5ZZFuuDYX9x0MIDoEnZ0bUHDauFpxuYHO3Jl +rRst7DPpmpG3G9HQumdBWe9hJhPoWsplA1NlYihBcS98S4j+8XTgoEftxA2YU10T +L++lHh5eNKAEadkWy+Xy1PRPltiOy/NbprgeMvYLAoGAKpt7DHcK8B0JdOnEzTuz +7mT6xRt2C9IASCiv92Fx1BPiPy4l9ukT4CJza/wpSpH3xyeB37afe0kQyU8lDrCF +iMU3RNTzTftwqO8GgtgntgW8ZKe9fuqzm9VLMQFyL+zdqEfGG6ROS8ipYLx9pn6x +FHc3UsmLmK0hfCr9B4Yo+C0= +-----END PRIVATE KEY----- diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk index 81c20aac9e..270f55a58f 100644 --- a/src/test/ssl/sslfiles.mk +++ b/src/test/ssl/sslfiles.mk @@ -27,7 +27,7 @@ SERVERS := server-cn-and-alt-names \ server-multiple-alt-names \ server-no-names \ server-revoked -CLIENTS := client client-dn client-revoked +CLIENTS := client client-dn client-revoked client_ext # # To add a new non-standard key, add it to SPECIAL_KEYS and then add a recipe diff --git a/src/test/ssl/t/003_sslinfo.pl b/src/test/ssl/t/003_sslinfo.pl new file mode 100644 index 0000000000..cf2e8dde0f --- /dev/null +++ b/src/test/ssl/t/003_sslinfo.pl @@ -0,0 +1,134 @@ + +# Copyright (c) 2021, PostgreSQL Global Development Group + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +use File::Copy; + +use FindBin; +use lib $FindBin::RealBin; + +use SSLServer; + +if ($ENV{with_ssl} ne 'openssl') +{ + plan skip_all => 'OpenSSL not supported by this build'; +} +else +{ + plan tests => 13; +} + +#### Some configuration + +# This is the hostname used to connect to the server. This cannot be a +# hostname, because the server certificate is always for the domain +# postgresql-ssl-regression.test. +my $SERVERHOSTADDR = '127.0.0.1'; +# This is the pattern to use in pg_hba.conf to match incoming connections. +my $SERVERHOSTCIDR = '127.0.0.1/32'; + +# Allocation of base connection string shared among multiple tests. +my $common_connstr; + +# The client's private key must not be world-readable, so take a copy +# of the key stored in the code tree and update its permissions. +my $client_tmp_key = "${PostgreSQL::Test::Utils::tmp_check}/client_ext.key"; +copy("ssl/client_ext.key", $client_tmp_key) + or die "couldn't copy ssl/client_ext.key to $client_tmp_key for permissions change: $!"; +chmod 0600, $client_tmp_key + or die "failed to change permissions on $client_tmp_key: $!"; + +#### Set up the server. + +note "setting up data directory"; +my $node = PostgreSQL::Test::Cluster->new('primary'); +$node->init; + +# PGHOST is enforced here to set up the node, subsequent connections +# will use a dedicated connection string. +$ENV{PGHOST} = $node->host; +$ENV{PGPORT} = $node->port; +$node->start; + +configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR, + 'trust', extensions => [ qw(sslinfo) ]); + +# We aren't using any CRL's in this suite so we can keep using server-revoked +# as server certificate for simple client.crt connection much like how the +# 001 test does. +switch_server_cert($node, 'server-revoked'); + +$common_connstr = + "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR " . + "user=ssltestuser sslcert=ssl/client_ext.crt sslkey=$client_tmp_key"; + +# Make sure we can connect even though previous test suites have established this +$node->connect_ok( + $common_connstr, + "certificate authorization succeeds with correct client cert in PEM format", +); + +my $result; + +$result = $node->safe_psql("certdb", "SELECT ssl_is_used();", + connstr => $common_connstr); +is($result, 't', "ssl_is_used() for TLS connection"); + +$result = $node->safe_psql("certdb", "SELECT ssl_version();", + connstr => $common_connstr . " ssl_min_protocol_version=TLSv1.2 " . + "ssl_max_protocol_version=TLSv1.2"); +is($result, 'TLSv1.2', "ssl_version() correctly returning TLS protocol"); + +$result = $node->safe_psql("certdb", + "SELECT ssl_cipher() = cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();", + connstr => $common_connstr); +is($result, 't', "ssl_cipher() compared with pg_stat_ssl"); + +$result = $node->safe_psql("certdb", "SELECT ssl_client_cert_present();", + connstr => $common_connstr); +is($result, 't', "ssl_client_cert_present() for connection with cert"); + +$result = $node->safe_psql("trustdb", "SELECT ssl_client_cert_present();", + connstr => "sslrootcert=ssl/root+server_ca.crt sslmode=require " . + "dbname=trustdb hostaddr=$SERVERHOSTADDR user=ssltestuser"); +is($result, 'f', "ssl_client_cert_present() for connection without cert"); + +$result = $node->safe_psql("certdb", + "SELECT ssl_client_serial() = client_serial FROM pg_stat_ssl WHERE pid = pg_backend_pid();", + connstr => $common_connstr); +is($result, 't', "ssl_client_serial() compared with pg_stat_ssl"); + +# Must not use safe_psql since we expect an error here +$result = $node->psql("certdb", "SELECT ssl_client_dn_field('invalid');", + connstr => $common_connstr); +is($result, '3', "ssl_client_dn_field() for an invalid field"); + +$result = $node->safe_psql("trustdb", "SELECT ssl_client_dn_field('commonName');", + connstr => "sslrootcert=ssl/root+server_ca.crt sslmode=require " . + "dbname=trustdb hostaddr=$SERVERHOSTADDR user=ssltestuser"); +is($result, '', "ssl_client_dn_field() for connection without cert"); + +$result = $node->safe_psql("certdb", + "SELECT '/CN=' || ssl_client_dn_field('commonName') = client_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();", + connstr => $common_connstr); +is($result, 't', "ssl_client_dn_field() for commonName"); + +$result = $node->safe_psql("certdb", + "SELECT ssl_issuer_dn() = issuer_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();", + connstr => $common_connstr); +is($result, 't', "ssl_issuer_dn() for connection with cert"); + +$result = $node->safe_psql("certdb", + "SELECT '/CN=' || ssl_issuer_field('commonName') = issuer_dn FROM pg_stat_ssl WHERE pid = pg_backend_pid();", + connstr => $common_connstr); +is($result, 't', "ssl_issuer_field() for commonName"); + +$result = $node->safe_psql("certdb", + "SELECT value, critical FROM ssl_extension_info() WHERE name = 'basicConstraints';", + connstr => $common_connstr); +is($result, 'CA:FALSE|t', 'extract extension from cert');