]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #13634: Add support for querying and disabling SSL compression.
authorAntoine Pitrou <solipsis@pitrou.net>
Tue, 20 Dec 2011 09:13:40 +0000 (10:13 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Tue, 20 Dec 2011 09:13:40 +0000 (10:13 +0100)
Doc/library/ssl.rst
Lib/ssl.py
Lib/test/ssl_servers.py
Lib/test/test_ssl.py
Misc/NEWS
Modules/_ssl.c

index 7017b8f4f41ef9325663a8bd3796a25f42d41539..3cd9554c081ca58348437ee622c0298cfcc77bcf 100644 (file)
@@ -436,6 +436,15 @@ Constants
 
    .. versionadded:: 3.3
 
+.. data:: OP_NO_COMPRESSION
+
+   Disable compression on the SSL channel.  This is useful if the application
+   protocol supports its own compression scheme.
+
+   This option is only available with OpenSSL 1.0.0 and later.
+
+   .. versionadded:: 3.3
+
 .. data:: HAS_SNI
 
    Whether the OpenSSL library has built-in support for the *Server Name
@@ -561,6 +570,16 @@ SSL sockets also have the following additional methods and attributes:
    version of the SSL protocol that defines its use, and the number of secret
    bits being used.  If no connection has been established, returns ``None``.
 
+.. method:: SSLSocket.compression()
+
+   Return the compression algorithm being used as a string, or ``None``
+   if the connection isn't compressed.
+
+   If the higher-level protocol supports its own compression mechanism,
+   you can use :data:`OP_NO_COMPRESSION` to disable SSL-level compression.
+
+   .. versionadded:: 3.3
+
 .. method:: SSLSocket.get_channel_binding(cb_type="tls-unique")
 
    Get channel binding data for current connection, as a bytes object.  Returns
index d2441042a5ef6dbda03fd34db231cf6065e8e47a..0b2f743f227971197699cde7fa534a7775010c9a 100644 (file)
@@ -70,6 +70,10 @@ from _ssl import (
     OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1,
     OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_ECDH_USE,
     )
+try:
+    from _ssl import OP_NO_COMPRESSION
+except ImportError:
+    pass
 from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
 from _ssl import (
     SSL_ERROR_ZERO_RETURN,
@@ -330,6 +334,13 @@ class SSLSocket(socket):
         else:
             return self._sslobj.cipher()
 
+    def compression(self):
+        self._checkClosed()
+        if not self._sslobj:
+            return None
+        else:
+            return self._sslobj.compression()
+
     def send(self, data, flags=0):
         self._checkClosed()
         if self._sslobj:
index 86bc95052e709fe3c2136628bc34a17df98716ab..becbfabb8cc44c61d9ccdb89e0058897db95a1bf 100644 (file)
@@ -97,6 +97,7 @@ class StatsRequestHandler(BaseHTTPRequestHandler):
         stats = {
             'session_cache': context.session_stats(),
             'cipher': sock.cipher(),
+            'compression': sock.compression(),
             }
         body = pprint.pformat(stats)
         body = body.encode('utf-8')
index 505550f269fe1cc8790f49883211bc31a8aa097e..76fb3e777c923debf39bafa9f127d477d29e8041 100644 (file)
@@ -100,6 +100,8 @@ class BasicSocketTests(unittest.TestCase):
         ssl.CERT_REQUIRED
         ssl.OP_CIPHER_SERVER_PREFERENCE
         ssl.OP_SINGLE_ECDH_USE
+        if ssl.OPENSSL_VERSION_INFO >= (1, 0):
+            ssl.OP_NO_COMPRESSION
         self.assertIn(ssl.HAS_SNI, {True, False})
 
     def test_random(self):
@@ -1185,7 +1187,12 @@ else:
             if connectionchatty:
                 if support.verbose:
                     sys.stdout.write(" client:  closing connection.\n")
+            stats = {
+                'compression': s.compression(),
+                'cipher': s.cipher(),
+            }
             s.close()
+            return stats
         finally:
             server.stop()
             server.join()
@@ -1814,6 +1821,25 @@ else:
                 server.stop()
                 server.join()
 
+        def test_compression(self):
+            context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+            context.load_cert_chain(CERTFILE)
+            stats = server_params_test(context, context,
+                                       chatty=True, connectionchatty=True)
+            if support.verbose:
+                sys.stdout.write(" got compression: {!r}\n".format(stats['compression']))
+            self.assertIn(stats['compression'], { None, 'ZLIB', 'RLE' })
+
+        @unittest.skipUnless(hasattr(ssl, 'OP_NO_COMPRESSION'),
+                             "ssl.OP_NO_COMPRESSION needed for this test")
+        def test_compression_disabled(self):
+            context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+            context.load_cert_chain(CERTFILE)
+            stats = server_params_test(context, context,
+                                       chatty=True, connectionchatty=True)
+            self.assertIs(stats['compression'], None)
+
+
 def test_main(verbose=False):
     if support.verbose:
         plats = {
index 926184d47fe767ce1061344d07f553906d1856ef..7f7213345daaa8918d6ba6f28cad18b5999b8ed4 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -419,6 +419,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #13634: Add support for querying and disabling SSL compression.
+
 - Issue #13627: Add support for SSL Elliptic Curve-based Diffie-Hellman
   key exchange, through the SSLContext.set_ecdh_curve() method and the
   ssl.OP_SINGLE_ECDH_USE option.
index 725f1485b4c96899caeb45143d2997b1385c69cb..480543cbe1642dd10e4e4dd3761f89807f6d7f54 100644 (file)
@@ -999,6 +999,25 @@ static PyObject *PySSL_cipher (PySSLSocket *self) {
     return NULL;
 }
 
+static PyObject *PySSL_compression(PySSLSocket *self) {
+#ifdef OPENSSL_NO_COMP
+    Py_RETURN_NONE;
+#else
+    const COMP_METHOD *comp_method;
+    const char *short_name;
+
+    if (self->ssl == NULL)
+        Py_RETURN_NONE;
+    comp_method = SSL_get_current_compression(self->ssl);
+    if (comp_method == NULL || comp_method->type == NID_undef)
+        Py_RETURN_NONE;
+    short_name = OBJ_nid2sn(comp_method->type);
+    if (short_name == NULL)
+        Py_RETURN_NONE;
+    return PyUnicode_DecodeFSDefault(short_name);
+#endif
+}
+
 static void PySSL_dealloc(PySSLSocket *self)
 {
     if (self->peer_cert)        /* Possible not to have one? */
@@ -1452,6 +1471,7 @@ static PyMethodDef PySSLMethods[] = {
     {"peer_certificate", (PyCFunction)PySSL_peercert, METH_VARARGS,
      PySSL_peercert_doc},
     {"cipher", (PyCFunction)PySSL_cipher, METH_NOARGS},
+    {"compression", (PyCFunction)PySSL_compression, METH_NOARGS},
     {"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS,
      PySSL_SSLshutdown_doc},
 #if HAVE_OPENSSL_FINISHED
@@ -2482,6 +2502,10 @@ PyInit__ssl(void)
     PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
                             SSL_OP_CIPHER_SERVER_PREFERENCE);
     PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE);
+#ifdef SSL_OP_NO_COMPRESSION
+    PyModule_AddIntConstant(m, "OP_NO_COMPRESSION",
+                            SSL_OP_NO_COMPRESSION);
+#endif
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
     r = Py_True;