blob: 18df3f4422d71659a56748cb772008c08ac6a1cf [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
16
Thomas Wouters1b7f8912007-09-19 03:06:30 +000017from BaseHTTPServer import HTTPServer
18from SimpleHTTPServer import SimpleHTTPRequestHandler
19
Thomas Woutersed03b412007-08-28 21:37:11 +000020# Optionally test SSL support, if we have it in the tested platform
21skip_expected = False
22try:
23 import ssl
24except ImportError:
25 skip_expected = True
26
27CERTFILE = None
Thomas Wouters1b7f8912007-09-19 03:06:30 +000028SVN_PYTHON_ORG_ROOT_CERT = None
Thomas Woutersed03b412007-08-28 21:37:11 +000029
Thomas Wouters1b7f8912007-09-19 03:06:30 +000030TESTPORT = 10025
Thomas Woutersed03b412007-08-28 21:37:11 +000031
32def handle_error(prefix):
33 exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
Thomas Wouters1b7f8912007-09-19 03:06:30 +000034 if test_support.verbose:
35 sys.stdout.write(prefix + exc_format)
Thomas Woutersed03b412007-08-28 21:37:11 +000036
37
38class BasicTests(unittest.TestCase):
39
Thomas Wouters1b7f8912007-09-19 03:06:30 +000040 def testCrucialConstants(self):
41 ssl.PROTOCOL_SSLv2
42 ssl.PROTOCOL_SSLv23
43 ssl.PROTOCOL_SSLv3
44 ssl.PROTOCOL_TLSv1
45 ssl.CERT_NONE
46 ssl.CERT_OPTIONAL
47 ssl.CERT_REQUIRED
Thomas Woutersed03b412007-08-28 21:37:11 +000048
Thomas Wouters1b7f8912007-09-19 03:06:30 +000049 def testRAND(self):
50 v = ssl.RAND_status()
51 if test_support.verbose:
52 sys.stdout.write("\n RAND_status is %d (%s)\n"
53 % (v, (v and "sufficient randomness") or
54 "insufficient randomness"))
Thomas Woutersed03b412007-08-28 21:37:11 +000055 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000056 ssl.RAND_egd(1)
57 except TypeError:
58 pass
Thomas Woutersed03b412007-08-28 21:37:11 +000059 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000060 print("didn't raise TypeError")
61 ssl.RAND_add("this is a random string", 75.0)
Thomas Woutersed03b412007-08-28 21:37:11 +000062
Thomas Wouters1b7f8912007-09-19 03:06:30 +000063 def testParseCert(self):
64 # note that this uses an 'unofficial' function in _ssl.c,
65 # provided solely for this test, to exercise the certificate
66 # parsing code
67 p = ssl._ssl._test_decode_cert(CERTFILE, False)
68 if test_support.verbose:
69 sys.stdout.write("\n" + pprint.pformat(p) + "\n")
Thomas Woutersed03b412007-08-28 21:37:11 +000070
Thomas Wouters1b7f8912007-09-19 03:06:30 +000071 def testDERtoPEM(self):
72
73 pem = open(SVN_PYTHON_ORG_ROOT_CERT, 'r').read()
74 d1 = ssl.PEM_cert_to_DER_cert(pem)
75 p2 = ssl.DER_cert_to_PEM_cert(d1)
76 d2 = ssl.PEM_cert_to_DER_cert(p2)
77 if (d1 != d2):
78 raise test_support.TestFailed("PEM-to-DER or DER-to-PEM translation failed")
79
Bill Janssen6e027db2007-11-15 22:23:56 +000080class NetworkedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +000081
Bill Janssen6e027db2007-11-15 22:23:56 +000082 def testFetchServerCert(self):
83
84 pem = ssl.get_server_certificate(("svn.python.org", 443))
85 if not pem:
86 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
87
88 try:
89 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE)
90 except ssl.SSLError as x:
91 #should fail
92 if test_support.verbose:
93 sys.stdout.write("%s\n" % x)
94 else:
95 raise test_support.TestFailed("Got server certificate %s for svn.python.org!" % pem)
96
97 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
98 if not pem:
99 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
100 if test_support.verbose:
101 sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000102
103 def testConnect(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000104
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
158
159try:
160 import threading
161except ImportError:
162 _have_threads = False
163else:
164
165 _have_threads = True
166
167 class ThreadedEchoServer(threading.Thread):
168
169 class ConnectionHandler(threading.Thread):
170
171 """A mildly complicated class, because we want it to work both
172 with and without the SSL wrapper around the socket connection, so
173 that we can test the STARTTLS functionality."""
174
Bill Janssen6e027db2007-11-15 22:23:56 +0000175 def __init__(self, server, connsock, addr):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000176 self.server = server
177 self.running = False
178 self.sock = connsock
Bill Janssen6e027db2007-11-15 22:23:56 +0000179 self.addr = addr
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000180 self.sock.setblocking(1)
181 self.sslconn = None
182 threading.Thread.__init__(self)
183 self.setDaemon(True)
184
185 def wrap_conn (self):
186 try:
187 self.sslconn = ssl.wrap_socket(self.sock, server_side=True,
188 certfile=self.server.certificate,
189 ssl_version=self.server.protocol,
190 ca_certs=self.server.cacerts,
191 cert_reqs=self.server.certreqs)
192 except:
193 if self.server.chatty:
Bill Janssen6e027db2007-11-15 22:23:56 +0000194 handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000195 if not self.server.expect_bad_connects:
196 # here, we want to stop the server, because this shouldn't
197 # happen in the context of our test case
198 self.running = False
199 # normally, we'd just stop here, but for the test
200 # harness, we want to stop the server
201 self.server.stop()
Bill Janssen6e027db2007-11-15 22:23:56 +0000202 self.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000203 return False
204
205 else:
206 if self.server.certreqs == ssl.CERT_REQUIRED:
207 cert = self.sslconn.getpeercert()
208 if test_support.verbose and self.server.chatty:
209 sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
210 cert_binary = self.sslconn.getpeercert(True)
211 if test_support.verbose and self.server.chatty:
212 sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n")
213 cipher = self.sslconn.cipher()
214 if test_support.verbose and self.server.chatty:
215 sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n")
216 return True
217
218 def read(self):
219 if self.sslconn:
220 return self.sslconn.read()
221 else:
222 return self.sock.recv(1024)
223
224 def write(self, bytes):
225 if self.sslconn:
226 return self.sslconn.write(bytes)
227 else:
228 return self.sock.send(bytes)
229
230 def close(self):
231 if self.sslconn:
232 self.sslconn.close()
233 else:
234 self.sock.close()
235
236 def run (self):
237 self.running = True
238 if not self.server.starttls_server:
239 if not self.wrap_conn():
240 return
241 while self.running:
242 try:
243 msg = self.read()
Bill Janssen6e027db2007-11-15 22:23:56 +0000244 amsg = (msg and str(msg, 'ASCII', 'strict')) or ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000245 if not msg:
246 # eof, so quit this handler
247 self.running = False
248 self.close()
Bill Janssen6e027db2007-11-15 22:23:56 +0000249 elif amsg.strip() == 'over':
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000250 if test_support.verbose and self.server.connectionchatty:
251 sys.stdout.write(" server: client closed connection\n")
252 self.close()
253 return
Bill Janssen6e027db2007-11-15 22:23:56 +0000254 elif (self.server.starttls_server and
255 amsg.strip() == 'STARTTLS'):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000256 if test_support.verbose and self.server.connectionchatty:
257 sys.stdout.write(" server: read STARTTLS from client, sending OK...\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000258 self.write("OK\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000259 if not self.wrap_conn():
260 return
261 else:
262 if (test_support.verbose and
263 self.server.connectionchatty):
264 ctype = (self.sslconn and "encrypted") or "unencrypted"
265 sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n"
266 % (repr(msg), ctype, repr(msg.lower()), ctype))
Bill Janssen6e027db2007-11-15 22:23:56 +0000267 self.write(amsg.lower().encode('ASCII', 'strict'))
268 except socket.error:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000269 if self.server.chatty:
270 handle_error("Test server failure:\n")
271 self.close()
272 self.running = False
273 # normally, we'd just stop here, but for the test
274 # harness, we want to stop the server
275 self.server.stop()
276 except:
277 handle_error('')
278
279 def __init__(self, port, certificate, ssl_version=None,
280 certreqs=None, cacerts=None, expect_bad_connects=False,
281 chatty=True, connectionchatty=False, starttls_server=False):
282 if ssl_version is None:
283 ssl_version = ssl.PROTOCOL_TLSv1
284 if certreqs is None:
285 certreqs = ssl.CERT_NONE
286 self.certificate = certificate
287 self.protocol = ssl_version
288 self.certreqs = certreqs
289 self.cacerts = cacerts
290 self.expect_bad_connects = expect_bad_connects
291 self.chatty = chatty
292 self.connectionchatty = connectionchatty
293 self.starttls_server = starttls_server
294 self.sock = socket.socket()
295 self.flag = None
296 if hasattr(socket, 'SO_REUSEADDR'):
297 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
298 if hasattr(socket, 'SO_REUSEPORT'):
299 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
300 self.sock.bind(('127.0.0.1', port))
301 self.active = False
302 threading.Thread.__init__(self)
303 self.setDaemon(False)
304
305 def start (self, flag=None):
306 self.flag = flag
307 threading.Thread.start(self)
308
309 def run (self):
310 self.sock.settimeout(0.5)
311 self.sock.listen(5)
312 self.active = True
313 if self.flag:
314 # signal an event
315 self.flag.set()
316 while self.active:
317 try:
318 newconn, connaddr = self.sock.accept()
319 if test_support.verbose and self.chatty:
320 sys.stdout.write(' server: new connection from '
Bill Janssen6e027db2007-11-15 22:23:56 +0000321 + repr(connaddr) + '\n')
322 handler = self.ConnectionHandler(self, newconn, connaddr)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000323 handler.start()
324 except socket.timeout:
325 pass
326 except KeyboardInterrupt:
327 self.stop()
328 except:
329 if self.chatty:
330 handle_error("Test server failure:\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000331 self.sock.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000332
333 def stop (self):
334 self.active = False
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000335
336 class AsyncoreHTTPSServer(threading.Thread):
337
338 class HTTPSServer(HTTPServer):
339
340 def __init__(self, server_address, RequestHandlerClass, certfile):
341
342 HTTPServer.__init__(self, server_address, RequestHandlerClass)
343 # we assume the certfile contains both private key and certificate
344 self.certfile = certfile
345 self.active = False
346 self.allow_reuse_address = True
347
Bill Janssen6e027db2007-11-15 22:23:56 +0000348 def __str__(self):
349 return ('<%s %s:%s>' %
350 (self.__class__.__name__,
351 self.server_name,
352 self.server_port))
353
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000354 def get_request (self):
355 # override this to wrap socket with SSL
356 sock, addr = self.socket.accept()
357 sslconn = ssl.wrap_socket(sock, server_side=True,
358 certfile=self.certfile)
359 return sslconn, addr
360
361 # The methods overridden below this are mainly so that we
362 # can run it in a thread and be able to stop it from another
363 # You probably wouldn't need them in other uses.
364
365 def server_activate(self):
366 # We want to run this in a thread for testing purposes,
367 # so we override this to set timeout, so that we get
368 # a chance to stop the server
369 self.socket.settimeout(0.5)
370 HTTPServer.server_activate(self)
371
372 def serve_forever(self):
373 # We want this to run in a thread, so we use a slightly
374 # modified version of "forever".
375 self.active = True
376 while self.active:
377 try:
378 self.handle_request()
379 except socket.timeout:
380 pass
381 except KeyboardInterrupt:
382 self.server_close()
383 return
384 except:
385 sys.stdout.write(''.join(traceback.format_exception(*sys.exc_info())));
386
387 def server_close(self):
388 # Again, we want this to run in a thread, so we need to override
389 # close to clear the "active" flag, so that serve_forever() will
390 # terminate.
391 HTTPServer.server_close(self)
392 self.active = False
393
394 class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):
395
396 # need to override translate_path to get a known root,
397 # instead of using os.curdir, since the test could be
398 # run from anywhere
399
400 server_version = "TestHTTPS/1.0"
401
402 root = None
403
404 def translate_path(self, path):
405 """Translate a /-separated PATH to the local filename syntax.
406
407 Components that mean special things to the local file system
408 (e.g. drive or directory names) are ignored. (XXX They should
409 probably be diagnosed.)
410
411 """
412 # abandon query parameters
413 path = urlparse.urlparse(path)[2]
414 path = os.path.normpath(urllib.unquote(path))
415 words = path.split('/')
416 words = filter(None, words)
417 path = self.root
418 for word in words:
419 drive, word = os.path.splitdrive(word)
420 head, word = os.path.split(word)
421 if word in self.root: continue
422 path = os.path.join(path, word)
423 return path
424
425 def log_message(self, format, *args):
426
427 # we override this to suppress logging unless "verbose"
428
Thomas Wouters89d996e2007-09-08 17:39:28 +0000429 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000430 sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" %
431 (self.server.server_address,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000432 self.server.server_port,
433 self.request.cipher(),
434 self.log_date_time_string(),
435 format%args))
Thomas Woutersed03b412007-08-28 21:37:11 +0000436
437
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000438 def __init__(self, port, certfile):
439 self.flag = None
440 self.active = False
441 self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0]
442 self.server = self.HTTPSServer(
443 ('', port), self.RootedHTTPRequestHandler, certfile)
Thomas Woutersed03b412007-08-28 21:37:11 +0000444 threading.Thread.__init__(self)
445 self.setDaemon(True)
446
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000447 def __str__(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000448 return "<%s %s>" % (self.__class__.__name__, self.server)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000449
450 def start (self, flag=None):
451 self.flag = flag
452 threading.Thread.start(self)
453
Thomas Woutersed03b412007-08-28 21:37:11 +0000454 def run (self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000455 self.active = True
456 if self.flag:
457 self.flag.set()
458 self.server.serve_forever()
459 self.active = False
460
461 def stop (self):
462 self.active = False
463 self.server.server_close()
464
465
466 def badCertTest (certfile):
467 server = ThreadedEchoServer(TESTPORT, CERTFILE,
468 certreqs=ssl.CERT_REQUIRED,
Bill Janssen6e027db2007-11-15 22:23:56 +0000469 cacerts=CERTFILE, chatty=False,
470 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000471 flag = threading.Event()
472 server.start(flag)
473 # wait for it to start
474 flag.wait()
475 # try to connect
476 try:
Thomas Woutersed03b412007-08-28 21:37:11 +0000477 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000478 s = ssl.wrap_socket(socket.socket(),
479 certfile=certfile,
480 ssl_version=ssl.PROTOCOL_TLSv1)
481 s.connect(('127.0.0.1', TESTPORT))
482 except ssl.SSLError as x:
Thomas Woutersed03b412007-08-28 21:37:11 +0000483 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000484 sys.stdout.write("\nSSLError is %s\n" % x)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000485 else:
486 raise test_support.TestFailed(
487 "Use of invalid cert should have failed!")
488 finally:
489 server.stop()
490 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000491
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000492 def serverParamsTest (certfile, protocol, certreqs, cacertsfile,
Bill Janssen6e027db2007-11-15 22:23:56 +0000493 client_certfile, client_protocol=None,
494 indata="FOO\n",
495 chatty=False, connectionchatty=False):
Thomas Woutersed03b412007-08-28 21:37:11 +0000496
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000497 server = ThreadedEchoServer(TESTPORT, certfile,
498 certreqs=certreqs,
499 ssl_version=protocol,
500 cacerts=cacertsfile,
501 chatty=chatty,
Bill Janssen6e027db2007-11-15 22:23:56 +0000502 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000503 flag = threading.Event()
504 server.start(flag)
505 # wait for it to start
506 flag.wait()
507 # try to connect
508 if client_protocol is None:
509 client_protocol = protocol
510 try:
Bill Janssen6e027db2007-11-15 22:23:56 +0000511 s = ssl.wrap_socket(socket.socket(),
512 certfile=client_certfile,
513 ca_certs=cacertsfile,
514 cert_reqs=certreqs,
515 ssl_version=client_protocol)
516 s.connect(('127.0.0.1', TESTPORT))
517 except ssl.SSLError as x:
518 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
519 except Exception as x:
520 raise test_support.TestFailed("Unexpected exception: " + str(x))
521 else:
522 if connectionchatty:
523 if test_support.verbose:
524 sys.stdout.write(
525 " client: sending %s...\n" % (repr(indata)))
526 s.write(indata.encode('ASCII', 'strict'))
527 outdata = s.read()
528 if connectionchatty:
529 if test_support.verbose:
530 sys.stdout.write(" client: read %s\n" % repr(outdata))
531 outdata = str(outdata, 'ASCII', 'strict')
532 if outdata != indata.lower():
533 raise test_support.TestFailed(
534 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
535 % (repr(outdata[:min(len(outdata),20)]), len(outdata),
536 repr(indata[:min(len(indata),20)].lower()), len(indata)))
537 s.write("over\n".encode("ASCII", "strict"))
538 if connectionchatty:
539 if test_support.verbose:
540 sys.stdout.write(" client: closing connection.\n")
541 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000542 finally:
543 server.stop()
544 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000545
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000546 def tryProtocolCombo (server_protocol,
547 client_protocol,
548 expectedToWork,
549 certsreqs=None):
Thomas Woutersed03b412007-08-28 21:37:11 +0000550
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000551 if certsreqs == None:
552 certsreqs = ssl.CERT_NONE
Thomas Woutersed03b412007-08-28 21:37:11 +0000553
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000554 if certsreqs == ssl.CERT_NONE:
555 certtype = "CERT_NONE"
556 elif certsreqs == ssl.CERT_OPTIONAL:
557 certtype = "CERT_OPTIONAL"
558 elif certsreqs == ssl.CERT_REQUIRED:
559 certtype = "CERT_REQUIRED"
Thomas Woutersed03b412007-08-28 21:37:11 +0000560 if test_support.verbose:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000561 formatstr = (expectedToWork and " %s->%s %s\n") or " {%s->%s} %s\n"
562 sys.stdout.write(formatstr %
563 (ssl.get_protocol_name(client_protocol),
564 ssl.get_protocol_name(server_protocol),
565 certtype))
566 try:
567 serverParamsTest(CERTFILE, server_protocol, certsreqs,
Bill Janssen6e027db2007-11-15 22:23:56 +0000568 CERTFILE, CERTFILE, client_protocol,
569 chatty=False, connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000570 except test_support.TestFailed:
571 if expectedToWork:
572 raise
573 else:
574 if not expectedToWork:
575 raise test_support.TestFailed(
576 "Client protocol %s succeeded with server protocol %s!"
577 % (ssl.get_protocol_name(client_protocol),
578 ssl.get_protocol_name(server_protocol)))
579
580
Bill Janssen6e027db2007-11-15 22:23:56 +0000581 class ThreadedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000582
583 def testEcho (self):
584
585 if test_support.verbose:
586 sys.stdout.write("\n")
587 serverParamsTest(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,
588 CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,
589 chatty=True, connectionchatty=True)
590
591 def testReadCert(self):
592
593 if test_support.verbose:
594 sys.stdout.write("\n")
595 s2 = socket.socket()
596 server = ThreadedEchoServer(TESTPORT, CERTFILE,
597 certreqs=ssl.CERT_NONE,
598 ssl_version=ssl.PROTOCOL_SSLv23,
599 cacerts=CERTFILE,
600 chatty=False)
601 flag = threading.Event()
602 server.start(flag)
603 # wait for it to start
604 flag.wait()
605 # try to connect
606 try:
607 try:
608 s = ssl.wrap_socket(socket.socket(),
609 certfile=CERTFILE,
610 ca_certs=CERTFILE,
611 cert_reqs=ssl.CERT_REQUIRED,
612 ssl_version=ssl.PROTOCOL_SSLv23)
613 s.connect(('127.0.0.1', TESTPORT))
614 except ssl.SSLError as x:
615 raise test_support.TestFailed(
616 "Unexpected SSL error: " + str(x))
617 except Exception as x:
618 raise test_support.TestFailed(
619 "Unexpected exception: " + str(x))
620 else:
621 if not s:
622 raise test_support.TestFailed(
623 "Can't SSL-handshake with test server")
624 cert = s.getpeercert()
625 if not cert:
626 raise test_support.TestFailed(
627 "Can't get peer certificate.")
628 cipher = s.cipher()
629 if test_support.verbose:
630 sys.stdout.write(pprint.pformat(cert) + '\n')
631 sys.stdout.write("Connection cipher is " + str(cipher) + '.\n')
Bill Janssen6e027db2007-11-15 22:23:56 +0000632 if 'subject' not in cert:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000633 raise test_support.TestFailed(
634 "No subject field in certificate: %s." %
635 pprint.pformat(cert))
636 if ((('organizationName', 'Python Software Foundation'),)
637 not in cert['subject']):
638 raise test_support.TestFailed(
639 "Missing or invalid 'organizationName' field in certificate subject; "
640 "should be 'Python Software Foundation'.");
641 s.close()
642 finally:
643 server.stop()
644 server.join()
645
646 def testNULLcert(self):
647 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
648 "nullcert.pem"))
649 def testMalformedCert(self):
650 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
651 "badcert.pem"))
652 def testMalformedKey(self):
653 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
654 "badkey.pem"))
655
Bill Janssen6e027db2007-11-15 22:23:56 +0000656 def testRudeShutdown(self):
657
658 listener_ready = threading.Event()
659 listener_gone = threading.Event()
660
661 # `listener` runs in a thread. It opens a socket listening on
662 # PORT, and sits in an accept() until the main thread connects.
663 # Then it rudely closes the socket, and sets Event `listener_gone`
664 # to let the main thread know the socket is gone.
665 def listener():
666 s = socket.socket()
667 if hasattr(socket, 'SO_REUSEADDR'):
668 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
669 if hasattr(socket, 'SO_REUSEPORT'):
670 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
671 s.bind(('127.0.0.1', TESTPORT))
672 s.listen(5)
673 listener_ready.set()
674 s.accept()
675 s = None # reclaim the socket object, which also closes it
676 listener_gone.set()
677
678 def connector():
679 listener_ready.wait()
680 s = socket.socket()
681 s.connect(('127.0.0.1', TESTPORT))
682 listener_gone.wait()
683 try:
684 ssl_sock = ssl.wrap_socket(s)
685 except IOError:
686 pass
687 else:
688 raise test_support.TestFailed(
689 'connecting to closed SSL socket should have failed')
690
691 t = threading.Thread(target=listener)
692 t.start()
693 connector()
694 t.join()
695
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000696 def testProtocolSSL2(self):
697 if test_support.verbose:
698 sys.stdout.write("\n")
699 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True)
700 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL)
701 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED)
702 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
703 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
704 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
705
706 def testProtocolSSL23(self):
707 if test_support.verbose:
708 sys.stdout.write("\n")
709 try:
710 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True)
711 except test_support.TestFailed as x:
712 # this fails on some older versions of OpenSSL (0.9.7l, for instance)
713 if test_support.verbose:
714 sys.stdout.write(
715 " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"
716 % str(x))
717 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)
718 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)
719 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)
720
721 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
722 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)
723 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
724
725 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
726 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
727 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
728
729 def testProtocolSSL3(self):
730 if test_support.verbose:
731 sys.stdout.write("\n")
732 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
733 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
734 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
735 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
736 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
737 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
738
739 def testProtocolTLS1(self):
740 if test_support.verbose:
741 sys.stdout.write("\n")
742 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
743 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
744 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
745 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
746 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
747 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
748
749 def testSTARTTLS (self):
750
751 msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4")
752
753 server = ThreadedEchoServer(TESTPORT, CERTFILE,
754 ssl_version=ssl.PROTOCOL_TLSv1,
755 starttls_server=True,
756 chatty=True,
757 connectionchatty=True)
758 flag = threading.Event()
759 server.start(flag)
760 # wait for it to start
761 flag.wait()
762 # try to connect
763 wrapped = False
764 try:
765 try:
766 s = socket.socket()
767 s.setblocking(1)
768 s.connect(('127.0.0.1', TESTPORT))
769 except Exception as x:
770 raise test_support.TestFailed("Unexpected exception: " + str(x))
771 else:
772 if test_support.verbose:
773 sys.stdout.write("\n")
774 for indata in msgs:
Bill Janssen6e027db2007-11-15 22:23:56 +0000775 msg = indata.encode('ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000776 if test_support.verbose:
777 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000778 " client: sending %s...\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000779 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000780 conn.write(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000781 outdata = conn.read()
782 else:
Bill Janssen6e027db2007-11-15 22:23:56 +0000783 s.send(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000784 outdata = s.recv(1024)
785 if (indata == "STARTTLS" and
Bill Janssen6e027db2007-11-15 22:23:56 +0000786 str(outdata, 'ASCII', 'replace').strip().lower().startswith("ok")):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000787 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000788 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000789 sys.stdout.write(
790 " client: read %s from server, starting TLS...\n"
Bill Janssen6e027db2007-11-15 22:23:56 +0000791 % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000792 conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
793
794 wrapped = True
795 else:
796 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000797 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000798 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000799 " client: read %s from server\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000800 if test_support.verbose:
801 sys.stdout.write(" client: closing connection.\n")
802 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000803 conn.write("over\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000804 else:
805 s.send("over\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000806 if wrapped:
807 conn.close()
808 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000809 s.close()
810 finally:
811 server.stop()
812 server.join()
813
Bill Janssen6e027db2007-11-15 22:23:56 +0000814 class AsyncoreTests(unittest.TestCase):
815
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000816 def testAsyncore(self):
817
818 server = AsyncoreHTTPSServer(TESTPORT, CERTFILE)
819 flag = threading.Event()
820 server.start(flag)
821 # wait for it to start
822 flag.wait()
823 # try to connect
824 try:
825 if test_support.verbose:
826 sys.stdout.write('\n')
827 d1 = open(CERTFILE, 'rb').read()
828 d2 = ''
829 # now fetch the same data from the HTTPS server
830 url = 'https://127.0.0.1:%d/%s' % (
831 TESTPORT, os.path.split(CERTFILE)[1])
832 f = urllib.urlopen(url)
833 dlen = f.info().getheader("content-length")
834 if dlen and (int(dlen) > 0):
835 d2 = f.read(int(dlen))
836 if test_support.verbose:
837 sys.stdout.write(
838 " client: read %d bytes from remote server '%s'\n"
839 % (len(d2), server))
840 f.close()
841 except:
842 msg = ''.join(traceback.format_exception(*sys.exc_info()))
843 if test_support.verbose:
844 sys.stdout.write('\n' + msg)
845 raise test_support.TestFailed(msg)
846 else:
847 if not (d1 == d2):
Bill Janssen6e027db2007-11-15 22:23:56 +0000848 print("d1 is", len(d1), repr(d1))
849 print("d2 is", len(d2), repr(d2))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000850 raise test_support.TestFailed(
851 "Couldn't fetch data from HTTPS server")
852 finally:
853 server.stop()
854 server.join()
855
856
857def findtestsocket(start, end):
858 def testbind(i):
859 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
860 try:
861 s.bind(("127.0.0.1", i))
862 except:
863 return 0
864 else:
865 return 1
866 finally:
867 s.close()
868
869 for i in range(start, end):
870 if testbind(i) and testbind(i+1):
871 return i
872 return 0
Thomas Woutersed03b412007-08-28 21:37:11 +0000873
874
875def test_main(verbose=False):
876 if skip_expected:
Thomas Wouters89d996e2007-09-08 17:39:28 +0000877 raise test_support.TestSkipped("No SSL support")
Thomas Woutersed03b412007-08-28 21:37:11 +0000878
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000879 global CERTFILE, TESTPORT, SVN_PYTHON_ORG_ROOT_CERT
Thomas Woutersed03b412007-08-28 21:37:11 +0000880 CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
881 "keycert.pem")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000882 SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
883 os.path.dirname(__file__) or os.curdir,
884 "https_svn_python_org_root.pem")
885
886 if (not os.path.exists(CERTFILE) or
887 not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
888 raise test_support.TestFailed("Can't read certificate files!")
Bill Janssen6e027db2007-11-15 22:23:56 +0000889
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000890 TESTPORT = findtestsocket(10025, 12000)
891 if not TESTPORT:
892 raise test_support.TestFailed("Can't find open port to test servers on!")
Thomas Woutersed03b412007-08-28 21:37:11 +0000893
894 tests = [BasicTests]
895
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000896 if test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +0000897 tests.append(NetworkedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000898
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000899 if _have_threads:
900 thread_info = test_support.threading_setup()
901 if thread_info and test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +0000902 tests.append(ThreadedTests)
903 tests.append(AsyncoreTests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000904
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000905 test_support.run_unittest(*tests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000906
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000907 if _have_threads:
908 test_support.threading_cleanup(*thread_info)
Thomas Woutersed03b412007-08-28 21:37:11 +0000909
910if __name__ == "__main__":
911 test_main()