gh-106687: _ssl: use uint64_t for SSL options (#106700)

SSL_CTX_get_options() uses uint64_t for options:
https://www.openssl.org/docs/man3.1/man3/SSL_CTX_get_options.html

Fix this compiler warning on Windows with MSC:

    conversion from 'uint64_t' to 'long', possible loss of data
This commit is contained in:
Victor Stinner 2023-07-17 17:55:30 +02:00 committed by GitHub
parent 036bb73656
commit ad95c7253a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 26 deletions

View File

@ -339,6 +339,15 @@ def test_constants(self):
ssl.OP_NO_TLSv1_2 ssl.OP_NO_TLSv1_2
self.assertEqual(ssl.PROTOCOL_TLS, ssl.PROTOCOL_SSLv23) self.assertEqual(ssl.PROTOCOL_TLS, ssl.PROTOCOL_SSLv23)
def test_options(self):
# gh-106687: SSL options values are unsigned integer (uint64_t)
for name in dir(ssl):
if not name.startswith('OP_'):
continue
with self.subTest(option=name):
value = getattr(ssl, name)
self.assertGreaterEqual(value, 0, f"ssl.{name}")
def test_ssl_types(self): def test_ssl_types(self):
ssl_types = [ ssl_types = [
_ssl._SSLContext, _ssl._SSLContext,
@ -951,6 +960,7 @@ def test_get_ciphers(self):
) )
def test_options(self): def test_options(self):
# Test default SSLContext options
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value
default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
@ -959,16 +969,30 @@ def test_options(self):
OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE | OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE |
OP_ENABLE_MIDDLEBOX_COMPAT) OP_ENABLE_MIDDLEBOX_COMPAT)
self.assertEqual(default, ctx.options) self.assertEqual(default, ctx.options)
# disallow TLSv1
with warnings_helper.check_warnings(): with warnings_helper.check_warnings():
ctx.options |= ssl.OP_NO_TLSv1 ctx.options |= ssl.OP_NO_TLSv1
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)
# allow TLSv1
with warnings_helper.check_warnings(): with warnings_helper.check_warnings():
ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1)
self.assertEqual(default, ctx.options) self.assertEqual(default, ctx.options)
# clear all options
ctx.options = 0 ctx.options = 0
# Ubuntu has OP_NO_SSLv3 forced on by default # Ubuntu has OP_NO_SSLv3 forced on by default
self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3)
# invalid options
with self.assertRaises(OverflowError):
ctx.options = -1
with self.assertRaises(OverflowError):
ctx.options = 2 ** 100
with self.assertRaises(TypeError):
ctx.options = "abc"
def test_verify_mode_protocol(self): def test_verify_mode_protocol(self):
with warnings_helper.check_warnings(): with warnings_helper.check_warnings():
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)

View File

