SSL regression test suite

Started by Heikki Linnakangasover 11 years ago11 messages
#1Heikki Linnakangas
hlinnakangas@vmware.com
1 attachment(s)

While working on the SSL refactoring patch, it struck me that we don't
have any regression tests for SSL support. A suite to test all the
different sslmodes etc. is essential before we can start implementing
alternatives to OpenSSL.

Now that we use TAP for testing client tools, I think we can use that to
test various SSL options too. I came up with the attached. Comments?

It currently assumes that the client's and the server's hostnames are
"postgres-client.test" and "postgres-server.test", respectively. That
makes it a bit tricky to run on a single systme. The README includes
instructions; basically you need to set up an additional loopback
device, and add entries to /etc/hosts for that.

It would make sense to separate the client and server portions of the
test so that you could run the server on one host and the client on
another. That's a TODO; I'm not sure how to do that.

- Heikki

Attachments:

ssl-regression-suite-1.patchtext/x-diff; name=ssl-regression-suite-1.patchDownload
commit 3657a5684fcf5ac840d819641b484a3d535a7331
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date:   Mon Aug 4 17:07:31 2014 +0300

    Add SSL regression test suite.

diff --git a/src/test/Makefile b/src/test/Makefile
index 0fd7eab..e6a7154 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -12,6 +12,6 @@ subdir = src/test
 top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS = regress isolation
+SUBDIRS = regress isolation ssl
 
 $(recurse)
diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
new file mode 100644
index 0000000..33a026a
--- /dev/null
+++ b/src/test/ssl/Makefile
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/ssl
+#
+# Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/ssl/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/ssl
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+CERTIFICATES := server-root client-root server client
+
+SSLFILES := $(CERTIFICATES:%=ssl/%.key) $(CERTIFICATES:%=ssl/%.crt)
+
+.PHONY: sslfiles
+sslfiles: $(SSLFILES)
+
+# Rule for creating private/public key pairs
+ssl/%.key:
+	openssl genrsa -out $@ 1024
+	chmod 0600 $@
+
+# Rule for creating CA certificates (client and server)
+ssl/%-root.crt: ssl/%-root.key ssl/%.config
+	openssl req -new -key ssl/$*-root.key -days 36500 -out ssl/$*-root.crt -x509 -config ssl/$*-root.config
+	echo "00" > ssl/$*-root.srl
+
+# Server & client certificates, signed by CA:
+ssl/%.crt: ssl/%.key ssl/%-root.crt
+	openssl req -new -key ssl/$*.key -out ssl/$*.csr -config ssl/$*.config
+# Sign the certificate with the right CA
+	openssl x509 -req -in ssl/$*.csr -CA ssl/$*-root.crt -CAkey ssl/$*-root.key -CAserial ssl/$*-root.srl -out ssl/$*.crt
+	rm ssl/$*.csr
+
+sslfiles-clean:
+	rm -f $(SSLFILES) ssl/client-root.srl ssl/server-root.srl
+
+check:
+	$(prove_check)
+
+installcheck:
+	rm -rf tmp_check
+	$(prove_installcheck)
diff --git a/src/test/ssl/README b/src/test/ssl/README
new file mode 100644
index 0000000..a2b3e37
--- /dev/null
+++ b/src/test/ssl/README
@@ -0,0 +1,53 @@
+src/test/ssl/README
+
+SSL regression tests
+====================
+
+This directory contains a test suite for SSL support.
+
+Setup
+=====
+
+The tests require some additional setup:
+
+1. The suite assumes that the server's fully-qualified hostname is
+   postgres-server.test. The server's listen_addresses is set to
+   postgres-server.test, and the client uses that hostname to connect.
+
+2. The client needs another hostname, alias-for-postgres-server.test, to be
+   set up, pointing to the same IP address as postgres-server.test.
+
+3. The server expects the client's IP address to resolve to postgres-client.test
+
+The easiest way to set up all three hostnames is to use the loopback network
+device. Configure it with the following command (tested on Linux):
+
+    ifconfig lo:10 127.0.10.1 netmask 255.255.255.0 up
+
+And then append the following to /etc/hosts:
+
+    127.0.10.1 postgres-client.test
+    127.0.10.2 postgres-server.test
+    127.0.10.2 alias-for-postgres-server.test
+
+
+Running the tests
+=================
+
+    make installcheck
+
+
+
+Certificates
+============
+
+The test suite needs four public/private key pairs and certificates to run:
+
+server-root: CA used to sign the server certificate
+client-root: CA used to sign client certificates
+server: the server's certificate, for hostname "postgres-server.test"
+client: the client's certificate, for user "ssltestuser"
+
+These keypairs and certificates are included in the ssl/ subdirectory, but
+the Makefile also contains a rule, "make certificates", to recreate them if
+you want to make changes.
diff --git a/src/test/ssl/ssl/client-root.config b/src/test/ssl/ssl/client-root.config
new file mode 100644
index 0000000..c7fd5f8
--- /dev/null
+++ b/src/test/ssl/ssl/client-root.config
@@ -0,0 +1,6 @@
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test CA for PostgreSQL SSL regression test client certs
diff --git a/src/test/ssl/ssl/client-root.crt b/src/test/ssl/ssl/client-root.crt
new file mode 100644
index 0000000..2295403
--- /dev/null
+++ b/src/test/ssl/ssl/client-root.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB/TCCAWYCCQDm71vKU5vw/zANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdU
+ZXN0IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50
+IGNlcnRzMCAXDTE0MDgwNDE0MDYzMFoYDzIxMTQwNzExMTQwNjMwWjBCMUAwPgYD
+VQQDDDdUZXN0IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qg
+Y2xpZW50IGNlcnRzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzsE98l5D6
+PWBKO4a5xXsFzS52Vqbn+a/wj7i4HDsKFFkYp/z6VCPltbA07PPLfN9FpX1C9tRI
+j6Puw+CFSwBqRQiyhbKxmDuIEPK8zeh3709Gekqd80598mHoYN6OuTNTcifsFgv8
+GgFHqgB/q+aHxJtozaVoAibjuNvTgOYlgwIDAQABMA0GCSqGSIb3DQEBCwUAA4GB
+AK1u67db7/dtkRbvV4Zexbstb6lQaFwGQw9tcQXbQyZOAR0tkZ40Jka1Y7nXdAKm
+jIegxGMaO7jz1n3JyIZ6QLFuyT+HcVaYR9nqmgPdqmRjek+VDJLiWC28nIWvrNgj
+1sb2AHMmsgm7U1oTXfRHguWs7PRIQHhVEhq5MlcXeIcw
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client-root.key b/src/test/ssl/ssl/client-root.key
new file mode 100644
index 0000000..5ad3e83
--- /dev/null
+++ b/src/test/ssl/ssl/client-root.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQCzsE98l5D6PWBKO4a5xXsFzS52Vqbn+a/wj7i4HDsKFFkYp/z6
+VCPltbA07PPLfN9FpX1C9tRIj6Puw+CFSwBqRQiyhbKxmDuIEPK8zeh3709Gekqd
+80598mHoYN6OuTNTcifsFgv8GgFHqgB/q+aHxJtozaVoAibjuNvTgOYlgwIDAQAB
+AoGBALGBmXQmMiTSHt4WIchAyn/3qk9i2GzO3rxQ7hSKZtRVN5LA2IreNbwFmPUf
+otLBH7soeS5+sUShCTukKpbieZvZdjEAMrwScshRwx4n74PQBm+mGSKZv2F7+Noi
+QSafpVrvr6xKBSvM4RCy1rVTfA8qpsrlKUEnmK4OGlFAYLYZAkEA3Rq0+o3GPlJi
+4JSeRXvr1wOGbLDYDmEY5bZRY/pnsFxA1jcvfPIzS/TD53eV0ELR/zG8PUmGHmIa
+yyXFK2a+FQJBANAMSSmWOPCtfiwonHJkdTm8WQwvmHb27WvQuTMG/FRs0hB090GR
+fr3/zrhrFxPf9jrgfwP0tzFSy0g8z3SI0zcCQQDJ3uR2DpN9u0LDwW1wC2Ccg39s
+JVpeZpCQyxEssyeQgepAq0oUTh4/r05eO3TxHNEWqpYvbr2hZ/kGmYmXwsqxAkEA
+l6pdK5P3rnzLniV852eUjaJgyCFqZE9ehVqDqE9PY7xw5s5d8c6/NoNlj8uB51s9
+hW5jKd8cLTjOOLscATg9wQJADZM8TboSwoM5A63VGt89ERAAFEkLcGW7JT8CrPFS
+R33an5L6L8rTqBQ2GYOAAyOXNiE/SA2cCEQ/6O1KgDH31g==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/client-root.srl b/src/test/ssl/ssl/client-root.srl
new file mode 100644
index 0000000..8a0f05e
--- /dev/null
+++ b/src/test/ssl/ssl/client-root.srl
@@ -0,0 +1 @@
+01
diff --git a/src/test/ssl/ssl/client.config b/src/test/ssl/ssl/client.config
new file mode 100644
index 0000000..56c8f4f
--- /dev/null
+++ b/src/test/ssl/ssl/client.config
@@ -0,0 +1,6 @@
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = ssltestuser
diff --git a/src/test/ssl/ssl/client.crt b/src/test/ssl/ssl/client.crt
new file mode 100644
index 0000000..4dd2e89
--- /dev/null
+++ b/src/test/ssl/ssl/client.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBxzCCATACAQEwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
+Fw0xNDA4MDQxNDA2MzBaFw0xNDA5MDMxNDA2MzBaMBYxFDASBgNVBAMMC3NzbHRl
+c3R1c2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkm/TdFLy3Pb3UqZwb
+NSP2VwyAzlcmAT2faElMfrjg/Bv8Fv44IsFmCR0+qlJthkBnEK+eIp9YlBuGBIlX
+DZzPb4cC9O8sZf2oy/9oHeZP0k81iKjkLgdK9Cr7MaG9HvmOfwVTFWT5HnWw6dNI
+TfUFbeMhl3/5Hr9k5MLjCYPVawIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFB2LsqT
+Lsgc0VNidlbnc30/xwGVZUXD68YW+0MgYRvLsko8+ujx094JxCIDWW+yZnXt3hV5
+bHx/ol9FpTXE2wmdxmTnwy7GuD+EtqUXp/+N7mc9eMTstuRRCoog1V3vjnPXVjTe
+8mMMV/g9AeW0uHHkt2dSFKkIdf/LrTAZLTWp
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client.key b/src/test/ssl/ssl/client.key
new file mode 100644
index 0000000..1398db4
--- /dev/null
+++ b/src/test/ssl/ssl/client.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCkm/TdFLy3Pb3UqZwbNSP2VwyAzlcmAT2faElMfrjg/Bv8Fv44
+IsFmCR0+qlJthkBnEK+eIp9YlBuGBIlXDZzPb4cC9O8sZf2oy/9oHeZP0k81iKjk
+LgdK9Cr7MaG9HvmOfwVTFWT5HnWw6dNITfUFbeMhl3/5Hr9k5MLjCYPVawIDAQAB
+AoGAQMTozVDKjKNrnVD7jq0TMGvCDLCkE90rHR0QsluubUBl1oRJlVb/mCF/81db
+RaMsliE3qNZgsp7cUZhZXfqKN2dSWrPc8xRqcW2D6vE21qP/aFg7TCwFFX5g/UOW
+B9gN6bJIjZcAJCObwlZ0i6MB5UYTQHJsdRQJ14HqHx9DkrECQQDPCof5mC6HAhLm
+iew0alYb6nBqUBabmiL4A73pnHixa3ElA/eGgjjrZFSHUuhap8WbHf7jRA5dMh8R
+qxYl73BJAkEAy4jCrQkIH1t3epsiahtfXxxYecrgLs+9rUjY8s9gbDADt4UMBgm+
+moh2H+YuI4OmLQudkd21zEY57Ifmr8qAEwJAd0/DUkOvtF+ukqoys3YAD3BHvgxP
+KvZlZnWJkMF6EAwxlLo3f401zfjweVd+zRdX2e8sPr2uZWiH3P+x8MSN+QJBAKuS
+9/kB2hUE9+0lBZfIx1bYAEV7HgyYFt8Sv7+/zRqmRxvXTlFwuXpvepRdZ5uMiPME
+Dao+6dfvgzi/P1oFLH0CQQCANUPm4B5B+O91A+EKVI5NwF6+FXLFPTkA7XjkLsNz
+uJqF/C4eGvCfBxXbcUOyu7gTMakM6yIvJGmL5N5550VT
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-root.config b/src/test/ssl/ssl/server-root.config
new file mode 100644
index 0000000..16ef29d
--- /dev/null
+++ b/src/test/ssl/ssl/server-root.config
@@ -0,0 +1,6 @@
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test CA for PostgreSQL SSL regression test server certs
diff --git a/src/test/ssl/ssl/server-root.crt b/src/test/ssl/ssl/server-root.crt
new file mode 100644
index 0000000..3b6e552
--- /dev/null
+++ b/src/test/ssl/ssl/server-root.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB/TCCAWYCCQCCMEPUwcYHhDANBgkqhkiG9w0BAQsFADBCMUAwPgYDVQQDDDdU
+ZXN0IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVy
+IGNlcnRzMCAXDTE0MDgwNDE0MDYzMFoYDzIxMTQwNzExMTQwNjMwWjBCMUAwPgYD
+VQQDDDdUZXN0IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qg
+c2VydmVyIGNlcnRzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjtQ1ZkmCw
+i0wVfCrWhR60kKoSHbZRIL1EbPwbuhOcOo7GKKdyEAqpNXCiQY2zqK6resimPukv
+IIxY2mYmhSlYd9qyFZoxFxx8NwWZL2re7IJVk3NXd+FpQ0KDNCt4sBoNcxv8uJ+k
+YAJcniYZsQLclw4IT12tMcpCiO5r2iuuJQIDAQABMA0GCSqGSIb3DQEBCwUAA4GB
+AGhruBhhd1RV7monhEyTqR+zH7yD4l/+7ynPI/QkA2PdCQ2wkpJPVqaIG4kN1XkU
+53Wwuh7EzchEl7mMWRgqbRgucc0YL++B6l2Y5jRdWnAi5Ju59fOzg58AuoQQYLhH
+f3a47gyprNUPAkfix+vU+2rMaPR0fXXX7NuEPXGrHlz8
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-root.key b/src/test/ssl/ssl/server-root.key
new file mode 100644
index 0000000..a3e23bb
--- /dev/null
+++ b/src/test/ssl/ssl/server-root.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDjtQ1ZkmCwi0wVfCrWhR60kKoSHbZRIL1EbPwbuhOcOo7GKKdy
+EAqpNXCiQY2zqK6resimPukvIIxY2mYmhSlYd9qyFZoxFxx8NwWZL2re7IJVk3NX
+d+FpQ0KDNCt4sBoNcxv8uJ+kYAJcniYZsQLclw4IT12tMcpCiO5r2iuuJQIDAQAB
+AoGBAJsx295vqJzK49lE3oGFC09vxapBO/CBSt4nFZDkOtdhBcxDCxTvoASBmrnL
+ygNn9VyEe9rqx81J9r3cZIDegzUylOKJDe82dl662AextBRKxOVVEyfIwvKq+rRP
+lvxMgQih6peodItBWwdR3FYawH0f0ydTFXWl6swxi+UboufhAkEA9l1fzBIeArZU
+1Yebn/wFZ43CEnpoquiv/ntk2v08By7yHQOFjV66vXhqjRdVCaVS1DPQqYb+jqpm
+LsHSPe9ZDQJBAOyc4JcM6EgjTA9XUASd0VX/bMn9+iekbN7vqnQ8g6aRD8Duc6fI
+OAXB61gt6wHTKBYmSEdXl5Vw+C/sLS1BM3kCQGmMy7Q0tuLWlzX8qXI7mV0qYNFl
+3F4M3woad7VS9VrmhBhmH9vXkA4I/y1/p5FAYWJE6MsY6Qraenjh1V9voikCQFLj
+9m4UUH+NFgU90kN7wi09aTAuMGeY26cSEQXdeUVuBjXRk2TQ6Idj0v22QGEIRz/T
+M3kCv5DT3a50L7Nt5wkCQEMG+YJawAPKW8gMebXqMd5/VPaIe2eybB9YTraIXUVU
+s9DAGeaVv+iGXIqoBsa0Mez0TJ9mzFWa3h421AEsRBQ=
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-root.srl b/src/test/ssl/ssl/server-root.srl
new file mode 100644
index 0000000..8a0f05e
--- /dev/null
+++ b/src/test/ssl/ssl/server-root.srl
@@ -0,0 +1 @@
+01
diff --git a/src/test/ssl/ssl/server.config b/src/test/ssl/ssl/server.config
new file mode 100644
index 0000000..c3d7201
--- /dev/null
+++ b/src/test/ssl/ssl/server.config
@@ -0,0 +1,6 @@
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = postgres-server.test
diff --git a/src/test/ssl/ssl/server.crt b/src/test/ssl/ssl/server.crt
new file mode 100644
index 0000000..8b525f7
--- /dev/null
+++ b/src/test/ssl/ssl/server.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB0DCCATkCAQEwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNlcnZlciBjZXJ0czAe
+Fw0xNDA4MDQxNDA2MzBaFw0xNDA5MDMxNDA2MzBaMB8xHTAbBgNVBAMMFHBvc3Rn
+cmVzLXNlcnZlci50ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL0E5X
+54Dw1VTXHYvYUIuE2j7wwGutyX3gT8SpdxPtTWD/cfJsqj5p02WWdL+AdnSqXBnV
+ZkIJ4J7nBsqO9QnTlSpmQGoeXMlX0Ic4xmMgM+ayiQThHJ7/n7qchTdE5noZVRrd
+6XckF6W6JWsojZaY3EBQsKhB2TCaoNdzHQ0zgQIDAQABMA0GCSqGSIb3DQEBCwUA
+A4GBAKoR4p+vtf1mnOdEpwjpCuoYvRs6QcC1gVAkyDaO7T9V+U1Tkmdq05KMf+iF
+miA+A8LeFvEnc2lNeE8nqySmJx6RG2UclhizgsRhLTZl79Pu4LdBbXAx5Tc1MrHB
+n4H8hc8ekpBCuOg7rz6n22aLx3UJSDzTDGZoNbXlUPARRqIm
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server.key b/src/test/ssl/ssl/server.key
new file mode 100644
index 0000000..91db421
--- /dev/null
+++ b/src/test/ssl/ssl/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDL0E5X54Dw1VTXHYvYUIuE2j7wwGutyX3gT8SpdxPtTWD/cfJs
+qj5p02WWdL+AdnSqXBnVZkIJ4J7nBsqO9QnTlSpmQGoeXMlX0Ic4xmMgM+ayiQTh
+HJ7/n7qchTdE5noZVRrd6XckF6W6JWsojZaY3EBQsKhB2TCaoNdzHQ0zgQIDAQAB
+AoGAZAVMDTOdQZNP2Wm0kWmlroL0VG356gVx8rzyxD+d1d0ddv7Se6Voj8Kgnh+Z
+Q0/enSQpwWI3kmVbVgEtMs7qDL0VEYnvUdcVlM44VTxEKeOlIeWNJzA+iwFefJ13
+xICD/ikqAujy1+M5ctQSBG/Diwnyd18sDYTaXxqnTGhmzIkCQQDycQif5RZ7lQqj
+QjGrHO22eKHXxJD8+8tM+/IHmLDOpH8dh1BCItvbh4BMT00ozqJRYpGW28Dv0tuU
+Mnv2qMXfAkEA1zY/oHshdX+Jl7KfDXHtNdD54+4lo7UCHTY8nYTWSKOse73uIi5n
+OrjyPxTxppeT3LImDfGGz3trn6AFn4NynwJADfM5FtI8v/GsggZeC0WH3BcG4P57
+hUMLyKs6mvtLOSi388AEezm8Qt6CIGHzw6RYLKyqSe7tJB+S6O0auu+tKwJAII8h
+gyr9veQEsgGhFIym4ZxzIeu2oBTTdA3vj7k4Hhc9Eh+C9oLktTqj061cfeKyyRHe
+tf9TcPJwLt8r2p0tawJBANak2VFGrNCvE5kF7WSyCtX6PndPyCpstOGK7oy4brJ2
+8psqKePnamaL7W0WjJuTmbN1GotPf0dlZsP4pTpuUWI=
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
new file mode 100644
index 0000000..a5734dc
--- /dev/null
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -0,0 +1,141 @@
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 16;
+
+
+#### Part 1. Set up the server.
+
+#my $tempdir = TestLib::tempdir;
+my $tempdir = 'tmp_check';
+
+start_test_server($tempdir);
+
+# Create a test user and two databases
+psql 'postgres', "CREATE USER ssltestuser";
+psql 'postgres', "CREATE USER anotheruser";
+psql 'postgres', "CREATE DATABASE trustdb";
+psql 'postgres', "CREATE DATABASE certdb";
+
+# enable SSL and set up server key
+open CONF, ">>$tempdir/pgdata/postgresql.conf";
+print CONF "fsync=off\n";
+print CONF "ssl=on\n";
+print CONF "ssl_ca_file='client-root.crt'\n";
+print CONF "listen_addresses='postgres-server.test'\n";
+print CONF "log_connections=on\n";
+print CONF "log_hostname=on\n";
+print CONF "log_statement=all\n";
+close CONF;
+
+# Copy server certificate and key, and client root certificate, to the data dir
+system_or_bail "cp ssl/server.crt '$tempdir'/pgdata";
+system_or_bail "cp ssl/server.key '$tempdir'/pgdata";
+system_or_bail "chmod 0600 '$tempdir'/pgdata/server.key";
+system_or_bail "cp ssl/client-root.crt '$tempdir'/pgdata";
+
+# Only accept SSL connections, from host "postgres-client.test".
+# When connecting to certdb, also check the client certificate.
+open HBA, ">>$tempdir/pgdata/pg_hba.conf";
+# TYPE  DATABASE        USER            ADDRESS                 METHOD
+print HBA "hostssl	trustdb all postgres-client.test trust\n";
+print HBA "hostssl	certdb all postgres-client.test cert\n";
+close HBA;
+
+# Stop and restart server to reload the new config. We cannot use
+# restart_test_server() because that overrides listen_addresses to only allow
+# Unix domain socket connections.
+
+system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/pgdata", '-w';
+system_or_bail 'pg_ctl', 'start', '-D', "$tempdir/pgdata", '-w', '-l',
+	"$tempdir/logfile";
+
+my $common_connstr;
+
+# Define a couple of helper functions to test connecting to the server.
+#
+# The first argument is a (part of a) connection string, and it's also printed
+# out as the test case name. It is appended to $common_connstr global variable,
+# which also contains a libpq connection string.
+#
+# The second argument is a hostname to connect to.
+sub test_connect_ok {
+	my $connstr = $_[0];
+	my $hostname = $_[1];
+
+	command_ok([ 'psql',
+				 '-c', "SELECT 'Connection test with $connstr'",
+				 '-d', "$common_connstr host=$hostname $connstr"],
+			   "$connstr");
+}
+
+sub test_connect_fails {
+	my $connstr = $_[0];
+	my $hostname = $_[1];
+
+	command_fails([ 'psql',
+					'-c', "SELECT 'Connection test with $connstr'",
+					'-d', "$common_connstr host=$hostname $connstr"],
+				  "$connstr (should fail)");
+}
+
+
+### Part 2. Run client-side tests.
+###
+### Test that the client accepts/rejects the connection correctly, depending
+### on sslmode and whether the server's certificate looks correct. No
+### client certificate is used in these tests.
+
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid";
+my $host="postgres-server.test";
+
+# The server should not accept non-SSL connections
+test_connect_fails("sslmode=disable", $host);
+
+# Try without a root cert. In sslmode=require, this should work. In verify-ca
+# or verify-full mode it should fail
+test_connect_ok   ("sslrootcert=invalid sslmode=require", $host);
+test_connect_fails("sslrootcert=invalid sslmode=verify-ca", $host);
+test_connect_fails("sslrootcert=invalid sslmode=verify-full", $host);
+
+# 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("sslrootcert=ssl/client-root.crt sslmode=require", $host);
+test_connect_fails("sslrootcert=ssl/client-root.crt sslmode=verify-ca", $host);
+test_connect_fails("sslrootcert=ssl/client-root.crt sslmode=verify-full", $host);
+
+# And finally, with the correct root cert.
+test_connect_ok   ("sslrootcert=ssl/server-root.crt sslmode=require", $host);
+test_connect_ok   ("sslrootcert=ssl/server-root.crt sslmode=verify-ca", $host);
+test_connect_ok   ("sslrootcert=ssl/server-root.crt sslmode=verify-full", $host);
+
+# Check that connecting with verify-full fails, when the hostname doesn't
+# match the hostname in the server's certificate.
+$host="alias-for-postgres-server.test";
+test_connect_ok   ("sslrootcert=ssl/server-root.crt sslmode=require", $host);
+test_connect_ok   ("sslrootcert=ssl/server-root.crt sslmode=verify-ca", $host);
+test_connect_fails("sslrootcert=ssl/server-root.crt sslmode=verify-full", $host);
+
+
+
+### Part 2. Server-side tests.
+###
+### Test certificate authorization. 
+
+$common_connstr="sslrootcert=ssl/server-root.crt sslmode=require dbname=certdb";
+$host="postgres-server.test";
+
+# no client cert
+test_connect_fails("user=ssltestuser sslcert=invalid", $host);
+
+# correct client cert
+test_connect_ok   ("user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client.key", $host);
+
+# client cert belonging to another user
+test_connect_fails("user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client.key", $host);
+
+
+### Part 3. Test Certificate Revocation Lists
+###
+### TODO
+
#2Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#1)
Re: SSL regression test suite

