Issue #8109: The ssl module now has support for server-side SNI, thanks to a :meth:`SSLContext.set_servername_callback` method.
Patch by Daniel Black.
This commit is contained in:
parent
3c9850aad7
commit
58ddc9d743
|
@ -533,6 +533,19 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. data:: ALERT_DESCRIPTION_HANDSHAKE_FAILURE
|
||||||
|
ALERT_DESCRIPTION_INTERNAL_ERROR
|
||||||
|
ALERT_DESCRIPTION_*
|
||||||
|
|
||||||
|
Alert Descriptions from :rfc:`5246` and others. The `IANA TLS Alert Registry
|
||||||
|
<http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6>`_
|
||||||
|
contains this list and references to the RFCs where their meaning is defined.
|
||||||
|
|
||||||
|
Used as the return value of the callback function in
|
||||||
|
:meth:`SSLContext.set_servername_callback`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
|
||||||
SSL Sockets
|
SSL Sockets
|
||||||
-----------
|
-----------
|
||||||
|
@ -780,6 +793,55 @@ to speed up repeated connections from the same clients.
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. method:: SSLContext.set_servername_callback(server_name_callback)
|
||||||
|
|
||||||
|
Register a callback function that will be called after the TLS Client Hello
|
||||||
|
handshake message has been received by the SSL/TLS server when the TLS client
|
||||||
|
specifies a server name indication. The server name indication mechanism
|
||||||
|
is specified in :rfc:`6066` section 3 - Server Name Indication.
|
||||||
|
|
||||||
|
Only one callback can be set per ``SSLContext``. If *server_name_callback*
|
||||||
|
is ``None`` then the callback is disabled. Calling this function a
|
||||||
|
subsequent time will disable the previously registered callback.
|
||||||
|
|
||||||
|
The callback function, *server_name_callback*, will be called with three
|
||||||
|
arguments; the first being the :class:`ssl.SSLSocket`, the second is a string
|
||||||
|
that represents the server name that the client is intending to communicate
|
||||||
|
and the third argument is the original :class:`SSLContext`. The server name
|
||||||
|
argument is the IDNA decoded server name.
|
||||||
|
|
||||||
|
A typical use of this callback is to change the :class:`ssl.SSLSocket`'s
|
||||||
|
:attr:`SSLSocket.context` attribute to a new object of type
|
||||||
|
:class:`SSLContext` representing a certificate chain that matches the server
|
||||||
|
name.
|
||||||
|
|
||||||
|
Due to the early negotiation phase of the TLS connection, only limited
|
||||||
|
methods and attributes are usable like
|
||||||
|
:meth:`SSLSocket.selected_npn_protocol` and :attr:`SSLSocket.context`.
|
||||||
|
:meth:`SSLSocket.getpeercert`, :meth:`SSLSocket.getpeercert`,
|
||||||
|
:meth:`SSLSocket.cipher` and :meth:`SSLSocket.compress` methods require that
|
||||||
|
the TLS connection has progressed beyond the TLS Client Hello and therefore
|
||||||
|
will not contain return meaningful values nor can they be called safely.
|
||||||
|
|
||||||
|
The *server_name_callback* function must return ``None`` to allow the
|
||||||
|
the TLS negotiation to continue. If a TLS failure is required, a constant
|
||||||
|
:const:`ALERT_DESCRIPTION_* <ALERT_DESCRIPTION_INTERNAL_ERROR>` can be
|
||||||
|
returned. Other return values will result in a TLS fatal error with
|
||||||
|
:const:`ALERT_DESCRIPTION_INTERNAL_ERROR`.
|
||||||
|
|
||||||
|
If there is a IDNA decoding error on the server name, the TLS connection
|
||||||
|
will terminate with an :const:`ALERT_DESCRIPTION_INTERNAL_ERROR` fatal TLS
|
||||||
|
alert message to the client.
|
||||||
|
|
||||||
|
If an exception is raised from the *server_name_callback* function the TLS
|
||||||
|
connection will terminate with a fatal TLS alert message
|
||||||
|
:const:`ALERT_DESCRIPTION_HANDSHAKE_FAILURE`.
|
||||||
|
|
||||||
|
This method will raise :exc:`NotImplementedError` if the OpenSSL library
|
||||||
|
had OPENSSL_NO_TLSEXT defined when it was built.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. method:: SSLContext.load_dh_params(dhfile)
|
.. method:: SSLContext.load_dh_params(dhfile)
|
||||||
|
|
||||||
Load the key generation parameters for Diffie-Helman (DH) key exchange.
|
Load the key generation parameters for Diffie-Helman (DH) key exchange.
|
||||||
|
@ -1313,3 +1375,12 @@ use the ``openssl ciphers`` command on your system.
|
||||||
|
|
||||||
`RFC 4366: Transport Layer Security (TLS) Extensions <http://www.ietf.org/rfc/rfc4366>`_
|
`RFC 4366: Transport Layer Security (TLS) Extensions <http://www.ietf.org/rfc/rfc4366>`_
|
||||||
Blake-Wilson et. al.
|
Blake-Wilson et. al.
|
||||||
|
|
||||||
|
`RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 <http://www.ietf.org/rfc/rfc5246>`_
|
||||||
|
T. Dierks et. al.
|
||||||
|
|
||||||
|
`RFC 6066: Transport Layer Security (TLS) Extensions <http://www.ietf.org/rfc/rfc6066>`_
|
||||||
|
D. Eastlake
|
||||||
|
|
||||||
|
`IANA TLS: Transport Layer Security (TLS) Parameters <http://www.iana.org/assignments/tls-parameters/tls-parameters.xml>`_
|
||||||
|
IANA
|
||||||
|
|
92
Lib/ssl.py
92
Lib/ssl.py
|
@ -52,6 +52,37 @@ PROTOCOL_SSLv2
|
||||||
PROTOCOL_SSLv3
|
PROTOCOL_SSLv3
|
||||||
PROTOCOL_SSLv23
|
PROTOCOL_SSLv23
|
||||||
PROTOCOL_TLSv1
|
PROTOCOL_TLSv1
|
||||||
|
|
||||||
|
The following constants identify various SSL alert message descriptions as per
|
||||||
|
http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6
|
||||||
|
|
||||||
|
ALERT_DESCRIPTION_CLOSE_NOTIFY
|
||||||
|
ALERT_DESCRIPTION_UNEXPECTED_MESSAGE
|
||||||
|
ALERT_DESCRIPTION_BAD_RECORD_MAC
|
||||||
|
ALERT_DESCRIPTION_RECORD_OVERFLOW
|
||||||
|
ALERT_DESCRIPTION_DECOMPRESSION_FAILURE
|
||||||
|
ALERT_DESCRIPTION_HANDSHAKE_FAILURE
|
||||||
|
ALERT_DESCRIPTION_BAD_CERTIFICATE
|
||||||
|
ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE
|
||||||
|
ALERT_DESCRIPTION_CERTIFICATE_REVOKED
|
||||||
|
ALERT_DESCRIPTION_CERTIFICATE_EXPIRED
|
||||||
|
ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN
|
||||||
|
ALERT_DESCRIPTION_ILLEGAL_PARAMETER
|
||||||
|
ALERT_DESCRIPTION_UNKNOWN_CA
|
||||||
|
ALERT_DESCRIPTION_ACCESS_DENIED
|
||||||
|
ALERT_DESCRIPTION_DECODE_ERROR
|
||||||
|
ALERT_DESCRIPTION_DECRYPT_ERROR
|
||||||
|
ALERT_DESCRIPTION_PROTOCOL_VERSION
|
||||||
|
ALERT_DESCRIPTION_INSUFFICIENT_SECURITY
|
||||||
|
ALERT_DESCRIPTION_INTERNAL_ERROR
|
||||||
|
ALERT_DESCRIPTION_USER_CANCELLED
|
||||||
|
ALERT_DESCRIPTION_NO_RENEGOTIATION
|
||||||
|
ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION
|
||||||
|
ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE
|
||||||
|
ALERT_DESCRIPTION_UNRECOGNIZED_NAME
|
||||||
|
ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE
|
||||||
|
ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE
|
||||||
|
ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
@ -66,35 +97,24 @@ from _ssl import (
|
||||||
SSLSyscallError, SSLEOFError,
|
SSLSyscallError, SSLEOFError,
|
||||||
)
|
)
|
||||||
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
||||||
from _ssl import (
|
|
||||||
OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1,
|
|
||||||
OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
from _ssl import OP_NO_COMPRESSION
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
from _ssl import OP_SINGLE_ECDH_USE
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
|
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
|
||||||
from _ssl import (
|
|
||||||
SSL_ERROR_ZERO_RETURN,
|
def _import_symbols(prefix):
|
||||||
SSL_ERROR_WANT_READ,
|
for n in dir(_ssl):
|
||||||
SSL_ERROR_WANT_WRITE,
|
if n.startswith(prefix):
|
||||||
SSL_ERROR_WANT_X509_LOOKUP,
|
globals()[n] = getattr(_ssl, n)
|
||||||
SSL_ERROR_SYSCALL,
|
|
||||||
SSL_ERROR_SSL,
|
_import_symbols('OP_')
|
||||||
SSL_ERROR_WANT_CONNECT,
|
_import_symbols('ALERT_DESCRIPTION_')
|
||||||
SSL_ERROR_EOF,
|
_import_symbols('SSL_ERROR_')
|
||||||
SSL_ERROR_INVALID_ERROR_CODE,
|
|
||||||
)
|
|
||||||
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN
|
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN
|
||||||
|
|
||||||
from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23,
|
from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23,
|
||||||
PROTOCOL_TLSv1)
|
PROTOCOL_TLSv1)
|
||||||
from _ssl import _OPENSSL_API_VERSION
|
from _ssl import _OPENSSL_API_VERSION
|
||||||
|
|
||||||
|
|
||||||
_PROTOCOL_NAMES = {
|
_PROTOCOL_NAMES = {
|
||||||
PROTOCOL_TLSv1: "TLSv1",
|
PROTOCOL_TLSv1: "TLSv1",
|
||||||
PROTOCOL_SSLv23: "SSLv23",
|
PROTOCOL_SSLv23: "SSLv23",
|
||||||
|
@ -190,7 +210,7 @@ class SSLContext(_SSLContext):
|
||||||
"""An SSLContext holds various SSL-related configuration options and
|
"""An SSLContext holds various SSL-related configuration options and
|
||||||
data, such as certificates and possibly a private key."""
|
data, such as certificates and possibly a private key."""
|
||||||
|
|
||||||
__slots__ = ('protocol',)
|
__slots__ = ('protocol', '__weakref__')
|
||||||
|
|
||||||
def __new__(cls, protocol, *args, **kwargs):
|
def __new__(cls, protocol, *args, **kwargs):
|
||||||
self = _SSLContext.__new__(cls, protocol)
|
self = _SSLContext.__new__(cls, protocol)
|
||||||
|
@ -238,7 +258,7 @@ class SSLSocket(socket):
|
||||||
_context=None):
|
_context=None):
|
||||||
|
|
||||||
if _context:
|
if _context:
|
||||||
self.context = _context
|
self._context = _context
|
||||||
else:
|
else:
|
||||||
if server_side and not certfile:
|
if server_side and not certfile:
|
||||||
raise ValueError("certfile must be specified for server-side "
|
raise ValueError("certfile must be specified for server-side "
|
||||||
|
@ -247,16 +267,16 @@ class SSLSocket(socket):
|
||||||
raise ValueError("certfile must be specified")
|
raise ValueError("certfile must be specified")
|
||||||
if certfile and not keyfile:
|
if certfile and not keyfile:
|
||||||
keyfile = certfile
|
keyfile = certfile
|
||||||
self.context = SSLContext(ssl_version)
|
self._context = SSLContext(ssl_version)
|
||||||
self.context.verify_mode = cert_reqs
|
self._context.verify_mode = cert_reqs
|
||||||
if ca_certs:
|
if ca_certs:
|
||||||
self.context.load_verify_locations(ca_certs)
|
self._context.load_verify_locations(ca_certs)
|
||||||
if certfile:
|
if certfile:
|
||||||
self.context.load_cert_chain(certfile, keyfile)
|
self._context.load_cert_chain(certfile, keyfile)
|
||||||
if npn_protocols:
|
if npn_protocols:
|
||||||
self.context.set_npn_protocols(npn_protocols)
|
self._context.set_npn_protocols(npn_protocols)
|
||||||
if ciphers:
|
if ciphers:
|
||||||
self.context.set_ciphers(ciphers)
|
self._context.set_ciphers(ciphers)
|
||||||
self.keyfile = keyfile
|
self.keyfile = keyfile
|
||||||
self.certfile = certfile
|
self.certfile = certfile
|
||||||
self.cert_reqs = cert_reqs
|
self.cert_reqs = cert_reqs
|
||||||
|
@ -298,7 +318,7 @@ class SSLSocket(socket):
|
||||||
if connected:
|
if connected:
|
||||||
# create the SSL object
|
# create the SSL object
|
||||||
try:
|
try:
|
||||||
self._sslobj = self.context._wrap_socket(self, server_side,
|
self._sslobj = self._context._wrap_socket(self, server_side,
|
||||||
server_hostname)
|
server_hostname)
|
||||||
if do_handshake_on_connect:
|
if do_handshake_on_connect:
|
||||||
timeout = self.gettimeout()
|
timeout = self.gettimeout()
|
||||||
|
@ -310,6 +330,14 @@ class SSLSocket(socket):
|
||||||
except OSError as x:
|
except OSError as x:
|
||||||
self.close()
|
self.close()
|
||||||
raise x
|
raise x
|
||||||
|
@property
|
||||||
|
def context(self):
|
||||||
|
return self._context
|
||||||
|
|
||||||
|
@context.setter
|
||||||
|
def context(self, ctx):
|
||||||
|
self._context = ctx
|
||||||
|
self._sslobj.context = ctx
|
||||||
|
|
||||||
def dup(self):
|
def dup(self):
|
||||||
raise NotImplemented("Can't dup() %s instances" %
|
raise NotImplemented("Can't dup() %s instances" %
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMLgD0kAKDb5cFyP
|
||||||
|
jbwNfR5CtewdXC+kMXAWD8DLxiTTvhMW7qVnlwOm36mZlszHKvsRf05lT4pegiFM
|
||||||
|
9z2j1OlaN+ci/X7NU22TNN6crYSiN77FjYJP464j876ndSxyD+rzys386T+1r1aZ
|
||||||
|
aggEdkj1TsSsv1zWIYKlPIjlvhuxAgMBAAECgYA0aH+T2Vf3WOPv8KdkcJg6gCRe
|
||||||
|
yJKXOWgWRcicx/CUzOEsTxmFIDPLxqAWA3k7v0B+3vjGw5Y9lycV/5XqXNoQI14j
|
||||||
|
y09iNsumds13u5AKkGdTJnZhQ7UKdoVHfuP44ZdOv/rJ5/VD6F4zWywpe90pcbK+
|
||||||
|
AWDVtusgGQBSieEl1QJBAOyVrUG5l2yoUBtd2zr/kiGm/DYyXlIthQO/A3/LngDW
|
||||||
|
5/ydGxVsT7lAVOgCsoT+0L4efTh90PjzW8LPQrPBWVMCQQDS3h/FtYYd5lfz+FNL
|
||||||
|
9CEe1F1w9l8P749uNUD0g317zv1tatIqVCsQWHfVHNdVvfQ+vSFw38OORO00Xqs9
|
||||||
|
1GJrAkBkoXXEkxCZoy4PteheO/8IWWLGGr6L7di6MzFl1lIqwT6D8L9oaV2vynFT
|
||||||
|
DnKop0pa09Unhjyw57KMNmSE2SUJAkEArloTEzpgRmCq4IK2/NpCeGdHS5uqRlbh
|
||||||
|
1VIa/xGps7EWQl5Mn8swQDel/YP3WGHTjfx7pgSegQfkyaRtGpZ9OQJAa9Vumj8m
|
||||||
|
JAAtI0Bnga8hgQx7BhTQY4CadDxyiRGOGYhwUzYVCqkb2sbVRH9HnwUaJT7cWBY3
|
||||||
|
RnJdHOMXWem7/w==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 1 (0x0)
|
||||||
|
Serial Number: 12723342612721443281 (0xb09264b1f2da21d1)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server
|
||||||
|
Validity
|
||||||
|
Not Before: Jan 4 19:47:07 2013 GMT
|
||||||
|
Not After : Nov 13 19:47:07 2022 GMT
|
||||||
|
Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=localhost
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
Public-Key: (1024 bit)
|
||||||
|
Modulus:
|
||||||
|
00:c2:e0:0f:49:00:28:36:f9:70:5c:8f:8d:bc:0d:
|
||||||
|
7d:1e:42:b5:ec:1d:5c:2f:a4:31:70:16:0f:c0:cb:
|
||||||
|
c6:24:d3:be:13:16:ee:a5:67:97:03:a6:df:a9:99:
|
||||||
|
96:cc:c7:2a:fb:11:7f:4e:65:4f:8a:5e:82:21:4c:
|
||||||
|
f7:3d:a3:d4:e9:5a:37:e7:22:fd:7e:cd:53:6d:93:
|
||||||
|
34:de:9c:ad:84:a2:37:be:c5:8d:82:4f:e3:ae:23:
|
||||||
|
f3:be:a7:75:2c:72:0f:ea:f3:ca:cd:fc:e9:3f:b5:
|
||||||
|
af:56:99:6a:08:04:76:48:f5:4e:c4:ac:bf:5c:d6:
|
||||||
|
21:82:a5:3c:88:e5:be:1b:b1
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
2f:42:5f:a3:09:2c:fa:51:88:c7:37:7f:ea:0e:63:f0:a2:9a:
|
||||||
|
e5:5a:e2:c8:20:f0:3f:60:bc:c8:0f:b6:c6:76:ce:db:83:93:
|
||||||
|
f5:a3:33:67:01:8e:04:cd:00:9a:73:fd:f3:35:86:fa:d7:13:
|
||||||
|
e2:46:c6:9d:c0:29:53:d4:a9:90:b8:77:4b:e6:83:76:e4:92:
|
||||||
|
d6:9c:50:cf:43:d0:c6:01:77:61:9a:de:9b:70:f7:72:cd:59:
|
||||||
|
00:31:69:d9:b4:ca:06:9c:6d:c3:c7:80:8c:68:e6:b5:a2:f8:
|
||||||
|
ef:1d:bb:16:9f:77:77:ef:87:62:22:9b:4d:69:a4:3a:1a:f1:
|
||||||
|
21:5e:8c:32:ac:92:fd:15:6b:18:c2:7f:15:0d:98:30:ca:75:
|
||||||
|
8f:1a:71:df:da:1d:b2:ef:9a:e8:2d:2e:02:fd:4a:3c:aa:96:
|
||||||
|
0b:06:5d:35:b3:3d:24:87:4b:e0:b0:58:60:2f:45:ac:2e:48:
|
||||||
|
8a:b0:99:10:65:27:ff:cc:b1:d8:fd:bd:26:6b:b9:0c:05:2a:
|
||||||
|
f4:45:63:35:51:07:ed:83:85:fe:6f:69:cb:bb:40:a8:ae:b6:
|
||||||
|
3b:56:4a:2d:a4:ed:6d:11:2c:4d:ed:17:24:fd:47:bc:d3:41:
|
||||||
|
a2:d3:06:fe:0c:90:d8:d8:94:26:c4:ff:cc:a1:d8:42:77:eb:
|
||||||
|
fc:a9:94:71
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICpDCCAYwCCQCwkmSx8toh0TANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJY
|
||||||
|
WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNV
|
||||||
|
BAMMDW91ci1jYS1zZXJ2ZXIwHhcNMTMwMTA0MTk0NzA3WhcNMjIxMTEzMTk0NzA3
|
||||||
|
WjBfMQswCQYDVQQGEwJYWTEXMBUGA1UEBxMOQ2FzdGxlIEFudGhyYXgxIzAhBgNV
|
||||||
|
BAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRIwEAYDVQQDEwlsb2NhbGhv
|
||||||
|
c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMLgD0kAKDb5cFyPjbwNfR5C
|
||||||
|
tewdXC+kMXAWD8DLxiTTvhMW7qVnlwOm36mZlszHKvsRf05lT4pegiFM9z2j1Ola
|
||||||
|
N+ci/X7NU22TNN6crYSiN77FjYJP464j876ndSxyD+rzys386T+1r1aZaggEdkj1
|
||||||
|
TsSsv1zWIYKlPIjlvhuxAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAC9CX6MJLPpR
|
||||||
|
iMc3f+oOY/CimuVa4sgg8D9gvMgPtsZ2ztuDk/WjM2cBjgTNAJpz/fM1hvrXE+JG
|
||||||
|
xp3AKVPUqZC4d0vmg3bkktacUM9D0MYBd2Ga3ptw93LNWQAxadm0ygacbcPHgIxo
|
||||||
|
5rWi+O8duxafd3fvh2Iim01ppDoa8SFejDKskv0VaxjCfxUNmDDKdY8acd/aHbLv
|
||||||
|
mugtLgL9SjyqlgsGXTWzPSSHS+CwWGAvRawuSIqwmRBlJ//Msdj9vSZruQwFKvRF
|
||||||
|
YzVRB+2Dhf5vacu7QKiutjtWSi2k7W0RLE3tFyT9R7zTQaLTBv4MkNjYlCbE/8yh
|
||||||
|
2EJ36/yplHE=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,73 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAK5UQiMI5VkNs2Qv
|
||||||
|
L7gUaiDdFevNUXRjU4DHAe3ZzzYLZNE69h9gO9VCSS16tJ5fT5VEu0EZyGr0e3V2
|
||||||
|
NkX0ZoU0Hc/UaY4qx7LHmn5SYZpIxhJnkf7SyHJK1zUaGlU0/LxYqIuGCtF5dqx1
|
||||||
|
L2OQhEx1GM6RydHdgX69G64LXcY5AgMBAAECgYAhsRMfJkb9ERLMl/oG/5sLQu9L
|
||||||
|
pWDKt6+ZwdxzlZbggQ85CMYshjLKIod2DLL/sLf2x1PRXyRG131M1E3k8zkkz6de
|
||||||
|
R1uDrIN/x91iuYzfLQZGh8bMY7Yjd2eoroa6R/7DjpElGejLxOAaDWO0ST2IFQy9
|
||||||
|
myTGS2jSM97wcXfsSQJBANP3jelJoS5X6BRjTSneY21wcocxVuQh8pXpErALVNsT
|
||||||
|
drrFTeaBuZp7KvbtnIM5g2WRNvaxLZlAY/hXPJvi6ncCQQDSix1cebml6EmPlEZS
|
||||||
|
Mm8gwI2F9ufUunwJmBJcz826Do0ZNGByWDAM/JQZH4FX4GfAFNuj8PUb+GQfadkx
|
||||||
|
i1DPAkEA0lVsNHojvuDsIo8HGuzarNZQT2beWjJ1jdxh9t7HrTx7LIps6rb/fhOK
|
||||||
|
Zs0R6gVAJaEbcWAPZ2tFyECInAdnsQJAUjaeXXjuxFkjOFym5PvqpvhpivEx78Bu
|
||||||
|
JPTr3rAKXmfGMxxfuOa0xK1wSyshP6ZR/RBn/+lcXPKubhHQDOegwwJAJF1DBQnN
|
||||||
|
+/tLmOPULtDwfP4Zixn+/8GmGOahFoRcu6VIGHmRilJTn6MOButw7Glv2YdeC6l/
|
||||||
|
e83Gq6ffLVfKNQ==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 1 (0x0)
|
||||||
|
Serial Number: 12723342612721443282 (0xb09264b1f2da21d2)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server
|
||||||
|
Validity
|
||||||
|
Not Before: Jan 4 19:47:07 2013 GMT
|
||||||
|
Not After : Nov 13 19:47:07 2022 GMT
|
||||||
|
Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=fakehostname
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
Public-Key: (1024 bit)
|
||||||
|
Modulus:
|
||||||
|
00:ae:54:42:23:08:e5:59:0d:b3:64:2f:2f:b8:14:
|
||||||
|
6a:20:dd:15:eb:cd:51:74:63:53:80:c7:01:ed:d9:
|
||||||
|
cf:36:0b:64:d1:3a:f6:1f:60:3b:d5:42:49:2d:7a:
|
||||||
|
b4:9e:5f:4f:95:44:bb:41:19:c8:6a:f4:7b:75:76:
|
||||||
|
36:45:f4:66:85:34:1d:cf:d4:69:8e:2a:c7:b2:c7:
|
||||||
|
9a:7e:52:61:9a:48:c6:12:67:91:fe:d2:c8:72:4a:
|
||||||
|
d7:35:1a:1a:55:34:fc:bc:58:a8:8b:86:0a:d1:79:
|
||||||
|
76:ac:75:2f:63:90:84:4c:75:18:ce:91:c9:d1:dd:
|
||||||
|
81:7e:bd:1b:ae:0b:5d:c6:39
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
ad:45:8a:8e:ef:c6:ef:04:41:5c:2c:4a:84:dc:02:76:0c:d0:
|
||||||
|
66:0f:f0:16:04:58:4d:fd:68:b7:b8:d3:a8:41:a5:5c:3c:6f:
|
||||||
|
65:3c:d1:f8:ce:43:35:e7:41:5f:53:3d:c9:2c:c3:7d:fc:56:
|
||||||
|
4a:fa:47:77:38:9d:bb:97:28:0a:3b:91:19:7f:bc:74:ae:15:
|
||||||
|
6b:bd:20:36:67:45:a5:1e:79:d7:75:e6:89:5c:6d:54:84:d1:
|
||||||
|
95:d7:a7:b4:33:3c:af:37:c4:79:8f:5e:75:dc:75:c2:18:fb:
|
||||||
|
61:6f:2d:dc:38:65:5b:ba:67:28:d0:88:d7:8d:b9:23:5a:8e:
|
||||||
|
e8:c6:bb:db:ce:d5:b8:41:2a:ce:93:08:b6:95:ad:34:20:18:
|
||||||
|
d5:3b:37:52:74:50:0b:07:2c:b0:6d:a4:4c:7b:f4:e0:fd:d1:
|
||||||
|
af:17:aa:20:cd:62:e3:f0:9d:37:69:db:41:bd:d4:1c:fb:53:
|
||||||
|
20:da:88:9d:76:26:67:ce:01:90:a7:80:1d:a9:5b:39:73:68:
|
||||||
|
54:0a:d1:2a:03:1b:8f:3c:43:5d:5d:c4:51:f1:a7:e7:11:da:
|
||||||
|
31:2c:49:06:af:04:f4:b8:3c:99:c4:20:b9:06:36:a2:00:92:
|
||||||
|
61:1d:0c:6d:24:05:e2:82:e1:47:db:a0:5f:ba:b9:fb:ba:fa:
|
||||||
|
49:12:1e:ce
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICpzCCAY8CCQCwkmSx8toh0jANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJY
|
||||||
|
WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNV
|
||||||
|
BAMMDW91ci1jYS1zZXJ2ZXIwHhcNMTMwMTA0MTk0NzA3WhcNMjIxMTEzMTk0NzA3
|
||||||
|
WjBiMQswCQYDVQQGEwJYWTEXMBUGA1UEBxMOQ2FzdGxlIEFudGhyYXgxIzAhBgNV
|
||||||
|
BAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRUwEwYDVQQDEwxmYWtlaG9z
|
||||||
|
dG5hbWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK5UQiMI5VkNs2QvL7gU
|
||||||
|
aiDdFevNUXRjU4DHAe3ZzzYLZNE69h9gO9VCSS16tJ5fT5VEu0EZyGr0e3V2NkX0
|
||||||
|
ZoU0Hc/UaY4qx7LHmn5SYZpIxhJnkf7SyHJK1zUaGlU0/LxYqIuGCtF5dqx1L2OQ
|
||||||
|
hEx1GM6RydHdgX69G64LXcY5AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAK1Fio7v
|
||||||
|
xu8EQVwsSoTcAnYM0GYP8BYEWE39aLe406hBpVw8b2U80fjOQzXnQV9TPcksw338
|
||||||
|
Vkr6R3c4nbuXKAo7kRl/vHSuFWu9IDZnRaUeedd15olcbVSE0ZXXp7QzPK83xHmP
|
||||||
|
XnXcdcIY+2FvLdw4ZVu6ZyjQiNeNuSNajujGu9vO1bhBKs6TCLaVrTQgGNU7N1J0
|
||||||
|
UAsHLLBtpEx79OD90a8XqiDNYuPwnTdp20G91Bz7UyDaiJ12JmfOAZCngB2pWzlz
|
||||||
|
aFQK0SoDG488Q11dxFHxp+cR2jEsSQavBPS4PJnEILkGNqIAkmEdDG0kBeKC4Ufb
|
||||||
|
oF+6ufu6+kkSHs4=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -2,6 +2,7 @@
|
||||||
and friends."""
|
and friends."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from subprocess import *
|
from subprocess import *
|
||||||
|
@ -20,11 +21,52 @@ req_template = """
|
||||||
|
|
||||||
[req_x509_extensions]
|
[req_x509_extensions]
|
||||||
subjectAltName = DNS:{hostname}
|
subjectAltName = DNS:{hostname}
|
||||||
|
|
||||||
|
[ ca ]
|
||||||
|
default_ca = CA_default
|
||||||
|
|
||||||
|
[ CA_default ]
|
||||||
|
dir = cadir
|
||||||
|
database = $dir/index.txt
|
||||||
|
default_md = sha1
|
||||||
|
default_days = 3600
|
||||||
|
certificate = pycacert.pem
|
||||||
|
private_key = pycakey.pem
|
||||||
|
serial = $dir/serial
|
||||||
|
RANDFILE = $dir/.rand
|
||||||
|
|
||||||
|
policy = policy_match
|
||||||
|
|
||||||
|
[ policy_match ]
|
||||||
|
countryName = match
|
||||||
|
stateOrProvinceName = optional
|
||||||
|
organizationName = match
|
||||||
|
organizationalUnitName = optional
|
||||||
|
commonName = supplied
|
||||||
|
emailAddress = optional
|
||||||
|
|
||||||
|
[ policy_anything ]
|
||||||
|
countryName = optional
|
||||||
|
stateOrProvinceName = optional
|
||||||
|
localityName = optional
|
||||||
|
organizationName = optional
|
||||||
|
organizationalUnitName = optional
|
||||||
|
commonName = supplied
|
||||||
|
emailAddress = optional
|
||||||
|
|
||||||
|
|
||||||
|
[ v3_ca ]
|
||||||
|
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid:always,issuer
|
||||||
|
basicConstraints = CA:true
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
here = os.path.abspath(os.path.dirname(__file__))
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
def make_cert_key(hostname):
|
def make_cert_key(hostname, sign=False):
|
||||||
|
print("creating cert for " + hostname)
|
||||||
tempnames = []
|
tempnames = []
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
@ -33,10 +75,25 @@ def make_cert_key(hostname):
|
||||||
try:
|
try:
|
||||||
with open(req_file, 'w') as f:
|
with open(req_file, 'w') as f:
|
||||||
f.write(req_template.format(hostname=hostname))
|
f.write(req_template.format(hostname=hostname))
|
||||||
args = ['req', '-new', '-days', '3650', '-nodes', '-x509',
|
args = ['req', '-new', '-days', '3650', '-nodes',
|
||||||
'-newkey', 'rsa:1024', '-keyout', key_file,
|
'-newkey', 'rsa:1024', '-keyout', key_file,
|
||||||
'-out', cert_file, '-config', req_file]
|
'-config', req_file]
|
||||||
|
if sign:
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
tempnames.append(f.name)
|
||||||
|
reqfile = f.name
|
||||||
|
args += ['-out', reqfile ]
|
||||||
|
|
||||||
|
else:
|
||||||
|
args += ['-x509', '-out', cert_file ]
|
||||||
check_call(['openssl'] + args)
|
check_call(['openssl'] + args)
|
||||||
|
|
||||||
|
if sign:
|
||||||
|
args = ['ca', '-config', req_file, '-out', cert_file, '-outdir', 'cadir',
|
||||||
|
'-policy', 'policy_anything', '-batch', '-infiles', reqfile ]
|
||||||
|
check_call(['openssl'] + args)
|
||||||
|
|
||||||
|
|
||||||
with open(cert_file, 'r') as f:
|
with open(cert_file, 'r') as f:
|
||||||
cert = f.read()
|
cert = f.read()
|
||||||
with open(key_file, 'r') as f:
|
with open(key_file, 'r') as f:
|
||||||
|
@ -46,6 +103,32 @@ def make_cert_key(hostname):
|
||||||
for name in tempnames:
|
for name in tempnames:
|
||||||
os.remove(name)
|
os.remove(name)
|
||||||
|
|
||||||
|
TMP_CADIR = 'cadir'
|
||||||
|
|
||||||
|
def unmake_ca():
|
||||||
|
shutil.rmtree(TMP_CADIR)
|
||||||
|
|
||||||
|
def make_ca():
|
||||||
|
os.mkdir(TMP_CADIR)
|
||||||
|
with open(os.path.join('cadir','index.txt'),'a+') as f:
|
||||||
|
pass # empty file
|
||||||
|
with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
|
||||||
|
f.write('unique_subject = no')
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile("w") as t:
|
||||||
|
t.write(req_template.format(hostname='our-ca-server'))
|
||||||
|
t.flush()
|
||||||
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
|
args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes',
|
||||||
|
'-newkey', 'rsa:2048', '-keyout', 'pycakey.pem',
|
||||||
|
'-out', f.name,
|
||||||
|
'-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=our-ca-server']
|
||||||
|
check_call(['openssl'] + args)
|
||||||
|
args = ['ca', '-config', t.name, '-create_serial',
|
||||||
|
'-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR,
|
||||||
|
'-keyfile', 'pycakey.pem', '-days', '3650',
|
||||||
|
'-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ]
|
||||||
|
check_call(['openssl'] + args)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.chdir(here)
|
os.chdir(here)
|
||||||
|
@ -54,11 +137,34 @@ if __name__ == '__main__':
|
||||||
f.write(cert)
|
f.write(cert)
|
||||||
with open('ssl_key.pem', 'w') as f:
|
with open('ssl_key.pem', 'w') as f:
|
||||||
f.write(key)
|
f.write(key)
|
||||||
|
print("password protecting ssl_key.pem in ssl_key.passwd.pem")
|
||||||
|
check_call(['openssl','rsa','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-des3','-passout','pass:somepass'])
|
||||||
|
check_call(['openssl','rsa','-in','ssl_key.pem','-out','keycert.passwd.pem','-des3','-passout','pass:somepass'])
|
||||||
|
|
||||||
with open('keycert.pem', 'w') as f:
|
with open('keycert.pem', 'w') as f:
|
||||||
f.write(key)
|
f.write(key)
|
||||||
f.write(cert)
|
f.write(cert)
|
||||||
|
|
||||||
|
with open('keycert.passwd.pem', 'a+') as f:
|
||||||
|
f.write(cert)
|
||||||
|
|
||||||
# For certificate matching tests
|
# For certificate matching tests
|
||||||
|
make_ca()
|
||||||
cert, key = make_cert_key('fakehostname')
|
cert, key = make_cert_key('fakehostname')
|
||||||
with open('keycert2.pem', 'w') as f:
|
with open('keycert2.pem', 'w') as f:
|
||||||
f.write(key)
|
f.write(key)
|
||||||
f.write(cert)
|
f.write(cert)
|
||||||
|
|
||||||
|
cert, key = make_cert_key('localhost', True)
|
||||||
|
with open('keycert3.pem', 'w') as f:
|
||||||
|
f.write(key)
|
||||||
|
f.write(cert)
|
||||||
|
|
||||||
|
cert, key = make_cert_key('fakehostname', True)
|
||||||
|
with open('keycert4.pem', 'w') as f:
|
||||||
|
f.write(key)
|
||||||
|
f.write(cert)
|
||||||
|
|
||||||
|
unmake_ca()
|
||||||
|
print("\n\nPlease change the values in test_ssl.py, test_parse_cert function related to notAfter,notBefore and serialNumber")
|
||||||
|
check_call(['openssl','x509','-in','keycert.pem','-dates','-serial','-noout'])
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 3 (0x2)
|
||||||
|
Serial Number: 12723342612721443280 (0xb09264b1f2da21d0)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server
|
||||||
|
Validity
|
||||||
|
Not Before: Jan 4 19:47:07 2013 GMT
|
||||||
|
Not After : Jan 2 19:47:07 2023 GMT
|
||||||
|
Subject: C=XY, O=Python Software Foundation CA, CN=our-ca-server
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
Public-Key: (2048 bit)
|
||||||
|
Modulus:
|
||||||
|
00:e7:de:e9:e3:0c:9f:00:b6:a1:fd:2b:5b:96:d2:
|
||||||
|
6f:cc:e0:be:86:b9:20:5e:ec:03:7a:55:ab:ea:a4:
|
||||||
|
e9:f9:49:85:d2:66:d5:ed:c7:7a:ea:56:8e:2d:8f:
|
||||||
|
e7:42:e2:62:28:a9:9f:d6:1b:8e:eb:b5:b4:9c:9f:
|
||||||
|
14:ab:df:e6:94:8b:76:1d:3e:6d:24:61:ed:0c:bf:
|
||||||
|
00:8a:61:0c:df:5c:c8:36:73:16:00:cd:47:ba:6d:
|
||||||
|
a4:a4:74:88:83:23:0a:19:fc:09:a7:3c:4a:4b:d3:
|
||||||
|
e7:1d:2d:e4:ea:4c:54:21:f3:26:db:89:37:18:d4:
|
||||||
|
02:bb:40:32:5f:a4:ff:2d:1c:f7:d4:bb:ec:8e:cf:
|
||||||
|
5c:82:ac:e6:7c:08:6c:48:85:61:07:7f:25:e0:5c:
|
||||||
|
e0:bc:34:5f:e0:b9:04:47:75:c8:47:0b:8d:bc:d6:
|
||||||
|
c8:68:5f:33:83:62:d2:20:44:35:b1:ad:81:1a:8a:
|
||||||
|
cd:bc:35:b0:5c:8b:47:d6:18:e9:9c:18:97:cc:01:
|
||||||
|
3c:29:cc:e8:1e:e4:e4:c1:b8:de:e7:c2:11:18:87:
|
||||||
|
5a:93:34:d8:a6:25:f7:14:71:eb:e4:21:a2:d2:0f:
|
||||||
|
2e:2e:d4:62:00:35:d3:d6:ef:5c:60:4b:4c:a9:14:
|
||||||
|
e2:dd:15:58:46:37:33:26:b7:e7:2e:5d:ed:42:e4:
|
||||||
|
c5:4d
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
X509v3 extensions:
|
||||||
|
X509v3 Subject Key Identifier:
|
||||||
|
BC:DD:62:D9:76:DA:1B:D2:54:6B:CF:E0:66:9B:1E:1E:7B:56:0C:0B
|
||||||
|
X509v3 Authority Key Identifier:
|
||||||
|
keyid:BC:DD:62:D9:76:DA:1B:D2:54:6B:CF:E0:66:9B:1E:1E:7B:56:0C:0B
|
||||||
|
|
||||||
|
X509v3 Basic Constraints:
|
||||||
|
CA:TRUE
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
7d:0a:f5:cb:8d:d3:5d:bd:99:8e:f8:2b:0f:ba:eb:c2:d9:a6:
|
||||||
|
27:4f:2e:7b:2f:0e:64:d8:1c:35:50:4e:ee:fc:90:b9:8d:6d:
|
||||||
|
a8:c5:c6:06:b0:af:f3:2d:bf:3b:b8:42:07:dd:18:7d:6d:95:
|
||||||
|
54:57:85:18:60:47:2f:eb:78:1b:f9:e8:17:fd:5a:0d:87:17:
|
||||||
|
28:ac:4c:6a:e6:bc:29:f4:f4:55:70:29:42:de:85:ea:ab:6c:
|
||||||
|
23:06:64:30:75:02:8e:53:bc:5e:01:33:37:cc:1e:cd:b8:a4:
|
||||||
|
fd:ca:e4:5f:65:3b:83:1c:86:f1:55:02:a0:3a:8f:db:91:b7:
|
||||||
|
40:14:b4:e7:8d:d2:ee:73:ba:e3:e5:34:2d:bc:94:6f:4e:24:
|
||||||
|
06:f7:5f:8b:0e:a7:8e:6b:de:5e:75:f4:32:9a:50:b1:44:33:
|
||||||
|
9a:d0:05:e2:78:82:ff:db:da:8a:63:eb:a9:dd:d1:bf:a0:61:
|
||||||
|
ad:e3:9e:8a:24:5d:62:0e:e7:4c:91:7f:ef:df:34:36:3b:2f:
|
||||||
|
5d:f5:84:b2:2f:c4:6d:93:96:1a:6f:30:28:f1:da:12:9a:64:
|
||||||
|
b4:40:33:1d:bd:de:2b:53:a8:ea:be:d6:bc:4e:96:f5:44:fb:
|
||||||
|
32:18:ae:d5:1f:f6:69:af:b6:4e:7b:1d:58:ec:3b:a9:53:a3:
|
||||||
|
5e:58:c8:9e
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDbTCCAlWgAwIBAgIJALCSZLHy2iHQMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV
|
||||||
|
BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEW
|
||||||
|
MBQGA1UEAwwNb3VyLWNhLXNlcnZlcjAeFw0xMzAxMDQxOTQ3MDdaFw0yMzAxMDIx
|
||||||
|
OTQ3MDdaME0xCzAJBgNVBAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUg
|
||||||
|
Rm91bmRhdGlvbiBDQTEWMBQGA1UEAwwNb3VyLWNhLXNlcnZlcjCCASIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAOfe6eMMnwC2of0rW5bSb8zgvoa5IF7sA3pV
|
||||||
|
q+qk6flJhdJm1e3HeupWji2P50LiYiipn9Ybjuu1tJyfFKvf5pSLdh0+bSRh7Qy/
|
||||||
|
AIphDN9cyDZzFgDNR7ptpKR0iIMjChn8Cac8SkvT5x0t5OpMVCHzJtuJNxjUArtA
|
||||||
|
Ml+k/y0c99S77I7PXIKs5nwIbEiFYQd/JeBc4Lw0X+C5BEd1yEcLjbzWyGhfM4Ni
|
||||||
|
0iBENbGtgRqKzbw1sFyLR9YY6ZwYl8wBPCnM6B7k5MG43ufCERiHWpM02KYl9xRx
|
||||||
|
6+QhotIPLi7UYgA109bvXGBLTKkU4t0VWEY3Mya35y5d7ULkxU0CAwEAAaNQME4w
|
||||||
|
HQYDVR0OBBYEFLzdYtl22hvSVGvP4GabHh57VgwLMB8GA1UdIwQYMBaAFLzdYtl2
|
||||||
|
2hvSVGvP4GabHh57VgwLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
|
||||||
|
AH0K9cuN0129mY74Kw+668LZpidPLnsvDmTYHDVQTu78kLmNbajFxgawr/Mtvzu4
|
||||||
|
QgfdGH1tlVRXhRhgRy/reBv56Bf9Wg2HFyisTGrmvCn09FVwKULeheqrbCMGZDB1
|
||||||
|
Ao5TvF4BMzfMHs24pP3K5F9lO4MchvFVAqA6j9uRt0AUtOeN0u5zuuPlNC28lG9O
|
||||||
|
JAb3X4sOp45r3l519DKaULFEM5rQBeJ4gv/b2opj66nd0b+gYa3jnookXWIO50yR
|
||||||
|
f+/fNDY7L131hLIvxG2TlhpvMCjx2hKaZLRAMx293itTqOq+1rxOlvVE+zIYrtUf
|
||||||
|
9mmvtk57HVjsO6lTo15YyJ4=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDn3unjDJ8AtqH9
|
||||||
|
K1uW0m/M4L6GuSBe7AN6VavqpOn5SYXSZtXtx3rqVo4tj+dC4mIoqZ/WG47rtbSc
|
||||||
|
nxSr3+aUi3YdPm0kYe0MvwCKYQzfXMg2cxYAzUe6baSkdIiDIwoZ/AmnPEpL0+cd
|
||||||
|
LeTqTFQh8ybbiTcY1AK7QDJfpP8tHPfUu+yOz1yCrOZ8CGxIhWEHfyXgXOC8NF/g
|
||||||
|
uQRHdchHC4281shoXzODYtIgRDWxrYEais28NbBci0fWGOmcGJfMATwpzOge5OTB
|
||||||
|
uN7nwhEYh1qTNNimJfcUcevkIaLSDy4u1GIANdPW71xgS0ypFOLdFVhGNzMmt+cu
|
||||||
|
Xe1C5MVNAgMBAAECggEBAJPM7QuUrPn4cLN/Ysd15lwTWn9oHDFFgkYFvCs66gXE
|
||||||
|
ju/6Kx2BjWE4wTJby09AHM/MqB0DvguT7Mf1Q2j3tPQ1HZowg8OwRDleuwp6KIls
|
||||||
|
jBbhL0Jdl/5HC67ktWvZ9wNvO/wFG1rQfT6FVajf9LUbWEaSZbOG2SLhHfsHorzu
|
||||||
|
xjTJaI3bQ/0+79B1exwk5ruwhzFRd/XpY8hls7D/RfPIuHDlBghkW3N59KFWrf5h
|
||||||
|
6bNEh2THm0+IyGcGqs0FD+QCOXyvsjwSUswqrr2ctLREOeDcd5ReUjSxYgjcJRrm
|
||||||
|
J7ceIY/+uwDJxw/OlnmBvF6pQMkKwYW2gFztu+g2t4UCgYEA/9yo01Exz4crxXsy
|
||||||
|
tAlnDJM++nZcm07rtFjTKHUfKY/cCgNTa8udM0svnfwlid/dpgLsI38gx04HHC1i
|
||||||
|
EZ4acz+ToIWedLxM0nq73//xeRWEazOvCz1mMTZaMldahTWAyzN8qVK2B/625Yy4
|
||||||
|
wNYWyweBBwEB8MzaCs73spksXOsCgYEA5/7wvhiofYGFAfMuANeJIwDL2OtBnoOv
|
||||||
|
mVNfCmi3GC38fzwyi5ZpskWDiS2woJ+LQfs9Qu4EcZbUFLd7gbeOvb5gmFUtYope
|
||||||
|
LitUUKunIR18MkQ+mQDBpQPQPhk4QJP5reCbWkrfTu7b5o/iS41s6fBTFmuzhLcT
|
||||||
|
C71vFdCyeKcCgYAiCCqYeOtELDmBOeLDmaCQRqGQ1N96dOPbCBmF/xYXBCCDYG/f
|
||||||
|
HaUaJnz96YTgstsbcrYP/p/Qgqtlbw/lQf9IpwMuzbcG1ejt8g89OyDWNyt2ytgU
|
||||||
|
iaUnFJCos3/Byh0Iah/BsdOueo2/OJl2ZMOBW80orlSgv86cs2y037TL4wKBgQDm
|
||||||
|
OOyW+MlbowhnIvfoBfwlLEkefnej4nKD6WRLZBcue5Qyf355X06Mhsc9foXlH+6G
|
||||||
|
D9h/bswiHNdhp6N82rdgPGiHQx/CxiUoE/+b/nvgNO5mw6qLE2EXbG1e8pAMJcyE
|
||||||
|
bHw+YkawggDfELI036fRj5gki8SeUz8nS1nNgElbyQKBgCRDX9Jh+MwSLu4QBWdt
|
||||||
|
/fi+lv3K6kun/fI7EOV1vCV/j871tICu7pu5BrOLxAHqoVfU9AUX299/2KjCb5pv
|
||||||
|
kjogiUK6qWCWBlfuqDNWGCoUGt1rhznUva0nNjSMy5rinBhhjpROZC2pw48lOluP
|
||||||
|
UuvXsaPph7GTqPuy4Kab12YC
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -48,6 +48,11 @@ KEY_PASSWORD = "somepass"
|
||||||
CAPATH = data_file("capath")
|
CAPATH = data_file("capath")
|
||||||
BYTES_CAPATH = os.fsencode(CAPATH)
|
BYTES_CAPATH = os.fsencode(CAPATH)
|
||||||
|
|
||||||
|
# Two keys and certs signed by the same CA (for SNI tests)
|
||||||
|
SIGNED_CERTFILE = data_file("keycert3.pem")
|
||||||
|
SIGNED_CERTFILE2 = data_file("keycert4.pem")
|
||||||
|
SIGNING_CA = data_file("pycacert.pem")
|
||||||
|
|
||||||
SVN_PYTHON_ORG_ROOT_CERT = data_file("https_svn_python_org_root.pem")
|
SVN_PYTHON_ORG_ROOT_CERT = data_file("https_svn_python_org_root.pem")
|
||||||
|
|
||||||
EMPTYCERT = data_file("nullcert.pem")
|
EMPTYCERT = data_file("nullcert.pem")
|
||||||
|
@ -59,6 +64,7 @@ NOKIACERT = data_file("nokia.pem")
|
||||||
DHFILE = data_file("dh512.pem")
|
DHFILE = data_file("dh512.pem")
|
||||||
BYTES_DHFILE = os.fsencode(DHFILE)
|
BYTES_DHFILE = os.fsencode(DHFILE)
|
||||||
|
|
||||||
|
|
||||||
def handle_error(prefix):
|
def handle_error(prefix):
|
||||||
exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
|
exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
|
@ -89,6 +95,8 @@ def skip_if_broken_ubuntu_ssl(func):
|
||||||
else:
|
else:
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
needs_sni = unittest.skipUnless(ssl.HAS_SNI, "SNI support needed for this test")
|
||||||
|
|
||||||
|
|
||||||
class BasicSocketTests(unittest.TestCase):
|
class BasicSocketTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -142,6 +150,7 @@ class BasicSocketTests(unittest.TestCase):
|
||||||
(('organizationName', 'Python Software Foundation'),),
|
(('organizationName', 'Python Software Foundation'),),
|
||||||
(('commonName', 'localhost'),))
|
(('commonName', 'localhost'),))
|
||||||
)
|
)
|
||||||
|
# Note the next three asserts will fail if the keys are regenerated
|
||||||
self.assertEqual(p['notAfter'], 'Oct 5 23:01:56 2020 GMT')
|
self.assertEqual(p['notAfter'], 'Oct 5 23:01:56 2020 GMT')
|
||||||
self.assertEqual(p['notBefore'], 'Oct 8 23:01:56 2010 GMT')
|
self.assertEqual(p['notBefore'], 'Oct 8 23:01:56 2010 GMT')
|
||||||
self.assertEqual(p['serialNumber'], 'D7C7381919AFC24E')
|
self.assertEqual(p['serialNumber'], 'D7C7381919AFC24E')
|
||||||
|
@ -585,6 +594,34 @@ class ContextTests(unittest.TestCase):
|
||||||
self.assertRaises(ValueError, ctx.set_ecdh_curve, "foo")
|
self.assertRaises(ValueError, ctx.set_ecdh_curve, "foo")
|
||||||
self.assertRaises(ValueError, ctx.set_ecdh_curve, b"foo")
|
self.assertRaises(ValueError, ctx.set_ecdh_curve, b"foo")
|
||||||
|
|
||||||
|
@needs_sni
|
||||||
|
def test_sni_callback(self):
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
|
||||||
|
# set_servername_callback expects a callable, or None
|
||||||
|
self.assertRaises(TypeError, ctx.set_servername_callback)
|
||||||
|
self.assertRaises(TypeError, ctx.set_servername_callback, 4)
|
||||||
|
self.assertRaises(TypeError, ctx.set_servername_callback, "")
|
||||||
|
self.assertRaises(TypeError, ctx.set_servername_callback, ctx)
|
||||||
|
|
||||||
|
def dummycallback(sock, servername, ctx):
|
||||||
|
pass
|
||||||
|
ctx.set_servername_callback(None)
|
||||||
|
ctx.set_servername_callback(dummycallback)
|
||||||
|
|
||||||
|
@needs_sni
|
||||||
|
def test_sni_callback_refcycle(self):
|
||||||
|
# Reference cycles through the servername callback are detected
|
||||||
|
# and cleared.
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
def dummycallback(sock, servername, ctx, cycle=ctx):
|
||||||
|
pass
|
||||||
|
ctx.set_servername_callback(dummycallback)
|
||||||
|
wr = weakref.ref(ctx)
|
||||||
|
del ctx, dummycallback
|
||||||
|
gc.collect()
|
||||||
|
self.assertIs(wr(), None)
|
||||||
|
|
||||||
|
|
||||||
class SSLErrorTests(unittest.TestCase):
|
class SSLErrorTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -1249,7 +1286,7 @@ else:
|
||||||
raise AssertionError("Use of invalid cert should have failed!")
|
raise AssertionError("Use of invalid cert should have failed!")
|
||||||
|
|
||||||
def server_params_test(client_context, server_context, indata=b"FOO\n",
|
def server_params_test(client_context, server_context, indata=b"FOO\n",
|
||||||
chatty=True, connectionchatty=False):
|
chatty=True, connectionchatty=False, sni_name=None):
|
||||||
"""
|
"""
|
||||||
Launch a server, connect a client to it and try various reads
|
Launch a server, connect a client to it and try various reads
|
||||||
and writes.
|
and writes.
|
||||||
|
@ -1259,7 +1296,8 @@ else:
|
||||||
chatty=chatty,
|
chatty=chatty,
|
||||||
connectionchatty=False)
|
connectionchatty=False)
|
||||||
with server:
|
with server:
|
||||||
with client_context.wrap_socket(socket.socket()) as s:
|
with client_context.wrap_socket(socket.socket(),
|
||||||
|
server_hostname=sni_name) as s:
|
||||||
s.connect((HOST, server.port))
|
s.connect((HOST, server.port))
|
||||||
for arg in [indata, bytearray(indata), memoryview(indata)]:
|
for arg in [indata, bytearray(indata), memoryview(indata)]:
|
||||||
if connectionchatty:
|
if connectionchatty:
|
||||||
|
@ -1283,6 +1321,7 @@ else:
|
||||||
stats.update({
|
stats.update({
|
||||||
'compression': s.compression(),
|
'compression': s.compression(),
|
||||||
'cipher': s.cipher(),
|
'cipher': s.cipher(),
|
||||||
|
'peercert': s.getpeercert(),
|
||||||
'client_npn_protocol': s.selected_npn_protocol()
|
'client_npn_protocol': s.selected_npn_protocol()
|
||||||
})
|
})
|
||||||
s.close()
|
s.close()
|
||||||
|
@ -1988,6 +2027,100 @@ else:
|
||||||
if len(stats['server_npn_protocols']) else 'nothing'
|
if len(stats['server_npn_protocols']) else 'nothing'
|
||||||
self.assertEqual(server_result, expected, msg % (server_result, "server"))
|
self.assertEqual(server_result, expected, msg % (server_result, "server"))
|
||||||
|
|
||||||
|
def sni_contexts(self):
|
||||||
|
server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
server_context.load_cert_chain(SIGNED_CERTFILE)
|
||||||
|
other_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
other_context.load_cert_chain(SIGNED_CERTFILE2)
|
||||||
|
client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
client_context.verify_mode = ssl.CERT_REQUIRED
|
||||||
|
client_context.load_verify_locations(SIGNING_CA)
|
||||||
|
return server_context, other_context, client_context
|
||||||
|
|
||||||
|
def check_common_name(self, stats, name):
|
||||||
|
cert = stats['peercert']
|
||||||
|
self.assertIn((('commonName', name),), cert['subject'])
|
||||||
|
|
||||||
|
@needs_sni
|
||||||
|
def test_sni_callback(self):
|
||||||
|
calls = []
|
||||||
|
server_context, other_context, client_context = self.sni_contexts()
|
||||||
|
|
||||||
|
def servername_cb(ssl_sock, server_name, initial_context):
|
||||||
|
calls.append((server_name, initial_context))
|
||||||
|
ssl_sock.context = other_context
|
||||||
|
server_context.set_servername_callback(servername_cb)
|
||||||
|
|
||||||
|
stats = server_params_test(client_context, server_context,
|
||||||
|
chatty=True,
|
||||||
|
sni_name='supermessage')
|
||||||
|
# The hostname was fetched properly, and the certificate was
|
||||||
|
# changed for the connection.
|
||||||
|
self.assertEqual(calls, [("supermessage", server_context)])
|
||||||
|
# CERTFILE4 was selected
|
||||||
|
self.check_common_name(stats, 'fakehostname')
|
||||||
|
|
||||||
|
# Check disabling the callback
|
||||||
|
calls = []
|
||||||
|
server_context.set_servername_callback(None)
|
||||||
|
|
||||||
|
stats = server_params_test(client_context, server_context,
|
||||||
|
chatty=True,
|
||||||
|
sni_name='notfunny')
|
||||||
|
# Certificate didn't change
|
||||||
|
self.check_common_name(stats, 'localhost')
|
||||||
|
self.assertEqual(calls, [])
|
||||||
|
|
||||||
|
@needs_sni
|
||||||
|
def test_sni_callback_alert(self):
|
||||||
|
# Returning a TLS alert is reflected to the connecting client
|
||||||
|
server_context, other_context, client_context = self.sni_contexts()
|
||||||
|
|
||||||
|
def cb_returning_alert(ssl_sock, server_name, initial_context):
|
||||||
|
return ssl.ALERT_DESCRIPTION_ACCESS_DENIED
|
||||||
|
server_context.set_servername_callback(cb_returning_alert)
|
||||||
|
|
||||||
|
with self.assertRaises(ssl.SSLError) as cm:
|
||||||
|
stats = server_params_test(client_context, server_context,
|
||||||
|
chatty=False,
|
||||||
|
sni_name='supermessage')
|
||||||
|
self.assertEqual(cm.exception.reason, 'TLSV1_ALERT_ACCESS_DENIED')
|
||||||
|
|
||||||
|
@needs_sni
|
||||||
|
def test_sni_callback_raising(self):
|
||||||
|
# Raising fails the connection with a TLS handshake failure alert.
|
||||||
|
server_context, other_context, client_context = self.sni_contexts()
|
||||||
|
|
||||||
|
def cb_raising(ssl_sock, server_name, initial_context):
|
||||||
|
1/0
|
||||||
|
server_context.set_servername_callback(cb_raising)
|
||||||
|
|
||||||
|
with self.assertRaises(ssl.SSLError) as cm, \
|
||||||
|
support.captured_stderr() as stderr:
|
||||||
|
stats = server_params_test(client_context, server_context,
|
||||||
|
chatty=False,
|
||||||
|
sni_name='supermessage')
|
||||||
|
self.assertEqual(cm.exception.reason, 'SSLV3_ALERT_HANDSHAKE_FAILURE')
|
||||||
|
self.assertIn("ZeroDivisionError", stderr.getvalue())
|
||||||
|
|
||||||
|
@needs_sni
|
||||||
|
def test_sni_callback_wrong_return_type(self):
|
||||||
|
# Returning the wrong return type terminates the TLS connection
|
||||||
|
# with an internal error alert.
|
||||||
|
server_context, other_context, client_context = self.sni_contexts()
|
||||||
|
|
||||||
|
def cb_wrong_return_type(ssl_sock, server_name, initial_context):
|
||||||
|
return "foo"
|
||||||
|
server_context.set_servername_callback(cb_wrong_return_type)
|
||||||
|
|
||||||
|
with self.assertRaises(ssl.SSLError) as cm, \
|
||||||
|
support.captured_stderr() as stderr:
|
||||||
|
stats = server_params_test(client_context, server_context,
|
||||||
|
chatty=False,
|
||||||
|
sni_name='supermessage')
|
||||||
|
self.assertEqual(cm.exception.reason, 'TLSV1_ALERT_INTERNAL_ERROR')
|
||||||
|
self.assertIn("TypeError", stderr.getvalue())
|
||||||
|
|
||||||
|
|
||||||
def test_main(verbose=False):
|
def test_main(verbose=False):
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
|
@ -2011,6 +2144,7 @@ def test_main(verbose=False):
|
||||||
for filename in [
|
for filename in [
|
||||||
CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE,
|
CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE,
|
||||||
ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY,
|
ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY,
|
||||||
|
SIGNED_CERTFILE, SIGNED_CERTFILE2, SIGNING_CA,
|
||||||
BADCERT, BADKEY, EMPTYCERT]:
|
BADCERT, BADKEY, EMPTYCERT]:
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise support.TestFailed("Can't read certificate file %r" % filename)
|
raise support.TestFailed("Can't read certificate file %r" % filename)
|
||||||
|
|
|
@ -116,6 +116,7 @@ Dominic Binks
|
||||||
Philippe Biondi
|
Philippe Biondi
|
||||||
Stuart Bishop
|
Stuart Bishop
|
||||||
Roy Bixler
|
Roy Bixler
|
||||||
|
Daniel Black
|
||||||
Jonathan Black
|
Jonathan Black
|
||||||
Renaud Blanch
|
Renaud Blanch
|
||||||
Mike Bland
|
Mike Bland
|
||||||
|
|
|
@ -204,6 +204,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #8109: The ssl module now has support for server-side SNI, thanks
|
||||||
|
to a :meth:`SSLContext.set_servername_callback` method. Patch by Daniel
|
||||||
|
Black.
|
||||||
|
|
||||||
- Issue #16860: In tempfile, use O_CLOEXEC when available to set the
|
- Issue #16860: In tempfile, use O_CLOEXEC when available to set the
|
||||||
close-on-exec flag atomically.
|
close-on-exec flag atomically.
|
||||||
|
|
||||||
|
|
253
Modules/_ssl.c
253
Modules/_ssl.c
|
@ -181,12 +181,16 @@ typedef struct {
|
||||||
char *npn_protocols;
|
char *npn_protocols;
|
||||||
int npn_protocols_len;
|
int npn_protocols_len;
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
PyObject *set_hostname;
|
||||||
|
#endif
|
||||||
} PySSLContext;
|
} PySSLContext;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
PyObject *Socket; /* weakref to socket on which we're layered */
|
PyObject *Socket; /* weakref to socket on which we're layered */
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
PySSLContext *ctx; /* weakref to SSL context */
|
||||||
X509 *peer_cert;
|
X509 *peer_cert;
|
||||||
int shutdown_seen_zero;
|
int shutdown_seen_zero;
|
||||||
enum py_ssl_server_or_client socket_type;
|
enum py_ssl_server_or_client socket_type;
|
||||||
|
@ -437,11 +441,12 @@ _setSSLError (char *errstr, int errcode, char *filename, int lineno) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static PySSLSocket *
|
static PySSLSocket *
|
||||||
newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
|
newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
|
||||||
enum py_ssl_server_or_client socket_type,
|
enum py_ssl_server_or_client socket_type,
|
||||||
char *server_hostname)
|
char *server_hostname)
|
||||||
{
|
{
|
||||||
PySSLSocket *self;
|
PySSLSocket *self;
|
||||||
|
SSL_CTX *ctx = sslctx->ctx;
|
||||||
|
|
||||||
self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
|
self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
|
||||||
if (self == NULL)
|
if (self == NULL)
|
||||||
|
@ -450,6 +455,8 @@ newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
|
||||||
self->peer_cert = NULL;
|
self->peer_cert = NULL;
|
||||||
self->ssl = NULL;
|
self->ssl = NULL;
|
||||||
self->Socket = NULL;
|
self->Socket = NULL;
|
||||||
|
self->ctx = sslctx;
|
||||||
|
Py_INCREF(sslctx);
|
||||||
|
|
||||||
/* Make sure the SSL error state is initialized */
|
/* Make sure the SSL error state is initialized */
|
||||||
(void) ERR_get_state();
|
(void) ERR_get_state();
|
||||||
|
@ -458,6 +465,7 @@ newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
|
||||||
PySSL_BEGIN_ALLOW_THREADS
|
PySSL_BEGIN_ALLOW_THREADS
|
||||||
self->ssl = SSL_new(ctx);
|
self->ssl = SSL_new(ctx);
|
||||||
PySSL_END_ALLOW_THREADS
|
PySSL_END_ALLOW_THREADS
|
||||||
|
SSL_set_app_data(self->ssl,self);
|
||||||
SSL_set_fd(self->ssl, sock->sock_fd);
|
SSL_set_fd(self->ssl, sock->sock_fd);
|
||||||
#ifdef SSL_MODE_AUTO_RETRY
|
#ifdef SSL_MODE_AUTO_RETRY
|
||||||
SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY);
|
SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY);
|
||||||
|
@ -1164,6 +1172,38 @@ static PyObject *PySSL_compression(PySSLSocket *self) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PySSLContext *PySSL_get_context(PySSLSocket *self, void *closure) {
|
||||||
|
Py_INCREF(self->ctx);
|
||||||
|
return self->ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int PySSL_set_context(PySSLSocket *self, PyObject *value,
|
||||||
|
void *closure) {
|
||||||
|
|
||||||
|
if (PyObject_TypeCheck(value, &PySSLContext_Type)) {
|
||||||
|
|
||||||
|
Py_INCREF(value);
|
||||||
|
Py_DECREF(self->ctx);
|
||||||
|
self->ctx = (PySSLContext *) value;
|
||||||
|
SSL_set_SSL_CTX(self->ssl, self->ctx->ctx);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(PySSL_set_context_doc,
|
||||||
|
"_setter_context(ctx)\n\
|
||||||
|
\
|
||||||
|
This changes the context associated with the SSLSocket. This is typically\n\
|
||||||
|
used from within a callback function set by the set_servername_callback\n\
|
||||||
|
on the SSLContext to change the certificate information associated with the\n\
|
||||||
|
SSLSocket before the cryptographic exchange handshake messages\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void PySSL_dealloc(PySSLSocket *self)
|
static void PySSL_dealloc(PySSLSocket *self)
|
||||||
{
|
{
|
||||||
if (self->peer_cert) /* Possible not to have one? */
|
if (self->peer_cert) /* Possible not to have one? */
|
||||||
|
@ -1171,6 +1211,7 @@ static void PySSL_dealloc(PySSLSocket *self)
|
||||||
if (self->ssl)
|
if (self->ssl)
|
||||||
SSL_free(self->ssl);
|
SSL_free(self->ssl);
|
||||||
Py_XDECREF(self->Socket);
|
Py_XDECREF(self->Socket);
|
||||||
|
Py_XDECREF(self->ctx);
|
||||||
PyObject_Del(self);
|
PyObject_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1606,6 +1647,12 @@ If the TLS handshake is not yet complete, None is returned");
|
||||||
|
|
||||||
#endif /* HAVE_OPENSSL_FINISHED */
|
#endif /* HAVE_OPENSSL_FINISHED */
|
||||||
|
|
||||||
|
static PyGetSetDef ssl_getsetlist[] = {
|
||||||
|
{"context", (getter) PySSL_get_context,
|
||||||
|
(setter) PySSL_set_context, PySSL_set_context_doc},
|
||||||
|
{NULL}, /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
static PyMethodDef PySSLMethods[] = {
|
static PyMethodDef PySSLMethods[] = {
|
||||||
{"do_handshake", (PyCFunction)PySSL_SSLdo_handshake, METH_NOARGS},
|
{"do_handshake", (PyCFunction)PySSL_SSLdo_handshake, METH_NOARGS},
|
||||||
{"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS,
|
{"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS,
|
||||||
|
@ -1660,6 +1707,8 @@ static PyTypeObject PySSLSocket_Type = {
|
||||||
0, /*tp_iter*/
|
0, /*tp_iter*/
|
||||||
0, /*tp_iternext*/
|
0, /*tp_iternext*/
|
||||||
PySSLMethods, /*tp_methods*/
|
PySSLMethods, /*tp_methods*/
|
||||||
|
0, /*tp_members*/
|
||||||
|
ssl_getsetlist, /*tp_getset*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1715,6 +1764,9 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
self->ctx = ctx;
|
self->ctx = ctx;
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
self->npn_protocols = NULL;
|
self->npn_protocols = NULL;
|
||||||
|
#endif
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
self->set_hostname = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Defaults */
|
/* Defaults */
|
||||||
SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
|
SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
|
||||||
|
@ -1729,9 +1781,28 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
return (PyObject *)self;
|
return (PyObject *)self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
context_traverse(PySSLContext *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
Py_VISIT(self->set_hostname);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
context_clear(PySSLContext *self)
|
||||||
|
{
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
Py_CLEAR(self->set_hostname);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
context_dealloc(PySSLContext *self)
|
context_dealloc(PySSLContext *self)
|
||||||
{
|
{
|
||||||
|
context_clear(self);
|
||||||
SSL_CTX_free(self->ctx);
|
SSL_CTX_free(self->ctx);
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
PyMem_Free(self->npn_protocols);
|
PyMem_Free(self->npn_protocols);
|
||||||
|
@ -2223,7 +2294,7 @@ context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
res = (PyObject *) newPySSLSocket(self->ctx, sock, server_side,
|
res = (PyObject *) newPySSLSocket(self, sock, server_side,
|
||||||
hostname);
|
hostname);
|
||||||
if (hostname != NULL)
|
if (hostname != NULL)
|
||||||
PyMem_Free(hostname);
|
PyMem_Free(hostname);
|
||||||
|
@ -2308,6 +2379,131 @@ set_ecdh_curve(PySSLContext *self, PyObject *name)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
static int
|
||||||
|
_servername_callback(SSL *s, int *al, void *args)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
PySSLContext *ssl_ctx = (PySSLContext *) args;
|
||||||
|
PySSLSocket *ssl;
|
||||||
|
PyObject *servername_o;
|
||||||
|
PyObject *servername_idna;
|
||||||
|
PyObject *result;
|
||||||
|
/* The high-level ssl.SSLSocket object */
|
||||||
|
PyObject *ssl_socket;
|
||||||
|
PyGILState_STATE gstate;
|
||||||
|
const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
|
||||||
|
|
||||||
|
gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
if (ssl_ctx->set_hostname == NULL) {
|
||||||
|
/* remove race condition in this the call back while if removing the
|
||||||
|
* callback is in progress */
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl = SSL_get_app_data(s);
|
||||||
|
assert(PySSLSocket_Check(ssl));
|
||||||
|
ssl_socket = PyWeakref_GetObject(ssl->Socket);
|
||||||
|
Py_INCREF(ssl_socket);
|
||||||
|
if (ssl_socket == Py_None) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
servername_o = PyBytes_FromString(servername);
|
||||||
|
if (servername_o == NULL) {
|
||||||
|
PyErr_WriteUnraisable((PyObject *) ssl_ctx);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
servername_idna = PyUnicode_FromEncodedObject(servername_o, "idna", NULL);
|
||||||
|
if (servername_idna == NULL) {
|
||||||
|
PyErr_WriteUnraisable(servername_o);
|
||||||
|
Py_DECREF(servername_o);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Py_DECREF(servername_o);
|
||||||
|
result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket,
|
||||||
|
servername_idna, ssl_ctx, NULL);
|
||||||
|
Py_DECREF(ssl_socket);
|
||||||
|
Py_DECREF(servername_idna);
|
||||||
|
|
||||||
|
if (result == NULL) {
|
||||||
|
PyErr_WriteUnraisable(ssl_ctx->set_hostname);
|
||||||
|
*al = SSL_AD_HANDSHAKE_FAILURE;
|
||||||
|
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (result != Py_None) {
|
||||||
|
*al = (int) PyLong_AsLong(result);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
PyErr_WriteUnraisable(result);
|
||||||
|
*al = SSL_AD_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
Py_DECREF(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_DECREF(ssl_socket);
|
||||||
|
*al = SSL_AD_INTERNAL_ERROR;
|
||||||
|
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(PySSL_set_servername_callback_doc,
|
||||||
|
"set_servername_callback(method)\n\
|
||||||
|
\
|
||||||
|
This sets a callback that will be called when a server name is provided by\n\
|
||||||
|
the SSL/TLS client in the SNI extension.\n\
|
||||||
|
\
|
||||||
|
If the argument is None then the callback is disabled. The method is called\n\
|
||||||
|
with the SSLSocket, the server name as a string, and the SSLContext object.\n\
|
||||||
|
See RFC 6066 for details of the SNI");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
set_servername_callback(PySSLContext *self, PyObject *args)
|
||||||
|
{
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
PyObject *cb;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &cb))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Py_CLEAR(self->set_hostname);
|
||||||
|
if (cb == Py_None) {
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!PyCallable_Check(cb)) {
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"not a callable object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(cb);
|
||||||
|
self->set_hostname = cb;
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback);
|
||||||
|
SSL_CTX_set_tlsext_servername_arg(self->ctx, self);
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
#else
|
||||||
|
PyErr_SetString(PyExc_NotImplementedError,
|
||||||
|
"The TLS extension servername callback, "
|
||||||
|
"SSL_CTX_set_tlsext_servername_callback, "
|
||||||
|
"is not in the current OpenSSL library.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef context_getsetlist[] = {
|
static PyGetSetDef context_getsetlist[] = {
|
||||||
{"options", (getter) get_options,
|
{"options", (getter) get_options,
|
||||||
(setter) set_options, NULL},
|
(setter) set_options, NULL},
|
||||||
|
@ -2337,6 +2533,8 @@ static struct PyMethodDef context_methods[] = {
|
||||||
{"set_ecdh_curve", (PyCFunction) set_ecdh_curve,
|
{"set_ecdh_curve", (PyCFunction) set_ecdh_curve,
|
||||||
METH_O, NULL},
|
METH_O, NULL},
|
||||||
#endif
|
#endif
|
||||||
|
{"set_servername_callback", (PyCFunction) set_servername_callback,
|
||||||
|
METH_VARARGS, PySSL_set_servername_callback_doc},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2360,10 +2558,10 @@ static PyTypeObject PySSLContext_Type = {
|
||||||
0, /*tp_getattro*/
|
0, /*tp_getattro*/
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
0, /*tp_doc*/
|
0, /*tp_doc*/
|
||||||
0, /*tp_traverse*/
|
(traverseproc) context_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
(inquiry) context_clear, /*tp_clear*/
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
0, /*tp_weaklistoffset*/
|
0, /*tp_weaklistoffset*/
|
||||||
0, /*tp_iter*/
|
0, /*tp_iter*/
|
||||||
|
@ -2743,6 +2941,51 @@ PyInit__ssl(void)
|
||||||
PyModule_AddIntConstant(m, "CERT_REQUIRED",
|
PyModule_AddIntConstant(m, "CERT_REQUIRED",
|
||||||
PY_SSL_CERT_REQUIRED);
|
PY_SSL_CERT_REQUIRED);
|
||||||
|
|
||||||
|
/* Alert Descriptions from ssl.h */
|
||||||
|
/* note RESERVED constants no longer intended for use have been removed */
|
||||||
|
/* http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 */
|
||||||
|
|
||||||
|
#define ADD_AD_CONSTANT(s) \
|
||||||
|
PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_"#s, \
|
||||||
|
SSL_AD_##s)
|
||||||
|
|
||||||
|
ADD_AD_CONSTANT(CLOSE_NOTIFY);
|
||||||
|
ADD_AD_CONSTANT(UNEXPECTED_MESSAGE);
|
||||||
|
ADD_AD_CONSTANT(BAD_RECORD_MAC);
|
||||||
|
ADD_AD_CONSTANT(RECORD_OVERFLOW);
|
||||||
|
ADD_AD_CONSTANT(DECOMPRESSION_FAILURE);
|
||||||
|
ADD_AD_CONSTANT(HANDSHAKE_FAILURE);
|
||||||
|
ADD_AD_CONSTANT(BAD_CERTIFICATE);
|
||||||
|
ADD_AD_CONSTANT(UNSUPPORTED_CERTIFICATE);
|
||||||
|
ADD_AD_CONSTANT(CERTIFICATE_REVOKED);
|
||||||
|
ADD_AD_CONSTANT(CERTIFICATE_EXPIRED);
|
||||||
|
ADD_AD_CONSTANT(CERTIFICATE_UNKNOWN);
|
||||||
|
ADD_AD_CONSTANT(ILLEGAL_PARAMETER);
|
||||||
|
ADD_AD_CONSTANT(UNKNOWN_CA);
|
||||||
|
ADD_AD_CONSTANT(ACCESS_DENIED);
|
||||||
|
ADD_AD_CONSTANT(DECODE_ERROR);
|
||||||
|
ADD_AD_CONSTANT(DECRYPT_ERROR);
|
||||||
|
ADD_AD_CONSTANT(PROTOCOL_VERSION);
|
||||||
|
ADD_AD_CONSTANT(INSUFFICIENT_SECURITY);
|
||||||
|
ADD_AD_CONSTANT(INTERNAL_ERROR);
|
||||||
|
ADD_AD_CONSTANT(USER_CANCELLED);
|
||||||
|
ADD_AD_CONSTANT(NO_RENEGOTIATION);
|
||||||
|
ADD_AD_CONSTANT(UNSUPPORTED_EXTENSION);
|
||||||
|
ADD_AD_CONSTANT(CERTIFICATE_UNOBTAINABLE);
|
||||||
|
ADD_AD_CONSTANT(UNRECOGNIZED_NAME);
|
||||||
|
/* Not all constants are in old OpenSSL versions */
|
||||||
|
#ifdef SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE
|
||||||
|
ADD_AD_CONSTANT(BAD_CERTIFICATE_STATUS_RESPONSE);
|
||||||
|
#endif
|
||||||
|
#ifdef SSL_AD_BAD_CERTIFICATE_HASH_VALUE
|
||||||
|
ADD_AD_CONSTANT(BAD_CERTIFICATE_HASH_VALUE);
|
||||||
|
#endif
|
||||||
|
#ifdef SSL_AD_UNKNOWN_PSK_IDENTITY
|
||||||
|
ADD_AD_CONSTANT(UNKNOWN_PSK_IDENTITY);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef ADD_AD_CONSTANT
|
||||||
|
|
||||||
/* protocol versions */
|
/* protocol versions */
|
||||||
#ifndef OPENSSL_NO_SSL2
|
#ifndef OPENSSL_NO_SSL2
|
||||||
PyModule_AddIntConstant(m, "PROTOCOL_SSLv2",
|
PyModule_AddIntConstant(m, "PROTOCOL_SSLv2",
|
||||||
|
|
Loading…
Reference in New Issue