diff options
author | Christian Heimes <christian@python.org> | 2018-02-24 01:35:08 (GMT) |
---|---|---|
committer | Nathaniel J. Smith <njs@pobox.com> | 2018-02-24 01:35:08 (GMT) |
commit | 11a1493bc4198f1def5e572049485779cf54dc57 (patch) | |
tree | 8ad419e997569f9a9d05ea7e213d5092086608fa /Modules/_ssl.c | |
parent | 82ab13d756a04eab1dae58629473b95ddf363484 (diff) | |
download | cpython-11a1493bc4198f1def5e572049485779cf54dc57.zip cpython-11a1493bc4198f1def5e572049485779cf54dc57.tar.gz cpython-11a1493bc4198f1def5e572049485779cf54dc57.tar.bz2 |
[bpo-28414] Make all hostnames in SSL module IDN A-labels (GH-5128)
Previously, the ssl module stored international domain names (IDNs)
as U-labels. This is problematic for a number of reasons -- for
example, it made it impossible for users to use a different version
of IDNA than the one built into Python.
After this change, we always convert to A-labels as soon as possible,
and use them for all internal processing. In particular, server_hostname
attribute is now an A-label, and on the server side there's a new
sni_callback that receives the SNI servername as an A-label rather than
a U-label.
Diffstat (limited to 'Modules/_ssl.c')
-rw-r--r-- | Modules/_ssl.c | 131 |
1 files changed, 77 insertions, 54 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 7545e91..a0f8c1c 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -337,13 +337,14 @@ typedef struct { unsigned int alpn_protocols_len; #endif #ifndef OPENSSL_NO_TLSEXT - PyObject *set_hostname; + PyObject *set_sni_cb; #endif int check_hostname; /* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct. * We have to maintain our own copy. OpenSSL's hostflags default to 0. */ unsigned int hostflags; + int protocol; } PySSLContext; typedef struct { @@ -407,8 +408,6 @@ class _ssl.SSLSession "PySSLSession *" "&PySSLSession_Type" static int PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout); - -#define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type) #define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type) #define PySSLMemoryBIO_Check(v) (Py_TYPE(v) == &PySSLMemoryBIO_Type) #define PySSLSession_Check(v) (Py_TYPE(v) == &PySSLSession_Type) @@ -761,7 +760,7 @@ _ssl_configure_hostname(PySSLSocket *self, const char* server_hostname) ERR_clear_error(); } - hostname = PyUnicode_Decode(server_hostname, len, "idna", "strict"); + hostname = PyUnicode_Decode(server_hostname, len, "ascii", "strict"); if (hostname == NULL) { goto error; } @@ -1992,7 +1991,7 @@ PyDoc_STRVAR(PySSL_set_context_doc, "_setter_context(ctx)\n\ \ This changes the context associated with the SSLSocket. This is typically\n\ -used from within a callback function set by the set_servername_callback\n\ +used from within a callback function set by the sni_callback\n\ on the SSLContext to change the certificate information associated with the\n\ SSLSocket before the cryptographic exchange handshake messages\n"); @@ -2850,6 +2849,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) } self->ctx = ctx; self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; + self->protocol = proto_version; #if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG) self->npn_protocols = NULL; #endif @@ -2857,7 +2857,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->alpn_protocols = NULL; #endif #ifndef OPENSSL_NO_TLSEXT - self->set_hostname = NULL; + self->set_sni_cb = NULL; #endif /* Don't check host name by default */ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { @@ -2968,7 +2968,7 @@ static int context_traverse(PySSLContext *self, visitproc visit, void *arg) { #ifndef OPENSSL_NO_TLSEXT - Py_VISIT(self->set_hostname); + Py_VISIT(self->set_sni_cb); #endif return 0; } @@ -2977,7 +2977,7 @@ static int context_clear(PySSLContext *self) { #ifndef OPENSSL_NO_TLSEXT - Py_CLEAR(self->set_hostname); + Py_CLEAR(self->set_sni_cb); #endif return 0; } @@ -3354,6 +3354,10 @@ set_check_hostname(PySSLContext *self, PyObject *arg, void *c) return 0; } +static PyObject * +get_protocol(PySSLContext *self, void *c) { + return PyLong_FromLong(self->protocol); +} typedef struct { PyThreadState *thread_state; @@ -3818,9 +3822,9 @@ _ssl__SSLContext__wrap_socket_impl(PySSLContext *self, PyObject *sock, PyObject *res; /* server_hostname is either None (or absent), or to be encoded - using the idna encoding. */ + as IDN A-label (ASCII str). */ if (hostname_obj != Py_None) { - if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname)) + if (!PyArg_Parse(hostname_obj, "es", "ascii", &hostname)) return NULL; } @@ -3851,9 +3855,9 @@ _ssl__SSLContext__wrap_bio_impl(PySSLContext *self, PySSLMemoryBIO *incoming, PyObject *res; /* server_hostname is either None (or absent), or to be encoded - using the idna encoding. */ + as IDN A-label (ASCII str). */ if (hostname_obj != Py_None) { - if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname)) + if (!PyArg_Parse(hostname_obj, "es", "ascii", &hostname)) return NULL; } @@ -3967,15 +3971,13 @@ _servername_callback(SSL *s, int *al, void *args) int ret; PySSLContext *ssl_ctx = (PySSLContext *) args; PySSLSocket *ssl; - PyObject *servername_o; - PyObject *servername_idna; PyObject *result; /* The high-level ssl.SSLSocket object */ PyObject *ssl_socket; const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); PyGILState_STATE gstate = PyGILState_Ensure(); - if (ssl_ctx->set_hostname == NULL) { + if (ssl_ctx->set_sni_cb == NULL) { /* remove race condition in this the call back while if removing the * callback is in progress */ PyGILState_Release(gstate); @@ -4005,35 +4007,46 @@ _servername_callback(SSL *s, int *al, void *args) goto error; if (servername == NULL) { - result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket, + result = PyObject_CallFunctionObjArgs(ssl_ctx->set_sni_cb, ssl_socket, Py_None, ssl_ctx, NULL); } else { - servername_o = PyBytes_FromString(servername); - if (servername_o == NULL) { + PyObject *servername_bytes; + PyObject *servername_str; + + servername_bytes = PyBytes_FromString(servername); + if (servername_bytes == NULL) { PyErr_WriteUnraisable((PyObject *) ssl_ctx); goto error; } - servername_idna = PyUnicode_FromEncodedObject(servername_o, "idna", NULL); - if (servername_idna == NULL) { - PyErr_WriteUnraisable(servername_o); - Py_DECREF(servername_o); + /* server_hostname was encoded to an A-label by our caller; put it + * back into a str object, but still as an A-label (bpo-28414) + */ + servername_str = PyUnicode_FromEncodedObject(servername_bytes, "ascii", NULL); + Py_DECREF(servername_bytes); + if (servername_str == NULL) { + PyErr_WriteUnraisable(servername_bytes); goto error; } - Py_DECREF(servername_o); - result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket, - servername_idna, ssl_ctx, NULL); - Py_DECREF(servername_idna); + result = PyObject_CallFunctionObjArgs( + ssl_ctx->set_sni_cb, ssl_socket, servername_str, + ssl_ctx, NULL); + Py_DECREF(servername_str); } Py_DECREF(ssl_socket); if (result == NULL) { - PyErr_WriteUnraisable(ssl_ctx->set_hostname); + PyErr_WriteUnraisable(ssl_ctx->set_sni_cb); *al = SSL_AD_HANDSHAKE_FAILURE; ret = SSL_TLSEXT_ERR_ALERT_FATAL; } else { - if (result != Py_None) { + /* Result may be None, a SSLContext or an integer + * None and SSLContext are OK, integer or other values are an error. + */ + if (result == Py_None) { + ret = SSL_TLSEXT_ERR_OK; + } else { *al = (int) PyLong_AsLong(result); if (PyErr_Occurred()) { PyErr_WriteUnraisable(result); @@ -4041,9 +4054,6 @@ _servername_callback(SSL *s, int *al, void *args) } ret = SSL_TLSEXT_ERR_ALERT_FATAL; } - else { - ret = SSL_TLSEXT_ERR_OK; - } Py_DECREF(result); } @@ -4059,49 +4069,59 @@ error: } #endif -/*[clinic input] -_ssl._SSLContext.set_servername_callback - method as cb: object - / - -Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension. - -If the argument is None then the callback is disabled. The method is called -with the SSLSocket, the server name as a string, and the SSLContext object. -See RFC 6066 for details of the SNI extension. -[clinic start generated code]*/ - static PyObject * -_ssl__SSLContext_set_servername_callback(PySSLContext *self, PyObject *cb) -/*[clinic end generated code: output=3439a1b2d5d3b7ea input=a2a83620197d602b]*/ +get_sni_callback(PySSLContext *self, void *c) { + PyObject *cb = self->set_sni_cb; + if (cb == NULL) { + Py_RETURN_NONE; + } + Py_INCREF(cb); + return cb; +} + +static int +set_sni_callback(PySSLContext *self, PyObject *arg, void *c) +{ + if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) { + PyErr_SetString(PyExc_ValueError, + "sni_callback cannot be set on TLS_CLIENT context"); + return -1; + } #if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) - Py_CLEAR(self->set_hostname); - if (cb == Py_None) { + Py_CLEAR(self->set_sni_cb); + if (arg == Py_None) { SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); } else { - if (!PyCallable_Check(cb)) { + if (!PyCallable_Check(arg)) { SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); PyErr_SetString(PyExc_TypeError, "not a callable object"); - return NULL; + return -1; } - Py_INCREF(cb); - self->set_hostname = cb; + Py_INCREF(arg); + self->set_sni_cb = arg; SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback); SSL_CTX_set_tlsext_servername_arg(self->ctx, self); } - Py_RETURN_NONE; + return 0; #else PyErr_SetString(PyExc_NotImplementedError, "The TLS extension servername callback, " "SSL_CTX_set_tlsext_servername_callback, " "is not in the current OpenSSL library."); - return NULL; + return -1; #endif } +PyDoc_STRVAR(PySSLContext_sni_callback_doc, +"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\ +\n\ +If the argument is None then the callback is disabled. The method is called\n\ +with the SSLSocket, the server name as a string, and the SSLContext object.\n\ +See RFC 6066 for details of the SNI extension."); + /*[clinic input] _ssl._SSLContext.cert_store_stats @@ -4217,8 +4237,12 @@ static PyGetSetDef context_getsetlist[] = { (setter) set_check_hostname, NULL}, {"_host_flags", (getter) get_host_flags, (setter) set_host_flags, NULL}, + {"sni_callback", (getter) get_sni_callback, + (setter) set_sni_callback, PySSLContext_sni_callback_doc}, {"options", (getter) get_options, (setter) set_options, NULL}, + {"protocol", (getter) get_protocol, + NULL, NULL}, {"verify_flags", (getter) get_verify_flags, (setter) set_verify_flags, NULL}, {"verify_mode", (getter) get_verify_mode, @@ -4238,7 +4262,6 @@ static struct PyMethodDef context_methods[] = { _SSL__SSLCONTEXT_SESSION_STATS_METHODDEF _SSL__SSLCONTEXT_SET_DEFAULT_VERIFY_PATHS_METHODDEF _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF - _SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF |