aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Modules/_ssl
diff options
context:
space:
mode:
authornkozlovskiy <nmk@ydb.tech>2023-09-29 12:24:06 +0300
committernkozlovskiy <nmk@ydb.tech>2023-09-29 12:41:34 +0300
commite0e3e1717e3d33762ce61950504f9637a6e669ed (patch)
treebca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/tools/python3/src/Modules/_ssl
parent38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff)
downloadydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz
add ydb deps
Diffstat (limited to 'contrib/tools/python3/src/Modules/_ssl')
-rw-r--r--contrib/tools/python3/src/Modules/_ssl/cert.c245
-rw-r--r--contrib/tools/python3/src/Modules/_ssl/clinic/cert.c.h60
-rw-r--r--contrib/tools/python3/src/Modules/_ssl/debughelpers.c219
-rw-r--r--contrib/tools/python3/src/Modules/_ssl/misc.c34
4 files changed, 558 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Modules/_ssl/cert.c b/contrib/tools/python3/src/Modules/_ssl/cert.c
new file mode 100644
index 0000000000..bda66dc4d9
--- /dev/null
+++ b/contrib/tools/python3/src/Modules/_ssl/cert.c
@@ -0,0 +1,245 @@
+#include "Python.h"
+#include "../_ssl.h"
+
+#include "openssl/err.h"
+#include "openssl/bio.h"
+#include "openssl/pem.h"
+#include "openssl/x509.h"
+
+/*[clinic input]
+module _ssl
+class _ssl.Certificate "PySSLCertificate *" "PySSLCertificate_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=780fc647948cfffc]*/
+
+#include "clinic/cert.c.h"
+
+static PyObject *
+newCertificate(PyTypeObject *type, X509 *cert, int upref)
+{
+ PySSLCertificate *self;
+
+ assert(type != NULL && type->tp_alloc != NULL);
+ assert(cert != NULL);
+
+ self = (PySSLCertificate *) type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+ if (upref == 1) {
+ X509_up_ref(cert);
+ }
+ self->cert = cert;
+ self->hash = -1;
+
+ return (PyObject *) self;
+}
+
+static PyObject *
+_PySSL_CertificateFromX509(_sslmodulestate *state, X509 *cert, int upref)
+{
+ return newCertificate(state->PySSLCertificate_Type, cert, upref);
+}
+
+static PyObject*
+_PySSL_CertificateFromX509Stack(_sslmodulestate *state, STACK_OF(X509) *stack, int upref)
+{
+ int len, i;
+ PyObject *result = NULL;
+
+ len = sk_X509_num(stack);
+ result = PyList_New(len);
+ if (result == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < len; i++) {
+ X509 *cert = sk_X509_value(stack, i);
+ PyObject *ocert = _PySSL_CertificateFromX509(state, cert, upref);
+ if (ocert == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyList_SetItem(result, i, ocert);
+ }
+ return result;
+}
+
+/*[clinic input]
+_ssl.Certificate.public_bytes
+ format: int(c_default="PY_SSL_ENCODING_PEM") = Encoding.PEM
+
+[clinic start generated code]*/
+
+static PyObject *
+_ssl_Certificate_public_bytes_impl(PySSLCertificate *self, int format)
+/*[clinic end generated code: output=c01ddbb697429e12 input=4d38c45e874b0e64]*/
+{
+ BIO *bio;
+ int retcode;
+ PyObject *result;
+ _sslmodulestate *state = get_state_cert(self);
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL) {
+ PyErr_SetString(state->PySSLErrorObject,
+ "failed to allocate BIO");
+ return NULL;
+ }
+ switch(format) {
+ case PY_SSL_ENCODING_PEM:
+ retcode = PEM_write_bio_X509(bio, self->cert);
+ break;
+ case PY_SSL_ENCODING_PEM_AUX:
+ retcode = PEM_write_bio_X509_AUX(bio, self->cert);
+ break;
+ case PY_SSL_ENCODING_DER:
+ retcode = i2d_X509_bio(bio, self->cert);
+ break;
+ default:
+ PyErr_SetString(PyExc_ValueError, "Unsupported format");
+ BIO_free(bio);
+ return NULL;
+ }
+ if (retcode != 1) {
+ BIO_free(bio);
+ _setSSLError(state, NULL, 0, __FILE__, __LINE__);
+ return NULL;
+ }
+ if (format == PY_SSL_ENCODING_DER) {
+ result = _PySSL_BytesFromBIO(state, bio);
+ } else {
+ result = _PySSL_UnicodeFromBIO(state, bio, "error");
+ }
+ BIO_free(bio);
+ return result;
+}
+
+
+/*[clinic input]
+_ssl.Certificate.get_info
+
+[clinic start generated code]*/
+
+static PyObject *
+_ssl_Certificate_get_info_impl(PySSLCertificate *self)
+/*[clinic end generated code: output=0f0deaac54f4408b input=ba2c1694b39d0778]*/
+{
+ return _decode_certificate(get_state_cert(self), self->cert);
+}
+
+static PyObject*
+_x509name_print(_sslmodulestate *state, X509_NAME *name, int indent, unsigned long flags)
+{
+ PyObject *res;
+ BIO *biobuf;
+
+ biobuf = BIO_new(BIO_s_mem());
+ if (biobuf == NULL) {
+ PyErr_SetString(PyExc_MemoryError, "failed to allocate BIO");
+ return NULL;
+ }
+
+ if (X509_NAME_print_ex(biobuf, name, indent, flags) <= 0) {
+ _setSSLError(state, NULL, 0, __FILE__, __LINE__);
+ BIO_free(biobuf);
+ return NULL;
+ }
+ res = _PySSL_UnicodeFromBIO(state, biobuf, "strict");
+ BIO_free(biobuf);
+ return res;
+}
+
+/* ************************************************************************
+ * PySSLCertificate_Type
+ */
+
+static PyObject *
+certificate_repr(PySSLCertificate *self)
+{
+ PyObject *osubject, *result;
+
+ /* subject string is ASCII encoded, UTF-8 chars are quoted */
+ osubject = _x509name_print(
+ get_state_cert(self),
+ X509_get_subject_name(self->cert),
+ 0,
+ XN_FLAG_RFC2253
+ );
+ if (osubject == NULL)
+ return NULL;
+ result = PyUnicode_FromFormat(
+ "<%s '%U'>",
+ Py_TYPE(self)->tp_name, osubject
+ );
+ Py_DECREF(osubject);
+ return result;
+}
+
+static Py_hash_t
+certificate_hash(PySSLCertificate *self)
+{
+ if (self->hash == (Py_hash_t)-1) {
+ unsigned long hash;
+ hash = X509_subject_name_hash(self->cert);
+ if ((Py_hash_t)hash == (Py_hash_t)-1) {
+ self->hash = -2;
+ } else {
+ self->hash = (Py_hash_t)hash;
+ }
+ }
+ return self->hash;
+}
+
+static PyObject *
+certificate_richcompare(PySSLCertificate *self, PyObject *other, int op)
+{
+ int cmp;
+ _sslmodulestate *state = get_state_cert(self);
+
+ if (Py_TYPE(other) != state->PySSLCertificate_Type) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ /* only support == and != */
+ if ((op != Py_EQ) && (op != Py_NE)) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ cmp = X509_cmp(self->cert, ((PySSLCertificate*)other)->cert);
+ if (((op == Py_EQ) && (cmp == 0)) || ((op == Py_NE) && (cmp != 0))) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static void
+certificate_dealloc(PySSLCertificate *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ X509_free(self->cert);
+ Py_TYPE(self)->tp_free(self);
+ Py_DECREF(tp);
+}
+
+static PyMethodDef certificate_methods[] = {
+ /* methods */
+ _SSL_CERTIFICATE_PUBLIC_BYTES_METHODDEF
+ _SSL_CERTIFICATE_GET_INFO_METHODDEF
+ {NULL, NULL}
+};
+
+static PyType_Slot PySSLCertificate_slots[] = {
+ {Py_tp_dealloc, certificate_dealloc},
+ {Py_tp_repr, certificate_repr},
+ {Py_tp_hash, certificate_hash},
+ {Py_tp_richcompare, certificate_richcompare},
+ {Py_tp_methods, certificate_methods},
+ {0, 0},
+};
+
+static PyType_Spec PySSLCertificate_spec = {
+ "_ssl.Certificate",
+ sizeof(PySSLCertificate),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE,
+ PySSLCertificate_slots,
+};
diff --git a/contrib/tools/python3/src/Modules/_ssl/clinic/cert.c.h b/contrib/tools/python3/src/Modules/_ssl/clinic/cert.c.h
new file mode 100644
index 0000000000..53cedabc3f
--- /dev/null
+++ b/contrib/tools/python3/src/Modules/_ssl/clinic/cert.c.h
@@ -0,0 +1,60 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_ssl_Certificate_public_bytes__doc__,
+"public_bytes($self, /, format=Encoding.PEM)\n"
+"--\n"
+"\n");
+
+#define _SSL_CERTIFICATE_PUBLIC_BYTES_METHODDEF \
+ {"public_bytes", _PyCFunction_CAST(_ssl_Certificate_public_bytes), METH_FASTCALL|METH_KEYWORDS, _ssl_Certificate_public_bytes__doc__},
+
+static PyObject *
+_ssl_Certificate_public_bytes_impl(PySSLCertificate *self, int format);
+
+static PyObject *
+_ssl_Certificate_public_bytes(PySSLCertificate *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"format", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "public_bytes", 0};
+ PyObject *argsbuf[1];
+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+ int format = PY_SSL_ENCODING_PEM;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!noptargs) {
+ goto skip_optional_pos;
+ }
+ format = _PyLong_AsInt(args[0]);
+ if (format == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+skip_optional_pos:
+ return_value = _ssl_Certificate_public_bytes_impl(self, format);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_ssl_Certificate_get_info__doc__,
+"get_info($self, /)\n"
+"--\n"
+"\n");
+
+#define _SSL_CERTIFICATE_GET_INFO_METHODDEF \
+ {"get_info", (PyCFunction)_ssl_Certificate_get_info, METH_NOARGS, _ssl_Certificate_get_info__doc__},
+
+static PyObject *
+_ssl_Certificate_get_info_impl(PySSLCertificate *self);
+
+static PyObject *
+_ssl_Certificate_get_info(PySSLCertificate *self, PyObject *Py_UNUSED(ignored))
+{
+ return _ssl_Certificate_get_info_impl(self);
+}
+/*[clinic end generated code: output=18885c4d167d5244 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/src/Modules/_ssl/debughelpers.c b/contrib/tools/python3/src/Modules/_ssl/debughelpers.c
new file mode 100644
index 0000000000..03c125eb44
--- /dev/null
+++ b/contrib/tools/python3/src/Modules/_ssl/debughelpers.c
@@ -0,0 +1,219 @@
+/* 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 */
+ PyObject *ssl_socket = NULL; /* ssl.SSLSocket or ssl.SSLObject */
+ 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;
+ }
+
+ if (ssl_obj->owner)
+ ssl_socket = PyWeakref_GetObject(ssl_obj->owner);
+ else if (ssl_obj->Socket)
+ ssl_socket = PyWeakref_GetObject(ssl_obj->Socket);
+ else
+ ssl_socket = (PyObject *)ssl_obj;
+ Py_INCREF(ssl_socket);
+
+ /* 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) {
+ PyErr_Fetch(&ssl_obj->exc_type, &ssl_obj->exc_value, &ssl_obj->exc_tb);
+ } 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) {
+ Py_INCREF(self->msg_cb);
+ return 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;
+ }
+ Py_INCREF(arg);
+ self->msg_cb = 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;
+ static PyThread_type_lock *lock = NULL;
+
+ 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->keylog_bio == NULL) {
+ return;
+ }
+
+ /* Allocate a static lock to synchronize writes to keylog file.
+ * 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.
+ */
+ if (lock == NULL) {
+ lock = PyThread_allocate_lock();
+ if (lock == NULL) {
+ PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock");
+ PyErr_Fetch(&ssl_obj->exc_type, &ssl_obj->exc_value,
+ &ssl_obj->exc_tb);
+ return;
+ }
+ }
+
+ PySSL_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);
+ PySSL_END_ALLOW_THREADS
+
+ if (res == -1) {
+ errno = e;
+ PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError,
+ ssl_obj->ctx->keylog_filename);
+ PyErr_Fetch(&ssl_obj->exc_type, &ssl_obj->exc_value, &ssl_obj->exc_tb);
+ }
+ PyGILState_Release(threadstate);
+}
+
+static PyObject *
+_PySSLContext_get_keylog_filename(PySSLContext *self, void *c) {
+ if (self->keylog_filename != NULL) {
+ Py_INCREF(self->keylog_filename);
+ return self->keylog_filename;
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int
+_PySSLContext_set_keylog_filename(PySSLContext *self, PyObject *arg, void *c) {
+ FILE *fp;
+ /* 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;
+ PySSL_BEGIN_ALLOW_THREADS
+ BIO_free_all(bio);
+ PySSL_END_ALLOW_THREADS
+ }
+
+ 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;
+ }
+ Py_INCREF(arg);
+ self->keylog_filename = arg;
+
+ /* Write a header for seekable, empty files (this excludes pipes). */
+ PySSL_BEGIN_ALLOW_THREADS
+ 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
+ SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback);
+ return 0;
+}
diff --git a/contrib/tools/python3/src/Modules/_ssl/misc.c b/contrib/tools/python3/src/Modules/_ssl/misc.c
new file mode 100644
index 0000000000..4de091d57e
--- /dev/null
+++ b/contrib/tools/python3/src/Modules/_ssl/misc.c
@@ -0,0 +1,34 @@
+#include "Python.h"
+#include "../_ssl.h"
+
+#include "openssl/bio.h"
+
+/* BIO_s_mem() to PyBytes
+ */
+static PyObject *
+_PySSL_BytesFromBIO(_sslmodulestate *state, BIO *bio)
+{
+ long size;
+ char *data = NULL;
+ size = BIO_get_mem_data(bio, &data);
+ if (data == NULL || size < 0) {
+ PyErr_SetString(PyExc_ValueError, "Not a memory BIO");
+ return NULL;
+ }
+ return PyBytes_FromStringAndSize(data, size);
+}
+
+/* BIO_s_mem() to PyUnicode
+ */
+static PyObject *
+_PySSL_UnicodeFromBIO(_sslmodulestate *state, BIO *bio, const char *error)
+{
+ long size;
+ char *data = NULL;
+ size = BIO_get_mem_data(bio, &data);
+ if (data == NULL || size < 0) {
+ PyErr_SetString(PyExc_ValueError, "Not a memory BIO");
+ return NULL;
+ }
+ return PyUnicode_DecodeUTF8(data, size, error);
+}