From 368c77885d7925334e8dabcce655b6a82f0a9a8f Mon Sep 17 00:00:00 2001 From: David Zhang Date: Tue, 6 Aug 2024 15:38:14 -0700 Subject: [PATCH 2/2] v1 WIP OCSP simplify .res generation and regress test --- src/test/ssl/conf/ocsp_ca.config | 39 ++++++++++ src/test/ssl/sslfiles.mk | 59 +++++++++++++-- src/test/ssl/t/001_ssltests.pl | 100 ++++++++++++++++++++++++++ src/test/ssl/t/SSL/Backend/OpenSSL.pm | 3 + src/test/ssl/t/SSL/Server.pm | 4 ++ 5 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 src/test/ssl/conf/ocsp_ca.config diff --git a/src/test/ssl/conf/ocsp_ca.config b/src/test/ssl/conf/ocsp_ca.config new file mode 100644 index 0000000000..04f78bacb8 --- /dev/null +++ b/src/test/ssl/conf/ocsp_ca.config @@ -0,0 +1,39 @@ +# An OpenSSL format CSR config file for creating the ocsp responder certificate. +# This configuration file is also used when operating the CA. +# +# This certificate is used to sign OCSP resonpose. + +[ req ] +distinguished_name = req_distinguished_name +prompt = no +req_extensions = v3_ocsp + +[ req_distinguished_name ] +CN = Test CA for PostgreSQL SSL regression test ocsp response + +# Extensions for OCSP responder certs +[ v3_ocsp ] +basicConstraints = CA:false +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = OCSPSigning + +[ ca ] +default_ca = ocsp + +# A shell of a CA, mostly duplicating the server CA, which is used only during +# the OCSP index generation recipes. +[ ocsp ] +dir = ./ssl/ + +# The database (or "index") is the main thing we want. +database = ./ssl/ocsp-certindex + +# Everything else should all be unused, so we specify whatever's most +# convenient. In particular there's no need to have a unique cert/key pair for +# this. +certificate = ./ssl/server_ca.crt +private_key = ./ssl/server_ca.key +serial = ./ssl/ocsp_ca.srl +default_md = sha256 +policy = policy_match + diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk index 88c93ec18d..6f314b7153 100644 --- a/src/test/ssl/sslfiles.mk +++ b/src/test/ssl/sslfiles.mk @@ -35,6 +35,10 @@ SERVERS := server-cn-and-alt-names \ server-revoked CLIENTS := client client-dn client-revoked client_ext client-long \ client-revoked-utf8 +OCSPS := server-ocsp-good \ + server-ocsp-revoked \ + server-ocsp-expired \ + server-ocsp-unknown # # To add a new non-standard certificate, add it to SPECIAL_CERTS and then add @@ -64,12 +68,13 @@ COMBINATIONS := \ ssl/client+client_ca.crt \ ssl/server-cn-only+server_ca.crt -CERTIFICATES := root_ca server_ca client_ca $(SERVERS) $(CLIENTS) +CERTIFICATES := root_ca server_ca client_ca ocsp_ca $(SERVERS) $(CLIENTS) STANDARD_CERTS := $(CERTIFICATES:%=ssl/%.crt) STANDARD_KEYS := $(CERTIFICATES:%=ssl/%.key) CRLS := ssl/root.crl \ ssl/client.crl \ ssl/server.crl +OCSPRES := $(OCSPS:%=ssl/%.res) SSLFILES := \ $(STANDARD_CERTS) \ @@ -77,7 +82,8 @@ SSLFILES := \ $(SPECIAL_CERTS) \ $(SPECIAL_KEYS) \ $(COMBINATIONS) \ - $(CRLS) + $(CRLS) \ + $(OCSPRES) SSLDIRS := ssl/client-crldir \ ssl/server-crldir \ ssl/root+client-crldir \ @@ -175,13 +181,14 @@ $(STANDARD_KEYS): # CA_CERTS := ssl/server_ca.crt ssl/client_ca.crt -SERVER_CERTS := $(SERVERS:%=ssl/%.crt) +SERVER_CERTS := $(SERVERS:%=ssl/%.crt) ssl/ocsp_ca.crt CLIENT_CERTS := $(CLIENTS:%=ssl/%.crt) # See the "CA State" section below. root_ca_state_files := ssl/root_ca-certindex ssl/root_ca-certindex.attr ssl/root_ca.srl server_ca_state_files := ssl/server_ca-certindex ssl/server_ca-certindex.attr ssl/server_ca.srl client_ca_state_files := ssl/client_ca-certindex ssl/client_ca-certindex.attr ssl/client_ca.srl +ocsp_ca_state_files := ssl/ocsp-certindex ssl/ocsp-certindex.attr ssl/ocsp_ca.srl # These are the workhorse recipes. `openssl ca` can't be safely run from # parallel processes, so we must mark the entire Makefile .NOTPARALLEL. @@ -210,6 +217,7 @@ ssl/%.csr: ssl/%.key conf/%.config # .INTERMEDIATE: $(root_ca_state_files) $(server_ca_state_files) $(client_ca_state_files) +.INTERMEDIATE: $(ocsp_ca_state_files) # OpenSSL requires a directory to put all generated certificates in. We don't # use this for anything, but we need a location. @@ -227,6 +235,49 @@ ssl/%-certindex.attr: ssl/%.srl: date +%Y%m%d%H%M%S00 > $@ +# +# OCSP +# +.INTERMEDIATE: $(OCSPS:%=ssl/%.idx) + +# to generate an ocsp response with status 'good' +ssl/server-ocsp-good.idx: conf/ocsp_ca.config ssl/server-cn-only.crt | $(ocsp_ca_state_files) + : > ssl/ocsp-certindex + openssl ca -config conf/ocsp_ca.config -valid ssl/server-cn-only.crt + cp ssl/ocsp-certindex $@ + +# to generate an ocsp response with status 'revoked' +ssl/server-ocsp-revoked.idx: conf/ocsp_ca.config ssl/server-cn-only.crt | $(ocsp_ca_state_files) + : > ssl/ocsp-certindex + openssl ca -config conf/ocsp_ca.config -revoke ssl/server-cn-only.crt + cp ssl/ocsp-certindex $@ + +# to generate an ocsp response with status 'unknown' +ssl/server-ocsp-unknown.idx: + touch $@ + +# to generate an ocsp response with status 'good' but nextUpdate 'expired' in 1 minute +ssl/server-ocsp-expired.idx: ssl/server-ocsp-good.idx + cp $< $@ + +# All of the responses have the server cert in the chain. +OCSPCHAIN = -issuer ssl/server_ca.crt -cert ssl/server-cn-only.crt +$(OCSPRES): ssl/server_ca.crt ssl/server-cn-only.crt + +# Additionally, the server CA is part of the server-ca-* responses. +ssl/server-ca-%.res: OCSPCHAIN += -issuer ssl/root_ca.crt -cert ssl/server_ca.crt +ssl/server-ca-%.res: ssl/root_ca.crt + +# Most responses should "never" expire, except the ones being explicitly tested +# for expiration. +OCSPEXP = -ndays 10000 +ssl/%-ocsp-expired.res: OCSPEXP = -nmin 1 + +$(OCSPRES): ssl/%.res: ssl/%.idx ssl/ocsp_ca.crt ssl/ocsp_ca.key ssl/root+server_ca.crt + $(OPENSSL) ocsp -index $< -respout $@ -rsigner ssl/ocsp_ca.crt \ + -rkey ssl/ocsp_ca.key -CA ssl/root+server_ca.crt \ + $(OCSPEXP) $(OCSPCHAIN) + # # CRLs # @@ -262,7 +313,7 @@ ssl/%-crldir: .PHONY: sslfiles-clean sslfiles-clean: - rm -f $(SSLFILES) ssl/*.old ssl/*.csr ssl/*.srl ssl/*-certindex* + rm -f $(SSLFILES) ssl/*.old ssl/*.csr ssl/*.srl ssl/*-certindex* ssl/*.idx ssl/*.res rm -rf $(SSLDIRS) ssl/new_certs_dir # The difference between the below clean targets and sslfiles-clean is that the diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index b877327023..39eace0c7f 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -905,4 +905,104 @@ $node->connect_fails( # ] ); +# Test ocsp stapling. +# Use a stapled ocsp response with different status for certificate server-cn-only +# so that the client can verify the ocsp response when sslocspstapling is set. +# server-cn-only certificates status is 'good' +switch_server_cert( + $node, + certfile => 'server-cn-only', + keyfile => 'server-cn-only', + cafile => 'root_ca', + ocspfile => 'server-ocsp-good'); + +# Reset the common_connstr +$common_connstr = + "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=ocsp-good.pg-ssltest.test"; + +# Continue the TLS connection when certificate status is 'good' in stapled ocsp response. +$node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=1", + "connect with valid stapled ocsp response when sslocspstapling=1"); + +# Continue the TLS connection when no ocsp response is required. +$node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=0", + "connect without requesting ocsp response when sslocspstapling=0"); + +# server-cn-only certificates status is 'revoked' +switch_server_cert( + $node, + certfile => 'server-cn-only', + keyfile => 'server-cn-only', + cafile => 'root_ca', + ocspfile => 'server-ocsp-revoked'); + +# Fail the TLS connection when certificate status is 'revoked' in stapled ocsp response. +$node->connect_fails( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=1", + "failed with a revoked ocsp response when sslocspstapling=1"); + +# Continue the TLS connection when no ocsp response is required. +$node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=0", + "connect without requesting ocsp response when sslocspstapling=0"); + +# server-cn-only certificates status is 'unknown' +switch_server_cert( + $node, + certfile => 'server-cn-only', + keyfile => 'server-cn-only', + cafile => 'root_ca', + ocspfile => 'server-ocsp-unknown'); + +# Fail the TLS connection when certificate status is 'unknown' in stapled ocsp response. +$node->connect_fails( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=1", + "failed with a revoked ocsp response when sslocspstapling=1"); + +# Continue the TLS connection when no ocsp response is required. +$node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=0", + "connect without requesting ocsp response when sslocspstapling=0"); + +# server-cn-only certificates status is 'expired' +switch_server_cert( + $node, + certfile => 'server-cn-only', + keyfile => 'server-cn-only', + cafile => 'root_ca', + ocspfile => 'server-ocsp-expired'); + +# Fail the TLS connection when stapled ocsp response is 'expired'. +$node->connect_fails( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=1", + "failed with an expired ocsp response when sslocspstapling=1"); + +# Continue the TLS connection when no ocsp response is required. +$node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=0", + "connect without requesting ocsp response when sslocspstapling=0"); + + +# Use a stapled ocsp response which doesn't match the certificate server-ip-cn-only +# so that the client will fail the TLS connection when sslocspstapling is set. +switch_server_cert( + $node, + certfile => 'server-ip-cn-only', + keyfile => 'server-ip-cn-only', + cafile => 'root_ca', + ocspfile => 'server-ocsp-good'); + +# Fail the TLS connection when stapled ocsp response doesn't match certificate. +$node->connect_fails( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=1", + "failed with an expired ocsp response when sslocspstapling=1"); + +# Continue the TLS connection when no ocsp response is required. +$node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslocspstapling=0", + "connect without requesting ocsp response when sslocspstapling=0"); + + done_testing(); diff --git a/src/test/ssl/t/SSL/Backend/OpenSSL.pm b/src/test/ssl/t/SSL/Backend/OpenSSL.pm index 410b4b1a3f..3f4c1a0fe4 100644 --- a/src/test/ssl/t/SSL/Backend/OpenSSL.pm +++ b/src/test/ssl/t/SSL/Backend/OpenSSL.pm @@ -73,6 +73,7 @@ sub init _copy_files("ssl/root+client_ca.crt", $pgdata); _copy_files("ssl/root_ca.crt", $pgdata); _copy_files("ssl/root+client.crl", $pgdata); + _copy_files("ssl/server-*.res", $pgdata); mkdir("$pgdata/root+client-crldir") or die "unable to create server CRL dir $pgdata/root+client-crldir: $!"; _copy_files("ssl/root+client-crldir/*", "$pgdata/root+client-crldir/"); @@ -186,6 +187,8 @@ sub set_server_cert . "ssl_crl_file='$params->{crlfile}'\n"; $sslconf .= "ssl_crl_dir='$params->{crldir}'\n" if defined $params->{crldir}; + $sslconf .= "ssl_ocsp_file='$params->{ocspfile}.res'\n" + if defined $params->{ocspfile}; return $sslconf; } diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm index 021eec74ab..97d49236ec 100644 --- a/src/test/ssl/t/SSL/Server.pm +++ b/src/test/ssl/t/SSL/Server.pm @@ -261,6 +261,10 @@ The CA certificate to use. Implementation is SSL backend specific. The certificate file to use. Implementation is SSL backend specific. +=item ocspfile => B + +The ocsp stapling file to use. Implementation is SSL backend specific. + =item keyfile => B The private key file to use. Implementation is SSL backend specific. -- 2.34.1