summaryrefslogtreecommitdiff
path: root/Lib/ssl.py
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2019-05-31 11:44:05 +0200
committerGitHub <noreply@github.com>2019-05-31 11:44:05 +0200
commitc7f7069e77c58e83b847c0bfe4d5aadf6add2e68 (patch)
tree306bee26619ebc132be4b98fd60d0daf79964cf0 /Lib/ssl.py
parente9b51c0ad81da1da11ae65840ac8b50a8521373c (diff)
downloadcpython-git-c7f7069e77c58e83b847c0bfe4d5aadf6add2e68.tar.gz
bpo-34271: Add ssl debugging helpers (GH-10031)
The ssl module now can dump key material to a keylog file and trace TLS protocol messages with a tracing callback. The default and stdlib contexts also support SSLKEYLOGFILE env var. The msg_callback and related enums are private members. The feature is designed for internal debugging and not for end users. Signed-off-by: Christian Heimes <christian@python.org>
Diffstat (limited to 'Lib/ssl.py')
-rw-r--r--Lib/ssl.py172
1 files changed, 171 insertions, 1 deletions
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 793ed496c7..f5fa6aeec2 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -165,6 +165,90 @@ class TLSVersion(_IntEnum):
MAXIMUM_SUPPORTED = _ssl.PROTO_MAXIMUM_SUPPORTED
+class _TLSContentType(_IntEnum):
+ """Content types (record layer)
+
+ See RFC 8446, section B.1
+ """
+ CHANGE_CIPHER_SPEC = 20
+ ALERT = 21
+ HANDSHAKE = 22
+ APPLICATION_DATA = 23
+ # pseudo content types
+ HEADER = 0x100
+ INNER_CONTENT_TYPE = 0x101
+
+
+class _TLSAlertType(_IntEnum):
+ """Alert types for TLSContentType.ALERT messages
+
+ See RFC 8466, section B.2
+ """
+ CLOSE_NOTIFY = 0
+ UNEXPECTED_MESSAGE = 10
+ BAD_RECORD_MAC = 20
+ DECRYPTION_FAILED = 21
+ RECORD_OVERFLOW = 22
+ DECOMPRESSION_FAILURE = 30
+ HANDSHAKE_FAILURE = 40
+ NO_CERTIFICATE = 41
+ BAD_CERTIFICATE = 42
+ UNSUPPORTED_CERTIFICATE = 43
+ CERTIFICATE_REVOKED = 44
+ CERTIFICATE_EXPIRED = 45
+ CERTIFICATE_UNKNOWN = 46
+ ILLEGAL_PARAMETER = 47
+ UNKNOWN_CA = 48
+ ACCESS_DENIED = 49
+ DECODE_ERROR = 50
+ DECRYPT_ERROR = 51
+ EXPORT_RESTRICTION = 60
+ PROTOCOL_VERSION = 70
+ INSUFFICIENT_SECURITY = 71
+ INTERNAL_ERROR = 80
+ INAPPROPRIATE_FALLBACK = 86
+ USER_CANCELED = 90
+ NO_RENEGOTIATION = 100
+ MISSING_EXTENSION = 109
+ UNSUPPORTED_EXTENSION = 110
+ CERTIFICATE_UNOBTAINABLE = 111
+ UNRECOGNIZED_NAME = 112
+ BAD_CERTIFICATE_STATUS_RESPONSE = 113
+ BAD_CERTIFICATE_HASH_VALUE = 114
+ UNKNOWN_PSK_IDENTITY = 115
+ CERTIFICATE_REQUIRED = 116
+ NO_APPLICATION_PROTOCOL = 120
+
+
+class _TLSMessageType(_IntEnum):
+ """Message types (handshake protocol)
+
+ See RFC 8446, section B.3
+ """
+ HELLO_REQUEST = 0
+ CLIENT_HELLO = 1
+ SERVER_HELLO = 2
+ HELLO_VERIFY_REQUEST = 3
+ NEWSESSION_TICKET = 4
+ END_OF_EARLY_DATA = 5
+ HELLO_RETRY_REQUEST = 6
+ ENCRYPTED_EXTENSIONS = 8
+ CERTIFICATE = 11
+ SERVER_KEY_EXCHANGE = 12
+ CERTIFICATE_REQUEST = 13
+ SERVER_DONE = 14
+ CERTIFICATE_VERIFY = 15
+ CLIENT_KEY_EXCHANGE = 16
+ FINISHED = 20
+ CERTIFICATE_URL = 21
+ CERTIFICATE_STATUS = 22
+ SUPPLEMENTAL_DATA = 23
+ KEY_UPDATE = 24
+ NEXT_PROTO = 67
+ MESSAGE_HASH = 254
+ CHANGE_CIPHER_SPEC = 0x0101
+
+
if sys.platform == "win32":
from _ssl import enum_certificates, enum_crls
@@ -524,6 +608,83 @@ class SSLContext(_SSLContext):
return True
@property
+ def _msg_callback(self):
+ """TLS message callback
+
+ The message callback provides a debugging hook to analyze TLS
+ connections. The callback is called for any TLS protocol message
+ (header, handshake, alert, and more), but not for application data.
+ Due to technical limitations, the callback can't be used to filter
+ traffic or to abort a connection. Any exception raised in the
+ callback is delayed until the handshake, read, or write operation
+ has been performed.
+
+ def msg_cb(conn, direction, version, content_type, msg_type, data):
+ pass
+
+ conn
+ :class:`SSLSocket` or :class:`SSLObject` instance
+ direction
+ ``read`` or ``write``
+ version
+ :class:`TLSVersion` enum member or int for unknown version. For a
+ frame header, it's the header version.
+ content_type
+ :class:`_TLSContentType` enum member or int for unsupported
+ content type.
+ msg_type
+ Either a :class:`_TLSContentType` enum number for a header
+ message, a :class:`_TLSAlertType` enum member for an alert
+ message, a :class:`_TLSMessageType` enum member for other
+ messages, or int for unsupported message types.
+ data
+ Raw, decrypted message content as bytes
+ """
+ inner = super()._msg_callback
+ if inner is not None:
+ return inner.user_function
+ else:
+ return None
+
+ @_msg_callback.setter
+ def _msg_callback(self, callback):
+ if callback is None:
+ super(SSLContext, SSLContext)._msg_callback.__set__(self, None)
+ return
+
+ if not hasattr(callback, '__call__'):
+ raise TypeError(f"{callback} is not callable.")
+
+ def inner(conn, direction, version, content_type, msg_type, data):
+ try:
+ version = TLSVersion(version)
+ except TypeError:
+ pass
+
+ try:
+ content_type = _TLSContentType(content_type)
+ except TypeError:
+ pass
+
+ if content_type == _TLSContentType.HEADER:
+ msg_enum = _TLSContentType
+ elif content_type == _TLSContentType.ALERT:
+ msg_enum = _TLSAlertType
+ else:
+ msg_enum = _TLSMessageType
+ try:
+ msg_type = msg_enum(msg_type)
+ except TypeError:
+ pass
+
+ return callback(conn, direction, version,
+ content_type, msg_type, data)
+
+ inner.user_function = callback
+
+ super(SSLContext, SSLContext)._msg_callback.__set__(self, inner)
+
+ @property
def protocol(self):
return _SSLMethod(super().protocol)
@@ -576,6 +737,11 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
# CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system
# root CA certificates for the given purpose. This may fail silently.
context.load_default_certs(purpose)
+ # OpenSSL 1.1.1 keylog file
+ if hasattr(context, 'keylog_filename'):
+ keylogfile = os.environ.get('SSLKEYLOGFILE')
+ if keylogfile and not sys.flags.ignore_environment:
+ context.keylog_filename = keylogfile
return context
def _create_unverified_context(protocol=PROTOCOL_TLS, *, cert_reqs=CERT_NONE,
@@ -617,7 +783,11 @@ def _create_unverified_context(protocol=PROTOCOL_TLS, *, cert_reqs=CERT_NONE,
# CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system
# root CA certificates for the given purpose. This may fail silently.
context.load_default_certs(purpose)
-
+ # OpenSSL 1.1.1 keylog file
+ if hasattr(context, 'keylog_filename'):
+ keylogfile = os.environ.get('SSLKEYLOGFILE')
+ if keylogfile and not sys.flags.ignore_environment:
+ context.keylog_filename = keylogfile
return context
# Used by http.client if no context is explicitly passed.