import python3-3.6.8-15.1.el8

This commit is contained in:
CentOS Sources 2019-11-05 13:48:44 -05:00 committed by Andrew Lukoshko
parent 10e0f3ba3c
commit 20f7ec7cf2
11 changed files with 5778 additions and 299 deletions

View File

@ -1,245 +0,0 @@
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
index 4748ba4..986d5e9 100644
--- a/Lib/ensurepip/__init__.py
+++ b/Lib/ensurepip/__init__.py
@@ -1,8 +1,10 @@
import os
import os.path
import pkgutil
+import shutil
import sys
import tempfile
+from ensurepip import rewheel
__all__ = ["version", "bootstrap"]
@@ -24,8 +26,15 @@ def _run_pip(args, additional_paths=None):
sys.path = additional_paths + sys.path
# Install the bundled software
- import pip._internal
- return pip._internal.main(args)
+ try:
+ # pip 10
+ from pip._internal import main
+ except ImportError:
+ # pip 9
+ from pip import main
+ if args[0] in ["install", "list", "wheel"]:
+ args.append('--pre')
+ return main(args)
def version():
@@ -88,20 +97,39 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
# omit pip and easy_install
os.environ["ENSUREPIP_OPTIONS"] = "install"
+ whls = []
+ rewheel_dir = None
+ # try to see if we have system-wide versions of _PROJECTS
+ dep_records = rewheel.find_system_records([p[0] for p in _PROJECTS])
+ # TODO: check if system-wide versions are the newest ones
+ # if --upgrade is used?
+ if all(dep_records):
+ # if we have all _PROJECTS installed system-wide, we'll recreate
+ # wheels from them and install those
+ rewheel_dir = tempfile.TemporaryDirectory()
+ for dr in dep_records:
+ new_whl = rewheel.rewheel_from_record(dr, rewheel_dir.name)
+ whls.append(os.path.join(rewheel_dir.name, new_whl))
+ else:
+ # if we don't have all the _PROJECTS installed system-wide,
+ # let's just fall back to bundled wheels
+ for project, version in _PROJECTS:
+ whl = os.path.join(
+ os.path.dirname(__file__),
+ "_bundled",
+ "{}-{}-py2.py3-none-any.whl".format(project, version)
+ )
+ whls.append(whl)
+
with tempfile.TemporaryDirectory() as tmpdir:
# Put our bundled wheels into a temporary directory and construct the
# additional paths that need added to sys.path
additional_paths = []
- for project, version in _PROJECTS:
- wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
- whl = pkgutil.get_data(
- "ensurepip",
- "_bundled/{}".format(wheel_name),
- )
- with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
- fp.write(whl)
-
- additional_paths.append(os.path.join(tmpdir, wheel_name))
+ for whl in whls:
+ shutil.copy(whl, tmpdir)
+ additional_paths.append(os.path.join(tmpdir, os.path.basename(whl)))
+ if rewheel_dir:
+ rewheel_dir.cleanup()
# Construct the arguments to be passed to the pip command
args = ["install", "--no-index", "--find-links", tmpdir]
diff --git a/Lib/ensurepip/rewheel/__init__.py b/Lib/ensurepip/rewheel/__init__.py
new file mode 100644
index 0000000..753c764
--- /dev/null
+++ b/Lib/ensurepip/rewheel/__init__.py
@@ -0,0 +1,143 @@
+import argparse
+import codecs
+import csv
+import email.parser
+import os
+import io
+import re
+import site
+import subprocess
+import sys
+import zipfile
+
+def run():
+ parser = argparse.ArgumentParser(description='Recreate wheel of package with given RECORD.')
+ parser.add_argument('record_path',
+ help='Path to RECORD file')
+ parser.add_argument('-o', '--output-dir',
+ help='Dir where to place the wheel, defaults to current working dir.',
+ dest='outdir',
+ default=os.path.curdir)
+
+ ns = parser.parse_args()
+ retcode = 0
+ try:
+ print(rewheel_from_record(**vars(ns)))
+ except BaseException as e:
+ print('Failed: {}'.format(e))
+ retcode = 1
+ sys.exit(1)
+
+def find_system_records(projects):
+ """Return list of paths to RECORD files for system-installed projects.
+
+ If a project is not installed, the resulting list contains None instead
+ of a path to its RECORD
+ """
+ records = []
+ # get system site-packages dirs
+ sys_sitepack = site.getsitepackages([sys.base_prefix, sys.base_exec_prefix])
+ sys_sitepack = [sp for sp in sys_sitepack if os.path.exists(sp)]
+ # try to find all projects in all system site-packages
+ for project in projects:
+ path = None
+ for sp in sys_sitepack:
+ dist_info_re = os.path.join(sp, project) + r'-[^\{0}]+\.dist-info'.format(os.sep)
+ candidates = [os.path.join(sp, p) for p in os.listdir(sp)]
+ # filter out candidate dirs based on the above regexp
+ filtered = [c for c in candidates if re.match(dist_info_re, c)]
+ # if we have 0 or 2 or more dirs, something is wrong...
+ if len(filtered) == 1:
+ path = filtered[0]
+ if path is not None:
+ records.append(os.path.join(path, 'RECORD'))
+ else:
+ records.append(None)
+ return records
+
+def rewheel_from_record(record_path, outdir):
+ """Recreates a whee of package with given record_path and returns path
+ to the newly created wheel."""
+ site_dir = os.path.dirname(os.path.dirname(record_path))
+ record_relpath = record_path[len(site_dir):].strip(os.path.sep)
+ to_write, to_omit = get_records_to_pack(site_dir, record_relpath)
+ new_wheel_name = get_wheel_name(record_path)
+ new_wheel_path = os.path.join(outdir, new_wheel_name + '.whl')
+
+ new_wheel = zipfile.ZipFile(new_wheel_path, mode='w', compression=zipfile.ZIP_DEFLATED)
+ # we need to write a new record with just the files that we will write,
+ # e.g. not binaries and *.pyc/*.pyo files
+ new_record = io.StringIO()
+ writer = csv.writer(new_record)
+
+ # handle files that we can write straight away
+ for f, sha_hash, size in to_write:
+ new_wheel.write(os.path.join(site_dir, f), arcname=f)
+ writer.writerow([f, sha_hash,size])
+
+ # rewrite the old wheel file with a new computed one
+ writer.writerow([record_relpath, '', ''])
+ new_wheel.writestr(record_relpath, new_record.getvalue())
+
+ new_wheel.close()
+
+ return new_wheel.filename
+
+def get_wheel_name(record_path):
+ """Return proper name of the wheel, without .whl."""
+
+ wheel_info_path = os.path.join(os.path.dirname(record_path), 'WHEEL')
+ with codecs.open(wheel_info_path, encoding='utf-8') as wheel_info_file:
+ wheel_info = email.parser.Parser().parsestr(wheel_info_file.read())
+
+ metadata_path = os.path.join(os.path.dirname(record_path), 'METADATA')
+ with codecs.open(metadata_path, encoding='utf-8') as metadata_file:
+ metadata = email.parser.Parser().parsestr(metadata_file.read())
+
+ # construct name parts according to wheel spec
+ distribution = metadata.get('Name')
+ version = metadata.get('Version')
+ build_tag = '' # nothing for now
+ lang_tag = []
+ for t in wheel_info.get_all('Tag'):
+ lang_tag.append(t.split('-')[0])
+ lang_tag = '.'.join(lang_tag)
+ abi_tag, plat_tag = wheel_info.get('Tag').split('-')[1:3]
+ # leave out build tag, if it is empty
+ to_join = filter(None, [distribution, version, build_tag, lang_tag, abi_tag, plat_tag])
+ return '-'.join(list(to_join))
+
+def get_records_to_pack(site_dir, record_relpath):
+ """Accepts path of sitedir and path of RECORD file relative to it.
+ Returns two lists:
+ - list of files that can be written to new RECORD straight away
+ - list of files that shouldn't be written or need some processing
+ (pyc and pyo files, scripts)
+ """
+ record_file_path = os.path.join(site_dir, record_relpath)
+ with codecs.open(record_file_path, encoding='utf-8') as record_file:
+ record_contents = record_file.read()
+ # temporary fix for https://github.com/pypa/pip/issues/1376
+ # we need to ignore files under ".data" directory
+ data_dir = os.path.dirname(record_relpath).strip(os.path.sep)
+ data_dir = data_dir[:-len('dist-info')] + 'data'
+
+ to_write = []
+ to_omit = []
+ for l in record_contents.splitlines():
+ spl = l.split(',')
+ if len(spl) == 3:
+ # new record will omit (or write differently):
+ # - abs paths, paths with ".." (entry points),
+ # - pyc+pyo files
+ # - the old RECORD file
+ # TODO: is there any better way to recognize an entry point?
+ if os.path.isabs(spl[0]) or spl[0].startswith('..') or \
+ spl[0].endswith('.pyc') or spl[0].endswith('.pyo') or \
+ spl[0] == record_relpath or spl[0].startswith(data_dir):
+ to_omit.append(spl)
+ else:
+ to_write.append(spl)
+ else:
+ pass # bad RECORD or empty line
+ return to_write, to_omit
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 85e2ee3..4d34130 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1256,7 +1256,7 @@ LIBSUBDIRS= tkinter tkinter/test tkinter/test/test_tkinter \
test/test_asyncio \
collections concurrent concurrent/futures encodings \
email email/mime test/test_email test/test_email/data \
- ensurepip ensurepip/_bundled \
+ ensurepip ensurepip/_bundled ensurepip/rewheel \
html json test/test_json http dbm xmlrpc \
sqlite3 sqlite3/test \
logging csv wsgiref urllib \

