From 33bab96f787715532cc4912076c71e5d063e5df5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Thu, 9 Jul 2020 00:07:43 +0300 Subject: python3: Upgrade 3.7.7 -> 3.7.8 Backported patch removed. (From OE-Core rev: 2c33c482ba6691ae82252f192b3c03e8c8fb7d55) Signed-off-by: Adrian Bunk Signed-off-by: Anuj Mittal Signed-off-by: Richard Purdie --- ...VE-2020-8492-Fix-AbstractBasicAuthHandler.patch | 248 --------------- meta/recipes-devtools/python/python3_3.7.7.bb | 344 --------------------- meta/recipes-devtools/python/python3_3.7.8.bb | 343 ++++++++++++++++++++ 3 files changed, 343 insertions(+), 592 deletions(-) delete mode 100644 meta/recipes-devtools/python/files/0001-bpo-39503-CVE-2020-8492-Fix-AbstractBasicAuthHandler.patch delete mode 100644 meta/recipes-devtools/python/python3_3.7.7.bb create mode 100644 meta/recipes-devtools/python/python3_3.7.8.bb diff --git a/meta/recipes-devtools/python/files/0001-bpo-39503-CVE-2020-8492-Fix-AbstractBasicAuthHandler.patch b/meta/recipes-devtools/python/files/0001-bpo-39503-CVE-2020-8492-Fix-AbstractBasicAuthHandler.patch deleted file mode 100644 index e16b99bcb9..0000000000 --- a/meta/recipes-devtools/python/files/0001-bpo-39503-CVE-2020-8492-Fix-AbstractBasicAuthHandler.patch +++ /dev/null @@ -1,248 +0,0 @@ -From 0b297d4ff1c0e4480ad33acae793fbaf4bf015b4 Mon Sep 17 00:00:00 2001 -From: Victor Stinner -Date: Thu, 2 Apr 2020 02:52:20 +0200 -Subject: [PATCH] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler - (GH-18284) - -Upstream-Status: Backport -(https://github.com/python/cpython/commit/0b297d4ff1c0e4480ad33acae793fbaf4bf015b4) - -CVE: CVE-2020-8492 - -The AbstractBasicAuthHandler class of the urllib.request module uses -an inefficient regular expression which can be exploited by an -attacker to cause a denial of service. Fix the regex to prevent the -catastrophic backtracking. Vulnerability reported by Ben Caller -and Matt Schwager. - -AbstractBasicAuthHandler of urllib.request now parses all -WWW-Authenticate HTTP headers and accepts multiple challenges per -header: use the realm of the first Basic challenge. - -Co-Authored-By: Serhiy Storchaka -Signed-off-by: Trevor Gamblin ---- - Lib/test/test_urllib2.py | 90 ++++++++++++------- - Lib/urllib/request.py | 69 ++++++++++---- - .../2020-03-25-16-02-16.bpo-39503.YmMbYn.rst | 3 + - .../2020-01-30-16-15-29.bpo-39503.B299Yq.rst | 5 ++ - 4 files changed, 115 insertions(+), 52 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2020-03-25-16-02-16.bpo-39503.YmMbYn.rst - create mode 100644 Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst - -diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py -index 8abedaac98..e69ac3e213 100644 ---- a/Lib/test/test_urllib2.py -+++ b/Lib/test/test_urllib2.py -@@ -1446,40 +1446,64 @@ class HandlerTests(unittest.TestCase): - bypass = {'exclude_simple': True, 'exceptions': []} - self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass)) - -- def test_basic_auth(self, quote_char='"'): -- opener = OpenerDirector() -- password_manager = MockPasswordManager() -- auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager) -- realm = "ACME Widget Store" -- http_handler = MockHTTPHandler( -- 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' % -- (quote_char, realm, quote_char)) -- opener.add_handler(auth_handler) -- opener.add_handler(http_handler) -- self._test_basic_auth(opener, auth_handler, "Authorization", -- realm, http_handler, password_manager, -- "http://acme.example.com/protected", -- "http://acme.example.com/protected", -- ) -- -- def test_basic_auth_with_single_quoted_realm(self): -- self.test_basic_auth(quote_char="'") -- -- def test_basic_auth_with_unquoted_realm(self): -- opener = OpenerDirector() -- password_manager = MockPasswordManager() -- auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager) -- realm = "ACME Widget Store" -- http_handler = MockHTTPHandler( -- 401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm) -- opener.add_handler(auth_handler) -- opener.add_handler(http_handler) -- with self.assertWarns(UserWarning): -+ def check_basic_auth(self, headers, realm): -+ with self.subTest(realm=realm, headers=headers): -+ opener = OpenerDirector() -+ password_manager = MockPasswordManager() -+ auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager) -+ body = '\r\n'.join(headers) + '\r\n\r\n' -+ http_handler = MockHTTPHandler(401, body) -+ opener.add_handler(auth_handler) -+ opener.add_handler(http_handler) - self._test_basic_auth(opener, auth_handler, "Authorization", -- realm, http_handler, password_manager, -- "http://acme.example.com/protected", -- "http://acme.example.com/protected", -- ) -+ realm, http_handler, password_manager, -+ "http://acme.example.com/protected", -+ "http://acme.example.com/protected") -+ -+ def test_basic_auth(self): -+ realm = "realm2@example.com" -+ realm2 = "realm2@example.com" -+ basic = f'Basic realm="{realm}"' -+ basic2 = f'Basic realm="{realm2}"' -+ other_no_realm = 'Otherscheme xxx' -+ digest = (f'Digest realm="{realm2}", ' -+ f'qop="auth, auth-int", ' -+ f'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ' -+ f'opaque="5ccc069c403ebaf9f0171e9517f40e41"') -+ for realm_str in ( -+ # test "quote" and 'quote' -+ f'Basic realm="{realm}"', -+ f"Basic realm='{realm}'", -+ -+ # charset is ignored -+ f'Basic realm="{realm}", charset="UTF-8"', -+ -+ # Multiple challenges per header -+ f'{basic}, {basic2}', -+ f'{basic}, {other_no_realm}', -+ f'{other_no_realm}, {basic}', -+ f'{basic}, {digest}', -+ f'{digest}, {basic}', -+ ): -+ headers = [f'WWW-Authenticate: {realm_str}'] -+ self.check_basic_auth(headers, realm) -+ -+ # no quote: expect a warning -+ with support.check_warnings(("Basic Auth Realm was unquoted", -+ UserWarning)): -+ headers = [f'WWW-Authenticate: Basic realm={realm}'] -+ self.check_basic_auth(headers, realm) -+ -+ # Multiple headers: one challenge per header. -+ # Use the first Basic realm. -+ for challenges in ( -+ [basic, basic2], -+ [basic, digest], -+ [digest, basic], -+ ): -+ headers = [f'WWW-Authenticate: {challenge}' -+ for challenge in challenges] -+ self.check_basic_auth(headers, realm) - - def test_proxy_basic_auth(self): - opener = OpenerDirector() -diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py -index 7fe50535da..2a3d71554f 100644 ---- a/Lib/urllib/request.py -+++ b/Lib/urllib/request.py -@@ -937,8 +937,15 @@ class AbstractBasicAuthHandler: - - # allow for double- and single-quoted realm values - # (single quotes are a violation of the RFC, but appear in the wild) -- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' -- 'realm=(["\']?)([^"\']*)\\2', re.I) -+ rx = re.compile('(?:^|,)' # start of the string or ',' -+ '[ \t]*' # optional whitespaces -+ '([^ \t]+)' # scheme like "Basic" -+ '[ \t]+' # mandatory whitespaces -+ # realm=xxx -+ # realm='xxx' -+ # realm="xxx" -+ 'realm=(["\']?)([^"\']*)\\2', -+ re.I) - - # XXX could pre-emptively send auth info already accepted (RFC 2617, - # end of section 2, and section 1.2 immediately after "credentials" -@@ -950,27 +957,51 @@ class AbstractBasicAuthHandler: - self.passwd = password_mgr - self.add_password = self.passwd.add_password - -+ def _parse_realm(self, header): -+ # parse WWW-Authenticate header: accept multiple challenges per header -+ found_challenge = False -+ for mo in AbstractBasicAuthHandler.rx.finditer(header): -+ scheme, quote, realm = mo.groups() -+ if quote not in ['"', "'"]: -+ warnings.warn("Basic Auth Realm was unquoted", -+ UserWarning, 3) -+ -+ yield (scheme, realm) -+ -+ found_challenge = True -+ -+ if not found_challenge: -+ if header: -+ scheme = header.split()[0] -+ else: -+ scheme = '' -+ yield (scheme, None) -+ - def http_error_auth_reqed(self, authreq, host, req, headers): - # host may be an authority (without userinfo) or a URL with an - # authority -- # XXX could be multiple headers -- authreq = headers.get(authreq, None) -+ headers = headers.get_all(authreq) -+ if not headers: -+ # no header found -+ return - -- if authreq: -- scheme = authreq.split()[0] -- if scheme.lower() != 'basic': -- raise ValueError("AbstractBasicAuthHandler does not" -- " support the following scheme: '%s'" % -- scheme) -- else: -- mo = AbstractBasicAuthHandler.rx.search(authreq) -- if mo: -- scheme, quote, realm = mo.groups() -- if quote not in ['"',"'"]: -- warnings.warn("Basic Auth Realm was unquoted", -- UserWarning, 2) -- if scheme.lower() == 'basic': -- return self.retry_http_basic_auth(host, req, realm) -+ unsupported = None -+ for header in headers: -+ for scheme, realm in self._parse_realm(header): -+ if scheme.lower() != 'basic': -+ unsupported = scheme -+ continue -+ -+ if realm is not None: -+ # Use the first matching Basic challenge. -+ # Ignore following challenges even if they use the Basic -+ # scheme. -+ return self.retry_http_basic_auth(host, req, realm) -+ -+ if unsupported is not None: -+ raise ValueError("AbstractBasicAuthHandler does not " -+ "support the following scheme: %r" -+ % (scheme,)) - - def retry_http_basic_auth(self, host, req, realm): - user, pw = self.passwd.find_user_password(realm, host) -diff --git a/Misc/NEWS.d/next/Library/2020-03-25-16-02-16.bpo-39503.YmMbYn.rst b/Misc/NEWS.d/next/Library/2020-03-25-16-02-16.bpo-39503.YmMbYn.rst -new file mode 100644 -index 0000000000..be80ce79d9 ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2020-03-25-16-02-16.bpo-39503.YmMbYn.rst -@@ -0,0 +1,3 @@ -+:class:`~urllib.request.AbstractBasicAuthHandler` of :mod:`urllib.request` -+now parses all WWW-Authenticate HTTP headers and accepts multiple challenges -+per header: use the realm of the first Basic challenge. -diff --git a/Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst b/Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst -new file mode 100644 -index 0000000000..9f2800581c ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst -@@ -0,0 +1,5 @@ -+CVE-2020-8492: The :class:`~urllib.request.AbstractBasicAuthHandler` class of the -+:mod:`urllib.request` module uses an inefficient regular expression which can -+be exploited by an attacker to cause a denial of service. Fix the regex to -+prevent the catastrophic backtracking. Vulnerability reported by Ben Caller -+and Matt Schwager. --- -2.24.1 - diff --git a/meta/recipes-devtools/python/python3_3.7.7.bb b/meta/recipes-devtools/python/python3_3.7.7.bb deleted file mode 100644 index 4d2578c817..0000000000 --- a/meta/recipes-devtools/python/python3_3.7.7.bb +++ /dev/null @@ -1,344 +0,0 @@ -SUMMARY = "The Python Programming Language" -HOMEPAGE = "http://www.python.org" -LICENSE = "PSFv2" -SECTION = "devel/python" - -LIC_FILES_CHKSUM = "file://LICENSE;md5=203a6dbc802ee896020a47161e759642" - -SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \ - file://run-ptest \ - file://create_manifest3.py \ - file://get_module_deps3.py \ - file://python3-manifest.json \ - file://check_build_completeness.py \ - file://cgi_py.patch \ - file://0001-Do-not-add-usr-lib-termcap-to-linker-flags-to-avoid-.patch \ - ${@bb.utils.contains('PACKAGECONFIG', 'tk', '', 'file://avoid_warning_about_tkinter.patch', d)} \ - file://0001-Do-not-use-the-shell-version-of-python-config-that-w.patch \ - file://python-config.patch \ - file://0001-Makefile.pre-use-qemu-wrapper-when-gathering-profile.patch \ - file://0001-Do-not-hardcode-lib-as-location-for-site-packages-an.patch \ - file://0001-python3-use-cc_basename-to-replace-CC-for-checking-c.patch \ - file://0002-Don-t-do-runtime-test-to-get-float-byte-order.patch \ - file://0003-setup.py-pass-missing-libraries-to-Extension-for-mul.patch \ - file://0001-Lib-sysconfig.py-fix-another-place-where-lib-is-hard.patch \ - file://0001-Makefile-fix-Issue36464-parallel-build-race-problem.patch \ - file://0001-bpo-36852-proper-detection-of-mips-architecture-for-.patch \ - file://crosspythonpath.patch \ - file://reformat_sysconfig.py \ - file://0001-Use-FLAG_REF-always-for-interned-strings.patch \ - file://0001-test_locale.py-correct-the-test-output-format.patch \ - file://0017-setup.py-do-not-report-missing-dependencies-for-disa.patch \ - file://0001-bpo-39503-CVE-2020-8492-Fix-AbstractBasicAuthHandler.patch \ - " - -SRC_URI_append_class-native = " \ - file://0001-distutils-sysconfig-append-STAGING_LIBDIR-python-sys.patch \ - file://12-distutils-prefix-is-inside-staging-area.patch \ - file://0001-Don-t-search-system-for-headers-libraries.patch \ - " -SRC_URI_append_class-nativesdk = " \ - file://0001-main.c-if-OEPYTHON3HOME-is-set-use-instead-of-PYTHON.patch \ - " - -SRC_URI[md5sum] = "172c650156f7bea68ce31b2fd01fa766" -SRC_URI[sha256sum] = "06a0a9f1bf0d8cd1e4121194d666c4e28ddae4dd54346de6c343206599f02136" - -# exclude pre-releases for both python 2.x and 3.x -UPSTREAM_CHECK_REGEX = "[Pp]ython-(?P\d+(\.\d+)+).tar" - -CVE_PRODUCT = "python" - -# This is not exploitable when glibc has CVE-2016-10739 fixed. -CVE_CHECK_WHITELIST += "CVE-2019-18348" - -PYTHON_MAJMIN = "3.7" -PYTHON_BINABI = "${PYTHON_MAJMIN}m" - -S = "${WORKDIR}/Python-${PV}" - -BBCLASSEXTEND = "native nativesdk" - -inherit autotools pkgconfig qemu ptest multilib_header update-alternatives - -MULTILIB_SUFFIX = "${@d.getVar('base_libdir',1).split('/')[-1]}" - -ALTERNATIVE_${PN}-dev = "python-config" -ALTERNATIVE_LINK_NAME[python-config] = "${bindir}/python${PYTHON_BINABI}-config" -ALTERNATIVE_TARGET[python-config] = "${bindir}/python${PYTHON_BINABI}-config-${MULTILIB_SUFFIX}" - - -DEPENDS = "bzip2-replacement-native libffi bzip2 openssl sqlite3 zlib virtual/libintl xz virtual/crypt util-linux libtirpc libnsl2" -DEPENDS_append_class-target = " python3-native" -DEPENDS_append_class-nativesdk = " python3-native" - -EXTRA_OECONF = " --without-ensurepip --enable-shared" -EXTRA_OECONF_append_class-native = " --bindir=${bindir}/${PN}" - -export CROSSPYTHONPATH="${STAGING_LIBDIR_NATIVE}/python${PYTHON_MAJMIN}/lib-dynload/" - -EXTRANATIVEPATH += "python3-native" - -CACHED_CONFIGUREVARS = " \ - ac_cv_file__dev_ptmx=yes \ - ac_cv_file__dev_ptc=no \ - ac_cv_working_tzset=yes \ -" -python() { - # PGO currently causes builds to not be reproducible, so disable it for - # now. See YOCTO #13407 - if bb.utils.contains('MACHINE_FEATURES', 'qemu-usermode', True, False, d) and d.getVar('BUILD_REPRODUCIBLE_BINARIES') != '1': - d.setVar('PACKAGECONFIG_PGO', 'pgo') - else: - d.setVar('PACKAGECONFIG_PGO', '') -} - -PACKAGECONFIG_class-target ??= "readline ${PACKAGECONFIG_PGO} gdbm" -PACKAGECONFIG_class-native ??= "readline gdbm" -PACKAGECONFIG_class-nativesdk ??= "readline gdbm" -PACKAGECONFIG[readline] = ",,readline" -# Use profile guided optimisation by running PyBench inside qemu-user -PACKAGECONFIG[pgo] = "--enable-optimizations,,qemu-native" -PACKAGECONFIG[tk] = ",,tk" -PACKAGECONFIG[gdbm] = ",,gdbm" - -do_configure_prepend () { - mkdir -p ${B}/Modules - cat > ${B}/Modules/Setup.local << EOF -*disabled* -${@bb.utils.contains('PACKAGECONFIG', 'gdbm', '', '_gdbm _dbm', d)} -${@bb.utils.contains('PACKAGECONFIG', 'readline', '', 'readline', d)} -EOF -} - -CPPFLAGS_append = " -I${STAGING_INCDIR}/ncursesw -I${STAGING_INCDIR}/uuid" - -EXTRA_OEMAKE = '\ - STAGING_LIBDIR=${STAGING_LIBDIR} \ - STAGING_INCDIR=${STAGING_INCDIR} \ - LIB=${baselib} \ -' - -do_compile_prepend_class-target() { - if ${@bb.utils.contains('PACKAGECONFIG', 'pgo', 'true', 'false', d)}; then - qemu_binary="${@qemu_wrapper_cmdline(d, '${STAGING_DIR_TARGET}', ['${B}', '${STAGING_DIR_TARGET}/${base_libdir}'])}" - cat >pgo-wrapper < ${B}/Modules/Setup.local << EOF +*disabled* +${@bb.utils.contains('PACKAGECONFIG', 'gdbm', '', '_gdbm _dbm', d)} +${@bb.utils.contains('PACKAGECONFIG', 'readline', '', 'readline', d)} +EOF +} + +CPPFLAGS_append = " -I${STAGING_INCDIR}/ncursesw -I${STAGING_INCDIR}/uuid" + +EXTRA_OEMAKE = '\ + STAGING_LIBDIR=${STAGING_LIBDIR} \ + STAGING_INCDIR=${STAGING_INCDIR} \ + LIB=${baselib} \ +' + +do_compile_prepend_class-target() { + if ${@bb.utils.contains('PACKAGECONFIG', 'pgo', 'true', 'false', d)}; then + qemu_binary="${@qemu_wrapper_cmdline(d, '${STAGING_DIR_TARGET}', ['${B}', '${STAGING_DIR_TARGET}/${base_libdir}'])}" + cat >pgo-wrapper <