Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Modules/_ssl/debughelpers.c
12 views
1
/* Debug helpers */
2
3
#ifndef SSL3_MT_CHANGE_CIPHER_SPEC
4
/* Dummy message type for handling CCS like a normal handshake message
5
* not defined in OpenSSL 1.0.2
6
*/
7
#define SSL3_MT_CHANGE_CIPHER_SPEC 0x0101
8
#endif
9
10
static void
11
_PySSL_msg_callback(int write_p, int version, int content_type,
12
const void *buf, size_t len, SSL *ssl, void *arg)
13
{
14
const char *cbuf = (const char *)buf;
15
PyGILState_STATE threadstate;
16
PyObject *res = NULL;
17
PySSLSocket *ssl_obj = NULL; /* ssl._SSLSocket, borrowed ref */
18
int msg_type;
19
20
threadstate = PyGILState_Ensure();
21
22
ssl_obj = (PySSLSocket *)SSL_get_app_data(ssl);
23
assert(Py_IS_TYPE(ssl_obj, get_state_sock(ssl_obj)->PySSLSocket_Type));
24
if (ssl_obj->ctx->msg_cb == NULL) {
25
PyGILState_Release(threadstate);
26
return;
27
}
28
29
PyObject *ssl_socket; /* ssl.SSLSocket or ssl.SSLObject */
30
if (ssl_obj->owner)
31
ssl_socket = _PyWeakref_GET_REF(ssl_obj->owner);
32
else if (ssl_obj->Socket)
33
ssl_socket = _PyWeakref_GET_REF(ssl_obj->Socket);
34
else
35
ssl_socket = (PyObject *)Py_NewRef(ssl_obj);
36
assert(ssl_socket != NULL); // _PyWeakref_GET_REF() can return NULL
37
38
/* assume that OpenSSL verifies all payload and buf len is of sufficient
39
length */
40
switch(content_type) {
41
case SSL3_RT_CHANGE_CIPHER_SPEC:
42
msg_type = SSL3_MT_CHANGE_CIPHER_SPEC;
43
break;
44
case SSL3_RT_ALERT:
45
/* byte 0: level */
46
/* byte 1: alert type */
47
msg_type = (int)cbuf[1];
48
break;
49
case SSL3_RT_HANDSHAKE:
50
msg_type = (int)cbuf[0];
51
break;
52
#ifdef SSL3_RT_HEADER
53
case SSL3_RT_HEADER:
54
/* frame header encodes version in bytes 1..2 */
55
version = cbuf[1] << 8 | cbuf[2];
56
msg_type = (int)cbuf[0];
57
break;
58
#endif
59
#ifdef SSL3_RT_INNER_CONTENT_TYPE
60
case SSL3_RT_INNER_CONTENT_TYPE:
61
msg_type = (int)cbuf[0];
62
break;
63
#endif
64
default:
65
/* never SSL3_RT_APPLICATION_DATA */
66
msg_type = -1;
67
break;
68
}
69
70
res = PyObject_CallFunction(
71
ssl_obj->ctx->msg_cb, "Osiiiy#",
72
ssl_socket, write_p ? "write" : "read",
73
version, content_type, msg_type,
74
buf, len
75
);
76
if (res == NULL) {
77
ssl_obj->exc = PyErr_GetRaisedException();
78
} else {
79
Py_DECREF(res);
80
}
81
Py_XDECREF(ssl_socket);
82
83
PyGILState_Release(threadstate);
84
}
85
86
87
static PyObject *
88
_PySSLContext_get_msg_callback(PySSLContext *self, void *c) {
89
if (self->msg_cb != NULL) {
90
return Py_NewRef(self->msg_cb);
91
} else {
92
Py_RETURN_NONE;
93
}
94
}
95
96
static int
97
_PySSLContext_set_msg_callback(PySSLContext *self, PyObject *arg, void *c) {
98
Py_CLEAR(self->msg_cb);
99
if (arg == Py_None) {
100
SSL_CTX_set_msg_callback(self->ctx, NULL);
101
}
102
else {
103
if (!PyCallable_Check(arg)) {
104
SSL_CTX_set_msg_callback(self->ctx, NULL);
105
PyErr_SetString(PyExc_TypeError,
106
"not a callable object");
107
return -1;
108
}
109
self->msg_cb = Py_NewRef(arg);
110
SSL_CTX_set_msg_callback(self->ctx, _PySSL_msg_callback);
111
}
112
return 0;
113
}
114
115
static void
116
_PySSL_keylog_callback(const SSL *ssl, const char *line)
117
{
118
PyGILState_STATE threadstate;
119
PySSLSocket *ssl_obj = NULL; /* ssl._SSLSocket, borrowed ref */
120
int res, e;
121
122
threadstate = PyGILState_Ensure();
123
124
ssl_obj = (PySSLSocket *)SSL_get_app_data(ssl);
125
assert(Py_IS_TYPE(ssl_obj, get_state_sock(ssl_obj)->PySSLSocket_Type));
126
PyThread_type_lock lock = get_state_sock(ssl_obj)->keylog_lock;
127
assert(lock != NULL);
128
if (ssl_obj->ctx->keylog_bio == NULL) {
129
return;
130
}
131
/*
132
* The lock is neither released on exit nor on fork(). The lock is
133
* also shared between all SSLContexts although contexts may write to
134
* their own files. IMHO that's good enough for a non-performance
135
* critical debug helper.
136
*/
137
138
PySSL_BEGIN_ALLOW_THREADS
139
PyThread_acquire_lock(lock, 1);
140
res = BIO_printf(ssl_obj->ctx->keylog_bio, "%s\n", line);
141
e = errno;
142
(void)BIO_flush(ssl_obj->ctx->keylog_bio);
143
PyThread_release_lock(lock);
144
PySSL_END_ALLOW_THREADS
145
146
if (res == -1) {
147
errno = e;
148
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError,
149
ssl_obj->ctx->keylog_filename);
150
ssl_obj->exc = PyErr_GetRaisedException();
151
}
152
PyGILState_Release(threadstate);
153
}
154
155
static PyObject *
156
_PySSLContext_get_keylog_filename(PySSLContext *self, void *c) {
157
if (self->keylog_filename != NULL) {
158
return Py_NewRef(self->keylog_filename);
159
} else {
160
Py_RETURN_NONE;
161
}
162
}
163
164
static int
165
_PySSLContext_set_keylog_filename(PySSLContext *self, PyObject *arg, void *c) {
166
FILE *fp;
167
/* Reset variables and callback first */
168
SSL_CTX_set_keylog_callback(self->ctx, NULL);
169
Py_CLEAR(self->keylog_filename);
170
if (self->keylog_bio != NULL) {
171
BIO *bio = self->keylog_bio;
172
self->keylog_bio = NULL;
173
PySSL_BEGIN_ALLOW_THREADS
174
BIO_free_all(bio);
175
PySSL_END_ALLOW_THREADS
176
}
177
178
if (arg == Py_None) {
179
/* None disables the callback */
180
return 0;
181
}
182
183
/* _Py_fopen_obj() also checks that arg is of proper type. */
184
fp = _Py_fopen_obj(arg, "a" PY_STDIOTEXTMODE);
185
if (fp == NULL)
186
return -1;
187
188
self->keylog_bio = BIO_new_fp(fp, BIO_CLOSE | BIO_FP_TEXT);
189
if (self->keylog_bio == NULL) {
190
PyErr_SetString(get_state_ctx(self)->PySSLErrorObject,
191
"Can't malloc memory for keylog file");
192
return -1;
193
}
194
self->keylog_filename = Py_NewRef(arg);
195
196
/* Write a header for seekable, empty files (this excludes pipes). */
197
PySSL_BEGIN_ALLOW_THREADS
198
if (BIO_tell(self->keylog_bio) == 0) {
199
BIO_puts(self->keylog_bio,
200
"# TLS secrets log file, generated by OpenSSL / Python\n");
201
(void)BIO_flush(self->keylog_bio);
202
}
203
PySSL_END_ALLOW_THREADS
204
SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback);
205
return 0;
206
}
207
208