summaryrefslogtreecommitdiffstats
path: root/Modules/_ssl.c
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2019-05-31 09:44:05 (GMT)
committerGitHub <noreply@github.com>2019-05-31 09:44:05 (GMT)
commitc7f7069e77c58e83b847c0bfe4d5aadf6add2e68 (patch)
tree306bee26619ebc132be4b98fd60d0daf79964cf0 /Modules/_ssl.c
parente9b51c0ad81da1da11ae65840ac8b50a8521373c (diff)
downloadcpython-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.c104
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,