Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-94598: Remove deprecated ssl modules features #94599

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 46 additions & 267 deletions Doc/library/ssl.rst

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions Lib/ftplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,7 @@ def auth(self):
'''Set up secure control connection by using TLS/SSL.'''
if isinstance(self.sock, ssl.SSLSocket):
raise ValueError("Already using TLS")
if self.ssl_version >= ssl.PROTOCOL_TLS:
resp = self.voidcmd('AUTH TLS')
else:
resp = self.voidcmd('AUTH SSL')
resp = self.voidcmd('AUTH TLS')
self.sock = self.context.wrap_socket(self.sock, server_hostname=self.host)
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
return resp
Expand Down
110 changes: 74 additions & 36 deletions Lib/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,8 @@

The following constants identify various SSL protocol variants:

PROTOCOL_SSLv2
PROTOCOL_SSLv3
PROTOCOL_SSLv23
PROTOCOL_TLS
PROTOCOL_TLS_CLIENT
PROTOCOL_TLS_SERVER
PROTOCOL_TLSv1
PROTOCOL_TLSv1_1
PROTOCOL_TLSv1_2

The following constants identify various SSL alert message descriptions as per
http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6
Expand Down Expand Up @@ -95,7 +88,7 @@
import os
from collections import namedtuple
from enum import Enum as _Enum, IntEnum as _IntEnum, IntFlag as _IntFlag
from enum import _simple_enum
from enum import _simple_enum, KEEP as _KEEP

import _ssl # if we can't import it, let the error propagate

Expand All @@ -122,12 +115,7 @@

_IntEnum._convert_(
'_SSLMethod', __name__,
lambda name: name.startswith('PROTOCOL_') and name != 'PROTOCOL_SSLv23',
source=_ssl)

_IntFlag._convert_(
'Options', __name__,
lambda name: name.startswith('OP_'),
lambda name: name.startswith('PROTOCOL_'),
source=_ssl)

_IntEnum._convert_(
Expand All @@ -150,18 +138,47 @@
lambda name: name.startswith('CERT_'),
source=_ssl)

PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS
_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()}

_SSLv2_IF_EXISTS = getattr(_SSLMethod, 'PROTOCOL_SSLv2', None)

@_simple_enum(_IntFlag, boundary=_KEEP)
class Options:
OP_NULL = 0

OP_CIPHER_SERVER_PREFERENCE = _ssl.OP_CIPHER_SERVER_PREFERENCE
OP_ENABLE_MIDDLEBOX_COMPAT = _ssl.OP_ENABLE_MIDDLEBOX_COMPAT
OP_IGNORE_UNEXPECTED_EOF = _ssl.OP_IGNORE_UNEXPECTED_EOF
OP_NO_COMPRESSION = _ssl.OP_NO_COMPRESSION
OP_NO_RENEGOTIATION = _ssl.OP_NO_RENEGOTIATION
OP_NO_TICKET = _ssl.OP_NO_TICKET
OP_SINGLE_DH_USE = _ssl.OP_SINGLE_DH_USE
OP_SINGLE_ECDH_USE = _ssl.OP_SINGLE_ECDH_USE
# OP_NO_SSL / OP_NO_TLS are not available as global vars. They are kept in
# in Options for human-readable representation in that SSLContext.options.
OP_NO_SSLv2 = _ssl.OP_NO_SSLv2
OP_NO_SSLv3 = _ssl.OP_NO_SSLv3
OP_NO_TLSv1 = _ssl.OP_NO_TLSv1
OP_NO_TLSv1_1 = _ssl.OP_NO_TLSv1_1
OP_NO_TLSv1_2 = _ssl.OP_NO_TLSv1_2
OP_NO_TLSv1_3 = _ssl.OP_NO_TLSv1_3

OP_ALL = _ssl.OP_ALL