On Mon, Aug 4, 2014 at 10:38 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

Now that we use TAP for testing client tools, I think we can use that to
test various SSL options too. I came up with the attached. Comments?

It currently assumes that the client's and the server's hostnames are
"postgres-client.test" and "postgres-server.test", respectively. That makes
it a bit tricky to run on a single systme. The README includes instructions;
basically you need to set up an additional loopback device, and add entries
to /etc/hosts for that.

That seems so onerous that I think few people will do it, and not
regularly, resulting in the tests breaking and nobody noticing.
Reconfiguring the loopback interface like that requires root
privilege, and won't survive a reboot, and doing it in the system
configuration will require hackery specific to the particular flavor
of Linux you're running, and probably other hackery on non-Linux
systems (never mind Windows). Why can't you make it work over
127.0.0.1?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Robert Haas (#2)
1 attachment(s)
Re: SSL regression test suite

On 08/05/2014 10:46 PM, Robert Haas wrote:

On Mon, Aug 4, 2014 at 10:38 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

Now that we use TAP for testing client tools, I think we can use that to
test various SSL options too. I came up with the attached. Comments?

It currently assumes that the client's and the server's hostnames are
"postgres-client.test" and "postgres-server.test", respectively. That makes
it a bit tricky to run on a single systme. The README includes instructions;
basically you need to set up an additional loopback device, and add entries
to /etc/hosts for that.

That seems so onerous that I think few people will do it, and not
regularly, resulting in the tests breaking and nobody noticing.
Reconfiguring the loopback interface like that requires root
privilege, and won't survive a reboot, and doing it in the system
configuration will require hackery specific to the particular flavor
of Linux you're running, and probably other hackery on non-Linux
systems (never mind Windows).

Yeah, you're probably right.

Why can't you make it work over 127.0.0.1?

I wanted it to be easy to run the client and the server on different
hosts. As soon as we have more than one SSL implementation, it would be
really nice to do interoperability testing between a client and a server
using different implementations.

Also, to test sslmode=verify-full, where the client checks that the
server certificate's hostname matches the hostname that it connected to,
you need to have two aliases for the same server, one that matches the
certificate and one that doesn't. But I think I found a way around that
part; if the certificate is set up for "localhost", and connect to
"127.0.0.1", you get a mismatch.

So, I got rid of the DNS setup, it only depends localhost/127.0.0.1 now.
Patch attached. That means that it's not easy to run the client and the
server on different hosts, but we can improve that later.

- Heikki

Attachments:

ssl-regression-suite-2.patchtext/x-diff; name=ssl-regression-suite-2.patchDownload
commit 140c590ca86a0ba4a6b422e4b618cd459b84175f
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date:   Wed Aug 6 18:43:39 2014 +0300

    Refactor cert file stuff in client

diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index f950fc3..cee7b2e 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -780,57 +780,21 @@ destroy_ssl_system(void)
 static int
 initialize_SSL(PGconn *conn)
 {
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
 	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
-		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else if (stat(fnbuf, &buf) != 0)
-	{
-		/*
-		 * If file is not present, just go on without a client cert; server
-		 * might or might not accept the connection.  Any other error,
-		 * however, is grounds for complaint.
-		 */
-		if (errno != ENOENT && errno != ENOTDIR)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
-							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-			return -1;
-		}
-		have_cert = false;
-	}
-	else
+	char	   *sslcertfile = NULL;
+	char	   *engine = NULL;
+	char	   *keyname = NULL;
+	char	   *sslkeyfile = NULL;
+	char	   *sslrootcert = NULL;
+	char	   *sslcrl = NULL;
+	int			ret = -1;
+
+	if (!pqsecure_get_ssl_files(conn,
+								&sslcertfile, &sslkeyfile, &engine, &keyname,
+								&sslrootcert, &sslcrl))
+		return PGRES_POLLING_READING;
+
+	if (sslcertfile)
 	{
 		/*
 		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
@@ -855,216 +819,146 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, sslcertfile) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		if (SSL_use_certificate_file(conn->ssl, sslcertfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		/* need to load the associated private key, too */
-		have_cert = true;
-
 #ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_unlock(&ssl_config_mutex);
 #endif
 	}
 
 	/*
-	 * Read the SSL key. If a key is specified, treat it as an engine:key
-	 * combination if there is colon present - we don't support files with
-	 * colon in the name. The exception is if the second character is a colon,
-	 * in which case it can be a Windows filename with drive specification.
+	 * If an engine:key specification was given, load that.
 	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	if (engine)
 	{
 #ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
+		conn->engine = ENGINE_by_id(engine);
+		if (conn->engine == NULL)
 		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			engine_colon = strchr(engine_str, ':');
-
-			*engine_colon = '\0';		/* engine_str now has engine name */
-			engine_colon++;		/* engine_colon now has key name */
-
-			conn->engine = ENGINE_by_id(engine_str);
-			if (conn->engine == NULL)
-			{
-				char	   *err = SSLerrmessage();
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
+							  engine, err);
+			SSLerrfree(err);
+			goto fail;
+		}
 
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
+		if (ENGINE_init(conn->engine) == 0)
+		{
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-										   NULL, NULL);
-			if (pkey == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			free(engine_str);
-
-			fnbuf[0] = '\0';	/* indicate we're not going to load from a
-								 * file */
-		}
-		else
-#endif   /* USE_SSL_ENGINE */
-		{
-			/* PGSSLKEY is not an engine, treat it as a filename */
-			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+							  engine, err);
+			SSLerrfree(err);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
 
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
+		pkey = ENGINE_load_private_key(conn->engine, keyname, NULL, NULL);
+		if (pkey == NULL)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#endif
+#else
+		/*
+		 * should not happen; pqsecure_get_ssl_files doesn't return an
+		 * engine spec if not compiled with USE_SSL_ENGINE.
+		 */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("engine not supported\n"));
+		goto fail;
+#endif   /* USE_SSL_ENGINE */
+	}
 
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+	/* Read the client private key file */
+	if (sslkeyfile)
+	{
+		if (SSL_use_PrivateKey_file(conn->ssl, sslkeyfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslkeyfile, err);
 			SSLerrfree(err);
-			return -1;
+			goto fail;
 		}
 	}
 
-	/* verify that the cert and key go together */
-	if (have_cert &&
+	/* Verify that the cert and key go together */
+	if (sslcertfile &&
 		SSL_check_private_key(conn->ssl) != 1)
 	{
 		char	   *err = SSLerrmessage();
 
 		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-						  fnbuf, err);
+						  sslkeyfile, err);
 		SSLerrfree(err);
-		return -1;
+		goto fail;
 	}
 
 	/*
-	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
+	 * Load root cert, if exists. (pqsecure_get_ssl_files already complained
+	 * if no root cert was configured but sslmode requires verification
+	 * of server certificates.)
 	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] != '\0' &&
-		stat(fnbuf, &buf) == 0)
+	if (sslrootcert)
 	{
 		X509_STORE *cvstore;
 
@@ -1075,35 +969,28 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		if (SSL_CTX_load_verify_locations(SSL_context, sslrootcert, NULL) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslrootcert, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
 		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
 		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-			else if (have_homedir)
-				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-			else
-				fnbuf[0] = '\0';
-
 			/* Set the flags to check against the complete CRL chain */
-			if (fnbuf[0] != '\0' &&
-				X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
+			if (sslcrl &&
+				X509_STORE_load_locations(cvstore, sslcrl, NULL) == 1)
 			{
 				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
 #ifdef X509_V_FLAG_CRL_CHECK
@@ -1114,12 +1001,12 @@ initialize_SSL(PGconn *conn)
 
 				printfPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
+								  sslcrl);
 				SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 				pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-				return -1;
+				goto fail;
 #endif
 			}
 			/* if not found, silently ignore;  we do not require CRL */
@@ -1130,31 +1017,6 @@ initialize_SSL(PGconn *conn)
 
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
 	}
-	else
-	{
-		/*
-		 * stat() failed; assume root file doesn't exist.  If sslmode is
-		 * verify-ca or verify-full, this is an error.  Otherwise, continue
-		 * without performing any server cert verification.
-		 */
-		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
-		{
-			/*
-			 * The only way to reach here with an empty filename is if
-			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
-			 * that it seems worth having a specialized error message for it.
-			 */
-			if (fnbuf[0] == '\0')
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not get home directory to locate root certificate file\n"
-												"Either provide the file or change sslmode to disable server certificate verification.\n"));
-			else
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("root certificate file \"%s\" does not exist\n"
-							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
-			return -1;
-		}
-	}
 
 	/*
 	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
@@ -1167,9 +1029,27 @@ initialize_SSL(PGconn *conn)
 	}
 #endif
 
-	return 0;
+	/* success */
+	ret = 0;
+
+fail:
+	if (sslcertfile)
+		free(sslcertfile);
+	if (engine)
+		free(engine);
+	if (keyname)
+		free(keyname);
+	if (sslkeyfile)
+		free(sslkeyfile);
+	if (sslrootcert)
+		free(sslrootcert);
+	if (sslcrl)
+		free(sslcrl);
+
+	return ret;
 }
 
+
 /*
  *	Attempt to negotiate SSL connection.
  */
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 66778b2..e19fc61 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -475,3 +475,297 @@ pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
 }
 
 #endif   /* ENABLE_THREAD_SAFETY && !WIN32 */