@ -3025,7 +3025,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
/*[clinic end generated code: output=2cf0d7a0741b6bd1 input=8d58a805b95fc534]*/ /*[clinic end generated code: output=2cf0d7a0741b6bd1 input=8d58a805b95fc534]*/
{ {
PySSLContext *self; PySSLContext *self;
long options; uint64_t options;
const SSL_METHOD *method = NULL; const SSL_METHOD *method = NULL;
SSL_CTX *ctx = NULL; SSL_CTX *ctx = NULL;
X509_VERIFY_PARAM *params; X509_VERIFY_PARAM *params;
@ -3618,20 +3618,32 @@ PyDoc_STRVAR(PySSLContext_security_level_doc, "The current security level");
static PyObject * static PyObject *
get_options(PySSLContext *self, void *c) get_options(PySSLContext *self, void *c)
{ {
return PyLong_FromLong(SSL_CTX_get_options(self->ctx)); uint64_t options = SSL_CTX_get_options(self->ctx);
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(options));
return PyLong_FromUnsignedLongLong(options);
} }
static int static int
set_options(PySSLContext *self, PyObject *arg, void *c) set_options(PySSLContext *self, PyObject *arg, void *c)
{ {
long new_opts, opts, set, clear; PyObject *new_opts_obj;
long opt_no = ( unsigned long long new_opts_arg;
uint64_t new_opts, opts, clear, set;
uint64_t opt_no = (
SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3 SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3
); );
if (!PyArg_Parse(arg, "l", &new_opts)) if (!PyArg_Parse(arg, "O!", &PyLong_Type, &new_opts_obj)) {
return -1; return -1;
}
new_opts_arg = PyLong_AsUnsignedLongLong(new_opts_obj);
if (new_opts_arg == (unsigned long long)-1 && PyErr_Occurred()) {
return -1;
}
Py_BUILD_ASSERT(sizeof(new_opts) >= sizeof(new_opts_arg));
new_opts = (uint64_t)new_opts_arg;
opts = SSL_CTX_get_options(self->ctx); opts = SSL_CTX_get_options(self->ctx);
clear = opts & ~new_opts; clear = opts & ~new_opts;
set = ~opts & new_opts; set = ~opts & new_opts;
@ -3645,8 +3657,9 @@ set_options(PySSLContext *self, PyObject *arg, void *c)
if (clear) { if (clear) {
SSL_CTX_clear_options(self->ctx, clear); SSL_CTX_clear_options(self->ctx, clear);
} }
if (set) if (set) {
SSL_CTX_set_options(self->ctx, set); SSL_CTX_set_options(self->ctx, set);
}
return 0; return 0;
} }
@ -5754,10 +5767,24 @@ sslmodule_init_socketapi(PyObject *module)
return 0; return 0;
} }
static int
sslmodule_add_option(PyObject *m, const char *name, uint64_t value)
{
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(value));
PyObject *obj = PyLong_FromUnsignedLongLong(value);
if (obj == NULL) {
return -1;
}
int res = PyModule_AddObjectRef(m, name, obj);
Py_DECREF(obj);
return res;
}
static int static int
sslmodule_init_constants(PyObject *m) sslmodule_init_constants(PyObject *m)
{ {
PyModule_AddStringConstant(m, "_DEFAULT_CIPHERS", PyModule_AddStringConstant(m, "_DEFAULT_CIPHERS",
PY_SSL_DEFAULT_CIPHER_STRING); PY_SSL_DEFAULT_CIPHER_STRING);
@ -5877,46 +5904,47 @@ sslmodule_init_constants(PyObject *m)
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1_2", PyModule_AddIntConstant(m, "PROTOCOL_TLSv1_2",
PY_SSL_VERSION_TLS1_2); PY_SSL_VERSION_TLS1_2);
#define ADD_OPTION(NAME, VALUE) if (sslmodule_add_option(m, NAME, (VALUE)) < 0) return -1
/* protocol options */ /* protocol options */
PyModule_AddIntConstant(m, "OP_ALL", ADD_OPTION("OP_ALL", SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); ADD_OPTION("OP_NO_SSLv2", SSL_OP_NO_SSLv2);
PyModule_AddIntConstant(m, "OP_NO_SSLv2", SSL_OP_NO_SSLv2); ADD_OPTION("OP_NO_SSLv3", SSL_OP_NO_SSLv3);
PyModule_AddIntConstant(m, "OP_NO_SSLv3", SSL_OP_NO_SSLv3); ADD_OPTION("OP_NO_TLSv1", SSL_OP_NO_TLSv1);
PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1); ADD_OPTION("OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1);
PyModule_AddIntConstant(m, "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1); ADD_OPTION("OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2);
PyModule_AddIntConstant(m, "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2);
#ifdef SSL_OP_NO_TLSv1_3 #ifdef SSL_OP_NO_TLSv1_3
PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", SSL_OP_NO_TLSv1_3); ADD_OPTION("OP_NO_TLSv1_3", SSL_OP_NO_TLSv1_3);
#else #else
PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", 0); ADD_OPTION("OP_NO_TLSv1_3", 0);
#endif #endif
PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE", ADD_OPTION("OP_CIPHER_SERVER_PREFERENCE",
SSL_OP_CIPHER_SERVER_PREFERENCE); SSL_OP_CIPHER_SERVER_PREFERENCE);
PyModule_AddIntConstant(m, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE); ADD_OPTION("OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE);
PyModule_AddIntConstant(m, "OP_NO_TICKET", SSL_OP_NO_TICKET); ADD_OPTION("OP_NO_TICKET", SSL_OP_NO_TICKET);
PyModule_AddIntConstant(m, "OP_LEGACY_SERVER_CONNECT", ADD_OPTION("OP_LEGACY_SERVER_CONNECT",
SSL_OP_LEGACY_SERVER_CONNECT); SSL_OP_LEGACY_SERVER_CONNECT);
#ifdef SSL_OP_SINGLE_ECDH_USE #ifdef SSL_OP_SINGLE_ECDH_USE
PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE); ADD_OPTION("OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE);
#endif #endif
#ifdef SSL_OP_NO_COMPRESSION #ifdef SSL_OP_NO_COMPRESSION
PyModule_AddIntConstant(m, "OP_NO_COMPRESSION", ADD_OPTION("OP_NO_COMPRESSION",
SSL_OP_NO_COMPRESSION); SSL_OP_NO_COMPRESSION);
#endif #endif
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT #ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
PyModule_AddIntConstant(m, "OP_ENABLE_MIDDLEBOX_COMPAT", ADD_OPTION("OP_ENABLE_MIDDLEBOX_COMPAT",
SSL_OP_ENABLE_MIDDLEBOX_COMPAT); SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
#endif #endif
#ifdef SSL_OP_NO_RENEGOTIATION #ifdef SSL_OP_NO_RENEGOTIATION
PyModule_AddIntConstant(m, "OP_NO_RENEGOTIATION", ADD_OPTION("OP_NO_RENEGOTIATION",
SSL_OP_NO_RENEGOTIATION); SSL_OP_NO_RENEGOTIATION);
#endif #endif
#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF #ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
PyModule_AddIntConstant(m, "OP_IGNORE_UNEXPECTED_EOF", ADD_OPTION("OP_IGNORE_UNEXPECTED_EOF",
SSL_OP_IGNORE_UNEXPECTED_EOF); SSL_OP_IGNORE_UNEXPECTED_EOF);
#endif #endif
#ifdef SSL_OP_ENABLE_KTLS #ifdef SSL_OP_ENABLE_KTLS
PyModule_AddIntConstant(m, "OP_ENABLE_KTLS", SSL_OP_ENABLE_KTLS); ADD_OPTION("OP_ENABLE_KTLS", SSL_OP_ENABLE_KTLS);
#endif #endif
#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT #ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT