An interpreted, interactive, object-oriented programming language
CentOS Sources
2017-08-01 71084d584ff953f5463757ec6536406320560b4d
commit | author | age
f63228 1
CS 2 # HG changeset patch
3 # User Alex Gaynor <alex.gaynor@gmail.com>
4 # Date 1409862802 25200
5 # Node ID 16c86a6bdbe2a545dd2de02dc9f347c2b3ae7220
6 # Parent  f17ab9fed3b03191df975ecdde2cc07cee915319
7 Issue #20421: Add a .version() method to SSL sockets exposing the actual protocol version in use.
8
9 Backport from default.
10
11 diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
12 --- a/Doc/library/ssl.rst
13 +++ b/Doc/library/ssl.rst
14 @@ -867,10 +867,10 @@ SSL sockets also have the following addi
15  
16  .. method:: SSLSocket.selected_npn_protocol()
17  
18 -   Returns the protocol that was selected during the TLS/SSL handshake. If
19 -   :meth:`SSLContext.set_npn_protocols` was not called, or if the other party
20 -   does not support NPN, or if the handshake has not yet happened, this will
21 -   return ``None``.
22 +   Returns the higher-level protocol that was selected during the TLS/SSL
23 +   handshake. If :meth:`SSLContext.set_npn_protocols` was not called, or
24 +   if the other party does not support NPN, or if the handshake has not yet
25 +   happened, this will return ``None``.
26  
27     .. versionadded:: 2.7.9
28  
29 @@ -882,6 +882,16 @@ SSL sockets also have the following addi
30     returned socket should always be used for further communication with the
31     other side of the connection, rather than the original socket.
32  
33 +.. method:: SSLSocket.version()
34 +
35 +   Return the actual SSL protocol version negotiated by the connection
36 +   as a string, or ``None`` is no secure connection is established.
37 +   As of this writing, possible return values include ``"SSLv2"``,
38 +   ``"SSLv3"``, ``"TLSv1"``, ``"TLSv1.1"`` and ``"TLSv1.2"``.
39 +   Recent OpenSSL versions may define more return values.
40 +
41 +   .. versionadded:: 3.5
42 +
43  .. attribute:: SSLSocket.context
44  
45     The :class:`SSLContext` object this SSL socket is tied to.  If the SSL
46 diff --git a/Lib/ssl.py b/Lib/ssl.py
47 --- a/Lib/ssl.py
48 +++ b/Lib/ssl.py
49 @@ -862,6 +862,15 @@ class SSLSocket(socket):
50              return None
51          return self._sslobj.tls_unique_cb()
52  
53 +    def version(self):
54 +        """
55 +        Return a string identifying the protocol version used by the
56 +        current SSL channel, or None if there is no established channel.
57 +        """
58 +        if self._sslobj is None:
59 +            return None
60 +        return self._sslobj.version()
61 +
62  
63  def wrap_socket(sock, keyfile=None, certfile=None,
64                  server_side=False, cert_reqs=CERT_NONE,
65 diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
66 --- a/Lib/test/test_ssl.py
67 +++ b/Lib/test/test_ssl.py
68 @@ -1904,7 +1904,8 @@ else:
69                      'compression': s.compression(),
70                      'cipher': s.cipher(),
71                      'peercert': s.getpeercert(),
72 -                    'client_npn_protocol': s.selected_npn_protocol()
73 +                    'client_npn_protocol': s.selected_npn_protocol(),
74 +                    'version': s.version(),
75                  })
76                  s.close()
77              stats['server_npn_protocols'] = server.selected_protocols
78 @@ -1912,6 +1913,13 @@ else:
79  
80      def try_protocol_combo(server_protocol, client_protocol, expect_success,
81                             certsreqs=None, server_options=0, client_options=0):
82 +        """
83 +        Try to SSL-connect using *client_protocol* to *server_protocol*.
84 +        If *expect_success* is true, assert that the connection succeeds,
85 +        if it's false, assert that the connection fails.
86 +        Also, if *expect_success* is a string, assert that it is the protocol
87 +        version actually used by the connection.
88 +        """
89          if certsreqs is None:
90              certsreqs = ssl.CERT_NONE
91          certtype = {
92 @@ -1941,8 +1949,8 @@ else:
93              ctx.load_cert_chain(CERTFILE)
94              ctx.load_verify_locations(CERTFILE)
95          try:
96 -            server_params_test(client_context, server_context,
97 -                               chatty=False, connectionchatty=False)
98 +            stats = server_params_test(client_context, server_context,
99 +                                       chatty=False, connectionchatty=False)
100          # Protocol mismatch can result in either an SSLError, or a
101          # "Connection reset by peer" error.
102          except ssl.SSLError:
103 @@ -1957,6 +1965,10 @@ else:
104                      "Client protocol %s succeeded with server protocol %s!"
105                      % (ssl.get_protocol_name(client_protocol),
106                         ssl.get_protocol_name(server_protocol)))
107 +            elif (expect_success is not True
108 +                  and expect_success != stats['version']):
109 +                raise AssertionError("version mismatch: expected %r, got %r"
110 +                                     % (expect_success, stats['version']))
111  
112  
113      class ThreadedTests(unittest.TestCase):
114 @@ -2186,17 +2198,17 @@ else:
115                          sys.stdout.write(
116                              " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"
117                              % str(x))
118 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)
119 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, 'SSLv3')
120              try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)
121 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)
122 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1')
123  
124 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
125 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_OPTIONAL)
126              try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)
127 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
128 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_OPTIONAL)
129  
130 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
131 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_REQUIRED)
132              try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
133 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
134 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_REQUIRED)
135  
136              # Server with specific SSL options
137              try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False,
138 @@ -2213,9 +2225,9 @@ else:
139              """Connecting to an SSLv3 server with various client options"""
140              if support.verbose:
141                  sys.stdout.write("\n")
142 -            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
143 -            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
144 -            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
145 +            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3')
146 +            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_OPTIONAL)
147 +            try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_REQUIRED)
148              if hasattr(ssl, 'PROTOCOL_SSLv2'):
149                  try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
150              try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False,
151 @@ -2223,7 +2235,7 @@ else:
152              try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
153              if no_sslv2_implies_sslv3_hello():
154                  # No SSLv2 => client will use an SSLv3 hello on recent OpenSSLs
155 -                try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, True,
156 +                try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, 'SSLv3',
157                                     client_options=ssl.OP_NO_SSLv2)
158  
159          @skip_if_broken_ubuntu_ssl
160 @@ -2231,9 +2243,9 @@ else:
161              """Connecting to a TLSv1 server with various client options"""
162              if support.verbose:
163                  sys.stdout.write("\n")
164 -            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
165 -            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
166 -            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
167 +            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1')
168 +            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_OPTIONAL)
169 +            try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_REQUIRED)
170              if hasattr(ssl, 'PROTOCOL_SSLv2'):
171                  try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
172              try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
173 @@ -2248,14 +2260,14 @@ else:
174                 Testing against older TLS versions."""
175              if support.verbose:
176                  sys.stdout.write("\n")
177 -            try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, True)
178 +            try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, 'TLSv1.1')
179              if hasattr(ssl, 'PROTOCOL_SSLv2'):
180                  try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv2, False)
181              try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv3, False)
182              try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv23, False,
183                                 client_options=ssl.OP_NO_TLSv1_1)
184  
185 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_1, True)
186 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_1, 'TLSv1.1')
187              try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1, False)
188              try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, False)
189  
190 @@ -2268,7 +2280,7 @@ else:
191                 Testing against older TLS versions."""
192              if support.verbose:
193                  sys.stdout.write("\n")
194 -            try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, True,
195 +            try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2',
196                                 server_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2,
197                                 client_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2,)
198              if hasattr(ssl, 'PROTOCOL_SSLv2'):
199 @@ -2277,7 +2289,7 @@ else:
200              try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv23, False,
201                                 client_options=ssl.OP_NO_TLSv1_2)
202  
203 -            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_2, True)
204 +            try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2')
205              try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False)
206              try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False)
207              try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False)
208 @@ -2619,6 +2631,21 @@ else:
209                          s.connect((HOST, server.port))
210              self.assertIn("no shared cipher", str(server.conn_errors[0]))
211  
212 +        def test_version_basic(self):
213 +            """
214 +            Basic tests for SSLSocket.version().
215 +            More tests are done in the test_protocol_*() methods.
216 +            """
217 +            context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
218 +            with ThreadedEchoServer(CERTFILE,
219 +                                    ssl_version=ssl.PROTOCOL_TLSv1,
220 +                                    chatty=False) as server:
221 +                with closing(context.wrap_socket(socket.socket())) as s:
222 +                    self.assertIs(s.version(), None)
223 +                    s.connect((HOST, server.port))
224 +                    self.assertEqual(s.version(), "TLSv1")
225 +                self.assertIs(s.version(), None)
226 +
227          @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
228          def test_default_ecdh_curve(self):
229              # Issue #21015: elliptic curve-based Diffie Hellman key exchange
230 diff --git a/Modules/_ssl.c b/Modules/_ssl.c
231 --- a/Modules/_ssl.c
232 +++ b/Modules/_ssl.c
233 @@ -1384,6 +1384,18 @@ static PyObject *PySSL_cipher (PySSLSock
234      return NULL;
235  }
236  
237 +static PyObject *PySSL_version(PySSLSocket *self)
238 +{
239 +    const char *version;
240 +
241 +    if (self->ssl == NULL)
242 +        Py_RETURN_NONE;
243 +    version = SSL_get_version(self->ssl);
244 +    if (!strcmp(version, "unknown"))
245 +        Py_RETURN_NONE;
246 +    return PyUnicode_FromString(version);
247 +}
248 +
249  #ifdef OPENSSL_NPN_NEGOTIATED
250  static PyObject *PySSL_selected_npn_protocol(PySSLSocket *self) {
251      const unsigned char *out;
252 @@ -1907,6 +1919,7 @@ static PyMethodDef PySSLMethods[] = {
253      {"peer_certificate", (PyCFunction)PySSL_peercert, METH_VARARGS,
254       PySSL_peercert_doc},
255      {"cipher", (PyCFunction)PySSL_cipher, METH_NOARGS},
256 +    {"version", (PyCFunction)PySSL_version, METH_NOARGS},
257  #ifdef OPENSSL_NPN_NEGOTIATED
258      {"selected_npn_protocol", (PyCFunction)PySSL_selected_npn_protocol, METH_NOARGS},
259  #endif
260