Skip to content

Commit f9b0674

Browse files
committed
Handle absence of keylog more gracefully
1 parent 2222a94 commit f9b0674

File tree

3 files changed

+45
-30
lines changed

3 files changed

+45
-30
lines changed

Lib/test/test_ssl.py

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
from ssl import Purpose, TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType
4545

4646
Py_DEBUG_WIN32 = support.Py_DEBUG and sys.platform == 'win32'
47+
HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename')
48+
requires_keylog = unittest.skipUnless(
49+
HAS_KEYLOG, 'test requires OpenSSL 1.1.1 with keylog callback')
4750

4851
PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
4952
HOST = socket_helper.HOST
@@ -1567,6 +1570,7 @@ def msg_cb(conn, direction, version, content_type, msg_type, data, cycle=ctx):
15671570
gc.collect()
15681571
self.assertIs(wr(), None)
15691572

1573+
@requires_keylog
15701574
def test_keylog_filename_refcycle(self):
15711575
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
15721576
ctx.keylog_filename = os_helper.TESTFN
@@ -5282,9 +5286,6 @@ def test_internal_chain_server(self):
52825286
self.assertEqual(res, b'\x02\n')
52835287

52845288

5285-
HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename')
5286-
requires_keylog = unittest.skipUnless(
5287-
HAS_KEYLOG, 'test requires OpenSSL 1.1.1 with keylog callback')
52885289

52895290
class TestSSLDebug(unittest.TestCase):
52905291

@@ -5294,17 +5295,13 @@ def keylog_lines(self, fname=os_helper.TESTFN):
52945295

52955296
@requires_keylog
52965297
def test_keylog_defaults(self):
5298+
os_helper.unlink(os_helper.TESTFN)
52975299
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
52985300
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
52995301
self.assertEqual(ctx.keylog_filename, None)
53005302

53015303
self.assertFalse(os.path.isfile(os_helper.TESTFN))
5302-
try:
5303-
ctx.keylog_filename = os_helper.TESTFN
5304-
except RuntimeError:
5305-
if Py_DEBUG_WIN32:
5306-
self.skipTest("not supported on Win32 debug build")
5307-
raise
5304+
ctx.keylog_filename = os_helper.TESTFN
53085305
self.assertEqual(ctx.keylog_filename, os_helper.TESTFN)
53095306
self.assertTrue(os.path.isfile(os_helper.TESTFN))
53105307
self.assertEqual(self.keylog_lines(), 1)
@@ -5325,12 +5322,7 @@ def test_keylog_filename(self):
53255322
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
53265323
client_context, server_context, hostname = testing_context()
53275324

5328-
try:
5329-
client_context.keylog_filename = os_helper.TESTFN
5330-
except RuntimeError:
5331-
if Py_DEBUG_WIN32:
5332-
self.skipTest("not supported on Win32 debug build")
5333-
raise
5325+
client_context.keylog_filename = os_helper.TESTFN
53345326

53355327
server = ThreadedEchoServer(context=server_context, chatty=False)
53365328
with server:
@@ -5373,12 +5365,7 @@ def test_keylog_env(self):
53735365
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
53745366
self.assertEqual(ctx.keylog_filename, None)
53755367

5376-
try:
5377-
ctx = ssl.create_default_context()
5378-
except RuntimeError:
5379-
if Py_DEBUG_WIN32:
5380-
self.skipTest("not supported on Win32 debug build")
5381-
raise
5368+
ctx = ssl.create_default_context()
53825369
self.assertEqual(ctx.keylog_filename, os_helper.TESTFN)
53835370

53845371
ctx = ssl._create_stdlib_context()