OP_ALL = Options.OP_ALL
OP_CIPHER_SERVER_PREFERENCE = Options.OP_CIPHER_SERVER_PREFERENCE
OP_ENABLE_MIDDLEBOX_COMPAT = Options.OP_ENABLE_MIDDLEBOX_COMPAT
OP_IGNORE_UNEXPECTED_EOF = Options.OP_IGNORE_UNEXPECTED_EOF
OP_NO_COMPRESSION = Options.OP_NO_COMPRESSION
OP_NO_RENEGOTIATION = Options.OP_NO_RENEGOTIATION
OP_NO_TICKET = Options.OP_NO_TICKET
OP_SINGLE_DH_USE = Options.OP_SINGLE_DH_USE
OP_SINGLE_ECDH_USE = Options.OP_SINGLE_ECDH_USE


@_simple_enum(_IntEnum)
class TLSVersion:
MINIMUM_SUPPORTED = _ssl.PROTO_MINIMUM_SUPPORTED
SSLv3 = _ssl.PROTO_SSLv3
TLSv1 = _ssl.PROTO_TLSv1
TLSv1_1 = _ssl.PROTO_TLSv1_1
TLSv1_2 = _ssl.PROTO_TLSv1_2
TLSv1_3 = _ssl.PROTO_TLSv1_3
MAXIMUM_SUPPORTED = _ssl.PROTO_MAXIMUM_SUPPORTED
Expand Down Expand Up @@ -254,6 +271,39 @@ class _TLSMessageType:
CHANGE_CIPHER_SPEC = 0x0101


_REMOVED_PROTOCOLS = frozenset({
"PROTOCOL_SSLv2", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_TLS",
})
_REMOVED_OPTIONS = frozenset({
"OP_NO_SSLv2", "OP_NO_SSLv3", "OP_NO_TLSv1", "OP_NO_TLSv1_1",
"OP_NO_TLSv1_2", "OP_NO_TLSv1_3",
})

def __getattr__(name):
"""Warn about removed PROTOCOL and OP_NO constants"""
# The module __getattr__ hook does not polute the module namespaces with
# deprecated constants.
if name in _REMOVED_OPTIONS:
msg = (
"ssl.{name} is no longer supported. The constant will "
"be removed in {remove}. Use SSLContext's 'minimum_version' "
"and 'maximum_version' properties instead."
)
warnings._deprecated(name, message=msg, remove=(3, 13))
return getattr(Options, name)

if name in _REMOVED_PROTOCOLS:
msg = (
"ssl.{name} is no longer supported. The constant will "
"be removed in {remove}. Use ssl.PROTOCOL_TLS_CLIENT or "
"ssl.PROTOCOL_TLS_SERVER instead."
)
warnings._deprecated(name, message=msg, remove=(3, 13))
return NotImplemented

raise AttributeError(f'module {__name__!r} has no attribute {name!r}')


if sys.platform == "win32":
from _ssl import enum_certificates, enum_crls

Expand Down Expand Up @@ -427,17 +477,6 @@ class SSLContext(_SSLContext):
sslsocket_class = None # SSLSocket is assigned later.
sslobject_class = None # SSLObject is assigned later.

def __new__(cls, protocol=None, *args, **kwargs):

This comment was marked as outdated.

if protocol is None:
warnings.warn(
"ssl.SSLContext() without protocol argument is deprecated.",
category=DeprecationWarning,
stacklevel=2
)
protocol = PROTOCOL_TLS
self = _SSLContext.__new__(cls, protocol)
return self

def _encode_hostname(self, hostname):
if hostname is None:
return None
Expand Down Expand Up @@ -541,8 +580,6 @@ def minimum_version(self):

@minimum_version.setter
def minimum_version(self, value):
if value == TLSVersion.SSLv3:
self.options &= ~Options.OP_NO_SSLv3
super(SSLContext, SSLContext).minimum_version.__set__(self, value)

@property
Expand Down Expand Up @@ -691,9 +728,11 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
if not isinstance(purpose, _ASN1Object):
raise TypeError(purpose)