View File

@ -0,0 +1,70 @@
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
index 09c572d..167d27b 100644
--- a/Lib/ensurepip/__init__.py
+++ b/Lib/ensurepip/__init__.py
@@ -1,16 +1,27 @@
+import distutils.version
+import glob
import os
import os.path
-import pkgutil
import sys
import tempfile
__all__ = ["version", "bootstrap"]
+_WHEEL_DIR = "/usr/share/python{}-wheels/".format(sys.version_info[0])
-_SETUPTOOLS_VERSION = "40.6.2"
+def _get_most_recent_wheel_version(pkg):
+ prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg))
+ suffix = "-py2.py3-none-any.whl"
+ pattern = "{}*{}".format(prefix, suffix)
+ versions = (p[len(prefix):-len(suffix)] for p in glob.glob(pattern))
+ return str(max(versions, key=distutils.version.LooseVersion))
+
+
+_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools")
+
+_PIP_VERSION = _get_most_recent_wheel_version("pip")
-_PIP_VERSION = "18.1"
_PROJECTS = [
("setuptools", _SETUPTOOLS_VERSION),
@@ -23,9 +34,15 @@ def _run_pip(args, additional_paths=None):
if additional_paths is not None:
sys.path = additional_paths + sys.path
- # Install the bundled software
- import pip._internal
- return pip._internal.main(args)
+ try:
+ # pip 10
+ from pip._internal import main
+ except ImportError:
+ # pip 9
+ from pip import main
+ if args[0] in ["install", "list", "wheel"]:
+ args.append('--pre')
+ return main(args)
def version():
@@ -94,12 +111,9 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
additional_paths = []
for project, version in _PROJECTS:
wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
- whl = pkgutil.get_data(
- "ensurepip",
- "_bundled/{}".format(wheel_name),
- )
- with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
- fp.write(whl)
+ with open(os.path.join(_WHEEL_DIR, wheel_name), "rb") as sfp:
+ with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
+ fp.write(sfp.read())
additional_paths.append(os.path.join(tmpdir, wheel_name))

View File

@ -0,0 +1,111 @@
From c660debb97f4f422255a82fef2d77804552c043a Mon Sep 17 00:00:00 2001
From: Christian Heimes <christian@python.org>
Date: Tue, 15 Jan 2019 18:16:30 +0100
Subject: [PATCH] bpo-35746: Fix segfault in ssl's cert parser
CVE-2019-5010, Fix a NULL pointer deref in ssl module. The cert parser did
not handle CRL distribution points with empty DP or URI correctly. A
malicious or buggy certificate can result into segfault.
Signed-off-by: Christian Heimes <christian@python.org>
---
Lib/test/talos-2019-0758.pem | 22 +++++++++++++++++++
Lib/test/test_ssl.py | 22 +++++++++++++++++++
.../2019-01-15-18-16-05.bpo-35746.nMSd0j.rst | 3 +++
Modules/_ssl.c | 4 ++++
4 files changed, 51 insertions(+)
create mode 100644 Lib/test/talos-2019-0758.pem
create mode 100644 Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst
diff --git a/Lib/test/talos-2019-0758.pem b/Lib/test/talos-2019-0758.pem
new file mode 100644
index 000000000000..13b95a77fd8a
--- /dev/null
+++ b/Lib/test/talos-2019-0758.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApKgAwIBAgIBAjALBgkqhkiG9w0BAQswHzELMAkGA1UEBhMCVUsxEDAO
+BgNVBAMTB2NvZHktY2EwHhcNMTgwNjE4MTgwMDU4WhcNMjgwNjE0MTgwMDU4WjA7
+MQswCQYDVQQGEwJVSzEsMCoGA1UEAxMjY29kZW5vbWljb24tdm0tMi50ZXN0Lmxh
+bC5jaXNjby5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC63fGB
+J80A9Av1GB0bptslKRIUtJm8EeEu34HkDWbL6AJY0P8WfDtlXjlPaLqFa6sqH6ES
+V48prSm1ZUbDSVL8R6BYVYpOlK8/48xk4pGTgRzv69gf5SGtQLwHy8UPBKgjSZoD
+5a5k5wJXGswhKFFNqyyxqCvWmMnJWxXTt2XDCiWc4g4YAWi4O4+6SeeHVAV9rV7C
+1wxqjzKovVe2uZOHjKEzJbbIU6JBPb6TRfMdRdYOw98n1VXDcKVgdX2DuuqjCzHP
+WhU4Tw050M9NaK3eXp4Mh69VuiKoBGOLSOcS8reqHIU46Reg0hqeL8LIL6OhFHIF
+j7HR6V1X6F+BfRS/AgMBAAGjgdYwgdMwCQYDVR0TBAIwADAdBgNVHQ4EFgQUOktp
+HQjxDXXUg8prleY9jeLKeQ4wTwYDVR0jBEgwRoAUx6zgPygZ0ZErF9sPC4+5e2Io
+UU+hI6QhMB8xCzAJBgNVBAYTAlVLMRAwDgYDVQQDEwdjb2R5LWNhggkA1QEAuwb7
+2s0wCQYDVR0SBAIwADAuBgNVHREEJzAlgiNjb2Rlbm9taWNvbi12bS0yLnRlc3Qu
+bGFsLmNpc2NvLmNvbTAOBgNVHQ8BAf8EBAMCBaAwCwYDVR0fBAQwAjAAMAsGCSqG
+SIb3DQEBCwOCAQEAvqantx2yBlM11RoFiCfi+AfSblXPdrIrHvccepV4pYc/yO6p
+t1f2dxHQb8rWH3i6cWag/EgIZx+HJQvo0rgPY1BFJsX1WnYf1/znZpkUBGbVmlJr
+t/dW1gSkNS6sPsM0Q+7HPgEv8CPDNK5eo7vU2seE0iWOkxSyVUuiCEY9ZVGaLVit
+p0C78nZ35Pdv4I+1cosmHl28+es1WI22rrnmdBpH8J1eY6WvUw2xuZHLeNVN0TzV
+Q3qq53AaCWuLOD1AjESWuUCxMZTK9DPS4JKXTK8RLyDeqOvJGjsSWp3kL0y3GaQ+
+10T1rfkKJub2+m9A9duin1fn6tHc2wSvB7m3DA==
+-----END CERTIFICATE-----
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 7f6b93148f45..1fc657f4d867 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -115,6 +115,7 @@ def data_file(*name):
BADKEY = data_file("badkey.pem")
NOKIACERT = data_file("nokia.pem")
NULLBYTECERT = data_file("nullbytecert.pem")
+TALOS_INVALID_CRLDP = data_file("talos-2019-0758.pem")
DHFILE = data_file("ffdh3072.pem")
BYTES_DHFILE = os.fsencode(DHFILE)
@@ -348,6 +349,27 @@ def test_parse_cert(self):
self.assertEqual(p['crlDistributionPoints'],
('http://SVRIntl-G3-crl.verisign.com/SVRIntlG3.crl',))
+ def test_parse_cert_CVE_2019_5010(self):
+ p = ssl._ssl._test_decode_cert(TALOS_INVALID_CRLDP)
+ if support.verbose:
+ sys.stdout.write("\n" + pprint.pformat(p) + "\n")
+ self.assertEqual(
+ p,
+ {
+ 'issuer': (
+ (('countryName', 'UK'),), (('commonName', 'cody-ca'),)),
+ 'notAfter': 'Jun 14 18:00:58 2028 GMT',
+ 'notBefore': 'Jun 18 18:00:58 2018 GMT',
+ 'serialNumber': '02',
+ 'subject': ((('countryName', 'UK'),),
+ (('commonName',
+ 'codenomicon-vm-2.test.lal.cisco.com'),)),
+ 'subjectAltName': (
+ ('DNS', 'codenomicon-vm-2.test.lal.cisco.com'),),
+ 'version': 3
+ }
+ )
+
def test_parse_cert_CVE_2013_4238(self):
p = ssl._ssl._test_decode_cert(NULLBYTECERT)
if support.verbose:
diff --git a/Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst b/Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst
new file mode 100644
index 000000000000..dffe347eec84
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst
@@ -0,0 +1,3 @@
+[CVE-2019-5010] Fix a NULL pointer deref in ssl module. The cert parser did
+not handle CRL distribution points with empty DP or URI correctly. A
+malicious or buggy certificate can result into segfault.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 4e3352d9e661..0e720e268d93 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -1515,6 +1515,10 @@ _get_crl_dp(X509 *certificate) {
STACK_OF(GENERAL_NAME) *gns;
dp = sk_DIST_POINT_value(dps, i);
+ if (dp->distpoint == NULL) {
+ /* Ignore empty DP value, CVE-2019-5010 */
+ continue;
+ }
gns = dp->distpoint->name.fullname;
for (j=0; j < sk_GENERAL_NAME_num(gns); j++) {

View File

@ -0,0 +1,44 @@
bpo-32947: test_ssl fixes for TLS 1.3 and OpenSSL 1.1.1
Backport partially commit 529525fb5a8fd9b96ab4021311a598c77588b918:
complete the previous partial backport (commit
2a4ee8aa01d61b6a9c8e9c65c211e61bdb471826.
Reported upstream:
* https://bugs.python.org/issue32947#msg333990
* https://github.com/python/cpython/pull/11612
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 7f8f636..05c09a6 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -2021,6 +2021,16 @@ if _have_threads:
sys.stdout.write(" server: read %r (%s), sending back %r (%s)...\n"
% (msg, ctype, msg.lower(), ctype))
self.write(msg.lower())
+ except ConnectionResetError:
+ # XXX: OpenSSL 1.1.1 sometimes raises ConnectionResetError
+ # when connection is not shut down gracefully.
+ if self.server.chatty and support.verbose:
+ sys.stdout.write(
+ " Connection reset by peer: {}\n".format(
+ self.addr)
+ )
+ self.close()
+ self.running = False
except OSError:
if self.server.chatty:
handle_error("Test server failure:\n")
@@ -2100,6 +2110,11 @@ if _have_threads:
pass
except KeyboardInterrupt:
self.stop()
+ except BaseException as e:
+ if support.verbose and self.chatty:
+ sys.stdout.write(
+ ' connection handling failed: ' + repr(e) + '\n')
+
self.sock.close()
def stop(self):

View File

@ -0,0 +1,41 @@
commit 86ed41792d394f804d2c9e695ac8b257220fbdee
Author: Victor Stinner <vstinner@redhat.com>
Date: Tue Mar 12 17:17:13 2019 +0100
Fix test_tarfile on ppc64
Fix sparse file tests of test_tarfile on ppc64le with the tmpfs
filesystem.
* https://bugzilla.redhat.com/show_bug.cgi?id=1639490
* https://bugs.python.org/issue35772
* https://github.com/python/cpython/commit/d1dd6be613381b996b9071443ef081de8e5f3aff
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 4cd7d53..bd8b05f 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -973,16 +973,21 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase):
def _fs_supports_holes():
# Return True if the platform knows the st_blocks stat attribute and
# uses st_blocks units of 512 bytes, and if the filesystem is able to
- # store holes in files.
+ # store holes of 4 KiB in files.
+ #
+ # The function returns False if page size is larger than 4 KiB.
+ # For example, ppc64 uses pages of 64 KiB.
if sys.platform.startswith("linux"):
# Linux evidentially has 512 byte st_blocks units.
name = os.path.join(TEMPDIR, "sparse-test")
with open(name, "wb") as fobj:
+ # Seek to "punch a hole" of 4 KiB
fobj.seek(4096)
+ fobj.write(b'x' * 4096)
fobj.truncate()
s = os.stat(name)
support.unlink(name)
- return s.st_blocks == 0
+ return (s.st_blocks * 512 < s.st_size)
else:
return False

View File

@ -0,0 +1,150 @@
From 7e200e0763f5b71c199aaf98bd5588f291585619 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
Date: Tue, 7 May 2019 17:28:47 +0200
Subject: [PATCH] bpo-30458: Disallow control chars in http URLs. (GH-12755)
(GH-13154)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Disallow control chars in http URLs in urllib.urlopen. This addresses a potential security problem for applications that do not sanity check their URLs where http request headers could be injected.
Disable https related urllib tests on a build without ssl (GH-13032)
These tests require an SSL enabled build. Skip these tests when python is built without SSL to fix test failures.
Use http.client.InvalidURL instead of ValueError as the new error case's exception. (GH-13044)
Backport Co-Authored-By: Miro Hrončok <miro@hroncok.cz>
---
Lib/http/client.py | 15 ++++++
Lib/test/test_urllib.py | 53 +++++++++++++++++++
Lib/test/test_xmlrpc.py | 7 ++-
.../2019-04-10-08-53-30.bpo-30458.51E-DA.rst | 1 +
4 files changed, 75 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst
diff --git a/Lib/http/client.py b/Lib/http/client.py
index 1de151c38e..2afd452fe3 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -140,6 +140,16 @@ _MAXHEADERS = 100
_is_legal_header_name = re.compile(rb'[^:\s][^:\r\n]*').fullmatch
_is_illegal_header_value = re.compile(rb'\n(?![ \t])|\r(?![ \t\n])').search
+# These characters are not allowed within HTTP URL paths.
+# See https://tools.ietf.org/html/rfc3986#section-3.3 and the
+# https://tools.ietf.org/html/rfc3986#appendix-A pchar definition.
+# Prevents CVE-2019-9740. Includes control characters such as \r\n.
+# We don't restrict chars above \x7f as putrequest() limits us to ASCII.
+_contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f]')
+# Arguably only these _should_ allowed:
+# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
+# We are more lenient for assumed real world compatibility purposes.
+
# We always set the Content-Length header for these methods because some
# servers will otherwise respond with a 411
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
@@ -1101,6 +1111,11 @@ class HTTPConnection:
self._method = method
if not url:
url = '/'
+ # Prevent CVE-2019-9740.
+ match = _contains_disallowed_url_pchar_re.search(url)
+ if match:
+ raise InvalidURL(f"URL can't contain control characters. {url!r} "
+ f"(found at least {match.group()!r})")
request = '%s %s %s' % (method, url, self._http_vsn_str)
# Non-ASCII characters should have been eliminated earlier
diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py
index 2ac73b58d8..7214492eca 100644
--- a/Lib/test/test_urllib.py
+++ b/Lib/test/test_urllib.py
@@ -329,6 +329,59 @@ class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin, FakeFTPMixin):
finally:
self.unfakehttp()
+ @unittest.skipUnless(ssl, "ssl module required")
+ def test_url_with_control_char_rejected(self):
+ for char_no in list(range(0, 0x21)) + [0x7f]:
+ char = chr(char_no)
+ schemeless_url = f"//localhost:7777/test{char}/"
+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
+ try:
+ # We explicitly test urllib.request.urlopen() instead of the top
+ # level 'def urlopen()' function defined in this... (quite ugly)
+ # test suite. They use different url opening codepaths. Plain
+ # urlopen uses FancyURLOpener which goes via a codepath that
+ # calls urllib.parse.quote() on the URL which makes all of the
+ # above attempts at injection within the url _path_ safe.
+ escaped_char_repr = repr(char).replace('\\', r'\\')
+ InvalidURL = http.client.InvalidURL
+ with self.assertRaisesRegex(
+ InvalidURL, f"contain control.*{escaped_char_repr}"):
+ urllib.request.urlopen(f"http:{schemeless_url}")
+ with self.assertRaisesRegex(
+ InvalidURL, f"contain control.*{escaped_char_repr}"):
+ urllib.request.urlopen(f"https:{schemeless_url}")
+ # This code path quotes the URL so there is no injection.
+ resp = urlopen(f"http:{schemeless_url}")
+ self.assertNotIn(char, resp.geturl())
+ finally:
+ self.unfakehttp()
+
+ @unittest.skipUnless(ssl, "ssl module required")
+ def test_url_with_newline_header_injection_rejected(self):
+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
+ host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
+ schemeless_url = "//" + host + ":8080/test/?test=a"
+ try:
+ # We explicitly test urllib.request.urlopen() instead of the top
+ # level 'def urlopen()' function defined in this... (quite ugly)
+ # test suite. They use different url opening codepaths. Plain
+ # urlopen uses FancyURLOpener which goes via a codepath that
+ # calls urllib.parse.quote() on the URL which makes all of the
+ # above attempts at injection within the url _path_ safe.
+ InvalidURL = http.client.InvalidURL
+ with self.assertRaisesRegex(
+ InvalidURL, r"contain control.*\\r.*(found at least . .)"):
+ urllib.request.urlopen(f"http:{schemeless_url}")
+ with self.assertRaisesRegex(InvalidURL, r"contain control.*\\n"):
+ urllib.request.urlopen(f"https:{schemeless_url}")
+ # This code path quotes the URL so there is no injection.
+ resp = urlopen(f"http:{schemeless_url}")
+ self.assertNotIn(' ', resp.geturl())
+ self.assertNotIn('\r', resp.geturl())
+ self.assertNotIn('\n', resp.geturl())
+ finally:
+ self.unfakehttp()
+
def test_read_0_9(self):
# "0.9" response accepted (but not "simple responses" without
# a status line)
diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py
index 32263f7f0b..0e002ec4ef 100644
--- a/Lib/test/test_xmlrpc.py
+++ b/Lib/test/test_xmlrpc.py
@@ -945,7 +945,12 @@ class SimpleServerTestCase(BaseServerTestCase):
def test_partial_post(self):
# Check that a partial POST doesn't make the server loop: issue #14001.
conn = http.client.HTTPConnection(ADDR, PORT)
- conn.request('POST', '/RPC2 HTTP/1.0\r\nContent-Length: 100\r\n\r\nbye')
+ conn.send('POST /RPC2 HTTP/1.0\r\n'
+ 'Content-Length: 100\r\n\r\n'
+ 'bye HTTP/1.1\r\n'
+ f'Host: {ADDR}:{PORT}\r\n'
+ 'Accept-Encoding: identity\r\n'
+ 'Content-Length: 0\r\n\r\n'.encode('ascii'))
conn.close()
def test_context_manager(self):
diff --git a/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst
new file mode 100644
index 0000000000..ed8027fb4d
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst
@@ -0,0 +1 @@
+Address CVE-2019-9740 by disallowing URL paths with embedded whitespace or control characters through into the underlying http client request. Such potentially malicious header injection URLs now cause an http.client.InvalidURL exception to be raised.
--
2.21.0

View File

@ -0,0 +1,49 @@
diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py
index 649a5b8..0061a52 100644
--- a/Lib/test/test_urllib.py
+++ b/Lib/test/test_urllib.py
@@ -16,6 +16,7 @@ except ImportError:
ssl = None
import sys
import tempfile
+import warnings
from nturl2path import url2pathname, pathname2url
from base64 import b64encode
@@ -1463,6 +1464,23 @@ class URLopener_Tests(unittest.TestCase):
"spam://c:|windows%/:=&?~#+!$,;'@()*[]|/path/"),
"//c:|windows%/:=&?~#+!$,;'@()*[]|/path/")
+ def test_local_file_open(self):
+ # bpo-35907, CVE-2019-9948: urllib must reject local_file:// scheme
+ class DummyURLopener(urllib.request.URLopener):
+ def open_local_file(self, url):
+ return url
+
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("ignore", DeprecationWarning)
+
+ for url in ('local_file://example', 'local-file://example'):
+ self.assertRaises(OSError, urllib.request.urlopen, url)
+ self.assertRaises(OSError, urllib.request.URLopener().open, url)
+ self.assertRaises(OSError, urllib.request.URLopener().retrieve, url)
+ self.assertRaises(OSError, DummyURLopener().open, url)
+ self.assertRaises(OSError, DummyURLopener().retrieve, url)
+
+
# Just commented them out.
# Can't really tell why keep failing in windows and sparc.
# Everywhere else they work ok, but on those machines, sometimes
diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
index d28f2f8..c9945d9 100644
--- a/Lib/urllib/request.py
+++ b/Lib/urllib/request.py
@@ -1747,7 +1747,7 @@ class URLopener:
name = 'open_' + urltype
self.type = urltype
name = name.replace('-', '_')
- if not hasattr(self, name):
+ if not hasattr(self, name) or name == 'open_local_file':
if proxy:
return self.open_unknown_proxy(proxy, fullurl, data)
else:

