diff options
author | Christian Heimes <christian@python.org> | 2019-05-31 09:44:05 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-31 09:44:05 (GMT) |
commit | c7f7069e77c58e83b847c0bfe4d5aadf6add2e68 (patch) | |
tree | 306bee26619ebc132be4b98fd60d0daf79964cf0 /Modules/_ssl.c | |
parent | e9b51c0ad81da1da11ae65840ac8b50a8521373c (diff) | |
download | cpython-c7f7069e77c58e83b847c0bfe4d5aadf6add2e68.zip cpython-c7f7069e77c58e83b847c0bfe4d5aadf6add2e68.tar.gz cpython-c7f7069e77c58e83b847c0bfe4d5aadf6add2e68.tar.bz2 |
bpo-34271: Add ssl debugging helpers (GH-10031)
The ssl module now can dump key material to a keylog file and trace TLS
protocol messages with a tracing callback. The default and stdlib
contexts also support SSLKEYLOGFILE env var.
The msg_callback and related enums are private members. The feature
is designed for internal debugging and not for end users.
Signed-off-by: Christian Heimes <christian@python.org>
Diffstat (limited to 'Modules/_ssl.c')
-rw-r--r-- | Modules/_ssl.c | 104 |
1 files changed, 93 insertions, 11 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 4fb7dca..f40127d 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -185,6 +185,10 @@ static void _PySSLFixErrno(void) { # define HAVE_NPN 0 #endif +#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) +#define HAVE_OPENSSL_KEYLOG 1 +#endif + #ifndef INVALID_SOCKET /* MS defines this */ #define INVALID_SOCKET (-1) #endif @@ -424,6 +428,11 @@ typedef struct { #ifdef TLS1_3_VERSION int post_handshake_auth; #endif + PyObject *msg_cb; +#ifdef HAVE_OPENSSL_KEYLOG + PyObject *keylog_filename; + BIO *keylog_bio; +#endif } PySSLContext; typedef struct { @@ -444,6 +453,13 @@ typedef struct { PyObject *owner; /* Python level "owner" passed to servername callback */ PyObject *server_hostname; _PySSLError err; /* last seen error from various sources */ + /* Some SSL callbacks don't have error reporting. Callback wrappers + * store exception information on the socket. The handshake, read, write, + * and shutdown methods check for chained exceptions. + */ + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; } PySSLSocket; typedef struct { @@ -517,6 +533,8 @@ typedef enum { #define GET_SOCKET_TIMEOUT(sock) \ ((sock != NULL) ? (sock)->sock_timeout : 0) +#include "_ssl/debughelpers.c" + /* * SSL errors. */ @@ -703,6 +721,18 @@ fail: Py_XDECREF(verify_obj); } +static int +PySSL_ChainExceptions(PySSLSocket *sslsock) { + if (sslsock->exc_type == NULL) + return 0; + + _PyErr_ChainExceptions(sslsock->exc_type, sslsock->exc_value, sslsock->exc_tb); + sslsock->exc_type = NULL; + sslsock->exc_value = NULL; + sslsock->exc_tb = NULL; + return -1; +} + static PyObject * PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) { @@ -796,6 +826,7 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) } fill_and_set_sslerror(sslsock, type, p, errstr, lineno, e); ERR_clear_error(); + PySSL_ChainExceptions(sslsock); return NULL; } @@ -903,6 +934,9 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, self->owner = NULL; self->server_hostname = NULL; self->err = err; + self->exc_type = NULL; + self->exc_value = NULL; + self->exc_tb = NULL; /* Make sure the SSL error state is initialized */ ERR_clear_error(); @@ -1052,11 +1086,12 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) Py_XDECREF(sock); if (ret < 1) return PySSL_SetError(self, ret, __FILE__, __LINE__); - + if (PySSL_ChainExceptions(self) < 0) + return NULL; Py_RETURN_NONE; - error: Py_XDECREF(sock); + PySSL_ChainExceptions(self); return NULL; } @@ -2151,8 +2186,26 @@ PyDoc_STRVAR(PySSL_get_owner_doc, "The Python-level owner of this object.\ Passed as \"self\" in servername callback."); +static int +PySSL_traverse(PySSLSocket *self, visitproc visit, void *arg) +{ + Py_VISIT(self->exc_type); + Py_VISIT(self->exc_value); + Py_VISIT(self->exc_tb); + return 0; +} + +static int +PySSL_clear(PySSLSocket *self) +{ + Py_CLEAR(self->exc_type); + Py_CLEAR(self->exc_value); + Py_CLEAR(self->exc_tb); + return 0; +} -static void PySSL_dealloc(PySSLSocket *self) +static void +PySSL_dealloc(PySSLSocket *self) { if (self->ssl) SSL_free(self->ssl); @@ -2333,13 +2386,14 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) err.ssl == SSL_ERROR_WANT_WRITE); Py_XDECREF(sock); - if (len > 0) - return PyLong_FromLong(len); - else + if (len <= 0) return PySSL_SetError(self, len, __FILE__, __LINE__); - + if (PySSL_ChainExceptions(self) < 0) + return NULL; + return PyLong_FromLong(len); error: Py_XDECREF(sock); + PySSL_ChainExceptions(self); return NULL; } @@ -2486,6 +2540,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1, PySSL_SetError(self, count, __FILE__, __LINE__); goto error; } + if (self->exc_type != NULL) + goto error; done: Py_XDECREF(sock); @@ -2498,6 +2554,7 @@ done: } error: + PySSL_ChainExceptions(self); Py_XDECREF(sock); if (!group_right_1) Py_XDECREF(dest); @@ -2601,11 +2658,13 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) /* Retain the SSL error code */ break; } - if (ret < 0) { Py_XDECREF(sock); - return PySSL_SetError(self, ret, __FILE__, __LINE__); + PySSL_SetError(self, ret, __FILE__, __LINE__); + return NULL; } + if (self->exc_type != NULL) + goto error; if (sock) /* It's already INCREF'ed */ return (PyObject *) sock; @@ -2614,6 +2673,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) error: Py_XDECREF(sock); + PySSL_ChainExceptions(self); return NULL; } @@ -2889,8 +2949,8 @@ static PyTypeObject PySSLSocket_Type = { 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ + (traverseproc) PySSL_traverse, /*tp_traverse*/ + (inquiry) PySSL_clear, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ @@ -3002,6 +3062,11 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->ctx = ctx; self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; self->protocol = proto_version; + self->msg_cb = NULL; +#ifdef HAVE_OPENSSL_KEYLOG + self->keylog_filename = NULL; + self->keylog_bio = NULL; +#endif #if HAVE_NPN self->npn_protocols = NULL; #endif @@ -3127,6 +3192,7 @@ context_traverse(PySSLContext *self, visitproc visit, void *arg) #ifndef OPENSSL_NO_TLSEXT Py_VISIT(self->set_sni_cb); #endif + Py_VISIT(self->msg_cb); return 0; } @@ -3136,6 +3202,16 @@ context_clear(PySSLContext *self) #ifndef OPENSSL_NO_TLSEXT Py_CLEAR(self->set_sni_cb); #endif + Py_CLEAR(self->msg_cb); +#ifdef HAVE_OPENSSL_KEYLOG + Py_CLEAR(self->keylog_filename); + if (self->keylog_bio != NULL) { + PySSL_BEGIN_ALLOW_THREADS + BIO_free_all(self->keylog_bio); + PySSL_END_ALLOW_THREADS + self->keylog_bio = NULL; + } +#endif return 0; } @@ -4570,6 +4646,12 @@ static PyGetSetDef context_getsetlist[] = { {"maximum_version", (getter) get_maximum_version, (setter) set_maximum_version, NULL}, #endif +#ifdef HAVE_OPENSSL_KEYLOG + {"keylog_filename", (getter) _PySSLContext_get_keylog_filename, + (setter) _PySSLContext_set_keylog_filename, NULL}, +#endif + {"_msg_callback", (getter) _PySSLContext_get_msg_callback, + (setter) _PySSLContext_set_msg_callback, NULL}, {"sni_callback", (getter) get_sni_callback, (setter) set_sni_callback, PySSLContext_sni_callback_doc}, {"options", (getter) get_options, |