Hi Hackers,
This is the third version patch for "Certificate status check using OCSP
Stapling" with ssl regression test cases added.
Here is how I run the ssl regression test:
./configure --enable-tap-tests --with-openssl
make -j
cd src/test/ssl
make sslfiles
make check PG_TEST_EXTRA=ssl
expected results:
# +++ tap check in src/test/ssl +++
t/001_ssltests.pl .. ok
t/002_scram.pl ..... ok
t/003_sslinfo.pl ... ok
All tests successful.
Files=3, Tests=279, 17 wallclock secs ( 0.05 usr 0.01 sys + 2.32
cusr 2.16 csys = 4.54 CPU)
Result: PASS
Notes, before executing the SSL regression tests with the command `make
check PG_TEST_EXTRA=ssl`, it is necessary to wait for 1 minute after
running `make sslfiles`. This delay is required because the newly
generated OCSP responses for the 'expired' test cases need 1 minute to
pass the nextUpdate period. Once the stapled OCSP response files for the
tests are committed as test input, there is no need to wait, similar to
certificate files.
Any comments or feedback would be greatly appreciated!
Thank you,
David
From 99fc46ed0bf05eedbe7539890d946db472617150 Mon Sep 17 00:00:00 2001
From: David Zhang <idraw...@gmail.com>
Date: Tue, 5 Mar 2024 15:31:22 -0800
Subject: [PATCH 1/3] support certificate status check using OCSP stapling
---
src/backend/libpq/be-secure-openssl.c | 87 ++++++++
src/backend/libpq/be-secure.c | 1 +
src/backend/utils/misc/guc_tables.c | 10 +
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/libpq/libpq.h | 1 +
src/interfaces/libpq/fe-connect.c | 37 ++++
src/interfaces/libpq/fe-secure-openssl.c | 198 ++++++++++++++++++
src/interfaces/libpq/libpq-int.h | 1 +
8 files changed, 336 insertions(+)
diff --git a/src/backend/libpq/be-secure-openssl.c
b/src/backend/libpq/be-secure-openssl.c
index e12b1cc9e3..c727634dfa 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -50,6 +50,7 @@
#include <openssl/ec.h>
#endif
#include <openssl/x509v3.h>
+#include <openssl/ocsp.h>
/* default init hook can be overridden by a shared library */
@@ -81,6 +82,8 @@ static bool ssl_is_server_start;
static int ssl_protocol_version_to_openssl(int v);
static const char *ssl_protocol_version_to_string(int v);
+static int ocsp_stapling_cb(SSL *ssl);
+
/* for passing data back from verify_cb() */
static const char *cert_errdetail;
@@ -429,6 +432,9 @@ be_tls_open_server(Port *port)
return -1;
}
+ /* set up OCSP stapling callback */
+ SSL_CTX_set_tlsext_status_cb(SSL_context, ocsp_stapling_cb);
+
/* set up debugging/info callback */
SSL_CTX_set_info_callback(SSL_context, info_cb);
@@ -1653,3 +1659,84 @@ default_openssl_tls_init(SSL_CTX *context, bool
isServerStart)
SSL_CTX_set_default_passwd_cb(context,
dummy_ssl_passwd_cb);
}
}
+
+/*
+ * OCSP stapling callback function for the server side.
+ *
+ * This function is responsible for providing the OCSP stapling response to
+ * the client during the SSL/TLS handshake, based on the client's request.
+ *
+ * Parameters:
+ * - ssl: SSL/TLS connection object.
+ *
+ * Returns:
+ * - SSL_TLSEXT_ERR_OK: OCSP stapling response successfully provided.
+ * - SSL_TLSEXT_ERR_NOACK: OCSP stapling response not provided due to errors.
+ *
+ * Steps:
+ * 1. Check if the server-side OCSP stapling feature is enabled.
+ * 2. Read OCSP response from file if client requested OCSP stapling.
+ * 3. Set the OCSP stapling response in the SSL/TLS connection.
+ */
+static int ocsp_stapling_cb(SSL *ssl)
+{
+ int resp_len = -1;
+ BIO *bio = NULL;
+ OCSP_RESPONSE *resp = NULL;
+ unsigned char *rspder = NULL;
+
+ /* return, if ssl_ocsp_file not enabled on server */
+ if (ssl_ocsp_file == NULL)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not find ssl_ocsp_file")));
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ /* whether the client requested OCSP stapling */
+ if (SSL_get_tlsext_status_type(ssl) == TLSEXT_STATUSTYPE_ocsp)
+ {
+ bio = BIO_new_file(ssl_ocsp_file, "r");
+ if (bio == NULL)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not read
ssl_ocsp_file")));
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+ BIO_free(bio);
+ if (resp == NULL)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not convert OCSP
response to intarnal format")));
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ resp_len = i2d_OCSP_RESPONSE(resp, &rspder);
+ OCSP_RESPONSE_free(resp);
+ if (resp_len <= 0)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not convert OCSP
response to der format")));
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ /* set up the OCSP stapling response */
+ if (SSL_set_tlsext_status_ocsp_resp(ssl, rspder, resp_len) != 1)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not set up OCSP stapling
response")));
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ return SSL_TLSEXT_ERR_NOACK;
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 5612c29f8b..03ebc29e34 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -34,6 +34,7 @@
char *ssl_library;
char *ssl_cert_file;
+char *ssl_ocsp_file;
char *ssl_key_file;
char *ssl_ca_file;
char *ssl_crl_file;
diff --git a/src/backend/utils/misc/guc_tables.c
b/src/backend/utils/misc/guc_tables.c
index 45013582a7..48ba746614 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -4531,6 +4531,16 @@ struct config_string ConfigureNamesString[] =
NULL, NULL, NULL
},
+ {
+ {"ssl_ocsp_file", PGC_SIGHUP, CONN_AUTH_SSL,
+ gettext_noop("Location of the SSL certificate OCSP
stapling file."),
+ NULL
+ },
+ &ssl_ocsp_file,
+ "",
+ NULL, NULL, NULL
+ },
+
{
{"synchronous_standby_names", PGC_SIGHUP, REPLICATION_PRIMARY,
gettext_noop("Number of synchronous standbys and list
of names of potential synchronous ones."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample
b/src/backend/utils/misc/postgresql.conf.sample
index edcc0282b2..e7fe38afd9 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -116,6 +116,7 @@
#ssl = off
#ssl_ca_file = ''
#ssl_cert_file = 'server.crt'
+#ssl_ocsp_file = 'server.res'
#ssl_crl_file = ''
#ssl_crl_dir = ''
#ssl_key_file = 'server.key'
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 6171a0d17a..edef65fd39 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -89,6 +89,7 @@ extern bool pq_check_connection(void);
*/
extern PGDLLIMPORT char *ssl_library;
extern PGDLLIMPORT char *ssl_cert_file;
+extern PGDLLIMPORT char *ssl_ocsp_file;
extern PGDLLIMPORT char *ssl_key_file;
extern PGDLLIMPORT char *ssl_ca_file;
extern PGDLLIMPORT char *ssl_crl_file;
diff --git a/src/interfaces/libpq/fe-connect.c
b/src/interfaces/libpq/fe-connect.c
index d4e10a0c4f..335da4df54 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -324,6 +324,10 @@ static const internalPQconninfoOption PQconninfoOptions[]
= {
"SSL-Maximum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") ==
8 */
offsetof(struct pg_conn, ssl_max_protocol_version)},
+ {"sslocspstapling", "PGSSLOCSPSTAPLING", "0", NULL,
+ "SSL-OCSP-Stapling", "", 1,
+ offsetof(struct pg_conn, sslocspstapling)},
+
/*
* As with SSL, all GSS options are exposed even in builds that don't
have
* support.
@@ -438,6 +442,7 @@ static void pgpassfileWarning(PGconn *conn);
static void default_threadlock(int acquire);
static bool sslVerifyProtocolVersion(const char *version);
static bool sslVerifyProtocolRange(const char *min, const char *max);
+static bool sslVerifyOcspStapling(const char *stapling);
/* global variable because fe-auth.c needs to access it */
@@ -1576,6 +1581,18 @@ pqConnectOptions2(PGconn *conn)
return false;
}
+ /*
+ * Validate sslocspstapling settings
+ */
+ if (!sslVerifyOcspStapling(conn->sslocspstapling))
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
+
"sslocspstapling",
+
conn->sslocspstapling);
+ return false;
+ }
+
/*
* validate sslcertmode option
*/
@@ -4388,6 +4405,7 @@ freePGconn(PGconn *conn)
free(conn->require_auth);
free(conn->ssl_min_protocol_version);
free(conn->ssl_max_protocol_version);
+ free(conn->sslocspstapling);
free(conn->gssencmode);
free(conn->krbsrvname);
free(conn->gsslib);
@@ -7326,6 +7344,25 @@ sslVerifyProtocolRange(const char *min, const char *max)
return true;
}
+/*
+ * Check sslocspstapling is set properly
+ */
+static bool
+sslVerifyOcspStapling(const char *stapling)
+{
+ /*
+ * An empty string or a NULL value is considered valid
+ */
+ if (!stapling || strlen(stapling) == 0)
+ return true;
+
+ if (pg_strcasecmp(stapling, "0") == 0 ||
+ pg_strcasecmp(stapling, "1") == 0)
+ return true;
+
+ /* anything else is wrong */
+ return false;
+}
/*
* Obtain user's home directory, return in given buffer
diff --git a/src/interfaces/libpq/fe-secure-openssl.c
b/src/interfaces/libpq/fe-secure-openssl.c
index 8110882262..07f73980cb 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -62,6 +62,7 @@
#include <openssl/engine.h>
#endif
#include <openssl/x509v3.h>
+#include <openssl/ocsp.h>
static int verify_cb(int ok, X509_STORE_CTX *ctx);
@@ -95,6 +96,9 @@ static pthread_mutex_t ssl_config_mutex =
PTHREAD_MUTEX_INITIALIZER;
static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
static int ssl_protocol_version_to_openssl(const char *protocol);
+static int ocsp_response_check_cb(SSL *ssl);
+#define OCSP_CERT_STATUS_OK 1
+#define OCSP_CERT_STATUS_NOK (-1)
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions
*/
@@ -1182,6 +1186,36 @@ initialize_SSL(PGconn *conn)
have_cert = true;
}
+ /* Enable OCSP stapling for certificate status check */
+ if (conn->sslocspstapling &&
+ strlen(conn->sslocspstapling) != 0 &&
+ (strcmp(conn->sslocspstapling, "1") == 0))
+ {
+ /* set up certificate status request */
+ if (SSL_CTX_set_tlsext_status_type(SSL_context,
+ TLSEXT_STATUSTYPE_ocsp) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+ libpq_append_conn_error(conn,
+ "could not set up certificate status
request: %s", err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+
+ /* set up OCSP response callback */
+ if (SSL_CTX_set_tlsext_status_cb(SSL_context,
+ ocsp_response_check_cb) <= 0)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+ libpq_append_conn_error(conn,
+ "could not set up OCSP response
callback: %s", err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+ }
+
/*
* The SSL context is now loaded with the correct root and client
* certificates. Create a connection-specific SSL object. The private
key
@@ -2043,3 +2077,167 @@ ssl_protocol_version_to_openssl(const char *protocol)
return -1;
}
+
+
+/*
+ * Verify OCSP stapling response in the context of an SSL/TLS connection.
+ *
+ * This function checks whether the server provided an OCSP response
+ * as part of the TLS handshake, verifies its integrity, and checks the
+ * revocation status of the presented certificates.
+ *
+ * Parameters:
+ * - ssl: SSL/TLS connection object.
+ *
+ * Returns:
+ * - OCSP_CERT_STATUS_OK: OCSP stapling was not requested or status is OK.
+ * - OCSP_CERT_STATUS_NOK: OCSP verification failed or status is not OK.
+ *
+ * Steps:
+ * 1. Retrieve OCSP response during handshake.
+ * 2. Perform basic OCSP response verification.
+ * 3. Verify each revocation status in the OCSP response.
+ *
+ * Cleanup:
+ * - Free allocated memory for the OCSP response and basic response.
+ */
+static int ocsp_response_check_cb(SSL *ssl)
+{
+ const unsigned char *resp_data;
+ long resp_len = 0;
+ int resp_count = 0;
+ int cert_index = 0;
+ OCSP_RESPONSE *ocsp_resp = NULL;
+ OCSP_BASICRESP *basic_resp = NULL;
+ STACK_OF(X509) *peer_chain = NULL;
+ int ocsp_status = OCSP_CERT_STATUS_NOK;
+ X509 *ocsp_signer = NULL;
+ STACK_OF(X509) *ocsp_issuers = NULL;
+ X509_STORE *cert_store = NULL;
+
+ /*
+ * step-1: retrieve OCSP response
+ */
+ /* check if requested certificate status */
+ if (SSL_get_tlsext_status_type(ssl) != TLSEXT_STATUSTYPE_ocsp)
+ return OCSP_CERT_STATUS_OK;
+
+ /* check if got OCSP response */
+ resp_len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp_data);
+ if (resp_data == NULL)
+ goto cleanup; /* no OCSP response */
+
+ /* convert OCSP response to internal format */
+ ocsp_resp = d2i_OCSP_RESPONSE(NULL, &resp_data, resp_len);
+ if (ocsp_resp == NULL)
+ goto cleanup; /* failed to convert OCSP response */
+
+ /*
+ * step-2: verify the basic of OCSP response
+ */
+ if (OCSP_response_status(ocsp_resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+ goto cleanup; /* OCSP response not successful */
+
+ /* get OCSP basic response structure */
+ basic_resp = OCSP_response_get1_basic(ocsp_resp);
+ if (basic_resp == NULL)
+ goto cleanup; /* failed to get basic OCSP response */
+ resp_count = OCSP_resp_count(basic_resp);
+ if (resp_count < 1)
+ goto cleanup;
+
+ /*
+ * step-3: verify each revocation status
+ */
+ OCSP_resp_get0_signer(basic_resp, &ocsp_signer, NULL);
+ if (ocsp_signer == NULL)
+ goto cleanup;
+ ocsp_issuers = sk_X509_new_null();
+ if (ocsp_issuers == NULL)
+ goto cleanup;
+ if (!sk_X509_push(ocsp_issuers, ocsp_signer))
+ goto cleanup;
+
+ /* get certificate chain from peer */
+ peer_chain = SSL_get_peer_cert_chain(ssl);
+ if (peer_chain == NULL)
+ goto cleanup;
+
+ /* get local certificate store */
+ cert_store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(ssl));
+ if (cert_store == NULL)
+ goto cleanup;
+
+ /* verify ocsp response signature */
+ if (OCSP_basic_verify(basic_resp, ocsp_issuers, cert_store,
OCSP_TRUSTOTHER) != 1)
+ goto cleanup; /* basic verification failed */
+
+ /* check each ocsp response status */
+ for (cert_index = 0; cert_index < resp_count; cert_index ++)
+ {
+ X509 *my_cert;
+ X509 *my_issuer;
+ OCSP_CERTID *my_cid;
+ int my_status;
+ int rev_reason;
+ ASN1_GENERALIZEDTIME *rev_time;
+ ASN1_GENERALIZEDTIME *this_update;
+ ASN1_GENERALIZEDTIME *next_update;
+
+ /* get certificate and issuer to construct ocsp status lookup
id */
+ my_cert = sk_X509_value(peer_chain, cert_index);
+ if (cert_index + 1 == resp_count)
+ {
+ X509_STORE_CTX *inctx = NULL;
+ X509_OBJECT *obj;
+
+ inctx = X509_STORE_CTX_new();
+ if (inctx == NULL)
+ goto cleanup;
+ if (!X509_STORE_CTX_init(inctx, cert_store, NULL, NULL))
+ goto cleanup;
+ obj = X509_STORE_CTX_get_obj_by_subject(inctx,
X509_LU_X509, X509_get_issuer_name(my_cert));
+ if (obj == NULL)
+ goto cleanup;
+
+ my_cid = OCSP_cert_to_id(NULL, my_cert,
X509_OBJECT_get0_X509(obj));
+ X509_OBJECT_free(obj);
+ }
+ else
+ {
+ my_issuer = sk_X509_value(peer_chain, cert_index + 1);
+ my_cid = OCSP_cert_to_id(NULL, my_cert, my_issuer);
+ }
+
+ if (my_cid == NULL)
+ goto cleanup;
+
+ /* verify ocsp status for given certificate */
+ if (OCSP_resp_find_status(basic_resp, my_cid,
+ &my_status, &rev_reason,
+ &rev_time, &this_update, &next_update) != 1)
+ goto cleanup; /* internal error */
+
+ if (my_status == V_OCSP_CERTSTATUS_GOOD)
+ {
+ if (OCSP_check_validity(this_update, next_update, 0,
-1) != 1)
+ goto cleanup; /* expired */
+ continue;
+ }
+ else if (my_status == V_OCSP_CERTSTATUS_REVOKED)
+ goto cleanup; /* revoked */
+ else if (my_status == V_OCSP_CERTSTATUS_UNKNOWN)
+ goto cleanup; /* unknown */
+ }
+ if (cert_index == resp_count)
+ ocsp_status = OCSP_CERT_STATUS_OK;
+
+cleanup:
+ if (ocsp_resp != NULL)
+ OCSP_RESPONSE_free(ocsp_resp);
+
+ if (basic_resp != NULL)
+ OCSP_BASICRESP_free(basic_resp);
+
+ return ocsp_status;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 82c18f870d..e65885cd39 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -405,6 +405,7 @@ struct pg_conn
char *gssdelegation; /* Try to delegate GSS credentials? (0
or 1) */
char *ssl_min_protocol_version; /* minimum TLS protocol version
*/
char *ssl_max_protocol_version; /* maximum TLS protocol version
*/
+ char *sslocspstapling; /* request ocsp stapling from server */
char *target_session_attrs; /* desired session properties */
char *require_auth; /* name of the expected auth method */
char *load_balance_hosts; /* load balance over hosts */
--
2.34.1
From de75ecb6eb6d19177e213c77da000766171c95a5 Mon Sep 17 00:00:00 2001
From: David Zhang <idraw...@gmail.com>
Date: Tue, 5 Mar 2024 15:32:05 -0800
Subject: [PATCH 2/3] doc - support certificate status check using OCSP
stapling
---
doc/src/sgml/config.sgml | 17 +++++++++++++++++
doc/src/sgml/libpq.sgml | 30 ++++++++++++++++++++++++++++++
doc/src/sgml/runtime.sgml | 6 ++++++
3 files changed, 53 insertions(+)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b38cbd714a..28c0d3e00a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1262,6 +1262,23 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
+ <varlistentry id="guc-ssl-ocsp-file" xreflabel="ssl_ocsp_file">
+ <term><varname>ssl_ocsp_file</varname> (<type>string</type>)
+ <indexterm>
+ <primary><varname>ssl_ocsp_file</varname> configuration
parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Specifies the name of the file containing the SSL server stapled
+ certificate status (OCSP Stapling). Relative paths are relative to the
+ data directory. This parameter can only be set in the
+ <filename>postgresql.conf</filename> file or on the server command
line.
+ The default is empty, meaning no OCSP Stapling file is loaded.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-ssl-crl-file" xreflabel="ssl_crl_file">
<term><varname>ssl_crl_file</varname> (<type>string</type>)
<indexterm>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 1d8998efb2..a6457453f5 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1761,6 +1761,21 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem>
</varlistentry>
+ <varlistentry id="libpq-connect-sslocspstapling"
xreflabel="sslocspstapling">
+ <term><literal>sslocspstapling</literal></term>
+ <listitem>
+ <para>
+ If set to 1, an <acronym>SSL</acronym> connection to the server
+ will send a certificate status request.
<application>libpq</application>
+ will then refuse to connect if the server does not provide a valid OCSP
+ Stapling response. If set to 0 (default),
+ <application>libpq</application> will neither request the certificate
+ status nor check OCSP Stapling response. This option is only
available if
+ <productname>PostgreSQL</productname> is compiled with SSL support.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-connect-sslkey" xreflabel="sslkey">
<term><literal>sslkey</literal></term>
<listitem>
@@ -8323,6 +8338,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void
*passThrough)
</para>
</listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary><envar>PGSSLOCSPSTAPLING</envar></primary>
+ </indexterm>
+ <envar>PGSSLOCSPSTAPLING</envar> behaves the same as the <xref
+ linkend="libpq-connect-sslocspstapling"/> connection parameter.
+ </para>
+ </listitem>
+
<listitem>
<para>
<indexterm>
@@ -8899,6 +8924,11 @@
ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
Windows).
</para>
+ <para>
+ Stapled server certificate status (OCSP Stapling) will be checked,
+ if the parameter <literal>sslocspstapling</literal> is set to 1.
+ </para>
+
<para>
The location of the root certificate file and the CRL can be changed by
setting
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 6047b8171d..0fa946a2ee 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2405,6 +2405,12 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
<entry>sent to client to indicate server's identity</entry>
</row>
+ <row>
+ <entry><xref linkend="guc-ssl-ocsp-file"/></entry>
+ <entry>server certificate status stapled by ocsp responder</entry>
+ <entry>sent to client to indicate server certificate status</entry>
+ </row>
+
<row>
<entry><xref linkend="guc-ssl-key-file"/>
(<filename>$PGDATA/server.key</filename>)</entry>
<entry>server private key</entry>
--
2.34.1
From 442c78dc326d68af3e0b7453a2ec629d70196531 Mon Sep 17 00:00:00 2001
From: David Zhang <idraw...@gmail.com>
Date: Tue, 5 Mar 2024 15:38:35 -0800
Subject: [PATCH 3/3] test - support certificate status check using OCSP
staplin
---
src/test/ssl/conf/ocsp_ca.config | 18 +++
src/test/ssl/sslfiles.mk | 124 +++++++++++++++-
src/test/ssl/t/001_ssltests.pl | 198 ++++++++++++++++++++++++++
src/test/ssl/t/SSL/Backend/OpenSSL.pm | 3 +
src/test/ssl/t/SSL/Server.pm | 4 +
5 files changed, 342 insertions(+), 5 deletions(-)
create mode 100644 src/test/ssl/conf/ocsp_ca.config
diff --git a/src/test/ssl/conf/ocsp_ca.config b/src/test/ssl/conf/ocsp_ca.config
new file mode 100644
index 0000000000..b866085911
--- /dev/null
+++ b/src/test/ssl/conf/ocsp_ca.config
@@ -0,0 +1,18 @@
+# An OpenSSL format CSR config file for creating the ocsp responder
certificate.
+# This configuration file is also used when operating the CA.
+#
+# This certificate is used to sign OCSP resonpose.
+
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+req_extensions = v3_ocsp
+
+[ req_distinguished_name ]
+CN = Test CA for PostgreSQL SSL regression test ocsp
response
+
+# Extensions for OCSP responder certs
+[ v3_ocsp ]
+basicConstraints = CA:false
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk
index 88c93ec18d..fd92970698 100644
--- a/src/test/ssl/sslfiles.mk
+++ b/src/test/ssl/sslfiles.mk
@@ -35,6 +35,14 @@ SERVERS := server-cn-and-alt-names \
server-revoked
CLIENTS := client client-dn client-revoked client_ext client-long \
client-revoked-utf8
+OCSPS := server-ocsp-good \
+ server-ocsp-revoked \
+ server-ocsp-expired \
+ server-ocsp-unknown \
+ server-ca-ocsp-good \
+ server-ca-ocsp-revoked \
+ server-ca-ocsp-expired \
+ server-ca-ocsp-unknown
#
# To add a new non-standard certificate, add it to SPECIAL_CERTS and then add
@@ -62,14 +70,16 @@ COMBINATIONS := \
ssl/root+client_ca.crt \
ssl/root+client.crl \
ssl/client+client_ca.crt \
- ssl/server-cn-only+server_ca.crt
+ ssl/server-cn-only+server_ca.crt \
+ ssl/server-ip-cn-only+server_ca.crt
-CERTIFICATES := root_ca server_ca client_ca $(SERVERS) $(CLIENTS)
+CERTIFICATES := root_ca server_ca client_ca ocsp_ca $(SERVERS) $(CLIENTS)
STANDARD_CERTS := $(CERTIFICATES:%=ssl/%.crt)
STANDARD_KEYS := $(CERTIFICATES:%=ssl/%.key)
CRLS := ssl/root.crl \
ssl/client.crl \
ssl/server.crl
+OCSPRES := $(OCSPS:%=ssl/%.res)
SSLFILES := \
$(STANDARD_CERTS) \
@@ -77,7 +87,8 @@ SSLFILES := \
$(SPECIAL_CERTS) \
$(SPECIAL_KEYS) \
$(COMBINATIONS) \
- $(CRLS)
+ $(CRLS) \
+ $(OCSPRES)
SSLDIRS := ssl/client-crldir \
ssl/server-crldir \
ssl/root+client-crldir \
@@ -154,6 +165,9 @@ ssl/client+client_ca.crt: ssl/client.crt ssl/client_ca.crt
# for the server, to present to a client that only knows the root
ssl/server-cn-only+server_ca.crt: ssl/server-cn-only.crt ssl/server_ca.crt
+# for the server, to check when stapled ocsp response doesn't match server
certificate
+ssl/server-ip-cn-only+server_ca.crt: ssl/server-ip-cn-only.crt
ssl/server_ca.crt
+
# 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
@@ -174,7 +188,7 @@ $(STANDARD_KEYS):
# Standard certificates
#
-CA_CERTS := ssl/server_ca.crt ssl/client_ca.crt
+CA_CERTS := ssl/server_ca.crt ssl/client_ca.crt ssl/ocsp_ca.crt
SERVER_CERTS := $(SERVERS:%=ssl/%.crt)
CLIENT_CERTS := $(CLIENTS:%=ssl/%.crt)
@@ -227,6 +241,106 @@ ssl/%-certindex.attr:
ssl/%.srl:
date +%Y%m%d%H%M%S00 > $@
+#
+# OCSP
+#
+.INTERMEDIATE: $(OCSPS:%=ssl/%.idx)
+# given status 'V' without 'revocation date' to generate an ocsp response with
status 'good' for 10000 days
+ssl/server-ocsp-good.idx: ssl/server-cn-only.crt
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number=$$($(OPENSSL) x509 -in $< -noout -serial | cut -d "=" -f
2); \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ echo "V\t$$expiration_date\t\t$$serial_number\tunknown\t$$cert_subject"
> $@
+
+# given status 'R' and 'revocation date' to generate an ocsp response with
status 'revoked' for 10000 days
+ssl/server-ocsp-revoked.idx: ssl/server-cn-only.crt
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number=$$($(OPENSSL) x509 -in $< -noout -serial | cut -d "=" -f
2); \
+ revocation_date=$$(date --utc +'%y%m%d%H%M%SZ'); \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ echo
"R\t$$expiration_date\t$$revocation_date\t$$serial_number\tunknown\t$$cert_subject"
> $@
+
+# generate an ocsp response with status 'unknown' using a none-existing
certificate serial number 1970010100000000
+ssl/server-ocsp-unknown.idx: ssl/server-cn-only.crt
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number="1970010100000000"; \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ echo "V\t$$expiration_date\t\t$$serial_number\tunknown\t$$cert_subject"
> $@
+
+# generate an ocsp response with status 'good' but nextUpdate 'expired' in
only 1 minute
+ssl/server-ocsp-expired.idx: ssl/server-cn-only.crt
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number=$$($(OPENSSL) x509 -in $< -noout -serial | cut -d "=" -f
2); \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ echo "V\t$$expiration_date\t\t$$serial_number\tunknown\t$$cert_subject"
> $@
+
+# server-cn-only.crt (good), ocsp response for server_ca.crt in
(good|revoked|unknown|expired)
+# good, good
+ssl/server-ca-ocsp-good.idx: ssl/server_ca.crt ssl/server-ocsp-good.idx
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number=$$($(OPENSSL) x509 -in $< -noout -serial | cut -d "=" -f
2); \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ cat ssl/server-ocsp-good.idx > $@; \
+ echo "V\t$$expiration_date\t\t$$serial_number\tunknown\t$$cert_subject"
>> $@
+
+# good, revoked
+ssl/server-ca-ocsp-revoked.idx: ssl/server_ca.crt ssl/server-ocsp-good.idx
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number=$$($(OPENSSL) x509 -in $< -noout -serial | cut -d "=" -f
2); \
+ revocation_date=$$(date --utc +'%y%m%d%H%M%SZ'); \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ cat ssl/server-ocsp-good.idx > $@; \
+ echo
"R\t$$expiration_date\t$$revocation_date\t$$serial_number\tunknown\t$$cert_subject"
>> $@
+
+# good, unknown
+ssl/server-ca-ocsp-unknown.idx: ssl/server_ca.crt ssl/server-ocsp-good.idx
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number="1970010100000001"; \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ cat ssl/server-ocsp-good.idx > $@; \
+ echo "V\t$$expiration_date\t\t$$serial_number\tunknown\t$$cert_subject"
>> $@
+
+# good, expired
+ssl/server-ca-ocsp-expired.idx: ssl/server_ca.crt ssl/server-ocsp-good.idx
+ expiration_date=$$($(OPENSSL) x509 -in $< -noout -dates | grep
"notAfter" | cut -d "=" -f 2 | xargs -I {} date --date="{}" --utc
+'%Y%m%d%H%M%S'Z); \
+ serial_number=$$($(OPENSSL) x509 -in $< -noout -serial | cut -d "=" -f
2); \
+ cert_subject=$$($(OPENSSL) x509 -in $< -noout -subject | sed
's/subject=OU = /\/OU=/' | sed 's/, CN = /\/CN=/'); \
+ cat ssl/server-ocsp-good.idx > $@; \
+ echo "V\t$$expiration_date\t\t$$serial_number\tunknown\t$$cert_subject"
>> $@
+
+$(OCSPRES):
+# server-cn-only: 'good'
+ssl/server-ocsp-good.res: ssl/server-ocsp-good.idx ssl/server-cn-only.crt
ssl/ocsp_ca.crt ssl/root+server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -issuer ssl/server_ca.crt -ndays
10000 -cert ssl/server-cn-only.crt -respout $@
+
+# server-cn-only: 'revoked'
+ssl/server-ocsp-revoked.res: ssl/server-ocsp-revoked.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -issuer ssl/server_ca.crt -ndays
10000 -cert ssl/server-cn-only.crt -respout $@
+
+# server-cn-only: 'unknown'
+ssl/server-ocsp-unknown.res: ssl/server-ocsp-unknown.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -issuer ssl/server_ca.crt -ndays
10000 -cert ssl/server-cn-only.crt -respout $@
+
+# server-cn-only: 'expired'
+ssl/server-ocsp-expired.res: ssl/server-ocsp-expired.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -issuer ssl/server_ca.crt -nmin 1
-cert ssl/server-cn-only.crt -respout $@
+
+# server-cn-only, server_ca: 'good, good'
+ssl/server-ca-ocsp-good.res: ssl/server-ca-ocsp-good.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt ssl/server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -ndays 10000 -issuer
ssl/server_ca.crt -cert ssl/server-cn-only.crt -issuer ssl/root_ca.crt -cert
ssl/server_ca.crt -respout $@
+
+# server-cn-only, server_ca: 'good, revoked'
+ssl/server-ca-ocsp-revoked.res: ssl/server-ca-ocsp-revoked.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt ssl/server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -ndays 10000 -issuer
ssl/server_ca.crt -cert ssl/server-cn-only.crt -issuer ssl/root_ca.crt -cert
ssl/server_ca.crt -respout $@
+
+# server-cn-only, server_ca: 'good, unknown'
+ssl/server-ca-ocsp-unknown.res: ssl/server-ca-ocsp-unknown.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt ssl/server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -ndays 10000 -issuer
ssl/server_ca.crt -cert ssl/server-cn-only.crt -issuer ssl/root_ca.crt -cert
ssl/server_ca.crt -respout $@
+
+# server-cn-only, server_ca: 'good, expired'
+ssl/server-ca-ocsp-expired.res: ssl/server-ca-ocsp-expired.idx
ssl/server-cn-only.crt ssl/ocsp_ca.crt ssl/root+server_ca.crt ssl/server_ca.crt
+ $(OPENSSL) ocsp -index $< -rsigner ssl/ocsp_ca.crt -rkey
ssl/ocsp_ca.key -CA ssl/root+server_ca.crt -nmin 1 -issuer ssl/server_ca.crt
-cert ssl/server-cn-only.crt -issuer ssl/root_ca.crt -cert ssl/server_ca.crt
-respout $@
+
#
# CRLs
#
@@ -262,7 +376,7 @@ ssl/%-crldir:
.PHONY: sslfiles-clean
sslfiles-clean:
- rm -f $(SSLFILES) ssl/*.old ssl/*.csr ssl/*.srl ssl/*-certindex*
+ rm -f $(SSLFILES) ssl/*.old ssl/*.csr ssl/*.srl ssl/*-certindex*
ssl/*.idx ssl/*.res
rm -rf $(SSLDIRS) ssl/new_certs_dir
# The difference between the below clean targets and sslfiles-clean is that the
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 94ff043c8e..92656542ae 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -904,4 +904,202 @@ $node->connect_fails(
# ]
);
+# Test ocsp stapling.
+# Use a stapled ocsp response with different status for certificate
server-cn-only
+# so that the client can verify the ocsp response when sslocspstapling is set.
+# server-cn-only certificates status is 'good'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ocsp-good');
+
+# Reset the common_connstr
+$common_connstr =
+ "$default_ssl_connstr user=ssltestuser dbname=trustdb
hostaddr=$SERVERHOSTADDR host=ocsp-good.pg-ssltest.test";
+
+# Continue the TLS connection when certificate status is 'good' in stapled
ocsp response.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "connect with valid stapled ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# server-cn-only certificates status is 'revoked'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ocsp-revoked');
+
+# Fail the TLS connection when certificate status is 'revoked' in stapled ocsp
response.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with a revoked ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# server-cn-only certificates status is 'unknown'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ocsp-unknown');
+
+# Fail the TLS connection when certificate status is 'unknown' in stapled ocsp
response.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with a revoked ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# server-cn-only certificates status is 'expired'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ocsp-expired');
+
+# Fail the TLS connection when stapled ocsp response is 'expired'.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with an expired ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# Use a stapled ocsp response with different status for certificate
server-cn-only and server_ca
+# so that the client can verify the ocsp response when sslocspstapling is set.
+# server-cn-only, server_ca: 'good, good'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only+server_ca',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ca-ocsp-good');
+
+# Reset the common_connstr
+$common_connstr =
+ "$default_ssl_connstr user=ssltestuser dbname=trustdb
hostaddr=$SERVERHOSTADDR host=ocsp-good.pg-ssltest.test";
+
+# Continue the TLS connection when certificate status is 'good' in stapled
ocsp response.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "connect with valid stapled ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# server-cn-only, server_ca: 'good, revoked'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only+server_ca',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ca-ocsp-revoked');
+
+# Fail the TLS connection when certificate status is 'revoked' in stapled ocsp
response.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with a revoked ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# server-cn-only, server_ca: 'good, unknown'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only+server_ca',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ca-ocsp-unknown');
+
+# Fail the TLS connection when certificate status is 'unknown' in stapled ocsp
response.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with a revoked ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+# server-cn-only, server_ca: 'good, expired'
+switch_server_cert(
+ $node,
+ certfile => 'server-cn-only+server_ca',
+ keyfile => 'server-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ca-ocsp-expired');
+
+# Fail the TLS connection when stapled ocsp response is 'expired'.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with an expired ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+
+# Use a stapled ocsp response which doesn't match the certificate
server-ip-cn-only
+# so that the client will fail the TLS connection when sslocspstapling is set.
+switch_server_cert(
+ $node,
+ certfile => 'server-ip-cn-only',
+ keyfile => 'server-ip-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ocsp-good');
+
+# Fail the TLS connection when stapled ocsp response doesn't match certificate.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with an expired ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+
+# Use a stapled ocsp response which doesn't match the certificate
server-ip-cn-only
+# so that the client will fail the TLS connection when sslocspstapling is set.
+switch_server_cert(
+ $node,
+ certfile => 'server-ip-cn-only+server_ca',
+ keyfile => 'server-ip-cn-only',
+ cafile => 'root_ca',
+ ocspfile => 'server-ca-ocsp-good');
+
+# Fail the TLS connection when stapled ocsp response doesn't match certificate.
+$node->connect_fails(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=1",
+ "failed with an expired ocsp response when sslocspstapling=1");
+
+# Continue the TLS connection when no ocsp response is required.
+$node->connect_ok(
+ "$common_connstr sslrootcert=ssl/root_ca.crt sslmode=verify-ca
sslocspstapling=0",
+ "connect without requesting ocsp response when sslocspstapling=0");
+
+
done_testing();
diff --git a/src/test/ssl/t/SSL/Backend/OpenSSL.pm
b/src/test/ssl/t/SSL/Backend/OpenSSL.pm
index 410b4b1a3f..3f4c1a0fe4 100644
--- a/src/test/ssl/t/SSL/Backend/OpenSSL.pm
+++ b/src/test/ssl/t/SSL/Backend/OpenSSL.pm
@@ -73,6 +73,7 @@ sub init
_copy_files("ssl/root+client_ca.crt", $pgdata);
_copy_files("ssl/root_ca.crt", $pgdata);
_copy_files("ssl/root+client.crl", $pgdata);
+ _copy_files("ssl/server-*.res", $pgdata);
mkdir("$pgdata/root+client-crldir")
or die "unable to create server CRL dir $pgdata/root+client-crldir:
$!";
_copy_files("ssl/root+client-crldir/*", "$pgdata/root+client-crldir/");
@@ -186,6 +187,8 @@ sub set_server_cert
. "ssl_crl_file='$params->{crlfile}'\n";
$sslconf .= "ssl_crl_dir='$params->{crldir}'\n"
if defined $params->{crldir};
+ $sslconf .= "ssl_ocsp_file='$params->{ocspfile}.res'\n"
+ if defined $params->{ocspfile};
return $sslconf;
}
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index 149a938511..f806b433da 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -255,6 +255,10 @@ The CA certificate to use. Implementation is SSL backend
specific.
The certificate file to use. Implementation is SSL backend specific.
+=item ocspfile => B<value>
+
+The ocsp stapling file to use. Implementation is SSL backend specific.
+
=item keyfile => B<value>
The private key file to use. Implementation is SSL backend specific.
--
2.34.1