Issue #8550: Add first class `SSLContext` objects to the ssl module.
This commit is contained in:
parent
8eac60d9af
commit
152efa2ae2
|
@ -36,6 +36,11 @@ additional :meth:`read` and :meth:`write` methods, along with a method,
|
|||
connection, and a method, :meth:`cipher`, to retrieve the cipher being used for
|
||||
the secure connection.
|
||||
|
||||
For more sophisticated applications, the :class:`ssl.SSLContext` class
|
||||
helps manage settings and certificates, which can then be inherited
|
||||
by SSL sockets created through the :meth:`SSLContext.wrap_socket` method.
|
||||
|
||||
|
||||
Functions, Constants, and Exceptions
|
||||
------------------------------------
|
||||
|
||||
|
@ -64,19 +69,6 @@ Functions, Constants, and Exceptions
|
|||
connection. See the discussion of :ref:`ssl-certificates` for more
|
||||
information on how the certificate is stored in the ``certfile``.
|
||||
|
||||
Often the private key is stored in the same file as the certificate; in this
|
||||
case, only the ``certfile`` parameter need be passed. If the private key is
|
||||
stored in a separate file, both parameters must be used. If the private key
|
||||
is stored in the ``certfile``, it should come before the first certificate in
|
||||
the certificate chain::
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
... (private key in base64 encoding) ...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
... (certificate in base64 PEM encoding) ...
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
The parameter ``server_side`` is a boolean which identifies whether
|
||||
server-side or client-side behavior is desired from this socket.
|
||||
|
||||
|
@ -208,24 +200,36 @@ Functions, Constants, and Exceptions
|
|||
|
||||
.. data:: CERT_NONE
|
||||
|
||||
Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when no
|
||||
certificates will be required or validated from the other side of the socket
|
||||
connection.
|
||||
Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
|
||||
parameter to :func:`wrap_socket`. In this mode (the default), no
|
||||
certificates will be required from the other side of the socket connection.
|
||||
If a certificate is received from the other end, no attempt to validate it
|
||||
is made.
|
||||
|
||||
See the discussion of :ref:`ssl-security` below.
|
||||
|
||||
.. data:: CERT_OPTIONAL
|
||||
|
||||
Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when no
|
||||
certificates will be required from the other side of the socket connection,
|
||||
but if they are provided, will be validated. Note that use of this setting
|
||||
requires a valid certificate validation file also be passed as a value of the
|
||||
``ca_certs`` parameter.
|
||||
Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
|
||||
parameter to :func:`wrap_socket`. In this mode no certificates will be
|
||||
required from the other side of the socket connection; but if they
|
||||
are provided, validation will be attempted and an :class:`SSLError`
|
||||
will be raised on failure.
|
||||
|
||||
Use of this setting requires a valid set of CA certificates to
|
||||
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
|
||||
value of the ``ca_certs`` parameter to :func:`wrap_socket`.
|
||||
|
||||
.. data:: CERT_REQUIRED
|
||||
|
||||
Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when
|
||||
certificates will be required from the other side of the socket connection.
|
||||
Note that use of this setting requires a valid certificate validation file
|
||||
also be passed as a value of the ``ca_certs`` parameter.
|
||||
Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
|
||||
parameter to :func:`wrap_socket`. In this mode, certificates are
|
||||
required from the other side of the socket connection; an :class:`SSLError`
|
||||
will be raised if no certificate is provided, or if its validation fails.
|
||||
|
||||
Use of this setting requires a valid set of CA certificates to
|
||||
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
|
||||
value of the ``ca_certs`` parameter to :func:`wrap_socket`.
|
||||
|
||||
.. data:: PROTOCOL_SSLv2
|
||||
|
||||
|
@ -284,8 +288,8 @@ Functions, Constants, and Exceptions
|
|||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
SSLSocket Objects
|
||||
-----------------
|
||||
SSL Sockets
|
||||
-----------
|
||||
|
||||
.. method:: SSLSocket.read(nbytes=1024, buffer=None)
|
||||
|
||||
|
@ -371,6 +375,83 @@ SSLSocket Objects
|
|||
returned socket should always be used for further communication with the
|
||||
other side of the connection, rather than the original socket.
|
||||
|
||||
|
||||
SSL Contexts
|
||||
------------
|
||||
|
||||
.. class:: SSLContext(protocol)
|
||||
|
||||
An object holding various data longer-lived than single SSL connections,
|
||||
such as SSL configuration options, certificate(s) and private key(s).
|
||||
You must pass *protocol* which must be one of the ``PROTOCOL_*`` constants
|
||||
defined in this module. :data:`PROTOCOL_SSLv23` is recommended for
|
||||
maximum interoperability.
|
||||
|
||||
:class:`SSLContext` objects have the following methods and attributes:
|
||||
|
||||
.. method:: SSLContext.load_cert_chain(certfile, keyfile=None)
|
||||
|
||||
Load a private key and the corresponding certificate. The *certfile*
|
||||
string must be the path to a single file in PEM format containing the
|
||||
certificate as well as any number of CA certificates needed to establish
|
||||
the certificate's authenticity. The *keyfile* string, if present, must
|
||||
point to a file containing the private key in. Otherwise the private
|
||||
key will be taken from *certfile* as well. See the discussion of
|
||||
:ref:`ssl-certificates` for more information on how the certificate
|
||||
is stored in the *certfile*.
|
||||
|
||||
An :class:`SSLError` is raised if the private key doesn't
|
||||
match with the certificate.
|
||||
|
||||
.. method:: SSLContext.load_verify_locations(cafile=None, capath=None)
|
||||
|
||||
Load a set of "certification authority" (CA) certificates used to validate
|
||||
other peers' certificates when :data:`verify_mode` is other than
|
||||
:data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified.
|
||||
|
||||
The *cafile* string, if present, is the path to a file of concatenated
|
||||
CA certificates in PEM format. See the discussion of
|
||||
:ref:`ssl-certificates` for more information about how to arrange the
|
||||
certificates in this file.
|
||||
|
||||
The *capath* string, if present, is
|
||||
the path to a directory containing several CA certificates in PEM format,
|
||||
following an `OpenSSL specific layout
|
||||
<http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html>`_.
|
||||
|
||||
.. method:: SSLContext.set_ciphers(ciphers)
|
||||
|
||||
Set the available ciphers for sockets created with this context.
|
||||
It should be a string in the `OpenSSL cipher list format
|
||||
<http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT>`_.
|
||||
If no cipher can be selected (because compile-time options or other
|
||||
configuration forbids use of all the specified ciphers), an
|
||||
:class:`SSLError` will be raised.
|
||||
|
||||
.. note::
|
||||
when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will
|
||||
give the currently selected cipher.
|
||||
|
||||
.. method:: SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True)
|
||||
|
||||
Wrap an existing Python socket *sock* and return an :class:`SSLSocket`
|
||||
object. The SSL socket is tied to the context, its settings and
|
||||
certificates. The parameters *server_side*, *do_handshake_on_connect*
|
||||
and *suppress_ragged_eofs* have the same meaning as in the top-level
|
||||
:func:`wrap_socket` function.
|
||||
|
||||
.. attribute:: SSLContext.protocol
|
||||
|
||||
The protocol version chosen when constructing the context. This attribute
|
||||
is read-only.
|
||||
|
||||
.. attribute:: SSLContext.verify_mode
|
||||
|
||||
Whether to try to verify other peers' certificates and how to behave
|
||||
if verification fails. This attribute must be one of
|
||||
:data:`CERT_NONE`, :data:`CERT_OPTIONAL` or :data:`CERT_REQUIRED`.
|
||||
|
||||
|
||||
.. index:: single: certificates
|
||||
|
||||
.. index:: single: X509 certificate
|
||||
|
@ -416,6 +497,9 @@ and a footer line::
|
|||
... (certificate in base64 PEM encoding) ...
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Certificate chains
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The Python files which contain certificates can contain a sequence of
|
||||
certificates, sometimes called a *certificate chain*. This chain should start
|
||||
with the specific certificate for the principal who "is" the client or server,
|
||||
|
@ -439,6 +523,9 @@ certification authority's certificate::
|
|||
... (the root certificate for the CA's issuer)...
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
CA certificates
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
If you are going to require validation of the other side of the connection's
|
||||
certificate, you need to provide a "CA certs" file, filled with the certificate
|
||||
chains for each issuer you are willing to trust. Again, this file just contains
|
||||
|
@ -458,6 +545,25 @@ peer is supposed to furnish the other certificates necessary to chain from its
|
|||
certificate to a root certificate. See :rfc:`4158` for more discussion of the
|
||||
way in which certification chains can be built.
|
||||
|
||||
Combined key and certificate
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Often the private key is stored in the same file as the certificate; in this
|
||||
case, only the ``certfile`` parameter to :meth:`SSLContext.load_cert_chain`
|
||||
and :func:`wrap_socket` needs to be passed. If the private key is stored
|
||||
with the certificate, it should come before the first certificate in
|
||||
the certificate chain::
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
... (private key in base64 encoding) ...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
... (certificate in base64 PEM encoding) ...
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Self-signed certificates
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you are going to create a server that provides SSL-encrypted connection
|
||||
services, you will need to acquire a certificate for that service. There are
|
||||
many ways of acquiring appropriate certificates, such as buying one from a
|
||||
|
@ -530,8 +636,7 @@ certificate, sends some bytes, and reads part of the response::
|
|||
print(pprint.pformat(ssl_sock.getpeercert()))
|
||||
|
||||
# Set a simple HTTP request -- use http.client in actual code.
|
||||
ssl_sock.write("""GET / HTTP/1.0\r
|
||||
Host: www.verisign.com\r\n\r\n""")
|
||||
ssl_sock.write(b"GET / HTTP/1.0\r\nHost: www.verisign.com\r\n\r\n")
|
||||
|
||||
# Read a chunk of data. Will not necessarily
|
||||
# read all the data returned by the server.
|
||||
|
@ -561,39 +666,91 @@ this::
|
|||
|
||||
which is a fairly poorly-formed ``subject`` field.
|
||||
|
||||
This other example first creates an SSL context, instructs it to verify
|
||||
certificates sent by peers, and feeds it a set of recognized certificate
|
||||
authorities (CA)::
|
||||
|
||||
>>> context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
>>> context.verify_mode = ssl.CERT_OPTIONAL
|
||||
>>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
|
||||
|
||||
(it is assumed your operating system places a bundle of all CA certificates
|
||||
in ``/etc/ssl/certs/ca-bundle.crt``; if not, you'll get an error and have
|
||||
to adjust the location)
|
||||
|
||||
When you use the context to connect to a server, :const:`CERT_OPTIONAL`
|
||||
validates the server certificate: it ensures that the server certificate
|
||||
was signed with one of the CA certificates, and checks the signature for
|
||||
correctness::
|
||||
|
||||
>>> conn = context.wrap_socket(socket.socket(socket.AF_INET))
|
||||
>>> conn.connect(("linuxfr.org", 443))
|
||||
|
||||
You should then fetch the certificate and check its fields for conformity.
|
||||
Here, the ``commonName`` field in the ``subject`` matches the desired HTTPS
|
||||
host ``linuxfr.org``::
|
||||
|
||||
>>> pprint.pprint(conn.getpeercert())
|
||||
{'notAfter': 'Jun 26 21:41:46 2011 GMT',
|
||||
'subject': ((('commonName', 'linuxfr.org'),),),
|
||||
'subjectAltName': (('DNS', 'linuxfr.org'), ('othername', '<unsupported>'))}
|
||||
|
||||
Now that you are assured of its authenticity, you can proceed to talk with
|
||||
the server::
|
||||
|
||||
>>> conn.write(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n")
|
||||
38
|
||||
>>> pprint.pprint(conn.read().split(b"\r\n"))
|
||||
[b'HTTP/1.1 302 Found',
|
||||
b'Date: Sun, 16 May 2010 13:43:28 GMT',
|
||||
b'Server: Apache/2.2',
|
||||
b'Location: https://linuxfr.org/pub/',
|
||||
b'Vary: Accept-Encoding',
|
||||
b'Connection: close',
|
||||
b'Content-Type: text/html; charset=iso-8859-1',
|
||||
b'',
|
||||
b'']
|
||||
|
||||
|
||||
See the discussion of :ref:`ssl-security` below.
|
||||
|
||||
|
||||
Server-side operation
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For server operation, typically you'd need to have a server certificate, and
|
||||
private key, each in a file. You'd open a socket, bind it to a port, call
|
||||
:meth:`listen` on it, then start waiting for clients to connect::
|
||||
For server operation, typically you'll need to have a server certificate, and
|
||||
private key, each in a file. You'll first create a context holding the key
|
||||
and the certificate, so that clients can check your authenticity. Then
|
||||
you'll open a socket, bind it to a port, call :meth:`listen` on it, and start
|
||||
waiting for clients to connect::
|
||||
|
||||
import socket, ssl
|
||||
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")
|
||||
|
||||
bindsocket = socket.socket()
|
||||
bindsocket.bind(('myaddr.mydomain.com', 10023))
|
||||
bindsocket.listen(5)
|
||||
|
||||
When one did, you'd call :meth:`accept` on the socket to get the new socket from
|
||||
the other end, and use :func:`wrap_socket` to create a server-side SSL context
|
||||
for it::
|
||||
When a client connects, you'll call :meth:`accept` on the socket to get the
|
||||
new socket from the other end, and use the context's :meth:`SSLContext.wrap_socket`
|
||||
method to create a server-side SSL socket for the connection::
|
||||
|
||||
while True:
|
||||
newsocket, fromaddr = bindsocket.accept()
|
||||
connstream = ssl.wrap_socket(newsocket,
|
||||
server_side=True,
|
||||
certfile="mycertfile",
|
||||
keyfile="mykeyfile",
|
||||
ssl_version=ssl.PROTOCOL_TLSv1)
|
||||
connstream = context.wrap_socket(newsocket, server_side=True)
|
||||
try:
|
||||
deal_with_client(connstream)
|
||||
finally:
|
||||
connstream.close()
|
||||
|
||||
Then you'd read data from the ``connstream`` and do something with it till you
|
||||
Then you'll read data from the ``connstream`` and do something with it till you
|
||||
are finished with the client (or the client is finished with you)::
|
||||
|
||||
def deal_with_client(connstream):
|
||||
|
||||
data = connstream.read()
|
||||
# null data means the client is finished with us
|
||||
# empty data means the client is finished with us
|
||||
while data:
|
||||
if not do_something(connstream, data):
|
||||
# we'll assume do_something returns False
|
||||
|
@ -601,9 +758,41 @@ are finished with the client (or the client is finished with you)::
|
|||
break
|
||||
data = connstream.read()
|
||||
# finished with client
|
||||
connstream.close()
|
||||
|
||||
And go back to listening for new client connections.
|
||||
And go back to listening for new client connections (of course, a real server
|
||||
would probably handle each client connection in a separate thread, or put
|
||||
the sockets in non-blocking mode and use an event loop).
|
||||
|
||||
|
||||
.. _ssl-security:
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
Verifying certificates
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:const:`CERT_NONE` is the default. Since it does not authenticate the other
|
||||
peer, it can be insecure, especially in client mode where most of time you
|
||||
would like to ensure the authenticity of the server you're talking to.
|
||||
Therefore, when in client mode, it is highly recommended to use
|
||||
:const:`CERT_REQUIRED`. However, it is in itself not sufficient; you also
|
||||
have to check that the server certificate (obtained with
|
||||
:meth:`SSLSocket.getpeercert`) matches the desired service. The exact way
|
||||
of doing so depends on the higher-level protocol used; for example, with
|
||||
HTTPS, you'll check that the host name in the URL matches either the
|
||||
``commonName`` field in the ``subjectName``, or one of the ``DNS`` fields
|
||||
in the ``subjectAltName``.
|
||||
|
||||
In server mode, if you want to authenticate your clients using the SSL layer
|
||||
(rather than using a higher-level authentication mechanism), you'll also have
|
||||
to specify :const:`CERT_REQUIRED` and similarly check the client certificate.
|
||||
|
||||
.. note::
|
||||
|
||||
In client mode, :const:`CERT_OPTIONAL` and :const:`CERT_REQUIRED` are
|
||||
equivalent unless anonymous ciphers are enabled (they are disabled
|
||||
by default).
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
|
71
Lib/ssl.py
71
Lib/ssl.py
|
@ -59,7 +59,7 @@ import textwrap
|
|||
import _ssl # if we can't import it, let the error propagate
|
||||
|
||||
from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
|
||||
from _ssl import SSLError
|
||||
from _ssl import _SSLContext, SSLError
|
||||
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
||||
from _ssl import (PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23,
|
||||
PROTOCOL_TLSv1)
|
||||
|
@ -84,8 +84,29 @@ import base64 # for DER-to-PEM translation
|
|||
import traceback
|
||||
import errno
|
||||
|
||||
class SSLSocket(socket):
|
||||
|
||||
class SSLContext(_SSLContext):
|
||||
"""An SSLContext holds various SSL-related configuration options and
|
||||
data, such as certificates and possibly a private key."""
|
||||
|
||||
__slots__ = ('protocol',)
|
||||
|
||||
def __new__(cls, protocol, *args, **kwargs):
|
||||
return _SSLContext.__new__(cls, protocol)
|
||||
|
||||
def __init__(self, protocol):
|
||||
self.protocol = protocol
|
||||
|
||||
def wrap_socket(self, sock, server_side=False,
|
||||
do_handshake_on_connect=True,
|
||||
suppress_ragged_eofs=True):
|
||||
return SSLSocket(sock=sock, server_side=server_side,
|
||||
do_handshake_on_connect=do_handshake_on_connect,
|
||||
suppress_ragged_eofs=suppress_ragged_eofs,
|
||||
_context=self)
|
||||
|
||||
|
||||
class SSLSocket(socket):
|
||||
"""This class implements a subtype of socket.socket that wraps
|
||||
the underlying OS socket in an SSL context when necessary, and
|
||||
provides read and write methods over that channel."""
|
||||
|
@ -95,8 +116,31 @@ class SSLSocket(socket):
|
|||
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
|
||||
do_handshake_on_connect=True,
|
||||
family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None,
|
||||
suppress_ragged_eofs=True, ciphers=None):
|
||||
suppress_ragged_eofs=True, ciphers=None,
|
||||
_context=None):
|
||||
|
||||
if _context:
|
||||
self.context = _context
|
||||
else:
|
||||
if certfile and not keyfile:
|
||||
keyfile = certfile
|
||||
self.context = SSLContext(ssl_version)
|
||||
self.context.verify_mode = cert_reqs
|
||||
if ca_certs:
|
||||
self.context.load_verify_locations(ca_certs)
|
||||
if certfile:
|
||||
self.context.load_cert_chain(certfile, keyfile)
|
||||
if ciphers:
|
||||
self.context.set_ciphers(ciphers)
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
self.cert_reqs = cert_reqs
|
||||
self.ssl_version = ssl_version
|
||||
self.ca_certs = ca_certs
|
||||
self.ciphers = ciphers
|
||||
|
||||
self.do_handshake_on_connect = do_handshake_on_connect
|
||||
self.suppress_ragged_eofs = suppress_ragged_eofs
|
||||
connected = False
|
||||
if sock is not None:
|
||||
socket.__init__(self,
|
||||
|
@ -119,18 +163,12 @@ class SSLSocket(socket):
|
|||
else:
|
||||
socket.__init__(self, family=family, type=type, proto=proto)
|
||||
|
||||
if certfile and not keyfile:
|
||||
keyfile = certfile
|
||||
|
||||
self._closed = False
|
||||
self._sslobj = None
|
||||
if connected:
|
||||
# create the SSL object
|
||||
try:
|
||||
self._sslobj = _ssl.sslwrap(self, server_side,
|
||||
keyfile, certfile,
|
||||
cert_reqs, ssl_version, ca_certs,
|
||||
ciphers)
|
||||
self._sslobj = self.context._wrap_socket(self, server_side)
|
||||
if do_handshake_on_connect:
|
||||
timeout = self.gettimeout()
|
||||
if timeout == 0.0:
|
||||
|
@ -142,15 +180,6 @@ class SSLSocket(socket):
|
|||
self.close()
|
||||
raise x
|
||||
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
self.cert_reqs = cert_reqs
|
||||
self.ssl_version = ssl_version
|
||||
self.ca_certs = ca_certs
|
||||
self.ciphers = ciphers
|
||||
self.do_handshake_on_connect = do_handshake_on_connect
|
||||
self.suppress_ragged_eofs = suppress_ragged_eofs
|
||||
|
||||
def dup(self):
|
||||
raise NotImplemented("Can't dup() %s instances" %
|
||||
self.__class__.__name__)
|
||||
|
@ -331,9 +360,7 @@ class SSLSocket(socket):
|
|||
if self._sslobj:
|
||||
raise ValueError("attempt to connect already-connected SSLSocket!")
|
||||
socket.connect(self, addr)
|
||||
self._sslobj = _ssl.sslwrap(self, False, self.keyfile, self.certfile,
|
||||
self.cert_reqs, self.ssl_version,
|
||||
self.ca_certs, self.ciphers)
|
||||
self._sslobj = self.context._wrap_socket(self, False)
|
||||
try:
|
||||
if self.do_handshake_on_connect:
|
||||
self.do_handshake()
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICLDCCAdYCAQAwDQYJKoZIhvcNAQEEBQAwgaAxCzAJBgNVBAYTAlBUMRMwEQYD
|
||||
VQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5ldXJv
|
||||
bmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMTEmJy
|
||||
dXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZpMB4X
|
||||
DTk2MDkwNTAzNDI0M1oXDTk2MTAwNTAzNDI0M1owgaAxCzAJBgNVBAYTAlBUMRMw
|
||||
EQYDVQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5l
|
||||
dXJvbmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMT
|
||||
EmJydXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZp
|
||||
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNw
|
||||
L4lYKbpzzlmC5beaQXeQ2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAATAN
|
||||
BgkqhkiG9w0BAQQFAANBAFqPEKFjk6T6CKTHvaQeEAsX0/8YHPHqH/9AnhSjrwuX
|
||||
9EBc0n6bVGhN7XaXd6sJ7dym9sbsWxb+pJdurnkxjx4=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,41 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
|
||||
IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
|
||||
IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
|
||||
Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
|
||||
BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
|
||||
MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
|
||||
ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
||||
CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
|
||||
8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
|
||||
zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
|
||||
fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
|
||||
w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
|
||||
G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
|
||||
epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
|
||||
laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
|
||||
QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
|
||||
fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
|
||||
YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
|
||||
ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
|
||||
gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
|
||||
MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
|
||||
IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
|
||||
dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
|
||||
czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
|
||||
dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
|
||||
aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
|
||||
AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
|
||||
b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
|
||||
ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
|
||||
nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
|
||||
18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
|
||||
gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
|
||||
Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
|
||||
sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
|
||||
SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
|
||||
CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
|
||||
GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
|
||||
zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
|
||||
omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
|
||||
-----END CERTIFICATE-----
|
|
@ -10,6 +10,7 @@ import gc
|
|||
import os
|
||||
import errno
|
||||
import pprint
|
||||
import tempfile
|
||||
import urllib.parse, urllib.request
|
||||
import traceback
|
||||
import asyncore
|
||||
|
@ -25,8 +26,30 @@ except ImportError:
|
|||
skip_expected = True
|
||||
|
||||
HOST = support.HOST
|
||||
CERTFILE = None
|
||||
SVN_PYTHON_ORG_ROOT_CERT = None
|
||||
PROTOCOLS = [
|
||||
ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3,
|
||||
ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1
|
||||
]
|
||||
|
||||
data_file = lambda name: os.path.join(os.path.dirname(__file__), name)
|
||||
fsencode = lambda name: name.encode(sys.getfilesystemencoding(), "surrogateescape")
|
||||
|
||||
CERTFILE = data_file("keycert.pem")
|
||||
BYTES_CERTFILE = fsencode(CERTFILE)
|
||||
ONLYCERT = data_file("ssl_cert.pem")
|
||||
ONLYKEY = data_file("ssl_key.pem")
|
||||
BYTES_ONLYCERT = fsencode(ONLYCERT)
|
||||
BYTES_ONLYKEY = fsencode(ONLYKEY)
|
||||
CAPATH = data_file("capath")
|
||||
BYTES_CAPATH = fsencode(CAPATH)
|
||||
|
||||
SVN_PYTHON_ORG_ROOT_CERT = data_file("https_svn_python_org_root.pem")
|
||||
|
||||
EMPTYCERT = data_file("nullcert.pem")
|
||||
BADCERT = data_file("badcert.pem")
|
||||
WRONGCERT = data_file("XXXnonexisting.pem")
|
||||
BADKEY = data_file("badkey.pem")
|
||||
|
||||
|
||||
def handle_error(prefix):
|
||||
exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
|
||||
|
@ -34,7 +57,7 @@ def handle_error(prefix):
|
|||
sys.stdout.write(prefix + exc_format)
|
||||
|
||||
|
||||
class BasicTests(unittest.TestCase):
|
||||
class BasicSocketTests(unittest.TestCase):
|
||||
|
||||
def test_constants(self):
|
||||
ssl.PROTOCOL_SSLv2
|
||||
|
@ -116,11 +139,10 @@ class BasicTests(unittest.TestCase):
|
|||
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
|
||||
cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT")
|
||||
s.connect(remote)
|
||||
# Error checking occurs when connecting, because the SSL context
|
||||
# isn't created before.
|
||||
# Error checking can happen at instantiation or when connecting
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
|
||||
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
|
||||
cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx")
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
|
||||
s.connect(remote)
|
||||
|
||||
@support.cpython_only
|
||||
|
@ -143,25 +165,102 @@ class BasicTests(unittest.TestCase):
|
|||
self.assertEqual(timeout, ss.gettimeout())
|
||||
|
||||
|
||||
class ContextTests(unittest.TestCase):
|
||||
|
||||
def test_constructor(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv2)
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv3)
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
self.assertRaises(TypeError, ssl.SSLContext)
|
||||
self.assertRaises(ValueError, ssl.SSLContext, -1)
|
||||
self.assertRaises(ValueError, ssl.SSLContext, 42)
|
||||
|
||||
def test_protocol(self):
|
||||
for proto in PROTOCOLS:
|
||||
ctx = ssl.SSLContext(proto)
|
||||
self.assertEqual(ctx.protocol, proto)
|
||||
|
||||
def test_ciphers(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
ctx.set_ciphers("ALL")
|
||||
ctx.set_ciphers("DEFAULT")
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
|
||||
ctx.set_ciphers("^$:,;?*'dorothyx")
|
||||
|
||||
def test_verify(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
# Default value
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
|
||||
ctx.verify_mode = ssl.CERT_OPTIONAL
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_OPTIONAL)
|
||||
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
|
||||
with self.assertRaises(TypeError):
|
||||
ctx.verify_mode = None
|
||||
with self.assertRaises(ValueError):
|
||||
ctx.verify_mode = 42
|
||||
|
||||
def test_load_cert_chain(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
# Combined key and cert in a single file
|
||||
ctx.load_cert_chain(CERTFILE)
|
||||
ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE)
|
||||
self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "system lib"):
|
||||
ctx.load_cert_chain(WRONGCERT)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
|
||||
ctx.load_cert_chain(BADCERT)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
|
||||
ctx.load_cert_chain(EMPTYCERT)
|
||||
# Separate key and cert
|
||||
ctx.load_cert_chain(ONLYCERT, ONLYKEY)
|
||||
ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY)
|
||||
ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=BYTES_ONLYKEY)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
|
||||
ctx.load_cert_chain(ONLYCERT)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
|
||||
ctx.load_cert_chain(ONLYKEY)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
|
||||
ctx.load_cert_chain(certfile=ONLYKEY, keyfile=ONLYCERT)
|
||||
# Mismatching key and cert
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "key values mismatch"):
|
||||
ctx.load_cert_chain(CERTFILE, ONLYKEY)
|
||||
|
||||
def test_load_verify_locations(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
ctx.load_verify_locations(CERTFILE)
|
||||
ctx.load_verify_locations(cafile=CERTFILE, capath=None)
|
||||
ctx.load_verify_locations(BYTES_CERTFILE)
|
||||
ctx.load_verify_locations(cafile=BYTES_CERTFILE, capath=None)
|
||||
self.assertRaises(TypeError, ctx.load_verify_locations)
|
||||
self.assertRaises(TypeError, ctx.load_verify_locations, None, None)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "system lib"):
|
||||
ctx.load_verify_locations(WRONGCERT)
|
||||
with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
|
||||
ctx.load_verify_locations(BADCERT)
|
||||
ctx.load_verify_locations(CERTFILE, CAPATH)
|
||||
ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH)
|
||||
|
||||
|
||||
class NetworkedTests(unittest.TestCase):
|
||||
|
||||
def test_connect(self):
|
||||
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
|
||||
cert_reqs=ssl.CERT_NONE)
|
||||
try:
|
||||
s.connect(("svn.python.org", 443))
|
||||
c = s.getpeercert()
|
||||
if c:
|
||||
self.fail("Peer cert %s shouldn't be here!")
|
||||
self.assertEqual({}, s.getpeercert())
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
# this should fail because we have no verification certs
|
||||
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
|
||||
cert_reqs=ssl.CERT_REQUIRED)
|
||||
try:
|
||||
s.connect(("svn.python.org", 443))
|
||||
except ssl.SSLError:
|
||||
pass
|
||||
finally:
|
||||
self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed",
|
||||
s.connect, ("svn.python.org", 443))
|
||||
s.close()
|
||||
|
||||
# this should succeed because we specify the root cert
|
||||
|
@ -170,6 +269,56 @@ class NetworkedTests(unittest.TestCase):
|
|||
ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
|
||||
try:
|
||||
s.connect(("svn.python.org", 443))
|
||||
self.assertTrue(s.getpeercert())
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
def test_connect_with_context(self):
|
||||
# Same as test_connect, but with a separately created context
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
s = ctx.wrap_socket(socket.socket(socket.AF_INET))
|
||||
s.connect(("svn.python.org", 443))
|
||||
try:
|
||||
self.assertEqual({}, s.getpeercert())
|
||||
finally:
|
||||
s.close()
|
||||
# This should fail because we have no verification certs
|
||||
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||
s = ctx.wrap_socket(socket.socket(socket.AF_INET))
|
||||
self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed",
|
||||
s.connect, ("svn.python.org", 443))
|
||||
s.close()
|
||||
# This should succeed because we specify the root cert
|
||||
ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT)
|
||||
s = ctx.wrap_socket(socket.socket(socket.AF_INET))
|
||||
s.connect(("svn.python.org", 443))
|
||||
try:
|
||||
cert = s.getpeercert()
|
||||
self.assertTrue(cert)
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
def test_connect_capath(self):
|
||||
# Verify server certificates using the `capath` argument
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||
ctx.load_verify_locations(capath=CAPATH)
|
||||
s = ctx.wrap_socket(socket.socket(socket.AF_INET))
|
||||
s.connect(("svn.python.org", 443))
|
||||
try:
|
||||
cert = s.getpeercert()
|
||||
self.assertTrue(cert)
|
||||
finally:
|
||||
s.close()
|
||||
# Same with a bytes `capath` argument
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||
ctx.load_verify_locations(capath=BYTES_CAPATH)
|
||||
s = ctx.wrap_socket(socket.socket(socket.AF_INET))
|
||||
s.connect(("svn.python.org", 443))
|
||||
try:
|
||||
cert = s.getpeercert()
|
||||
self.assertTrue(cert)
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
|
@ -1227,18 +1376,14 @@ def test_main(verbose=False):
|
|||
if skip_expected:
|
||||
raise unittest.SkipTest("No SSL support")
|
||||
|
||||
global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT
|
||||
CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
|
||||
"keycert.pem")
|
||||
SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
|
||||
os.path.dirname(__file__) or os.curdir,
|
||||
"https_svn_python_org_root.pem")
|
||||
for filename in [
|
||||
CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE,
|
||||
ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY,
|
||||
BADCERT, BADKEY, EMPTYCERT]:
|
||||
if not os.path.exists(filename):
|
||||
raise support.TestFailed("Can't read certificate file %r" % filename)
|
||||
|
||||
if (not os.path.exists(CERTFILE) or
|
||||
not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
|
||||
raise support.TestFailed("Can't read certificate files!")
|
||||
|
||||
tests = [BasicTests]
|
||||
tests = [ContextTests, BasicSocketTests]
|
||||
|
||||
if support.is_resource_enabled('network'):
|
||||
tests.append(NetworkedTests)
|
||||
|
|
|
@ -363,6 +363,8 @@ C-API
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #8550: Add first class ``SSLContext`` objects to the ssl module.
|
||||
|
||||
- Issue #8681: Make the zlib module's error messages more informative when
|
||||
the zlib itself doesn't give any detailed explanation.
|
||||
|
||||
|
|
531
Modules/_ssl.c
531
Modules/_ssl.c
|
@ -113,25 +113,31 @@ static unsigned int _ssl_locks_count = 0;
|
|||
# undef HAVE_OPENSSL_RAND
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
SSL_CTX *ctx;
|
||||
} PySSLContext;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *Socket; /* weakref to socket on which we're layered */
|
||||
SSL_CTX* ctx;
|
||||
SSL* ssl;
|
||||
X509* peer_cert;
|
||||
SSL *ssl;
|
||||
X509 *peer_cert;
|
||||
int shutdown_seen_zero;
|
||||
} PySSLSocket;
|
||||
|
||||
} PySSLObject;
|
||||
static PyTypeObject PySSLContext_Type;
|
||||
static PyTypeObject PySSLSocket_Type;
|
||||
|
||||
static PyTypeObject PySSL_Type;
|
||||
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args);
|
||||
static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args);
|
||||
static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args);
|
||||
static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args);
|
||||
static int check_socket_and_wait_for_timeout(PySocketSockObject *s,
|
||||
int writing);
|
||||
static PyObject *PySSL_peercert(PySSLObject *self, PyObject *args);
|
||||
static PyObject *PySSL_cipher(PySSLObject *self);
|
||||
static PyObject *PySSL_peercert(PySSLSocket *self, PyObject *args);
|
||||
static PyObject *PySSL_cipher(PySSLSocket *self);
|
||||
|
||||
#define PySSLObject_Check(v) (Py_TYPE(v) == &PySSL_Type)
|
||||
#define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type)
|
||||
#define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type)
|
||||
|
||||
typedef enum {
|
||||
SOCKET_IS_NONBLOCKING,
|
||||
|
@ -154,7 +160,7 @@ typedef enum {
|
|||
*/
|
||||
|
||||
static PyObject *
|
||||
PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno)
|
||||
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||
{
|
||||
PyObject *v;
|
||||
char buf[2048];
|
||||
|
@ -258,126 +264,28 @@ _setSSLError (char *errstr, int errcode, char *filename, int lineno) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static PySSLObject *
|
||||
newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
||||
enum py_ssl_server_or_client socket_type,
|
||||
enum py_ssl_cert_requirements certreq,
|
||||
enum py_ssl_version proto_version,
|
||||
char *cacerts_file, char *ciphers)
|
||||
static PySSLSocket *
|
||||
newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
|
||||
enum py_ssl_server_or_client socket_type)
|
||||
{
|
||||
PySSLObject *self;
|
||||
char *errstr = NULL;
|
||||
int ret;
|
||||
int verification_mode;
|
||||
PySSLSocket *self;
|
||||
|
||||
self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */
|
||||
self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
self->peer_cert = NULL;
|
||||
self->ssl = NULL;
|
||||
self->ctx = NULL;
|
||||
self->Socket = NULL;
|
||||
|
||||
/* Make sure the SSL error state is initialized */
|
||||
(void) ERR_get_state();
|
||||
ERR_clear_error();
|
||||
|
||||
if ((key_file && !cert_file) || (!key_file && cert_file)) {
|
||||
errstr = ERRSTR("Both the key & certificate files "
|
||||
"must be specified");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((socket_type == PY_SSL_SERVER) &&
|
||||
((key_file == NULL) || (cert_file == NULL))) {
|
||||
errstr = ERRSTR("Both the key & certificate files "
|
||||
"must be specified for server-side operation");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
if (proto_version == PY_SSL_VERSION_TLS1)
|
||||
self->ctx = SSL_CTX_new(TLSv1_method()); /* Set up context */
|
||||
else if (proto_version == PY_SSL_VERSION_SSL3)
|
||||
self->ctx = SSL_CTX_new(SSLv3_method()); /* Set up context */
|
||||
else if (proto_version == PY_SSL_VERSION_SSL2)
|
||||
self->ctx = SSL_CTX_new(SSLv2_method()); /* Set up context */
|
||||
else if (proto_version == PY_SSL_VERSION_SSL23)
|
||||
self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */
|
||||
self->ssl = SSL_new(ctx);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
|
||||
if (self->ctx == NULL) {
|
||||
errstr = ERRSTR("Invalid SSL protocol variant specified.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ciphers != NULL) {
|
||||
ret = SSL_CTX_set_cipher_list(self->ctx, ciphers);
|
||||
if (ret == 0) {
|
||||
errstr = ERRSTR("No cipher can be selected.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (certreq != PY_SSL_CERT_NONE) {
|
||||
if (cacerts_file == NULL) {
|
||||
errstr = ERRSTR("No root certificates specified for "
|
||||
"verification of other-side certificates.");
|
||||
goto fail;
|
||||
} else {
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
ret = SSL_CTX_load_verify_locations(self->ctx,
|
||||
cacerts_file,
|
||||
NULL);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
if (ret != 1) {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key_file) {
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file,
|
||||
SSL_FILETYPE_PEM);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
if (ret != 1) {
|
||||
_setSSLError(NULL, ret, __FILE__, __LINE__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
ret = SSL_CTX_use_certificate_chain_file(self->ctx,
|
||||
cert_file);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
if (ret != 1) {
|
||||
/*
|
||||
fprintf(stderr, "ret is %d, errcode is %lu, %lu, with file \"%s\"\n",
|
||||
ret, ERR_peek_error(), ERR_peek_last_error(), cert_file);
|
||||
*/
|
||||
if (ERR_peek_last_error() != 0) {
|
||||
_setSSLError(NULL, ret, __FILE__, __LINE__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ssl compatibility */
|
||||
SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
|
||||
|
||||
verification_mode = SSL_VERIFY_NONE;
|
||||
if (certreq == PY_SSL_CERT_OPTIONAL)
|
||||
verification_mode = SSL_VERIFY_PEER;
|
||||
else if (certreq == PY_SSL_CERT_REQUIRED)
|
||||
verification_mode = (SSL_VERIFY_PEER |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
|
||||
SSL_CTX_set_verify(self->ctx, verification_mode,
|
||||
NULL); /* set verify lvl */
|
||||
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
self->ssl = SSL_new(self->ctx); /* New ssl struct */
|
||||
PySSL_END_ALLOW_THREADS
|
||||
SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */
|
||||
SSL_set_fd(self->ssl, sock->sock_fd);
|
||||
#ifdef SSL_MODE_AUTO_RETRY
|
||||
SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY);
|
||||
#endif
|
||||
|
@ -385,8 +293,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
|||
/* If the socket is in non-blocking mode or timeout mode, set the BIO
|
||||
* to non-blocking mode (blocking is the default)
|
||||
*/
|
||||
if (Sock->sock_timeout >= 0.0) {
|
||||
/* Set both the read and write BIO's to non-blocking mode */
|
||||
if (sock->sock_timeout >= 0.0) {
|
||||
BIO_set_nbio(SSL_get_rbio(self->ssl), 1);
|
||||
BIO_set_nbio(SSL_get_wbio(self->ssl), 1);
|
||||
}
|
||||
|
@ -398,57 +305,13 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
|
|||
SSL_set_accept_state(self->ssl);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
|
||||
self->Socket = PyWeakref_NewRef((PyObject *) Sock, Py_None);
|
||||
self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);
|
||||
return self;
|
||||
fail:
|
||||
if (errstr)
|
||||
PyErr_SetString(PySSLErrorObject, errstr);
|
||||
Py_DECREF(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PySSL_sslwrap(PyObject *self, PyObject *args)
|
||||
{
|
||||
PySocketSockObject *Sock;
|
||||
int server_side = 0;
|
||||
int verification_mode = PY_SSL_CERT_NONE;
|
||||
int protocol = PY_SSL_VERSION_SSL23;
|
||||
char *key_file = NULL;
|
||||
char *cert_file = NULL;
|
||||
char *cacerts_file = NULL;
|
||||
char *ciphers = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!i|zziizz:sslwrap",
|
||||
PySocketModule.Sock_Type,
|
||||
&Sock,
|
||||
&server_side,
|
||||
&key_file, &cert_file,
|
||||
&verification_mode, &protocol,
|
||||
&cacerts_file, &ciphers))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
fprintf(stderr,
|
||||
"server_side is %d, keyfile %p, certfile %p, verify_mode %d, "
|
||||
"protocol %d, certs %p\n",
|
||||
server_side, key_file, cert_file, verification_mode,
|
||||
protocol, cacerts_file);
|
||||
*/
|
||||
|
||||
return (PyObject *) newPySSLObject(Sock, key_file, cert_file,
|
||||
server_side, verification_mode,
|
||||
protocol, cacerts_file,
|
||||
ciphers);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(ssl_doc,
|
||||
"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n"
|
||||
" cacertsfile, ciphers]) -> sslobject");
|
||||
|
||||
/* SSL object methods */
|
||||
|
||||
static PyObject *PySSL_SSLdo_handshake(PySSLObject *self)
|
||||
static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self)
|
||||
{
|
||||
int ret;
|
||||
int err;
|
||||
|
@ -986,7 +849,7 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) {
|
|||
|
||||
|
||||
static PyObject *
|
||||
PySSL_peercert(PySSLObject *self, PyObject *args)
|
||||
PySSL_peercert(PySSLSocket *self, PyObject *args)
|
||||
{
|
||||
PyObject *retval = NULL;
|
||||
int len;
|
||||
|
@ -1017,8 +880,7 @@ PySSL_peercert(PySSLObject *self, PyObject *args)
|
|||
return retval;
|
||||
|
||||
} else {
|
||||
|
||||
verification = SSL_CTX_get_verify_mode(self->ctx);
|
||||
verification = SSL_CTX_get_verify_mode(SSL_get_SSL_CTX(self->ssl));
|
||||
if ((verification & SSL_VERIFY_PEER) == 0)
|
||||
return PyDict_New();
|
||||
else
|
||||
|
@ -1038,7 +900,7 @@ If the optional argument is True, returns a DER-encoded copy of the\n\
|
|||
peer certificate, or None if no certificate was provided. This will\n\
|
||||
return the certificate even if it wasn't validated.");
|
||||
|
||||
static PyObject *PySSL_cipher (PySSLObject *self) {
|
||||
static PyObject *PySSL_cipher (PySSLSocket *self) {
|
||||
|
||||
PyObject *retval, *v;
|
||||
SSL_CIPHER *current;
|
||||
|
@ -1084,14 +946,12 @@ static PyObject *PySSL_cipher (PySSLObject *self) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void PySSL_dealloc(PySSLObject *self)
|
||||
static void PySSL_dealloc(PySSLSocket *self)
|
||||
{
|
||||
if (self->peer_cert) /* Possible not to have one? */
|
||||
X509_free (self->peer_cert);
|
||||
if (self->ssl)
|
||||
SSL_free(self->ssl);
|
||||
if (self->ctx)
|
||||
SSL_CTX_free(self->ctx);
|
||||
Py_XDECREF(self->Socket);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
@ -1166,7 +1026,7 @@ normal_return:
|
|||
return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
|
||||
}
|
||||
|
||||
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
|
||||
static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args)
|
||||
{
|
||||
Py_buffer buf;
|
||||
int len;
|
||||
|
@ -1250,7 +1110,7 @@ PyDoc_STRVAR(PySSL_SSLwrite_doc,
|
|||
Writes the string s into the SSL object. Returns the number\n\
|
||||
of bytes written.");
|
||||
|
||||
static PyObject *PySSL_SSLpending(PySSLObject *self)
|
||||
static PyObject *PySSL_SSLpending(PySSLSocket *self)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
|
@ -1269,7 +1129,7 @@ PyDoc_STRVAR(PySSL_SSLpending_doc,
|
|||
Returns the number of already decrypted bytes available for read,\n\
|
||||
pending on the connection.\n");
|
||||
|
||||
static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
|
||||
static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args)
|
||||
{
|
||||
PyObject *dest = NULL;
|
||||
Py_buffer buf;
|
||||
|
@ -1392,7 +1252,7 @@ PyDoc_STRVAR(PySSL_SSLread_doc,
|
|||
\n\
|
||||
Read up to len bytes from the SSL socket.");
|
||||
|
||||
static PyObject *PySSL_SSLshutdown(PySSLObject *self)
|
||||
static PyObject *PySSL_SSLshutdown(PySSLSocket *self)
|
||||
{
|
||||
int err, ssl_err, sockstate, nonblocking;
|
||||
int zeros = 0;
|
||||
|
@ -1497,10 +1357,10 @@ static PyMethodDef PySSLMethods[] = {
|
|||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject PySSL_Type = {
|
||||
static PyTypeObject PySSLSocket_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"ssl.SSLContext", /*tp_name*/
|
||||
sizeof(PySSLObject), /*tp_basicsize*/
|
||||
"_ssl._SSLSocket", /*tp_name*/
|
||||
sizeof(PySSLSocket), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
/* methods */
|
||||
(destructor)PySSL_dealloc, /*tp_dealloc*/
|
||||
|
@ -1529,6 +1389,306 @@ static PyTypeObject PySSL_Type = {
|
|||
PySSLMethods, /*tp_methods*/
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* _SSLContext objects
|
||||
*/
|
||||
|
||||
static PyObject *
|
||||
context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char *kwlist[] = {"protocol", NULL};
|
||||
PySSLContext *self;
|
||||
int proto_version = PY_SSL_VERSION_SSL23;
|
||||
SSL_CTX *ctx = NULL;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args, kwds, "i:_SSLContext", kwlist,
|
||||
&proto_version))
|
||||
return NULL;
|
||||
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
if (proto_version == PY_SSL_VERSION_TLS1)
|
||||
ctx = SSL_CTX_new(TLSv1_method());
|
||||
else if (proto_version == PY_SSL_VERSION_SSL3)
|
||||
ctx = SSL_CTX_new(SSLv3_method());
|
||||
else if (proto_version == PY_SSL_VERSION_SSL2)
|
||||
ctx = SSL_CTX_new(SSLv2_method());
|
||||
else if (proto_version == PY_SSL_VERSION_SSL23)
|
||||
ctx = SSL_CTX_new(SSLv23_method());
|
||||
else
|
||||
proto_version = -1;
|
||||
PySSL_END_ALLOW_THREADS
|
||||
|
||||
if (proto_version == -1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"invalid protocol version");
|
||||
return NULL;
|
||||
}
|
||||
if (ctx == NULL) {
|
||||
PyErr_SetString(PySSLErrorObject,
|
||||
"failed to allocate SSL context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(type != NULL && type->tp_alloc != NULL);
|
||||
self = (PySSLContext *) type->tp_alloc(type, 0);
|
||||
if (self == NULL) {
|
||||
SSL_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
self->ctx = ctx;
|
||||
/* Defaults */
|
||||
SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
|
||||
SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static void
|
||||
context_dealloc(PySSLContext *self)
|
||||
{
|
||||
SSL_CTX_free(self->ctx);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
set_ciphers(PySSLContext *self, PyObject *args)
|
||||
{
|
||||
int ret;
|
||||
const char *cipherlist;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s:set_ciphers", &cipherlist))
|
||||
return NULL;
|
||||
ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist);
|
||||
if (ret == 0) {
|
||||
PyErr_SetString(PySSLErrorObject,
|
||||
"No cipher can be selected.");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
get_verify_mode(PySSLContext *self, void *c)
|
||||
{
|
||||
switch (SSL_CTX_get_verify_mode(self->ctx)) {
|
||||
case SSL_VERIFY_NONE:
|
||||
return PyLong_FromLong(PY_SSL_CERT_NONE);
|
||||
case SSL_VERIFY_PEER:
|
||||
return PyLong_FromLong(PY_SSL_CERT_OPTIONAL);
|
||||
case SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
|
||||
return PyLong_FromLong(PY_SSL_CERT_REQUIRED);
|
||||
}
|
||||
PyErr_SetString(PySSLErrorObject,
|
||||
"invalid return value from SSL_CTX_get_verify_mode");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
|
||||
{
|
||||
int n, mode;
|
||||
if (!PyArg_Parse(arg, "i", &n))
|
||||
return -1;
|
||||
if (n == PY_SSL_CERT_NONE)
|
||||
mode = SSL_VERIFY_NONE;
|
||||
else if (n == PY_SSL_CERT_OPTIONAL)
|
||||
mode = SSL_VERIFY_PEER;
|
||||
else if (n == PY_SSL_CERT_REQUIRED)
|
||||
mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
else {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"invalid value for verify_mode");
|
||||
return -1;
|
||||
}
|
||||
SSL_CTX_set_verify(self->ctx, mode, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char *kwlist[] = {"certfile", "keyfile", NULL};
|
||||
PyObject *certfile, *keyfile = NULL;
|
||||
PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL;
|
||||
int r;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds,
|
||||
"O|O:load_cert_chain", kwlist,
|
||||
&certfile, &keyfile))
|
||||
return NULL;
|
||||
if (keyfile == Py_None)
|
||||
keyfile = NULL;
|
||||
if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"certfile should be a valid filesystem path");
|
||||
return NULL;
|
||||
}
|
||||
if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"keyfile should be a valid filesystem path");
|
||||
goto error;
|
||||
}
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
r = SSL_CTX_use_certificate_chain_file(self->ctx,
|
||||
PyBytes_AS_STRING(certfile_bytes));
|
||||
PySSL_END_ALLOW_THREADS
|
||||
if (r != 1) {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
goto error;
|
||||
}
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
r = SSL_CTX_use_RSAPrivateKey_file(self->ctx,
|
||||
PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes),
|
||||
SSL_FILETYPE_PEM);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
Py_XDECREF(keyfile_bytes);
|
||||
Py_XDECREF(certfile_bytes);
|
||||
if (r != 1) {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
r = SSL_CTX_check_private_key(self->ctx);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
if (r != 1) {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
|
||||
error:
|
||||
Py_XDECREF(keyfile_bytes);
|
||||
Py_XDECREF(certfile_bytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char *kwlist[] = {"cafile", "capath", NULL};
|
||||
PyObject *cafile = NULL, *capath = NULL;
|
||||
PyObject *cafile_bytes = NULL, *capath_bytes = NULL;
|
||||
const char *cafile_buf = NULL, *capath_buf = NULL;
|
||||
int r;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds,
|
||||
"|OO:load_verify_locations", kwlist,
|
||||
&cafile, &capath))
|
||||
return NULL;
|
||||
if (cafile == Py_None)
|
||||
cafile = NULL;
|
||||
if (capath == Py_None)
|
||||
capath = NULL;
|
||||
if (cafile == NULL && capath == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"cafile and capath cannot be both omitted");
|
||||
return NULL;
|
||||
}
|
||||
if (cafile && !PyUnicode_FSConverter(cafile, &cafile_bytes)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"cafile should be a valid filesystem path");
|
||||
return NULL;
|
||||
}
|
||||
if (capath && !PyUnicode_FSConverter(capath, &capath_bytes)) {
|
||||
Py_DECREF(cafile_bytes);
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"capath should be a valid filesystem path");
|
||||
return NULL;
|
||||
}
|
||||
if (cafile)
|
||||
cafile_buf = PyBytes_AS_STRING(cafile_bytes);
|
||||
if (capath)
|
||||
capath_buf = PyBytes_AS_STRING(capath_bytes);
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
Py_XDECREF(cafile_bytes);
|
||||
Py_XDECREF(capath_bytes);
|
||||
if (r != 1) {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char *kwlist[] = {"sock", "server_side", NULL};
|
||||
PySocketSockObject *sock;
|
||||
int server_side = 0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!i:_wrap_socket", kwlist,
|
||||
PySocketModule.Sock_Type,
|
||||
&sock, &server_side))
|
||||
return NULL;
|
||||
|
||||
return (PyObject *) newPySSLSocket(self->ctx, sock, server_side);
|
||||
}
|
||||
|
||||
static PyGetSetDef context_getsetlist[] = {
|
||||
{"verify_mode", (getter) get_verify_mode,
|
||||
(setter) set_verify_mode, NULL},
|
||||
{NULL}, /* sentinel */
|
||||
};
|
||||
|
||||
static struct PyMethodDef context_methods[] = {
|
||||
{"_wrap_socket", (PyCFunction) context_wrap_socket,
|
||||
METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"set_ciphers", (PyCFunction) set_ciphers,
|
||||
METH_VARARGS, NULL},
|
||||
{"load_cert_chain", (PyCFunction) load_cert_chain,
|
||||
METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"load_verify_locations", (PyCFunction) load_verify_locations,
|
||||
METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject PySSLContext_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_ssl._SSLContext", /*tp_name*/
|
||||
sizeof(PySSLContext), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor)context_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_reserved*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash*/
|
||||
0, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||
0, /*tp_doc*/
|
||||
0, /*tp_traverse*/
|
||||
0, /*tp_clear*/
|
||||
0, /*tp_richcompare*/
|
||||
0, /*tp_weaklistoffset*/
|
||||
0, /*tp_iter*/
|
||||
0, /*tp_iternext*/
|
||||
context_methods, /*tp_methods*/
|
||||
0, /*tp_members*/
|
||||
context_getsetlist, /*tp_getset*/
|
||||
0, /*tp_base*/
|
||||
0, /*tp_dict*/
|
||||
0, /*tp_descr_get*/
|
||||
0, /*tp_descr_set*/
|
||||
0, /*tp_dictoffset*/
|
||||
0, /*tp_init*/
|
||||
0, /*tp_alloc*/
|
||||
context_new, /*tp_new*/
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE_OPENSSL_RAND
|
||||
|
||||
/* helper routines for seeding the SSL PRNG */
|
||||
|
@ -1598,8 +1758,6 @@ fails or if it does provide enough data to seed PRNG.");
|
|||
/* List of functions exported by this module. */
|
||||
|
||||
static PyMethodDef PySSL_methods[] = {
|
||||
{"sslwrap", PySSL_sslwrap,
|
||||
METH_VARARGS, ssl_doc},
|
||||
{"_test_decode_cert", PySSL_test_decode_certificate,
|
||||
METH_VARARGS},
|
||||
#ifdef HAVE_OPENSSL_RAND
|
||||
|
@ -1708,7 +1866,9 @@ PyInit__ssl(void)
|
|||
unsigned int major, minor, fix, patch, status;
|
||||
PySocketModule_APIObject *socket_api;
|
||||
|
||||
if (PyType_Ready(&PySSL_Type) < 0)
|
||||
if (PyType_Ready(&PySSLContext_Type) < 0)
|
||||
return NULL;
|
||||
if (PyType_Ready(&PySSLSocket_Type) < 0)
|
||||
return NULL;
|
||||
|
||||
m = PyModule_Create(&_sslmodule);
|
||||
|
@ -1741,8 +1901,11 @@ PyInit__ssl(void)
|
|||
return NULL;
|
||||
if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(d, "SSLType",
|
||||
(PyObject *)&PySSL_Type) != 0)
|
||||
if (PyDict_SetItemString(d, "_SSLContext",
|
||||
(PyObject *)&PySSLContext_Type) != 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(d, "_SSLSocket",
|
||||
(PyObject *)&PySSLSocket_Type) != 0)
|
||||
return NULL;
|
||||
PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN",
|
||||
PY_SSL_ERROR_ZERO_RETURN);
|
||||
|
|
Loading…
Reference in New Issue