[Python-ideas] Expose reasons for SSL/TLS cert verification failures

Chi Hsuan Yen yan12125 at gmail.com
Fri Sep 9 06:23:48 EDT 2016


Hi Python enthusiasts,

Currently _ssl.c always reports CERTIFICATE_VERIFY_FAILED for any
certification verification errors. In OpenSSL, it's possible to tell from
different reasons that lead to CERTIFICATE_VERIFY_FAILED. For example,
https://expired.badssl.com/ reports X509_V_ERR_CERT_HAS_EXPIRED, and
https://self-signed.badssl.com/ reports
X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT. Seems CPython does not expose such
information yet? I hope it can be added to CPython. For example, creating a
new exception class SSLCertificateError, which is a subclass of SSLError,
that provides error codes like X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT. Any
ideas?

The attachment is a naive try to printf some information about a
verification failure. It's just a proof-of-concept and does not provide any
practical advantage :)

Best,

Yen Chi Hsuan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160909/f43afa28/attachment.html>
-------------- next part --------------
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2770,16 +2770,24 @@ get_verify_mode(PySSLContext *self, void
     case SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
         return PyLong_FromLong(PY_SSL_CERT_REQUIRED);
     }
     PyErr_SetString(PySSLErrorObject,
                     "invalid return value from SSL_CTX_get_verify_mode");
     return NULL;
 }
 
+static int verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+    long x509_err = X509_STORE_CTX_get_error(ctx);
+    printf("ok = %d, err = %ld, errstr = %s\n", ok, x509_err,
+           X509_verify_cert_error_string(x509_err));
+    return ok;
+}
+
 static int
 set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
 {
     int n, mode;
     if (!PyArg_Parse(arg, "i", &n))
         return -1;
     if (n == PY_SSL_CERT_NONE)
         mode = SSL_VERIFY_NONE;
@@ -2794,16 +2802,22 @@ set_verify_mode(PySSLContext *self, PyOb
     }
     if (mode == SSL_VERIFY_NONE && self->check_hostname) {
         PyErr_SetString(PyExc_ValueError,
                         "Cannot set verify_mode to CERT_NONE when "
                         "check_hostname is enabled.");
         return -1;
     }
     SSL_CTX_set_verify(self->ctx, mode, NULL);
+
+    {
+        X509_STORE *store = SSL_CTX_get_cert_store(self->ctx);
+        X509_STORE_set_verify_cb(store, verify_cb);
+    }
+
     return 0;
 }
 
 static PyObject *
 get_verify_flags(PySSLContext *self, void *c)
 {
     X509_STORE *store;
     X509_VERIFY_PARAM *param;


More information about the Python-ideas mailing list