blob: 34bb31a4f70f193b06f0ccb35983b46d7b334a74 [file] [log] [blame]
Thomas Woutersed03b412007-08-28 21:37:11 +00001# Test the support for SSL and sockets
2
3import sys
4import unittest
5from test import test_support
6import socket
Bill Janssen6e027db2007-11-15 22:23:56 +00007import select
Thomas Woutersed03b412007-08-28 21:37:11 +00008import errno
Thomas Woutersed03b412007-08-28 21:37:11 +00009import subprocess
10import time
11import os
12import pprint
Thomas Wouters1b7f8912007-09-19 03:06:30 +000013import urllib, urlparse
Thomas Woutersed03b412007-08-28 21:37:11 +000014import shutil
15import traceback
Bill Janssen54cc54c2007-12-14 22:08:56 +000016import asyncore
Thomas Woutersed03b412007-08-28 21:37:11 +000017
Thomas Wouters1b7f8912007-09-19 03:06:30 +000018from BaseHTTPServer import HTTPServer
19from SimpleHTTPServer import SimpleHTTPRequestHandler
20
Thomas Woutersed03b412007-08-28 21:37:11 +000021# Optionally test SSL support, if we have it in the tested platform
22skip_expected = False
23try:
24 import ssl
25except ImportError:
26 skip_expected = True
27
28CERTFILE = None
Thomas Wouters1b7f8912007-09-19 03:06:30 +000029SVN_PYTHON_ORG_ROOT_CERT = None
Thomas Woutersed03b412007-08-28 21:37:11 +000030
Thomas Wouters1b7f8912007-09-19 03:06:30 +000031TESTPORT = 10025
Thomas Woutersed03b412007-08-28 21:37:11 +000032
33def handle_error(prefix):
34 exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
Thomas Wouters1b7f8912007-09-19 03:06:30 +000035 if test_support.verbose:
36 sys.stdout.write(prefix + exc_format)
Thomas Woutersed03b412007-08-28 21:37:11 +000037
38
39class BasicTests(unittest.TestCase):
40
Georg Brandlfceab5a2008-01-19 20:08:23 +000041 def testSSLconnect(self):
42 if not test_support.is_resource_enabled('network'):
43 return
44 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
45 cert_reqs=ssl.CERT_NONE)
46 s.connect(("svn.python.org", 443))
47 c = s.getpeercert()
48 if c:
49 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
50 s.close()
51
52 # this should fail because we have no verification certs
53 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
54 cert_reqs=ssl.CERT_REQUIRED)
55 try:
56 s.connect(("svn.python.org", 443))
57 except ssl.SSLError:
58 pass
59 finally:
60 s.close()
61
Thomas Wouters1b7f8912007-09-19 03:06:30 +000062 def testCrucialConstants(self):
63 ssl.PROTOCOL_SSLv2
64 ssl.PROTOCOL_SSLv23
65 ssl.PROTOCOL_SSLv3
66 ssl.PROTOCOL_TLSv1
67 ssl.CERT_NONE
68 ssl.CERT_OPTIONAL
69 ssl.CERT_REQUIRED
Thomas Woutersed03b412007-08-28 21:37:11 +000070
Thomas Wouters1b7f8912007-09-19 03:06:30 +000071 def testRAND(self):
72 v = ssl.RAND_status()
73 if test_support.verbose:
74 sys.stdout.write("\n RAND_status is %d (%s)\n"
75 % (v, (v and "sufficient randomness") or
76 "insufficient randomness"))
Thomas Woutersed03b412007-08-28 21:37:11 +000077 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000078 ssl.RAND_egd(1)
79 except TypeError:
80 pass
Thomas Woutersed03b412007-08-28 21:37:11 +000081 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000082 print("didn't raise TypeError")
83 ssl.RAND_add("this is a random string", 75.0)
Thomas Woutersed03b412007-08-28 21:37:11 +000084
Thomas Wouters1b7f8912007-09-19 03:06:30 +000085 def testParseCert(self):
86 # note that this uses an 'unofficial' function in _ssl.c,
87 # provided solely for this test, to exercise the certificate
88 # parsing code
89 p = ssl._ssl._test_decode_cert(CERTFILE, False)
90 if test_support.verbose:
91 sys.stdout.write("\n" + pprint.pformat(p) + "\n")
Thomas Woutersed03b412007-08-28 21:37:11 +000092
Thomas Wouters1b7f8912007-09-19 03:06:30 +000093 def testDERtoPEM(self):
94
95 pem = open(SVN_PYTHON_ORG_ROOT_CERT, 'r').read()
96 d1 = ssl.PEM_cert_to_DER_cert(pem)
97 p2 = ssl.DER_cert_to_PEM_cert(d1)
98 d2 = ssl.PEM_cert_to_DER_cert(p2)
99 if (d1 != d2):
100 raise test_support.TestFailed("PEM-to-DER or DER-to-PEM translation failed")
101
Bill Janssen6e027db2007-11-15 22:23:56 +0000102class NetworkedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000103
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000104 def testConnect(self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000105 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
106 cert_reqs=ssl.CERT_NONE)
107 s.connect(("svn.python.org", 443))
108 c = s.getpeercert()
109 if c:
110 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
111 s.close()
112
113 # this should fail because we have no verification certs
114 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
115 cert_reqs=ssl.CERT_REQUIRED)
Thomas Woutersed03b412007-08-28 21:37:11 +0000116 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000117 s.connect(("svn.python.org", 443))
118 except ssl.SSLError:
119 pass
120 finally:
121 s.close()
122
123 # this should succeed because we specify the root cert
124 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
125 cert_reqs=ssl.CERT_REQUIRED,
126 ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
127 try:
128 s.connect(("svn.python.org", 443))
129 except ssl.SSLError as x:
130 raise test_support.TestFailed("Unexpected exception %s" % x)
131 finally:
132 s.close()
133
Bill Janssen6e027db2007-11-15 22:23:56 +0000134 def testNonBlockingHandshake(self):
135 s = socket.socket(socket.AF_INET)
136 s.connect(("svn.python.org", 443))
137 s.setblocking(False)
138 s = ssl.wrap_socket(s,
139 cert_reqs=ssl.CERT_NONE,
140 do_handshake_on_connect=False)
141 count = 0
142 while True:
143 try:
144 count += 1
145 s.do_handshake()
146 break
147 except ssl.SSLError as err:
148 if err.args[0] == ssl.SSL_ERROR_WANT_READ:
149 select.select([s], [], [])
150 elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
151 select.select([], [s], [])
152 else:
153 raise
154 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000155 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000156 sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000157
Bill Janssen54cc54c2007-12-14 22:08:56 +0000158 def testFetchServerCert(self):
159
160 pem = ssl.get_server_certificate(("svn.python.org", 443))
161 if not pem:
162 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
163
164 return
165
166 try:
167 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE)
168 except ssl.SSLError as x:
169 #should fail
170 if test_support.verbose:
171 sys.stdout.write("%s\n" % x)
172 else:
173 raise test_support.TestFailed("Got server certificate %s for svn.python.org!" % pem)
174
175 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
176 if not pem:
177 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
178 if test_support.verbose:
179 sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem)
180
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000181
182try:
183 import threading
184except ImportError:
185 _have_threads = False
186else:
187
188 _have_threads = True
189
190 class ThreadedEchoServer(threading.Thread):
191
192 class ConnectionHandler(threading.Thread):
193
194 """A mildly complicated class, because we want it to work both
195 with and without the SSL wrapper around the socket connection, so
196 that we can test the STARTTLS functionality."""
197
Bill Janssen6e027db2007-11-15 22:23:56 +0000198 def __init__(self, server, connsock, addr):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000199 self.server = server
200 self.running = False
201 self.sock = connsock
Bill Janssen6e027db2007-11-15 22:23:56 +0000202 self.addr = addr
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000203 self.sock.setblocking(1)
204 self.sslconn = None
205 threading.Thread.__init__(self)
206 self.setDaemon(True)
207
208 def wrap_conn (self):
209 try:
210 self.sslconn = ssl.wrap_socket(self.sock, server_side=True,
211 certfile=self.server.certificate,
212 ssl_version=self.server.protocol,
213 ca_certs=self.server.cacerts,
214 cert_reqs=self.server.certreqs)
215 except:
216 if self.server.chatty:
Bill Janssen6e027db2007-11-15 22:23:56 +0000217 handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000218 if not self.server.expect_bad_connects:
219 # here, we want to stop the server, because this shouldn't
220 # happen in the context of our test case
221 self.running = False
222 # normally, we'd just stop here, but for the test
223 # harness, we want to stop the server
224 self.server.stop()
Bill Janssen6e027db2007-11-15 22:23:56 +0000225 self.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000226 return False
227
228 else:
229 if self.server.certreqs == ssl.CERT_REQUIRED:
230 cert = self.sslconn.getpeercert()
231 if test_support.verbose and self.server.chatty:
232 sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
233 cert_binary = self.sslconn.getpeercert(True)
234 if test_support.verbose and self.server.chatty:
235 sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n")
236 cipher = self.sslconn.cipher()
237 if test_support.verbose and self.server.chatty:
238 sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n")
239 return True
240
241 def read(self):
242 if self.sslconn:
243 return self.sslconn.read()
244 else:
245 return self.sock.recv(1024)
246
247 def write(self, bytes):
248 if self.sslconn:
249 return self.sslconn.write(bytes)
250 else:
251 return self.sock.send(bytes)
252
253 def close(self):
254 if self.sslconn:
255 self.sslconn.close()
256 else:
257 self.sock.close()
258
259 def run (self):
260 self.running = True
261 if not self.server.starttls_server:
262 if not self.wrap_conn():
263 return
264 while self.running:
265 try:
266 msg = self.read()
Bill Janssen6e027db2007-11-15 22:23:56 +0000267 amsg = (msg and str(msg, 'ASCII', 'strict')) or ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000268 if not msg:
269 # eof, so quit this handler
270 self.running = False
271 self.close()
Bill Janssen6e027db2007-11-15 22:23:56 +0000272 elif amsg.strip() == 'over':
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000273 if test_support.verbose and self.server.connectionchatty:
274 sys.stdout.write(" server: client closed connection\n")
275 self.close()
276 return
Bill Janssen6e027db2007-11-15 22:23:56 +0000277 elif (self.server.starttls_server and
278 amsg.strip() == 'STARTTLS'):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000279 if test_support.verbose and self.server.connectionchatty:
280 sys.stdout.write(" server: read STARTTLS from client, sending OK...\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000281 self.write("OK\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000282 if not self.wrap_conn():
283 return
284 else:
285 if (test_support.verbose and
286 self.server.connectionchatty):
287 ctype = (self.sslconn and "encrypted") or "unencrypted"
288 sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n"
289 % (repr(msg), ctype, repr(msg.lower()), ctype))
Bill Janssen6e027db2007-11-15 22:23:56 +0000290 self.write(amsg.lower().encode('ASCII', 'strict'))
291 except socket.error:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000292 if self.server.chatty:
293 handle_error("Test server failure:\n")
294 self.close()
295 self.running = False
296 # normally, we'd just stop here, but for the test
297 # harness, we want to stop the server
298 self.server.stop()
299 except:
300 handle_error('')
301
302 def __init__(self, port, certificate, ssl_version=None,
303 certreqs=None, cacerts=None, expect_bad_connects=False,
304 chatty=True, connectionchatty=False, starttls_server=False):
305 if ssl_version is None:
306 ssl_version = ssl.PROTOCOL_TLSv1
307 if certreqs is None:
308 certreqs = ssl.CERT_NONE
309 self.certificate = certificate
310 self.protocol = ssl_version
311 self.certreqs = certreqs
312 self.cacerts = cacerts
313 self.expect_bad_connects = expect_bad_connects
314 self.chatty = chatty
315 self.connectionchatty = connectionchatty
316 self.starttls_server = starttls_server
317 self.sock = socket.socket()
318 self.flag = None
319 if hasattr(socket, 'SO_REUSEADDR'):
320 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
321 if hasattr(socket, 'SO_REUSEPORT'):
322 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
323 self.sock.bind(('127.0.0.1', port))
324 self.active = False
325 threading.Thread.__init__(self)
326 self.setDaemon(False)
327
328 def start (self, flag=None):
329 self.flag = flag
330 threading.Thread.start(self)
331
332 def run (self):
333 self.sock.settimeout(0.5)
334 self.sock.listen(5)
335 self.active = True
336 if self.flag:
337 # signal an event
338 self.flag.set()
339 while self.active:
340 try:
341 newconn, connaddr = self.sock.accept()
342 if test_support.verbose and self.chatty:
343 sys.stdout.write(' server: new connection from '
Bill Janssen6e027db2007-11-15 22:23:56 +0000344 + repr(connaddr) + '\n')
345 handler = self.ConnectionHandler(self, newconn, connaddr)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000346 handler.start()
347 except socket.timeout:
348 pass
349 except KeyboardInterrupt:
350 self.stop()
351 except:
352 if self.chatty:
353 handle_error("Test server failure:\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000354 self.sock.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000355
356 def stop (self):
357 self.active = False
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000358
Bill Janssen54cc54c2007-12-14 22:08:56 +0000359 class OurHTTPSServer(threading.Thread):
360
361 # This one's based on HTTPServer, which is based on SocketServer
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000362
363 class HTTPSServer(HTTPServer):
364
365 def __init__(self, server_address, RequestHandlerClass, certfile):
366
367 HTTPServer.__init__(self, server_address, RequestHandlerClass)
368 # we assume the certfile contains both private key and certificate
369 self.certfile = certfile
370 self.active = False
371 self.allow_reuse_address = True
372
Bill Janssen6e027db2007-11-15 22:23:56 +0000373 def __str__(self):
374 return ('<%s %s:%s>' %
375 (self.__class__.__name__,
376 self.server_name,
377 self.server_port))
378
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000379 def get_request (self):
380 # override this to wrap socket with SSL
381 sock, addr = self.socket.accept()
382 sslconn = ssl.wrap_socket(sock, server_side=True,
383 certfile=self.certfile)
384 return sslconn, addr
385
386 # The methods overridden below this are mainly so that we
387 # can run it in a thread and be able to stop it from another
388 # You probably wouldn't need them in other uses.
389
390 def server_activate(self):
391 # We want to run this in a thread for testing purposes,
392 # so we override this to set timeout, so that we get
393 # a chance to stop the server
394 self.socket.settimeout(0.5)
395 HTTPServer.server_activate(self)
396
397 def serve_forever(self):
398 # We want this to run in a thread, so we use a slightly
399 # modified version of "forever".
400 self.active = True
401 while self.active:
402 try:
403 self.handle_request()
404 except socket.timeout:
405 pass
406 except KeyboardInterrupt:
407 self.server_close()
408 return
409 except:
410 sys.stdout.write(''.join(traceback.format_exception(*sys.exc_info())));
411
412 def server_close(self):
413 # Again, we want this to run in a thread, so we need to override
414 # close to clear the "active" flag, so that serve_forever() will
415 # terminate.
416 HTTPServer.server_close(self)
417 self.active = False
418
419 class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):
420
421 # need to override translate_path to get a known root,
422 # instead of using os.curdir, since the test could be
423 # run from anywhere
424
425 server_version = "TestHTTPS/1.0"
426
427 root = None
428
429 def translate_path(self, path):
430 """Translate a /-separated PATH to the local filename syntax.
431
432 Components that mean special things to the local file system
433 (e.g. drive or directory names) are ignored. (XXX They should
434 probably be diagnosed.)
435
436 """
437 # abandon query parameters
438 path = urlparse.urlparse(path)[2]
439 path = os.path.normpath(urllib.unquote(path))
440 words = path.split('/')
441 words = filter(None, words)
442 path = self.root
443 for word in words:
444 drive, word = os.path.splitdrive(word)
445 head, word = os.path.split(word)
446 if word in self.root: continue
447 path = os.path.join(path, word)
448 return path
449
450 def log_message(self, format, *args):
451
452 # we override this to suppress logging unless "verbose"
453
Thomas Wouters89d996e2007-09-08 17:39:28 +0000454 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000455 sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" %
456 (self.server.server_address,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000457 self.server.server_port,
458 self.request.cipher(),
459 self.log_date_time_string(),
460 format%args))
Thomas Woutersed03b412007-08-28 21:37:11 +0000461
462
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000463 def __init__(self, port, certfile):
464 self.flag = None
465 self.active = False
466 self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0]
467 self.server = self.HTTPSServer(
468 ('', port), self.RootedHTTPRequestHandler, certfile)
Thomas Woutersed03b412007-08-28 21:37:11 +0000469 threading.Thread.__init__(self)
470 self.setDaemon(True)
471
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000472 def __str__(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000473 return "<%s %s>" % (self.__class__.__name__, self.server)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000474
475 def start (self, flag=None):
476 self.flag = flag
477 threading.Thread.start(self)
478
Thomas Woutersed03b412007-08-28 21:37:11 +0000479 def run (self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000480 self.active = True
481 if self.flag:
482 self.flag.set()
483 self.server.serve_forever()
484 self.active = False
485
486 def stop (self):
487 self.active = False
488 self.server.server_close()
489
490
Bill Janssen54cc54c2007-12-14 22:08:56 +0000491 class AsyncoreEchoServer(threading.Thread):
492
493 # this one's based on asyncore.dispatcher
494
495 class EchoServer (asyncore.dispatcher):
496
497 class ConnectionHandler (asyncore.dispatcher_with_send):
498
499 def __init__(self, conn, certfile):
500 self.socket = ssl.wrap_socket(conn, server_side=True,
501 certfile=certfile,
502 do_handshake_on_connect=False)
503 asyncore.dispatcher_with_send.__init__(self, self.socket)
504 # now we have to do the handshake
505 # we'll just do it the easy way, and block the connection
506 # till it's finished. If we were doing it right, we'd
507 # do this in multiple calls to handle_read...
508 self.do_handshake(block=True)
509
510 def readable(self):
511 if isinstance(self.socket, ssl.SSLSocket):
512 while self.socket.pending() > 0:
513 self.handle_read_event()
514 return True
515
516 def handle_read(self):
517 data = self.recv(1024)
518 if test_support.verbose:
519 sys.stdout.write(" server: read %s from client\n" % repr(data))
520 if not data:
521 self.close()
522 else:
523 self.send(str(data, 'ASCII', 'strict').lower().encode('ASCII', 'strict'))
524
525 def handle_close(self):
526 if test_support.verbose:
527 sys.stdout.write(" server: closed connection %s\n" % self.socket)
528
529 def handle_error(self):
530 raise
531
532 def __init__(self, port, certfile):
533 self.port = port
534 self.certfile = certfile
535 asyncore.dispatcher.__init__(self)
536 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
537 self.bind(('', port))
538 self.listen(5)
539
540 def handle_accept(self):
541 sock_obj, addr = self.accept()
542 if test_support.verbose:
543 sys.stdout.write(" server: new connection from %s:%s\n" %addr)
544 self.ConnectionHandler(sock_obj, self.certfile)
545
546 def handle_error(self):
547 raise
548
549 def __init__(self, port, certfile):
550 self.flag = None
551 self.active = False
552 self.server = self.EchoServer(port, certfile)
553 threading.Thread.__init__(self)
554 self.setDaemon(True)
555
556 def __str__(self):
557 return "<%s %s>" % (self.__class__.__name__, self.server)
558
559 def start (self, flag=None):
560 self.flag = flag
561 threading.Thread.start(self)
562
563 def run (self):
564 self.active = True
565 if self.flag:
566 self.flag.set()
567 while self.active:
568 try:
569 asyncore.loop(1)
570 except:
571 pass
572
573 def stop (self):
574 self.active = False
575 self.server.close()
576
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000577 def badCertTest (certfile):
578 server = ThreadedEchoServer(TESTPORT, CERTFILE,
579 certreqs=ssl.CERT_REQUIRED,
Bill Janssen6e027db2007-11-15 22:23:56 +0000580 cacerts=CERTFILE, chatty=False,
581 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000582 flag = threading.Event()
583 server.start(flag)
584 # wait for it to start
585 flag.wait()
586 # try to connect
587 try:
Thomas Woutersed03b412007-08-28 21:37:11 +0000588 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000589 s = ssl.wrap_socket(socket.socket(),
590 certfile=certfile,
591 ssl_version=ssl.PROTOCOL_TLSv1)
592 s.connect(('127.0.0.1', TESTPORT))
593 except ssl.SSLError as x:
Thomas Woutersed03b412007-08-28 21:37:11 +0000594 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000595 sys.stdout.write("\nSSLError is %s\n" % x)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000596 else:
597 raise test_support.TestFailed(
598 "Use of invalid cert should have failed!")
599 finally:
600 server.stop()
601 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000602
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000603 def serverParamsTest (certfile, protocol, certreqs, cacertsfile,
Bill Janssen6e027db2007-11-15 22:23:56 +0000604 client_certfile, client_protocol=None,
605 indata="FOO\n",
606 chatty=False, connectionchatty=False):
Thomas Woutersed03b412007-08-28 21:37:11 +0000607
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000608 server = ThreadedEchoServer(TESTPORT, certfile,
609 certreqs=certreqs,
610 ssl_version=protocol,
611 cacerts=cacertsfile,
612 chatty=chatty,
Bill Janssen6e027db2007-11-15 22:23:56 +0000613 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000614 flag = threading.Event()
615 server.start(flag)
616 # wait for it to start
617 flag.wait()
618 # try to connect
619 if client_protocol is None:
620 client_protocol = protocol
621 try:
Bill Janssen6e027db2007-11-15 22:23:56 +0000622 s = ssl.wrap_socket(socket.socket(),
Bill Janssen54cc54c2007-12-14 22:08:56 +0000623 server_side=False,
Bill Janssen6e027db2007-11-15 22:23:56 +0000624 certfile=client_certfile,
625 ca_certs=cacertsfile,
626 cert_reqs=certreqs,
627 ssl_version=client_protocol)
628 s.connect(('127.0.0.1', TESTPORT))
629 except ssl.SSLError as x:
630 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
631 except Exception as x:
632 raise test_support.TestFailed("Unexpected exception: " + str(x))
633 else:
634 if connectionchatty:
635 if test_support.verbose:
636 sys.stdout.write(
637 " client: sending %s...\n" % (repr(indata)))
638 s.write(indata.encode('ASCII', 'strict'))
639 outdata = s.read()
640 if connectionchatty:
641 if test_support.verbose:
642 sys.stdout.write(" client: read %s\n" % repr(outdata))
643 outdata = str(outdata, 'ASCII', 'strict')
644 if outdata != indata.lower():
645 raise test_support.TestFailed(
646 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
647 % (repr(outdata[:min(len(outdata),20)]), len(outdata),
648 repr(indata[:min(len(indata),20)].lower()), len(indata)))
649 s.write("over\n".encode("ASCII", "strict"))
650 if connectionchatty:
651 if test_support.verbose:
652 sys.stdout.write(" client: closing connection.\n")
653 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000654 finally:
655 server.stop()
656 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000657
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000658 def tryProtocolCombo (server_protocol,
659 client_protocol,
660 expectedToWork,
661 certsreqs=None):
Thomas Woutersed03b412007-08-28 21:37:11 +0000662
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000663 if certsreqs == None:
664 certsreqs = ssl.CERT_NONE
Thomas Woutersed03b412007-08-28 21:37:11 +0000665
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000666 if certsreqs == ssl.CERT_NONE:
667 certtype = "CERT_NONE"
668 elif certsreqs == ssl.CERT_OPTIONAL:
669 certtype = "CERT_OPTIONAL"
670 elif certsreqs == ssl.CERT_REQUIRED:
671 certtype = "CERT_REQUIRED"
Thomas Woutersed03b412007-08-28 21:37:11 +0000672 if test_support.verbose:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000673 formatstr = (expectedToWork and " %s->%s %s\n") or " {%s->%s} %s\n"
674 sys.stdout.write(formatstr %
675 (ssl.get_protocol_name(client_protocol),
676 ssl.get_protocol_name(server_protocol),
677 certtype))
678 try:
679 serverParamsTest(CERTFILE, server_protocol, certsreqs,
Bill Janssen6e027db2007-11-15 22:23:56 +0000680 CERTFILE, CERTFILE, client_protocol,
681 chatty=False, connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000682 except test_support.TestFailed:
683 if expectedToWork:
684 raise
685 else:
686 if not expectedToWork:
687 raise test_support.TestFailed(
688 "Client protocol %s succeeded with server protocol %s!"
689 % (ssl.get_protocol_name(client_protocol),
690 ssl.get_protocol_name(server_protocol)))
691
692
Bill Janssen6e027db2007-11-15 22:23:56 +0000693 class ThreadedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000694
695 def testEcho (self):
696
697 if test_support.verbose:
698 sys.stdout.write("\n")
699 serverParamsTest(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,
700 CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,
701 chatty=True, connectionchatty=True)
702
703 def testReadCert(self):
704
705 if test_support.verbose:
706 sys.stdout.write("\n")
707 s2 = socket.socket()
708 server = ThreadedEchoServer(TESTPORT, CERTFILE,
709 certreqs=ssl.CERT_NONE,
710 ssl_version=ssl.PROTOCOL_SSLv23,
711 cacerts=CERTFILE,
712 chatty=False)
713 flag = threading.Event()
714 server.start(flag)
715 # wait for it to start
716 flag.wait()
717 # try to connect
718 try:
719 try:
720 s = ssl.wrap_socket(socket.socket(),
721 certfile=CERTFILE,
722 ca_certs=CERTFILE,
723 cert_reqs=ssl.CERT_REQUIRED,
724 ssl_version=ssl.PROTOCOL_SSLv23)
725 s.connect(('127.0.0.1', TESTPORT))
726 except ssl.SSLError as x:
727 raise test_support.TestFailed(
728 "Unexpected SSL error: " + str(x))
729 except Exception as x:
730 raise test_support.TestFailed(
731 "Unexpected exception: " + str(x))
732 else:
733 if not s:
734 raise test_support.TestFailed(
735 "Can't SSL-handshake with test server")
736 cert = s.getpeercert()
737 if not cert:
738 raise test_support.TestFailed(
739 "Can't get peer certificate.")
740 cipher = s.cipher()
741 if test_support.verbose:
742 sys.stdout.write(pprint.pformat(cert) + '\n')
743 sys.stdout.write("Connection cipher is " + str(cipher) + '.\n')
Bill Janssen6e027db2007-11-15 22:23:56 +0000744 if 'subject' not in cert:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000745 raise test_support.TestFailed(
746 "No subject field in certificate: %s." %
747 pprint.pformat(cert))
748 if ((('organizationName', 'Python Software Foundation'),)
749 not in cert['subject']):
750 raise test_support.TestFailed(
751 "Missing or invalid 'organizationName' field in certificate subject; "
752 "should be 'Python Software Foundation'.");
753 s.close()
754 finally:
755 server.stop()
756 server.join()
757
758 def testNULLcert(self):
759 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
760 "nullcert.pem"))
761 def testMalformedCert(self):
762 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
763 "badcert.pem"))
764 def testMalformedKey(self):
765 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
766 "badkey.pem"))
767
Bill Janssen6e027db2007-11-15 22:23:56 +0000768 def testRudeShutdown(self):
769
770 listener_ready = threading.Event()
771 listener_gone = threading.Event()
772
773 # `listener` runs in a thread. It opens a socket listening on
774 # PORT, and sits in an accept() until the main thread connects.
775 # Then it rudely closes the socket, and sets Event `listener_gone`
776 # to let the main thread know the socket is gone.
777 def listener():
778 s = socket.socket()
779 if hasattr(socket, 'SO_REUSEADDR'):
780 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
781 if hasattr(socket, 'SO_REUSEPORT'):
782 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
783 s.bind(('127.0.0.1', TESTPORT))
784 s.listen(5)
785 listener_ready.set()
786 s.accept()
787 s = None # reclaim the socket object, which also closes it
788 listener_gone.set()
789
790 def connector():
791 listener_ready.wait()
792 s = socket.socket()
793 s.connect(('127.0.0.1', TESTPORT))
794 listener_gone.wait()
795 try:
796 ssl_sock = ssl.wrap_socket(s)
797 except IOError:
798 pass
799 else:
800 raise test_support.TestFailed(
801 'connecting to closed SSL socket should have failed')
802
803 t = threading.Thread(target=listener)
804 t.start()
805 connector()
806 t.join()
807
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000808 def testProtocolSSL2(self):
809 if test_support.verbose:
810 sys.stdout.write("\n")
811 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True)
812 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL)
813 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED)
814 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
815 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
816 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
817
818 def testProtocolSSL23(self):
819 if test_support.verbose:
820 sys.stdout.write("\n")
821 try:
822 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True)
823 except test_support.TestFailed as x:
824 # this fails on some older versions of OpenSSL (0.9.7l, for instance)
825 if test_support.verbose:
826 sys.stdout.write(
827 " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"
828 % str(x))
829 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)
830 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)
831 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)
832
833 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
834 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)
835 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
836
837 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
838 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
839 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
840
841 def testProtocolSSL3(self):
842 if test_support.verbose:
843 sys.stdout.write("\n")
844 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
845 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
846 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
847 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
848 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
849 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
850
851 def testProtocolTLS1(self):
852 if test_support.verbose:
853 sys.stdout.write("\n")
854 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
855 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
856 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
857 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
858 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
859 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
860
861 def testSTARTTLS (self):
862
863 msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4")
864
865 server = ThreadedEchoServer(TESTPORT, CERTFILE,
866 ssl_version=ssl.PROTOCOL_TLSv1,
867 starttls_server=True,
868 chatty=True,
869 connectionchatty=True)
870 flag = threading.Event()
871 server.start(flag)
872 # wait for it to start
873 flag.wait()
874 # try to connect
875 wrapped = False
876 try:
877 try:
878 s = socket.socket()
879 s.setblocking(1)
880 s.connect(('127.0.0.1', TESTPORT))
881 except Exception as x:
882 raise test_support.TestFailed("Unexpected exception: " + str(x))
883 else:
884 if test_support.verbose:
885 sys.stdout.write("\n")
886 for indata in msgs:
Bill Janssen6e027db2007-11-15 22:23:56 +0000887 msg = indata.encode('ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000888 if test_support.verbose:
889 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000890 " client: sending %s...\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000891 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000892 conn.write(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000893 outdata = conn.read()
894 else:
Bill Janssen6e027db2007-11-15 22:23:56 +0000895 s.send(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000896 outdata = s.recv(1024)
897 if (indata == "STARTTLS" and
Bill Janssen6e027db2007-11-15 22:23:56 +0000898 str(outdata, 'ASCII', 'replace').strip().lower().startswith("ok")):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000899 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000900 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000901 sys.stdout.write(
902 " client: read %s from server, starting TLS...\n"
Bill Janssen6e027db2007-11-15 22:23:56 +0000903 % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000904 conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
905
906 wrapped = True
907 else:
908 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000909 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000910 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000911 " client: read %s from server\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000912 if test_support.verbose:
913 sys.stdout.write(" client: closing connection.\n")
914 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000915 conn.write("over\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000916 else:
917 s.send("over\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000918 if wrapped:
919 conn.close()
920 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000921 s.close()
922 finally:
923 server.stop()
924 server.join()
925
Bill Janssen54cc54c2007-12-14 22:08:56 +0000926 def testSocketServer(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000927
Bill Janssen54cc54c2007-12-14 22:08:56 +0000928 server = OurHTTPSServer(TESTPORT, CERTFILE)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000929 flag = threading.Event()
930 server.start(flag)
931 # wait for it to start
932 flag.wait()
933 # try to connect
934 try:
935 if test_support.verbose:
936 sys.stdout.write('\n')
937 d1 = open(CERTFILE, 'rb').read()
938 d2 = ''
939 # now fetch the same data from the HTTPS server
940 url = 'https://127.0.0.1:%d/%s' % (
941 TESTPORT, os.path.split(CERTFILE)[1])
942 f = urllib.urlopen(url)
943 dlen = f.info().getheader("content-length")
944 if dlen and (int(dlen) > 0):
945 d2 = f.read(int(dlen))
946 if test_support.verbose:
947 sys.stdout.write(
948 " client: read %d bytes from remote server '%s'\n"
949 % (len(d2), server))
950 f.close()
951 except:
952 msg = ''.join(traceback.format_exception(*sys.exc_info()))
953 if test_support.verbose:
954 sys.stdout.write('\n' + msg)
955 raise test_support.TestFailed(msg)
956 else:
957 if not (d1 == d2):
Bill Janssen6e027db2007-11-15 22:23:56 +0000958 print("d1 is", len(d1), repr(d1))
959 print("d2 is", len(d2), repr(d2))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000960 raise test_support.TestFailed(
961 "Couldn't fetch data from HTTPS server")
962 finally:
963 server.stop()
964 server.join()
965
Bill Janssen54cc54c2007-12-14 22:08:56 +0000966 def testAsyncoreServer(self):
967
968 if test_support.verbose:
969 sys.stdout.write("\n")
970
971 indata="FOO\n"
972 server = AsyncoreEchoServer(TESTPORT, CERTFILE)
973 flag = threading.Event()
974 server.start(flag)
975 # wait for it to start
976 flag.wait()
977 # try to connect
978 try:
979 s = ssl.wrap_socket(socket.socket())
980 s.connect(('127.0.0.1', TESTPORT))
981 except ssl.SSLError as x:
982 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
983 except Exception as x:
984 raise test_support.TestFailed("Unexpected exception: " + str(x))
985 else:
986 if test_support.verbose:
987 sys.stdout.write(
988 " client: sending %s...\n" % (repr(indata)))
989 s.sendall(indata.encode('ASCII', 'strict'))
990 outdata = s.recv()
991 if test_support.verbose:
992 sys.stdout.write(" client: read %s\n" % repr(outdata))
993 outdata = str(outdata, 'ASCII', 'strict')
994 if outdata != indata.lower():
995 raise test_support.TestFailed(
996 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
997 % (repr(outdata[:min(len(outdata),20)]), len(outdata),
998 repr(indata[:min(len(indata),20)].lower()), len(indata)))
999 s.write("over\n".encode("ASCII", "strict"))
1000 if test_support.verbose:
1001 sys.stdout.write(" client: closing connection.\n")
1002 s.close()
1003 finally:
1004 server.stop()
1005 server.join()
1006
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001007
1008def findtestsocket(start, end):
1009 def testbind(i):
1010 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1011 try:
1012 s.bind(("127.0.0.1", i))
1013 except:
1014 return 0
1015 else:
1016 return 1
1017 finally:
1018 s.close()
1019
1020 for i in range(start, end):
1021 if testbind(i) and testbind(i+1):
1022 return i
1023 return 0
Thomas Woutersed03b412007-08-28 21:37:11 +00001024
1025
1026def test_main(verbose=False):
1027 if skip_expected:
Thomas Wouters89d996e2007-09-08 17:39:28 +00001028 raise test_support.TestSkipped("No SSL support")
Thomas Woutersed03b412007-08-28 21:37:11 +00001029
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001030 global CERTFILE, TESTPORT, SVN_PYTHON_ORG_ROOT_CERT
Thomas Woutersed03b412007-08-28 21:37:11 +00001031 CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
1032 "keycert.pem")
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001033 SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
1034 os.path.dirname(__file__) or os.curdir,
1035 "https_svn_python_org_root.pem")
1036
1037 if (not os.path.exists(CERTFILE) or
1038 not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
1039 raise test_support.TestFailed("Can't read certificate files!")
Bill Janssen6e027db2007-11-15 22:23:56 +00001040
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001041 TESTPORT = findtestsocket(10025, 12000)
1042 if not TESTPORT:
1043 raise test_support.TestFailed("Can't find open port to test servers on!")
Thomas Woutersed03b412007-08-28 21:37:11 +00001044
1045 tests = [BasicTests]
1046
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001047 if test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +00001048 tests.append(NetworkedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +00001049
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001050 if _have_threads:
1051 thread_info = test_support.threading_setup()
1052 if thread_info and test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +00001053 tests.append(ThreadedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +00001054
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001055 test_support.run_unittest(*tests)
Thomas Woutersed03b412007-08-28 21:37:11 +00001056
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001057 if _have_threads:
1058 test_support.threading_cleanup(*thread_info)
Thomas Woutersed03b412007-08-28 21:37:11 +00001059
1060if __name__ == "__main__":
1061 test_main()