View File

@ -0,0 +1,117 @@
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 883201f..cf4d84d 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -3891,6 +3891,37 @@ class TestPostHandshakeAuth(unittest.TestCase):
s.write(b'PHA')
self.assertIn(b'WRONG_SSL_VERSION', s.recv(1024))
+ def test_bpo37428_pha_cert_none(self):
+ # verify that post_handshake_auth does not implicitly enable cert
+ # validation.
+ hostname = 'localhost'
+ client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ client_context.post_handshake_auth = True
+ client_context.load_cert_chain(SIGNED_CERTFILE)
+ # no cert validation and CA on client side
+ client_context.check_hostname = False
+ client_context.verify_mode = ssl.CERT_NONE
+
+ server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+ server_context.load_cert_chain(SIGNED_CERTFILE)
+ server_context.load_verify_locations(SIGNING_CA)
+ server_context.post_handshake_auth = True
+ server_context.verify_mode = ssl.CERT_REQUIRED
+
+ server = ThreadedEchoServer(context=server_context, chatty=False)
+ with server:
+ with client_context.wrap_socket(socket.socket(),
+ server_hostname=hostname) as s:
+ s.connect((HOST, server.port))
+ s.write(b'HASCERT')
+ self.assertEqual(s.recv(1024), b'FALSE\n')
+ s.write(b'PHA')
+ self.assertEqual(s.recv(1024), b'OK\n')
+ s.write(b'HASCERT')
+ self.assertEqual(s.recv(1024), b'TRUE\n')
+ # server cert has not been validated
+ self.assertEqual(s.getpeercert(), {})
+
def test_main(verbose=False):
if support.verbose:
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index ec366f0..9bf1cde 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -732,6 +732,26 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
#endif
SSL_set_mode(self->ssl, mode);
+#ifdef TLS1_3_VERSION
+ if (sslctx->post_handshake_auth == 1) {
+ if (socket_type == PY_SSL_SERVER) {
+ /* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE.
+ * Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and
+ * only in combination with SSL_VERIFY_PEER flag. */
+ int mode = SSL_get_verify_mode(self->ssl);
+ if (mode & SSL_VERIFY_PEER) {
+ int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
+ verify_cb = SSL_get_verify_callback(self->ssl);
+ mode |= SSL_VERIFY_POST_HANDSHAKE;
+ SSL_set_verify(self->ssl, mode, verify_cb);
+ }
+ } else {
+ /* client socket */
+ SSL_set_post_handshake_auth(self->ssl, 1);
+ }
+ }
+#endif
+
#if HAVE_SNI
if (server_hostname != NULL) {
/* Don't send SNI for IP addresses. We cannot simply use inet_aton() and
@@ -2765,10 +2785,10 @@ _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n)
"invalid value for verify_mode");
return -1;
}
-#ifdef TLS1_3_VERSION
- if (self->post_handshake_auth)
- mode |= SSL_VERIFY_POST_HANDSHAKE;
-#endif
+
+ /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
+ * server sockets and SSL_set_post_handshake_auth() for client. */
+
/* keep current verify cb */
verify_cb = SSL_CTX_get_verify_callback(self->ctx);
SSL_CTX_set_verify(self->ctx, mode, verify_cb);
@@ -3346,8 +3366,6 @@ get_post_handshake_auth(PySSLContext *self, void *c) {
#if TLS1_3_VERSION
static int
set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) {
- int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
- int mode = SSL_CTX_get_verify_mode(self->ctx);
int pha = PyObject_IsTrue(arg);
if (pha == -1) {
@@ -3355,17 +3373,8 @@ set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) {
}
self->post_handshake_auth = pha;
- /* client-side socket setting, ignored by server-side */
- SSL_CTX_set_post_handshake_auth(self->ctx, pha);
-
- /* server-side socket setting, ignored by client-side */
- verify_cb = SSL_CTX_get_verify_callback(self->ctx);
- if (pha) {
- mode |= SSL_VERIFY_POST_HANDSHAKE;
- } else {
- mode ^= SSL_VERIFY_POST_HANDSHAKE;
- }
- SSL_CTX_set_verify(self->ctx, mode, verify_cb);
+ /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
+ * server sockets and SSL_set_post_handshake_auth() for client. */
return 0;
}

View File

@ -0,0 +1,70 @@
diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst
index 2f59ece..d756916 100644
--- a/Doc/library/http.client.rst
+++ b/Doc/library/http.client.rst
@@ -88,6 +88,11 @@ The module provides the following classes:
:func:`ssl._create_unverified_context` can be passed to the *context*
parameter.
+ .. versionchanged:: 3.7.4
+ This class now enables TLS 1.3
+ :attr:`ssl.SSLContext.post_handshake_auth` for the default *context* or
+ when *cert_file* is passed with a custom *context*.
+
.. deprecated:: 3.6
*key_file* and *cert_file* are deprecated in favor of *context*.
diff --git a/Lib/http/client.py b/Lib/http/client.py
index 1a6bd8a..f0d2642 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -1390,6 +1390,9 @@ else:
self.cert_file = cert_file
if context is None:
context = ssl._create_default_https_context()
+ # enable PHA for TLS 1.3 connections if available
+ if context.post_handshake_auth is not None:
+ context.post_handshake_auth = True
will_verify = context.verify_mode != ssl.CERT_NONE
if check_hostname is None:
check_hostname = context.check_hostname
@@ -1398,6 +1401,10 @@ else:
"either CERT_OPTIONAL or CERT_REQUIRED")
if key_file or cert_file:
context.load_cert_chain(cert_file, key_file)
+ # cert and key file means the user wants to authenticate.
+ # enable TLS 1.3 PHA implicitly even for custom contexts.
+ if context.post_handshake_auth is not None:
+ context.post_handshake_auth = True
self._context = context
self._check_hostname = check_hostname
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 714d521..5795b7a 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -1709,6 +1709,24 @@ class HTTPSTest(TestCase):
self.assertEqual(h, c.host)
self.assertEqual(p, c.port)
+ def test_tls13_pha(self):
+ import ssl
+ if not ssl.HAS_TLSv1_3:
+ self.skipTest('TLS 1.3 support required')
+ # just check status of PHA flag
+ h = client.HTTPSConnection('localhost', 443)
+ self.assertTrue(h._context.post_handshake_auth)
+
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ self.assertFalse(context.post_handshake_auth)
+ h = client.HTTPSConnection('localhost', 443, context=context)
+ self.assertIs(h._context, context)
+ self.assertFalse(h._context.post_handshake_auth)
+
+ h = client.HTTPSConnection('localhost', 443, context=context,
+ cert_file=CERT_localhost)
+ self.assertTrue(h._context.post_handshake_auth)
+
class RequestBodyTest(TestCase):
"""Test cases where a request includes a message body."""

