diff --git a/SOURCES/00330-CVE-2018-20852.patch b/SOURCES/00330-CVE-2018-20852.patch new file mode 100644 index 0000000..97ff719 --- /dev/null +++ b/SOURCES/00330-CVE-2018-20852.patch @@ -0,0 +1,93 @@ +diff --git a/Lib/cookielib.py b/Lib/cookielib.py +index f9c8d2f..9144e1f 100644 +--- a/Lib/cookielib.py ++++ b/Lib/cookielib.py +@@ -1123,6 +1123,11 @@ class DefaultCookiePolicy(CookiePolicy): + req_host, erhn = eff_request_host(request) + domain = cookie.domain + ++ if domain and not domain.startswith("."): ++ dotdomain = "." + domain ++ else: ++ dotdomain = domain ++ + # strict check of non-domain cookies: Mozilla does this, MSIE5 doesn't + if (cookie.version == 0 and + (self.strict_ns_domain & self.DomainStrictNonDomain) and +@@ -1135,7 +1140,7 @@ class DefaultCookiePolicy(CookiePolicy): + _debug(" effective request-host name %s does not domain-match " + "RFC 2965 cookie domain %s", erhn, domain) + return False +- if cookie.version == 0 and not ("."+erhn).endswith(domain): ++ if cookie.version == 0 and not ("."+erhn).endswith(dotdomain): + _debug(" request-host %s does not match Netscape cookie domain " + "%s", req_host, domain) + return False +@@ -1149,7 +1154,11 @@ class DefaultCookiePolicy(CookiePolicy): + req_host = "."+req_host + if not erhn.startswith("."): + erhn = "."+erhn +- if not (req_host.endswith(domain) or erhn.endswith(domain)): ++ if domain and not domain.startswith("."): ++ dotdomain = "." + domain ++ else: ++ dotdomain = domain ++ if not (req_host.endswith(dotdomain) or erhn.endswith(dotdomain)): + #_debug(" request domain %s does not match cookie domain %s", + # req_host, domain) + return False +diff --git a/Lib/test/test_cookielib.py b/Lib/test/test_cookielib.py +index dd0ad32..b4f5ea0 100644 +--- a/Lib/test/test_cookielib.py ++++ b/Lib/test/test_cookielib.py +@@ -353,6 +353,7 @@ class CookieTests(TestCase): + ("http://foo.bar.com/", ".foo.bar.com", True), + ("http://foo.bar.com/", "foo.bar.com", True), + ("http://foo.bar.com/", ".bar.com", True), ++ ("http://foo.bar.com/", "bar.com", True), + ("http://foo.bar.com/", "com", True), + ("http://foo.com/", "rhubarb.foo.com", False), + ("http://foo.com/", ".foo.com", True), +@@ -363,6 +364,8 @@ class CookieTests(TestCase): + ("http://foo/", "foo", True), + ("http://foo/", "foo.local", True), + ("http://foo/", ".local", True), ++ ("http://barfoo.com", ".foo.com", False), ++ ("http://barfoo.com", "foo.com", False), + ]: + request = urllib2.Request(url) + r = pol.domain_return_ok(domain, request) +@@ -910,6 +913,33 @@ class CookieTests(TestCase): + c.add_cookie_header(req) + self.assertTrue(not req.has_header("Cookie")) + ++ c.clear() ++ ++ pol.set_blocked_domains([]) ++ req = Request("http://acme.com/") ++ res = FakeResponse(headers, "http://acme.com/") ++ cookies = c.make_cookies(res, req) ++ c.extract_cookies(res, req) ++ self.assertEqual(len(c), 1) ++ ++ req = Request("http://acme.com/") ++ c.add_cookie_header(req) ++ self.assertTrue(req.has_header("Cookie")) ++ ++ req = Request("http://badacme.com/") ++ c.add_cookie_header(req) ++ self.assertFalse(pol.return_ok(cookies[0], req)) ++ self.assertFalse(req.has_header("Cookie")) ++ ++ p = pol.set_blocked_domains(["acme.com"]) ++ req = Request("http://acme.com/") ++ c.add_cookie_header(req) ++ self.assertFalse(req.has_header("Cookie")) ++ ++ req = Request("http://badacme.com/") ++ c.add_cookie_header(req) ++ self.assertFalse(req.has_header("Cookie")) ++ + def test_secure(self): + from cookielib import CookieJar, DefaultCookiePolicy + diff --git a/SOURCES/00332-CVE-2019-16056.patch b/SOURCES/00332-CVE-2019-16056.patch new file mode 100644 index 0000000..aff257f --- /dev/null +++ b/SOURCES/00332-CVE-2019-16056.patch @@ -0,0 +1,54 @@ +diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py +index 690db2c..dc49d2e 100644 +--- a/Lib/email/_parseaddr.py ++++ b/Lib/email/_parseaddr.py +@@ -336,7 +336,12 @@ class AddrlistClass: + aslist.append('@') + self.pos += 1 + self.gotonext() +- return EMPTYSTRING.join(aslist) + self.getdomain() ++ domain = self.getdomain() ++ if not domain: ++ # Invalid domain, return an empty address instead of returning a ++ # local part to denote failed parsing. ++ return EMPTYSTRING ++ return EMPTYSTRING.join(aslist) + domain + + def getdomain(self): + """Get the complete domain name from an address.""" +@@ -351,6 +356,10 @@ class AddrlistClass: + elif self.field[self.pos] == '.': + self.pos += 1 + sdlist.append('.') ++ elif self.field[self.pos] == '@': ++ # bpo-34155: Don't parse domains with two `@` like ++ # `a@malicious.org@important.com`. ++ return EMPTYSTRING + elif self.field[self.pos] in self.atomends: + break + else: +diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py +index b32da9d..1739033 100644 +--- a/Lib/email/test/test_email.py ++++ b/Lib/email/test/test_email.py +@@ -2308,6 +2308,20 @@ class TestMiscellaneous(TestEmailBase): + self.assertEqual(Utils.parseaddr('<>'), ('', '')) + self.assertEqual(Utils.formataddr(Utils.parseaddr('<>')), '') + ++ def test_parseaddr_multiple_domains(self): ++ self.assertEqual( ++ Utils.parseaddr('a@b@c'), ++ ('', '') ++ ) ++ self.assertEqual( ++ Utils.parseaddr('a@b.c@c'), ++ ('', '') ++ ) ++ self.assertEqual( ++ Utils.parseaddr('a@172.17.0.1@c'), ++ ('', '') ++ ) ++ + def test_noquote_dump(self): + self.assertEqual( + Utils.formataddr(('A Silly Person', 'person@dom.ain')), diff --git a/SOURCES/99999-python-2.7.5-issues-17979-17998.patch b/SOURCES/99999-python-2.7.5-issues-17979-17998.patch deleted file mode 100644 index fbf2388..0000000 --- a/SOURCES/99999-python-2.7.5-issues-17979-17998.patch +++ /dev/null @@ -1,129 +0,0 @@ - -# HG changeset patch -# User Serhiy Storchaka -# Date 1369166013 -10800 -# Node ID 8408eed151ebee1c546414f1f40be46c1ad76077 -# Parent 7fce9186accb10122e45d975f4b380c2ed0fae35 -Issue #17979: Fixed the re module in build with --disable-unicode. - -diff --git a/Modules/sre.h b/Modules/sre.h ---- a/Modules/sre.h -+++ b/Modules/sre.h -@@ -23,8 +23,8 @@ - # define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX + 1u) - # endif - #else --# define SRE_CODE unsigned long --# if SIZEOF_SIZE_T > SIZEOF_LONG -+# define SRE_CODE unsigned int -+# if SIZEOF_SIZE_T > SIZEOF_INT - # define SRE_MAXREPEAT (~(SRE_CODE)0) - # else - # define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX + 1u) - - -# HG changeset patch -# User Serhiy Storchaka -# Date 1375547193 -10800 -# Node ID e5e425fd1e4f7e859abdced43621203cdfa87a16 -# Parent 8205e72b5cfcdb7a3450c80f3368eff610bc650c -Issue #17998: Fix an internal error in regular expression engine. - -diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py ---- a/Lib/test/test_re.py -+++ b/Lib/test/test_re.py -@@ -907,6 +907,16 @@ class ReTests(unittest.TestCase): - self.assertEqual(m.group(1), "") - self.assertEqual(m.group(2), "y") - -+ def test_issue17998(self): -+ for reps in '*', '+', '?', '{1}': -+ for mod in '', '?': -+ pattern = '.' + reps + mod + 'yz' -+ self.assertEqual(re.compile(pattern, re.S).findall('xyz'), -+ ['xyz'], msg=pattern) -+ pattern = pattern.encode() -+ self.assertEqual(re.compile(pattern, re.S).findall(b'xyz'), -+ [b'xyz'], msg=pattern) -+ - - - def run_re_tests(): -diff --git a/Modules/_sre.c b/Modules/_sre.c ---- a/Modules/_sre.c -+++ b/Modules/_sre.c -@@ -1028,7 +1028,7 @@ entrance: - TRACE(("|%p|%p|REPEAT_ONE %d %d\n", ctx->pattern, ctx->ptr, - ctx->pattern[1], ctx->pattern[2])); - -- if (ctx->pattern[1] > end - ctx->ptr) -+ if ((Py_ssize_t) ctx->pattern[1] > end - ctx->ptr) - RETURN_FAILURE; /* cannot match */ - - state->ptr = ctx->ptr; -@@ -1111,7 +1111,7 @@ entrance: - TRACE(("|%p|%p|MIN_REPEAT_ONE %d %d\n", ctx->pattern, ctx->ptr, - ctx->pattern[1], ctx->pattern[2])); - -- if (ctx->pattern[1] > end - ctx->ptr) -+ if ((Py_ssize_t) ctx->pattern[1] > end - ctx->ptr) - RETURN_FAILURE; /* cannot match */ - - state->ptr = ctx->ptr; -@@ -1210,7 +1210,7 @@ entrance: - TRACE(("|%p|%p|MAX_UNTIL %d\n", ctx->pattern, - ctx->ptr, ctx->count)); - -- if (ctx->count < ctx->u.rep->pattern[1]) { -+ if (ctx->count < (Py_ssize_t) ctx->u.rep->pattern[1]) { - /* not enough matches */ - ctx->u.rep->count = ctx->count; - DO_JUMP(JUMP_MAX_UNTIL_1, jump_max_until_1, -@@ -1224,7 +1224,7 @@ entrance: - RETURN_FAILURE; - } - -- if ((ctx->count < ctx->u.rep->pattern[2] || -+ if ((ctx->count < (Py_ssize_t) ctx->u.rep->pattern[2] || - ctx->u.rep->pattern[2] == SRE_MAXREPEAT) && - state->ptr != ctx->u.rep->last_ptr) { - /* we may have enough matches, but if we can -@@ -1273,7 +1273,7 @@ entrance: - TRACE(("|%p|%p|MIN_UNTIL %d %p\n", ctx->pattern, - ctx->ptr, ctx->count, ctx->u.rep->pattern)); - -- if (ctx->count < ctx->u.rep->pattern[1]) { -+ if (ctx->count < (Py_ssize_t) ctx->u.rep->pattern[1]) { - /* not enough matches */ - ctx->u.rep->count = ctx->count; - DO_JUMP(JUMP_MIN_UNTIL_1, jump_min_until_1, -@@ -1302,7 +1302,7 @@ entrance: - - LASTMARK_RESTORE(); - -- if ((ctx->count >= ctx->u.rep->pattern[2] -+ if ((ctx->count >= (Py_ssize_t) ctx->u.rep->pattern[2] - && ctx->u.rep->pattern[2] != SRE_MAXREPEAT) || - state->ptr == ctx->u.rep->last_ptr) - RETURN_FAILURE; -diff --git a/Modules/sre.h b/Modules/sre.h ---- a/Modules/sre.h -+++ b/Modules/sre.h -@@ -20,14 +20,14 @@ - # if SIZEOF_SIZE_T > 4 - # define SRE_MAXREPEAT (~(SRE_CODE)0) - # else --# define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX + 1u) -+# define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX) - # endif - #else - # define SRE_CODE unsigned int - # if SIZEOF_SIZE_T > SIZEOF_INT - # define SRE_MAXREPEAT (~(SRE_CODE)0) - # else --# define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX + 1u) -+# define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX) - # endif - #endif - - diff --git a/SPECS/python.spec b/SPECS/python.spec index 7970af5..f302aaf 100644 --- a/SPECS/python.spec +++ b/SPECS/python.spec @@ -114,7 +114,7 @@ Summary: An interpreted, interactive, object-oriented programming language Name: %{python} # Remember to also rebase python-docs when changing this: Version: 2.7.5 -Release: 86%{?dist} +Release: 88%{?dist} License: Python Group: Development/Languages Requires: %{python}-libs%{?_isa} = %{version}-%{release} @@ -1307,7 +1307,17 @@ Patch324: 00324-disallow-control-chars-in-http-urls.patch # Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1704174 Patch325: 00325-CVE-2019-9948.patch -Patch99999: 99999-python-2.7.5-issues-17979-17998.patch +# 00330 # +# Fix CVE-2018-20852: cookie domain check returning incorrect results +# Fixed upstream: https://bugs.python.org/issue35121 +# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1741551 +Patch330: 00330-CVE-2018-20852.patch + +# 00332 # +# Fix CVE-2019-16056: Dont parse domains containing @ +# Fixed upstream: https://bugs.python.org/issue34155 +# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1750773 +Patch332: 00332-CVE-2019-16056.patch # (New patches go here ^^^) # @@ -1753,10 +1763,9 @@ mv Modules/cryptmodule.c Modules/_cryptmodule.c %patch320 -p1 %patch324 -p1 %patch325 -p1 +%patch330 -p1 +%patch332 -p1 -%ifarch %{arm} %{ix86} ppc -%patch99999 -p1 -%endif # This shouldn't be necesarry, but is right now (2.2a3) find -name "*~" |xargs rm -f @@ -2629,8 +2638,13 @@ rm -fr %{buildroot} # ====================================================== %changelog -* Tue Aug 13 2019 Pablo Greco - 2.7.5-86 -- Fix regex processing in 32 bit arches +* Wed Sep 25 2019 Charalampos Stratakis - 2.7.5-88 +- Security fix for CVE-2019-16056 +Resolves: rhbz#1750773 + +* Tue Aug 27 2019 Charalampos Stratakis - 2.7.5-87 +- Fix CVE-2018-20852 +Resolves: rhbz#1741551 * Tue Jun 11 2019 Charalampos Stratakis - 2.7.5-86 - Security fix for CVE-2019-10160