# SSLContext sets OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION,
# OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE and OP_SINGLE_ECDH_USE
# by default.
# SSLContext sets OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1, OP_NO_TLSv1_1,
# OP_NO_COMPRESSION, OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE,
# and OP_SINGLE_ECDH_USE by default.
# PROTOCOL_TLS_CLIENT enables cert and hostname verification by
# default, too.
if purpose == Purpose.SERVER_AUTH:
# verify certs and host name in client mode
context = SSLContext(PROTOCOL_TLS_CLIENT)
Expand Down Expand Up @@ -1356,7 +1395,6 @@ def version(self):
SSLContext.sslsocket_class = SSLSocket
SSLContext.sslobject_class = SSLObject


# some utility functions

def cert_time_to_seconds(cert_time):
Expand Down
22 changes: 0 additions & 22 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,6 @@ def test_create_unix_server_path_socket_error(self):

def _create_ssl_context(self, certfile, keyfile=None):
sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslcontext.options |= ssl.OP_NO_SSLv2
sslcontext.load_cert_chain(certfile, keyfile)
return sslcontext

Expand Down Expand Up @@ -988,11 +987,6 @@ def test_create_server_ssl_verify_failed(self):
lambda: proto, test_utils.SIGNED_CERTFILE)

sslcontext_client = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext_client.options |= ssl.OP_NO_SSLv2
sslcontext_client.verify_mode = ssl.CERT_REQUIRED
if hasattr(sslcontext_client, 'check_hostname'):
sslcontext_client.check_hostname = True


# no CA loaded
f_c = self.loop.create_connection(MyProto, host, port,
Expand All @@ -1018,10 +1012,6 @@ def test_create_unix_server_ssl_verify_failed(self):
lambda: proto, test_utils.SIGNED_CERTFILE)

sslcontext_client = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext_client.options |= ssl.OP_NO_SSLv2
sslcontext_client.verify_mode = ssl.CERT_REQUIRED
if hasattr(sslcontext_client, 'check_hostname'):
sslcontext_client.check_hostname = True

# no CA loaded
f_c = self.loop.create_unix_connection(MyProto, path,
Expand All @@ -1047,12 +1037,8 @@ def test_create_server_ssl_match_failed(self):
lambda: proto, test_utils.SIGNED_CERTFILE)

sslcontext_client = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext_client.options |= ssl.OP_NO_SSLv2
sslcontext_client.verify_mode = ssl.CERT_REQUIRED
sslcontext_client.load_verify_locations(
cafile=test_utils.SIGNING_CA)
if hasattr(sslcontext_client, 'check_hostname'):
sslcontext_client.check_hostname = True

# incorrect server_hostname
f_c = self.loop.create_connection(MyProto, host, port,
Expand All @@ -1078,11 +1064,7 @@ def test_create_unix_server_ssl_verified(self):
lambda: proto, test_utils.SIGNED_CERTFILE)

sslcontext_client = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext_client.options |= ssl.OP_NO_SSLv2
sslcontext_client.verify_mode = ssl.CERT_REQUIRED
sslcontext_client.load_verify_locations(cafile=test_utils.SIGNING_CA)
if hasattr(sslcontext_client, 'check_hostname'):
sslcontext_client.check_hostname = True

# Connection succeeds with correct CA and server hostname.
f_c = self.loop.create_unix_connection(MyProto, path,
Expand All @@ -1104,11 +1086,7 @@ def test_create_server_ssl_verified(self):
lambda: proto, test_utils.SIGNED_CERTFILE)

sslcontext_client = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslcontext_client.options |= ssl.OP_NO_SSLv2
sslcontext_client.verify_mode = ssl.CERT_REQUIRED
sslcontext_client.load_verify_locations(cafile=test_utils.SIGNING_CA)
if hasattr(sslcontext_client, 'check_hostname'):
sslcontext_client.check_hostname = True

# Connection succeeds with correct CA and server hostname.
f_c = self.loop.create_connection(MyProto, host, port,
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_asyncio/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ def unix_client(self, *args, **kwargs):

def _create_server_ssl_context(self, certfile, keyfile=None):
sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslcontext.options |= ssl.OP_NO_SSLv2
sslcontext.load_cert_chain(certfile, keyfile)
return sslcontext

Expand Down
Loading