4925
SOURCES/00329-fips.patch Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ URL: https://www.python.org/
# WARNING When rebasing to a new Python version, # WARNING When rebasing to a new Python version,
# remember to update the python3-docs package as well # remember to update the python3-docs package as well
Version: %{pybasever}.8 Version: %{pybasever}.8
Release: 4%{?dist} Release: 15.1%{?dist}
License: Python License: Python
@ -25,6 +25,10 @@ License: Python
# Note that the bcond macros are named for the CLI option they create. # Note that the bcond macros are named for the CLI option they create.
# "%%bcond_without" means "ENABLE by default and create a --without option" # "%%bcond_without" means "ENABLE by default and create a --without option"
# Whether to use RPM build wheels from the python-{pip,setuptools}-wheel package
# Uses upstream bundled prebuilt wheels otherwise
%bcond_without rpmwheels
# Expensive optimizations (mainly, profile-guided optimizations) # Expensive optimizations (mainly, profile-guided optimizations)
%ifarch %{ix86} x86_64 %ifarch %{ix86} x86_64
%bcond_without optimizations %bcond_without optimizations
@ -37,9 +41,6 @@ License: Python
# Run the test suite in %%check # Run the test suite in %%check
%bcond_without tests %bcond_without tests
# Ability to reuse RPM-installed pip using rewheel
%bcond_without rewheel
# Extra build for debugging the interpreter or C-API extensions # Extra build for debugging the interpreter or C-API extensions
# (the -debug subpackages) # (the -debug subpackages)
%bcond_without debug_build %bcond_without debug_build
@ -188,6 +189,7 @@ BuildRequires: ncurses-devel
BuildRequires: openssl-devel BuildRequires: openssl-devel
BuildRequires: pkgconfig BuildRequires: pkgconfig
BuildRequires: readline-devel BuildRequires: readline-devel
BuildRequires: redhat-rpm-config >= 118
BuildRequires: sqlite-devel BuildRequires: sqlite-devel
BuildRequires: gdb BuildRequires: gdb
@ -208,9 +210,9 @@ BuildRequires: /usr/bin/dtrace
# workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig) # workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig)
BuildRequires: /usr/sbin/ifconfig BuildRequires: /usr/sbin/ifconfig
%if %{with rewheel} %if %{with rpmwheels}
BuildRequires: python3-setuptools BuildRequires: python3-setuptools-wheel
BuildRequires: python3-pip BuildRequires: python3-pip-wheel
# Verify that the BuildRoot includes python36. # Verify that the BuildRoot includes python36.
# Not actually needed for build. # Not actually needed for build.
@ -320,10 +322,9 @@ Patch170: 00170-gc-assertions.patch
Patch178: 00178-dont-duplicate-flags-in-sysconfig.patch Patch178: 00178-dont-duplicate-flags-in-sysconfig.patch
# 00189 # # 00189 #
# Add the rewheel module, allowing to recreate wheels from already installed # Instead of bundled wheels, use our RPM packaged wheels from
# ones # /usr/share/python3-wheels
# https://github.com/bkabrda/rewheel Patch189: 00189-use-rpm-wheels.patch
Patch189: 00189-add-rewheel-module.patch
# 00205 # # 00205 #
# LIBPL variable in makefile takes LIBPL from configure.ac # LIBPL variable in makefile takes LIBPL from configure.ac
@ -356,13 +357,78 @@ Patch274: 00274-fix-arch-names.patch
# See also: https://bugzilla.redhat.com/show_bug.cgi?id=1489816 # See also: https://bugzilla.redhat.com/show_bug.cgi?id=1489816
Patch294: 00294-define-TLS-cipher-suite-on-build-time.patch Patch294: 00294-define-TLS-cipher-suite-on-build-time.patch
# 00317 #
# Security fix for CVE-2019-5010: Fix segfault in ssl's cert parser
# https://bugzilla.redhat.com/show_bug.cgi?id=1666789
# Fixed upstream: https://bugs.python.org/issue35746
Patch317: 00317-CVE-2019-5010.patch
# 00318 #
# test_ssl fixes for TLS 1.3 and OpenSSL 1.1.1
# https://bugzilla.redhat.com/show_bug.cgi?id=1639531
# https://bugs.python.org/issue32947#msg333990
# https://github.com/python/cpython/pull/11612
Patch318: 00318-test-ssl-fix-for-tls-13.patch
# 00319 #
# Fix test_tarfile on ppc64
# https://bugzilla.redhat.com/show_bug.cgi?id=1639490
# https://bugs.python.org/issue35772
Patch319: 00319-test_tarfile_ppc64.patch
# 00320 # # 00320 #
# Security fix for CVE-2019-9636 and CVE-2019-10160: Information Disclosure due to urlsplit improper NFKC normalization # Security fix for CVE-2019-9636 and CVE-2019-10160: Information Disclosure due to urlsplit improper NFKC normalization
# Fixed upstream: https://bugs.python.org/issue36216 and https://bugs.python.org/issue36742 # Fixed upstream: https://bugs.python.org/issue36216 and https://bugs.python.org/issue36742
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1693973 # Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1689318
# and https://bugzilla.redhat.com/show_bug.cgi?id=1714756
Patch320: 00320-CVE-2019-9636-and-CVE-2019-10160.patch Patch320: 00320-CVE-2019-9636-and-CVE-2019-10160.patch
# 00324 #
# Disallow control chars in http URLs
# Security fix for CVE-2019-9740 and CVE-2019-9947
# Fixed upstream: https://bugs.python.org/issue30458
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1704365
# and https://bugzilla.redhat.com/show_bug.cgi?id=1703531
Patch324: 00324-disallow-control-chars-in-http-urls.patch
# 00325 #
# Unnecessary URL scheme exists to allow local_file:// reading file in urllib
# Security fix for CVE-2019-9948
# Fixed upstream: https://bugs.python.org/issue35907
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1714643
Patch325: 00325-CVE-2019-9948.patch
# 00326 #
# Don't set the post-handshake authentication verify flag on client side
# on TLS 1.3, as it also implicitly enables cert chain validation and an
# SSL/TLS connection will fail when verify mode is set to CERT_NONE.
# Fixed upstream: https://bugs.python.org/issue37428
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1725721
Patch326: 00326-do-not-set-PHA-verify-flag-on-client-side.patch
# 00327 #
# Enable TLS 1.3 post-handshake authentication in http.client for default
# context or if a cert_file is passed to HTTPSConnection
# Fixed upstream: https://bugs.python.org/issue37440
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1671353
Patch327: 00327-enable-tls-1.3-PHA-in-http.client.patch
# 00329 #
# Support OpenSSL FIPS mode
# - Fallback implementations md5, sha1, sha256, sha512 are removed in favor of OpenSSL wrappers
# - In FIPS mode, OpenSSL wrappers are always used in hashlib
# - add a new "usedforsecurity" keyword argument to the various digest
# algorithms in hashlib so that you can whitelist a callsite with
# "usedforsecurity=False"
# - OpenSSL wrappers for the hashes blake2{b512,s256},
# sha3_{224,256,384,512}, shake_{128,256} are now exported from _hashlib
# - In FIPS mode, the blake2, sha3 and shake hashes use OpenSSL wrappers
# and do not offer extended functionality (keys, tree hashing, custom digest size)
# - In FIPS mode, hmac.HMAC can only be instantiated with an OpenSSL wrapper
# or an string with OpenSSL hash name as the "digestmod" argument.
# The argument must be specified (instead of defaulting to md5).
# Resolves: rhbz#1731424
Patch329: 00329-fips.patch
# (New patches go here ^^^) # (New patches go here ^^^)
# #
# When adding new patches to "python" and "python3" in Fedora, EL, etc., # When adding new patches to "python" and "python3" in Fedora, EL, etc.,
@ -388,9 +454,21 @@ Provides: python(abi) = %{pybasever}
Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: %{name}-libs%{?_isa} = %{version}-%{release}
%if %{with rewheel} %if %{with rpmwheels}
# RHEL8 was forked from F28 and thus required python3-setuptools/pip here
# for the rewheel module to work. We've since backported the use of RPM
# prepared wheels from F29+ into RHEL8, and thus this dependency isn't
# strictly needed.
# However, it is possible, that some packages in BaseOS actually depend on
# setuptools/pip without declaring the dependency in their spec file. Thus
# we're keeping these dependencies here to avoid the possibility of breaking
# them.
Requires: platform-python-setuptools Requires: platform-python-setuptools
Requires: platform-python-pip Requires: platform-python-pip
Requires: python3-setuptools-wheel
Requires: python3-pip-wheel
%endif %endif
# Runtime require alternatives # Runtime require alternatives
@ -439,6 +517,14 @@ Requires: chkconfig
Requires: gdbm%{?_isa} >= 1:1.13 Requires: gdbm%{?_isa} >= 1:1.13
%endif %endif
%if %{with rpmwheels}
Requires: python3-setuptools-wheel
Requires: python3-pip-wheel
%else
Provides: bundled(python3-pip) = 18.1
Provides: bundled(python3-setuptools) = 40.6.2
%endif
# There are files in the standard library that have python shebang. # There are files in the standard library that have python shebang.
# We've filtered the automatic requirement out so libs are installable without # We've filtered the automatic requirement out so libs are installable without
# the main package. This however makes it pulled in by default. # the main package. This however makes it pulled in by default.
@ -494,11 +580,14 @@ Requires: python-rpm-macros
Requires: python3-rpm-macros Requires: python3-rpm-macros
Requires: python3-rpm-generators Requires: python3-rpm-generators
# https://bugzilla.redhat.com/show_bug.cgi?id=1217376 # This is not "API" (packages that need setuptools should still BuildRequire it)
# https://bugzilla.redhat.com/show_bug.cgi?id=1496757 # However some packages apparently can build both with and without setuptools
# https://bugzilla.redhat.com/show_bug.cgi?id=1218294 # producing egg-info as file or directory (depending on setuptools presence).
# TODO change to a specific subpackage once available (#1218294) # Directory-to-file updates are problematic in RPM, so we ensure setuptools is
Requires: redhat-rpm-config # installed when -devel is required.
# See https://bugzilla.redhat.com/show_bug.cgi?id=1623914
# See https://fedoraproject.org/wiki/Packaging:Directory_Replacement
Requires: platform-python-setuptools
Provides: %{name}-2to3 = %{version}-%{release} Provides: %{name}-2to3 = %{version}-%{release}
Provides: 2to3 = %{version}-%{release} Provides: 2to3 = %{version}-%{release}
@ -612,11 +701,6 @@ so extensions for both versions can co-exist in the same directory.
rm -r Modules/expat rm -r Modules/expat
rm -r Modules/zlib rm -r Modules/zlib
%if %{with rewheel}
%global pip_version %(pip%{pybasever} --version | cut -d' ' -f2)
sed -r -i s/'_PIP_VERSION = "[0-9.]+"'/'_PIP_VERSION = "%{pip_version}"'/ Lib/ensurepip/__init__.py
%endif
# #
# Apply patches: # Apply patches:
# #
@ -633,8 +717,9 @@ sed -r -i s/'_PIP_VERSION = "[0-9.]+"'/'_PIP_VERSION = "%{pip_version}"'/ Lib/en
%patch170 -p1 %patch170 -p1
%patch178 -p1 %patch178 -p1
%if %{with rewheel} %if %{with rpmwheels}
%patch189 -p1 %patch189 -p1
rm Lib/ensurepip/_bundled/*.whl
%endif %endif
%patch205 -p1 %patch205 -p1
@ -642,7 +727,15 @@ sed -r -i s/'_PIP_VERSION = "[0-9.]+"'/'_PIP_VERSION = "%{pip_version}"'/ Lib/en
%patch262 -p1 %patch262 -p1
%patch274 -p1 %patch274 -p1
%patch294 -p1 %patch294 -p1
%patch317 -p1
%patch318 -p1
%patch319 -p1
%patch320 -p1 %patch320 -p1
%patch324 -p1
%patch325 -p1
%patch326 -p1
%patch327 -p1
%patch329 -p1
# Remove files that should be generated by the build # Remove files that should be generated by the build
@ -678,13 +771,21 @@ topdir=$(pwd)
%endif %endif
# Set common compiler/linker flags # Set common compiler/linker flags
export CFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE -fPIC -fwrapv" # We utilize the %%extension_...flags macros here so users building C/C++
export CXXFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE -fPIC -fwrapv" # extensions with our python won't get all the compiler/linker flags used
# in RHEL RPMs.
# Standard library built here will still use the %%build_...flags,
# RHEL packages utilizing %%py3_build will use them as well
# https://fedoraproject.org/wiki/Changes/Python_Extension_Flags
export CFLAGS="%{extension_cflags} -D_GNU_SOURCE -fPIC -fwrapv"
export CFLAGS_NODIST="%{build_cflags} -D_GNU_SOURCE -fPIC -fwrapv"
export CXXFLAGS="%{extension_cxxflags} -D_GNU_SOURCE -fPIC -fwrapv"
export CPPFLAGS="$(pkg-config --cflags-only-I libffi)" export CPPFLAGS="$(pkg-config --cflags-only-I libffi)"
export OPT="$RPM_OPT_FLAGS -D_GNU_SOURCE -fPIC -fwrapv" export OPT="%{extension_cflags} -D_GNU_SOURCE -fPIC -fwrapv"
export LINKCC="gcc" export LINKCC="gcc"
export CFLAGS="$CFLAGS $(pkg-config --cflags openssl)" export CFLAGS="$CFLAGS $(pkg-config --cflags openssl)"
export LDFLAGS="$RPM_LD_FLAGS -g $(pkg-config --libs-only-L openssl)" export LDFLAGS="%{extension_ldflags} -g $(pkg-config --libs-only-L openssl)"
export LDFLAGS_NODIST="%{build_ldflags} -g $(pkg-config --libs-only-L openssl)"
# We can build several different configurations of Python: regular and debug. # We can build several different configurations of Python: regular and debug.
# Define a common function that does one build: # Define a common function that does one build:
@ -994,7 +1095,7 @@ rm %{buildroot}%{_mandir}/man1/python3.1
# Install the unversioned-python script and its man page # Install the unversioned-python script and its man page
install -m 755 %{SOURCE12} %{buildroot}%{_libexecdir}/no-python install -m 755 %{SOURCE12} %{buildroot}%{_libexecdir}/no-python
install -m 644 %{SOURCE13} %{buildroot}%{_mandir}/man1/unversioned-python.1.gz install -m 644 %{SOURCE13} %{buildroot}%{_mandir}/man1/unversioned-python.1
# Touch the files that are controlled by `alternatives` so we can declare them # Touch the files that are controlled by `alternatives` so we can declare them
# as ghosts in the files section # as ghosts in the files section
touch %{buildroot}%{_bindir}/unversioned-python touch %{buildroot}%{_bindir}/unversioned-python
@ -1066,10 +1167,11 @@ CheckPython() {
%ifarch %{mips64} %ifarch %{mips64}
-x test_ctypes \ -x test_ctypes \
%endif %endif
%ifarch s390x
-x test_gdb \
%endif
%ifarch ppc64le %ifarch ppc64le
-x test_buffer \ -x test_gdb \
-x test_tarfile \
-x test_ssl \
%endif %endif
echo FINISHED: CHECKING OF PYTHON FOR CONFIGURATION: $ConfName echo FINISHED: CHECKING OF PYTHON FOR CONFIGURATION: $ConfName
@ -1137,7 +1239,9 @@ fi
%exclude %{_bindir}/pyvenv %exclude %{_bindir}/pyvenv
%{_bindir}/pyvenv-%{pybasever} %{_bindir}/pyvenv-%{pybasever}
%{_mandir}/*/*
%{_mandir}/man1/python3.6.1*
%{_mandir}/man1/unversioned-python.1*
%files libs %files libs
%license LICENSE %license LICENSE
@ -1172,13 +1276,11 @@ fi
%dir %{pylibdir}/ensurepip/__pycache__/ %dir %{pylibdir}/ensurepip/__pycache__/
%{pylibdir}/ensurepip/*.py %{pylibdir}/ensurepip/*.py
%{pylibdir}/ensurepip/__pycache__/*%{bytecode_suffixes} %{pylibdir}/ensurepip/__pycache__/*%{bytecode_suffixes}
%if %{with rpmwheels}
%exclude %{pylibdir}/ensurepip/_bundled %exclude %{pylibdir}/ensurepip/_bundled
%else
%if %{with rewheel} %dir %{pylibdir}/ensurepip/_bundled
%dir %{pylibdir}/ensurepip/rewheel/ %{pylibdir}/ensurepip/_bundled/*.whl
%dir %{pylibdir}/ensurepip/rewheel/__pycache__/
%{pylibdir}/ensurepip/rewheel/*.py
%{pylibdir}/ensurepip/rewheel/__pycache__/*%{bytecode_suffixes}
%endif %endif
# The majority of the test module lives in the test subpackage # The majority of the test module lives in the test subpackage
@ -1206,11 +1308,8 @@ fi
%{pylibdir}/pydoc_data %{pylibdir}/pydoc_data
%{dynload_dir}/_blake2.%{SOABI_optimized}.so %{dynload_dir}/_blake2.%{SOABI_optimized}.so
%{dynload_dir}/_md5.%{SOABI_optimized}.so
%{dynload_dir}/_sha1.%{SOABI_optimized}.so
%{dynload_dir}/_sha256.%{SOABI_optimized}.so
%{dynload_dir}/_sha3.%{SOABI_optimized}.so %{dynload_dir}/_sha3.%{SOABI_optimized}.so
%{dynload_dir}/_sha512.%{SOABI_optimized}.so %{dynload_dir}/_hmacopenssl.%{SOABI_optimized}.so
%{dynload_dir}/_asyncio.%{SOABI_optimized}.so %{dynload_dir}/_asyncio.%{SOABI_optimized}.so
%{dynload_dir}/_bisect.%{SOABI_optimized}.so %{dynload_dir}/_bisect.%{SOABI_optimized}.so
@ -1445,11 +1544,8 @@ fi
# ...with debug builds of the built-in "extension" modules: # ...with debug builds of the built-in "extension" modules:
%{dynload_dir}/_blake2.%{SOABI_debug}.so %{dynload_dir}/_blake2.%{SOABI_debug}.so
%{dynload_dir}/_md5.%{SOABI_debug}.so
%{dynload_dir}/_sha1.%{SOABI_debug}.so
%{dynload_dir}/_sha256.%{SOABI_debug}.so
%{dynload_dir}/_sha3.%{SOABI_debug}.so %{dynload_dir}/_sha3.%{SOABI_debug}.so
%{dynload_dir}/_sha512.%{SOABI_debug}.so %{dynload_dir}/_hmacopenssl.%{SOABI_debug}.so
%{dynload_dir}/_asyncio.%{SOABI_debug}.so %{dynload_dir}/_asyncio.%{SOABI_debug}.so
%{dynload_dir}/_bisect.%{SOABI_debug}.so %{dynload_dir}/_bisect.%{SOABI_debug}.so
@ -1564,15 +1660,66 @@ fi
# ====================================================== # ======================================================
%changelog %changelog
* Fri Jun 07 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-4 * Fri Oct 11 2019 Tomas Orsava <torsava@redhat.com> - 3.6.8-15.1
- Patch 329 (FIPS) modified: Added workaround for mod_ssl:
Skip error checking in _Py_hashlib_fips_error
Resolves: rhbz#1760106
* Thu Aug 29 2019 Tomas Orsava <torsava@redhat.com> - 3.6.8-15
- Patch 329 that adds support for OpenSSL FIPS mode has been improved and
bugfixed
Resolves: rhbz#1744670 rhbz#1745499 rhbz#1745685
* Tue Aug 06 2019 Tomas Orsava <torsava@redhat.com> - 3.6.8-14
- Adding a new patch 329 that adds support for OpenSSL FIPS mode
- Explicitly listing man pages in files section to fix an RPM warning
Resolves: rhbz#1731424
* Tue Jul 02 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-13
- Do not set PHA verify flag on client side (rhbz#1725721)
- Enable TLS 1.3 post-handshake authentication in http.client (rhbz#1671353)
* Fri Jun 21 2019 Miro Hrončok <mhroncok@redhat.com> - 3.6.8-12
- Use RPM built wheels of pip and setuptools in ensurepip instead of our rewheel patch
- Require platform-python-setuptools from platform-python-devel to prevent packaging errors
Resolves: rhbz#1701286
* Fri Jun 07 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-11
- Fix for CVE-2019-10160 - Fix for CVE-2019-10160
Resolves: rhbz#1714756 Resolves: rhbz#1689318
* Fri May 03 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-3 * Wed May 29 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-10
- Updated fix for CVE-2019-9636 (rhbz#1714756) - Security fix for CVE-2019-9948
Resolves: rhbz#1714643
* Wed Apr 3 2019 Miro Hrončok <mhroncok@redhat.com> - 3.6.8-2 * Tue May 21 2019 Miro Hrončok <mhroncok@redhat.com> - 3.6.8-9
- Security fix for CVE-2019-9636 (rhbz#1693973) - Reduced default build flags used to build extension modules
https://fedoraproject.org/wiki/Changes/Python_Extension_Flags
Resolves: rhbz#1634784
* Mon May 13 2019 Tomas Orsava <torsava@redhat.com> - 3.6.8-8
- gzip the unversioned-python man page
Resolves: rhbz#1665514
* Wed May 08 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-7
- Disallow control chars in http URLs
- Fixes CVE-2019-9740 and CVE-2019-9947
Resolves: rhbz#1704365 and rhbz#1703531
* Fri May 03 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-6
- Updated fix for CVE-2019-9636 (rhbz#1689318)
* Wed Apr 3 2019 Miro Hrončok <mhroncok@redhat.com> - 3.6.8-5
- Security fix for CVE-2019-9636 (rhbz#1689318)
* Wed Mar 20 2019 Victor Stinner <vstinner@redhat.com> - 3.6.8-4
- Security fix for CVE-2019-5010 (rhbz#1666789)
* Wed Mar 13 2019 Victor Stinner <vstinner@redhat.com> - 3.6.8-3
- Fix test_tarfile on ppc64 (rhbz#1639490)
* Fri Jan 18 2019 Victor Stinner <vstinner@redhat.com> - 3.6.8-2
- test_ssl fixes for TLS 1.3 and OpenSSL 1.1.1 (rhbz#1639531)
* Wed Jan 09 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-1 * Wed Jan 09 2019 Charalampos Stratakis <cstratak@redhat.com> - 3.6.8-1
- Update to 3.6.8 - Update to 3.6.8