From 6431a7863cfa7f8374da6a80375760615b651fed Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Mon, 8 Feb 2021 23:52:37 +0100 Subject: [PATCH v31 3/9] nss: Add NSS specific tests This adds the SSL/Backend/NSS which implements the setup and teardown required as well as adds the NSS configuration to the existing tests. The OpenSSL testfiles are reused by repackaging them into NSS databases in order for the tests to be cross-library. Additional testfiles are generated by using only the NSS toolchain as well as rudimentary tests using these. --- src/test/ssl/Makefile | 244 ++++++++++++++++++ src/test/ssl/t/001_ssltests.pl | 342 +++++++++++++++++--------- src/test/ssl/t/002_scram.pl | 31 ++- src/test/ssl/t/SSL/Backend/NSS.pm | 61 +++++ src/test/ssl/t/SSL/Backend/OpenSSL.pm | 19 +- src/test/ssl/t/SSL/Server.pm | 48 +--- 6 files changed, 582 insertions(+), 163 deletions(-) create mode 100644 src/test/ssl/t/SSL/Backend/NSS.pm diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile index 4b53fdf6c0..8d4afa9dd0 100644 --- a/src/test/ssl/Makefile +++ b/src/test/ssl/Makefile @@ -33,6 +33,36 @@ SSLFILES := $(CERTIFICATES:%=ssl/%.key) $(CERTIFICATES:%=ssl/%.crt) \ SSLDIRS := ssl/client-crldir ssl/server-crldir \ ssl/root+client-crldir ssl/root+server-crldir +# Even though we in practice could get away with far fewer NSS databases, they +# are generated to mimic the setup for the OpenSSL tests in order to ensure +# we isolate the same behavior between the backends. The database name should +# contain the files included for easier test suite code reading. +NSSFILES := ssl/nss/client_ca.crt.db \ + ssl/nss/server_ca.crt.db \ + ssl/nss/root+server_ca.crt.db \ + ssl/nss/root+client_ca.crt.db \ + ssl/nss/client.crt__client.key.db \ + ssl/nss/client-revoked.crt__client-revoked.key.db \ + ssl/nss/server-cn-only.crt__server-password.key.db \ + ssl/nss/server-cn-only.crt__server-cn-only.key.db \ + ssl/nss/root.crl \ + ssl/nss/server.crl \ + ssl/nss/client.crl \ + ssl/nss/server-multiple-alt-names.crt__server-multiple-alt-names.key.db \ + ssl/nss/server-single-alt-name.crt__server-single-alt-name.key.db \ + ssl/nss/server-cn-and-alt-names.crt__server-cn-and-alt-names.key.db \ + ssl/nss/server-no-names.crt__server-no-names.key.db \ + ssl/nss/server-revoked.crt__server-revoked.key.db \ + ssl/nss/root+client.crl \ + ssl/nss/client+client_ca.crt__client.key.db \ + ssl/nss/client.crt__client-encrypted-pem.key.db \ + ssl/nss/root+server_ca.crt__server.crl.db \ + ssl/nss/root+server_ca.crt__root+server.crl.db \ + ssl/nss/root+server_ca.crt__root+server.crldir.db \ + ssl/nss/native_ca-root.db \ + ssl/nss/native_server-root.db \ + ssl/nss/native_client-root.db + # This target re-generates all the key and certificate files. Usually we just # use the ones that are committed to the tree without rebuilding them. # @@ -40,6 +70,10 @@ SSLDIRS := ssl/client-crldir ssl/server-crldir \ # sslfiles: $(SSLFILES) $(SSLDIRS) +# Generate NSS certificate databases corresponding to the OpenSSL certificates. +# This target will fail unless preceded by nssfiles-clean. +nssfiles: $(NSSFILES) + # OpenSSL requires a directory to put all generated certificates in. We don't # use this for anything, but we need a location. ssl/new_certs_dir: @@ -67,6 +101,35 @@ ssl/%_ca.crt: ssl/%_ca.key %_ca.config ssl/root_ca.crt ssl/new_certs_dir rm ssl/temp_ca.crt ssl/temp_ca_signed.crt echo "01" > ssl/$*_ca.srl +ssl/nss/%_ca.crt.db: ssl/%_ca.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n $*_ca.crt -i ssl/$*_ca.crt -t "CT,C,C" + +ssl/nss/root+server_ca.crt__server.crl.db: ssl/root+server_ca.crt ssl/nss/server.crl + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/root+server_ca.crt -i ssl/root+server_ca.crt -t "CT,C,C" + crlutil -I -i ssl/nss/server.crl -d $@ -B + +ssl/nss/root+server_ca.crt__root+server.crl.db: ssl/root+server_ca.crt ssl/nss/root.crl ssl/nss/server.crl + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/root+server_ca.crt -i ssl/root+server_ca.crt -t "CT,C,C" + crlutil -I -i ssl/nss/root.crl -d $@ -B + crlutil -I -i ssl/nss/server.crl -d $@ -B + +ssl/nss/root+server_ca.crt__root+server.crldir.db: ssl/root+server_ca.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/root+server_ca.crt -i ssl/root+server_ca.crt -t "CT,C,C" + crlutil -I -i ssl/nss/root.crl -d $@ -B + for c in $(shell ls ssl/root+server-crldir) ; do \ + echo $${c} ; \ + openssl crl -in ssl/root+server-crldir/$${c} -outform der -out ssl/nss/$${c} ; \ + crlutil -I -i ssl/nss/$${c} -d $@ -B ; \ + done + # Server certificates, signed by server CA: ssl/server-%.crt: ssl/server-%.key ssl/server_ca.crt server-%.config openssl req -new -key ssl/server-$*.key -out ssl/server-$*.csr -config server-$*.config @@ -80,6 +143,86 @@ ssl/server-ss.crt: ssl/server-cn-only.key ssl/server-cn-only.crt server-cn-only. openssl x509 -req -days 10000 -in ssl/server-ss.csr -signkey ssl/server-cn-only.key -out ssl/server-ss.crt -extensions v3_req -extfile server-cn-only.config rm ssl/server-ss.csr +# pk12util won't preserve the password when importing the password protected +# key, the password must be set on the database *before* importing it as the +# password in the pkcs12 envelope will be dropped. +ssl/nss/server-cn-only.crt__server-password.key.db: ssl/server-cn-only.crt + $(MKDIR_P) $@ + echo "secret1" > password.txt + certutil -d "sql:$@" -N -f password.txt + certutil -d "sql:$@" -A -n ssl/server-cn-only.crt -i ssl/server-cn-only.crt -t "CT,C,C" -f password.txt + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" -f password.txt + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" -f password.txt + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" -f password.txt + openssl pkcs12 -export -out ssl/nss/server-password.pfx -inkey ssl/server-password.key -in ssl/server-cn-only.crt -certfile ssl/server_ca.crt -passin 'pass:secret1' -passout 'pass:secret1' + pk12util -i ssl/nss/server-password.pfx -d "sql:$@" -W 'secret1' -K 'secret1' + +ssl/nss/server-cn-only.crt__server-cn-only.key.db: ssl/server-cn-only.crt ssl/server-cn-only.key + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/server-cn-only.crt -i ssl/server-cn-only.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/server-cn-only.pfx -inkey ssl/server-cn-only.key -in ssl/server-cn-only.crt -certfile ssl/server_ca.crt -passout pass: + pk12util -i ssl/nss/server-cn-only.pfx -d "sql:$@" -W '' + +ssl/nss/server-cn-only.crt__server-cn-only.key.crldir.db: ssl/nss/server-cn-only.crt__server-cn-only.key.db + for c in $(shell ls ssl/root+client-crldir) ; do \ + echo $${c} ; \ + openssl crl -in ssl/root+client-crldir/$${c} -outform der -out ssl/nss/$${c} ; \ + crlutil -I -i ssl/nss/$${c} -d $@ -B ; \ + done + +ssl/nss/server-multiple-alt-names.crt__server-multiple-alt-names.key.db: ssl/server-multiple-alt-names.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/server-multiple-alt-names.crt -i ssl/server-multiple-alt-names.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/server-multiple-alt-names.pfx -inkey ssl/server-multiple-alt-names.key -in ssl/server-multiple-alt-names.crt -certfile ssl/server-multiple-alt-names.crt -passout pass: + pk12util -i ssl/nss/server-multiple-alt-names.pfx -d "sql:$@" -W '' + +ssl/nss/server-single-alt-name.crt__server-single-alt-name.key.db: ssl/server-single-alt-name.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/server-single-alt-name.crt -i ssl/server-single-alt-name.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/server-single-alt-name.pfx -inkey ssl/server-single-alt-name.key -in ssl/server-single-alt-name.crt -certfile ssl/server-single-alt-name.crt -passout pass: + pk12util -i ssl/nss/server-single-alt-name.pfx -d "sql:$@" -W '' + +ssl/nss/server-cn-and-alt-names.crt__server-cn-and-alt-names.key.db: ssl/server-cn-and-alt-names.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/server-cn-and-alt-names.crt -i ssl/server-cn-and-alt-names.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/server-cn-and-alt-names.pfx -inkey ssl/server-cn-and-alt-names.key -in ssl/server-cn-and-alt-names.crt -certfile ssl/server-cn-and-alt-names.crt -passout pass: + pk12util -i ssl/nss/server-cn-and-alt-names.pfx -d $@ -W '' + +ssl/nss/server-no-names.crt__server-no-names.key.db: ssl/server-no-names.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/server-no-names.crt -i ssl/server-no-names.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/server-no-names.pfx -inkey ssl/server-no-names.key -in ssl/server-no-names.crt -certfile ssl/server-no-names.crt -passout pass: + pk12util -i ssl/nss/server-no-names.pfx -d "sql:$@" -W '' + +ssl/nss/server-revoked.crt__server-revoked.key.db: ssl/server-revoked.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/server-revoked.crt -i ssl/server-revoked.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n server_ca.crt -i ssl/server_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root_ca.crt -i ssl/root_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/server-revoked.pfx -inkey ssl/server-revoked.key -in ssl/server-revoked.crt -certfile ssl/server-revoked.crt -passout pass: + pk12util -i ssl/nss/server-revoked.pfx -d "sql:$@" -W '' + # Password-protected version of server-cn-only.key ssl/server-password.key: ssl/server-cn-only.key openssl rsa -aes256 -in $< -out $@ -passout 'pass:secret1' @@ -91,6 +234,27 @@ ssl/client.crt: ssl/client.key ssl/client_ca.crt openssl x509 -in ssl/temp.crt -out ssl/client.crt # to keep just the PEM cert rm ssl/client.csr ssl/temp.crt +# Client certificate, signed by client CA +ssl/nss/client.crt__client.key.db: ssl/client.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/client.crt -i ssl/client.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n root+client_ca.crt -i ssl/root+client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/client.pfx -inkey ssl/client.key -in ssl/client.crt -certfile ssl/client_ca.crt -passout pass: + pk12util -i ssl/nss/client.pfx -d "sql:$@" -W '' + +# Client certificate with encrypted key, signed by client CA +ssl/nss/client.crt__client-encrypted-pem.key.db: ssl/client.crt + $(MKDIR_P) $@ + echo 'dUmmyP^#+' > $@.pass + certutil -d "sql:$@" -N -f $@.pass + certutil -d "sql:$@" -A -f $@.pass -n ssl/client.crt -i ssl/client.crt -t "CT,C,C" + certutil -d "sql:$@" -A -f $@.pass -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -f $@.pass -n root+server_ca.crt -i ssl/root+server_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/client-encrypted-pem.pfx -inkey ssl/client-encrypted-pem.key -in ssl/client.crt -certfile ssl/client_ca.crt -passin pass:'dUmmyP^#+' -passout pass:'dUmmyP^#+' + pk12util -i ssl/nss/client-encrypted-pem.pfx -d "sql:$@" -W 'dUmmyP^#+' -k $@.pass + # Another client certificate, signed by the client CA. This one is revoked. ssl/client-revoked.crt: ssl/client-revoked.key ssl/client_ca.crt client.config openssl req -new -key ssl/client-revoked.key -out ssl/client-revoked.csr -config client.config @@ -98,6 +262,14 @@ ssl/client-revoked.crt: ssl/client-revoked.key ssl/client_ca.crt client.config openssl x509 -in ssl/temp.crt -out ssl/client-revoked.crt # to keep just the PEM cert rm ssl/client-revoked.csr ssl/temp.crt +ssl/nss/client-revoked.crt__client-revoked.key.db: ssl/client-revoked.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/client-revoked.crt -i ssl/client-revoked.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n client_ca.crt -i ssl/client_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/client-revoked.pfx -inkey ssl/client-revoked.key -in ssl/client-revoked.crt -certfile ssl/client_ca.crt -passout pass: + pk12util -i ssl/nss/client-revoked.pfx -d "sql:$@" -W '' + # Convert the key to DER, to test our behaviour there too ssl/client-der.key: ssl/client.key openssl rsa -in ssl/client.key -outform DER -out ssl/client-der.key @@ -130,19 +302,40 @@ ssl/root+client_ca.crt: ssl/root_ca.crt ssl/client_ca.crt ssl/client+client_ca.crt: ssl/client.crt ssl/client_ca.crt cat $^ > $@ +# Client certificate, signed by client CA +ssl/nss/client+client_ca.crt__client.key.db: ssl/client+client_ca.crt + $(MKDIR_P) $@ + certutil -d "sql:$@" -N --empty-password + certutil -d "sql:$@" -A -n ssl/client+client_ca.crt -i ssl/client+client_ca.crt -t "CT,C,C" + certutil -d "sql:$@" -A -n ssl/root+server_ca.crt -i ssl/root+server_ca.crt -t "CT,C,C" + openssl pkcs12 -export -out ssl/nss/client.pfx -inkey ssl/client.key -in ssl/client.crt -certfile ssl/client_ca.crt -passout pass: + pk12util -i ssl/nss/client.pfx -d "sql:$@" -W '' + #### CRLs ssl/client.crl: ssl/client-revoked.crt openssl ca -config cas.config -name client_ca -revoke ssl/client-revoked.crt openssl ca -config cas.config -name client_ca -gencrl -out ssl/client.crl +ssl/nss/client.crl: ssl/client.crl + openssl crl -in $^ -outform der -out $@ + ssl/server.crl: ssl/server-revoked.crt openssl ca -config cas.config -name server_ca -revoke ssl/server-revoked.crt openssl ca -config cas.config -name server_ca -gencrl -out ssl/server.crl +ssl/nss/server.crl: ssl/server.crl + openssl crl -in $^ -outform der -out $@ + ssl/root.crl: ssl/root_ca.crt openssl ca -config cas.config -name root_ca -gencrl -out ssl/root.crl +ssl/nss/root.crl: ssl/root.crl + openssl crl -in $^ -outform der -out $@ + +ssl/nss/root+client.crl: ssl/root+client.crl + openssl crl -in $^ -outform der -out $@ + # If a CRL is used, OpenSSL requires a CRL file for *all* the CAs in the # chain, even if some of them are empty. ssl/root+server.crl: ssl/root.crl ssl/server.crl @@ -168,14 +361,65 @@ ssl/client-crldir: ssl/client.crl mkdir ssl/client-crldir cp ssl/client.crl ssl/client-crldir/`openssl crl -hash -noout -in ssl/client.crl`.r0 +#### NSS specific certificates and keys + +ssl/nss/native_ca-%.db: + $(MKDIR_P) ssl/nss/native_ca-$*.db + certutil -N -d "sql:ssl/nss/native_ca-$*.db/" --empty-password + echo y > nss_ca_params.txt + echo 10 >> nss_ca_params.txt + echo y >> nss_ca_params.txt + cat nss_ca_params.txt | certutil -S -d "sql:ssl/nss/native_ca-$*.db/" -n ca-$* \ + -s "CN=Test CA for PostgreSQL SSL regression tests,OU=PostgreSQL test suite" \ + -x -k rsa -g 2048 -m 5432 -t CTu,CTu,CTu \ + --keyUsage certSigning -2 --nsCertType sslCA,smimeCA,objectSigningCA \ + -z Makefile -Z SHA256 + rm nss_ca_params.txt + +ssl/nss/native_ca-%.pem: ssl/nss/native_ca-%.db + certutil -L -d "sql:ssl/nss/native_ca-$*.db/" -n ca-$* -a > ssl/nss/native_ca-$*.pem + +# Create and sign a server certificate +ssl/nss/native_server-%.db: ssl/nss/native_ca-%.pem + $(MKDIR_P) ssl/nss/native_server-$*.db + certutil -N -d "sql:ssl/nss/native_server-$*.db/" --empty-password + certutil -R -d "sql:ssl/nss/native_server-$*.db/" \ + -s "CN=common-name.pg-ssltest.test,OU=PostgreSQL test suite" \ + -o ssl/nss/native_server-$*.csr -g 2048 -Z SHA256 -z Makefile + echo 1 > nss_server_params.txt + echo 9 >> nss_server_params.txt + cat nss_server_params.txt | certutil -C -d "sql:ssl/nss/native_ca-$*.db/" -c ca-root -i ssl/nss/native_server-$*.csr \ + -o ssl/nss/native_server_$*.der -m 5433 --keyUsage dataEncipherment,digitalSignature,keyEncipherment \ + --nsCertType sslServer --certVersion 1 -Z SHA256 + certutil -A -d "sql:ssl/nss/native_server-$*.db/" -n ca-$* -t CTu,CTu,CTu -a -i ssl/nss/native_ca-$*.pem + certutil -A -d "sql:ssl/nss/native_server-$*.db/" -n ssl/native_server-$*.crt -t CTu,CTu,CTu -i ssl/nss/native_server_$*.der + rm nss_server_params.txt + +# Create and sign a client certificate +ssl/nss/native_client-%.db: ssl/nss/native_ca-%.pem + $(MKDIR_P) ssl/nss/native_client-$*.db + certutil -N -d "sql:ssl/nss/native_client-$*.db/" --empty-password + certutil -R -d "sql:ssl/nss/native_client-$*.db/" -s "CN=ssltestuser,OU=PostgreSQL test suite" \ + -o ssl/nss/native_client-$*.csr -g 2048 -Z SHA256 -z Makefile + certutil -C -d "sql:ssl/nss/native_ca-$*.db/" -c ca-$* -i ssl/nss/native_client-$*.csr -o ssl/nss/native_client-$*.der \ + -m 5434 --keyUsage keyEncipherment,dataEncipherment,digitalSignature --nsCertType sslClient \ + --certVersion 1 -Z SHA256 + certutil -A -d "sql:ssl/nss/native_client-$*.db" -n ca-$* -t CTu,CTu,CTu -a -i ssl/nss/native_ca-$*.pem + certutil -A -d "sql:ssl/nss/native_client-$*.db" -n native_client-$* -t CTu,CTu,CTu -i ssl/nss/native_client-$*.der + .PHONY: sslfiles-clean sslfiles-clean: rm -f $(SSLFILES) ssl/client_ca.srl ssl/server_ca.srl ssl/client_ca-certindex* ssl/server_ca-certindex* ssl/root_ca-certindex* ssl/root_ca.srl ssl/temp_ca.crt ssl/temp_ca_signed.crt rm -rf $(SSLDIRS) +.PHONY: nssfiles-clean +nssfiles-clean: + rm -rf ssl/nss + clean distclean maintainer-clean: rm -rf tmp_check rm -rf ssl/*.old ssl/new_certs_dir ssl/client*_tmp.key + rm -rf ssl/nss # Doesn't depend on $(SSLFILES) because we don't rebuild them by default check: diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index 6124d9ea23..a5513db71f 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -9,13 +9,22 @@ use lib $FindBin::RealBin; use SSL::Server; -if ($ENV{with_ssl} ne 'openssl') +my $openssl; +my $nss; + +if ($ENV{with_ssl} eq 'openssl') +{ + $openssl = 1; + plan tests => 103; +} +elsif ($ENV{with_ssl} eq 'nss') { - plan skip_all => 'OpenSSL not supported by this build'; + $nss = 1; + plan tests => 104; } else { - plan tests => 100; + plan skip_all => 'SSL not supported by this build'; } #### Some configuration @@ -51,19 +60,70 @@ configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR, note "testing password-protected keys"; -set_server_cert($node, 'server-cn-only', 'root+client_ca', - 'server-password', 'echo wrongpassword'); -command_fails( - [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ], - 'restart fails with password-protected key file with wrong password'); -$node->_update_pid(0); +# Since the passphrase callbacks operate at different stages in OpenSSL and +# NSS we have two separate blocks for them +SKIP: +{ + skip "Certificate passphrases aren't checked on server restart in NSS", 2 + if ($nss); + + switch_server_cert($node, + certfile => 'server-cn-only', + cafile => 'root+client_ca', + keyfile => 'server-password', + nssdatabase => 'server-cn-only.crt__server-password.key.db', + passphrase_cmd => 'echo wrongpassword', + restart => 'no' ); + + command_fails( + [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ], + 'restart fails with password-protected key file with wrong password'); + $node->_update_pid(0); + + switch_server_cert($node, + certfile => 'server-cn-only', + cafile => 'root+client_ca', + keyfile => 'server-password', + nssdatabase => 'server-cn-only.crt__server-password.key.db', + passphrase_cmd => 'echo secret1', + restart => 'no'); + + command_ok( + [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ], + 'restart succeeds with password-protected key file'); + $node->_update_pid(1); +} -set_server_cert($node, 'server-cn-only', 'root+client_ca', - 'server-password', 'echo secret1'); -command_ok( - [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ], - 'restart succeeds with password-protected key file'); -$node->_update_pid(1); +SKIP: +{ + skip "Certificate passphrases are checked on connection in NSS", 3 + if ($openssl); + + switch_server_cert($node, + certfile => 'server-cn-only', + cafile => 'root+client_ca', + keyfile => 'server-password', + nssdatabase => 'server-cn-only.crt__server-password.key.db', + passphrase_cmd => 'echo wrongpassword'); + + test_connect_fails( + "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test", + "sslrootcert=invalid sslmode=require", + qr/\QSSL error\E/, + "connect to server with incorrect key password configured"); + + switch_server_cert($node, + certfile => 'server-cn-only', + cafile => 'root+client_ca', + keyfile => 'server-password', + nssdatabase => 'server-cn-only.crt__server-password.key.db', + passphrase_cmd => 'echo secret1'); + + test_connect_ok( + "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test", + "sslrootcert=invalid sslmode=require", + "connect to server with correct key password configured"); +} # Test compatibility of SSL protocols. # TLSv1.1 is lower than TLSv1.2, so it won't work. @@ -91,7 +151,7 @@ command_ok( note "running client tests"; -switch_server_cert($node, 'server-cn-only'); +switch_server_cert($node, certfile => 'server-cn-only', nssdatabase => 'server-cn-only.crt__server-cn-only.key.db'); $common_connstr = "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test"; @@ -111,95 +171,118 @@ test_connect_ok( test_connect_fails( $common_connstr, "sslrootcert=invalid sslmode=verify-ca", - qr/root certificate file "invalid" does not exist/, + qr/root certificate file "invalid" does not exist|Peer's certificate issuer has been marked as not trusted by the user/, "connect without server root cert sslmode=verify-ca"); test_connect_fails( $common_connstr, "sslrootcert=invalid sslmode=verify-full", - qr/root certificate file "invalid" does not exist/, + qr/root certificate file "invalid" does not exist|Peer's certificate issuer has been marked as not trusted by the user/, "connect without server root cert sslmode=verify-full"); # Try with wrong root cert, should fail. (We're using the client CA as the # root, but the server's key is signed by the server CA.) -test_connect_fails($common_connstr, - "sslrootcert=ssl/client_ca.crt sslmode=require", - qr/SSL error/, "connect with wrong server root cert sslmode=require"); -test_connect_fails($common_connstr, - "sslrootcert=ssl/client_ca.crt sslmode=verify-ca", - qr/SSL error/, "connect with wrong server root cert sslmode=verify-ca"); -test_connect_fails($common_connstr, - "sslrootcert=ssl/client_ca.crt sslmode=verify-full", - qr/SSL error/, "connect with wrong server root cert sslmode=verify-full"); - -# Try with just the server CA's cert. This fails because the root file -# must contain the whole chain up to the root CA. -test_connect_fails($common_connstr, - "sslrootcert=ssl/server_ca.crt sslmode=verify-ca", - qr/SSL error/, "connect with server CA cert, without root CA"); +test_connect_fails( + $common_connstr, + "sslrootcert=ssl/client_ca.crt sslmode=require cert_database=ssl/nss/client_ca.crt.db", + qr/SSL error/, + "connect with wrong server root cert sslmode=require"); +test_connect_fails( + $common_connstr, + "sslrootcert=ssl/client_ca.crt sslmode=verify-ca cert_database=ssl/nss/client_ca.crt.db", + qr/SSL error/, + "connect with wrong server root cert sslmode=verify-ca"); +test_connect_fails( + $common_connstr, + "sslrootcert=ssl/client_ca.crt sslmode=verify-full cert_database=ssl/nss/client_ca.crt.db", + qr/SSL error/, + "connect with wrong server root cert sslmode=verify-full"); + +SKIP: +{ + # NSS supports partial chain validation, so this test doesn't work there. + # This is similar to the OpenSSL option X509_V_FLAG_PARTIAL_CHAIN which + # we don't allow. + skip "NSS support partial chain validation", 2 if ($nss); + # Try with just the server CA's cert. This fails because the root file + # must contain the whole chain up to the root CA. + test_connect_fails($common_connstr, + "sslrootcert=ssl/server_ca.crt sslmode=verify-ca", + qr/SSL error/, "connect with server CA cert, without root CA"); +} # And finally, with the correct root cert. test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=require", + "sslrootcert=ssl/root+server_ca.crt sslmode=require cert_database=ssl/nss/root+server_ca.crt.db", "connect with correct server CA cert file sslmode=require"); test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca cert_database=ssl/nss/root+server_ca.crt.db", "connect with correct server CA cert file sslmode=verify-ca"); test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-full", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-full cert_database=ssl/nss/root+server_ca.crt.db", "connect with correct server CA cert file sslmode=verify-full"); -# Test with cert root file that contains two certificates. The client should -# be able to pick the right one, regardless of the order in the file. -test_connect_ok( - $common_connstr, - "sslrootcert=ssl/both-cas-1.crt sslmode=verify-ca", - "cert root file that contains two certificates, order 1"); -test_connect_ok( - $common_connstr, - "sslrootcert=ssl/both-cas-2.crt sslmode=verify-ca", - "cert root file that contains two certificates, order 2"); +SKIP: +{ + skip "CA ordering is irrelevant in NSS databases", 2 if ($nss); + + # Test with cert root file that contains two certificates. The client should + # be able to pick the right one, regardless of the order in the file. + test_connect_ok( + $common_connstr, + "sslrootcert=ssl/both-cas-1.crt sslmode=verify-ca", + "cert root file that contains two certificates, order 1"); + # How about import the both-file into a database? + test_connect_ok( + $common_connstr, + "sslrootcert=ssl/both-cas-2.crt sslmode=verify-ca", + "cert root file that contains two certificates, order 2"); +} # CRL tests # Invalid CRL filename is the same as no CRL, succeeds test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid cert_database=ssl/nss/root+server_ca.crt.db", "sslcrl option with invalid file name"); -# A CRL belonging to a different CA is not accepted, fails -test_connect_fails( - $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl", - qr/SSL error/, - "CRL belonging to a different CA"); +SKIP: +{ + skip "CRL's are verified when adding to NSS database", 4 if ($nss); + # A CRL belonging to a different CA is not accepted, fails + test_connect_fails( + $common_connstr, + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl", + qr/SSL error/, + "CRL belonging to a different CA"); -# The same for CRL directory -test_connect_fails( - $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/client-crldir", - qr/SSL error/, - "directory CRL belonging to a different CA"); + # The same for CRL directory + test_connect_fails( + $common_connstr, + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/client-crldir", + qr/SSL error/, + "directory CRL belonging to a different CA"); +} # With the correct CRL, succeeds (this cert is not revoked) test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl cert_database=ssl/nss/root+server_ca.crt__root+server.crl.db", "CRL with a non-revoked cert"); # The same for CRL directory test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/root+server-crldir", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/root+server-crldir cert_database=ssl/nss/root+server_ca.crt__root+server.crldir.db", "directory CRL with a non-revoked cert"); # Check that connecting with verify-full fails, when the hostname doesn't # match the hostname in the server's certificate. $common_connstr = - "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR"; + "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/root+server_ca.crt.db"; test_connect_ok( $common_connstr, @@ -212,14 +295,14 @@ test_connect_ok( test_connect_fails( $common_connstr, "sslmode=verify-full host=wronghost.test", - qr/\Qserver certificate for "common-name.pg-ssltest.test" does not match host name "wronghost.test"\E/, + qr/\Qserver certificate for "common-name.pg-ssltest.test" does not match host name "wronghost.test"\E|requested domain name does not match the server's certificate/, "mismatch between host name and server certificate sslmode=verify-full"); # Test Subject Alternative Names. -switch_server_cert($node, 'server-multiple-alt-names'); +switch_server_cert($node, certfile => 'server-multiple-alt-names', nssdatabase => 'server-multiple-alt-names.crt__server-multiple-alt-names.key.db'); $common_connstr = - "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full"; + "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full cert_database=ssl/nss/root+server_ca.crt.db"; test_connect_ok( $common_connstr, @@ -237,20 +320,20 @@ test_connect_ok( test_connect_fails( $common_connstr, "host=wronghost.alt-name.pg-ssltest.test", - qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "wronghost.alt-name.pg-ssltest.test"\E/, + qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "wronghost.alt-name.pg-ssltest.test"\E|requested domain name does not match the server's certificate/, "host name not matching with X.509 Subject Alternative Names"); test_connect_fails( $common_connstr, "host=deep.subdomain.wildcard.pg-ssltest.test", - qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/, + qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E|requested domain name does not match the server's certificate/, "host name not matching with X.509 Subject Alternative Names wildcard"); # Test certificate with a single Subject Alternative Name. (this gives a # slightly different error message, that's all) -switch_server_cert($node, 'server-single-alt-name'); +switch_server_cert($node, certfile => 'server-single-alt-name', nssdatabase => 'server-single-alt-name.crt__server-single-alt-name.key.db'); $common_connstr = - "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full"; + "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full cert_database=ssl/nss/root+server_ca.crt.db"; test_connect_ok( $common_connstr, @@ -260,21 +343,21 @@ test_connect_ok( test_connect_fails( $common_connstr, "host=wronghost.alt-name.pg-ssltest.test", - qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "wronghost.alt-name.pg-ssltest.test"\E/, + qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "wronghost.alt-name.pg-ssltest.test"\E|requested domain name does not match the server's certificate/, "host name not matching with a single X.509 Subject Alternative Name"); test_connect_fails( $common_connstr, "host=deep.subdomain.wildcard.pg-ssltest.test", - qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/, + qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E|requested domain name does not match the server's certificate/, "host name not matching with a single X.509 Subject Alternative Name wildcard" ); # Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN # should be ignored when the certificate has both. -switch_server_cert($node, 'server-cn-and-alt-names'); +switch_server_cert($node, certfile => 'server-cn-and-alt-names', nssdatabase => 'server-cn-and-alt-names.crt__server-cn-and-alt-names.key.db'); $common_connstr = - "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full"; + "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full cert_database=ssl/nss/root+server_ca.crt.db"; test_connect_ok( $common_connstr, @@ -287,14 +370,14 @@ test_connect_ok( test_connect_fails( $common_connstr, "host=common-name.pg-ssltest.test", - qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 1 other name) does not match host name "common-name.pg-ssltest.test"\E/, + qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 1 other name) does not match host name "common-name.pg-ssltest.test"\E|requested domain name does not match the server's certificate/, "certificate with both a CN and SANs ignores CN"); # Finally, test a server certificate that has no CN or SANs. Of course, that's # not a very sensible certificate, but libpq should handle it gracefully. -switch_server_cert($node, 'server-no-names'); +switch_server_cert($node, certfile => 'server-no-names', nssdatabase => 'server-no-names.crt__server-no-names.key.db'); $common_connstr = - "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR"; + "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/root+server_ca.crt.db"; test_connect_ok( $common_connstr, @@ -303,11 +386,11 @@ test_connect_ok( test_connect_fails( $common_connstr, "sslmode=verify-full host=common-name.pg-ssltest.test", - qr/could not get server's host name from server certificate/, + qr/could not get server's host name from server certificate|requested domain name does not match the server's certificate./, "server certificate without CN or SANs sslmode=verify-full"); # Test that the CRL works -switch_server_cert($node, 'server-revoked'); +switch_server_cert($node, certfile => 'server-revoked', nssdatabase => 'server-revoked.crt__server-revoked.key.db'); $common_connstr = "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test"; @@ -315,16 +398,16 @@ $common_connstr = # Without the CRL, succeeds. With it, fails. test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca cert_database=ssl/nss/root+server_ca.crt.db", "connects without client-side CRL"); test_connect_fails( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/server.crl cert_database=ssl/nss/root+server_ca.crt__server.crl.db", qr/SSL error/, "does not connect with client-side CRL file"); test_connect_fails( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/root+server-crldir", + "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrldir=ssl/root+server-crldir cert_database=ssl/nss/root+server_ca.crt__root+server.crldir.db", qr/SSL error/, "does not connect with client-side CRL directory"); @@ -345,32 +428,50 @@ command_like( # Test min/max SSL protocol versions. test_connect_ok( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_min_protocol_version=TLSv1.2 ssl_max_protocol_version=TLSv1.2", + "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_min_protocol_version=TLSv1.2 ssl_max_protocol_version=TLSv1.2 cert_database=ssl/nss/root+server_ca.crt.db", "connection success with correct range of TLS protocol versions"); test_connect_fails( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_min_protocol_version=TLSv1.2 ssl_max_protocol_version=TLSv1.1", + "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_min_protocol_version=TLSv1.2 ssl_max_protocol_version=TLSv1.1 cert_database=ssl/nss/root+server_ca.crt.db", qr/invalid SSL protocol version range/, "connection failure with incorrect range of TLS protocol versions"); test_connect_fails( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_min_protocol_version=incorrect_tls", + "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_min_protocol_version=incorrect_tls cert_database=ssl/nss/root+server_ca.crt.db", qr/invalid ssl_min_protocol_version value/, "connection failure with an incorrect SSL protocol minimum bound"); test_connect_fails( $common_connstr, - "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_max_protocol_version=incorrect_tls", + "sslrootcert=ssl/root+server_ca.crt sslmode=require ssl_max_protocol_version=incorrect_tls cert_database=ssl/nss/root+server_ca.crt.db", qr/invalid ssl_max_protocol_version value/, "connection failure with an incorrect SSL protocol maximum bound"); +# tests of NSS generated certificates/keys +SKIP: +{ + skip "NSS specific tests", 1 if ($openssl); + + switch_server_cert($node, certfile => 'native_server-root', cafile => 'native_ca-root', nssdatabase => 'native_server-root.db'); + $common_connstr = + "user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test"; + + test_connect_ok( + $common_connstr, + "sslmode=require user=ssltestuser", + "NSS generated certificates" + ); +} + ### Server-side tests. ### ### Test certificate authorization. +switch_server_cert($node, certfile => 'server-revoked', nssdatabase => 'server-revoked.crt__server-revoked.key.db'); + note "running server tests"; $common_connstr = - "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR"; + "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/client.crt__client.key.db"; # no client cert test_connect_fails( @@ -386,32 +487,43 @@ test_connect_ok( "certificate authorization succeeds with correct client cert in PEM format" ); -# correct client cert in unencrypted DER -test_connect_ok( - $common_connstr, - "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-der_tmp.key", - "certificate authorization succeeds with correct client cert in DER format" -); +$common_connstr = + "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR"; + +SKIP: +{ + skip "NSS database not implemented in the Makefile", 1 if ($nss); + # correct client cert in unencrypted DER + test_connect_ok( + $common_connstr, + "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-der_tmp.key", + "certificate authorization succeeds with correct client cert in DER format" + ); +} # correct client cert in encrypted PEM test_connect_ok( $common_connstr, - "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-encrypted-pem_tmp.key sslpassword='dUmmyP^#+'", + "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-encrypted-pem_tmp.key sslpassword='dUmmyP^#+' cert_database=ssl/nss/client.crt__client-encrypted-pem.key.db", "certificate authorization succeeds with correct client cert in encrypted PEM format" ); -# correct client cert in encrypted DER -test_connect_ok( - $common_connstr, - "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-encrypted-der_tmp.key sslpassword='dUmmyP^#+'", - "certificate authorization succeeds with correct client cert in encrypted DER format" -); +SKIP: +{ + skip "NSS database not implemented in the Makefile", 1 if ($nss); + # correct client cert in encrypted DER + test_connect_ok( + $common_connstr, + "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-encrypted-der_tmp.key sslpassword='dUmmyP^#+'", + "certificate authorization succeeds with correct client cert in encrypted DER format" + ); +} # correct client cert in encrypted PEM with wrong password test_connect_fails( $common_connstr, - "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-encrypted-pem_tmp.key sslpassword='wrong'", - qr!\Qprivate key file "ssl/client-encrypted-pem_tmp.key": bad decrypt\E!, + "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client-encrypted-pem_tmp.key sslpassword='wrong' cert_database=ssl/nss/client.crt__client-encrypted-pem.key.db", + qr!connection requires a valid client certificate|\Qprivate key file "ssl/client-encrypted-pem_tmp.key": bad decrypt\E!, "certificate authorization fails with correct client cert and wrong password in encrypted PEM format" ); @@ -451,18 +563,19 @@ command_like( '-P', 'null=_null_', '-d', - "$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key", + "$common_connstr user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key cert_database=ssl/nss/client.crt__client.key.db", '-c', "SELECT * FROM pg_stat_ssl WHERE pid = pg_backend_pid()" ], qr{^pid,ssl,version,cipher,bits,client_dn,client_serial,issuer_dn\r?\n - ^\d+,t,TLSv[\d.]+,[\w-]+,\d+,/CN=ssltestuser,1,\Q/CN=Test CA for PostgreSQL SSL regression test client certs\E\r?$}mx, + ^\d+,t,TLSv[\d.]+,[\w-]+,\d+,/?CN=ssltestuser,1,/?\QCN=Test CA for PostgreSQL SSL regression test client certs\E\r?$}mx, 'pg_stat_ssl with client certificate'); # client key with wrong permissions SKIP: { skip "Permissions check not enforced on Windows", 2 if ($windows_os); + skip "Key not on filesystem with NSS", 2 if ($nss); test_connect_fails( $common_connstr, @@ -475,10 +588,13 @@ SKIP: test_connect_fails( $common_connstr, "user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key", - qr/certificate authentication failed for user "anotheruser"/, + qr/unable to verify certificate|certificate authentication failed for user "anotheruser"/, "certificate authorization fails with client cert belonging to another user" ); +$common_connstr = + "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/client-revoked.crt__client-revoked.key.db"; + # revoked client cert test_connect_fails( $common_connstr, @@ -490,7 +606,7 @@ test_connect_fails( # works, iff username matches Common Name # fails, iff username doesn't match Common Name. $common_connstr = - "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=verifydb hostaddr=$SERVERHOSTADDR"; + "sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=verifydb hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/client.crt__client.key.db"; test_connect_ok( $common_connstr, @@ -514,24 +630,28 @@ test_connect_ok( ); # intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file -switch_server_cert($node, 'server-cn-only', 'root_ca'); +switch_server_cert($node, certfile => 'server-cn-only', cafile => 'root_ca', nssdatabase => 'server-cn-only.crt__server-cn-only.key.db'); $common_connstr = - "user=ssltestuser dbname=certdb sslkey=ssl/client_tmp.key sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR"; + "user=ssltestuser dbname=certdb sslkey=ssl/client_tmp.key sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/client+client_ca.crt__client.key.db"; test_connect_ok( $common_connstr, "sslmode=require sslcert=ssl/client+client_ca.crt", "intermediate client certificate is provided by client"); -test_connect_fails($common_connstr, "sslmode=require sslcert=ssl/client.crt", - qr/SSL error/, "intermediate client certificate is missing"); + +test_connect_fails( + $common_connstr, + "sslmode=require sslcert=ssl/client.crt", + qr/connection requires a valid client certificate|SSL error/, + "intermediate client certificate is missing"); # test server-side CRL directory -switch_server_cert($node, 'server-cn-only', undef, undef, 'root+client-crldir'); +switch_server_cert($node, certfile => 'server-cn-only', crldir => 'root+client-crldir', nssdatabase => 'server-cn-only.crt__server-cn-only.key.crldir.db'); # revoked client cert test_connect_fails( $common_connstr, - "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked_tmp.key", + "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked_tmp.key cert_database=ssl/nss/client-revoked.crt__client-revoked.key.db", qr/SSL error/, "certificate authorization fails with revoked client cert with server-side CRL directory"); diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl index 3383b3e531..801c945e88 100644 --- a/src/test/ssl/t/002_scram.pl +++ b/src/test/ssl/t/002_scram.pl @@ -13,7 +13,26 @@ use lib $FindBin::RealBin; use SSL::Server; -if ($ENV{with_ssl} ne 'openssl') +my $openssl; +my $nss; + +my $supports_tls_server_end_point; + +if ($ENV{with_ssl} eq 'openssl') +{ + $openssl = 1; + # Determine whether build supports tls-server-end-point. + $supports_tls_server_end_point = + check_pg_config("#define HAVE_X509_GET_SIGNATURE_NID 1"); + plan tests => $supports_tls_server_end_point ? 9 : 10; +} +elsif ($ENV{with_ssl} eq 'nss') +{ + $nss = 1; + $supports_tls_server_end_point = 1; + plan tests => 9; +} +else { plan skip_all => 'SSL not supported by this build'; } @@ -23,12 +42,6 @@ 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'; -# Determine whether build supports tls-server-end-point. -my $supports_tls_server_end_point = - check_pg_config("#define HAVE_X509_GET_SIGNATURE_NID 1"); - -my $number_of_tests = $supports_tls_server_end_point ? 9 : 10; - # Allocation of base connection string shared among multiple tests. my $common_connstr; @@ -47,7 +60,7 @@ $node->start; # Configure server for SSL connections, with password handling. configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR, "scram-sha-256", "pass", "scram-sha-256"); -switch_server_cert($node, 'server-cn-only'); +switch_server_cert($node, certfile => 'server-cn-only', nssdatabase => 'server-cn-only.crt__server-cn-only.key.db'); $ENV{PGPASSWORD} = "pass"; $common_connstr = "dbname=trustdb sslmode=require sslcert=invalid sslrootcert=invalid hostaddr=$SERVERHOSTADDR"; @@ -97,7 +110,7 @@ my $client_tmp_key = "ssl/client_scram_tmp.key"; copy("ssl/client.key", $client_tmp_key); chmod 0600, $client_tmp_key; test_connect_fails( - "sslcert=ssl/client.crt sslkey=$client_tmp_key sslrootcert=invalid hostaddr=$SERVERHOSTADDR", + "sslcert=ssl/client.crt sslkey=$client_tmp_key sslrootcert=invalid hostaddr=$SERVERHOSTADDR cert_database=ssl/nss/client.crt__client.key.db", "dbname=certdb user=ssltestuser channel_binding=require", qr/channel binding required, but server authenticated client without channel binding/, "Cert authentication and channel_binding=require"); diff --git a/src/test/ssl/t/SSL/Backend/NSS.pm b/src/test/ssl/t/SSL/Backend/NSS.pm new file mode 100644 index 0000000000..20633fe41b --- /dev/null +++ b/src/test/ssl/t/SSL/Backend/NSS.pm @@ -0,0 +1,61 @@ +package SSL::Backend::NSS; + +use strict; +use warnings; +use Exporter; + +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(get_new_nss_backend); + +sub new +{ + my ($class) = @_; + + my $self = { _library => 'NSS' }; + + bless $self, $class; + + return $self; +} + +sub get_new_nss_backend +{ + my $class = 'SSL::Backend::NSS'; + + return $class->new(); +} + +sub init +{ + # Make sure the certificate databases are in place? +} + +sub get_library +{ + my ($self) = @_; + + return $self->{_library}; +} + +sub set_server_cert +{ + my $self = $_[0]; + my $params = $_[1]; + + $params->{cafile} = 'root+client_ca' unless defined $params->{cafile}; + + my $sslconf = + "ssl_ca_file='$params->{cafile}.crt'\n" + . "ssl_cert_file='ssl/$params->{certfile}.crt'\n" + . "ssl_crl_file=''\n" + . "ssl_database='nss/$params->{nssdatabase}'\n"; + + return $sslconf; +} + +sub cleanup +{ + # Something? +} + +1; diff --git a/src/test/ssl/t/SSL/Backend/OpenSSL.pm b/src/test/ssl/t/SSL/Backend/OpenSSL.pm index 62b11b7632..f74caf67fe 100644 --- a/src/test/ssl/t/SSL/Backend/OpenSSL.pm +++ b/src/test/ssl/t/SSL/Backend/OpenSSL.pm @@ -71,16 +71,19 @@ sub init # the server so that the configuration takes effect. sub set_server_cert { - my $self = $_[0]; - my $certfile = $_[1]; - my $cafile = $_[2] || "root+client_ca"; - my $keyfile = $_[3] || $certfile; + my $self = $_[0]; + my $params = $_[1]; + + $params->{cafile} = 'root+client_ca' unless defined $params->{cafile}; + $params->{crlfile} = 'root+client.crl' unless defined $params->{crlfile}; + $params->{keyfile} = $params->{certfile} unless defined $params->{keyfile}; my $sslconf = - "ssl_ca_file='$cafile.crt'\n" - . "ssl_cert_file='$certfile.crt'\n" - . "ssl_key_file='$keyfile.key'\n" - . "ssl_crl_file='root+client.crl'\n"; + "ssl_ca_file='$params->{cafile}.crt'\n" + . "ssl_cert_file='$params->{certfile}.crt'\n" + . "ssl_key_file='$params->{keyfile}.key'\n" + . "ssl_crl_file='$params->{crlfile}'\n"; + $sslconf .= "ssl_crl_dir='$params->{crldir}'\n" if defined $params->{crldir}; return $sslconf; } diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm index e59554f85b..da25645d25 100644 --- a/src/test/ssl/t/SSL/Server.pm +++ b/src/test/ssl/t/SSL/Server.pm @@ -55,7 +55,6 @@ elsif ($ENV{with_ssl} eq 'nss') use Exporter 'import'; our @EXPORT = qw( configure_test_server_for_ssl - set_server_cert switch_server_cert test_connect_fails test_connect_ok @@ -202,46 +201,25 @@ sub cleanup $backend->cleanup(); } -# Change the configuration to use given server cert file, -sub set_server_cert +# Change the configuration to use the given set of certificate, key, ca and +# CRL, and potentially reload the configuration by restarting the server so +# that the configuration takes effect. Restarting is the default, passing +# restart => 'no' opts out of it leaving the server running. +sub switch_server_cert { - my $node = $_[0]; - my $certfile = $_[1]; - my $cafile = $_[2] || "root+client_ca"; - my $keyfile = $_[3] || ''; - my $pwcmd = $_[4] || ''; - my $crlfile = "root+client.crl"; - my $crldir; - my $pgdata = $node->data_dir; - - $keyfile = $certfile if $keyfile eq ''; - - # defaults to use crl file - if (defined $_[3] || defined $_[4]) - { - $crlfile = $_[3]; - $crldir = $_[4]; - } + my $node = shift; + my %params = @_; + my $pgdata = $node->data_dir; open my $sslconf, '>', "$pgdata/sslconfig.conf"; print $sslconf "ssl=on\n"; - print $sslconf $backend->set_server_cert($certfile, $cafile, $keyfile); - print $sslconf "ssl_crl_file='$crlfile'\n" if defined $crlfile; - print $sslconf "ssl_crl_dir='$crldir'\n" if defined $crldir; - print $sslconf "ssl_passphrase_command='$pwcmd'\n" - unless $pwcmd eq ''; + print $sslconf $backend->set_server_cert(\%params); + print $sslconf "ssl_passphrase_command='" . $params{passphrase_cmd} . "'\n" + if defined $params{passphrase_cmd}; close $sslconf; - return; -} -# Change the configuration to use given server cert file, and reload -# the server so that the configuration takes effect. -# Takes the same arguments as set_server_cert, which it calls to do that -# piece of the work. -sub switch_server_cert -{ - my $node = $_[0]; - set_server_cert(@_); + return if (defined($params{restart}) && $params{restart} eq 'no'); + $node->restart; return; } -- 2.21.1 (Apple Git-122.3)