blob: 81943a5c99c927c4cb84db5a5b9a9588eee6636d [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
Thomas Wouters1b7f8912007-09-19 03:06:30 +000041 def testCrucialConstants(self):
42 ssl.PROTOCOL_SSLv2
43 ssl.PROTOCOL_SSLv23
44 ssl.PROTOCOL_SSLv3
45 ssl.PROTOCOL_TLSv1
46 ssl.CERT_NONE
47 ssl.CERT_OPTIONAL
48 ssl.CERT_REQUIRED
Thomas Woutersed03b412007-08-28 21:37:11 +000049
Thomas Wouters1b7f8912007-09-19 03:06:30 +000050 def testRAND(self):
51 v = ssl.RAND_status()
52 if test_support.verbose:
53 sys.stdout.write("\n RAND_status is %d (%s)\n"
54 % (v, (v and "sufficient randomness") or
55 "insufficient randomness"))
Thomas Woutersed03b412007-08-28 21:37:11 +000056 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000057 ssl.RAND_egd(1)
58 except TypeError:
59 pass
Thomas Woutersed03b412007-08-28 21:37:11 +000060 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000061 print("didn't raise TypeError")
62 ssl.RAND_add("this is a random string", 75.0)
Thomas Woutersed03b412007-08-28 21:37:11 +000063
Thomas Wouters1b7f8912007-09-19 03:06:30 +000064 def testParseCert(self):
65 # note that this uses an 'unofficial' function in _ssl.c,
66 # provided solely for this test, to exercise the certificate
67 # parsing code
68 p = ssl._ssl._test_decode_cert(CERTFILE, False)
69 if test_support.verbose:
70 sys.stdout.write("\n" + pprint.pformat(p) + "\n")
Thomas Woutersed03b412007-08-28 21:37:11 +000071
Thomas Wouters1b7f8912007-09-19 03:06:30 +000072 def testDERtoPEM(self):
73
74 pem = open(SVN_PYTHON_ORG_ROOT_CERT, 'r').read()
75 d1 = ssl.PEM_cert_to_DER_cert(pem)
76 p2 = ssl.DER_cert_to_PEM_cert(d1)
77 d2 = ssl.PEM_cert_to_DER_cert(p2)
78 if (d1 != d2):
79 raise test_support.TestFailed("PEM-to-DER or DER-to-PEM translation failed")
80
Bill Janssen6e027db2007-11-15 22:23:56 +000081class NetworkedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +000082
Thomas Wouters1b7f8912007-09-19 03:06:30 +000083 def testConnect(self):
Bill Janssen6e027db2007-11-15 22:23:56 +000084
Thomas Wouters1b7f8912007-09-19 03:06:30 +000085 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
86 cert_reqs=ssl.CERT_NONE)
87 s.connect(("svn.python.org", 443))
88 c = s.getpeercert()
89 if c:
90 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
91 s.close()
92
93 # this should fail because we have no verification certs
94 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
95 cert_reqs=ssl.CERT_REQUIRED)
Thomas Woutersed03b412007-08-28 21:37:11 +000096 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000097 s.connect(("svn.python.org", 443))
98 except ssl.SSLError:
99 pass
100 finally:
101 s.close()
102
103 # this should succeed because we specify the root cert
104 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
105 cert_reqs=ssl.CERT_REQUIRED,
106 ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
107 try:
108 s.connect(("svn.python.org", 443))
109 except ssl.SSLError as x:
110 raise test_support.TestFailed("Unexpected exception %s" % x)
111 finally:
112 s.close()
113
Bill Janssen6e027db2007-11-15 22:23:56 +0000114 def testNonBlockingHandshake(self):
115 s = socket.socket(socket.AF_INET)
116 s.connect(("svn.python.org", 443))
117 s.setblocking(False)
118 s = ssl.wrap_socket(s,
119 cert_reqs=ssl.CERT_NONE,
120 do_handshake_on_connect=False)
121 count = 0
122 while True:
123 try:
124 count += 1
125 s.do_handshake()
126 break
127 except ssl.SSLError as err:
128 if err.args[0] == ssl.SSL_ERROR_WANT_READ:
129 select.select([s], [], [])
130 elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
131 select.select([], [s], [])
132 else:
133 raise
134 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000135 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000136 sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000137
Bill Janssen54cc54c2007-12-14 22:08:56 +0000138 def testFetchServerCert(self):
139
140 pem = ssl.get_server_certificate(("svn.python.org", 443))
141 if not pem:
142 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
143
144 return
145
146 try:
147 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE)
148 except ssl.SSLError as x:
149 #should fail
150 if test_support.verbose:
151 sys.stdout.write("%s\n" % x)
152 else:
153 raise test_support.TestFailed("Got server certificate %s for svn.python.org!" % pem)
154
155 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
156 if not pem:
157 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
158 if test_support.verbose:
159 sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem)
160
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000161
162try:
163 import threading
164except ImportError:
165 _have_threads = False
166else:
167
168 _have_threads = True
169
170 class ThreadedEchoServer(threading.Thread):
171
172 class ConnectionHandler(threading.Thread):
173
174 """A mildly complicated class, because we want it to work both
175 with and without the SSL wrapper around the socket connection, so
176 that we can test the STARTTLS functionality."""
177
Bill Janssen6e027db2007-11-15 22:23:56 +0000178 def __init__(self, server, connsock, addr):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000179 self.server = server
180 self.running = False
181 self.sock = connsock
Bill Janssen6e027db2007-11-15 22:23:56 +0000182 self.addr = addr
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000183 self.sock.setblocking(1)
184 self.sslconn = None
185 threading.Thread.__init__(self)
186 self.setDaemon(True)
187
188 def wrap_conn (self):
189 try:
190 self.sslconn = ssl.wrap_socket(self.sock, server_side=True,
191 certfile=self.server.certificate,
192 ssl_version=self.server.protocol,
193 ca_certs=self.server.cacerts,
194 cert_reqs=self.server.certreqs)
195 except:
196 if self.server.chatty:
Bill Janssen6e027db2007-11-15 22:23:56 +0000197 handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000198 if not self.server.expect_bad_connects:
199 # here, we want to stop the server, because this shouldn't
200 # happen in the context of our test case
201 self.running = False
202 # normally, we'd just stop here, but for the test
203 # harness, we want to stop the server
204 self.server.stop()
Bill Janssen6e027db2007-11-15 22:23:56 +0000205 self.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000206 return False
207
208 else:
209 if self.server.certreqs == ssl.CERT_REQUIRED:
210 cert = self.sslconn.getpeercert()
211 if test_support.verbose and self.server.chatty:
212 sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
213 cert_binary = self.sslconn.getpeercert(True)
214 if test_support.verbose and self.server.chatty:
215 sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n")
216 cipher = self.sslconn.cipher()
217 if test_support.verbose and self.server.chatty:
218 sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n")
219 return True
220
221 def read(self):
222 if self.sslconn:
223 return self.sslconn.read()
224 else:
225 return self.sock.recv(1024)
226
227 def write(self, bytes):
228 if self.sslconn:
229 return self.sslconn.write(bytes)
230 else:
231 return self.sock.send(bytes)
232
233 def close(self):
234 if self.sslconn:
235 self.sslconn.close()
236 else:
237 self.sock.close()
238
239 def run (self):
240 self.running = True
241 if not self.server.starttls_server:
242 if not self.wrap_conn():
243 return
244 while self.running:
245 try:
246 msg = self.read()
Bill Janssen6e027db2007-11-15 22:23:56 +0000247 amsg = (msg and str(msg, 'ASCII', 'strict')) or ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000248 if not msg:
249 # eof, so quit this handler
250 self.running = False
251 self.close()
Bill Janssen6e027db2007-11-15 22:23:56 +0000252 elif amsg.strip() == 'over':
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000253 if test_support.verbose and self.server.connectionchatty:
254 sys.stdout.write(" server: client closed connection\n")
255 self.close()
256 return
Bill Janssen6e027db2007-11-15 22:23:56 +0000257 elif (self.server.starttls_server and
258 amsg.strip() == 'STARTTLS'):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000259 if test_support.verbose and self.server.connectionchatty:
260 sys.stdout.write(" server: read STARTTLS from client, sending OK...\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000261 self.write("OK\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000262 if not self.wrap_conn():
263 return
264 else:
265 if (test_support.verbose and
266 self.server.connectionchatty):
267 ctype = (self.sslconn and "encrypted") or "unencrypted"
268 sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n"
269 % (repr(msg), ctype, repr(msg.lower()), ctype))
Bill Janssen6e027db2007-11-15 22:23:56 +0000270 self.write(amsg.lower().encode('ASCII', 'strict'))
271 except socket.error:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000272 if self.server.chatty:
273 handle_error("Test server failure:\n")
274 self.close()
275 self.running = False
276 # normally, we'd just stop here, but for the test
277 # harness, we want to stop the server
278 self.server.stop()
279 except:
280 handle_error('')
281
282 def __init__(self, port, certificate, ssl_version=None,
283 certreqs=None, cacerts=None, expect_bad_connects=False,
284 chatty=True, connectionchatty=False, starttls_server=False):
285 if ssl_version is None:
286 ssl_version = ssl.PROTOCOL_TLSv1
287 if certreqs is None:
288 certreqs = ssl.CERT_NONE
289 self.certificate = certificate
290 self.protocol = ssl_version
291 self.certreqs = certreqs
292 self.cacerts = cacerts
293 self.expect_bad_connects = expect_bad_connects
294 self.chatty = chatty
295 self.connectionchatty = connectionchatty
296 self.starttls_server = starttls_server
297 self.sock = socket.socket()
298 self.flag = None
299 if hasattr(socket, 'SO_REUSEADDR'):
300 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
301 if hasattr(socket, 'SO_REUSEPORT'):
302 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
303 self.sock.bind(('127.0.0.1', port))
304 self.active = False
305 threading.Thread.__init__(self)
306 self.setDaemon(False)
307
308 def start (self, flag=None):
309 self.flag = flag
310 threading.Thread.start(self)
311
312 def run (self):
313 self.sock.settimeout(0.5)
314 self.sock.listen(5)
315 self.active = True
316 if self.flag:
317 # signal an event
318 self.flag.set()
319 while self.active:
320 try:
321 newconn, connaddr = self.sock.accept()
322 if test_support.verbose and self.chatty:
323 sys.stdout.write(' server: new connection from '
Bill Janssen6e027db2007-11-15 22:23:56 +0000324 + repr(connaddr) + '\n')
325 handler = self.ConnectionHandler(self, newconn, connaddr)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000326 handler.start()
327 except socket.timeout:
328 pass
329 except KeyboardInterrupt:
330 self.stop()
331 except:
332 if self.chatty:
333 handle_error("Test server failure:\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000334 self.sock.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000335
336 def stop (self):
337 self.active = False
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000338
Bill Janssen54cc54c2007-12-14 22:08:56 +0000339 class OurHTTPSServer(threading.Thread):
340
341 # This one's based on HTTPServer, which is based on SocketServer
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000342
343 class HTTPSServer(HTTPServer):
344
345 def __init__(self, server_address, RequestHandlerClass, certfile):
346
347 HTTPServer.__init__(self, server_address, RequestHandlerClass)
348 # we assume the certfile contains both private key and certificate
349 self.certfile = certfile
350 self.active = False
351 self.allow_reuse_address = True
352
Bill Janssen6e027db2007-11-15 22:23:56 +0000353 def __str__(self):
354 return ('<%s %s:%s>' %
355 (self.__class__.__name__,
356 self.server_name,
357 self.server_port))
358
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000359 def get_request (self):
360 # override this to wrap socket with SSL
361 sock, addr = self.socket.accept()
362 sslconn = ssl.wrap_socket(sock, server_side=True,
363 certfile=self.certfile)
364 return sslconn, addr
365
366 # The methods overridden below this are mainly so that we
367 # can run it in a thread and be able to stop it from another
368 # You probably wouldn't need them in other uses.
369
370 def server_activate(self):
371 # We want to run this in a thread for testing purposes,
372 # so we override this to set timeout, so that we get
373 # a chance to stop the server
374 self.socket.settimeout(0.5)
375 HTTPServer.server_activate(self)
376
377 def serve_forever(self):
378 # We want this to run in a thread, so we use a slightly
379 # modified version of "forever".
380 self.active = True
381 while self.active:
382 try:
383 self.handle_request()
384 except socket.timeout:
385 pass
386 except KeyboardInterrupt:
387 self.server_close()
388 return
389 except:
390 sys.stdout.write(''.join(traceback.format_exception(*sys.exc_info())));
391
392 def server_close(self):
393 # Again, we want this to run in a thread, so we need to override
394 # close to clear the "active" flag, so that serve_forever() will
395 # terminate.
396 HTTPServer.server_close(self)
397 self.active = False
398
399 class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):
400
401 # need to override translate_path to get a known root,
402 # instead of using os.curdir, since the test could be
403 # run from anywhere
404
405 server_version = "TestHTTPS/1.0"
406
407 root = None
408
409 def translate_path(self, path):
410 """Translate a /-separated PATH to the local filename syntax.
411
412 Components that mean special things to the local file system
413 (e.g. drive or directory names) are ignored. (XXX They should
414 probably be diagnosed.)
415
416 """
417 # abandon query parameters
418 path = urlparse.urlparse(path)[2]
419 path = os.path.normpath(urllib.unquote(path))
420 words = path.split('/')
421 words = filter(None, words)
422 path = self.root
423 for word in words:
424 drive, word = os.path.splitdrive(word)
425 head, word = os.path.split(word)
426 if word in self.root: continue
427 path = os.path.join(path, word)
428 return path
429
430 def log_message(self, format, *args):
431
432 # we override this to suppress logging unless "verbose"
433
Thomas Wouters89d996e2007-09-08 17:39:28 +0000434 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000435 sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" %
436 (self.server.server_address,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000437 self.server.server_port,
438 self.request.cipher(),
439 self.log_date_time_string(),
440 format%args))
Thomas Woutersed03b412007-08-28 21:37:11 +0000441
442
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000443 def __init__(self, port, certfile):
444 self.flag = None
445 self.active = False
446 self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0]
447 self.server = self.HTTPSServer(
448 ('', port), self.RootedHTTPRequestHandler, certfile)
Thomas Woutersed03b412007-08-28 21:37:11 +0000449 threading.Thread.__init__(self)
450 self.setDaemon(True)
451
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000452 def __str__(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000453 return "<%s %s>" % (self.__class__.__name__, self.server)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000454
455 def start (self, flag=None):
456 self.flag = flag
457 threading.Thread.start(self)
458
Thomas Woutersed03b412007-08-28 21:37:11 +0000459 def run (self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000460 self.active = True
461 if self.flag:
462 self.flag.set()
463 self.server.serve_forever()
464 self.active = False
465
466 def stop (self):
467 self.active = False
468 self.server.server_close()
469
470
Bill Janssen54cc54c2007-12-14 22:08:56 +0000471 class AsyncoreEchoServer(threading.Thread):
472
473 # this one's based on asyncore.dispatcher
474
475 class EchoServer (asyncore.dispatcher):
476
477 class ConnectionHandler (asyncore.dispatcher_with_send):
478
479 def __init__(self, conn, certfile):
480 self.socket = ssl.wrap_socket(conn, server_side=True,
481 certfile=certfile,
482 do_handshake_on_connect=False)
483 asyncore.dispatcher_with_send.__init__(self, self.socket)
484 # now we have to do the handshake
485 # we'll just do it the easy way, and block the connection
486 # till it's finished. If we were doing it right, we'd
487 # do this in multiple calls to handle_read...
488 self.do_handshake(block=True)
489
490 def readable(self):
491 if isinstance(self.socket, ssl.SSLSocket):
492 while self.socket.pending() > 0:
493 self.handle_read_event()
494 return True
495
496 def handle_read(self):
497 data = self.recv(1024)
498 if test_support.verbose:
499 sys.stdout.write(" server: read %s from client\n" % repr(data))
500 if not data:
501 self.close()
502 else:
503 self.send(str(data, 'ASCII', 'strict').lower().encode('ASCII', 'strict'))
504
505 def handle_close(self):
506 if test_support.verbose:
507 sys.stdout.write(" server: closed connection %s\n" % self.socket)
508
509 def handle_error(self):
510 raise
511
512 def __init__(self, port, certfile):
513 self.port = port
514 self.certfile = certfile
515 asyncore.dispatcher.__init__(self)
516 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
517 self.bind(('', port))
518 self.listen(5)
519
520 def handle_accept(self):
521 sock_obj, addr = self.accept()
522 if test_support.verbose:
523 sys.stdout.write(" server: new connection from %s:%s\n" %addr)
524 self.ConnectionHandler(sock_obj, self.certfile)
525
526 def handle_error(self):
527 raise
528
529 def __init__(self, port, certfile):
530 self.flag = None
531 self.active = False
532 self.server = self.EchoServer(port, certfile)
533 threading.Thread.__init__(self)
534 self.setDaemon(True)
535
536 def __str__(self):
537 return "<%s %s>" % (self.__class__.__name__, self.server)
538
539 def start (self, flag=None):
540 self.flag = flag
541 threading.Thread.start(self)
542
543 def run (self):
544 self.active = True
545 if self.flag:
546 self.flag.set()
547 while self.active:
548 try:
549 asyncore.loop(1)
550 except:
551 pass
552
553 def stop (self):
554 self.active = False
555 self.server.close()
556
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000557 def badCertTest (certfile):
558 server = ThreadedEchoServer(TESTPORT, CERTFILE,
559 certreqs=ssl.CERT_REQUIRED,
Bill Janssen6e027db2007-11-15 22:23:56 +0000560 cacerts=CERTFILE, chatty=False,
561 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000562 flag = threading.Event()
563 server.start(flag)
564 # wait for it to start
565 flag.wait()
566 # try to connect
567 try:
Thomas Woutersed03b412007-08-28 21:37:11 +0000568 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000569 s = ssl.wrap_socket(socket.socket(),
570 certfile=certfile,
571 ssl_version=ssl.PROTOCOL_TLSv1)
572 s.connect(('127.0.0.1', TESTPORT))
573 except ssl.SSLError as x:
Thomas Woutersed03b412007-08-28 21:37:11 +0000574 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000575 sys.stdout.write("\nSSLError is %s\n" % x)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000576 else:
577 raise test_support.TestFailed(
578 "Use of invalid cert should have failed!")
579 finally:
580 server.stop()
581 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000582
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000583 def serverParamsTest (certfile, protocol, certreqs, cacertsfile,
Bill Janssen6e027db2007-11-15 22:23:56 +0000584 client_certfile, client_protocol=None,
585 indata="FOO\n",
586 chatty=False, connectionchatty=False):
Thomas Woutersed03b412007-08-28 21:37:11 +0000587
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000588 server = ThreadedEchoServer(TESTPORT, certfile,
589 certreqs=certreqs,
590 ssl_version=protocol,
591 cacerts=cacertsfile,
592 chatty=chatty,
Bill Janssen6e027db2007-11-15 22:23:56 +0000593 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000594 flag = threading.Event()
595 server.start(flag)
596 # wait for it to start
597 flag.wait()
598 # try to connect
599 if client_protocol is None:
600 client_protocol = protocol
601 try:
Bill Janssen6e027db2007-11-15 22:23:56 +0000602 s = ssl.wrap_socket(socket.socket(),
Bill Janssen54cc54c2007-12-14 22:08:56 +0000603 server_side=False,
Bill Janssen6e027db2007-11-15 22:23:56 +0000604 certfile=client_certfile,
605 ca_certs=cacertsfile,
606 cert_reqs=certreqs,
607 ssl_version=client_protocol)
608 s.connect(('127.0.0.1', TESTPORT))
609 except ssl.SSLError as x:
610 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
611 except Exception as x:
612 raise test_support.TestFailed("Unexpected exception: " + str(x))
613 else:
614 if connectionchatty:
615 if test_support.verbose:
616 sys.stdout.write(
617 " client: sending %s...\n" % (repr(indata)))
618 s.write(indata.encode('ASCII', 'strict'))
619 outdata = s.read()
620 if connectionchatty:
621 if test_support.verbose:
622 sys.stdout.write(" client: read %s\n" % repr(outdata))
623 outdata = str(outdata, 'ASCII', 'strict')
624 if outdata != indata.lower():
625 raise test_support.TestFailed(
626 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
627 % (repr(outdata[:min(len(outdata),20)]), len(outdata),
628 repr(indata[:min(len(indata),20)].lower()), len(indata)))
629 s.write("over\n".encode("ASCII", "strict"))
630 if connectionchatty:
631 if test_support.verbose:
632 sys.stdout.write(" client: closing connection.\n")
633 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000634 finally:
635 server.stop()
636 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000637
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000638 def tryProtocolCombo (server_protocol,
639 client_protocol,
640 expectedToWork,
641 certsreqs=None):
Thomas Woutersed03b412007-08-28 21:37:11 +0000642
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000643 if certsreqs == None:
644 certsreqs = ssl.CERT_NONE
Thomas Woutersed03b412007-08-28 21:37:11 +0000645
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000646 if certsreqs == ssl.CERT_NONE:
647 certtype = "CERT_NONE"
648 elif certsreqs == ssl.CERT_OPTIONAL:
649 certtype = "CERT_OPTIONAL"
650 elif certsreqs == ssl.CERT_REQUIRED:
651 certtype = "CERT_REQUIRED"
Thomas Woutersed03b412007-08-28 21:37:11 +0000652 if test_support.verbose:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000653 formatstr = (expectedToWork and " %s->%s %s\n") or " {%s->%s} %s\n"
654 sys.stdout.write(formatstr %
655 (ssl.get_protocol_name(client_protocol),
656 ssl.get_protocol_name(server_protocol),
657 certtype))
658 try:
659 serverParamsTest(CERTFILE, server_protocol, certsreqs,
Bill Janssen6e027db2007-11-15 22:23:56 +0000660 CERTFILE, CERTFILE, client_protocol,
661 chatty=False, connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000662 except test_support.TestFailed:
663 if expectedToWork:
664 raise
665 else:
666 if not expectedToWork:
667 raise test_support.TestFailed(
668 "Client protocol %s succeeded with server protocol %s!"
669 % (ssl.get_protocol_name(client_protocol),
670 ssl.get_protocol_name(server_protocol)))
671
672
Bill Janssen6e027db2007-11-15 22:23:56 +0000673 class ThreadedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000674
675 def testEcho (self):
676
677 if test_support.verbose:
678 sys.stdout.write("\n")
679 serverParamsTest(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,
680 CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,
681 chatty=True, connectionchatty=True)
682
683 def testReadCert(self):
684
685 if test_support.verbose:
686 sys.stdout.write("\n")
687 s2 = socket.socket()
688 server = ThreadedEchoServer(TESTPORT, CERTFILE,
689 certreqs=ssl.CERT_NONE,
690 ssl_version=ssl.PROTOCOL_SSLv23,
691 cacerts=CERTFILE,
692 chatty=False)
693 flag = threading.Event()
694 server.start(flag)
695 # wait for it to start
696 flag.wait()
697 # try to connect
698 try:
699 try:
700 s = ssl.wrap_socket(socket.socket(),
701 certfile=CERTFILE,
702 ca_certs=CERTFILE,
703 cert_reqs=ssl.CERT_REQUIRED,
704 ssl_version=ssl.PROTOCOL_SSLv23)
705 s.connect(('127.0.0.1', TESTPORT))
706 except ssl.SSLError as x:
707 raise test_support.TestFailed(
708 "Unexpected SSL error: " + str(x))
709 except Exception as x:
710 raise test_support.TestFailed(
711 "Unexpected exception: " + str(x))
712 else:
713 if not s:
714 raise test_support.TestFailed(
715 "Can't SSL-handshake with test server")
716 cert = s.getpeercert()
717 if not cert:
718 raise test_support.TestFailed(
719 "Can't get peer certificate.")
720 cipher = s.cipher()
721 if test_support.verbose:
722 sys.stdout.write(pprint.pformat(cert) + '\n')
723 sys.stdout.write("Connection cipher is " + str(cipher) + '.\n')
Bill Janssen6e027db2007-11-15 22:23:56 +0000724 if 'subject' not in cert:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000725 raise test_support.TestFailed(
726 "No subject field in certificate: %s." %
727 pprint.pformat(cert))
728 if ((('organizationName', 'Python Software Foundation'),)
729 not in cert['subject']):
730 raise test_support.TestFailed(
731 "Missing or invalid 'organizationName' field in certificate subject; "
732 "should be 'Python Software Foundation'.");
733 s.close()
734 finally:
735 server.stop()
736 server.join()
737
738 def testNULLcert(self):
739 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
740 "nullcert.pem"))
741 def testMalformedCert(self):
742 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
743 "badcert.pem"))
744 def testMalformedKey(self):
745 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
746 "badkey.pem"))
747
Bill Janssen6e027db2007-11-15 22:23:56 +0000748 def testRudeShutdown(self):
749
750 listener_ready = threading.Event()
751 listener_gone = threading.Event()
752
753 # `listener` runs in a thread. It opens a socket listening on
754 # PORT, and sits in an accept() until the main thread connects.
755 # Then it rudely closes the socket, and sets Event `listener_gone`
756 # to let the main thread know the socket is gone.
757 def listener():
758 s = socket.socket()
759 if hasattr(socket, 'SO_REUSEADDR'):
760 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
761 if hasattr(socket, 'SO_REUSEPORT'):
762 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
763 s.bind(('127.0.0.1', TESTPORT))
764 s.listen(5)
765 listener_ready.set()
766 s.accept()
767 s = None # reclaim the socket object, which also closes it
768 listener_gone.set()
769
770 def connector():
771 listener_ready.wait()
772 s = socket.socket()
773 s.connect(('127.0.0.1', TESTPORT))
774 listener_gone.wait()
775 try:
776 ssl_sock = ssl.wrap_socket(s)
777 except IOError:
778 pass
779 else:
780 raise test_support.TestFailed(
781 'connecting to closed SSL socket should have failed')
782
783 t = threading.Thread(target=listener)
784 t.start()
785 connector()
786 t.join()
787
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000788 def testProtocolSSL2(self):
789 if test_support.verbose:
790 sys.stdout.write("\n")
791 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True)
792 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL)
793 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED)
794 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
795 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
796 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
797
798 def testProtocolSSL23(self):
799 if test_support.verbose:
800 sys.stdout.write("\n")
801 try:
802 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True)
803 except test_support.TestFailed as x:
804 # this fails on some older versions of OpenSSL (0.9.7l, for instance)
805 if test_support.verbose:
806 sys.stdout.write(
807 " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"
808 % str(x))
809 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)
810 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)
811 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)
812
813 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
814 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)
815 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
816
817 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
818 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
819 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
820
821 def testProtocolSSL3(self):
822 if test_support.verbose:
823 sys.stdout.write("\n")
824 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
825 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
826 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
827 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
828 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
829 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
830
831 def testProtocolTLS1(self):
832 if test_support.verbose:
833 sys.stdout.write("\n")
834 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
835 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
836 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
837 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
838 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
839 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
840
841 def testSTARTTLS (self):
842
843 msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4")
844
845 server = ThreadedEchoServer(TESTPORT, CERTFILE,
846 ssl_version=ssl.PROTOCOL_TLSv1,
847 starttls_server=True,
848 chatty=True,
849 connectionchatty=True)
850 flag = threading.Event()
851 server.start(flag)
852 # wait for it to start
853 flag.wait()
854 # try to connect
855 wrapped = False
856 try:
857 try:
858 s = socket.socket()
859 s.setblocking(1)
860 s.connect(('127.0.0.1', TESTPORT))
861 except Exception as x:
862 raise test_support.TestFailed("Unexpected exception: " + str(x))
863 else:
864 if test_support.verbose:
865 sys.stdout.write("\n")
866 for indata in msgs:
Bill Janssen6e027db2007-11-15 22:23:56 +0000867 msg = indata.encode('ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000868 if test_support.verbose:
869 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000870 " client: sending %s...\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000871 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000872 conn.write(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000873 outdata = conn.read()
874 else:
Bill Janssen6e027db2007-11-15 22:23:56 +0000875 s.send(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000876 outdata = s.recv(1024)
877 if (indata == "STARTTLS" and
Bill Janssen6e027db2007-11-15 22:23:56 +0000878 str(outdata, 'ASCII', 'replace').strip().lower().startswith("ok")):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000879 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000880 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000881 sys.stdout.write(
882 " client: read %s from server, starting TLS...\n"
Bill Janssen6e027db2007-11-15 22:23:56 +0000883 % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000884 conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
885
886 wrapped = True
887 else:
888 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000889 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000890 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000891 " client: read %s from server\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000892 if test_support.verbose:
893 sys.stdout.write(" client: closing connection.\n")
894 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000895 conn.write("over\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000896 else:
897 s.send("over\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000898 if wrapped:
899 conn.close()
900 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000901 s.close()
902 finally:
903 server.stop()
904 server.join()
905
Bill Janssen54cc54c2007-12-14 22:08:56 +0000906 def testSocketServer(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000907
Bill Janssen54cc54c2007-12-14 22:08:56 +0000908 server = OurHTTPSServer(TESTPORT, CERTFILE)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000909 flag = threading.Event()
910 server.start(flag)
911 # wait for it to start
912 flag.wait()
913 # try to connect
914 try:
915 if test_support.verbose:
916 sys.stdout.write('\n')
917 d1 = open(CERTFILE, 'rb').read()
918 d2 = ''
919 # now fetch the same data from the HTTPS server
920 url = 'https://127.0.0.1:%d/%s' % (
921 TESTPORT, os.path.split(CERTFILE)[1])
922 f = urllib.urlopen(url)
923 dlen = f.info().getheader("content-length")
924 if dlen and (int(dlen) > 0):
925 d2 = f.read(int(dlen))
926 if test_support.verbose:
927 sys.stdout.write(
928 " client: read %d bytes from remote server '%s'\n"
929 % (len(d2), server))
930 f.close()
931 except:
932 msg = ''.join(traceback.format_exception(*sys.exc_info()))
933 if test_support.verbose:
934 sys.stdout.write('\n' + msg)
935 raise test_support.TestFailed(msg)
936 else:
937 if not (d1 == d2):
Bill Janssen6e027db2007-11-15 22:23:56 +0000938 print("d1 is", len(d1), repr(d1))
939 print("d2 is", len(d2), repr(d2))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000940 raise test_support.TestFailed(
941 "Couldn't fetch data from HTTPS server")
942 finally:
943 server.stop()
944 server.join()
945
Bill Janssen54cc54c2007-12-14 22:08:56 +0000946 def testAsyncoreServer(self):
947
948 if test_support.verbose:
949 sys.stdout.write("\n")
950
951 indata="FOO\n"
952 server = AsyncoreEchoServer(TESTPORT, CERTFILE)
953 flag = threading.Event()
954 server.start(flag)
955 # wait for it to start
956 flag.wait()
957 # try to connect
958 try:
959 s = ssl.wrap_socket(socket.socket())
960 s.connect(('127.0.0.1', TESTPORT))
961 except ssl.SSLError as x:
962 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
963 except Exception as x:
964 raise test_support.TestFailed("Unexpected exception: " + str(x))
965 else:
966 if test_support.verbose:
967 sys.stdout.write(
968 " client: sending %s...\n" % (repr(indata)))
969 s.sendall(indata.encode('ASCII', 'strict'))
970 outdata = s.recv()
971 if test_support.verbose:
972 sys.stdout.write(" client: read %s\n" % repr(outdata))
973 outdata = str(outdata, 'ASCII', 'strict')
974 if outdata != indata.lower():
975 raise test_support.TestFailed(
976 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
977 % (repr(outdata[:min(len(outdata),20)]), len(outdata),
978 repr(indata[:min(len(indata),20)].lower()), len(indata)))
979 s.write("over\n".encode("ASCII", "strict"))
980 if test_support.verbose:
981 sys.stdout.write(" client: closing connection.\n")
982 s.close()
983 finally:
984 server.stop()
985 server.join()
986
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000987
988def findtestsocket(start, end):
989 def testbind(i):
990 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
991 try:
992 s.bind(("127.0.0.1", i))
993 except:
994 return 0
995 else:
996 return 1
997 finally:
998 s.close()
999
1000 for i in range(start, end):
1001 if testbind(i) and testbind(i+1):
1002 return i
1003 return 0
Thomas Woutersed03b412007-08-28 21:37:11 +00001004
1005
1006def test_main(verbose=False):
1007 if skip_expected:
Thomas Wouters89d996e2007-09-08 17:39:28 +00001008 raise test_support.TestSkipped("No SSL support")
Thomas Woutersed03b412007-08-28 21:37:11 +00001009
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001010 global CERTFILE, TESTPORT, SVN_PYTHON_ORG_ROOT_CERT
Thomas Woutersed03b412007-08-28 21:37:11 +00001011 CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
1012 "keycert.pem")
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001013 SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
1014 os.path.dirname(__file__) or os.curdir,
1015 "https_svn_python_org_root.pem")
1016
1017 if (not os.path.exists(CERTFILE) or
1018 not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
1019 raise test_support.TestFailed("Can't read certificate files!")
Bill Janssen6e027db2007-11-15 22:23:56 +00001020
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001021 TESTPORT = findtestsocket(10025, 12000)
1022 if not TESTPORT:
1023 raise test_support.TestFailed("Can't find open port to test servers on!")
Thomas Woutersed03b412007-08-28 21:37:11 +00001024
1025 tests = [BasicTests]
1026
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001027 if test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +00001028 tests.append(NetworkedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +00001029
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001030 if _have_threads:
1031 thread_info = test_support.threading_setup()
1032 if thread_info and test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +00001033 tests.append(ThreadedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +00001034
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001035 test_support.run_unittest(*tests)
Thomas Woutersed03b412007-08-28 21:37:11 +00001036
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001037 if _have_threads:
1038 test_support.threading_cleanup(*thread_info)
Thomas Woutersed03b412007-08-28 21:37:11 +00001039
1040if __name__ == "__main__":
1041 test_main()