Commit 98d19daf authored by Bill Janssen's avatar Bill Janssen

More work on SSL support.

* Much expanded test suite:

  All protocols tested against all other protocols.
  All protocols tested with all certificate options.
  Tests for bad key and bad cert.
  Test of STARTTLS functionality.
  Test of RAND_* functions.

* Fixes for threading/malloc bug.

* Issue 1065 fixed:

  sslsocket class renamed to SSLSocket.
  sslerror class renamed to SSLError.
  Function "wrap_socket" now used to wrap an existing socket.

* Issue 1583946 finally fixed:

  Support for subjectAltName added.
  Subject name now returned as proper DN list of RDNs.

* SSLError exported from socket as "sslerror".

* RAND_* functions properly exported from ssl.py.

* Documentation improved:

  Example of how to create a self-signed certificate.
  Better indexing.
parent a0c05512
...@@ -32,7 +32,7 @@ using the :meth:`update` method. At any point you can ask it for the ...@@ -32,7 +32,7 @@ using the :meth:`update` method. At any point you can ask it for the
:dfn:`digest` of the concatenation of the strings fed to it so far using the :dfn:`digest` of the concatenation of the strings fed to it so far using the
:meth:`digest` or :meth:`hexdigest` methods. :meth:`digest` or :meth:`hexdigest` methods.
.. index:: single: OpenSSL .. index:: single: OpenSSL; (use in module hashlib)
Constructors for hash algorithms that are always present in this module are Constructors for hash algorithms that are always present in this module are
:func:`md5`, :func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`, and :func:`md5`, :func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`, and
......
This diff is collapsed.
...@@ -1051,7 +1051,7 @@ else: ...@@ -1051,7 +1051,7 @@ else:
"Connect to a host on a given (SSL) port." "Connect to a host on a given (SSL) port."
sock = socket.create_connection((self.host, self.port), self.timeout) sock = socket.create_connection((self.host, self.port), self.timeout)
self.sock = ssl.sslsocket(sock, self.key_file, self.cert_file) self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
__all__.append("HTTPSConnection") __all__.append("HTTPSConnection")
...@@ -1083,7 +1083,7 @@ else: ...@@ -1083,7 +1083,7 @@ else:
def FakeSocket (sock, sslobj): def FakeSocket (sock, sslobj):
warnings.warn("FakeSocket is deprecated, and won't be in 3.x. " + warnings.warn("FakeSocket is deprecated, and won't be in 3.x. " +
"Use the result of ssl.sslsocket directly instead.", "Use the result of ssl.wrap_socket() directly instead.",
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
return sslobj return sslobj
......
...@@ -1147,7 +1147,7 @@ else: ...@@ -1147,7 +1147,7 @@ else:
self.port = port self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port)) self.sock.connect((host, port))
self.sslobj = ssl.sslsocket(self.sock, self.keyfile, self.certfile) self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
def read(self, size): def read(self, size):
...@@ -1199,7 +1199,7 @@ else: ...@@ -1199,7 +1199,7 @@ else:
def ssl(self): def ssl(self):
"""Return SSLObject instance used to communicate with the IMAP4 server. """Return SSLObject instance used to communicate with the IMAP4 server.
ssl = ssl.sslsocket(<instance>.socket) ssl = ssl.wrap_socket(<instance>.socket)
""" """
return self.sslobj return self.sslobj
......
...@@ -348,7 +348,7 @@ else: ...@@ -348,7 +348,7 @@ else:
if not self.sock: if not self.sock:
raise socket.error, msg raise socket.error, msg
self.file = self.sock.makefile('rb') self.file = self.sock.makefile('rb')
self.sslobj = ssl.sslsocket(self.sock, self.keyfile, self.certfile) self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
self._debugging = 0 self._debugging = 0
self.welcome = self._getresp() self.welcome = self._getresp()
......
...@@ -587,7 +587,7 @@ class SMTP: ...@@ -587,7 +587,7 @@ class SMTP:
if resp == 220: if resp == 220:
if not _have_ssl: if not _have_ssl:
raise RuntimeError("No SSL support included in this Python") raise RuntimeError("No SSL support included in this Python")
self.sock = ssl.sslsocket(self.sock, keyfile, certfile) self.sock = ssl.wrap_socket(self.sock, keyfile, certfile)
self.file = SSLFakeFile(self.sock) self.file = SSLFakeFile(self.sock)
return (resp, reply) return (resp, reply)
...@@ -720,7 +720,7 @@ if _have_ssl: ...@@ -720,7 +720,7 @@ if _have_ssl:
def _get_socket(self, host, port, timeout): def _get_socket(self, host, port, timeout):
if self.debuglevel > 0: print>>stderr, 'connect:', (host, port) if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
self.sock = socket.create_connection((host, port), timeout) self.sock = socket.create_connection((host, port), timeout)
self.sock = ssl.sslsocket(self.sock, self.keyfile, self.certfile) self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
self.file = SSLFakeFile(self.sock) self.file = SSLFakeFile(self.sock)
__all__.append("SMTP_SSL") __all__.append("SMTP_SSL")
......
...@@ -56,13 +56,13 @@ else: ...@@ -56,13 +56,13 @@ else:
# we do an internal import here because the ssl # we do an internal import here because the ssl
# module imports the socket module # module imports the socket module
import ssl as _realssl import ssl as _realssl
warnings.warn("socket.ssl() is deprecated. Use ssl.sslsocket() instead.", warnings.warn("socket.ssl() is deprecated. Use ssl.wrap_socket() instead.",
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
return _realssl.sslwrap_simple(sock, keyfile, certfile) return _realssl.sslwrap_simple(sock, keyfile, certfile)
# we need to import the same constants we used to... # we need to import the same constants we used to...
from _ssl import SSLError as sslerror
from _ssl import \ from _ssl import \
sslerror, \
RAND_add, \ RAND_add, \
RAND_egd, \ RAND_egd, \
RAND_status, \ RAND_status, \
......
...@@ -6,11 +6,11 @@ This module provides some more Pythonic support for SSL. ...@@ -6,11 +6,11 @@ This module provides some more Pythonic support for SSL.
Object types: Object types:
sslsocket -- subtype of socket.socket which does SSL over the socket SSLSocket -- subtype of socket.socket which does SSL over the socket
Exceptions: Exceptions:
sslerror -- exception raised for I/O errors SSLError -- exception raised for I/O errors
Functions: Functions:
...@@ -58,9 +58,11 @@ PROTOCOL_TLSv1 ...@@ -58,9 +58,11 @@ PROTOCOL_TLSv1
import os, sys import os, sys
import _ssl # if we can't import it, let the error propagate import _ssl # if we can't import it, let the error propagate
from _ssl import sslerror
from _ssl import SSLError
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
from _ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1 from _ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
from _ssl import RAND_status, RAND_egd, RAND_add
from _ssl import \ from _ssl import \
SSL_ERROR_ZERO_RETURN, \ SSL_ERROR_ZERO_RETURN, \
SSL_ERROR_WANT_READ, \ SSL_ERROR_WANT_READ, \
...@@ -75,8 +77,20 @@ from _ssl import \ ...@@ -75,8 +77,20 @@ from _ssl import \
from socket import socket from socket import socket
from socket import getnameinfo as _getnameinfo from socket import getnameinfo as _getnameinfo
def get_protocol_name (protocol_code):
if protocol_code == PROTOCOL_TLSv1:
return "TLSv1"
elif protocol_code == PROTOCOL_SSLv23:
return "SSLv23"
elif protocol_code == PROTOCOL_SSLv2:
return "SSLv2"
elif protocol_code == PROTOCOL_SSLv3:
return "SSLv3"
else:
return "<unknown>"
class sslsocket (socket): class SSLSocket (socket):
"""This class implements a subtype of socket.socket that wraps """This class implements a subtype of socket.socket that wraps
the underlying OS socket in an SSL context when necessary, and the underlying OS socket in an SSL context when necessary, and
...@@ -119,14 +133,21 @@ class sslsocket (socket): ...@@ -119,14 +133,21 @@ class sslsocket (socket):
return self._sslobj.write(data) return self._sslobj.write(data)
def getpeercert(self): def getpeercert(self, binary_form=False):
"""Returns a formatted version of the data in the """Returns a formatted version of the data in the
certificate provided by the other end of the SSL channel. certificate provided by the other end of the SSL channel.
Return None if no certificate was provided, {} if a Return None if no certificate was provided, {} if a
certificate was provided, but not validated.""" certificate was provided, but not validated."""
return self._sslobj.peer_certificate() return self._sslobj.peer_certificate(binary_form)
def cipher (self):
if not self._sslobj:
return None
else:
return self._sslobj.cipher()
def send (self, data, flags=0): def send (self, data, flags=0):
if self._sslobj: if self._sslobj:
...@@ -197,7 +218,7 @@ class sslsocket (socket): ...@@ -197,7 +218,7 @@ class sslsocket (socket):
# Here we assume that the socket is client-side, and not # Here we assume that the socket is client-side, and not
# connected at the time of the call. We connect it, then wrap it. # connected at the time of the call. We connect it, then wrap it.
if self._sslobj: if self._sslobj:
raise ValueError("attempt to connect already-connected sslsocket!") raise ValueError("attempt to connect already-connected SSLSocket!")
socket.connect(self, addr) socket.connect(self, addr)
self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile, self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
self.cert_reqs, self.ssl_version, self.cert_reqs, self.ssl_version,
...@@ -210,10 +231,18 @@ class sslsocket (socket): ...@@ -210,10 +231,18 @@ class sslsocket (socket):
SSL channel, and the address of the remote client.""" SSL channel, and the address of the remote client."""
newsock, addr = socket.accept(self) newsock, addr = socket.accept(self)
return (sslsocket(newsock, True, self.keyfile, self.certfile, return (SSLSocket(newsock, True, self.keyfile, self.certfile,
self.cert_reqs, self.ssl_version, self.cert_reqs, self.ssl_version,
self.ca_certs), addr) self.ca_certs), addr)
def wrap_socket(sock, keyfile=None, certfile=None,
server_side=False, cert_reqs=CERT_NONE,
ssl_version=PROTOCOL_SSLv23, ca_certs=None):
return SSLSocket(sock, keyfile=keyfile, certfile=certfile,
server_side=server_side, cert_reqs=cert_reqs,
ssl_version=ssl_version, ca_certs=ca_certs)
# some utility functions # some utility functions
......
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
Just bad cert data
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
Just bad cert data
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
Bad Key, though the cert should be OK
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
iHkC6gGdBJhogs4=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
Bad Key, though the cert should be OK
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
iHkC6gGdBJhogs4=
-----END CERTIFICATE-----
...@@ -1108,7 +1108,6 @@ _expectations['freebsd7'] = _expectations['freebsd4'] ...@@ -1108,7 +1108,6 @@ _expectations['freebsd7'] = _expectations['freebsd4']
class _ExpectedSkips: class _ExpectedSkips:
def __init__(self): def __init__(self):
import os.path import os.path
from test import test_socket_ssl
from test import test_timeout from test import test_timeout
self.valid = False self.valid = False
...@@ -1122,8 +1121,13 @@ class _ExpectedSkips: ...@@ -1122,8 +1121,13 @@ class _ExpectedSkips:
if not os.path.supports_unicode_filenames: if not os.path.supports_unicode_filenames:
self.expected.add('test_pep277') self.expected.add('test_pep277')
if test_socket_ssl.skip_expected: try:
self.expected.add('test_socket_ssl') from test import test_socket_ssl
except ImportError:
pass
else:
if test_socket_ssl.skip_expected:
self.expected.add('test_socket_ssl')
if test_timeout.skip_expected: if test_timeout.skip_expected:
self.expected.add('test_timeout') self.expected.add('test_timeout')
......
...@@ -115,7 +115,7 @@ class BasicTests(unittest.TestCase): ...@@ -115,7 +115,7 @@ class BasicTests(unittest.TestCase):
s = socket.socket(socket.AF_INET) s = socket.socket(socket.AF_INET)
s.connect(("www.sf.net", 443)) s.connect(("www.sf.net", 443))
fd = s._sock.fileno() fd = s._sock.fileno()
sock = ssl.sslsocket(s) sock = ssl.wrap_socket(s)
s = None s = None
sock.close() sock.close()
try: try:
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment