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