]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | # Test the support for SSL and sockets\r |
2 | \r | |
3 | import sys\r | |
4 | import unittest\r | |
5 | from test import test_support\r | |
6 | import asyncore\r | |
7 | import socket\r | |
8 | import select\r | |
9 | import time\r | |
10 | import gc\r | |
11 | import os\r | |
12 | import errno\r | |
13 | import pprint\r | |
14 | import urllib, urlparse\r | |
15 | import traceback\r | |
16 | import weakref\r | |
17 | import functools\r | |
18 | import platform\r | |
19 | \r | |
20 | from BaseHTTPServer import HTTPServer\r | |
21 | from SimpleHTTPServer import SimpleHTTPRequestHandler\r | |
22 | \r | |
23 | ssl = test_support.import_module("ssl")\r | |
24 | \r | |
25 | HOST = test_support.HOST\r | |
26 | CERTFILE = None\r | |
27 | SVN_PYTHON_ORG_ROOT_CERT = None\r | |
28 | \r | |
29 | def handle_error(prefix):\r | |
30 | exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))\r | |
31 | if test_support.verbose:\r | |
32 | sys.stdout.write(prefix + exc_format)\r | |
33 | \r | |
34 | \r | |
35 | class BasicTests(unittest.TestCase):\r | |
36 | \r | |
37 | def test_sslwrap_simple(self):\r | |
38 | # A crude test for the legacy API\r | |
39 | try:\r | |
40 | ssl.sslwrap_simple(socket.socket(socket.AF_INET))\r | |
41 | except IOError, e:\r | |
42 | if e.errno == 32: # broken pipe when ssl_sock.do_handshake(), this test doesn't care about that\r | |
43 | pass\r | |
44 | else:\r | |
45 | raise\r | |
46 | try:\r | |
47 | ssl.sslwrap_simple(socket.socket(socket.AF_INET)._sock)\r | |
48 | except IOError, e:\r | |
49 | if e.errno == 32: # broken pipe when ssl_sock.do_handshake(), this test doesn't care about that\r | |
50 | pass\r | |
51 | else:\r | |
52 | raise\r | |
53 | \r | |
54 | # Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2\r | |
55 | def skip_if_broken_ubuntu_ssl(func):\r | |
56 | if hasattr(ssl, 'PROTOCOL_SSLv2'):\r | |
57 | # We need to access the lower-level wrapper in order to create an\r | |
58 | # implicit SSL context without trying to connect or listen.\r | |
59 | try:\r | |
60 | import _ssl\r | |
61 | except ImportError:\r | |
62 | # The returned function won't get executed, just ignore the error\r | |
63 | pass\r | |
64 | @functools.wraps(func)\r | |
65 | def f(*args, **kwargs):\r | |
66 | try:\r | |
67 | s = socket.socket(socket.AF_INET)\r | |
68 | _ssl.sslwrap(s._sock, 0, None, None,\r | |
69 | ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None)\r | |
70 | except ssl.SSLError as e:\r | |
71 | if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and\r | |
72 | platform.linux_distribution() == ('debian', 'squeeze/sid', '')\r | |
73 | and 'Invalid SSL protocol variant specified' in str(e)):\r | |
74 | raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour")\r | |
75 | return func(*args, **kwargs)\r | |
76 | return f\r | |
77 | else:\r | |
78 | return func\r | |
79 | \r | |
80 | \r | |
81 | class BasicSocketTests(unittest.TestCase):\r | |
82 | \r | |
83 | def test_constants(self):\r | |
84 | #ssl.PROTOCOL_SSLv2\r | |
85 | ssl.PROTOCOL_SSLv23\r | |
86 | ssl.PROTOCOL_SSLv3\r | |
87 | ssl.PROTOCOL_TLSv1\r | |
88 | ssl.CERT_NONE\r | |
89 | ssl.CERT_OPTIONAL\r | |
90 | ssl.CERT_REQUIRED\r | |
91 | \r | |
92 | def test_random(self):\r | |
93 | v = ssl.RAND_status()\r | |
94 | if test_support.verbose:\r | |
95 | sys.stdout.write("\n RAND_status is %d (%s)\n"\r | |
96 | % (v, (v and "sufficient randomness") or\r | |
97 | "insufficient randomness"))\r | |
98 | try:\r | |
99 | ssl.RAND_egd(1)\r | |
100 | except TypeError:\r | |
101 | pass\r | |
102 | else:\r | |
103 | print "didn't raise TypeError"\r | |
104 | ssl.RAND_add("this is a random string", 75.0)\r | |
105 | \r | |
106 | def test_parse_cert(self):\r | |
107 | # note that this uses an 'unofficial' function in _ssl.c,\r | |
108 | # provided solely for this test, to exercise the certificate\r | |
109 | # parsing code\r | |
110 | p = ssl._ssl._test_decode_cert(CERTFILE, False)\r | |
111 | if test_support.verbose:\r | |
112 | sys.stdout.write("\n" + pprint.pformat(p) + "\n")\r | |
113 | \r | |
114 | def test_DER_to_PEM(self):\r | |
115 | with open(SVN_PYTHON_ORG_ROOT_CERT, 'r') as f:\r | |
116 | pem = f.read()\r | |
117 | d1 = ssl.PEM_cert_to_DER_cert(pem)\r | |
118 | p2 = ssl.DER_cert_to_PEM_cert(d1)\r | |
119 | d2 = ssl.PEM_cert_to_DER_cert(p2)\r | |
120 | self.assertEqual(d1, d2)\r | |
121 | if not p2.startswith(ssl.PEM_HEADER + '\n'):\r | |
122 | self.fail("DER-to-PEM didn't include correct header:\n%r\n" % p2)\r | |
123 | if not p2.endswith('\n' + ssl.PEM_FOOTER + '\n'):\r | |
124 | self.fail("DER-to-PEM didn't include correct footer:\n%r\n" % p2)\r | |
125 | \r | |
126 | def test_openssl_version(self):\r | |
127 | n = ssl.OPENSSL_VERSION_NUMBER\r | |
128 | t = ssl.OPENSSL_VERSION_INFO\r | |
129 | s = ssl.OPENSSL_VERSION\r | |
130 | self.assertIsInstance(n, (int, long))\r | |
131 | self.assertIsInstance(t, tuple)\r | |
132 | self.assertIsInstance(s, str)\r | |
133 | # Some sanity checks follow\r | |
134 | # >= 0.9\r | |
135 | self.assertGreaterEqual(n, 0x900000)\r | |
136 | # < 2.0\r | |
137 | self.assertLess(n, 0x20000000)\r | |
138 | major, minor, fix, patch, status = t\r | |
139 | self.assertGreaterEqual(major, 0)\r | |
140 | self.assertLess(major, 2)\r | |
141 | self.assertGreaterEqual(minor, 0)\r | |
142 | self.assertLess(minor, 256)\r | |
143 | self.assertGreaterEqual(fix, 0)\r | |
144 | self.assertLess(fix, 256)\r | |
145 | self.assertGreaterEqual(patch, 0)\r | |
146 | self.assertLessEqual(patch, 26)\r | |
147 | self.assertGreaterEqual(status, 0)\r | |
148 | self.assertLessEqual(status, 15)\r | |
149 | # Version string as returned by OpenSSL, the format might change\r | |
150 | self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)),\r | |
151 | (s, t))\r | |
152 | \r | |
153 | def test_ciphers(self):\r | |
154 | if not test_support.is_resource_enabled('network'):\r | |
155 | return\r | |
156 | remote = ("svn.python.org", 443)\r | |
157 | with test_support.transient_internet(remote[0]):\r | |
158 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
159 | cert_reqs=ssl.CERT_NONE, ciphers="ALL")\r | |
160 | s.connect(remote)\r | |
161 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
162 | cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT")\r | |
163 | s.connect(remote)\r | |
164 | # Error checking occurs when connecting, because the SSL context\r | |
165 | # isn't created before.\r | |
166 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
167 | cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx")\r | |
168 | with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):\r | |
169 | s.connect(remote)\r | |
170 | \r | |
171 | @test_support.cpython_only\r | |
172 | def test_refcycle(self):\r | |
173 | # Issue #7943: an SSL object doesn't create reference cycles with\r | |
174 | # itself.\r | |
175 | s = socket.socket(socket.AF_INET)\r | |
176 | ss = ssl.wrap_socket(s)\r | |
177 | wr = weakref.ref(ss)\r | |
178 | del ss\r | |
179 | self.assertEqual(wr(), None)\r | |
180 | \r | |
181 | def test_wrapped_unconnected(self):\r | |
182 | # The _delegate_methods in socket.py are correctly delegated to by an\r | |
183 | # unconnected SSLSocket, so they will raise a socket.error rather than\r | |
184 | # something unexpected like TypeError.\r | |
185 | s = socket.socket(socket.AF_INET)\r | |
186 | ss = ssl.wrap_socket(s)\r | |
187 | self.assertRaises(socket.error, ss.recv, 1)\r | |
188 | self.assertRaises(socket.error, ss.recv_into, bytearray(b'x'))\r | |
189 | self.assertRaises(socket.error, ss.recvfrom, 1)\r | |
190 | self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1)\r | |
191 | self.assertRaises(socket.error, ss.send, b'x')\r | |
192 | self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0))\r | |
193 | \r | |
194 | \r | |
195 | class NetworkedTests(unittest.TestCase):\r | |
196 | \r | |
197 | def test_connect(self):\r | |
198 | with test_support.transient_internet("svn.python.org"):\r | |
199 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
200 | cert_reqs=ssl.CERT_NONE)\r | |
201 | s.connect(("svn.python.org", 443))\r | |
202 | c = s.getpeercert()\r | |
203 | if c:\r | |
204 | self.fail("Peer cert %s shouldn't be here!")\r | |
205 | s.close()\r | |
206 | \r | |
207 | # this should fail because we have no verification certs\r | |
208 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
209 | cert_reqs=ssl.CERT_REQUIRED)\r | |
210 | try:\r | |
211 | s.connect(("svn.python.org", 443))\r | |
212 | except ssl.SSLError:\r | |
213 | pass\r | |
214 | finally:\r | |
215 | s.close()\r | |
216 | \r | |
217 | # this should succeed because we specify the root cert\r | |
218 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
219 | cert_reqs=ssl.CERT_REQUIRED,\r | |
220 | ca_certs=SVN_PYTHON_ORG_ROOT_CERT)\r | |
221 | try:\r | |
222 | s.connect(("svn.python.org", 443))\r | |
223 | finally:\r | |
224 | s.close()\r | |
225 | \r | |
226 | def test_connect_ex(self):\r | |
227 | # Issue #11326: check connect_ex() implementation\r | |
228 | with test_support.transient_internet("svn.python.org"):\r | |
229 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
230 | cert_reqs=ssl.CERT_REQUIRED,\r | |
231 | ca_certs=SVN_PYTHON_ORG_ROOT_CERT)\r | |
232 | try:\r | |
233 | self.assertEqual(0, s.connect_ex(("svn.python.org", 443)))\r | |
234 | self.assertTrue(s.getpeercert())\r | |
235 | finally:\r | |
236 | s.close()\r | |
237 | \r | |
238 | def test_non_blocking_connect_ex(self):\r | |
239 | # Issue #11326: non-blocking connect_ex() should allow handshake\r | |
240 | # to proceed after the socket gets ready.\r | |
241 | with test_support.transient_internet("svn.python.org"):\r | |
242 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
243 | cert_reqs=ssl.CERT_REQUIRED,\r | |
244 | ca_certs=SVN_PYTHON_ORG_ROOT_CERT,\r | |
245 | do_handshake_on_connect=False)\r | |
246 | try:\r | |
247 | s.setblocking(False)\r | |
248 | rc = s.connect_ex(('svn.python.org', 443))\r | |
249 | # EWOULDBLOCK under Windows, EINPROGRESS elsewhere\r | |
250 | self.assertIn(rc, (0, errno.EINPROGRESS, errno.EWOULDBLOCK))\r | |
251 | # Wait for connect to finish\r | |
252 | select.select([], [s], [], 5.0)\r | |
253 | # Non-blocking handshake\r | |
254 | while True:\r | |
255 | try:\r | |
256 | s.do_handshake()\r | |
257 | break\r | |
258 | except ssl.SSLError as err:\r | |
259 | if err.args[0] == ssl.SSL_ERROR_WANT_READ:\r | |
260 | select.select([s], [], [], 5.0)\r | |
261 | elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:\r | |
262 | select.select([], [s], [], 5.0)\r | |
263 | else:\r | |
264 | raise\r | |
265 | # SSL established\r | |
266 | self.assertTrue(s.getpeercert())\r | |
267 | finally:\r | |
268 | s.close()\r | |
269 | \r | |
270 | @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows")\r | |
271 | def test_makefile_close(self):\r | |
272 | # Issue #5238: creating a file-like object with makefile() shouldn't\r | |
273 | # delay closing the underlying "real socket" (here tested with its\r | |
274 | # file descriptor, hence skipping the test under Windows).\r | |
275 | with test_support.transient_internet("svn.python.org"):\r | |
276 | ss = ssl.wrap_socket(socket.socket(socket.AF_INET))\r | |
277 | ss.connect(("svn.python.org", 443))\r | |
278 | fd = ss.fileno()\r | |
279 | f = ss.makefile()\r | |
280 | f.close()\r | |
281 | # The fd is still open\r | |
282 | os.read(fd, 0)\r | |
283 | # Closing the SSL socket should close the fd too\r | |
284 | ss.close()\r | |
285 | gc.collect()\r | |
286 | with self.assertRaises(OSError) as e:\r | |
287 | os.read(fd, 0)\r | |
288 | self.assertEqual(e.exception.errno, errno.EBADF)\r | |
289 | \r | |
290 | def test_non_blocking_handshake(self):\r | |
291 | with test_support.transient_internet("svn.python.org"):\r | |
292 | s = socket.socket(socket.AF_INET)\r | |
293 | s.connect(("svn.python.org", 443))\r | |
294 | s.setblocking(False)\r | |
295 | s = ssl.wrap_socket(s,\r | |
296 | cert_reqs=ssl.CERT_NONE,\r | |
297 | do_handshake_on_connect=False)\r | |
298 | count = 0\r | |
299 | while True:\r | |
300 | try:\r | |
301 | count += 1\r | |
302 | s.do_handshake()\r | |
303 | break\r | |
304 | except ssl.SSLError, err:\r | |
305 | if err.args[0] == ssl.SSL_ERROR_WANT_READ:\r | |
306 | select.select([s], [], [])\r | |
307 | elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:\r | |
308 | select.select([], [s], [])\r | |
309 | else:\r | |
310 | raise\r | |
311 | s.close()\r | |
312 | if test_support.verbose:\r | |
313 | sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)\r | |
314 | \r | |
315 | def test_get_server_certificate(self):\r | |
316 | with test_support.transient_internet("svn.python.org"):\r | |
317 | pem = ssl.get_server_certificate(("svn.python.org", 443))\r | |
318 | if not pem:\r | |
319 | self.fail("No server certificate on svn.python.org:443!")\r | |
320 | \r | |
321 | try:\r | |
322 | pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE)\r | |
323 | except ssl.SSLError:\r | |
324 | #should fail\r | |
325 | pass\r | |
326 | else:\r | |
327 | self.fail("Got server certificate %s for svn.python.org!" % pem)\r | |
328 | \r | |
329 | pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT)\r | |
330 | if not pem:\r | |
331 | self.fail("No server certificate on svn.python.org:443!")\r | |
332 | if test_support.verbose:\r | |
333 | sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem)\r | |
334 | \r | |
335 | def test_algorithms(self):\r | |
336 | # Issue #8484: all algorithms should be available when verifying a\r | |
337 | # certificate.\r | |
338 | # SHA256 was added in OpenSSL 0.9.8\r | |
339 | if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15):\r | |
340 | self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION)\r | |
341 | # NOTE: https://sha256.tbs-internet.com is another possible test host\r | |
342 | remote = ("sha256.tbs-internet.com", 443)\r | |
343 | sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem")\r | |
344 | with test_support.transient_internet("sha256.tbs-internet.com"):\r | |
345 | s = ssl.wrap_socket(socket.socket(socket.AF_INET),\r | |
346 | cert_reqs=ssl.CERT_REQUIRED,\r | |
347 | ca_certs=sha256_cert,)\r | |
348 | try:\r | |
349 | s.connect(remote)\r | |
350 | if test_support.verbose:\r | |
351 | sys.stdout.write("\nCipher with %r is %r\n" %\r | |
352 | (remote, s.cipher()))\r | |
353 | sys.stdout.write("Certificate is:\n%s\n" %\r | |
354 | pprint.pformat(s.getpeercert()))\r | |
355 | finally:\r | |
356 | s.close()\r | |
357 | \r | |
358 | \r | |
359 | try:\r | |
360 | import threading\r | |
361 | except ImportError:\r | |
362 | _have_threads = False\r | |
363 | else:\r | |
364 | _have_threads = True\r | |
365 | \r | |
366 | class ThreadedEchoServer(threading.Thread):\r | |
367 | \r | |
368 | class ConnectionHandler(threading.Thread):\r | |
369 | \r | |
370 | """A mildly complicated class, because we want it to work both\r | |
371 | with and without the SSL wrapper around the socket connection, so\r | |
372 | that we can test the STARTTLS functionality."""\r | |
373 | \r | |
374 | def __init__(self, server, connsock):\r | |
375 | self.server = server\r | |
376 | self.running = False\r | |
377 | self.sock = connsock\r | |
378 | self.sock.setblocking(1)\r | |
379 | self.sslconn = None\r | |
380 | threading.Thread.__init__(self)\r | |
381 | self.daemon = True\r | |
382 | \r | |
383 | def show_conn_details(self):\r | |
384 | if self.server.certreqs == ssl.CERT_REQUIRED:\r | |
385 | cert = self.sslconn.getpeercert()\r | |
386 | if test_support.verbose and self.server.chatty:\r | |
387 | sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")\r | |
388 | cert_binary = self.sslconn.getpeercert(True)\r | |
389 | if test_support.verbose and self.server.chatty:\r | |
390 | sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n")\r | |
391 | cipher = self.sslconn.cipher()\r | |
392 | if test_support.verbose and self.server.chatty:\r | |
393 | sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n")\r | |
394 | \r | |
395 | def wrap_conn(self):\r | |
396 | try:\r | |
397 | self.sslconn = ssl.wrap_socket(self.sock, server_side=True,\r | |
398 | certfile=self.server.certificate,\r | |
399 | ssl_version=self.server.protocol,\r | |
400 | ca_certs=self.server.cacerts,\r | |
401 | cert_reqs=self.server.certreqs,\r | |
402 | ciphers=self.server.ciphers)\r | |
403 | except ssl.SSLError:\r | |
404 | # XXX Various errors can have happened here, for example\r | |
405 | # a mismatching protocol version, an invalid certificate,\r | |
406 | # or a low-level bug. This should be made more discriminating.\r | |
407 | if self.server.chatty:\r | |
408 | handle_error("\n server: bad connection attempt from " +\r | |
409 | str(self.sock.getpeername()) + ":\n")\r | |
410 | self.close()\r | |
411 | self.running = False\r | |
412 | self.server.stop()\r | |
413 | return False\r | |
414 | else:\r | |
415 | return True\r | |
416 | \r | |
417 | def read(self):\r | |
418 | if self.sslconn:\r | |
419 | return self.sslconn.read()\r | |
420 | else:\r | |
421 | return self.sock.recv(1024)\r | |
422 | \r | |
423 | def write(self, bytes):\r | |
424 | if self.sslconn:\r | |
425 | return self.sslconn.write(bytes)\r | |
426 | else:\r | |
427 | return self.sock.send(bytes)\r | |
428 | \r | |
429 | def close(self):\r | |
430 | if self.sslconn:\r | |
431 | self.sslconn.close()\r | |
432 | else:\r | |
433 | self.sock._sock.close()\r | |
434 | \r | |
435 | def run(self):\r | |
436 | self.running = True\r | |
437 | if not self.server.starttls_server:\r | |
438 | if isinstance(self.sock, ssl.SSLSocket):\r | |
439 | self.sslconn = self.sock\r | |
440 | elif not self.wrap_conn():\r | |
441 | return\r | |
442 | self.show_conn_details()\r | |
443 | while self.running:\r | |
444 | try:\r | |
445 | msg = self.read()\r | |
446 | if not msg:\r | |
447 | # eof, so quit this handler\r | |
448 | self.running = False\r | |
449 | self.close()\r | |
450 | elif msg.strip() == 'over':\r | |
451 | if test_support.verbose and self.server.connectionchatty:\r | |
452 | sys.stdout.write(" server: client closed connection\n")\r | |
453 | self.close()\r | |
454 | return\r | |
455 | elif self.server.starttls_server and msg.strip() == 'STARTTLS':\r | |
456 | if test_support.verbose and self.server.connectionchatty:\r | |
457 | sys.stdout.write(" server: read STARTTLS from client, sending OK...\n")\r | |
458 | self.write("OK\n")\r | |
459 | if not self.wrap_conn():\r | |
460 | return\r | |
461 | elif self.server.starttls_server and self.sslconn and msg.strip() == 'ENDTLS':\r | |
462 | if test_support.verbose and self.server.connectionchatty:\r | |
463 | sys.stdout.write(" server: read ENDTLS from client, sending OK...\n")\r | |
464 | self.write("OK\n")\r | |
465 | self.sslconn.unwrap()\r | |
466 | self.sslconn = None\r | |
467 | if test_support.verbose and self.server.connectionchatty:\r | |
468 | sys.stdout.write(" server: connection is now unencrypted...\n")\r | |
469 | else:\r | |
470 | if (test_support.verbose and\r | |
471 | self.server.connectionchatty):\r | |
472 | ctype = (self.sslconn and "encrypted") or "unencrypted"\r | |
473 | sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n"\r | |
474 | % (repr(msg), ctype, repr(msg.lower()), ctype))\r | |
475 | self.write(msg.lower())\r | |
476 | except ssl.SSLError:\r | |
477 | if self.server.chatty:\r | |
478 | handle_error("Test server failure:\n")\r | |
479 | self.close()\r | |
480 | self.running = False\r | |
481 | # normally, we'd just stop here, but for the test\r | |
482 | # harness, we want to stop the server\r | |
483 | self.server.stop()\r | |
484 | \r | |
485 | def __init__(self, certificate, ssl_version=None,\r | |
486 | certreqs=None, cacerts=None,\r | |
487 | chatty=True, connectionchatty=False, starttls_server=False,\r | |
488 | wrap_accepting_socket=False, ciphers=None):\r | |
489 | \r | |
490 | if ssl_version is None:\r | |
491 | ssl_version = ssl.PROTOCOL_TLSv1\r | |
492 | if certreqs is None:\r | |
493 | certreqs = ssl.CERT_NONE\r | |
494 | self.certificate = certificate\r | |
495 | self.protocol = ssl_version\r | |
496 | self.certreqs = certreqs\r | |
497 | self.cacerts = cacerts\r | |
498 | self.ciphers = ciphers\r | |
499 | self.chatty = chatty\r | |
500 | self.connectionchatty = connectionchatty\r | |
501 | self.starttls_server = starttls_server\r | |
502 | self.sock = socket.socket()\r | |
503 | self.flag = None\r | |
504 | if wrap_accepting_socket:\r | |
505 | self.sock = ssl.wrap_socket(self.sock, server_side=True,\r | |
506 | certfile=self.certificate,\r | |
507 | cert_reqs = self.certreqs,\r | |
508 | ca_certs = self.cacerts,\r | |
509 | ssl_version = self.protocol,\r | |
510 | ciphers = self.ciphers)\r | |
511 | if test_support.verbose and self.chatty:\r | |
512 | sys.stdout.write(' server: wrapped server socket as %s\n' % str(self.sock))\r | |
513 | self.port = test_support.bind_port(self.sock)\r | |
514 | self.active = False\r | |
515 | threading.Thread.__init__(self)\r | |
516 | self.daemon = True\r | |
517 | \r | |
518 | def start(self, flag=None):\r | |
519 | self.flag = flag\r | |
520 | threading.Thread.start(self)\r | |
521 | \r | |
522 | def run(self):\r | |
523 | self.sock.settimeout(0.05)\r | |
524 | self.sock.listen(5)\r | |
525 | self.active = True\r | |
526 | if self.flag:\r | |
527 | # signal an event\r | |
528 | self.flag.set()\r | |
529 | while self.active:\r | |
530 | try:\r | |
531 | newconn, connaddr = self.sock.accept()\r | |
532 | if test_support.verbose and self.chatty:\r | |
533 | sys.stdout.write(' server: new connection from '\r | |
534 | + str(connaddr) + '\n')\r | |
535 | handler = self.ConnectionHandler(self, newconn)\r | |
536 | handler.start()\r | |
537 | except socket.timeout:\r | |
538 | pass\r | |
539 | except KeyboardInterrupt:\r | |
540 | self.stop()\r | |
541 | self.sock.close()\r | |
542 | \r | |
543 | def stop(self):\r | |
544 | self.active = False\r | |
545 | \r | |
546 | class AsyncoreEchoServer(threading.Thread):\r | |
547 | \r | |
548 | class EchoServer(asyncore.dispatcher):\r | |
549 | \r | |
550 | class ConnectionHandler(asyncore.dispatcher_with_send):\r | |
551 | \r | |
552 | def __init__(self, conn, certfile):\r | |
553 | asyncore.dispatcher_with_send.__init__(self, conn)\r | |
554 | self.socket = ssl.wrap_socket(conn, server_side=True,\r | |
555 | certfile=certfile,\r | |
556 | do_handshake_on_connect=False)\r | |
557 | self._ssl_accepting = True\r | |
558 | \r | |
559 | def readable(self):\r | |
560 | if isinstance(self.socket, ssl.SSLSocket):\r | |
561 | while self.socket.pending() > 0:\r | |
562 | self.handle_read_event()\r | |
563 | return True\r | |
564 | \r | |
565 | def _do_ssl_handshake(self):\r | |
566 | try:\r | |
567 | self.socket.do_handshake()\r | |
568 | except ssl.SSLError, err:\r | |
569 | if err.args[0] in (ssl.SSL_ERROR_WANT_READ,\r | |
570 | ssl.SSL_ERROR_WANT_WRITE):\r | |
571 | return\r | |
572 | elif err.args[0] == ssl.SSL_ERROR_EOF:\r | |
573 | return self.handle_close()\r | |
574 | raise\r | |
575 | except socket.error, err:\r | |
576 | if err.args[0] == errno.ECONNABORTED:\r | |
577 | return self.handle_close()\r | |
578 | else:\r | |
579 | self._ssl_accepting = False\r | |
580 | \r | |
581 | def handle_read(self):\r | |
582 | if self._ssl_accepting:\r | |
583 | self._do_ssl_handshake()\r | |
584 | else:\r | |
585 | data = self.recv(1024)\r | |
586 | if data and data.strip() != 'over':\r | |
587 | self.send(data.lower())\r | |
588 | \r | |
589 | def handle_close(self):\r | |
590 | self.close()\r | |
591 | if test_support.verbose:\r | |
592 | sys.stdout.write(" server: closed connection %s\n" % self.socket)\r | |
593 | \r | |
594 | def handle_error(self):\r | |
595 | raise\r | |
596 | \r | |
597 | def __init__(self, certfile):\r | |
598 | self.certfile = certfile\r | |
599 | asyncore.dispatcher.__init__(self)\r | |
600 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM)\r | |
601 | self.port = test_support.bind_port(self.socket)\r | |
602 | self.listen(5)\r | |
603 | \r | |
604 | def handle_accept(self):\r | |
605 | sock_obj, addr = self.accept()\r | |
606 | if test_support.verbose:\r | |
607 | sys.stdout.write(" server: new connection from %s:%s\n" %addr)\r | |
608 | self.ConnectionHandler(sock_obj, self.certfile)\r | |
609 | \r | |
610 | def handle_error(self):\r | |
611 | raise\r | |
612 | \r | |
613 | def __init__(self, certfile):\r | |
614 | self.flag = None\r | |
615 | self.active = False\r | |
616 | self.server = self.EchoServer(certfile)\r | |
617 | self.port = self.server.port\r | |
618 | threading.Thread.__init__(self)\r | |
619 | self.daemon = True\r | |
620 | \r | |
621 | def __str__(self):\r | |
622 | return "<%s %s>" % (self.__class__.__name__, self.server)\r | |
623 | \r | |
624 | def start(self, flag=None):\r | |
625 | self.flag = flag\r | |
626 | threading.Thread.start(self)\r | |
627 | \r | |
628 | def run(self):\r | |
629 | self.active = True\r | |
630 | if self.flag:\r | |
631 | self.flag.set()\r | |
632 | while self.active:\r | |
633 | asyncore.loop(0.05)\r | |
634 | \r | |
635 | def stop(self):\r | |
636 | self.active = False\r | |
637 | self.server.close()\r | |
638 | \r | |
639 | class SocketServerHTTPSServer(threading.Thread):\r | |
640 | \r | |
641 | class HTTPSServer(HTTPServer):\r | |
642 | \r | |
643 | def __init__(self, server_address, RequestHandlerClass, certfile):\r | |
644 | HTTPServer.__init__(self, server_address, RequestHandlerClass)\r | |
645 | # we assume the certfile contains both private key and certificate\r | |
646 | self.certfile = certfile\r | |
647 | self.allow_reuse_address = True\r | |
648 | \r | |
649 | def __str__(self):\r | |
650 | return ('<%s %s:%s>' %\r | |
651 | (self.__class__.__name__,\r | |
652 | self.server_name,\r | |
653 | self.server_port))\r | |
654 | \r | |
655 | def get_request(self):\r | |
656 | # override this to wrap socket with SSL\r | |
657 | sock, addr = self.socket.accept()\r | |
658 | sslconn = ssl.wrap_socket(sock, server_side=True,\r | |
659 | certfile=self.certfile)\r | |
660 | return sslconn, addr\r | |
661 | \r | |
662 | class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):\r | |
663 | # need to override translate_path to get a known root,\r | |
664 | # instead of using os.curdir, since the test could be\r | |
665 | # run from anywhere\r | |
666 | \r | |
667 | server_version = "TestHTTPS/1.0"\r | |
668 | \r | |
669 | root = None\r | |
670 | \r | |
671 | def translate_path(self, path):\r | |
672 | """Translate a /-separated PATH to the local filename syntax.\r | |
673 | \r | |
674 | Components that mean special things to the local file system\r | |
675 | (e.g. drive or directory names) are ignored. (XXX They should\r | |
676 | probably be diagnosed.)\r | |
677 | \r | |
678 | """\r | |
679 | # abandon query parameters\r | |
680 | path = urlparse.urlparse(path)[2]\r | |
681 | path = os.path.normpath(urllib.unquote(path))\r | |
682 | words = path.split('/')\r | |
683 | words = filter(None, words)\r | |
684 | path = self.root\r | |
685 | for word in words:\r | |
686 | drive, word = os.path.splitdrive(word)\r | |
687 | head, word = os.path.split(word)\r | |
688 | if word in self.root: continue\r | |
689 | path = os.path.join(path, word)\r | |
690 | return path\r | |
691 | \r | |
692 | def log_message(self, format, *args):\r | |
693 | \r | |
694 | # we override this to suppress logging unless "verbose"\r | |
695 | \r | |
696 | if test_support.verbose:\r | |
697 | sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" %\r | |
698 | (self.server.server_address,\r | |
699 | self.server.server_port,\r | |
700 | self.request.cipher(),\r | |
701 | self.log_date_time_string(),\r | |
702 | format%args))\r | |
703 | \r | |
704 | \r | |
705 | def __init__(self, certfile):\r | |
706 | self.flag = None\r | |
707 | self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0]\r | |
708 | self.server = self.HTTPSServer(\r | |
709 | (HOST, 0), self.RootedHTTPRequestHandler, certfile)\r | |
710 | self.port = self.server.server_port\r | |
711 | threading.Thread.__init__(self)\r | |
712 | self.daemon = True\r | |
713 | \r | |
714 | def __str__(self):\r | |
715 | return "<%s %s>" % (self.__class__.__name__, self.server)\r | |
716 | \r | |
717 | def start(self, flag=None):\r | |
718 | self.flag = flag\r | |
719 | threading.Thread.start(self)\r | |
720 | \r | |
721 | def run(self):\r | |
722 | if self.flag:\r | |
723 | self.flag.set()\r | |
724 | self.server.serve_forever(0.05)\r | |
725 | \r | |
726 | def stop(self):\r | |
727 | self.server.shutdown()\r | |
728 | \r | |
729 | \r | |
730 | def bad_cert_test(certfile):\r | |
731 | """\r | |
732 | Launch a server with CERT_REQUIRED, and check that trying to\r | |
733 | connect to it with the given client certificate fails.\r | |
734 | """\r | |
735 | server = ThreadedEchoServer(CERTFILE,\r | |
736 | certreqs=ssl.CERT_REQUIRED,\r | |
737 | cacerts=CERTFILE, chatty=False)\r | |
738 | flag = threading.Event()\r | |
739 | server.start(flag)\r | |
740 | # wait for it to start\r | |
741 | flag.wait()\r | |
742 | # try to connect\r | |
743 | try:\r | |
744 | try:\r | |
745 | s = ssl.wrap_socket(socket.socket(),\r | |
746 | certfile=certfile,\r | |
747 | ssl_version=ssl.PROTOCOL_TLSv1)\r | |
748 | s.connect((HOST, server.port))\r | |
749 | except ssl.SSLError, x:\r | |
750 | if test_support.verbose:\r | |
751 | sys.stdout.write("\nSSLError is %s\n" % x[1])\r | |
752 | except socket.error, x:\r | |
753 | if test_support.verbose:\r | |
754 | sys.stdout.write("\nsocket.error is %s\n" % x[1])\r | |
755 | else:\r | |
756 | raise AssertionError("Use of invalid cert should have failed!")\r | |
757 | finally:\r | |
758 | server.stop()\r | |
759 | server.join()\r | |
760 | \r | |
761 | def server_params_test(certfile, protocol, certreqs, cacertsfile,\r | |
762 | client_certfile, client_protocol=None, indata="FOO\n",\r | |
763 | ciphers=None, chatty=True, connectionchatty=False,\r | |
764 | wrap_accepting_socket=False):\r | |
765 | """\r | |
766 | Launch a server, connect a client to it and try various reads\r | |
767 | and writes.\r | |
768 | """\r | |
769 | server = ThreadedEchoServer(certfile,\r | |
770 | certreqs=certreqs,\r | |
771 | ssl_version=protocol,\r | |
772 | cacerts=cacertsfile,\r | |
773 | ciphers=ciphers,\r | |
774 | chatty=chatty,\r | |
775 | connectionchatty=connectionchatty,\r | |
776 | wrap_accepting_socket=wrap_accepting_socket)\r | |
777 | flag = threading.Event()\r | |
778 | server.start(flag)\r | |
779 | # wait for it to start\r | |
780 | flag.wait()\r | |
781 | # try to connect\r | |
782 | if client_protocol is None:\r | |
783 | client_protocol = protocol\r | |
784 | try:\r | |
785 | s = ssl.wrap_socket(socket.socket(),\r | |
786 | certfile=client_certfile,\r | |
787 | ca_certs=cacertsfile,\r | |
788 | ciphers=ciphers,\r | |
789 | cert_reqs=certreqs,\r | |
790 | ssl_version=client_protocol)\r | |
791 | s.connect((HOST, server.port))\r | |
792 | for arg in [indata, bytearray(indata), memoryview(indata)]:\r | |
793 | if connectionchatty:\r | |
794 | if test_support.verbose:\r | |
795 | sys.stdout.write(\r | |
796 | " client: sending %s...\n" % (repr(arg)))\r | |
797 | s.write(arg)\r | |
798 | outdata = s.read()\r | |
799 | if connectionchatty:\r | |
800 | if test_support.verbose:\r | |
801 | sys.stdout.write(" client: read %s\n" % repr(outdata))\r | |
802 | if outdata != indata.lower():\r | |
803 | raise AssertionError(\r | |
804 | "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"\r | |
805 | % (outdata[:min(len(outdata),20)], len(outdata),\r | |
806 | indata[:min(len(indata),20)].lower(), len(indata)))\r | |
807 | s.write("over\n")\r | |
808 | if connectionchatty:\r | |
809 | if test_support.verbose:\r | |
810 | sys.stdout.write(" client: closing connection.\n")\r | |
811 | s.close()\r | |
812 | finally:\r | |
813 | server.stop()\r | |
814 | server.join()\r | |
815 | \r | |
816 | def try_protocol_combo(server_protocol,\r | |
817 | client_protocol,\r | |
818 | expect_success,\r | |
819 | certsreqs=None):\r | |
820 | if certsreqs is None:\r | |
821 | certsreqs = ssl.CERT_NONE\r | |
822 | certtype = {\r | |
823 | ssl.CERT_NONE: "CERT_NONE",\r | |
824 | ssl.CERT_OPTIONAL: "CERT_OPTIONAL",\r | |
825 | ssl.CERT_REQUIRED: "CERT_REQUIRED",\r | |
826 | }[certsreqs]\r | |
827 | if test_support.verbose:\r | |
828 | formatstr = (expect_success and " %s->%s %s\n") or " {%s->%s} %s\n"\r | |
829 | sys.stdout.write(formatstr %\r | |
830 | (ssl.get_protocol_name(client_protocol),\r | |
831 | ssl.get_protocol_name(server_protocol),\r | |
832 | certtype))\r | |
833 | try:\r | |
834 | # NOTE: we must enable "ALL" ciphers, otherwise an SSLv23 client\r | |
835 | # will send an SSLv3 hello (rather than SSLv2) starting from\r | |
836 | # OpenSSL 1.0.0 (see issue #8322).\r | |
837 | server_params_test(CERTFILE, server_protocol, certsreqs,\r | |
838 | CERTFILE, CERTFILE, client_protocol,\r | |
839 | ciphers="ALL", chatty=False)\r | |
840 | # Protocol mismatch can result in either an SSLError, or a\r | |
841 | # "Connection reset by peer" error.\r | |
842 | except ssl.SSLError:\r | |
843 | if expect_success:\r | |
844 | raise\r | |
845 | except socket.error as e:\r | |
846 | if expect_success or e.errno != errno.ECONNRESET:\r | |
847 | raise\r | |
848 | else:\r | |
849 | if not expect_success:\r | |
850 | raise AssertionError(\r | |
851 | "Client protocol %s succeeded with server protocol %s!"\r | |
852 | % (ssl.get_protocol_name(client_protocol),\r | |
853 | ssl.get_protocol_name(server_protocol)))\r | |
854 | \r | |
855 | \r | |
856 | class ThreadedTests(unittest.TestCase):\r | |
857 | \r | |
858 | def test_rude_shutdown(self):\r | |
859 | """A brutal shutdown of an SSL server should raise an IOError\r | |
860 | in the client when attempting handshake.\r | |
861 | """\r | |
862 | listener_ready = threading.Event()\r | |
863 | listener_gone = threading.Event()\r | |
864 | \r | |
865 | s = socket.socket()\r | |
866 | port = test_support.bind_port(s, HOST)\r | |
867 | \r | |
868 | # `listener` runs in a thread. It sits in an accept() until\r | |
869 | # the main thread connects. Then it rudely closes the socket,\r | |
870 | # and sets Event `listener_gone` to let the main thread know\r | |
871 | # the socket is gone.\r | |
872 | def listener():\r | |
873 | s.listen(5)\r | |
874 | listener_ready.set()\r | |
875 | s.accept()\r | |
876 | s.close()\r | |
877 | listener_gone.set()\r | |
878 | \r | |
879 | def connector():\r | |
880 | listener_ready.wait()\r | |
881 | c = socket.socket()\r | |
882 | c.connect((HOST, port))\r | |
883 | listener_gone.wait()\r | |
884 | try:\r | |
885 | ssl_sock = ssl.wrap_socket(c)\r | |
886 | except IOError:\r | |
887 | pass\r | |
888 | else:\r | |
889 | self.fail('connecting to closed SSL socket should have failed')\r | |
890 | \r | |
891 | t = threading.Thread(target=listener)\r | |
892 | t.start()\r | |
893 | try:\r | |
894 | connector()\r | |
895 | finally:\r | |
896 | t.join()\r | |
897 | \r | |
898 | @skip_if_broken_ubuntu_ssl\r | |
899 | def test_echo(self):\r | |
900 | """Basic test of an SSL client connecting to a server"""\r | |
901 | if test_support.verbose:\r | |
902 | sys.stdout.write("\n")\r | |
903 | server_params_test(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,\r | |
904 | CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,\r | |
905 | chatty=True, connectionchatty=True)\r | |
906 | \r | |
907 | def test_getpeercert(self):\r | |
908 | if test_support.verbose:\r | |
909 | sys.stdout.write("\n")\r | |
910 | s2 = socket.socket()\r | |
911 | server = ThreadedEchoServer(CERTFILE,\r | |
912 | certreqs=ssl.CERT_NONE,\r | |
913 | ssl_version=ssl.PROTOCOL_SSLv23,\r | |
914 | cacerts=CERTFILE,\r | |
915 | chatty=False)\r | |
916 | flag = threading.Event()\r | |
917 | server.start(flag)\r | |
918 | # wait for it to start\r | |
919 | flag.wait()\r | |
920 | # try to connect\r | |
921 | try:\r | |
922 | s = ssl.wrap_socket(socket.socket(),\r | |
923 | certfile=CERTFILE,\r | |
924 | ca_certs=CERTFILE,\r | |
925 | cert_reqs=ssl.CERT_REQUIRED,\r | |
926 | ssl_version=ssl.PROTOCOL_SSLv23)\r | |
927 | s.connect((HOST, server.port))\r | |
928 | cert = s.getpeercert()\r | |
929 | self.assertTrue(cert, "Can't get peer certificate.")\r | |
930 | cipher = s.cipher()\r | |
931 | if test_support.verbose:\r | |
932 | sys.stdout.write(pprint.pformat(cert) + '\n')\r | |
933 | sys.stdout.write("Connection cipher is " + str(cipher) + '.\n')\r | |
934 | if 'subject' not in cert:\r | |
935 | self.fail("No subject field in certificate: %s." %\r | |
936 | pprint.pformat(cert))\r | |
937 | if ((('organizationName', 'Python Software Foundation'),)\r | |
938 | not in cert['subject']):\r | |
939 | self.fail(\r | |
940 | "Missing or invalid 'organizationName' field in certificate subject; "\r | |
941 | "should be 'Python Software Foundation'.")\r | |
942 | s.close()\r | |
943 | finally:\r | |
944 | server.stop()\r | |
945 | server.join()\r | |
946 | \r | |
947 | def test_empty_cert(self):\r | |
948 | """Connecting with an empty cert file"""\r | |
949 | bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir,\r | |
950 | "nullcert.pem"))\r | |
951 | def test_malformed_cert(self):\r | |
952 | """Connecting with a badly formatted certificate (syntax error)"""\r | |
953 | bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir,\r | |
954 | "badcert.pem"))\r | |
955 | def test_nonexisting_cert(self):\r | |
956 | """Connecting with a non-existing cert file"""\r | |
957 | bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir,\r | |
958 | "wrongcert.pem"))\r | |
959 | def test_malformed_key(self):\r | |
960 | """Connecting with a badly formatted key (syntax error)"""\r | |
961 | bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir,\r | |
962 | "badkey.pem"))\r | |
963 | \r | |
964 | @skip_if_broken_ubuntu_ssl\r | |
965 | def test_protocol_sslv2(self):\r | |
966 | """Connecting to an SSLv2 server with various client options"""\r | |
967 | if test_support.verbose:\r | |
968 | sys.stdout.write("\n")\r | |
969 | try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True)\r | |
970 | try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL)\r | |
971 | try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED)\r | |
972 | try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)\r | |
973 | try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)\r | |
974 | try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)\r | |
975 | \r | |
976 | @skip_if_broken_ubuntu_ssl\r | |
977 | def test_protocol_sslv23(self):\r | |
978 | """Connecting to an SSLv23 server with various client options"""\r | |
979 | if test_support.verbose:\r | |
980 | sys.stdout.write("\n")\r | |
981 | try:\r | |
982 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True)\r | |
983 | except (ssl.SSLError, socket.error), x:\r | |
984 | # this fails on some older versions of OpenSSL (0.9.7l, for instance)\r | |
985 | if test_support.verbose:\r | |
986 | sys.stdout.write(\r | |
987 | " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"\r | |
988 | % str(x))\r | |
989 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)\r | |
990 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)\r | |
991 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)\r | |
992 | \r | |
993 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)\r | |
994 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)\r | |
995 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)\r | |
996 | \r | |
997 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)\r | |
998 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)\r | |
999 | try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)\r | |
1000 | \r | |
1001 | @skip_if_broken_ubuntu_ssl\r | |
1002 | def test_protocol_sslv3(self):\r | |
1003 | """Connecting to an SSLv3 server with various client options"""\r | |
1004 | if test_support.verbose:\r | |
1005 | sys.stdout.write("\n")\r | |
1006 | try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)\r | |
1007 | try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)\r | |
1008 | try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)\r | |
1009 | if hasattr(ssl, 'PROTOCOL_SSLv2'):\r | |
1010 | try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)\r | |
1011 | try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)\r | |
1012 | try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)\r | |
1013 | \r | |
1014 | @skip_if_broken_ubuntu_ssl\r | |
1015 | def test_protocol_tlsv1(self):\r | |
1016 | """Connecting to a TLSv1 server with various client options"""\r | |
1017 | if test_support.verbose:\r | |
1018 | sys.stdout.write("\n")\r | |
1019 | try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)\r | |
1020 | try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)\r | |
1021 | try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)\r | |
1022 | if hasattr(ssl, 'PROTOCOL_SSLv2'):\r | |
1023 | try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)\r | |
1024 | try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)\r | |
1025 | try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)\r | |
1026 | \r | |
1027 | def test_starttls(self):\r | |
1028 | """Switching from clear text to encrypted and back again."""\r | |
1029 | msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4", "ENDTLS", "msg 5", "msg 6")\r | |
1030 | \r | |
1031 | server = ThreadedEchoServer(CERTFILE,\r | |
1032 | ssl_version=ssl.PROTOCOL_TLSv1,\r | |
1033 | starttls_server=True,\r | |
1034 | chatty=True,\r | |
1035 | connectionchatty=True)\r | |
1036 | flag = threading.Event()\r | |
1037 | server.start(flag)\r | |
1038 | # wait for it to start\r | |
1039 | flag.wait()\r | |
1040 | # try to connect\r | |
1041 | wrapped = False\r | |
1042 | try:\r | |
1043 | s = socket.socket()\r | |
1044 | s.setblocking(1)\r | |
1045 | s.connect((HOST, server.port))\r | |
1046 | if test_support.verbose:\r | |
1047 | sys.stdout.write("\n")\r | |
1048 | for indata in msgs:\r | |
1049 | if test_support.verbose:\r | |
1050 | sys.stdout.write(\r | |
1051 | " client: sending %s...\n" % repr(indata))\r | |
1052 | if wrapped:\r | |
1053 | conn.write(indata)\r | |
1054 | outdata = conn.read()\r | |
1055 | else:\r | |
1056 | s.send(indata)\r | |
1057 | outdata = s.recv(1024)\r | |
1058 | if (indata == "STARTTLS" and\r | |
1059 | outdata.strip().lower().startswith("ok")):\r | |
1060 | # STARTTLS ok, switch to secure mode\r | |
1061 | if test_support.verbose:\r | |
1062 | sys.stdout.write(\r | |
1063 | " client: read %s from server, starting TLS...\n"\r | |
1064 | % repr(outdata))\r | |
1065 | conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)\r | |
1066 | wrapped = True\r | |
1067 | elif (indata == "ENDTLS" and\r | |
1068 | outdata.strip().lower().startswith("ok")):\r | |
1069 | # ENDTLS ok, switch back to clear text\r | |
1070 | if test_support.verbose:\r | |
1071 | sys.stdout.write(\r | |
1072 | " client: read %s from server, ending TLS...\n"\r | |
1073 | % repr(outdata))\r | |
1074 | s = conn.unwrap()\r | |
1075 | wrapped = False\r | |
1076 | else:\r | |
1077 | if test_support.verbose:\r | |
1078 | sys.stdout.write(\r | |
1079 | " client: read %s from server\n" % repr(outdata))\r | |
1080 | if test_support.verbose:\r | |
1081 | sys.stdout.write(" client: closing connection.\n")\r | |
1082 | if wrapped:\r | |
1083 | conn.write("over\n")\r | |
1084 | else:\r | |
1085 | s.send("over\n")\r | |
1086 | s.close()\r | |
1087 | finally:\r | |
1088 | server.stop()\r | |
1089 | server.join()\r | |
1090 | \r | |
1091 | def test_socketserver(self):\r | |
1092 | """Using a SocketServer to create and manage SSL connections."""\r | |
1093 | server = SocketServerHTTPSServer(CERTFILE)\r | |
1094 | flag = threading.Event()\r | |
1095 | server.start(flag)\r | |
1096 | # wait for it to start\r | |
1097 | flag.wait()\r | |
1098 | # try to connect\r | |
1099 | try:\r | |
1100 | if test_support.verbose:\r | |
1101 | sys.stdout.write('\n')\r | |
1102 | with open(CERTFILE, 'rb') as f:\r | |
1103 | d1 = f.read()\r | |
1104 | d2 = ''\r | |
1105 | # now fetch the same data from the HTTPS server\r | |
1106 | url = 'https://127.0.0.1:%d/%s' % (\r | |
1107 | server.port, os.path.split(CERTFILE)[1])\r | |
1108 | with test_support.check_py3k_warnings():\r | |
1109 | f = urllib.urlopen(url)\r | |
1110 | dlen = f.info().getheader("content-length")\r | |
1111 | if dlen and (int(dlen) > 0):\r | |
1112 | d2 = f.read(int(dlen))\r | |
1113 | if test_support.verbose:\r | |
1114 | sys.stdout.write(\r | |
1115 | " client: read %d bytes from remote server '%s'\n"\r | |
1116 | % (len(d2), server))\r | |
1117 | f.close()\r | |
1118 | self.assertEqual(d1, d2)\r | |
1119 | finally:\r | |
1120 | server.stop()\r | |
1121 | server.join()\r | |
1122 | \r | |
1123 | def test_wrapped_accept(self):\r | |
1124 | """Check the accept() method on SSL sockets."""\r | |
1125 | if test_support.verbose:\r | |
1126 | sys.stdout.write("\n")\r | |
1127 | server_params_test(CERTFILE, ssl.PROTOCOL_SSLv23, ssl.CERT_REQUIRED,\r | |
1128 | CERTFILE, CERTFILE, ssl.PROTOCOL_SSLv23,\r | |
1129 | chatty=True, connectionchatty=True,\r | |
1130 | wrap_accepting_socket=True)\r | |
1131 | \r | |
1132 | def test_asyncore_server(self):\r | |
1133 | """Check the example asyncore integration."""\r | |
1134 | indata = "TEST MESSAGE of mixed case\n"\r | |
1135 | \r | |
1136 | if test_support.verbose:\r | |
1137 | sys.stdout.write("\n")\r | |
1138 | server = AsyncoreEchoServer(CERTFILE)\r | |
1139 | flag = threading.Event()\r | |
1140 | server.start(flag)\r | |
1141 | # wait for it to start\r | |
1142 | flag.wait()\r | |
1143 | # try to connect\r | |
1144 | try:\r | |
1145 | s = ssl.wrap_socket(socket.socket())\r | |
1146 | s.connect(('127.0.0.1', server.port))\r | |
1147 | if test_support.verbose:\r | |
1148 | sys.stdout.write(\r | |
1149 | " client: sending %s...\n" % (repr(indata)))\r | |
1150 | s.write(indata)\r | |
1151 | outdata = s.read()\r | |
1152 | if test_support.verbose:\r | |
1153 | sys.stdout.write(" client: read %s\n" % repr(outdata))\r | |
1154 | if outdata != indata.lower():\r | |
1155 | self.fail(\r | |
1156 | "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"\r | |
1157 | % (outdata[:min(len(outdata),20)], len(outdata),\r | |
1158 | indata[:min(len(indata),20)].lower(), len(indata)))\r | |
1159 | s.write("over\n")\r | |
1160 | if test_support.verbose:\r | |
1161 | sys.stdout.write(" client: closing connection.\n")\r | |
1162 | s.close()\r | |
1163 | finally:\r | |
1164 | server.stop()\r | |
1165 | # wait for server thread to end\r | |
1166 | server.join()\r | |
1167 | \r | |
1168 | def test_recv_send(self):\r | |
1169 | """Test recv(), send() and friends."""\r | |
1170 | if test_support.verbose:\r | |
1171 | sys.stdout.write("\n")\r | |
1172 | \r | |
1173 | server = ThreadedEchoServer(CERTFILE,\r | |
1174 | certreqs=ssl.CERT_NONE,\r | |
1175 | ssl_version=ssl.PROTOCOL_TLSv1,\r | |
1176 | cacerts=CERTFILE,\r | |
1177 | chatty=True,\r | |
1178 | connectionchatty=False)\r | |
1179 | flag = threading.Event()\r | |
1180 | server.start(flag)\r | |
1181 | # wait for it to start\r | |
1182 | flag.wait()\r | |
1183 | # try to connect\r | |
1184 | s = ssl.wrap_socket(socket.socket(),\r | |
1185 | server_side=False,\r | |
1186 | certfile=CERTFILE,\r | |
1187 | ca_certs=CERTFILE,\r | |
1188 | cert_reqs=ssl.CERT_NONE,\r | |
1189 | ssl_version=ssl.PROTOCOL_TLSv1)\r | |
1190 | s.connect((HOST, server.port))\r | |
1191 | try:\r | |
1192 | # helper methods for standardising recv* method signatures\r | |
1193 | def _recv_into():\r | |
1194 | b = bytearray("\0"*100)\r | |
1195 | count = s.recv_into(b)\r | |
1196 | return b[:count]\r | |
1197 | \r | |
1198 | def _recvfrom_into():\r | |
1199 | b = bytearray("\0"*100)\r | |
1200 | count, addr = s.recvfrom_into(b)\r | |
1201 | return b[:count]\r | |
1202 | \r | |
1203 | # (name, method, whether to expect success, *args)\r | |
1204 | send_methods = [\r | |
1205 | ('send', s.send, True, []),\r | |
1206 | ('sendto', s.sendto, False, ["some.address"]),\r | |
1207 | ('sendall', s.sendall, True, []),\r | |
1208 | ]\r | |
1209 | recv_methods = [\r | |
1210 | ('recv', s.recv, True, []),\r | |
1211 | ('recvfrom', s.recvfrom, False, ["some.address"]),\r | |
1212 | ('recv_into', _recv_into, True, []),\r | |
1213 | ('recvfrom_into', _recvfrom_into, False, []),\r | |
1214 | ]\r | |
1215 | data_prefix = u"PREFIX_"\r | |
1216 | \r | |
1217 | for meth_name, send_meth, expect_success, args in send_methods:\r | |
1218 | indata = data_prefix + meth_name\r | |
1219 | try:\r | |
1220 | send_meth(indata.encode('ASCII', 'strict'), *args)\r | |
1221 | outdata = s.read()\r | |
1222 | outdata = outdata.decode('ASCII', 'strict')\r | |
1223 | if outdata != indata.lower():\r | |
1224 | self.fail(\r | |
1225 | "While sending with <<%s>> bad data "\r | |
1226 | "<<%r>> (%d) received; "\r | |
1227 | "expected <<%r>> (%d)\n" % (\r | |
1228 | meth_name, outdata[:20], len(outdata),\r | |
1229 | indata[:20], len(indata)\r | |
1230 | )\r | |
1231 | )\r | |
1232 | except ValueError as e:\r | |
1233 | if expect_success:\r | |
1234 | self.fail(\r | |
1235 | "Failed to send with method <<%s>>; "\r | |
1236 | "expected to succeed.\n" % (meth_name,)\r | |
1237 | )\r | |
1238 | if not str(e).startswith(meth_name):\r | |
1239 | self.fail(\r | |
1240 | "Method <<%s>> failed with unexpected "\r | |
1241 | "exception message: %s\n" % (\r | |
1242 | meth_name, e\r | |
1243 | )\r | |
1244 | )\r | |
1245 | \r | |
1246 | for meth_name, recv_meth, expect_success, args in recv_methods:\r | |
1247 | indata = data_prefix + meth_name\r | |
1248 | try:\r | |
1249 | s.send(indata.encode('ASCII', 'strict'))\r | |
1250 | outdata = recv_meth(*args)\r | |
1251 | outdata = outdata.decode('ASCII', 'strict')\r | |
1252 | if outdata != indata.lower():\r | |
1253 | self.fail(\r | |
1254 | "While receiving with <<%s>> bad data "\r | |
1255 | "<<%r>> (%d) received; "\r | |
1256 | "expected <<%r>> (%d)\n" % (\r | |
1257 | meth_name, outdata[:20], len(outdata),\r | |
1258 | indata[:20], len(indata)\r | |
1259 | )\r | |
1260 | )\r | |
1261 | except ValueError as e:\r | |
1262 | if expect_success:\r | |
1263 | self.fail(\r | |
1264 | "Failed to receive with method <<%s>>; "\r | |
1265 | "expected to succeed.\n" % (meth_name,)\r | |
1266 | )\r | |
1267 | if not str(e).startswith(meth_name):\r | |
1268 | self.fail(\r | |
1269 | "Method <<%s>> failed with unexpected "\r | |
1270 | "exception message: %s\n" % (\r | |
1271 | meth_name, e\r | |
1272 | )\r | |
1273 | )\r | |
1274 | # consume data\r | |
1275 | s.read()\r | |
1276 | \r | |
1277 | s.write("over\n".encode("ASCII", "strict"))\r | |
1278 | s.close()\r | |
1279 | finally:\r | |
1280 | server.stop()\r | |
1281 | server.join()\r | |
1282 | \r | |
1283 | def test_handshake_timeout(self):\r | |
1284 | # Issue #5103: SSL handshake must respect the socket timeout\r | |
1285 | server = socket.socket(socket.AF_INET)\r | |
1286 | host = "127.0.0.1"\r | |
1287 | port = test_support.bind_port(server)\r | |
1288 | started = threading.Event()\r | |
1289 | finish = False\r | |
1290 | \r | |
1291 | def serve():\r | |
1292 | server.listen(5)\r | |
1293 | started.set()\r | |
1294 | conns = []\r | |
1295 | while not finish:\r | |
1296 | r, w, e = select.select([server], [], [], 0.1)\r | |
1297 | if server in r:\r | |
1298 | # Let the socket hang around rather than having\r | |
1299 | # it closed by garbage collection.\r | |
1300 | conns.append(server.accept()[0])\r | |
1301 | \r | |
1302 | t = threading.Thread(target=serve)\r | |
1303 | t.start()\r | |
1304 | started.wait()\r | |
1305 | \r | |
1306 | try:\r | |
1307 | try:\r | |
1308 | c = socket.socket(socket.AF_INET)\r | |
1309 | c.settimeout(0.2)\r | |
1310 | c.connect((host, port))\r | |
1311 | # Will attempt handshake and time out\r | |
1312 | self.assertRaisesRegexp(ssl.SSLError, "timed out",\r | |
1313 | ssl.wrap_socket, c)\r | |
1314 | finally:\r | |
1315 | c.close()\r | |
1316 | try:\r | |
1317 | c = socket.socket(socket.AF_INET)\r | |
1318 | c.settimeout(0.2)\r | |
1319 | c = ssl.wrap_socket(c)\r | |
1320 | # Will attempt handshake and time out\r | |
1321 | self.assertRaisesRegexp(ssl.SSLError, "timed out",\r | |
1322 | c.connect, (host, port))\r | |
1323 | finally:\r | |
1324 | c.close()\r | |
1325 | finally:\r | |
1326 | finish = True\r | |
1327 | t.join()\r | |
1328 | server.close()\r | |
1329 | \r | |
1330 | \r | |
1331 | def test_main(verbose=False):\r | |
1332 | global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT\r | |
1333 | CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,\r | |
1334 | "keycert.pem")\r | |
1335 | SVN_PYTHON_ORG_ROOT_CERT = os.path.join(\r | |
1336 | os.path.dirname(__file__) or os.curdir,\r | |
1337 | "https_svn_python_org_root.pem")\r | |
1338 | \r | |
1339 | if (not os.path.exists(CERTFILE) or\r | |
1340 | not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):\r | |
1341 | raise test_support.TestFailed("Can't read certificate files!")\r | |
1342 | \r | |
1343 | tests = [BasicTests, BasicSocketTests]\r | |
1344 | \r | |
1345 | if test_support.is_resource_enabled('network'):\r | |
1346 | tests.append(NetworkedTests)\r | |
1347 | \r | |
1348 | if _have_threads:\r | |
1349 | thread_info = test_support.threading_setup()\r | |
1350 | if thread_info and test_support.is_resource_enabled('network'):\r | |
1351 | tests.append(ThreadedTests)\r | |
1352 | \r | |
1353 | try:\r | |
1354 | test_support.run_unittest(*tests)\r | |
1355 | finally:\r | |
1356 | if _have_threads:\r | |
1357 | test_support.threading_cleanup(*thread_info)\r | |
1358 | \r | |
1359 | if __name__ == "__main__":\r | |
1360 | test_main()\r |