From 8deb372ca053bd290dbe403a6a92db2a0fd0a7ce Mon Sep 17 00:00:00 2001 From: Michael Lazar Date: Sat, 30 May 2020 14:39:31 -0400 Subject: [PATCH] Fix verify_callback behavior, change "verified" to "authorised" to conform with the wording in the gemini specification. --- CHANGELOG.md | 2 +- README.md | 6 +++--- jetforce/protocol.py | 2 +- jetforce/tls.py | 19 ++++++++++++++----- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 465718b..49aed63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ Check out the updated examples in the *examples/* directory for more details. Jetforce will now accept self-signed and unvalidated client certificates. The ``capath`` and ``cafile`` arguments can still be provided, and will attempt to validate the certificate using of the underlying OpenSSL library. The result -of this validation will be saved in the ``TLS_CLIENT_VERIFIED`` environment +of this validation will be saved in the ``TLS_CLIENT_AUTHORISED`` environment variable so that each application can decide how it wants to accept/reject the connection. diff --git a/README.md b/README.md index b5e8d2d..5aef4f0 100644 --- a/README.md +++ b/README.md @@ -103,12 +103,12 @@ $ openssl req -newkey rsa:2048 -nodes -keyout {hostname}.key \ -nodes -x509 -out {hostname}.crt -subj "/CN={hostname}" ``` -Jetforce also supports TLS client certificates (both self-signed and CA verified). +Jetforce also supports TLS client certificates (both self-signed and CA authorised). Requests that are made with client certificates will include additional CGI/environment variables with information about the TLS connection. You can specify a CA for client validation with the ``--tls-cafile`` or ``--tls-capath`` -flags. Connections validated by the CA will have the ``TLS_CLIENT_VERIFIED`` environment +flags. Connections validated by the CA will have the ``TLS_CLIENT_AUTHORISED`` environment variable set to True. Instructions on how to generate CA's are outside of the scope of this readme, but you can find many helpful tutorials [online](https://www.makethenmakeinstall.com/2014/05/ssl-client-authentication-step-by-step/). @@ -159,7 +159,7 @@ Additional CGI variables will also be included when the connection uses a TLS cl | TLS_CLIENT_NOT_BEFORE | Certificate activation date. | ``2020-04-05T04:18:22Z`` | | TLS_CLIENT_NOT_AFTER | Certificate expiration date. | ``2021-04-05T04:18:22Z`` | | TLS_CLIENT_SERIAL_NUMBER | Certificate serial number. | ``73629018972631`` | -| TLS_CLIENT_VERIFIED | Was the certificate verified by OpenSSL? | ``0`` (not verified) / ``1`` (verified) | +| TLS_CLIENT_AUTHORISED | Was the certificate verified by the server's CA? | ``0`` (not authorised) / ``1`` (authorised) | The CGI script must then write the gemini response to the *stdout* stream. This includes the status code and meta string on the first line, and the diff --git a/jetforce/protocol.py b/jetforce/protocol.py index aed5d05..a0fab4a 100644 --- a/jetforce/protocol.py +++ b/jetforce/protocol.py @@ -162,7 +162,7 @@ class GeminiProtocol(LineOnlyReceiver): "TLS_CLIENT_NOT_AFTER": cert_data["not_after"], "TLS_CLIENT_SERIAL_NUMBER": cert_data["serial_number"], # Grab the value that was stashed during the TLS handshake - "TLS_CLIENT_VERIFIED": getattr(conn, "verified", False), + "TLS_CLIENT_AUTHORISED": getattr(conn, "authorised", False), } ) return environ diff --git a/jetforce/tls.py b/jetforce/tls.py index 9db0ae8..1e741c4 100644 --- a/jetforce/tls.py +++ b/jetforce/tls.py @@ -105,12 +105,21 @@ class GeminiCertificateOptions(CertificateOptions): """ Callback used by OpenSSL for client certificate verification. - preverify_ok returns the verification result that OpenSSL has already - obtained, so return this value to cede control to the underlying - library. Returning true will always allow client certificates, even if - they are self-signed. + preverify_ok will contain the verification result that OpenSSL has + determined based on the server's CA trust store. + + Return preverify_ok to cede control to the underlying library. + Return True to allow unverified, self-signed client certificates. + + This callback may be invoked multiple times during the TLS handshake. + If at any point OpenSSL returns a preverify_ok value of zero, we should + mark the certificate as not trusted. """ - conn.verified = preverify_ok + if not hasattr(conn, "authorised"): + conn.authorised = preverify_ok + else: + conn.authorised *= preverify_ok + return True def proto_select_callback(