Modules/_ssl.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ enum py_proto_version {
316316
#define PySSL_CB_MAXLEN 128
317317

318318

319+
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !(defined(MS_WINDOWS) && defined(Py_DEBUG))
320+
# define PY_HAS_KEYLOG 1
321+
#endif
322+
319323
typedef struct {
320324
PyObject_HEAD
321325
SSL_CTX *ctx;
@@ -328,8 +332,10 @@ typedef struct {
328332
int post_handshake_auth;
329333
#endif
330334
PyObject *msg_cb;
335+
#ifdef PY_HAS_KEYLOG
331336
PyObject *keylog_filename;
332337
BIO *keylog_bio;
338+
#endif
333339
/* Cached module state, also used in SSLSocket and SSLSession code. */
334340
_sslmodulestate *state;
335341
#ifndef OPENSSL_NO_PSK
@@ -355,7 +361,7 @@ typedef struct {
355361
PyObject_HEAD
356362
PyObject *Socket; /* weakref to socket on which we're layered */
357363
SSL *ssl;
358-
PySSLContext *ctx; /* weakref to SSL context */
364+
PySSLContext *ctx; /* SSL context */
359365
char shutdown_seen_zero;
360366
enum py_ssl_server_or_client socket_type;
361367
PyObject *owner; /* Python level "owner" passed to servername callback */
@@ -2343,7 +2349,8 @@ static int
23432349
_ssl__SSLSocket_context_set_impl(PySSLSocket *self, PyObject *value)
23442350
/*[clinic end generated code: output=6b0a6cc5cf33d9fe input=f7fc1674b660df96]*/
23452351
{
2346-
if (PyObject_TypeCheck(value, self->ctx->state->PySSLContext_Type)) {
2352+
_sslmodulestate *state = get_state_obj(self);
2353+
if (PyObject_TypeCheck(value, state->PySSLContext_Type)) {
23472354
Py_SETREF(self->ctx, (PySSLContext *)Py_NewRef(value));
23482355
SSL_set_SSL_CTX(self->ssl, self->ctx->ctx);
23492356
/* Set SSL* internal msg_callback to state of new context's state */
@@ -3487,8 +3494,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
34873494
self->ctx = ctx;
34883495
self->protocol = proto_version;
34893496
self->msg_cb = NULL;
3497+
#ifdef PY_HAS_KEYLOG
34903498
self->keylog_filename = NULL;
34913499
self->keylog_bio = NULL;
3500+
#endif
34923501
self->alpn_protocols = NULL;
34933502
self->set_sni_cb = NULL;
34943503
self->state = get_ssl_state(module);
@@ -3599,7 +3608,9 @@ context_traverse(PyObject *op, visitproc visit, void *arg)
35993608
PySSLContext *self = PySSLContext_CAST(op);
36003609
Py_VISIT(self->set_sni_cb);
36013610
Py_VISIT(self->msg_cb);
3611+
#ifdef PY_HAS_KEYLOG
36023612
Py_VISIT(self->keylog_filename);
3613+
#endif
36033614
#ifndef OPENSSL_NO_PSK
36043615
Py_VISIT(self->psk_client_callback);
36053616
Py_VISIT(self->psk_server_callback);
@@ -3614,18 +3625,22 @@ context_clear(PyObject *op)
36143625
PySSLContext *self = PySSLContext_CAST(op);
36153626
Py_CLEAR(self->set_sni_cb);
36163627
Py_CLEAR(self->msg_cb);
3628+
#ifdef PY_HAS_KEYLOG
36173629
Py_CLEAR(self->keylog_filename);
3630+
#endif
36183631
#ifndef OPENSSL_NO_PSK
36193632
Py_CLEAR(self->psk_client_callback);
36203633
Py_CLEAR(self->psk_server_callback);
36213634
#endif
3635+
#ifdef PY_HAS_KEYLOG
36223636
if (self->keylog_bio != NULL) {
36233637
Py_BEGIN_ALLOW_THREADS
36243638
BIO_free_all(self->keylog_bio);
36253639
Py_END_ALLOW_THREADS
36263640
_PySSL_FIX_ERRNO;
36273641
self->keylog_bio = NULL;
36283642
}
3643+
#endif
36293644
return 0;
36303645
}
36313646

@@ -5680,8 +5695,10 @@ static PyGetSetDef context_getsetlist[] = {
56805695
_SSL__SSLCONTEXT__HOST_FLAGS_GETSETDEF
56815696
_SSL__SSLCONTEXT_MINIMUM_VERSION_GETSETDEF
56825697
_SSL__SSLCONTEXT_MAXIMUM_VERSION_GETSETDEF
5698+
#ifdef PY_HAS_KEYLOG
56835699
{"keylog_filename", _PySSLContext_get_keylog_filename,
56845700
_PySSLContext_set_keylog_filename, NULL},
5701+
#endif
56855702
{"_msg_callback", _PySSLContext_get_msg_callback,
56865703
_PySSLContext_set_msg_callback, NULL},
56875704
_SSL__SSLCONTEXT_SNI_CALLBACK_GETSETDEF
@@ -6009,8 +6026,23 @@ PySSLSession_richcompare(PyObject *left, PyObject *right, int op)
60096026
return NULL;
60106027
}
60116028

6029+
PySSLSession *left_sess = PySSLSession_CAST(left);
6030+
PySSLSession *right_sess = PySSLSession_CAST(right);
6031+
6032+
if (left_sess->ctx == NULL || right_sess->ctx == NULL) {
6033+
if (op == Py_EQ) {
6034+
if (left == right) Py_RETURN_TRUE;
6035+
Py_RETURN_FALSE;
6036+
}
6037+
if (op == Py_NE) {
6038+
if (left != right) Py_RETURN_TRUE;
6039+
Py_RETURN_FALSE;
6040+
}
6041+
Py_RETURN_NOTIMPLEMENTED;
6042+
}
6043+
60126044
int result;
6013-
PyTypeObject *sesstype = PySSLSession_CAST(left)->ctx->state->PySSLSession_Type;
6045+
PyTypeObject *sesstype = left_sess->ctx->state->PySSLSession_Type;
60146046

60156047
if (!Py_IS_TYPE(left, sesstype) || !Py_IS_TYPE(right, sesstype)) {
60166048
Py_RETURN_NOTIMPLEMENTED;

Modules/_ssl/debughelpers.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ _PySSLContext_set_msg_callback(PyObject *op, PyObject *arg,
117117
return 0;
118118
}
119119

120+
#ifdef PY_HAS_KEYLOG
120121
static void
121122
_PySSL_keylog_callback(const SSL *ssl, const char *line)
122123
{
@@ -178,12 +179,6 @@ _PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg,
178179
PySSLContext *self = PySSLContext_CAST(op);
179180
FILE *fp;
180181

181-
#if defined(MS_WINDOWS) && defined(Py_DEBUG)
182-
PyErr_SetString(PyExc_NotImplementedError,
183-
"set_keylog_filename: unavailable on Windows debug build");
184-
return -1;
185-
#endif
186-
187182
/* Reset variables and callback first */
188183
SSL_CTX_set_keylog_callback(self->ctx, NULL);
189184
Py_CLEAR(self->keylog_filename);
@@ -225,3 +220,4 @@ _PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg,
225220
SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback);
226221
return 0;
227222
}
223+
#endif

0 commit comments

Comments
 (0)