1 # Wrapper module for _ssl, providing some additional facilities
2 # implemented in Python. Written by Bill Janssen.
4 """This module provides some more Pythonic support for SSL.
8 SSLSocket -- subtype of socket.socket which does SSL over the socket
12 SSLError -- exception raised for I/O errors
16 cert_time_to_seconds -- convert time string used for certificate
17 notBefore and notAfter functions to integer
18 seconds past the Epoch (the time values
19 returned from time.time())
21 fetch_server_certificate (HOST, PORT) -- fetch the certificate provided
22 by the server running on HOST at port PORT. No
23 validation of the certificate is performed.
30 SSL_ERROR_WANT_X509_LOOKUP
33 SSL_ERROR_WANT_CONNECT
36 SSL_ERROR_INVALID_ERROR_CODE
38 The following group define certificate requirements that one side is
39 allowing/requiring from the other side:
41 CERT_NONE - no certificates from the other side are required (or will
42 be looked at if provided)
43 CERT_OPTIONAL - certificates are not required, but if provided will be
44 validated, and if validation fails, the connection will
46 CERT_REQUIRED - certificates are required, and will be validated, and
47 if validation fails, the connection will also fail
49 The following constants identify various SSL protocol variants:
59 import _ssl
# if we can't import it, let the error propagate
61 from _ssl
import SSLError
62 from _ssl
import CERT_NONE
, CERT_OPTIONAL
, CERT_REQUIRED
63 from _ssl
import (PROTOCOL_SSLv2
, PROTOCOL_SSLv3
, PROTOCOL_SSLv23
,
65 from _ssl
import RAND_status
, RAND_egd
, RAND_add
67 SSL_ERROR_ZERO_RETURN
,
70 SSL_ERROR_WANT_X509_LOOKUP
,
73 SSL_ERROR_WANT_CONNECT
,
75 SSL_ERROR_INVALID_ERROR_CODE
,
78 from socket
import getnameinfo
as _getnameinfo
79 from socket
import error
as socket_error
80 from socket
import dup
as _dup
81 from socket
import socket
, AF_INET
, SOCK_STREAM
82 import base64
# for DER-to-PEM translation
86 class SSLSocket(socket
):
88 """This class implements a subtype of socket.socket that wraps
89 the underlying OS socket in an SSL context when necessary, and
90 provides read and write methods over that channel."""
92 def __init__(self
, sock
=None, keyfile
=None, certfile
=None,
93 server_side
=False, cert_reqs
=CERT_NONE
,
94 ssl_version
=PROTOCOL_SSLv23
, ca_certs
=None,
95 do_handshake_on_connect
=True,
96 family
=AF_INET
, type=SOCK_STREAM
, proto
=0, fileno
=None,
97 suppress_ragged_eofs
=True):
101 socket
.__init
__(self
,
105 fileno
=_dup(sock
.fileno()))
106 self
.settimeout(sock
.gettimeout())
107 # see if it's connected
110 except socket_error
as e
:
111 if e
.errno
!= errno
.ENOTCONN
:
116 elif fileno
is not None:
117 socket
.__init
__(self
, fileno
=fileno
)
119 socket
.__init
__(self
, family
=family
, type=type, proto
=proto
)
121 if certfile
and not keyfile
:
127 # create the SSL object
129 self
._sslobj
= _ssl
.sslwrap(self
, server_side
,
131 cert_reqs
, ssl_version
, ca_certs
)
132 if do_handshake_on_connect
:
133 timeout
= self
.gettimeout()
136 raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
139 except socket_error
as x
:
143 self
.keyfile
= keyfile
144 self
.certfile
= certfile
145 self
.cert_reqs
= cert_reqs
146 self
.ssl_version
= ssl_version
147 self
.ca_certs
= ca_certs
148 self
.do_handshake_on_connect
= do_handshake_on_connect
149 self
.suppress_ragged_eofs
= suppress_ragged_eofs
152 raise NotImplemented("Can't dup() %s instances" %
153 self
.__class
__.__name
__)
155 def _checkClosed(self
, msg
=None):
156 # raise an exception here if you wish to check for spurious closes
159 def read(self
, len=0, buffer=None):
160 """Read up to LEN bytes and return them.
161 Return zero-length string on EOF."""
166 v
= self
._sslobj
.read(buffer, len)
168 v
= self
._sslobj
.read(len or 1024)
170 except SSLError
as x
:
171 if x
.args
[0] == SSL_ERROR_EOF
and self
.suppress_ragged_eofs
:
179 def write(self
, data
):
180 """Write DATA to the underlying SSL channel. Returns
181 number of bytes of DATA actually transmitted."""
184 return self
._sslobj
.write(data
)
186 def getpeercert(self
, binary_form
=False):
187 """Returns a formatted version of the data in the
188 certificate provided by the other end of the SSL channel.
189 Return None if no certificate was provided, {} if a
190 certificate was provided, but not validated."""
193 return self
._sslobj
.peer_certificate(binary_form
)
200 return self
._sslobj
.cipher()
202 def send(self
, data
, flags
=0):
207 "non-zero flags not allowed in calls to send() on %s" %
211 v
= self
._sslobj
.write(data
)
212 except SSLError
as x
:
213 if x
.args
[0] == SSL_ERROR_WANT_READ
:
215 elif x
.args
[0] == SSL_ERROR_WANT_WRITE
:
222 return socket
.send(self
, data
, flags
)
224 def sendto(self
, data
, addr
, flags
=0):
227 raise ValueError("sendto not allowed on instances of %s" %
230 return socket
.sendto(self
, data
, addr
, flags
)
232 def sendall(self
, data
, flags
=0):
237 while (count
< amount
):
238 v
= self
.send(data
[count
:])
242 return socket
.sendall(self
, data
, flags
)
244 def recv(self
, buflen
=1024, flags
=0):
249 "non-zero flags not allowed in calls to recv() on %s" %
251 return self
.read(buflen
)
253 return socket
.recv(self
, buflen
, flags
)
255 def recv_into(self
, buffer, nbytes
=None, flags
=0):
257 if buffer and (nbytes
is None):
264 "non-zero flags not allowed in calls to recv_into() on %s" %
266 return self
.read(nbytes
, buffer)
268 return socket
.recv_into(self
, buffer, nbytes
, flags
)
270 def recvfrom(self
, addr
, buflen
=1024, flags
=0):
273 raise ValueError("recvfrom not allowed on instances of %s" %
276 return socket
.recvfrom(self
, addr
, buflen
, flags
)
278 def recvfrom_into(self
, buffer, nbytes
=None, flags
=0):
281 raise ValueError("recvfrom_into not allowed on instances of %s" %
284 return socket
.recvfrom_into(self
, buffer, nbytes
, flags
)
289 return self
._sslobj
.pending()
293 def shutdown(self
, how
):
296 socket
.shutdown(self
, how
)
300 s
= self
._sslobj
.shutdown()
304 raise ValueError("No SSL wrapper around " + str(self
))
306 def _real_close(self
):
308 # self._closed = True
309 socket
._real
_close
(self
)
311 def do_handshake(self
, block
=False):
312 """Perform a TLS/SSL handshake."""
314 timeout
= self
.gettimeout()
316 if timeout
== 0.0 and block
:
317 self
.settimeout(None)
318 self
._sslobj
.do_handshake()
320 self
.settimeout(timeout
)
322 def connect(self
, addr
):
323 """Connects to remote ADDR, and then wraps the connection in
326 # Here we assume that the socket is client-side, and not
327 # connected at the time of the call. We connect it, then wrap it.
329 raise ValueError("attempt to connect already-connected SSLSocket!")
330 socket
.connect(self
, addr
)
331 self
._sslobj
= _ssl
.sslwrap(self
, False, self
.keyfile
, self
.certfile
,
332 self
.cert_reqs
, self
.ssl_version
,
335 if self
.do_handshake_on_connect
:
342 """Accepts a new connection from a remote client, and returns
343 a tuple containing that new connection wrapped with a server-side
344 SSL channel, and the address of the remote client."""
346 newsock
, addr
= socket
.accept(self
)
347 return (SSLSocket(sock
=newsock
,
348 keyfile
=self
.keyfile
, certfile
=self
.certfile
,
350 cert_reqs
=self
.cert_reqs
,
351 ssl_version
=self
.ssl_version
,
352 ca_certs
=self
.ca_certs
,
353 do_handshake_on_connect
=
354 self
.do_handshake_on_connect
),
358 # sys.stderr.write("__del__ on %s\n" % repr(self))
362 def wrap_socket(sock
, keyfile
=None, certfile
=None,
363 server_side
=False, cert_reqs
=CERT_NONE
,
364 ssl_version
=PROTOCOL_SSLv23
, ca_certs
=None,
365 do_handshake_on_connect
=True,
366 suppress_ragged_eofs
=True):
368 return SSLSocket(sock
=sock
, keyfile
=keyfile
, certfile
=certfile
,
369 server_side
=server_side
, cert_reqs
=cert_reqs
,
370 ssl_version
=ssl_version
, ca_certs
=ca_certs
,
371 do_handshake_on_connect
=do_handshake_on_connect
,
372 suppress_ragged_eofs
=suppress_ragged_eofs
)
374 # some utility functions
376 def cert_time_to_seconds(cert_time
):
377 """Takes a date-time string in standard ASN1_print form
378 ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return
379 a Python time value in seconds past the epoch."""
382 return time
.mktime(time
.strptime(cert_time
, "%b %d %H:%M:%S %Y GMT"))
384 PEM_HEADER
= "-----BEGIN CERTIFICATE-----"
385 PEM_FOOTER
= "-----END CERTIFICATE-----"
387 def DER_cert_to_PEM_cert(der_cert_bytes
):
388 """Takes a certificate in binary DER format and returns the
389 PEM version of it as a string."""
391 f
= str(base64
.standard_b64encode(der_cert_bytes
), 'ASCII', 'strict')
392 return (PEM_HEADER
+ '\n' +
393 textwrap
.fill(f
, 64) + '\n' +
396 def PEM_cert_to_DER_cert(pem_cert_string
):
397 """Takes a certificate in ASCII PEM format and returns the
398 DER-encoded version of it as a byte sequence"""
400 if not pem_cert_string
.startswith(PEM_HEADER
):
401 raise ValueError("Invalid PEM encoding; must start with %s"
403 if not pem_cert_string
.strip().endswith(PEM_FOOTER
):
404 raise ValueError("Invalid PEM encoding; must end with %s"
406 d
= pem_cert_string
.strip()[len(PEM_HEADER
):-len(PEM_FOOTER
)]
407 return base64
.decodebytes(d
.encode('ASCII', 'strict'))
409 def get_server_certificate(addr
, ssl_version
=PROTOCOL_SSLv3
, ca_certs
=None):
410 """Retrieve the certificate from the server at the specified address,
411 and return it as a PEM-encoded string.
412 If 'ca_certs' is specified, validate the server cert against it.
413 If 'ssl_version' is specified, use it in the connection attempt."""
416 if (ca_certs
is not None):
417 cert_reqs
= CERT_REQUIRED
419 cert_reqs
= CERT_NONE
420 s
= wrap_socket(socket(), ssl_version
=ssl_version
,
421 cert_reqs
=cert_reqs
, ca_certs
=ca_certs
)
423 dercert
= s
.getpeercert(True)
425 return DER_cert_to_PEM_cert(dercert
)
427 def get_protocol_name(protocol_code
):
428 if protocol_code
== PROTOCOL_TLSv1
:
430 elif protocol_code
== PROTOCOL_SSLv23
:
432 elif protocol_code
== PROTOCOL_SSLv2
:
434 elif protocol_code
== PROTOCOL_SSLv3
: