Repository navigation
2.7.18.15: eMudhra ssl distrust (CVE-2026-115557) + ensurepip pip 20.3.4.post3 / setuptools 44.1.1.post3 - #91
Conversation
…VE-2026-115557) Backport of the upstream ssl SPKI denylist (python/cpython pythonGH-160042, 3.11 commit af52b41 via 7dd8b47) to 2.7.18.x. The ssl module now rejects TLS certificate chains that contain any of a set of compromised eMudhra intermediate CA keys. This is a last line of defense while root-store updates propagate to the OS, container images and bundled trust stores. The denylist matches on the SHA-256 digest of the DER SubjectPublicKeyInfo, so it also covers cross-signed certificates that reuse one of the distrusted keys. A matching certificate fails verification with X509_V_ERR_CERT_REJECTED. 2.7.18.x adaptations: - The table lives in Modules/_ssl_distrusted_spki.h (2.7 has no Modules/_ssl/ directory); it is listed in the setup.py depends for _ssl and in PCbuild/_ssl.vcxproj(.filters). - _ssl.c is C89-style: declarations at block start, no Py_ARRAY_LENGTH. - The verify callback is installed by the SSLContext.verify_mode setter (2.7's set_verify_mode), which every CERT_OPTIONAL/CERT_REQUIRED path goes through, including create_default_context() and wrap_socket(). - X509_V_ERR_UNSPECIFIED is guarded; OpenSSL < 1.0.2 falls back to X509_V_ERR_APPLICATION_VERIFICATION for the internal-error case. - 2.7 has no MemoryBIO and no SSLCertVerificationError, so the test runs the handshake against ThreadedEchoServer and asserts "certificate verify failed". It uses tempfile.mkdtemp() in place of TemporaryDirectory. distrusted.zip uses ZipCrypto, which 2.7's zipfile can read. - Tools/msi/msi.py ships Lib/test/distrusted.zip. Verified: test_distrusted_spki fails on 2.7.18.14 ("SSLError not raised") and passes with this change; the rest of test_ssl is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ensurepip now bundles the ActiveState security releases of the last Python 2.7-compatible pip and setuptools lines: - pip 20.3.4.post3 (https://github.com/ActiveState/pip/releases/tag/20.3.4.post3, sha256 e1e5a1543954a109fed9bc1c3af01f0cde6c46f79d3389c2afb884b64fcf2949). Its vendored certifi is 2026.10.19.post1, which drops the eMudhra/emSign roots (CVE-2026-115557), and it carries the earlier ActiveState backports for the 20.3.4 line. - setuptools 44.1.1.post3 (https://github.com/ActiveState/setuptools/releases/tag/v44.1.1.post3): CVE-2022-40897, CVE-2024-6345, CVE-2025-47273 and CVE-2026-59890. The release has no wheel asset, so this wheel was built from tag v44.1.1.post3 (e2a1db3) with bootstrap.py + bdist_wheel under Python 2.7 (sha256 fc42d2f957b1172962a59fad9367200cb7385dd1ac8b3a7a36c0251779f046a5). Its files match PyPI's setuptools 44.1.1 wheel except for the three fix files (command/egg_info.py, package_index.py, unicode_utils.py), and its entry point is easy_install-2.7 rather than easy_install-3.8. These replace pip 19.2.3 and setuptools 41.2.0. pip 20 keeps pip._internal.main() only as a deprecated shim that warns on every call, so _run_pip() now calls pip._internal.cli.main.main(), as upstream 3.x ensurepip did once it bundled pip >= 19.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bump PY_VERSION to 2.7.18.15 and add release notes for the security changes in this release: the eMudhra SPKI distrust in ssl (CVE-2026-115557, pythongh-160040), and the ensurepip upgrade to pip 20.3.4.post3 (certifi 2026.10.19.post1) and setuptools 44.1.1.post3 (CVE-2022-40897, CVE-2024-6345, CVE-2025-47273, CVE-2026-59890). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ezequielp-activestate
left a comment
There was a problem hiding this comment.
No blockers. Approving. One wording correction and two small suggestions below.
What I checked (Linux x86_64, CPython built from this branch and from 2.7 in a Debian 10 container, OpenSSL 1.1.1d)
ssl distrust (CVE-2026-115557)
-
The 8 SPKI SHA-256 digests in
Modules/_ssl_distrusted_spki.hare byte-for-byte the same as upstream'sModules/_ssl/distrusted_spki.h(3.11 af52b41) and the 3.10.22.1 PR (#87). Upstream has no later fix to the list or the callback on any branch. -
_ssl.cmatches upstream's callback apart from the C89 andX509_V_ERR_UNSPECIFIEDadjustments. Upstream's other hunk, which keeps the context callback innewPySSLSocketfor post-handshake auth, has nothing to apply to on 2.7: that code is commented out there. -
Enforcement, using the test fixture chain (leaf ←
EM OV TLS CA - G3C←emSign Root TLS CA - G3) against a local TLS server:client 2.7this branch ssl.create_default_context(cafile=root)connects certificate verify failed ssl.wrap_socket(cert_reqs=CERT_REQUIRED, ca_certs=root)connects certificate verify failed httplib.HTTPSConnection(context=…)connects certificate verify failed SSLContextwithCERT_OPTIONALconnects certificate verify failed -
test_ssl: 101 tests OK on this branch, 100 OK on2.7, both with 14 skipped. Copied onto2.7, the newtest_distrusted_spkifails with "SSLError not raised". -
test_ensurepip,test_httplib,test_urllib2andtest_urllib2_localnetpass on both builds.
ensurepip
pip-20.3.4.post3-py2.py3-none-any.whlis byte-identical to the asset on the ActiveState/pip20.3.4.post3release (sha256e1e5a154…2949).setuptools-44.1.1.post3-py2.py3-none-any.whl(fc42d2f9…f046a5): every file in it matches thev44.1.1.post3tag (e2a1db3). Against PyPI's 44.1.1 wheel onlycommand/egg_info.py,package_index.pyandunicode_utils.pydiffer._PIP_VERSIONand_SETUPTOOLS_VERSIONmatch the wheels, and the old 19.2.3 and 41.2.0 wheels are gone.- After
make altinstall,python2.7 -m ensurepip --default-pipinstalls pip 20.3.4.post3 and setuptools 44.1.1.post3.pip --version,python -m ensurepip --versionandpip download sixall work.Lib/test/distrusted.zipis installed. PY_VERSIONis 2.7.18.15, and the release notes describe both changes.
🟡 The certifi claim in the release notes and PR body is not accurate
Misc/NEWS.d/2.7.18.15.rst says pip's vendored certifi "no longer trusts the eMudhra/emSign roots". The bundled pip/_vendor/certifi/cacert.pem (certifi 2026.10.19.post1) still has emSign Root CA - G1, emSign Root CA - C1 and emSign ECC Root CA - C3. Only emSign ECC Root CA - G3 was removed. That matches upstream certifi 2026.10.19, and the pip wheels in #87 and #88 have the same three roots, so it isn't a packaging mistake. The sentence should say only ECC G3 was removed.
Upstream certifi 2026.10.23 has since also dropped emSign Root CA - C1 and emSign ECC Root CA - C3, leaving only G1. That would be worth picking up in the next pip .postN on all three lines. The SPKI denylist already blocks the 8 compromised intermediates for pip, because pip's connections go through this ssl module, so this isn't urgent.
⚪ Minor
- The ActiveState/setuptools
v44.1.1.post3release has no wheel asset, so the bundled wheel can't be checked against a published file, only against the tag's source, as above. Attaching the wheel to that release would make it traceable.
Not assessed
- Windows (MSVC build,
PCbuildandmsi.pychanges). - Builds against OpenSSL 1.0.2 or 3.x. I built against Debian's 1.1.1d only, and wired
_sslthroughModules/Setup.localbecause this container'ssetup.pydidn't find OpenSSL on either branch. - The full regression suite.
pip 20.3.4.post3 vendors certifi 2026.10.19.post1, which removes only the emSign ECC Root CA - G3 root, as upstream certifi 2026.10.19 does. emSign Root CA - G1, emSign Root CA - C1 and emSign ECC Root CA - C3 are still in pip/_vendor/certifi/cacert.pem. The note said that pip no longer trusts the eMudhra/emSign roots. pip's TLS goes through the ssl module, so the SPKI denylist still blocks the compromised intermediates for it. The ensurepip commit message (cf00e34) makes the same claim; it is left as is because the 2.7.18.15.rc1 tag already points past it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks. You're right about certifi. I checked
|
ezequielp-activestate
left a comment
There was a problem hiding this comment.
Re-review of 28706dd: still approved. The only change since my review is the 2.7.18.15 release note, which now says correctly that pip's vendored certifi 2026.10.19.post1 removes only emSign ECC Root CA - G3, and that G1, C1 and ECC C3 remain trusted. It also adds that pip's TLS goes through ssl, so the eMudhra SPKI denylist in this release still covers pip's connections. That holds: pip 20.3.4 verifies through ssl contexts, which the denylist hooks.
Leaving the ensurepip commit message as is, with the published rc1 tag pointing at d573c89, is reasonable. Picking up certifi 2026.10.23 in the next pip post-release covers the remaining two roots.
|
Follow-up on the setuptools point: the wheel is now attached to ActiveState/setuptools v44.1.1.post3. It's the same file bundled here (sha256 Also: pip releases with certifi 2026.10.23 are in review as ActiveState/pip#10–#14. Once they're released, this PR's ensurepip will move from pip 20.3.4.post3 to 20.3.4.post4. |
ensurepip now bundles pip 20.3.4.post4, the ActiveState/pip release asset (sha256 53eba762...e7c5), in place of 20.3.4.post3. Its vendored certifi moves from 2026.10.19.post1 to 2026.10.23.post1, ActiveState's Python 2.7 build of certifi 2026.10.23. That release also removes the emSign Root CA - C1 and emSign ECC Root CA - C3 roots, so emSign Root CA - G1 is the only emSign root left (follow-up to GHSA-6q48-6c7w-9gw2). The 2.7.18.15 release notes say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Bundled pip is now 20.3.4.post4 (ff19c1d). Its vendored certifi is 2026.10.23.post1, so emSign Root CA - G1 is the only emSign root left. The bundled wheel is byte-identical to the release asset.
|
ezequielp-activestate
left a comment
There was a problem hiding this comment.
Re-review of ff19c1d: still approved. The only change since my last review is the bundled pip, now 20.3.4.post4. The wheel in Lib/ensurepip/_bundled/ is byte-identical to the asset on the ActiveState pip 20.3.4.post4 release (sha256 53eba762…), the post3 wheel is removed, and _PIP_VERSION matches. In a Python 2.7 venv, python -m ensurepip from this tree installs pip 20.3.4.post4 and setuptools 44.1.1.post3, and pip's vendored certifi is 2026.10.23.post1 with emSign Root CA - G1 as its only emSign root. That matches the corrected release note.
2.7.18.15 security release on top of 2.7.18.14 (release tag
2.7.18.14=2.7@ 2cb6e5d).Tracking: Linear PROD-1555 (eMudhra/emSign distrust, alongside 3.9.25.1 #88 and 3.10.22.1 #87).
Changes
1. ssl: distrust the compromised eMudhra intermediate CA keys (CVE-2026-115557, pythongh-160040) — 6690fba
Backport of upstream's SPKI denylist (3.11 af52b41).
_ssl.cgets a verify callback that hashes each certificate's SubjectPublicKeyInfo with SHA-256 and rejects the 8 eMudhra intermediate keys withX509_V_ERR_CERT_REJECTED. Because it matches on the key, it also catches cross-signs. TheSSLContext.verify_modesetter installs the callback, so it covers everyCERT_OPTIONAL/CERT_REQUIREDpath, includingcreate_default_context(),urllib2andhttplib.Differences from the 3.x patch on 2.7:
Modules/_ssl_distrusted_spki.h, because 2.7 has noModules/_ssl/directory. It is wired into thesetup.pydepends andPCbuild/_ssl.vcxproj(.filters).Py_ARRAY_LENGTH.X509_V_ERR_UNSPECIFIEDis guarded for OpenSSL older than 1.0.2.MemoryBIOand noSSLCertVerificationError, so the test does the handshake againstThreadedEchoServerand asserts "certificate verify failed".Tools/msi/msi.pynow shipsLib/test/distrusted.zip. The zip is ZipCrypto-encrypted, which 2.7'szipfilecan read.2. ensurepip: pip 20.3.4.post4 + setuptools 44.1.1.post3 — cf00e34, ff19c1d
These replace pip 19.2.3 and setuptools 41.2.0.
53eba762…e7c5). Its vendored certifi is 2026.10.23.post1 (ActiveState's Python 2.7 build of certifi 2026.10.23), which removes emSign ECC Root CA - G3, emSign Root CA - C1 and emSign ECC Root CA - C3; emSign Root CA - G1 is the only emSign root left. (Originally 20.3.4.post3 with certifi 2026.10.19.post1, which still had C1 and ECC C3.) pip's connections go through thesslmodule, so the SPKI denylist above still blocks the 8 compromised intermediates for pip.fc42d2f9…f046a5. Its contents match PyPI's 44.1.1 wheel except for the three fix files._run_pip: now callspip._internal.cli.main.main. pip 20 keepspip._internal.main()only as a shim that prints a deprecation warning on every call.3. Release — d573c89
PY_VERSION2.7.18.15 andMisc/NEWS.d/2.7.18.15.rst.Testing (Linux x86_64, built against system OpenSSL 3.0.13)
test_ssl.test_distrusted_spki: fails on pristine 2.7.18.14 with "SSLError not raised", and passes with this change. Every other test_ssl result is identical between the two builds. The 27 pre-existing test_ssl failures come from building 2.7 against OpenSSL 3.0, and appear on the base build as well.test_ensurepip: passes.Install check:
make altinstallfollowed bypython2.7 -m ensurepip --default-pipinstalls pip 20.3.4.post3 and setuptools 44.1.1.post3 without a deprecation warning. On that install,urllib2HTTPS to pypi.org andpip download sixboth pass through the new verify callback. Building a setuptools egg and sdist works, andensurepip._uninstallworks.Full suite (
regrtest -j6 -w), compared against the pristine 2.7.18.14 build: no new failures. Both builds fail the same 4 tests: test_ftplib, test_socket, test_ssl and test_urllib2_localnet. These come from the local environment and OpenSSL 3.0. The base build also hit a flaky test_threading_local.Linux, against production OpenSSL 1.1.1w (the as-builds
openssl/1.11.0.23Linux artifact). I built the new and base trees the same way and ranregrtest -j6 -w -uall,-largefile,-cpu.test_distrusted_spki. It fails on 2.7.18.14 and passes on 2.7.18.15.NetworkedTests.test_context_setget, needs the network, and the test_ftplib failures are about the local source address. Both happen in this environment regardless of the change.Windows x64: not built here, because there's no MSVC on this box. It needs a build against the as-builds 1.1.1w Windows artifact.
After merge: tag
2.7.18.15(unprefixed, like2.7.18.14) on the merge commit, add the platform inventory version, and add CVE-scanner override rows.Update (ff19c1d): after the pip 20.3.4.post4 release, I re-tested on Linux against OpenSSL 1.1.1w. test_ensurepip passes. After
make altinstall,python2.7 -m ensurepip --default-pipinstalls pip 20.3.4.post4 and setuptools 44.1.1.post3. The vendored certifi reports 2026.10.23.post1, with emSign Root CA - G1 as the only emSign root, andpip download sixworks. The bundled wheel is byte-identical to the release asset.