+
+/*
+ *	Determine the filenames of SSL related files.
+ *
+ * The filenames returned are checked to exist; the caller should throw an
+ * error if one of the returned files can not be opened. This function
+ * performs some preliminariry sanity checks, e.g. if a certificate file
+ * exists, we must also find the corresponding private key file.
+ *
+ * *sslcert is set to the path of the client's certificate, and *sslkey to
+ * the path to the corresponding private key.  If compiled with
+ * USE_SSL_ENGINE, a pathname containing a colon is interpreted as a
+ * a two-part "engin:key" string, for specifying a hardware key.
+ *
+ * *sslrootcert and *sslcrl are set to the paths to CA certificate and
+ * CRL files, used to validate the server certificate.
+ *
+ * The returned strings are malloc'd, and the caller is responsible for
+ * freeing them. On error, returns false and sets the libpq error message.
+ */
+bool
+pqsecure_get_ssl_files(PGconn *conn, char **sslcert, char **sslkey,
+					   char **engine, char **keyname,
+					   char **sslrootcert, char **sslcrl)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+
+	*sslcert = NULL;
+	*sslkey = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Find the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			goto fail;
+		}
+	}
+	else
+	{
+		*sslcert = strdup(fnbuf);
+		if (*sslcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * Read the SSL key. If a key is specified, treat it as an engine:key
+	 * combination if there is colon present - we don't support files with
+	 * colon in the name. The exception is if the second character is a colon,
+	 * in which case it can be a Windows filename with drive specification.
+	 */
+	if (*sslcert && conn->sslkey && strlen(conn->sslkey) > 0)
+	{
+#ifdef USE_SSL_ENGINE
+		if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+			&& conn->sslkey[1] != ':'
+#endif
+			)
+		{
+			/* Colon, but not in second character, treat as engine:key */
+			char	   *engine_str = strdup(conn->sslkey);
+			char	   *engine_colon;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			*engine = strdup(engine_str);
+			if (*engine)
+				*keyname = strdup(engine_colon);
+
+			free(engine_str);
+
+			if (*engine == NULL || *keyname == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			fnbuf[0] = '\0';	/* indicate we're not going to load from a
+								 * file */
+		}
+		else
+#endif   /* USE_SSL_ENGINE */
+		{
+			/* PGSSLKEY is not an engine, treat it as a filename */
+			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		}
+	}
+	else if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (*sslcert && fnbuf[0] != '\0')
+	{
+		/* read the client key from file */
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			goto fail;
+		}
+#ifndef WIN32
+		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  fnbuf);
+			goto fail;
+		}
+#endif
+
+		*sslkey = strdup(fnbuf);
+		if (*sslkey == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] != '\0' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		*sslrootcert = strdup(fnbuf);
+		if (*sslrootcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+	else
+	{
+		/*
+		 * stat() failed; assume root file doesn't exist.  If sslmode is
+		 * verify-ca or verify-full, this is an error.  Otherwise, continue
+		 * without performing any server cert verification.
+		 */
+		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+		{
+			/*
+			 * The only way to reach here with an empty filename is if
+			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+			 * that it seems worth having a specialized error message for it.
+			 */
+			if (fnbuf[0] == '\0')
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not get home directory to locate root certificate file\n"
+												"Either provide the file or change sslmode to disable server certificate verification.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("root certificate file \"%s\" does not exist\n"
+							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+			goto fail;
+		}
+	}
+
+	/* If we have a root certificate, also try to load a CRL */
+	if (*sslrootcert)
+	{
+		if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+			strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0' &&
+			stat(fnbuf, &buf) != 0)
+		{
+			/*
+			 * If not found, silently ignore;  we do not require CRL.
+			 * Any other error, however, is grounds for complaint.
+			 */
+			if (errno != ENOENT && errno != ENOTDIR)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+				goto fail;
+			}
+		}
+		else
+		{
+			*sslcrl = strdup(fnbuf);
+			if (*sslcrl == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+		}
+	}
+
+	/* all done! */
+	return true;
+
+fail:
+	if (*sslcert)
+		free(*sslcert);
+	if (*engine)
+		free(*engine);
+	if (*keyname)
+		free(*keyname);
+	if (*sslkey)
+		free(*sslkey);
+	if (*sslrootcert)
+		free(*sslrootcert);
+	if (*sslcrl)
+		free(*sslcrl);
+
+	*sslcert = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslkey = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	return false;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6032904..caca480 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -627,6 +627,8 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
+extern bool pqsecure_get_ssl_files(PGconn *, char **sslcert, char **sslkey, char **engine, char **keyname, char **sslrootcert, char **sslcrl);
+
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
#4Andres Freund
andres@2ndquadrant.com
In reply to: Heikki Linnakangas (#3)
Re: SSL regression test suite

On 2014-08-12 14:01:18 +0300, Heikki Linnakangas wrote:

On 08/05/2014 10:46 PM, Robert Haas wrote:

Why can't you make it work over 127.0.0.1?

I wanted it to be easy to run the client and the server on different hosts.
As soon as we have more than one SSL implementation, it would be really nice
to do interoperability testing between a client and a server using different
implementations.

Also, to test sslmode=verify-full, where the client checks that the server
certificate's hostname matches the hostname that it connected to, you need
to have two aliases for the same server, one that matches the certificate
and one that doesn't. But I think I found a way around that part; if the
certificate is set up for "localhost", and connect to "127.0.0.1", you get a
mismatch.

Alternatively, and to e.g. test wildcard certs and such, I think you can
specify both host and hostaddr to connect to connect without actually
doing a dns lookup.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Andres Freund (#4)
Re: SSL regression test suite

On 08/12/2014 02:28 PM, Andres Freund wrote:

On 2014-08-12 14:01:18 +0300, Heikki Linnakangas wrote:

Also, to test sslmode=verify-full, where the client checks that the server
certificate's hostname matches the hostname that it connected to, you need
to have two aliases for the same server, one that matches the certificate
and one that doesn't. But I think I found a way around that part; if the
certificate is set up for "localhost", and connect to "127.0.0.1", you get a
mismatch.

Alternatively, and to e.g. test wildcard certs and such, I think you can
specify both host and hostaddr to connect to connect without actually
doing a dns lookup.

Oh, I didn't know that's possible! Yeah, that's a good solution.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Heikki Linnakangas (#5)
1 attachment(s)
Re: SSL regression test suite

On 08/12/2014 03:53 PM, Heikki Linnakangas wrote:

On 08/12/2014 02:28 PM, Andres Freund wrote:

On 2014-08-12 14:01:18 +0300, Heikki Linnakangas wrote:

Also, to test sslmode=verify-full, where the client checks that the server
certificate's hostname matches the hostname that it connected to, you need
to have two aliases for the same server, one that matches the certificate
and one that doesn't. But I think I found a way around that part; if the
certificate is set up for "localhost", and connect to "127.0.0.1", you get a
mismatch.

Alternatively, and to e.g. test wildcard certs and such, I think you can
specify both host and hostaddr to connect to connect without actually
doing a dns lookup.

Oh, I didn't know that's possible! Yeah, that's a good solution.

Here's a new version of the SSL regression suite I wrote earlier. It now
specifies both host and hostaddr in the connection string as Andres
suggested, so it no longer requires changes to network configuration. I
added a bunch of tests for the SAN feature that Alexey Klyukin wrote and
was committed earlier. Plus a lot of miscellaneous cleanup.

This probably needs some further cleanup before it's ready for
committing. One issues is that it creates a temporary cluster that
listens for TCP connections on localhost, which isn't safe on a
multi-user system.

- Heikki

Attachments:

ssl-regression-suite-3.patchtext/x-diff; name=ssl-regression-suite-3.patchDownload
diff --git a/src/test/Makefile b/src/test/Makefile
index 0fd7eab..e6a7154 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -12,6 +12,6 @@ subdir = src/test
 top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS = regress isolation
+SUBDIRS = regress isolation ssl
 
 $(recurse)
diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
new file mode 100644
index 0000000..8e0db47
--- /dev/null
+++ b/src/test/ssl/Makefile
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/ssl
+#
+# Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/ssl/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/ssl
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+CERTIFICATES := serverroot server-cn-and-alt-names \
+	server-cn-only server-single-alt-name server-multiple-alt-names \
+	server-no-names \
+	clientroot client
+
+SSLFILES := $(CERTIFICATES:%=ssl/%.key) $(CERTIFICATES:%=ssl/%.crt)
+
+sslfiles: $(SSLFILES)
+
+# Rule for creating private/public key pairs
+ssl/%.key:
+	openssl genrsa -out $@ 1024
+	chmod 0600 $@
+
+# Rule for creating CA certificates (client and server)
+ssl/%root.crt: ssl/%root.key %root.config
+	openssl req -new -key ssl/$*root.key -days 36500 -out ssl/$*root.crt -x509 -config $*root.config
+	echo "00" > ssl/$*root.srl
+
+# Server certificates, signed by server root CA:
+ssl/server-%.crt: ssl/server-%.key ssl/serverroot.crt
+# Generate a Certificate Sign Request (CSR)
+	openssl req -new -key ssl/server-$*.key -out ssl/server-$*.csr -config server-$*.config
+# Sign the certificate with the right CA
+	openssl x509 -req -in ssl/server-$*.csr -CA ssl/serverroot.crt -CAkey ssl/serverroot.key -CAserial ssl/serverroot.srl -out ssl/server-$*.crt -extfile server-$*.config -extensions v3_req
+	rm ssl/server-$*.csr
+
+# Client certificate, signed by the client root CA:
+ssl/client.crt: ssl/client.key ssl/clientroot.crt
+# Generate a Certificate Sign Request (CSR)
+	openssl req -new -key ssl/client.key -out ssl/client.csr -config client.config
+# Sign the certificate with the right CA
+	openssl x509 -req -in ssl/client.csr -CA ssl/clientroot.crt -CAkey ssl/clientroot.key -CAserial ssl/clientroot.srl -out ssl/client.crt
+	rm ssl/client.csr
+
+sslfiles-clean:
+	rm -f $(SSLFILES) ssl/client-root.srl ssl/server-root.srl
+
+check:
+	$(prove_check)
+
+installcheck:
+	rm -rf tmp_check
+	$(prove_installcheck)
diff --git a/src/test/ssl/README b/src/test/ssl/README
new file mode 100644
index 0000000..dfd2d79
--- /dev/null
+++ b/src/test/ssl/README
@@ -0,0 +1,43 @@
+src/test/ssl/README
+
+SSL regression tests
+====================
+
+This directory contains a test suite for SSL support.
+
+Running the tests
+=================
+
+    make check
+
+Certificates
+============
+
+The test suite needs a set of public/private key pairs and certificates to
+run:
+
+serverroot.crt: CA used to sign server certificates
+clientroot.crt: CA used to sign client certificates
+server-*.crt: server certificate, with small variations in the hostnames
+              present in the certificate.
+client.crt: a client certificate, for user "ssltestuser"
+
+For convenience, these keypairs and certificates are included in the ssl/
+subdirectory, but the Makefile also contains a rule, "make sslfiles", to
+recreate them if you want to make changes.
+
+
+TODO
+====
+
+* Allow the client-side of the tests to be run on different host easily.
+  Currently, you have to manually set up the certificates for the right
+  hostname, and modify the test file to skip setting up the server. And you
+  have to modify the server to accept connections from the client host.
+
+* Add more test coverage:
+  - CRLs
+  - intermediary CAs
+  - server.crt file with multiple certificates, and the private key in
+    server.key chooses the certificate to present to clients. (and the same
+    in client-side)
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
new file mode 100644
index 0000000..0d6adc5
--- /dev/null
+++ b/src/test/ssl/ServerSetup.pm
@@ -0,0 +1,93 @@
+# This module sets up a test server, for the SSL regression tests.
+#
+# The server is configured as follows:
+#
+# - SSL enabled, with ssl/server.crt and ssl/server.key as the server cert.
+# - ssl/clientroot.crt as the CA root for validating client certs.
+# - reject non-SSL connections
+# - a database called trustdb that lets anyone in
+# - another database called certdb that uses certificate authentiction, ie.
+#   the client must present a valid certificate signed by the client CA
+# - two users, called ssltestuser and anotheruser.
+#
+# The server is configured to only accept connections from localhost. If you
+# want to run the client from another host, you'll have to configure that
+# manually. You'll also need to create a new server certificate, because the
+# one included is issued for "localhost", so the client won't accept it in
+# verify-full mode.
+#
+package ServerSetup;
+
+use strict;
+use warnings;
+use TestLib;
+
+use Exporter 'import';
+our @EXPORT = qw(
+  configure_test_server_for_ssl switch_server_cert
+);
+
+sub configure_test_server_for_ssl
+{
+  my $tempdir = $_[0];
+
+  # Create test users and databases
+  psql 'postgres', "CREATE USER ssltestuser";
+  psql 'postgres', "CREATE USER anotheruser";
+  psql 'postgres', "CREATE DATABASE trustdb";
+  psql 'postgres', "CREATE DATABASE certdb";
+
+  # enable logging etc.
+  open CONF, ">>$tempdir/pgdata/postgresql.conf";
+  print CONF "fsync=off\n";
+  print CONF "log_connections=on\n";
+  print CONF "log_hostname=on\n";
+  print CONF "log_statement=all\n";
+
+  # enable SSL and set up server key
+  print CONF "include 'sslconfig.conf'";
+
+  close CONF;
+
+
+  # Copy all server certificates and keys, and client root cert, to the data dir
+  system_or_bail "cp ssl/server-*.crt '$tempdir'/pgdata";
+  system_or_bail "cp ssl/server-*.key '$tempdir'/pgdata";
+  system_or_bail "chmod 0600 '$tempdir'/pgdata/server-*.key";
+  system_or_bail "cp ssl/clientroot.crt '$tempdir'/pgdata";
+
+  # Only accept SSL connections from localhost. Our tests don't depend on this
+  # but seems best to keep it as narrow as possible for security reasons.
+  #
+  # When connecting to certdb, also check the client certificate.
+  open HBA, ">$tempdir/pgdata/pg_hba.conf";
+  print HBA "# TYPE  DATABASE        USER            ADDRESS                 METHOD\n";
+  print HBA "hostssl trustdb         ssltestuser     127.0.0.1/32            trust\n";
+  print HBA "hostssl trustdb         ssltestuser     ::1/128                 trust\n";
+  print HBA "hostssl certdb          ssltestuser     127.0.0.1/32            cert\n";
+  print HBA "hostssl certdb          ssltestuser     ::1/128                 cert\n";
+  close HBA;
+}
+
+# Change the configuration to use given server cert file, and restart
+# the server so that the configuration takes effect.
+sub switch_server_cert
+{
+  my $tempdir = $_[0];
+  my $certfile = $_[1];
+
+  open SSLCONF, ">$tempdir/pgdata/sslconfig.conf";
+  print SSLCONF "ssl=on\n";
+  print SSLCONF "ssl_ca_file='clientroot.crt'\n";
+  print SSLCONF "ssl_cert_file='$certfile.crt'\n";
+  print SSLCONF "ssl_key_file='$certfile.key'\n";
+  close SSLCONF;
+
+  # Stop and restart server to reload the new config. We cannot use
+  # restart_test_server() because that overrides listen_addresses to only all
+  # Unix domain socket connections.
+
+  system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/pgdata", '-w';
+  system_or_bail 'pg_ctl', 'start', '-D', "$tempdir/pgdata", '-w', '-l',
+        "$tempdir/logfile";
+}
diff --git a/src/test/ssl/client.config b/src/test/ssl/client.config
new file mode 100644
index 0000000..2e9d40d
--- /dev/null
+++ b/src/test/ssl/client.config
@@ -0,0 +1,13 @@
+# An OpenSSL format CSR config file for creating a client certificate.
+#
+# The certificate is for user "ssltestuser".
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = ssltestuser
+
+# no extensions in client certs
+[ v3_req ]
diff --git a/src/test/ssl/clientroot.config b/src/test/ssl/clientroot.config
new file mode 100644
index 0000000..8b28bcf
--- /dev/null
+++ b/src/test/ssl/clientroot.config
@@ -0,0 +1,10 @@
+# An OpenSSL format CSR config file for creating the client root certificate.
+#
+# This certificate is used to sign client certificates. It is self-signed.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test CA for PostgreSQL SSL regression test client certs
diff --git a/src/test/ssl/server-cn-and-alt-names.config b/src/test/ssl/server-cn-and-alt-names.config
new file mode 100644
index 0000000..f82cceb
--- /dev/null
+++ b/src/test/ssl/server-cn-and-alt-names.config
@@ -0,0 +1,23 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+# Note: According to RFC 2818 and 6125, the CN is ignored, when SANs are
+# present. In practice, the hostname that's put in the CN field is always
+# also listed as a SAN, but we intentionally don't do that here so that we
+# can test adherence to those RFCs.
+CN = common-name.pg-ssltest.test
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = dns1.alt-name.pg-ssltest.test
+DNS.2 = dns2.alt-name.pg-ssltest.test
diff --git a/src/test/ssl/serverroot.config b/src/test/ssl/serverroot.config
new file mode 100644
index 0000000..fcac4b9
--- /dev/null
+++ b/src/test/ssl/serverroot.config
@@ -0,0 +1,10 @@
+# An OpenSSL format CSR config file for creating the server root certificate.
+#
+# This certificate is used to sign server certificates. It is self-signed.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test CA for PostgreSQL SSL regression test server certs
diff --git a/src/test/ssl/ssl/client.crt b/src/test/ssl/ssl/client.crt
new file mode 100644
index 0000000..cb4d640
--- /dev/null
+++ b/src/test/ssl/ssl/client.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBxzCCATACAQEwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
+Fw0xNDA5MTIxMzU3MjJaFw0xNDEwMTIxMzU3MjJaMBYxFDASBgNVBAMMC3NzbHRl
+c3R1c2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZAGGRpHWYdjSOMSTr
+J3r+4NNlZ95oK0K+/dLjGo/4HaiWp3bdkzQQcyzr5Y+N6n8czuM9GH0gQcI5mh0E
+8Unt/VJja8NT/lZ4fcanThP5WkfRAqOQUgcu0jP5pX5zjVfH+AV6a11UhCQykVkE
+jN5wfAAsksHPwot4eXO10yEmjwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAKpj5yUC
+GVHLjO1lF0Qqz4Ks/GeO4HyV9uioiWa+OETRsSU6G06nte0wrKpHKK/LlAjP2hq7
++kkLlipQ7JEChpstXjuXKC1esjbcXKGXZvs8YLHnJHYA0bAC0Xi0dq3RmxK+zJ9B
+f1w9JXpXrzuVC5hmO4KZmpQfdVaWu5+SC5ay
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client.key b/src/test/ssl/ssl/client.key
new file mode 100644
index 0000000..7fb85fc
--- /dev/null
+++ b/src/test/ssl/ssl/client.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDZAGGRpHWYdjSOMSTrJ3r+4NNlZ95oK0K+/dLjGo/4HaiWp3bd
+kzQQcyzr5Y+N6n8czuM9GH0gQcI5mh0E8Unt/VJja8NT/lZ4fcanThP5WkfRAqOQ
+Ugcu0jP5pX5zjVfH+AV6a11UhCQykVkEjN5wfAAsksHPwot4eXO10yEmjwIDAQAB
+AoGBAISqwfvrwuVSJzFjhEGsInse8r8FcKjZAfaIcdzciXZUk5R/j6FJdMQ11hnm
+gAxweAQhzDW2+hC1g0v6F5HE/r+72QKxA2x1qRVdWnr5k3AOS9ZG92omXmsPxf3Z
+lesnmhEzMqCP+4c8o/dhY5hTd7FcvXmYevO3/iKnYtG2spPxAkEA98EynTfYzPgS
+7KlUKPLr/vv2xvmLTEZrv+vlwO2zEOFleZJcKh7+08F6VCw4+91Y0LKJtRjtyoXR
+m/a/5d4QOQJBAOA5LLRpcUHHhQtMT0Drf9rDczg8LI+SqRQaJ2IFUeWacnwS72rb
+PeAA9ppGCoayvdh7p/pC1oNfpBUfhDt6XQcCQQDnykaSNGePDCCYbashTHRicFTm
+vx0F1XSFcCvZnglDp7yKStYTSkBa2M6b5WvCp8mqJv8TASmnqh767ZWHd4RxAkEA
+k7EVYUCS+WESoX6CNaDpdo+HNWNtdqSD+lm/1zDp4PL6tf9C9H+1IfeAd93q0Q6r
+Pg01eALOiXpV+hPoZcUTwQJAECmxAJvZB1B9J9RTlVQpMfUBSNk1zv6RsR5kqLG7
+vuCWneLUwZLF7dqtF6wKL6kJkEGY4cgaADKKZVTnJ40mLw==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
new file mode 100644
index 0000000..a7eaa35
--- /dev/null
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -0,0 +1,186 @@
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 29;
+use ServerSetup;
+
+# Like TestLib.pm, we use IPC::Run
+BEGIN
+{
+	eval {
+		require IPC::Run;
+		import IPC::Run qw(run start);
+		1;
+	} or do
+	{
+		plan skip_all => "IPC::Run not available";
+	  }
+}
+
+#### 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';
+
+my $tempdir = TestLib::tempdir;
+
+
+# Define a couple of helper functions to test connecting to the server.
+
+my $common_connstr;
+
+sub run_test_psql {
+	my $connstr = $_[0];
+	my $logstring = $_[1];
+
+	my $cmd = [ 'psql',
+				'-A', '-t',
+				'-c', "SELECT 'connected with $connstr'",
+				'-d', "$connstr"
+		];
+
+    open CLIENTLOG, ">>$tempdir/client-log" or die "Could not open client-log file";
+	print CLIENTLOG "\n# Running test: $connstr $logstring\n";
+	close CLIENTLOG;
+
+	my $result = run $cmd, '>>', "$tempdir/client-log", '2>&1';
+	return $result;
+}
+
+#
+# The first argument is a (part of a) connection string, and it's also printed
+# out as the test case name. It is appended to $common_connstr global variable,
+# which also contains a libpq connection string.
+#
+# The second argument is a hostname to connect to.
+sub test_connect_ok {
+	my $connstr = $_[0];
+
+	my $result = run_test_psql("$common_connstr $connstr", "(should succeed)");
+	ok($result, $connstr);
+}
+
+sub test_connect_fails {
+	my $connstr = $_[0];
+
+	my $result = run_test_psql("$common_connstr $connstr", "(should fail)");
+	ok(!$result, "$connstr (should fail)");
+}
+
+# The client's private key must not be world-readable. Git doesn't track
+# permissions (except for the executable bit), so they might be wrong after
+# a checkout.
+system_or_bail "chmod 0600 ssl/client.key";
+
+#### Part 0. Set up the server.
+
+diag "setting up data directory in \"$tempdir\"...";
+start_test_server($tempdir);
+configure_test_server_for_ssl($tempdir);
+switch_server_cert($tempdir, 'server-cn-only');
+
+### Part 1. Run client-side tests.
+###
+### Test that libpq accepts/rejects the connection correctly, depending
+### on sslmode and whether the server's certificate looks correct. No
+### client certificate is used in these tests.
+
+diag "running client tests...";
+
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
+
+# The server should not accept non-SSL connections
+diag "test that the server doesn't accept non-SSL connections";
+test_connect_fails("sslmode=disable");
+
+# Try without a root cert. In sslmode=require, this should work. In verify-ca
+# or verify-full mode it should fail
+diag "connect without server root cert";
+test_connect_ok   ("sslrootcert=invalid sslmode=require");
+test_connect_fails("sslrootcert=invalid sslmode=verify-ca");
+test_connect_fails("sslrootcert=invalid 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)
+diag "connect without wrong server root cert";
+test_connect_fails("sslrootcert=ssl/clientroot.crt sslmode=require");
+test_connect_fails("sslrootcert=ssl/clientroot.crt sslmode=verify-ca");
+test_connect_fails("sslrootcert=ssl/clientroot.crt sslmode=verify-full");
+
+# And finally, with the correct root cert.
+diag "connect with correct server root cert";
+test_connect_ok   ("sslrootcert=ssl/serverroot.crt sslmode=require");
+test_connect_ok   ("sslrootcert=ssl/serverroot.crt sslmode=verify-ca");
+test_connect_ok   ("sslrootcert=ssl/serverroot.crt sslmode=verify-full");
+
+# Check that connecting with verify-full fails, when the hostname doesn't
+# match the hostname in the server's certificate.
+diag "test mismatch between hostname and server certificate";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/serverroot.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("sslmode=require host=wronghost.test");
+test_connect_ok   ("sslmode=verify-ca host=wronghost.test");
+test_connect_fails("sslmode=verify-full host=wronghost.test");
+
+# Test Subject Alternative Names.
+switch_server_cert($tempdir, 'server-multiple-alt-names');
+
+diag "test hostname matching with X509 Subject Alternative Names";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/serverroot.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("host=dns1.alt-name.pg-ssltest.test");
+test_connect_ok   ("host=dns2.alt-name.pg-ssltest.test");
+test_connect_ok   ("host=foo.wildcard.pg-ssltest.test");
+
+test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
+test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
+
+# Test certificate with a single Subject Alternative Name. (this gives a
+# slightly different error message, that's all)
+switch_server_cert($tempdir, 'server-single-alt-name');
+
+diag "test hostname matching with a single X509 Subject Alternative Name";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/serverroot.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("host=dns1.alt-name.pg-ssltest.test");
+
+test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
+test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
+
+# 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($tempdir, 'server-cn-and-alt-names');
+
+diag "test certificate with both a CN and SANs";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/serverroot.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("host=dns1.alt-name.pg-ssltest.test");
+test_connect_ok   ("host=dns2.alt-name.pg-ssltest.test");
+test_connect_fails("host=common-name.pg-ssltest.test");
+
+# 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($tempdir, 'server-no-names');
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/serverroot.crt hostaddr=$SERVERHOSTADDR";
+
+test_connect_ok("sslmode=verify-ca host=common-name.pg-ssltest.test");
+test_connect_fails("sslmode=verify-full host=common-name.pg-ssltest.test");
+
+
+### Part 2. Server-side tests.
+###
+### Test certificate authorization.
+
+diag "Testing certificate authorization...";
+$common_connstr="sslrootcert=ssl/serverroot.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR";
+
+# no client cert
+test_connect_fails("user=ssltestuser sslcert=invalid");
+
+# correct client cert
+test_connect_ok   ("user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client.key");
+
+# client cert belonging to another user
+test_connect_fails("user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client.key");
#7Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Heikki Linnakangas (#6)
1 attachment(s)
Re: SSL regression test suite

On 10/06/2014 04:21 PM, Heikki Linnakangas wrote:

Here's a new version of the SSL regression suite I wrote earlier. It now
specifies both host and hostaddr in the connection string as Andres
suggested, so it no longer requires changes to network configuration. I
added a bunch of tests for the SAN feature that Alexey Klyukin wrote and
was committed earlier. Plus a lot of miscellaneous cleanup.

And here's another version. It now includes tests for CRLs, and uses a
root CA that's used to sign the server and client CA's certificates, to
test that using intermediary CAs work.

This probably needs some further cleanup before it's ready for
committing. One issues is that it creates a temporary cluster that
listens for TCP connections on localhost, which isn't safe on a
multi-user system.

This issue remains. There isn't much we can do about it; SSL doesn't
work over Unix domain sockets. We could make it work, but that's a whole
different feature.

How do people feel about including this test suite in the source tree?
It's probably not suitable for running as part of "make check-world",
but it's extremely handy if you're working on a patch related to SSL.
I'd like to commit this, even if it has some rough edges. That way we
can improve it later, rather than have it fall into oblivion. Any
objections?

- Heikki

Attachments:

ssl-regression-suite-4.patchtext/x-diff; name=ssl-regression-suite-4.patchDownload
diff --git a/src/test/Makefile b/src/test/Makefile
index 9238860..1d6f789 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -12,7 +12,7 @@ subdir = src/test
 top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS = regress isolation modules
+SUBDIRS = regress isolation modules ssl
 
 # We want to recurse to all subdirs for all standard targets, except that
 # installcheck and install should not recurse into the subdirectory "modules".
diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
new file mode 100644
index 0000000..194267b
--- /dev/null
+++ b/src/test/ssl/Makefile
@@ -0,0 +1,126 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/ssl
+#
+# Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/ssl/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/ssl
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+CERTIFICATES := server_ca server-cn-and-alt-names \
+	server-cn-only server-single-alt-name server-multiple-alt-names \
+	server-no-names server-revoked server-ss \
+	client_ca client client-revoked \
+	root_ca
+
+SSLFILES := $(CERTIFICATES:%=ssl/%.key) $(CERTIFICATES:%=ssl/%.crt) \
+	ssl/client.crl ssl/server.crl ssl/root.crl \
+	ssl/both-cas-1.crt ssl/both-cas-2.crt \
+	ssl/root+server_ca.crt ssl/root+server.crl \
+	ssl/root+client_ca.crt ssl/root+client.crl
+
+sslfiles: $(SSLFILES)
+
+ssl/new_certs_dir:
+	mkdir ssl/new_certs_dir
+
+# Rule for creating private/public key pairs
+ssl/%.key:
+	openssl genrsa -out $@ 1024
+	chmod 0600 $@
+
+# Rules for creating root CA certificates
+ssl/root_ca.crt: ssl/root_ca.key cas.config
+	touch ssl/root_ca-certindex
+	openssl req -new -out ssl/root_ca.crt -x509 -config cas.config -config root_ca.config -key ssl/root_ca.key
+	echo "01" > ssl/root_ca.srl
+
+# for client and server CAs
+ssl/%_ca.crt: ssl/%_ca.key %_ca.config ssl/root_ca.crt ssl/new_certs_dir
+	touch ssl/$*_ca-certindex
+	openssl req -new -out ssl/temp_ca.crt -config cas.config -config $*_ca.config -key ssl/$*_ca.key
+# Sign the certificate with the root CA
+	openssl ca -name root_ca -batch -config cas.config -in ssl/temp_ca.crt -out ssl/temp_ca_signed.crt
+	openssl x509 -in ssl/temp_ca_signed.crt -out ssl/$*_ca.crt # to keep just the PEM cert
+	rm ssl/temp_ca.crt ssl/temp_ca_signed.crt
+	echo "01" > ssl/$*_ca.srl
+
+# 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
+	openssl ca -name server_ca -batch -config cas.config -in ssl/server-$*.csr -out ssl/temp.crt  -extensions v3_req -extfile server-$*.config
+	openssl x509 -in ssl/temp.crt -out ssl/server-$*.crt # to keep just the PEM cert
+	rm ssl/server-$*.csr
+
+# Self-signed version of server-cn-only.crt
+ssl/server-ss.crt: ssl/server-cn-only.key ssl/server-cn-only.crt server-cn-only.config
+	openssl req -new -key ssl/server-cn-only.key -out ssl/server-ss.csr -config server-cn-only.config
+	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
+
+# Client certificate, signed by the client CA:
+ssl/client.crt: ssl/client.key ssl/client_ca.crt
+	openssl req -new -key ssl/client.key -out ssl/client.csr -config client.config
+	openssl ca -name client_ca -batch -out ssl/temp.crt -config cas.config -infiles ssl/client.csr
+	openssl x509 -in ssl/temp.crt -out ssl/client.crt # to keep just the PEM cert
+	rm ssl/client.csr ssl/temp.crt
+
+# 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
+	openssl ca -name client_ca -batch -out ssl/temp.crt -config cas.config -infiles ssl/client-revoked.csr
+	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
+
+# Root certificate files that contains both CA certificates, for testing
+# that multiple certificates can be used.
+ssl/both-cas-1.crt: ssl/root_ca.crt ssl/client_ca.crt ssl/server_ca.crt
+	cat $^ > $@
+
+# The same, but the certs are in different order
+ssl/both-cas-2.crt: ssl/root_ca.crt ssl/server_ca.crt ssl/client_ca.crt
+	cat $^ > $@
+
+# A root certificate file for the client, to validate server certs.
+ssl/root+server_ca.crt: ssl/root_ca.crt ssl/server_ca.crt
+	cat $^ > $@
+
+# and for the server, to validate client certs
+ssl/root+client_ca.crt: ssl/root_ca.crt ssl/client_ca.crt
+	cat $^ > $@
+
+#### 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/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/root.crl: ssl/root_ca.crt
+	openssl ca -config cas.config -name root_ca -gencrl -out ssl/root.crl
+
+# 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
+	cat $^ > $@
+ssl/root+client.crl: ssl/root.crl ssl/client.crl
+	cat $^ > $@
+
+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
+
+check:
+	$(prove_check)
+
+installcheck:
+	rm -rf tmp_check
+	$(prove_installcheck)
diff --git a/src/test/ssl/README b/src/test/ssl/README
new file mode 100644
index 0000000..22b1bd1
--- /dev/null
+++ b/src/test/ssl/README
@@ -0,0 +1,78 @@
+src/test/ssl/README
+
+SSL regression tests
+====================
+
+This directory contains a test suite for SSL support.
+
+Running the tests
+=================
+
+    make check
+
+Certificates
+============
+
+The test suite needs a set of public/private key pairs and certificates to
+run:
+
+root_ca
+	root CA, use to sign the server and client CA certificates.
+
+server_ca
+	CA used to sign server certificates.
+
+client_ca
+	CA used to sign client certificates.
+
+server-cn-only
+server-cn-and-alt-names
+server-single-alt-name
+server-multiple-alt-names
+server-no-names
+	server certificates, with small variations in the hostnames present
+        in the certificate. Signed by server_ca.
+
+server-ss
+	same as server-cn-only, but self-signed.
+
+client
+	a client certificate, for user "ssltestuser". Signed by client_ca.
+
+client-revoked
+	like "client", but marked as revoked in the client CA's CRL.
+
+In addition, there are a few files that combine various certificates together
+in the same file:
+
+both-cas-1
+	Contains root_ca.crt, client_ca.crt and server_ca.crt, in that order.
+
+both-cas-2
+	Contains root_ca.crt, server_ca.crt and client_ca.crt, in that order.
+
+root+server_ca
+	Contains root_crt and server_ca.crt. For use as client's "sslrootcert"
+	option.
+
+root+client_ca
+	Contains root_crt and client_ca.crt. For use as server's "ssl_ca_file".
+
+There are also CRLs for each of the CAs: root.crl, server.crl and client.crl.
+
+For convenience, all of these keypairs and certificates are included in the
+ssl/ subdirectory. The Makefile also contains a rule, "make sslfiles", to
+recreate them if you need to make changes.
+
+TODO
+====
+
+* Allow the client-side of the tests to be run on different host easily.
+  Currently, you have to manually set up the certificates for the right
+  hostname, and modify the test file to skip setting up the server. And you
+  have to modify the server to accept connections from the client host.
+
+* Add more test coverage:
+  - server.crt file with multiple certificates, and the private key in
+    server.key chooses the certificate to present to clients. (and the same
+    in client-side)
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
new file mode 100644
index 0000000..358a3c1
--- /dev/null
+++ b/src/test/ssl/ServerSetup.pm
@@ -0,0 +1,99 @@
+# This module sets up a test server, for the SSL regression tests.
+#
+# The server is configured as follows:
+#
+# - SSL enabled, with the server certificate specified by argument to
+#   switch_server_cert function.
+# - ssl/root+client_ca.crt as the CA root for validating client certs.
+# - reject non-SSL connections
+# - a database called trustdb that lets anyone in
+# - another database called certdb that uses certificate authentiction, ie.
+#   the client must present a valid certificate signed by the client CA
+# - two users, called ssltestuser and anotheruser.
+#
+# The server is configured to only accept connections from localhost. If you
+# want to run the client from another host, you'll have to configure that
+# manually. You'll also need to create a new server certificate, because the
+# one included is issued for "localhost", so the client won't accept it in
+# verify-full mode.
+#
+package ServerSetup;
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More;
+
+use Exporter 'import';
+our @EXPORT = qw(
+  configure_test_server_for_ssl switch_server_cert
+);
+
+sub configure_test_server_for_ssl
+{
+  my $tempdir = $_[0];
+
+  # Create test users and databases
+  psql 'postgres', "CREATE USER ssltestuser";
+  psql 'postgres', "CREATE USER anotheruser";
+  psql 'postgres', "CREATE DATABASE trustdb";
+  psql 'postgres', "CREATE DATABASE certdb";
+
+  # enable logging etc.
+  open CONF, ">>$tempdir/pgdata/postgresql.conf";
+  print CONF "fsync=off\n";
+  print CONF "log_connections=on\n";
+  print CONF "log_hostname=on\n";
+  print CONF "log_statement=all\n";
+
+  # enable SSL and set up server key
+  print CONF "include 'sslconfig.conf'";
+
+  close CONF;
+
+
+  # Copy all server certificates and keys, and client root cert, to the data dir
+  system_or_bail "cp ssl/server-*.crt '$tempdir'/pgdata";
+  system_or_bail "cp ssl/server-*.key '$tempdir'/pgdata";
+  system_or_bail "chmod 0600 '$tempdir'/pgdata/server-*.key";
+  system_or_bail "cp ssl/root+client_ca.crt '$tempdir'/pgdata";
+  system_or_bail "cp ssl/root+client.crl '$tempdir'/pgdata";
+
+  # Only accept SSL connections from localhost. Our tests don't depend on this
+  # but seems best to keep it as narrow as possible for security reasons.
+  #
+  # When connecting to certdb, also check the client certificate.
+  open HBA, ">$tempdir/pgdata/pg_hba.conf";
+  print HBA "# TYPE  DATABASE        USER            ADDRESS                 METHOD\n";
+  print HBA "hostssl trustdb         ssltestuser     127.0.0.1/32            trust\n";
+  print HBA "hostssl trustdb         ssltestuser     ::1/128                 trust\n";
+  print HBA "hostssl certdb          ssltestuser     127.0.0.1/32            cert\n";
+  print HBA "hostssl certdb          ssltestuser     ::1/128                 cert\n";
+  close HBA;
+}
+
+# Change the configuration to use given server cert file, and restart
+# the server so that the configuration takes effect.
+sub switch_server_cert
+{
+  my $tempdir = $_[0];
+  my $certfile = $_[1];
+
+  diag "Restarting server with certfile \"$certfile\"...";
+
+  open SSLCONF, ">$tempdir/pgdata/sslconfig.conf";
+  print SSLCONF "ssl=on\n";
+  print SSLCONF "ssl_ca_file='root+client_ca.crt'\n";
+  print SSLCONF "ssl_cert_file='$certfile.crt'\n";
+  print SSLCONF "ssl_key_file='$certfile.key'\n";
+  print SSLCONF "ssl_crl_file='root+client.crl'\n";
+  close SSLCONF;
+
+  # Stop and restart server to reload the new config. We cannot use
+  # restart_test_server() because that overrides listen_addresses to only all
+  # Unix domain socket connections.
+
+  system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/pgdata", '-w';
+  system_or_bail 'pg_ctl', 'start', '-D', "$tempdir/pgdata", '-w', '-l',
+        "$tempdir/logfile";
+}
diff --git a/src/test/ssl/cas.config b/src/test/ssl/cas.config
new file mode 100644
index 0000000..9c6cbb9
--- /dev/null
+++ b/src/test/ssl/cas.config
@@ -0,0 +1,65 @@
+# This file contains the configuration for all the CAs.
+
+[ req ]
+prompt                 = no
+req_extensions         = v3_req
+
+# For Subject Alternative Names
+[ v3_req ]
+subjectAltName = @alt_names
+
+# Root CA, used to sign the certificates of the intermediary server and
+# client CAs.
+[ root_ca ]
+dir = ./ssl/
+database = ./ssl/root_ca-certindex
+serial = ./ssl/root_ca.srl
+default_md = sha1
+default_days= 10000
+default_crl_days= 10000
+certificate = ./ssl/root_ca.crt
+private_key = ./ssl/root_ca.key
+new_certs_dir = ./ssl/new_certs_dir
+policy					= policy_match
+email_in_dn				= no
+
+# CA used to sign all the server certificates.
+[ server_ca ]
+dir = ./ssl/
+database = ./ssl/server_ca-certindex
+default_md = sha1
+default_days= 10000
+default_crl_days= 10000
+certificate = ./ssl/server_ca.crt
+private_key = ./ssl/server_ca.key
+new_certs_dir = ./ssl/new_certs_dir
+serial = ./ssl/server_ca.srl
+policy					= policy_match
+email_in_dn				= no
+unique_subject = no
+crl = ./ssl/server.crl
+
+# CA used to sign all the client certificates.
+[ client_ca ]
+dir = ./ssl/
+database = ./ssl/client_ca-certindex
+default_md = sha1
+default_days= 10000
+default_crl_days= 10000
+certificate = ./ssl/client_ca.crt
+private_key = ./ssl/client_ca.key
+new_certs_dir = ./ssl/new_certs_dir
+serial = ./ssl/client_ca.srl
+policy					= policy_match
+email_in_dn				= no
+unique_subject = no
+crl = ./ssl/client.crl
+
+# This is common for all CAs.
+[ policy_match ]
+countryName = optional
+stateOrProvinceName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = optional
+emailAddress = optional
diff --git a/src/test/ssl/client.config b/src/test/ssl/client.config
new file mode 100644
index 0000000..2e9d40d
--- /dev/null
+++ b/src/test/ssl/client.config
@@ -0,0 +1,13 @@
+# An OpenSSL format CSR config file for creating a client certificate.
+#
+# The certificate is for user "ssltestuser".
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = ssltestuser
+
+# no extensions in client certs
+[ v3_req ]
diff --git a/src/test/ssl/client_ca.config b/src/test/ssl/client_ca.config
new file mode 100644
index 0000000..ef93d59
--- /dev/null
+++ b/src/test/ssl/client_ca.config
@@ -0,0 +1,11 @@
+# An OpenSSL format CSR config file for creating the client root certificate.
+# This configuration file is also used when operating the CA.
+#
+# This certificate is used to sign client certificates. It is self-signed.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test CA for PostgreSQL SSL regression test client certs
diff --git a/src/test/ssl/root_ca.config b/src/test/ssl/root_ca.config
new file mode 100644
index 0000000..72484d0
--- /dev/null
+++ b/src/test/ssl/root_ca.config
@@ -0,0 +1,9 @@
+# A root certificate authority. The server and client CA's certificates
+# are signed by this root CA.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test root CA for PostgreSQL SSL regression test suite
diff --git a/src/test/ssl/server-cn-and-alt-names.config b/src/test/ssl/server-cn-and-alt-names.config
new file mode 100644
index 0000000..6734251
--- /dev/null
+++ b/src/test/ssl/server-cn-and-alt-names.config
@@ -0,0 +1,25 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+# This certificate contains both a CN, and SANs.
+
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+# Note: According to RFC 2818 and 6125, the CN is ignored, when SANs are
+# present. In practice, the hostname that's put in the CN field is always
+# also listed as a SAN, but we intentionally don't do that here so that we
+# can test adherence to those RFCs.
+CN = common-name.pg-ssltest.test
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = dns1.alt-name.pg-ssltest.test
+DNS.2 = dns2.alt-name.pg-ssltest.test
diff --git a/src/test/ssl/server-cn-only.config b/src/test/ssl/server-cn-only.config
new file mode 100644
index 0000000..b4efff8
--- /dev/null
+++ b/src/test/ssl/server-cn-only.config
@@ -0,0 +1,15 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+CN = common-name.pg-ssltest.test
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+
diff --git a/src/test/ssl/server-multiple-alt-names.config b/src/test/ssl/server-multiple-alt-names.config
new file mode 100644
index 0000000..fe230df
--- /dev/null
+++ b/src/test/ssl/server-multiple-alt-names.config
@@ -0,0 +1,20 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+# This certificate contains multiple SANs, and no CN.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = dns1.alt-name.pg-ssltest.test
+DNS.2 = dns2.alt-name.pg-ssltest.test
+DNS.3 = *.wildcard.pg-ssltest.test
diff --git a/src/test/ssl/server-no-names.config b/src/test/ssl/server-no-names.config
new file mode 100644
index 0000000..92cf779
--- /dev/null
+++ b/src/test/ssl/server-no-names.config
@@ -0,0 +1,17 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+# This certificate contains no CN, nor SANs. Not very useful, but make
+# sure the client can handle it gracefully.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+
+[ alt_names ]
diff --git a/src/test/ssl/server-revoked.config b/src/test/ssl/server-revoked.config
new file mode 100644
index 0000000..5dbd74b
--- /dev/null
+++ b/src/test/ssl/server-revoked.config
@@ -0,0 +1,17 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+# This is identical to server-cn-only certificate, but this one is revoked
+# later.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+CN = common-name.pg-ssltest.test
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+
diff --git a/src/test/ssl/server-single-alt-name.config b/src/test/ssl/server-single-alt-name.config
new file mode 100644
index 0000000..2b3ddf4
--- /dev/null
+++ b/src/test/ssl/server-single-alt-name.config
@@ -0,0 +1,18 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+# This certificate has a single SAN, and no CN.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+req_extensions         = v3_req
+prompt                 = no
+
+[ req_distinguished_name ]
+OU = PostgreSQL test suite
+
+# For Subject Alternative Names
+[ v3_req ]
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = single.alt-name.pg-ssltest.test
diff --git a/src/test/ssl/server_ca.config b/src/test/ssl/server_ca.config
new file mode 100644
index 0000000..d6126d5
--- /dev/null
+++ b/src/test/ssl/server_ca.config
@@ -0,0 +1,11 @@
+# An OpenSSL format CSR config file for creating the server root certificate.
+# This configuration file is also used when operating the CA.
+#
+# This certificate is used to sign server certificates. It is self-signed.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN                     = Test CA for PostgreSQL SSL regression test server certs
diff --git a/src/test/ssl/ssl/.gitignore b/src/test/ssl/ssl/.gitignore
new file mode 100644
index 0000000..8feb864
--- /dev/null
+++ b/src/test/ssl/ssl/.gitignore
@@ -0,0 +1,2 @@
+*.old
+new_certs_dir
diff --git a/src/test/ssl/ssl/both-cas-1.crt b/src/test/ssl/ssl/both-cas-1.crt
new file mode 100644
index 0000000..7229f50
--- /dev/null
+++ b/src/test/ssl/ssl/both-cas-1.crt
@@ -0,0 +1,39 @@
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAWACCQD13ziQMRDLGTANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVU
+ZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBz
+dWl0ZTAeFw0xNDEyMDQxMTUyMDFaFw0xNTAxMDMxMTUyMDFaMEAxPjA8BgNVBAMM
+NVRlc3Qgcm9vdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0
+IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7TLars/P/obbNlsz
+cX/wZFnZ97L4dAiJAE+ZusoTqLalRnPbQEtrPfMA/eL/gjq69ehnPcehMIxnYRAV
++xqOnMiUacf+6TQBrjrnfCQZkYkngzYajTqhQogdM7sUHtvBvTs1EkjdVznQUN9B
+BRZi6zEvUMkc8/+KaiEKc0zAKQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAhmmj+R
+XP1+AREKWE33P8AkXTTGkXMvULZSgteHWxbBc08TbxJLTsqDvwp0lY/9nH48Ejx5
+XYIdDAED9Bwsm50y9u5p5OsO9YqHJfIsC9+Ui3paDHU543Y8CtZC4Ye5OcFn4/lp
+ew5Ix9E0LHJlY+LCfVEKSV0jDP6aMsYETpIe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQIwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIJfmyeyPe6g3+16+WsGB8LRMW
+zfMXKKjxsBd3Zu2ka7jvfKe4ockw87kY01k0G4NHJgWH5zO2OuCNDOa8z+GLqSSO
+LYoMvik9+BLgFR8zBPshy77Rpb3CtpDjJUAU8TWQOT0cC56IwEgj2zswctqKIeFg
+ogkTbfg5KTNKSd4VUwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIHyYzFTIvvrUFFD
+yxhU3xyH6nx7HC47fxN+1kQjDa4MjvNsm/dOKETvS4b6GUKOudEKENBHzJW08hhs
+vn8uvmWEmyYcUyhp9r5lH2oaa6fySbnc+PE8YD2WNe+et1OdIMwqVwOegCeI85FN
+UtZk2tJjiRXJxBlheaaBxrzYjOBO
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQEwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC15tOzDBVKaCRDz9L5LMpPk8DR
+RGHHOe4OuO6WTkUzjbjuKyiQbmtcp00R4dULbSM57ESvI/Ny0gPt+J/QKAOG8S5t
+09wDpKxKcgZSZ6Nd6FaK+D+ZhUVAkP3hB0ba0wo1JZff/0e4B+VJhXTjl7RRHfbr
+AEuDYFxv9T3K/Jq04wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJys1pnYvO+u8Wca
+6xUToGMpqTnImKa+dX8tMKsp6mXAN/dWrOVMDWnjBhQxShhAZBsaJ4iUeXPJlctw
+KzkUCQo6BsUbPMTSQlPuyHHdZBOTHDIW4SylKaBQvkundkhhBO7aHwFV3QjxZKcH
+XqpGyY2ryrgdj2D4+H55NDXYjj/m
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/both-cas-2.crt b/src/test/ssl/ssl/both-cas-2.crt
new file mode 100644
index 0000000..d85896c
--- /dev/null
+++ b/src/test/ssl/ssl/both-cas-2.crt
@@ -0,0 +1,39 @@
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAWACCQD13ziQMRDLGTANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVU
+ZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBz
+dWl0ZTAeFw0xNDEyMDQxMTUyMDFaFw0xNTAxMDMxMTUyMDFaMEAxPjA8BgNVBAMM
+NVRlc3Qgcm9vdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0
+IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7TLars/P/obbNlsz
+cX/wZFnZ97L4dAiJAE+ZusoTqLalRnPbQEtrPfMA/eL/gjq69ehnPcehMIxnYRAV
++xqOnMiUacf+6TQBrjrnfCQZkYkngzYajTqhQogdM7sUHtvBvTs1EkjdVznQUN9B
+BRZi6zEvUMkc8/+KaiEKc0zAKQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAhmmj+R
+XP1+AREKWE33P8AkXTTGkXMvULZSgteHWxbBc08TbxJLTsqDvwp0lY/9nH48Ejx5
+XYIdDAED9Bwsm50y9u5p5OsO9YqHJfIsC9+Ui3paDHU543Y8CtZC4Ye5OcFn4/lp
+ew5Ix9E0LHJlY+LCfVEKSV0jDP6aMsYETpIe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQEwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC15tOzDBVKaCRDz9L5LMpPk8DR
+RGHHOe4OuO6WTkUzjbjuKyiQbmtcp00R4dULbSM57ESvI/Ny0gPt+J/QKAOG8S5t
+09wDpKxKcgZSZ6Nd6FaK+D+ZhUVAkP3hB0ba0wo1JZff/0e4B+VJhXTjl7RRHfbr
+AEuDYFxv9T3K/Jq04wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJys1pnYvO+u8Wca
+6xUToGMpqTnImKa+dX8tMKsp6mXAN/dWrOVMDWnjBhQxShhAZBsaJ4iUeXPJlctw
+KzkUCQo6BsUbPMTSQlPuyHHdZBOTHDIW4SylKaBQvkundkhhBO7aHwFV3QjxZKcH
+XqpGyY2ryrgdj2D4+H55NDXYjj/m
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQIwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIJfmyeyPe6g3+16+WsGB8LRMW
+zfMXKKjxsBd3Zu2ka7jvfKe4ockw87kY01k0G4NHJgWH5zO2OuCNDOa8z+GLqSSO
+LYoMvik9+BLgFR8zBPshy77Rpb3CtpDjJUAU8TWQOT0cC56IwEgj2zswctqKIeFg
+ogkTbfg5KTNKSd4VUwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIHyYzFTIvvrUFFD
+yxhU3xyH6nx7HC47fxN+1kQjDa4MjvNsm/dOKETvS4b6GUKOudEKENBHzJW08hhs
+vn8uvmWEmyYcUyhp9r5lH2oaa6fySbnc+PE8YD2WNe+et1OdIMwqVwOegCeI85FN
+UtZk2tJjiRXJxBlheaaBxrzYjOBO
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client-revoked.crt b/src/test/ssl/ssl/client-revoked.crt
new file mode 100644
index 0000000..5503c8f
--- /dev/null
+++ b/src/test/ssl/ssl/client-revoked.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBxzCCATACAQIwDQYJKoZIhvcNAQEFBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
+Fw0xNDEyMDQxMTUyMDFaFw00MjA0MjExMTUyMDFaMBYxFDASBgNVBAMMC3NzbHRl
+c3R1c2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDg8LJSdf9nhkMSYmhn
++F3yqVSu+UXTcmPejKTBZRd4moLL2ti41K3M2xZDiZOn8V7To9AAD/tN3lPkn2y4
+ZqKD+zChVPJ5yUSpenVxKRckyK2pO4aNItgt60YJp119IG7mH/nfobl6nraI3xxk
+WGyT7O2sOOpokW9fF4DJfLe6lQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAMTRz+Nl
+hJeym72oGRIiJ7ODsIS4cVRQ13TwEtV/wPZ+skc/V7RFfHro5hyRwkbfIoZvhCld
+ZkyAXhQyiru0JzoklfEbOtwuC+J+XvXQ7aupIrnGRHF0yyEIYAEhgSRzaUvKWKlB
+gttm9tKwJuVCBYHh+cCGU0LnR3jhxVUqaL9d
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client-revoked.key b/src/test/ssl/ssl/client-revoked.key
new file mode 100644
index 0000000..1752e02
--- /dev/null
+++ b/src/test/ssl/ssl/client-revoked.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDg8LJSdf9nhkMSYmhn+F3yqVSu+UXTcmPejKTBZRd4moLL2ti4
+1K3M2xZDiZOn8V7To9AAD/tN3lPkn2y4ZqKD+zChVPJ5yUSpenVxKRckyK2pO4aN
+Itgt60YJp119IG7mH/nfobl6nraI3xxkWGyT7O2sOOpokW9fF4DJfLe6lQIDAQAB
+AoGBAKh3PGaL3zPuly8eqqkrl1kVPsopAQXCx083MHFzP+fgeJMqnWOYTW5+qyb7
+061VFbsWFcLmNUV1fIleaTOWEqG0BXkG8VgS0sxEEV6N4sR6ePK2tOA81ZxFhXOR
+bJx8oys2U0kZZVRLvuj5+KjLMSBwWHEIpobE+zz4F9xcTXjlAkEA/6O0yApJ7sBQ
+XS54tK3m7NCYU8yEUD3Yidg9SmaYjiNwhLZ2e9KreQEkcbiHR8R0FHUxzKb/dItt
+2SauaHpCzwJBAOFB6DF0KM0XsfK209LoGvcA6t/aazOtbBlq9I49siKBE74Z7wJu
+0xsH8ndCkBPatoSn2ZuuXv3ozGNU9J+JFVsCQQDAOdk2koYFgZbseoVJV3rNmAzy
+9laH//lTrcZoq70LJJr3MDzn3wIRe0psONWAobinqXhI60or2KxBHVUIOucBAkEA
+qfDSHzU2bvx4aNeb2Vr4tO7BRB8Bj5w/mLGDTSiokrV00o+4LMq1g4gsWeMi1YfE
++TG0z2nvCnoucKYwY4fFTwJAbW0FLKUzRvX8dM3nXxs8vGktH8TH+dqsUfrZt9ms
+2nF1wwAD2OUXf94dnRvlgSMC7RMbTPAeoHnkqCpb1w++lg==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/client.crl b/src/test/ssl/ssl/client.crl
new file mode 100644
index 0000000..83f9e67
--- /dev/null
+++ b/src/test/ssl/ssl/client.crl
@@ -0,0 +1,9 @@
+-----BEGIN X509 CRL-----
+MIIBHTCBhzANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0xNDEy
+MDQxMTUyMDFaFw00MjA0MjExMTUyMDFaMBQwEgIBAhcNMTQxMjA0MTE1MjAxWjAN
+BgkqhkiG9w0BAQUFAAOBgQDGZTiMkukrcJheXCzKlNKKTyteOmR/sQYj36nyyV2U
+Iac9gYSYAb8ecjUrtcL/innhDAupGUxGR3QltSPo6q1yn9L8BJWJIz+BqK6aV4fb
+3lqGtTQKr+8qaKC7mi5TBafJmkUiNsbclNZl/ooQPW+Gzm++JpunK4uGzdW+4I6/
+fQ==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/client.crt b/src/test/ssl/ssl/client.crt
new file mode 100644
index 0000000..9c24e0e
--- /dev/null
+++ b/src/test/ssl/ssl/client.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBxzCCATACAQEwDQYJKoZIhvcNAQEFBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe
+Fw0xNDEyMDQxMTUyMDFaFw00MjA0MjExMTUyMDFaMBYxFDASBgNVBAMMC3NzbHRl
+c3R1c2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDErK6Z/Mv8oTKe026e
+SXhtrAeHPVAWM69Sb3zeQb8bnoqusfc0jQhaqvQqq6UYyCPRsH2qAp8B8Cdf93/B
+I5WIKGWcj107fB+dxqeuCS8QHyvO9Ygr9KYHWMKMz4DR+AsWYqoBXxgFjzwDAQB9
+SZRMRgUyHR+qQRGEXkgMLgwrbQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACnd/MjL
+pGmAHIdbMKcIqMPmAhnkzyfoPE+V6V/6Fm3f9iwHYr4ivxTMhdTffFFyVMxvCDEw
+a0Hlx/wPPnbvsJLiWCHYzXJsyISarIU+euxUYQY1w2tTkmgITESM1eDq2SOMnvqK
+iLoSyGNPrq2tAWPTyx7il1Q72ZNl6w3w+uY4
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client.key b/src/test/ssl/ssl/client.key
new file mode 100644
index 0000000..786e529
--- /dev/null
+++ b/src/test/ssl/ssl/client.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDErK6Z/Mv8oTKe026eSXhtrAeHPVAWM69Sb3zeQb8bnoqusfc0
+jQhaqvQqq6UYyCPRsH2qAp8B8Cdf93/BI5WIKGWcj107fB+dxqeuCS8QHyvO9Ygr
+9KYHWMKMz4DR+AsWYqoBXxgFjzwDAQB9SZRMRgUyHR+qQRGEXkgMLgwrbQIDAQAB
+AoGAbbKLaKRR+sTGgUQY7Py5ySIsyMfwBZIqdeZtVWKCf5s8axgkdBE92aSEr9Ax
+M9Nd9zVjwhHYMrKKo8JeZZG9csrt/XxgHXDbp+6y4lx0SW1XOmOp39K7h9mUmEVj
+XtICn75z4xYvJDG61xjqtrkh0lKaDr87VDJuuIjbcB2RdNkCQQD3A0Jue8hjoKhN
+H/CjtF/zfL/rkY0BO2Ryyp882AsUZu4y6YbAkrUJbySVIEU7oHleZaTJ/tzC7Ifs
+3XNO7iTnAkEAy9SPQNGU0SNkR2/H7x5JdllMOlOZzl+YUMQpDzH08o/u3RnfcFM8
+72rYJenxLorKpuG5YXTYxRFet4GIhMqOiwJBAOHStgoh2lrSxurzl2FihxIoa6Em
+iP2mWbfkbF4IuWBmlcAv5QTrWt0MIiq/vOu9Uxgs3tHY0eTWr5GqB0AS0eMCQQCw
+S80LlzpMGXxmfTxEicGoZ1wTJrPlV7F6Se/pgKAIHI3RFsu3b4dI3PTO9iTwyIK3
+DI02ycWjzX5K4fKeSEQ5AkEAr87kSTl5xM9Z9Cew+FX3ICJRbRNChJEsMPgo+2GW
+PVrzAxEMk/zP0vb3Mjf5yYjpCYPF0BCgVRsbmN86DE5bng==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/client_ca.crt b/src/test/ssl/ssl/client_ca.crt
new file mode 100644
index 0000000..de21b49
--- /dev/null
+++ b/src/test/ssl/ssl/client_ca.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQIwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIJfmyeyPe6g3+16+WsGB8LRMW
+zfMXKKjxsBd3Zu2ka7jvfKe4ockw87kY01k0G4NHJgWH5zO2OuCNDOa8z+GLqSSO
+LYoMvik9+BLgFR8zBPshy77Rpb3CtpDjJUAU8TWQOT0cC56IwEgj2zswctqKIeFg
+ogkTbfg5KTNKSd4VUwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIHyYzFTIvvrUFFD
+yxhU3xyH6nx7HC47fxN+1kQjDa4MjvNsm/dOKETvS4b6GUKOudEKENBHzJW08hhs
+vn8uvmWEmyYcUyhp9r5lH2oaa6fySbnc+PE8YD2WNe+et1OdIMwqVwOegCeI85FN
+UtZk2tJjiRXJxBlheaaBxrzYjOBO
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/client_ca.key b/src/test/ssl/ssl/client_ca.key
new file mode 100644
index 0000000..3300188
--- /dev/null
+++ b/src/test/ssl/ssl/client_ca.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDIJfmyeyPe6g3+16+WsGB8LRMWzfMXKKjxsBd3Zu2ka7jvfKe4
+ockw87kY01k0G4NHJgWH5zO2OuCNDOa8z+GLqSSOLYoMvik9+BLgFR8zBPshy77R
+pb3CtpDjJUAU8TWQOT0cC56IwEgj2zswctqKIeFgogkTbfg5KTNKSd4VUwIDAQAB
+AoGBALNvsFu+IFuiFKgLsGT1fZr2Qi3ot+5kSopbp74pbhZBaUxzwl451YjoiGJk
+YI3huKEZyk2cDvVp9ZUfIuHVsUsRkUtlMYAWJoxypbLWFw0efa9TNDbsoxGSjs8N
+TCZOqK6VKEbckTd2Mg8vanB+A8PswOPW94es32Y9XKwBaFsJAkEA5WVHtYs8aczd
+uJMuteUkv2R0OFL8wgIgkXRyk0BNJjVYwe/DbvW/J6W2DTsvoeFMZ9U+p2tEX9ab
+ak7RlCFNtwJBAN9cWRfVzKY6P62UZmIdsvDYJNaWaamfZguKx69q0FD1jcjl0C8R
+3w6xCVrGQCPbbQibNTLbIKPC/jrUcu6c9UUCQQDNiNGXeAnJQiXnGvjfQVCLrBX1
+4WVW71D/Arcl+JcnhOTh31HcOZPski1r7XvgL12mKwrYNuQser0Fo1lkv/JBAkBx
+VOUrz+KP8Xw/8c1lOVaDF9jRPO6OD3/ymU8qtZLPkViIt/rC91lrle5+LZt71ilj
+tYTvsfnEvfrLFOLgKanVAkEAvyofcM5gr7gTiC+XxhjUyDNn2lYwoog+D67E6YvL
+chheY2FNRrqpCi0Zhi8KlUXnp4wtHA6zBW46l1xSxz4lYg==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/root+client.crl b/src/test/ssl/ssl/root+client.crl
new file mode 100644
index 0000000..47b8ab1
--- /dev/null
+++ b/src/test/ssl/ssl/root+client.crl
@@ -0,0 +1,17 @@
+-----BEGIN X509 CRL-----
+MIIBBDBvMA0GCSqGSIb3DQEBBQUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0xNDEyMDQx
+MTUyMDFaFw00MjA0MjExMTUyMDFaMA0GCSqGSIb3DQEBBQUAA4GBAK7EbBLD03t6
+zv2yRS6ByDg7X9CPbPVReUQ21ntI652lsJ4veAJeSWQXITEjC/mt+VkN8pKH8eEg
+hp0vZmS7zIzL+UdPZkJYokAdmBsmP1ymDvOHd52XssjM1e6d7pNKwk6Z40x6Tpvq
+cStL3sC4tomx+vn7zzSUcS3hwdcHvnwZ
+-----END X509 CRL-----
+-----BEGIN X509 CRL-----
+MIIBHTCBhzANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRzFw0xNDEy
+MDQxMTUyMDFaFw00MjA0MjExMTUyMDFaMBQwEgIBAhcNMTQxMjA0MTE1MjAxWjAN
+BgkqhkiG9w0BAQUFAAOBgQDGZTiMkukrcJheXCzKlNKKTyteOmR/sQYj36nyyV2U
+Iac9gYSYAb8ecjUrtcL/innhDAupGUxGR3QltSPo6q1yn9L8BJWJIz+BqK6aV4fb
+3lqGtTQKr+8qaKC7mi5TBafJmkUiNsbclNZl/ooQPW+Gzm++JpunK4uGzdW+4I6/
+fQ==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+client_ca.crt b/src/test/ssl/ssl/root+client_ca.crt
new file mode 100644
index 0000000..ebe1520
--- /dev/null
+++ b/src/test/ssl/ssl/root+client_ca.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAWACCQD13ziQMRDLGTANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVU
+ZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBz
+dWl0ZTAeFw0xNDEyMDQxMTUyMDFaFw0xNTAxMDMxMTUyMDFaMEAxPjA8BgNVBAMM
+NVRlc3Qgcm9vdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0
+IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7TLars/P/obbNlsz
+cX/wZFnZ97L4dAiJAE+ZusoTqLalRnPbQEtrPfMA/eL/gjq69ehnPcehMIxnYRAV
++xqOnMiUacf+6TQBrjrnfCQZkYkngzYajTqhQogdM7sUHtvBvTs1EkjdVznQUN9B
+BRZi6zEvUMkc8/+KaiEKc0zAKQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAhmmj+R
+XP1+AREKWE33P8AkXTTGkXMvULZSgteHWxbBc08TbxJLTsqDvwp0lY/9nH48Ejx5
+XYIdDAED9Bwsm50y9u5p5OsO9YqHJfIsC9+Ui3paDHU543Y8CtZC4Ye5OcFn4/lp
+ew5Ix9E0LHJlY+LCfVEKSV0jDP6aMsYETpIe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQIwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3QgY2xpZW50IGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIJfmyeyPe6g3+16+WsGB8LRMW
+zfMXKKjxsBd3Zu2ka7jvfKe4ockw87kY01k0G4NHJgWH5zO2OuCNDOa8z+GLqSSO
+LYoMvik9+BLgFR8zBPshy77Rpb3CtpDjJUAU8TWQOT0cC56IwEgj2zswctqKIeFg
+ogkTbfg5KTNKSd4VUwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIHyYzFTIvvrUFFD
+yxhU3xyH6nx7HC47fxN+1kQjDa4MjvNsm/dOKETvS4b6GUKOudEKENBHzJW08hhs
+vn8uvmWEmyYcUyhp9r5lH2oaa6fySbnc+PE8YD2WNe+et1OdIMwqVwOegCeI85FN
+UtZk2tJjiRXJxBlheaaBxrzYjOBO
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/root+server.crl b/src/test/ssl/ssl/root+server.crl
new file mode 100644
index 0000000..e904233
--- /dev/null
+++ b/src/test/ssl/ssl/root+server.crl
@@ -0,0 +1,17 @@
+-----BEGIN X509 CRL-----
+MIIBBDBvMA0GCSqGSIb3DQEBBQUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0xNDEyMDQx
+MTUyMDFaFw00MjA0MjExMTUyMDFaMA0GCSqGSIb3DQEBBQUAA4GBAK7EbBLD03t6
+zv2yRS6ByDg7X9CPbPVReUQ21ntI652lsJ4veAJeSWQXITEjC/mt+VkN8pKH8eEg
+hp0vZmS7zIzL+UdPZkJYokAdmBsmP1ymDvOHd52XssjM1e6d7pNKwk6Z40x6Tpvq
+cStL3sC4tomx+vn7zzSUcS3hwdcHvnwZ
+-----END X509 CRL-----
+-----BEGIN X509 CRL-----
+MIIBHTCBhzANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0xNDEy
+MDQxMTUyMDFaFw00MjA0MjExMTUyMDFaMBQwEgIBBhcNMTQxMjA0MTE1MjAxWjAN
+BgkqhkiG9w0BAQUFAAOBgQCmFnFkEt0+Ialw4E+4nIAJWJO9XDE71FdRfX3QChs8
+ZJtBseaMNeUC1FY1zHOYQhtMy+Uatda6hx/QiyidF2oP5KpWp+R11M554Ifxem3X
+KDQDBQNee+1IIJ7a1kxAUxeSNP+0a3/bmUxI5sbomINnKeIDqDO8d2vmO2VLxJm6
+MA==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root+server_ca.crt b/src/test/ssl/ssl/root+server_ca.crt
new file mode 100644
index 0000000..f886582
--- /dev/null
+++ b/src/test/ssl/ssl/root+server_ca.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAWACCQD13ziQMRDLGTANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVU
+ZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBz
+dWl0ZTAeFw0xNDEyMDQxMTUyMDFaFw0xNTAxMDMxMTUyMDFaMEAxPjA8BgNVBAMM
+NVRlc3Qgcm9vdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0
+IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7TLars/P/obbNlsz
+cX/wZFnZ97L4dAiJAE+ZusoTqLalRnPbQEtrPfMA/eL/gjq69ehnPcehMIxnYRAV
++xqOnMiUacf+6TQBrjrnfCQZkYkngzYajTqhQogdM7sUHtvBvTs1EkjdVznQUN9B
+BRZi6zEvUMkc8/+KaiEKc0zAKQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAhmmj+R
+XP1+AREKWE33P8AkXTTGkXMvULZSgteHWxbBc08TbxJLTsqDvwp0lY/9nH48Ejx5
+XYIdDAED9Bwsm50y9u5p5OsO9YqHJfIsC9+Ui3paDHU543Y8CtZC4Ye5OcFn4/lp
+ew5Ix9E0LHJlY+LCfVEKSV0jDP6aMsYETpIe
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQEwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC15tOzDBVKaCRDz9L5LMpPk8DR
+RGHHOe4OuO6WTkUzjbjuKyiQbmtcp00R4dULbSM57ESvI/Ny0gPt+J/QKAOG8S5t
+09wDpKxKcgZSZ6Nd6FaK+D+ZhUVAkP3hB0ba0wo1JZff/0e4B+VJhXTjl7RRHfbr
+AEuDYFxv9T3K/Jq04wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJys1pnYvO+u8Wca
+6xUToGMpqTnImKa+dX8tMKsp6mXAN/dWrOVMDWnjBhQxShhAZBsaJ4iUeXPJlctw
+KzkUCQo6BsUbPMTSQlPuyHHdZBOTHDIW4SylKaBQvkundkhhBO7aHwFV3QjxZKcH
+XqpGyY2ryrgdj2D4+H55NDXYjj/m
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/root.crl b/src/test/ssl/ssl/root.crl
new file mode 100644
index 0000000..fa53fd7
--- /dev/null
+++ b/src/test/ssl/ssl/root.crl
@@ -0,0 +1,8 @@
+-----BEGIN X509 CRL-----
+MIIBBDBvMA0GCSqGSIb3DQEBBQUAMEAxPjA8BgNVBAMMNVRlc3Qgcm9vdCBDQSBm
+b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHN1aXRlFw0xNDEyMDQx
+MTUyMDFaFw00MjA0MjExMTUyMDFaMA0GCSqGSIb3DQEBBQUAA4GBAK7EbBLD03t6
+zv2yRS6ByDg7X9CPbPVReUQ21ntI652lsJ4veAJeSWQXITEjC/mt+VkN8pKH8eEg
+hp0vZmS7zIzL+UdPZkJYokAdmBsmP1ymDvOHd52XssjM1e6d7pNKwk6Z40x6Tpvq
+cStL3sC4tomx+vn7zzSUcS3hwdcHvnwZ
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/root_ca.crt b/src/test/ssl/ssl/root_ca.crt
new file mode 100644
index 0000000..ca5faab
--- /dev/null
+++ b/src/test/ssl/ssl/root_ca.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAWACCQD13ziQMRDLGTANBgkqhkiG9w0BAQsFADBAMT4wPAYDVQQDDDVU
+ZXN0IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBz
+dWl0ZTAeFw0xNDEyMDQxMTUyMDFaFw0xNTAxMDMxMTUyMDFaMEAxPjA8BgNVBAMM
+NVRlc3Qgcm9vdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0
+IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7TLars/P/obbNlsz
+cX/wZFnZ97L4dAiJAE+ZusoTqLalRnPbQEtrPfMA/eL/gjq69ehnPcehMIxnYRAV
++xqOnMiUacf+6TQBrjrnfCQZkYkngzYajTqhQogdM7sUHtvBvTs1EkjdVznQUN9B
+BRZi6zEvUMkc8/+KaiEKc0zAKQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAhmmj+R
+XP1+AREKWE33P8AkXTTGkXMvULZSgteHWxbBc08TbxJLTsqDvwp0lY/9nH48Ejx5
+XYIdDAED9Bwsm50y9u5p5OsO9YqHJfIsC9+Ui3paDHU543Y8CtZC4Ye5OcFn4/lp
+ew5Ix9E0LHJlY+LCfVEKSV0jDP6aMsYETpIe
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/root_ca.key b/src/test/ssl/ssl/root_ca.key
new file mode 100644
index 0000000..c88af49
--- /dev/null
+++ b/src/test/ssl/ssl/root_ca.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDC7TLars/P/obbNlszcX/wZFnZ97L4dAiJAE+ZusoTqLalRnPb
+QEtrPfMA/eL/gjq69ehnPcehMIxnYRAV+xqOnMiUacf+6TQBrjrnfCQZkYkngzYa
+jTqhQogdM7sUHtvBvTs1EkjdVznQUN9BBRZi6zEvUMkc8/+KaiEKc0zAKQIDAQAB
+AoGAa1z8kqiQe86Edr9UslwEjOKo/r5IzEIU5WjPbywL25Ikr5nDfHLIV5QygUxV
+uEgBkzKYxCyqBOVZoCM9Ge5JrGcWO+N7IVVpirJRLgafu17sWyOsFIdT0QfNBYEl
+sdcz70c0Rsfk+hnsJ3KDOAxEhmPZe1mT9Rl6g1qpzva1/1ECQQDkxzDyhm8/F41B
+1z9m6Gz8X3fIb4cx1WpMZHG3XNyD5rzPiUhuIqATUHTCGIig0nYzYr1AOVNSN6pb
+5whOuW4VAkEA2h7bIZd3yfn1YNZYk24ORwZssE5r5ryQeOwuDGJH8FxPk/LqZE8T
+OX+ptPRyDowd2UZFRz0jxKl3RR4W/VtixQJAehaK2oI/j+3jpkVWQna64puX8tEB
+1uhLR+U6gl3+GC3kiOR8ULoNrwD6rjIlh52JErcYw9NT0cZ/FXhfiJOQWQJAFr3a
+2RDC05M1K0iN6aky4eLgmC1FAMSuR31Qe8gPehcV0PYlzBmWhosx9YT7E1s2jX3P
+IVNVlF6a6eDuQrIxhQJAL8ELX6MNW05PzETZpYSOTvkOeGND7INc5Md28Yv9SkPd
+c/HvFDVQF0OsgFrIcuBP8o7YaQBETJPqFHMao+WB6w==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-cn-and-alt-names.crt b/src/test/ssl/ssl/server-cn-and-alt-names.crt
new file mode 100644
index 0000000..20cc1c8
--- /dev/null
+++ b/src/test/ssl/ssl/server-cn-and-alt-names.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSTCCAbKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
+cnRzMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVowRjEeMBwGA1UECwwV
+UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1z
+c2x0ZXN0LnRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ0ERoXVz7aK
+ZLL2W8psViLKVorl1pvLz4m0Uw0X8iQkHgN+/gMNs2nHDzQFtbOc3NVbBxnnosbF
+XuGeCrlz+xK3J4Y5g6up9xNPCbtLM+RxBtMx/a/8naO+4yraQD51pZgNUMjSYeIL
+9UeB3VFg928+swacichtJIlwU3KAiEzRAgMBAAGjSzBJMEcGA1UdEQRAMD6CHWRu
+czEuYWx0LW5hbWUucGctc3NsdGVzdC50ZXN0gh1kbnMyLmFsdC1uYW1lLnBnLXNz
+bHRlc3QudGVzdDANBgkqhkiG9w0BAQUFAAOBgQBwQP7EuwvnURefrRvLKP+Txzwg
+xEZK/ZG/dSExX7CP8ib5JZQUuJpMzYmyGFbTpLJOU5qrE+vI2rxfHrWOYZU4IB1f
+u053N3slzi5ClKGKZt4y7LM4hupQ13xRfgIpasSJdEI3n/BCmeEdFVBqzKMQJxQe
+tHn3NapFkra7DHe2xQ==
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-cn-and-alt-names.key b/src/test/ssl/ssl/server-cn-and-alt-names.key
new file mode 100644
index 0000000..851dad9
--- /dev/null
+++ b/src/test/ssl/ssl/server-cn-and-alt-names.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCdBEaF1c+2imSy9lvKbFYiylaK5daby8+JtFMNF/IkJB4Dfv4D
+DbNpxw80BbWznNzVWwcZ56LGxV7hngq5c/sStyeGOYOrqfcTTwm7SzPkcQbTMf2v
+/J2jvuMq2kA+daWYDVDI0mHiC/VHgd1RYPdvPrMGnInIbSSJcFNygIhM0QIDAQAB
+AoGAFVtJhFaqo/d67uSXY5cMuDqxPr84S4STO/Ws/jDtnIDVHECfqCaq6o5KwRat
+ujpxxwtUke9xsnuSBjoK12KxGYoEFCstNJx2L77TvjkxcC85C6aGHWxLWCELqnn3
+3HmCE4I9i/kltO2YTje12nEWVkntqjvnqpAaFeQQ2vRO9KkCQQDOQo2FT0DX7VI7
+riSNjspfPQS5ESB1xTlcJL7aBS7iubAkVmRPBGM/UZpLLXacgV2WqUp6swcXculc
+SXyx15zDAkEAwuGxNx+SzoLxHzRi2P64+xCo3O4OOP7Fle0/Uyk+DyS9dFljdZbf
+mh28uKqflF0LRe+J4vKKqSHb3dqLdOrm2wJARAFdd96xmn/85QB9vM6fmtcbf4lO
+EoZ8aw0Sf//FfauLj++MEyF3N6FIJhFPUjq1CL+4dswgQnL4zhzMqDZW0QJAchas
+p8e9K1bvEESb5cthweGj6gsXmnhUdgw5eVb4tObeXuIB3xJffxsPo9CHsdSyx9OP
+FqTFVnSzAfNylxT55wJAICUYlyM6/VVMKb9bAoMz7nqg7N/utGEMijT2AqukQues
+jYm2TNtP033yibtWHjwBPDKL5JxsgDfG2x1LYiG6Kw==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-cn-only.crt b/src/test/ssl/ssl/server-cn-only.crt
new file mode 100644
index 0000000..e7bef70
--- /dev/null
+++ b/src/test/ssl/ssl/server-cn-only.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB/DCCAWWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
+cnRzMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVowRjEeMBwGA1UECwwV
+UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1z
+c2x0ZXN0LnRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAON0emWfWbLb
+4cSdixPiKiSwwiRbVw371L2t90jVY1ucJc/8YRUrRMhdsKdsP2NuwtFx1Mghspzt
+c/v6Dj/V9raYsHDGKK7OSPDF97GT9xM6yqm3FAY3l0QdP78XhiIZOhTO4fOJkAfQ
+LVhXca2X0krl0jF57/o5in6GHuyhulLPAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEA
+V7NgLA+RVPeWo5/TLsJyEf3tPnvpdq4Dfr/nDNDyWLhfrmny3Nuykfwap8JZHXG7
+oo+owRzgAXaJnr++5PvCo82Jp+gCNf5foZBx3GWdPsJY8d/0oREhFXhpqLCUUoiO
+2295A+mrgwfXoI+tlFypNb0T9x6qHOQlBUX+o1JBdnw=
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-cn-only.key b/src/test/ssl/ssl/server-cn-only.key
new file mode 100644
index 0000000..158a9d4
--- /dev/null
+++ b/src/test/ssl/ssl/server-cn-only.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDjdHpln1my2+HEnYsT4ioksMIkW1cN+9S9rfdI1WNbnCXP/GEV
+K0TIXbCnbD9jbsLRcdTIIbKc7XP7+g4/1fa2mLBwxiiuzkjwxfexk/cTOsqptxQG
+N5dEHT+/F4YiGToUzuHziZAH0C1YV3Gtl9JK5dIxee/6OYp+hh7sobpSzwIDAQAB
+AoGAHzYfiYxZQarcix9XM05InCpJKbYC9x9EbRbPJQZrEOoXYjfulnoOgTQiBodb
+F2jegOEO4ruFB/Wpgb0pcWcJ6Hgqh+GptulX1yWl7XzivvTDN6DO796pyNa581kN
+CrS9Sy0owktidlX5SJiXw2AOV1bNsvUBNapwyBFKsB3+XgECQQD/4yTJEAdRMZw1
+3czmcmzw+Aq/IBsd3w/GgX6jME02Br3LqnTIelNTPVNqEpy4wb6rNug2Dm79OWwh
+PNr799vPAkEA444gzKEUB7O2N8RnfLD5/n7Gl+P1MGOH0Rk6lxxAJ+Py8Itm1pV/
+3o9xwi1kguXdd+wqpL+B2gJDCpgSmJDZAQJBAIxbn5XaAOl8eN7jJr1RDoiuxdZI
+Whdsf063QStqFzAHSpwoh55f2szR2qtYQjblrxxjJcRg7mhf0vv4UXXcYukCQQDR
+wqZBewp3vxVtesLaklkgW8S9JwlRva3o9hSoTwZkvx+m1RnLHKxugFQg5q8MatAo
+R69XhqEwUX1zOpOJx5wBAkEAkvuuVdjo4baxildMPWSH/CcYlZs8c7ofs/Y6VkdV
+zIYMoGnGS7CfJbKLkXP7amlp9Gn1xgCPpbRJrik3Nafa9Q==
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-multiple-alt-names.crt b/src/test/ssl/ssl/server-multiple-alt-names.crt
new file mode 100644
index 0000000..473d61d
--- /dev/null
+++ b/src/test/ssl/ssl/server-multiple-alt-names.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICPzCCAaigAwIBAgIBBDANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
+cnRzMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVowIDEeMBwGA1UECwwV
+UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDMaR3uJ4+1P76DaUDWklA4L3Uic0ogorawfGuBO9pFB0w/kV6AAJGwhEy1DTi2
+neaAraa383F+e0Kpmbp1hXZ8k5DwKe6pHCv+R1RwRMqGrC6nQPM/tRsU97z7ROM8
++5QDE64zYtWkJGjQrpXmLC2sHSFIFyrHoi6MIzopKNSCpwIDAQABo2cwZTBjBgNV
+HREEXDBagh1kbnMxLmFsdC1uYW1lLnBnLXNzbHRlc3QudGVzdIIdZG5zMi5hbHQt
+bmFtZS5wZy1zc2x0ZXN0LnRlc3SCGioud2lsZGNhcmQucGctc3NsdGVzdC50ZXN0
+MA0GCSqGSIb3DQEBBQUAA4GBAG4lVLFuJsXsaeFpZBiudnklH17bAx11X51UsL7r
+oDp1AL2bHZnACqedHyed4n4+4UYezPbLOO5ITFSkIdkXYa5ohTjrwymhVGN9Sxlb
+1fitKKXWenvixOwPVk8g4e1Ev8JDofTPQNIFA7C8IbGunm8J0Pe7jF6KxlknP9A0
+x0Li
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-multiple-alt-names.key b/src/test/ssl/ssl/server-multiple-alt-names.key
new file mode 100644
index 0000000..9b905e2
--- /dev/null
+++ b/src/test/ssl/ssl/server-multiple-alt-names.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDMaR3uJ4+1P76DaUDWklA4L3Uic0ogorawfGuBO9pFB0w/kV6A
+AJGwhEy1DTi2neaAraa383F+e0Kpmbp1hXZ8k5DwKe6pHCv+R1RwRMqGrC6nQPM/
+tRsU97z7ROM8+5QDE64zYtWkJGjQrpXmLC2sHSFIFyrHoi6MIzopKNSCpwIDAQAB
+AoGBAI+m+/LHeLYO0yt1B60D7D5gE7ifPyQKVctX1RFgZ7eFNm+iEMByJfDgOSwv
+24BzHW+nGfhCrKsPorygHarDnY0TfInIX5OHaOgiJ2z6mQnCmH9nwX+ZAezeVlZR
+3QqdmRJzFRcqVD2cU2nk/DG2MJDpyqyfaBQ+FXHZGX03LWbRAkEA5SpdsgQkOW/S
+/5ENpACEixD9WU2EJjNOymddy64ODy6ug9Xc46nw6Xkn4EI+wmcmrmi65oH+hx+G
+g0k+aQmi2QJBAORYri1cNxupOSdQ7tY6DoPuMKMrV4FmuiF3XJWb2qZoNZLG4Lai
+kLn9A8yWJmKyu6V+tvz/DYRHlA870FDM4X8CQENoE8lB+JnAb7Lmqrl7wYDaTXsQ
+FvfZjapxfyBjIRWMKJ70sBVzLj6ueXE4axdpmfIhMiCNSh3awwko6SeiQvkCQHwx
+LypEiURmGUuk3QFuug5PMezM2d7rPDiPbq+AAL+Y1epqeDVc3VIKplJTJ7VueFhe
+PrADGBrlw0U1xurrQ4kCQCui0QT42Ppey48lvxu5S5+dOvkjF8p7Ml59aRqlcjPm
+2q38zM/AJlrekSclGszy/PjaXeZzFM+aCYKAsk8h7YE=
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-no-names.crt b/src/test/ssl/ssl/server-no-names.crt
new file mode 100644
index 0000000..0bb02f7
--- /dev/null
+++ b/src/test/ssl/ssl/server-no-names.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB1jCCAT+gAwIBAgIBBTANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
+cnRzMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVowIDEeMBwGA1UECwwV
+UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDXRZ+AOvlbHnupGqPsW4zYOE36wyMgwYnsafRMRkZd9R5GLtqnchIBdA9Lg2VP
+gK0355KTYtsdfdnC1kjflkeY+ZAvpRAMarV9fuE3z5kE3qDh4IsSJ3EyxoH7QwEL
+x01INf7QVEb/7y6s1Cw0GBonXnMel/8kfPBFpJ3+p4rw1wIDAQABMA0GCSqGSIb3
+DQEBBQUAA4GBAIvauevxaS0gnHu3RivMZp9UZe0r9Ja38CqdFnNUT+Z4MS0fOyWq
+9uz3JO7rdLmBtLEUNX2VNR8jIet9/gfxAO5MTYw+nSQ7Ci39kgSQYkCs4gZVS2TJ
+GeOpfcBipOFkI9O0nElAmNVFDB5j5bY5NfCNAMoD1q/FzovxlcskCOdh
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-no-names.key b/src/test/ssl/ssl/server-no-names.key
new file mode 100644
index 0000000..7453ba8
--- /dev/null
+++ b/src/test/ssl/ssl/server-no-names.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXwIBAAKBgQDXRZ+AOvlbHnupGqPsW4zYOE36wyMgwYnsafRMRkZd9R5GLtqn
+chIBdA9Lg2VPgK0355KTYtsdfdnC1kjflkeY+ZAvpRAMarV9fuE3z5kE3qDh4IsS
+J3EyxoH7QwELx01INf7QVEb/7y6s1Cw0GBonXnMel/8kfPBFpJ3+p4rw1wIDAQAB
+AoGBAKMdFCBbjzmlvVmC4BZlwDDNaPjLB0D4pQNHvV5WGVd0Nb5EHlWmL1J+mGBF
+bWxyOc4UX5Hh49lS1L+3EnyoKBKzsuPafLLXpVM2ujkkJt8iYenWUDqw1+g6zM97
+bHaQAa/U6+Mqn+dfcAn4FpYknZ0V4cvKqKw6CzjypkmHeLwxAkEA7F0tiV2nhkzN
+huOifLaxQHkOOBIgaFLGAMLwYHLlwVjPxk6O34+XPMehFQbetL431ZweUdGSY1fX
+jURXR72APQJBAOkn4AuhVWoS3lMWQc58kMCzY4+Xwd6ILKXMhFTZA2iNn7IUVEUe
+F2wjq292lu+tIfH+CdRjgCAC7B4OVaI2EqMCQQCZzHeY7ovXY5pIr05HgEkN/rc1
+3PWhbFrSnAX1fE3r5XItQ2jMJ47tSaiTGglH6o5CPHeuHYP3iG0FyvZQBAqxAkEA
+izskr81IFG/wE+3WnlgEmQ6HBdi6DQmEn/3hiEmPn3/zPYSmTiAKHKmwVn+a4sWg
+38G0XQCOIo+cMNaejJ99wQJBAINI+vfNR8A3wDsvz9hVpa+yZ4aDOeFETJprGsin
+D0v/xzviq1LPrOCuarioyEpYS47bzIKGSeAtC38VB4tQyEo=
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-revoked.crt b/src/test/ssl/ssl/server-revoked.crt
new file mode 100644
index 0000000..70150d2
--- /dev/null
+++ b/src/test/ssl/ssl/server-revoked.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB/DCCAWWgAwIBAgIBBjANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
+cnRzMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVowRjEeMBwGA1UECwwV
+UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1z
+c2x0ZXN0LnRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK09F4gQyZP/
+Z5fbOrLbQySBonwt9Wbb5iAEm5618oDk/YLkl1AQo2eoTabevY7+DwHPMMwR9MSA
+yUlvJ8Gc3MNAOIag0o63NNOZxYIzpqqAElOMPNE/FRlpVJyauGZ7lV/Y34vjtHxu
+4Pmi0jOLNMzUNjlN0rQrz0xaTGQ1rX1bAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEA
+ghgb9HNsSfyX1JMLYlCudOTQ/LuoXeXFsqFxRDAOXCCaSrH9T4lUZayBGNOd8kgZ
+FFHJo4WhZx7sE/foXuax/QGLi/mGrVw2xfJdD9SIQndzdnExoQndb+gvGSH/23s4
+Oif6jcSMPCLpoTaqVQdyPcw7DI9h26YzZ71IybBxPNE=
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-revoked.key b/src/test/ssl/ssl/server-revoked.key
new file mode 100644
index 0000000..550c1d4
--- /dev/null
+++ b/src/test/ssl/ssl/server-revoked.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCtPReIEMmT/2eX2zqy20MkgaJ8LfVm2+YgBJuetfKA5P2C5JdQ
+EKNnqE2m3r2O/g8BzzDMEfTEgMlJbyfBnNzDQDiGoNKOtzTTmcWCM6aqgBJTjDzR
+PxUZaVScmrhme5Vf2N+L47R8buD5otIzizTM1DY5TdK0K89MWkxkNa19WwIDAQAB
+AoGBAJNJ5b/hxgD2nXUXB4kZsrRPI37A9GxHehiu0kDWISBFkOTAxYVlIAj5p0vB
+BRmWF9xJ9AsNGTYY6QpuXzbVzzsqxpzqfrmcbpnEwJPIN74cWSBU3As6SVtkD414
+TjV3TxJlER87D4Jtk2vWvwjWt2tj7fAe/9B44l211jStT0/BAkEA5oRVIBUrLGhb
+ZCHMFEHfF5BtGYNUa54QDK1cJCmstv3CEfR/g1cHLKgnbXRzbIZ4u54sjY4PhviB
+nCVdr6umbwJBAMBjxj8BOFceswCgse3LdaO5O2YPh/h014iiwrll+XB5ufxq5mOa
+9gaKmE8eUBmuWwcE05xbzSUaBhCTjVNNrdUCQQCvylcIYlxMP0ECuWtiP2GcHL22
+aRql/yIKKOJNiaJ24klvW98qD+IewhVfOSEUr+++VD9xq9ZXfYeJxk0NvH7tAkAR
+hQR4mFPZGyKR3BBX5z8/OY7/LErlhT5bYvb4iyC77VnScqmoSGQ/FD/qdIg2znnb
+mcTraDC2QDhtKgKko15BAkAxtNExYOsPlW3kuhMRnDV3mB3h1TLghl9rYuHfSM5F
+9D7tpJ8FZa4P7BE5bfI1CPoRJsVVw0rUf1ihaMkUyxfI
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-single-alt-name.crt b/src/test/ssl/ssl/server-single-alt-name.crt
new file mode 100644
index 0000000..e0cdd82
--- /dev/null
+++ b/src/test/ssl/ssl/server-single-alt-name.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICBjCCAW+gAwIBAgIBAzANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNl
+cnRzMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVowIDEeMBwGA1UECwwV
+UG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQCqGqSv5i36Nk7FDQhRmBXi9M81Ox6YNAm1ha6Wj/MCfOlsCwWFfTFoTf5k1Why
+dB8yDlqGvZ2daf6mfgaui4R9uwVgbrPhhDUArtT1u5PBIqDdS10Z8E8iGStga/HQ
+J4CPlrXltX0ll4EC0EMj6SaF1dsYTMld4O7ipCZ62U7EowIDAQABoy4wLDAqBgNV
+HREEIzAhgh9zaW5nbGUuYWx0LW5hbWUucGctc3NsdGVzdC50ZXN0MA0GCSqGSIb3
+DQEBBQUAA4GBAC/otQQ0Ie+YqgH53nDvMpZ3Ol8xodOUWneRPI8Wf601Qi8Q4yY5
+9Gv+iU6ZEidn92y0hYXiHzItl/1K6LvIQM/yuro0j+48M/rdzl1qImSnxy0eGHbA
+3fOvUNt8ymYKsgsAfeqHZmWUidEZ/jF9Y72oER/6ImnmsbjEFtHVFDRL
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-single-alt-name.key b/src/test/ssl/ssl/server-single-alt-name.key
new file mode 100644
index 0000000..c486c26
--- /dev/null
+++ b/src/test/ssl/ssl/server-single-alt-name.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCqGqSv5i36Nk7FDQhRmBXi9M81Ox6YNAm1ha6Wj/MCfOlsCwWF
+fTFoTf5k1WhydB8yDlqGvZ2daf6mfgaui4R9uwVgbrPhhDUArtT1u5PBIqDdS10Z
+8E8iGStga/HQJ4CPlrXltX0ll4EC0EMj6SaF1dsYTMld4O7ipCZ62U7EowIDAQAB
+AoGANPD34psEIkS2vVNyDFsGLM2+k7WjrwE7KFjD3q5MlrCjwXGotUQilXD4xQ86
+Y6zKbLzU5eyr2ms7yzub/sUDZdBdJkU39NzEAf8dKN/UvhSGSZH1zxRCm1SmDrxZ
+BM3TEEGZUVrcamJF2EldPdbmBo8EiFyuPT7UMObvYt0li6kCQQDUol5X57+6ZGvF
+QrmE/3dz+zxa5fZikiG82mrpS9RiXlSssHbE4z04UARgLCGloqlFIQTsKCHnsK7i
+hTX41FL/AkEAzMvGWthn3QG1cVT8tLhinhAL2C/v9Mjq1SnlWi/9qs28mAtYa926
+wNv+DwcEOEvamCStj+n3q8LRZ/zxbpliXQJAVq7eoR1z9uuLV75s3QA8VUbdgvzu
+pZ6HLHMqVHM6YOOtxzylHny472UHc6FqEhkuwmTEmfV+ZPKNSQEfUJJWRwJBAK1Z
+p7LqDzCh66Xc3HNUyBUnW/9IxIKdNznsVrk6eiwELik9IUFc1GG/VZP+ynGks4mp
+MkjpML3xEDRHhU2rA/kCQQCzhjCPuiZtPpe7fM/gl78q+TSXu/Rr8e/LYYmHeRbq
+G9ojPe6Kx+toOPrOaaDLbRd6tkmwoLl9eSW/EW/aHwWY
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server-ss.crt b/src/test/ssl/ssl/server-ss.crt
new file mode 100644
index 0000000..e11d841
--- /dev/null
+++ b/src/test/ssl/ssl/server-ss.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICCDCCAXGgAwIBAgIJAJKfiuFnjnPZMA0GCSqGSIb3DQEBCwUAMEYxJDAiBgNV
+BAMMG2NvbW1vbi1uYW1lLnBnLXNzbHRlc3QudGVzdDEeMBwGA1UECwwVUG9zdGdy
+ZVNRTCB0ZXN0IHN1aXRlMB4XDTE0MTIwNDExNTIwMVoXDTQyMDQyMTExNTIwMVow
+RjEkMCIGA1UEAwwbY29tbW9uLW5hbWUucGctc3NsdGVzdC50ZXN0MR4wHAYDVQQL
+DBVQb3N0Z3JlU1FMIHRlc3Qgc3VpdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBAON0emWfWbLb4cSdixPiKiSwwiRbVw371L2t90jVY1ucJc/8YRUrRMhdsKds
+P2NuwtFx1Mghspztc/v6Dj/V9raYsHDGKK7OSPDF97GT9xM6yqm3FAY3l0QdP78X
+hiIZOhTO4fOJkAfQLVhXca2X0krl0jF57/o5in6GHuyhulLPAgMBAAEwDQYJKoZI
+hvcNAQELBQADgYEAoMiPEDM4EmgyOc6kVRSVa+Q+6Wc+O7WX7LmEZprXyJErQ51H
+X0KWcznjass1YzVeT+hCOyEQWSbEs8W1+b0FZleD6Cng9ZfD10Oz/4nCBy8al7sn
+GlQk0KHYlMOcQDfmFr5CNuoIo77rtapDlVxIhMAxBJKTlYvimbNh3XM9g4U=
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-ss.key b/src/test/ssl/ssl/server-ss.key
new file mode 100644
index 0000000..a32fe81
--- /dev/null
+++ b/src/test/ssl/ssl/server-ss.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDHz31rLL10XaTbD+K4UQcrtV+2Jq9C9U65lDk6LtsGVkW3os0P
+b0MtXZEijnPvVDa7Kwq4mvKzTJ/SZwtA6zxSC8gs9gpql3ZSINsrzHC9XNxNbUgZ
+gPGiAq9j8C2kRnTKae3tDsEa54XWZXTFniu0Lbk1lXEeompCZZv4wqaSfwIDAQAB
+AoGADrqV1TOsF4rbnyZRoSKf87HgB05ctwPcNMPfYBGaJaJwazP+B7g87HgsPa7g
+jvDXQ/7NQIRzhZINafYcl0F/5a7tbO6DyCXJzoYYZ4NOb8ng1HBpaBMhcigOeeeZ
+i+KBYDjPzEeVfUOxIADWuh8HuVQWgB2WOdWg2GSuC4MZrXkCQQD3AuKxW7W4PJzP
+ZMY4RpnvFlfUyNWKM/0vMeDv88QUK+LH1MWMSIsYPSBS5sTB8b7lR0kb3IhDkuNP
+MeoiZNIlAkEAzxTj5ITO92RL7JI85Z1WBMPgOjvw5ffQF95GiLUN7My1SB5KJUWX
+pI108sY6oigHNDYjlL8rfsJHoz3MpPaG0wJBAMNOwcXwuMebDXYivWSD1nUoGnyB
+6+5h2yA09SFlgjVc2eydfTHFrk2VD3jdRNgA+Kq7acAg6JFdlGPrGLDnPQ0CQGrs
+X2tMA82LVQSW0ajBn3ugY/PNpWoolaLtW0AVNFZzsJrHQQOTtmP5wkvkfLvjrSyR
+U7fnKZ8u02x/aV44CI8CQCQSiBF1mTqiNUZswX0z8m8KRiYplS9+UdtHqbwo91dw
+BD/KWzZKJjGEbc3RN1MIQUq02cp1ZU3pNU7afALBF4s=
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/ssl/server.crl b/src/test/ssl/ssl/server.crl
new file mode 100644
index 0000000..059f8c5
--- /dev/null
+++ b/src/test/ssl/ssl/server.crl
@@ -0,0 +1,9 @@
+-----BEGIN X509 CRL-----
+MIIBHTCBhzANBgkqhkiG9w0BAQUFADBCMUAwPgYDVQQDDDdUZXN0IENBIGZvciBQ
+b3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRzFw0xNDEy
+MDQxMTUyMDFaFw00MjA0MjExMTUyMDFaMBQwEgIBBhcNMTQxMjA0MTE1MjAxWjAN
+BgkqhkiG9w0BAQUFAAOBgQCmFnFkEt0+Ialw4E+4nIAJWJO9XDE71FdRfX3QChs8
+ZJtBseaMNeUC1FY1zHOYQhtMy+Uatda6hx/QiyidF2oP5KpWp+R11M554Ifxem3X
+KDQDBQNee+1IIJ7a1kxAUxeSNP+0a3/bmUxI5sbomINnKeIDqDO8d2vmO2VLxJm6
+MA==
+-----END X509 CRL-----
diff --git a/src/test/ssl/ssl/server_ca.crt b/src/test/ssl/ssl/server_ca.crt
new file mode 100644
index 0000000..b444bdc
--- /dev/null
+++ b/src/test/ssl/ssl/server_ca.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB8TCCAVoCAQEwDQYJKoZIhvcNAQEFBQAwQDE+MDwGA1UEAww1VGVzdCByb290
+IENBIGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc3VpdGUwHhcN
+MTQxMjA0MTE1MjAxWhcNNDIwNDIxMTE1MjAxWjBCMUAwPgYDVQQDDDdUZXN0IENB
+IGZvciBQb3N0Z3JlU1FMIFNTTCByZWdyZXNzaW9uIHRlc3Qgc2VydmVyIGNlcnRz
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC15tOzDBVKaCRDz9L5LMpPk8DR
+RGHHOe4OuO6WTkUzjbjuKyiQbmtcp00R4dULbSM57ESvI/Ny0gPt+J/QKAOG8S5t
+09wDpKxKcgZSZ6Nd6FaK+D+ZhUVAkP3hB0ba0wo1JZff/0e4B+VJhXTjl7RRHfbr
+AEuDYFxv9T3K/Jq04wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJys1pnYvO+u8Wca
+6xUToGMpqTnImKa+dX8tMKsp6mXAN/dWrOVMDWnjBhQxShhAZBsaJ4iUeXPJlctw
+KzkUCQo6BsUbPMTSQlPuyHHdZBOTHDIW4SylKaBQvkundkhhBO7aHwFV3QjxZKcH
+XqpGyY2ryrgdj2D4+H55NDXYjj/m
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server_ca.key b/src/test/ssl/ssl/server_ca.key
new file mode 100644
index 0000000..a7c3214
--- /dev/null
+++ b/src/test/ssl/ssl/server_ca.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC15tOzDBVKaCRDz9L5LMpPk8DRRGHHOe4OuO6WTkUzjbjuKyiQ
+bmtcp00R4dULbSM57ESvI/Ny0gPt+J/QKAOG8S5t09wDpKxKcgZSZ6Nd6FaK+D+Z
+hUVAkP3hB0ba0wo1JZff/0e4B+VJhXTjl7RRHfbrAEuDYFxv9T3K/Jq04wIDAQAB
+AoGBAKxkghg7iGYHQu9dpCXw9B/s+R2bgEuPNHWRgNTEg0MzuqNGFeCkNW4PRLSA
+4ic9HNiFeia+nLgiIAVFzzg44/VCvzD8P0EdJo9bRqV+mmm15YBcpV+F3I5RLOuq
+IWuRHyDbt+wsZyzdzPN0ElV3AUmj/0vkfX0xoRwXeGqimBFpAkEA5/PpK0qjvK3z
+hv4lC0nX3bmaKfFFEDRiufK+/WUGMHx8YS55CqjpcbR+xMPAFswWEcBEQnj6zbDm
+a4hEjlwXBwJBAMjCi9UcDe3Sp/mmxFklxmMusIHqldA5YsOyCjtSLxpgHJLdwcMx
+KWH3Q9nUrn4WxhlHhY8W6smNgQDzVk1TgEUCQCd2ef8hjcX2Gm6nIopPH+jbQP1N
+zSA6qWlVgWT/IRRyuX6XN4S2xDDSMpcrbqzyP/b5LSPaDWGdbTZyUqedx1UCQDjA
+/sTVNH7aAZCK+5D0I9xgE5f2mDmQL4KBL3FLr3M2Xn2KYT9sA3Xlb/IBtP6CM6hr
+1q733JH0Bdcd83TSuT0CQQCb4dzfNLuYscHBnQYsMCZvMSKmQZ2LKUANGra/mX+i
+7JZ7wngI548ypMK2lJWnb2Ce+0cR8GAPVHWOTx2srtH4
+-----END RSA PRIVATE KEY-----
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
new file mode 100644
index 0000000..b492a56
--- /dev/null
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -0,0 +1,223 @@
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 38;
+use ServerSetup;
+use File::Copy;
+
+# Like TestLib.pm, we use IPC::Run
+BEGIN
+{
+	eval {
+		require IPC::Run;
+		import IPC::Run qw(run start);
+		1;
+	} or do
+	{
+		plan skip_all => "IPC::Run not available";
+	  }
+}
+
+#### 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';
+
+my $tempdir = TestLib::tempdir;
+#my $tempdir = "tmp_check";
+
+
+# Define a couple of helper functions to test connecting to the server.
+
+my $common_connstr;
+
+sub run_test_psql {
+	my $connstr = $_[0];
+	my $logstring = $_[1];
+
+	my $cmd = [ 'psql',
+				'-A', '-t',
+				'-c', "SELECT 'connected with $connstr'",
+				'-d', "$connstr"
+		];
+
+    open CLIENTLOG, ">>$tempdir/client-log" or die "Could not open client-log file";
+	print CLIENTLOG "\n# Running test: $connstr $logstring\n";
+	close CLIENTLOG;
+
+	my $result = run $cmd, '>>', "$tempdir/client-log", '2>&1';
+	return $result;
+}
+
+#
+# The first argument is a (part of a) connection string, and it's also printed
+# out as the test case name. It is appended to $common_connstr global variable,
+# which also contains a libpq connection string.
+#
+# The second argument is a hostname to connect to.
+sub test_connect_ok {
+	my $connstr = $_[0];
+
+	my $result = run_test_psql("$common_connstr $connstr", "(should succeed)");
+	ok($result, $connstr);
+}
+
+sub test_connect_fails {
+	my $connstr = $_[0];
+
+	my $result = run_test_psql("$common_connstr $connstr", "(should fail)");
+	ok(!$result, "$connstr (should fail)");
+}
+
+# The client's private key must not be world-readable. Git doesn't track
+# permissions (except for the executable bit), so they might be wrong after
+# a checkout.
+system_or_bail "chmod 0600 ssl/client.key";
+
+#### Part 0. Set up the server.
+
+diag "setting up data directory in \"$tempdir\"...";
+start_test_server($tempdir);
+configure_test_server_for_ssl($tempdir);
+switch_server_cert($tempdir, 'server-cn-only');
+
+### Part 1. Run client-side tests.
+###
+### Test that libpq accepts/rejects the connection correctly, depending
+### on sslmode and whether the server's certificate looks correct. No
+### client certificate is used in these tests.
+
+diag "running client tests...";
+
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
+
+# The server should not accept non-SSL connections
+diag "test that the server doesn't accept non-SSL connections";
+test_connect_fails("sslmode=disable");
+
+# Try without a root cert. In sslmode=require, this should work. In verify-ca
+# or verify-full mode it should fail
+diag "connect without server root cert";
+test_connect_ok   ("sslrootcert=invalid sslmode=require");
+test_connect_fails("sslrootcert=invalid sslmode=verify-ca");
+test_connect_fails("sslrootcert=invalid 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)
+diag "connect without wrong server root cert";
+test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=require");
+test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=verify-ca");
+test_connect_fails("sslrootcert=ssl/client_ca.crt 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.
+diag "connect with server CA cert, without root CA";
+test_connect_fails("sslrootcert=ssl/server_ca.crt sslmode=verify-ca");
+
+# And finally, with the correct root cert.
+diag "connect with correct server CA cert file";
+test_connect_ok   ("sslrootcert=ssl/root+server_ca.crt sslmode=require");
+test_connect_ok   ("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca");
+test_connect_ok   ("sslrootcert=ssl/root+server_ca.crt 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   ("sslrootcert=ssl/both-cas-1.crt sslmode=verify-ca");
+test_connect_ok   ("sslrootcert=ssl/both-cas-2.crt sslmode=verify-ca");
+
+diag "testing sslcrl option with a non-revoked cert";
+
+# Invalid CRL filename is the same as no CRL, succeeds
+test_connect_ok   ("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid");
+# A CRL belonging to a different CA is not accepted, fails
+test_connect_fails("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl");
+# With the correct CRL, succeeds (this cert is not revoked)
+test_connect_ok   ("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl");
+
+# Check that connecting with verify-full fails, when the hostname doesn't
+# match the hostname in the server's certificate.
+diag "test mismatch between hostname and server certificate";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("sslmode=require host=wronghost.test");
+test_connect_ok   ("sslmode=verify-ca host=wronghost.test");
+test_connect_fails("sslmode=verify-full host=wronghost.test");
+
+# Test Subject Alternative Names.
+switch_server_cert($tempdir, 'server-multiple-alt-names');
+
+diag "test hostname matching with X509 Subject Alternative Names";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("host=dns1.alt-name.pg-ssltest.test");
+test_connect_ok   ("host=dns2.alt-name.pg-ssltest.test");
+test_connect_ok   ("host=foo.wildcard.pg-ssltest.test");
+
+test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
+test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
+
+# Test certificate with a single Subject Alternative Name. (this gives a
+# slightly different error message, that's all)
+switch_server_cert($tempdir, 'server-single-alt-name');
+
+diag "test hostname matching with a single X509 Subject Alternative Name";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("host=single.alt-name.pg-ssltest.test");
+
+test_connect_fails("host=wronghost.alt-name.pg-ssltest.test");
+test_connect_fails("host=deep.subdomain.wildcard.pg-ssltest.test");
+
+# 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($tempdir, 'server-cn-and-alt-names');
+
+diag "test certificate with both a CN and SANs";
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+
+test_connect_ok   ("host=dns1.alt-name.pg-ssltest.test");
+test_connect_ok   ("host=dns2.alt-name.pg-ssltest.test");
+test_connect_fails("host=common-name.pg-ssltest.test");
+
+# 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($tempdir, 'server-no-names');
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR";
+
+test_connect_ok   ("sslmode=verify-ca host=common-name.pg-ssltest.test");
+test_connect_fails("sslmode=verify-full host=common-name.pg-ssltest.test");
+
+# Test that the CRL works
+diag "Testing client-side CRL";
+switch_server_cert($tempdir, 'server-revoked');
+
+$common_connstr="user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
+
+# Without the CRL, succeeds. With it, fails.
+test_connect_ok   ("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca");
+test_connect_fails("sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl");
+
+### Part 2. Server-side tests.
+###
+### Test certificate authorization.
+
+diag "Testing certificate authorization...";
+$common_connstr="sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=certdb hostaddr=$SERVERHOSTADDR";
+
+# no client cert
+test_connect_fails("user=ssltestuser sslcert=invalid");
+
+# correct client cert
+test_connect_ok   ("user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client.key");
+
+# client cert belonging to another user
+test_connect_fails("user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client.key");
+
+# revoked client cert
+test_connect_fails("user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked.key");
+
+
+# All done! Save the log, before the temporary installation is deleted
+copy("$tempdir/client-log", "./client-log");
#8David Fetter
david@fetter.org
In reply to: Heikki Linnakangas (#7)
Re: SSL regression test suite

On Thu, Dec 04, 2014 at 02:42:41PM +0200, Heikki Linnakangas wrote:

On 10/06/2014 04:21 PM, Heikki Linnakangas wrote:

This probably needs some further cleanup before it's ready for
committing. One issues is that it creates a temporary cluster that
listens for TCP connections on localhost, which isn't safe on a
multi-user system.

This issue remains. There isn't much we can do about it; SSL doesn't work
over Unix domain sockets. We could make it work, but that's a whole
different feature.

How do people feel about including this test suite in the source tree? It's
probably not suitable for running as part of "make check-world",

What makes it unsuitable?

but it's extremely handy if you're working on a patch related to
SSL. I'd like to commit this, even if it has some rough edges. That
way we can improve it later, rather than have it fall into oblivion.
Any objections?

Not from me :)

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#7)
Re: SSL regression test suite

Heikki Linnakangas <hlinnakangas@vmware.com> writes:

On 10/06/2014 04:21 PM, Heikki Linnakangas wrote:

This probably needs some further cleanup before it's ready for
committing. One issues is that it creates a temporary cluster that
listens for TCP connections on localhost, which isn't safe on a
multi-user system.

This issue remains. There isn't much we can do about it; SSL doesn't
work over Unix domain sockets. We could make it work, but that's a whole
different feature.

How do people feel about including this test suite in the source tree?
It's probably not suitable for running as part of "make check-world",
but it's extremely handy if you're working on a patch related to SSL.
I'd like to commit this, even if it has some rough edges. That way we
can improve it later, rather than have it fall into oblivion. Any
objections?

As long as it's not run by any standard target, and there's some
documentation explaining why not, I see no reason it can't be in the
tree.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Heikki Linnakangas (#7)
Re: SSL regression test suite

Heikki Linnakangas wrote:

How do people feel about including this test suite in the source tree?

+1

It's probably not suitable for running as part of "make check-world",
but it's extremely handy if you're working on a patch related to SSL.
I'd like to commit this, even if it has some rough edges. That way we
can improve it later, rather than have it fall into oblivion. Any
objections?

To prevent it from breaking, one idea is to have one or more buildfarm
animals that run this test as a separate module.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Noah Misch
noah@leadboat.com
In reply to: Heikki Linnakangas (#7)
Re: SSL regression test suite

On Thu, Dec 04, 2014 at 02:42:41PM +0200, Heikki Linnakangas wrote:

On 10/06/2014 04:21 PM, Heikki Linnakangas wrote:

This probably needs some further cleanup before it's ready for
committing. One issues is that it creates a temporary cluster that
listens for TCP connections on localhost, which isn't safe on a
multi-user system.

This issue remains. There isn't much we can do about it; SSL doesn't work
over Unix domain sockets. We could make it work, but that's a whole
different feature.

A large subset of the test suite could be made secure. Omit or lock down
"trustdb", and skip affected tests. (Perhaps have an --unsafe-tests option to
reactivate them.) Instead of distributing frozen keys, generate all keys
on-demand. Ensure that key files have secure file modes from the start.
Having said that, ...

How do people feel about including this test suite in the source tree? It's
probably not suitable for running as part of "make check-world", but it's
extremely handy if you're working on a patch related to SSL. I'd like to
commit this, even if it has some rough edges. That way we can improve it
later, rather than have it fall into oblivion. Any objections?

... +1 for having this suite in the tree, even if check-world ignores it.
Echoing Tom's comment, the README should mention its security weakness.

Thanks,
nm

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers