blob: 9a120f25814427e9f3607f5e11a6e122801d65e0 [file] [log] [blame]
Thomas Woutersed03b412007-08-28 21:37:11 +00001# Wrapper module for _ssl, providing some additional facilities
2# implemented in Python. Written by Bill Janssen.
3
Guido van Rossum245b42e2007-08-29 03:59:57 +00004raise ImportError("ssl.py is temporarily out of order")
5
Thomas Woutersed03b412007-08-28 21:37:11 +00006"""\
7This module provides some more Pythonic support for SSL.
8
9Object types:
10
Thomas Wouters1b7f8912007-09-19 03:06:30 +000011 SSLSocket -- subtype of socket.socket which does SSL over the socket
Thomas Woutersed03b412007-08-28 21:37:11 +000012
13Exceptions:
14
Thomas Wouters1b7f8912007-09-19 03:06:30 +000015 SSLError -- exception raised for I/O errors
Thomas Woutersed03b412007-08-28 21:37:11 +000016
17Functions:
18
19 cert_time_to_seconds -- convert time string used for certificate
20 notBefore and notAfter functions to integer
21 seconds past the Epoch (the time values
22 returned from time.time())
23
24 fetch_server_certificate (HOST, PORT) -- fetch the certificate provided
25 by the server running on HOST at port PORT. No
26 validation of the certificate is performed.
27
28Integer constants:
29
30SSL_ERROR_ZERO_RETURN
31SSL_ERROR_WANT_READ
32SSL_ERROR_WANT_WRITE
33SSL_ERROR_WANT_X509_LOOKUP
34SSL_ERROR_SYSCALL
35SSL_ERROR_SSL
36SSL_ERROR_WANT_CONNECT
37
38SSL_ERROR_EOF
39SSL_ERROR_INVALID_ERROR_CODE
40
41The following group define certificate requirements that one side is
42allowing/requiring from the other side:
43
44CERT_NONE - no certificates from the other side are required (or will
45 be looked at if provided)
46CERT_OPTIONAL - certificates are not required, but if provided will be
47 validated, and if validation fails, the connection will
48 also fail
49CERT_REQUIRED - certificates are required, and will be validated, and
50 if validation fails, the connection will also fail
51
52The following constants identify various SSL protocol variants:
53
54PROTOCOL_SSLv2
55PROTOCOL_SSLv3
56PROTOCOL_SSLv23
57PROTOCOL_TLSv1
58"""
59
Thomas Wouters1b7f8912007-09-19 03:06:30 +000060import os, sys, textwrap
Thomas Woutersed03b412007-08-28 21:37:11 +000061
62import _ssl # if we can't import it, let the error propagate
Thomas Wouters1b7f8912007-09-19 03:06:30 +000063
64from _ssl import SSLError
Thomas Woutersed03b412007-08-28 21:37:11 +000065from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
66from _ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
Thomas Wouters1b7f8912007-09-19 03:06:30 +000067from _ssl import RAND_status, RAND_egd, RAND_add
Thomas Wouters47b49bf2007-08-30 22:15:33 +000068from _ssl import \
69 SSL_ERROR_ZERO_RETURN, \
70 SSL_ERROR_WANT_READ, \
71 SSL_ERROR_WANT_WRITE, \
72 SSL_ERROR_WANT_X509_LOOKUP, \
73 SSL_ERROR_SYSCALL, \
74 SSL_ERROR_SSL, \
75 SSL_ERROR_WANT_CONNECT, \
76 SSL_ERROR_EOF, \
77 SSL_ERROR_INVALID_ERROR_CODE
Thomas Woutersed03b412007-08-28 21:37:11 +000078
Thomas Wouters47b49bf2007-08-30 22:15:33 +000079from socket import socket
80from socket import getnameinfo as _getnameinfo
Thomas Wouters1b7f8912007-09-19 03:06:30 +000081import base64 # for DER-to-PEM translation
Thomas Wouters47b49bf2007-08-30 22:15:33 +000082
Thomas Wouters1b7f8912007-09-19 03:06:30 +000083class SSLSocket (socket):
Thomas Woutersed03b412007-08-28 21:37:11 +000084
Thomas Wouters47b49bf2007-08-30 22:15:33 +000085 """This class implements a subtype of socket.socket that wraps
86 the underlying OS socket in an SSL context when necessary, and
87 provides read and write methods over that channel."""
88
Thomas Woutersed03b412007-08-28 21:37:11 +000089 def __init__(self, sock, keyfile=None, certfile=None,
90 server_side=False, cert_reqs=CERT_NONE,
91 ssl_version=PROTOCOL_SSLv23, ca_certs=None):
92 socket.__init__(self, _sock=sock._sock)
93 if certfile and not keyfile:
94 keyfile = certfile
Thomas Wouters47b49bf2007-08-30 22:15:33 +000095 # see if it's connected
96 try:
97 socket.getpeername(self)
98 except:
99 # no, no connection yet
100 self._sslobj = None
Thomas Woutersed03b412007-08-28 21:37:11 +0000101 else:
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000102 # yes, create the SSL object
103 self._sslobj = _ssl.sslwrap(self._sock, server_side,
104 keyfile, certfile,
105 cert_reqs, ssl_version, ca_certs)
Thomas Woutersed03b412007-08-28 21:37:11 +0000106 self.keyfile = keyfile
107 self.certfile = certfile
108 self.cert_reqs = cert_reqs
109 self.ssl_version = ssl_version
110 self.ca_certs = ca_certs
111
112 def read(self, len=1024):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000113
114 """Read up to LEN bytes and return them.
115 Return zero-length string on EOF."""
116
Thomas Woutersed03b412007-08-28 21:37:11 +0000117 return self._sslobj.read(len)
118
119 def write(self, data):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000120
121 """Write DATA to the underlying SSL channel. Returns
122 number of bytes of DATA actually transmitted."""
123
Thomas Woutersed03b412007-08-28 21:37:11 +0000124 return self._sslobj.write(data)
125
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000126 def getpeercert(self, binary_form=False):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000127
128 """Returns a formatted version of the data in the
129 certificate provided by the other end of the SSL channel.
130 Return None if no certificate was provided, {} if a
131 certificate was provided, but not validated."""
132
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000133 return self._sslobj.peer_certificate(binary_form)
134
135 def cipher (self):
136
137 if not self._sslobj:
138 return None
139 else:
140 return self._sslobj.cipher()
Thomas Woutersed03b412007-08-28 21:37:11 +0000141
142 def send (self, data, flags=0):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000143 if self._sslobj:
144 if flags != 0:
145 raise ValueError(
146 "non-zero flags not allowed in calls to send() on %s" %
147 self.__class__)
148 return self._sslobj.write(data)
149 else:
150 return socket.send(self, data, flags)
Thomas Woutersed03b412007-08-28 21:37:11 +0000151
152 def send_to (self, data, addr, flags=0):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000153 if self._sslobj:
154 raise ValueError("send_to not allowed on instances of %s" %
155 self.__class__)
156 else:
157 return socket.send_to(self, data, addr, flags)
Thomas Woutersed03b412007-08-28 21:37:11 +0000158
159 def sendall (self, data, flags=0):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000160 if self._sslobj:
161 if flags != 0:
162 raise ValueError(
163 "non-zero flags not allowed in calls to sendall() on %s" %
164 self.__class__)
165 return self._sslobj.write(data)
166 else:
167 return socket.sendall(self, data, flags)
Thomas Woutersed03b412007-08-28 21:37:11 +0000168
169 def recv (self, buflen=1024, flags=0):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000170 if self._sslobj:
171 if flags != 0:
172 raise ValueError(
173 "non-zero flags not allowed in calls to sendall() on %s" %
174 self.__class__)
175 return self._sslobj.read(data, buflen)
176 else:
177 return socket.recv(self, buflen, flags)
Thomas Woutersed03b412007-08-28 21:37:11 +0000178
179 def recv_from (self, addr, buflen=1024, flags=0):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000180 if self._sslobj:
181 raise ValueError("recv_from not allowed on instances of %s" %
182 self.__class__)
183 else:
184 return socket.recv_from(self, addr, buflen, flags)
Thomas Woutersed03b412007-08-28 21:37:11 +0000185
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000186 def shutdown(self, how):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000187 self._sslobj = None
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000188 socket.shutdown(self, how)
Thomas Woutersed03b412007-08-28 21:37:11 +0000189
190 def close(self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000191 self._sslobj = None
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000192 socket.close(self)
Thomas Woutersed03b412007-08-28 21:37:11 +0000193
194 def connect(self, addr):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000195
196 """Connects to remote ADDR, and then wraps the connection in
197 an SSL channel."""
198
Thomas Woutersed03b412007-08-28 21:37:11 +0000199 # Here we assume that the socket is client-side, and not
200 # connected at the time of the call. We connect it, then wrap it.
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000201 if self._sslobj:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000202 raise ValueError("attempt to connect already-connected SSLSocket!")
Thomas Woutersed03b412007-08-28 21:37:11 +0000203 socket.connect(self, addr)
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000204 self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
Thomas Woutersed03b412007-08-28 21:37:11 +0000205 self.cert_reqs, self.ssl_version,
206 self.ca_certs)
207
208 def accept(self):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000209
210 """Accepts a new connection from a remote client, and returns
211 a tuple containing that new connection wrapped with a server-side
212 SSL channel, and the address of the remote client."""
213
214 newsock, addr = socket.accept(self)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000215 return (SSLSocket(newsock, True, self.keyfile, self.certfile,
216 self.cert_reqs, self.ssl_version,
217 self.ca_certs), addr)
Thomas Woutersed03b412007-08-28 21:37:11 +0000218
219
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000220 def makefile(self, mode='r', bufsize=-1):
221
222 """Ouch. Need to make and return a file-like object that
223 works with the SSL connection."""
224
225 if self._sslobj:
226 return SSLFileStream(self._sslobj, mode, bufsize)
227 else:
228 return socket.makefile(self, mode, bufsize)
229
230
231class SSLFileStream:
232
233 """A class to simulate a file stream on top of a socket.
234 Most of this is just lifted from the socket module, and
235 adjusted to work with an SSL stream instead of a socket."""
236
237
238 default_bufsize = 8192
239 name = "<SSL stream>"
240
241 __slots__ = ["mode", "bufsize", "softspace",
242 # "closed" is a property, see below
243 "_sslobj", "_rbufsize", "_wbufsize", "_rbuf", "_wbuf",
244 "_close", "_fileno"]
245
246 def __init__(self, sslobj, mode='rb', bufsize=-1, close=False):
247 self._sslobj = sslobj
248 self.mode = mode # Not actually used in this version
249 if bufsize < 0:
250 bufsize = self.default_bufsize
251 self.bufsize = bufsize
252 self.softspace = False
253 if bufsize == 0:
254 self._rbufsize = 1
255 elif bufsize == 1:
256 self._rbufsize = self.default_bufsize
257 else:
258 self._rbufsize = bufsize
259 self._wbufsize = bufsize
260 self._rbuf = "" # A string
261 self._wbuf = [] # A list of strings
262 self._close = close
263 self._fileno = -1
264
265 def _getclosed(self):
266 return self._sslobj is None
267 closed = property(_getclosed, doc="True if the file is closed")
268
269 def fileno(self):
270 return self._fileno
271
272 def close(self):
273 try:
274 if self._sslobj:
275 self.flush()
276 finally:
277 if self._close and self._sslobj:
278 self._sslobj.close()
279 self._sslobj = None
280
281 def __del__(self):
282 try:
283 self.close()
284 except:
285 # close() may fail if __init__ didn't complete
286 pass
287
288 def flush(self):
289 if self._wbuf:
290 buffer = "".join(self._wbuf)
291 self._wbuf = []
292 count = 0
293 while (count < len(buffer)):
294 written = self._sslobj.write(buffer)
295 count += written
296 buffer = buffer[written:]
297
298 def write(self, data):
299 data = str(data) # XXX Should really reject non-string non-buffers
300 if not data:
301 return
302 self._wbuf.append(data)
303 if (self._wbufsize == 0 or
304 self._wbufsize == 1 and '\n' in data or
305 self._get_wbuf_len() >= self._wbufsize):
306 self.flush()
307
308 def writelines(self, list):
309 # XXX We could do better here for very long lists
310 # XXX Should really reject non-string non-buffers
311 self._wbuf.extend(filter(None, map(str, list)))
312 if (self._wbufsize <= 1 or
313 self._get_wbuf_len() >= self._wbufsize):
314 self.flush()
315
316 def _get_wbuf_len(self):
317 buf_len = 0
318 for x in self._wbuf:
319 buf_len += len(x)
320 return buf_len
321
322 def read(self, size=-1):
323 data = self._rbuf
324 if size < 0:
325 # Read until EOF
326 buffers = []
327 if data:
328 buffers.append(data)
329 self._rbuf = ""
330 if self._rbufsize <= 1:
331 recv_size = self.default_bufsize
332 else:
333 recv_size = self._rbufsize
334 while True:
335 data = self._sslobj.read(recv_size)
336 if not data:
337 break
338 buffers.append(data)
339 return "".join(buffers)
340 else:
341 # Read until size bytes or EOF seen, whichever comes first
342 buf_len = len(data)
343 if buf_len >= size:
344 self._rbuf = data[size:]
345 return data[:size]
346 buffers = []
347 if data:
348 buffers.append(data)
349 self._rbuf = ""
350 while True:
351 left = size - buf_len
352 recv_size = max(self._rbufsize, left)
353 data = self._sslobj.read(recv_size)
354 if not data:
355 break
356 buffers.append(data)
357 n = len(data)
358 if n >= left:
359 self._rbuf = data[left:]
360 buffers[-1] = data[:left]
361 break
362 buf_len += n
363 return "".join(buffers)
364
365 def readline(self, size=-1):
366 data = self._rbuf
367 if size < 0:
368 # Read until \n or EOF, whichever comes first
369 if self._rbufsize <= 1:
370 # Speed up unbuffered case
371 assert data == ""
372 buffers = []
373 while data != "\n":
374 data = self._sslobj.read(1)
375 if not data:
376 break
377 buffers.append(data)
378 return "".join(buffers)
379 nl = data.find('\n')
380 if nl >= 0:
381 nl += 1
382 self._rbuf = data[nl:]
383 return data[:nl]
384 buffers = []
385 if data:
386 buffers.append(data)
387 self._rbuf = ""
388 while True:
389 data = self._sslobj.read(self._rbufsize)
390 if not data:
391 break
392 buffers.append(data)
393 nl = data.find('\n')
394 if nl >= 0:
395 nl += 1
396 self._rbuf = data[nl:]
397 buffers[-1] = data[:nl]
398 break
399 return "".join(buffers)
400 else:
401 # Read until size bytes or \n or EOF seen, whichever comes first
402 nl = data.find('\n', 0, size)
403 if nl >= 0:
404 nl += 1
405 self._rbuf = data[nl:]
406 return data[:nl]
407 buf_len = len(data)
408 if buf_len >= size:
409 self._rbuf = data[size:]
410 return data[:size]
411 buffers = []
412 if data:
413 buffers.append(data)
414 self._rbuf = ""
415 while True:
416 data = self._sslobj.read(self._rbufsize)
417 if not data:
418 break
419 buffers.append(data)
420 left = size - buf_len
421 nl = data.find('\n', 0, left)
422 if nl >= 0:
423 nl += 1
424 self._rbuf = data[nl:]
425 buffers[-1] = data[:nl]
426 break
427 n = len(data)
428 if n >= left:
429 self._rbuf = data[left:]
430 buffers[-1] = data[:left]
431 break
432 buf_len += n
433 return "".join(buffers)
434
435 def readlines(self, sizehint=0):
436 total = 0
437 list = []
438 while True:
439 line = self.readline()
440 if not line:
441 break
442 list.append(line)
443 total += len(line)
444 if sizehint and total >= sizehint:
445 break
446 return list
447
448 # Iterator protocols
449
450 def __iter__(self):
451 return self
452
453 def next(self):
454 line = self.readline()
455 if not line:
456 raise StopIteration
457 return line
458
459
460
461
462def wrap_socket(sock, keyfile=None, certfile=None,
463 server_side=False, cert_reqs=CERT_NONE,
464 ssl_version=PROTOCOL_SSLv23, ca_certs=None):
465
466 return SSLSocket(sock, keyfile=keyfile, certfile=certfile,
467 server_side=server_side, cert_reqs=cert_reqs,
468 ssl_version=ssl_version, ca_certs=ca_certs)
469
Thomas Woutersed03b412007-08-28 21:37:11 +0000470# some utility functions
471
472def cert_time_to_seconds(cert_time):
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000473
474 """Takes a date-time string in standard ASN1_print form
475 ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return
476 a Python time value in seconds past the epoch."""
477
Thomas Woutersed03b412007-08-28 21:37:11 +0000478 import time
479 return time.mktime(time.strptime(cert_time, "%b %d %H:%M:%S %Y GMT"))
480
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000481PEM_HEADER = "-----BEGIN CERTIFICATE-----"
482PEM_FOOTER = "-----END CERTIFICATE-----"
483
484def DER_cert_to_PEM_cert(der_cert_bytes):
485
486 """Takes a certificate in binary DER format and returns the
487 PEM version of it as a string."""
488
489 if hasattr(base64, 'standard_b64encode'):
490 # preferred because older API gets line-length wrong
491 f = base64.standard_b64encode(der_cert_bytes)
492 return (PEM_HEADER + '\n' +
493 textwrap.fill(f, 64) +
494 PEM_FOOTER + '\n')
495 else:
496 return (PEM_HEADER + '\n' +
497 base64.encodestring(der_cert_bytes) +
498 PEM_FOOTER + '\n')
499
500def PEM_cert_to_DER_cert(pem_cert_string):
501
502 """Takes a certificate in ASCII PEM format and returns the
503 DER-encoded version of it as a byte sequence"""
504
505 if not pem_cert_string.startswith(PEM_HEADER):
506 raise ValueError("Invalid PEM encoding; must start with %s"
507 % PEM_HEADER)
508 if not pem_cert_string.strip().endswith(PEM_FOOTER):
509 raise ValueError("Invalid PEM encoding; must end with %s"
510 % PEM_FOOTER)
511 d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
512 return base64.decodestring(d)
513
514def get_server_certificate (addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None):
515
516 """Retrieve the certificate from the server at the specified address,
517 and return it as a PEM-encoded string.
518 If 'ca_certs' is specified, validate the server cert against it.
519 If 'ssl_version' is specified, use it in the connection attempt."""
520
521 host, port = addr
522 if (ca_certs is not None):
523 cert_reqs = CERT_REQUIRED
524 else:
525 cert_reqs = CERT_NONE
526 s = wrap_socket(socket(), ssl_version=ssl_version,
527 cert_reqs=cert_reqs, ca_certs=ca_certs)
528 s.connect(addr)
529 dercert = s.getpeercert(True)
530 s.close()
531 return DER_cert_to_PEM_cert(dercert)
532
533def get_protocol_name (protocol_code):
534 if protocol_code == PROTOCOL_TLSv1:
535 return "TLSv1"
536 elif protocol_code == PROTOCOL_SSLv23:
537 return "SSLv23"
538 elif protocol_code == PROTOCOL_SSLv2:
539 return "SSLv2"
540 elif protocol_code == PROTOCOL_SSLv3:
541 return "SSLv3"
542 else:
543 return "<unknown>"
544
545
Thomas Woutersed03b412007-08-28 21:37:11 +0000546# a replacement for the old socket.ssl function
547
548def sslwrap_simple (sock, keyfile=None, certfile=None):
549
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000550 """A replacement for the old socket.ssl function. Designed
551 for compability with Python 2.5 and earlier. Will disappear in
552 Python 3.0."""
553
Thomas Woutersed03b412007-08-28 21:37:11 +0000554 return _ssl.sslwrap(sock._sock, 0, keyfile, certfile, CERT_NONE,
555 PROTOCOL_SSLv23, None)