Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python310 for openSUSE:Factory 
checked in at 2026-11-09 17:45:30
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python310 (Old)
 and      /work/SRC/openSUSE:Factory/.python310.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python310"

Mon Nov  9 17:45:30 2026 rev:86 rq:1396358 version:3.10.22

Changes:
--------
--- /work/SRC/openSUSE:Factory/python310/python310.changes      2026-10-22 
15:54:22.599365740 +0200
+++ /work/SRC/openSUSE:Factory/.python310.new.3759290/python310.changes 
2026-11-09 17:45:34.026426245 +0100
@@ -1,0 +2,7 @@
+Fri Nov  6 19:38:08 UTC 2026 - Matej Cepl <[email protected]>
+
+- CVE-2026-115557: Distrust compromised eMudhra
+  intermediate CA keys (bsc#1287357)
+   CVE-2026-115557-distrust-emudhra-ca-keys.patch
+
+-------------------------------------------------------------------

New:
----
  CVE-2026-115557-distrust-emudhra-ca-keys.patch

----------(New B)----------
  New:  intermediate CA keys (bsc#1287357)
   CVE-2026-115557-distrust-emudhra-ca-keys.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python310.spec ++++++
--- /var/tmp/diff_new_pack.40TU0c/_old  2026-11-09 17:45:35.827501323 +0100
+++ /var/tmp/diff_new_pack.40TU0c/_new  2026-11-09 17:45:35.832501531 +0100
@@ -230,6 +230,9 @@
 # PATCH-FIX-UPSTREAM bsc1263083-http-cookies-atob-utf8.patch bsc#1263083 
[email protected]
 # Use decodeURIComponent() for UTF-8 support in js_output()
 Patch63:        bsc1263083-http-cookies-atob-utf8.patch
+# PATCH-FIX-UPSTREAM CVE-2026-115557-distrust-emudhra-ca-keys.patch 
bsc#1287357 Matej Cepl <[email protected]>
+# Distrust compromised eMudhra intermediate CA keys
+Patch64:        CVE-2026-115557-distrust-emudhra-ca-keys.patch
 ### END OF PATCHES                                                             
 
 BuildRequires:  autoconf-archive
 BuildRequires:  automake
@@ -550,6 +553,9 @@
 # Don't fail on warnings when building documentation
 sed -i -e '/^SPHINXERRORHANDLING/s/-W//' Doc/Makefile
 
+# Prepare testing zip archive from the patched version (`base64` is part of 
coreutils)
+base64 -d Lib/test/distrusted.zip.b64 >Lib/test/distrusted.zip
+
 %build
 %if %{with doc}
 TODAY_DATE=`date -r %{SOURCE0} "+%%B %%d, %%Y"`

++++++ CVE-2026-115557-distrust-emudhra-ca-keys.patch ++++++
Index: Python-3.10.22/Lib/test/distrusted.zip.b64
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ Python-3.10.22/Lib/test/distrusted.zip.b64  2026-11-06 20:40:03.092785125 
+0100
@@ -0,0 +1,89 @@
+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
+LRGEGqnrM5dKHV27nUAUbU7aBbjeiUznr9p8UyYWnaolSEsH1dpbLEzzfWeMW5NDH/6Xn5ZR8BvY
+GqnJ+1n1GSUien+mR5wuikLk4OI+z49mehRlBLjGY1spJGW6K3o42ouRTUlaDMFwVwVGgp1fIjqA
+xP7v4xuiMtMehjyAdN9QcN3oL956twPuB6lNlGmndhr7jtlf8WqAtHVHgdgYbOhi0Ams2VL9UEsH
+CKv9ww4lAgAAKgMAAFBLAQIeAwoAAAAAABpjVl0AAAAAAAAAAAAAAAALAAAAAAAAAAAAEADtQQAA
+AABkaXN0cnVzdGVkL1BLAQIeAxQACQAIABpjVl2GodrkzAsAAHUVAAAhAAAAAAAAAAEAAACkgSkA
+AABkaXN0cnVzdGVkL2Rpc3RydXN0ZWRfa2V5Y2VydC5wZW1QSwECHgMUAAkACAAaY1ZdoZp39n8D
+AADZBAAAJgAAAAAAAAABAAAApIFEDAAAZGlzdHJ1c3RlZC9kaXN0cnVzdGVkX2ludGVybWVkaWF0
+ZS5wZW1QSwECHgMUAAkACAAaY1Zdq/3DDiUCAAAqAwAAHgAAAAAAAAABAAAApIEXEAAAZGlzdHJ1
+c3RlZC9kaXN0cnVzdGVkX3Jvb3QucGVtUEsFBgAAAAAEAAQAKAEAAIgSAAAAAA==
Index: Python-3.10.22/Lib/test/test_ssl.py
===================================================================
--- Python-3.10.22.orig/Lib/test/test_ssl.py    2026-10-01 02:48:01.000000000 
+0200
+++ Python-3.10.22/Lib/test/test_ssl.py 2026-11-06 20:40:03.093368174 +0100
@@ -9,12 +9,15 @@
 from test.support import socket_helper
 from test.support import threading_helper
 from test.support import warnings_helper
+import base64
 import contextlib
 import re
 import socket
 import select
 import struct
 import time
+import tempfile
+import zipfile
 import datetime
 import gc
 import http.client
@@ -131,6 +134,7 @@
 SIGNED_CERTFILE2_HOSTNAME = 'fakehostname'
 SIGNED_CERTFILE_ECC = data_file("keycertecc.pem")
 SIGNED_CERTFILE_ECC_HOSTNAME = 'localhost-ecc'
+DISTRUSTED_SPKI_ZIP_B64 = data_file("distrusted.zip.b64")
 
 # Same certificate as pycacert.pem, but without extra text in file
 SIGNING_CA = data_file("capath", "ceff1710.0")
@@ -2103,6 +2107,26 @@
         c_in.write(s_out.read())
         client.unwrap()
 
+    def test_distrusted_spki(self):
+        client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+        server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+        with tempfile.TemporaryDirectory() as tmpdir:
+            zip_path = os.path.join(tmpdir, "distrusted.zip")
+            with open(DISTRUSTED_SPKI_ZIP_B64, "rb") as f:
+                with open(zip_path, "wb") as zf:
+                    zf.write(base64.b64decode(f.read()))
+            with zipfile.ZipFile(zip_path) as z:
+                z.extractall(tmpdir, pwd=b"Brave Sir Robin")
+            certdir = os.path.join(tmpdir, "distrusted")
+            client_context.load_verify_locations(
+                os.path.join(certdir, "distrusted_root.pem"))
+            server_context.load_cert_chain(
+                os.path.join(certdir, "distrusted_keycert.pem"))
+
+        with self.assertRaises(ssl.SSLCertVerificationError) as cm:
+            connected_bio_pair(client_context, server_context, "localhost")
+        self.assertEqual(cm.exception.verify_code, 28)
+
     def test_sni_callback_context_released_and_callback_raises(self):
         # Variant of the test below without a HelloRetryRequest: the callback
         # switches the connection to another context, drops the last
Index: 
Python-3.10.22/Misc/NEWS.d/next/Security/2026-10-21-16-55-07.gh-issue-160040.9PjFjC.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.10.22/Misc/NEWS.d/next/Security/2026-10-21-16-55-07.gh-issue-160040.9PjFjC.rst
     2026-11-06 20:40:03.093758700 +0100
@@ -0,0 +1,12 @@
+.. cve: CVE-2026-115557
+
+:mod:`ssl` now rejects TLS certificate chains that contain any of a set of
+compromised eMudhra intermediate CA keys.  This is an extraordinary measure and
+a last line of defense: removing trust is primarily the job of the platform 
root
+store, and the built-in denylist only blocks connections while that update has
+not yet reached the OS, a container image, or a bundled trust store in various
+Python packages.  Entries are added to the denylist only under a very high
+threshold.
+The denylist matches on the public key (the SHA-256 digest of the :rfc:`5280`
+``SubjectPublicKeyInfo``), so it also covers the cross-signed certificates that
+reuse one of the distrusted keys.
Index: Python-3.10.22/Modules/_ssl.c
===================================================================
--- Python-3.10.22.orig/Modules/_ssl.c  2026-10-01 02:48:01.000000000 +0200
+++ Python-3.10.22/Modules/_ssl.c       2026-11-06 20:40:03.094162180 +0100
@@ -54,6 +54,7 @@
 /* Include OpenSSL header files */
 #include "openssl/rsa.h"
 #include "openssl/crypto.h"
+#include "openssl/evp.h"
 #include "openssl/x509.h"
 #include "openssl/x509v3.h"
 #include "openssl/pem.h"
@@ -880,6 +881,7 @@
                 int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
                 verify_cb = SSL_get_verify_callback(self->ssl);
                 mode |= SSL_VERIFY_POST_HANDSHAKE;
+                /* Keep ctx verify callback (distrusted SPKI check) */
                 SSL_set_verify(self->ssl, mode, verify_cb);
             }
         } else {
@@ -2985,11 +2987,70 @@
  * _SSLContext objects
  */
 
+#include "_ssl/distrusted_spki.h"
+
+/* Returns 1 if the SHA-256 of the cert's SPKI is on the distrusted list,
+   0 if not (okay), -1 on an internal error. */
+static int
+_cert_has_distrusted_spki(X509 *cert)
+{
+    unsigned char *spki = NULL;
+    int spki_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &spki);
+    if (spki_len <= 0 || spki == NULL) {
+        return -1;
+    }
+
+    unsigned char digest[EVP_MAX_MD_SIZE];
+    unsigned int digest_len = 0;
+    int ok = EVP_Digest(spki, (size_t)spki_len, digest, &digest_len,
+                        EVP_sha256(), NULL);
+    OPENSSL_free(spki);
+    assert(sizeof(_distrusted_spki[0]) == SHA256_DIGEST_LENGTH);
+    if (!ok
+        || digest_len != SHA256_DIGEST_LENGTH
+        || sizeof(_distrusted_spki[0]) != SHA256_DIGEST_LENGTH) {
+        return -1;
+    }
+
+    for (size_t i = 0; i < Py_ARRAY_LENGTH(_distrusted_spki); i++) {
+        if (memcmp(digest, _distrusted_spki[i], SHA256_DIGEST_LENGTH) == 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* Verify callback, called once per cert. Reject distrusted SPKIs. */
+static int
+_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+    /* Don't override an error OpenSSL already reported for this certificate. 
*/
+    if (!preverify_ok) {
+        return preverify_ok;
+    }
+    X509 *cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+    if (cert == NULL) {
+        return preverify_ok;
+    }
+    switch (_cert_has_distrusted_spki(cert)) {
+    case 0:
+        /* okay: not on the distrusted list */
+        return preverify_ok;
+    case 1:
+        /* rejected: public key is on the distrusted list */
+        X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REJECTED);
+        return 0;
+    default:
+        /* internal error */
+        X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_UNSPECIFIED);
+        return 0;
+    }
+}
+
 static int
 _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n)
 {
     int mode;
-    int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
 
     switch(n) {
     case PY_SSL_CERT_NONE:
@@ -3010,9 +3071,7 @@
     /* 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);
+    SSL_CTX_set_verify(self->ctx, mode, _ssl_verify_callback);
     return 0;
 }
 
Index: Python-3.10.22/Modules/_ssl/distrusted_spki.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ Python-3.10.22/Modules/_ssl/distrusted_spki.h       2026-11-06 
20:40:03.094488144 +0100
@@ -0,0 +1,75 @@
+/* Denylist of distrusted SubjectPublicKeyInfo (SPKI).
+ *
+ * Verification rejects any chain with a cert whose SPKI SHA-256 digest is in
+ * the _distrusted_spki table.  We match the key, not a cert fingerprint, so
+ * cross-signs that reuse the key are blocked too.
+ *
+ * This is an extraordinary measure and last line of defense.  Removing trust 
is
+ * primarily the job of the platform root store. This list only blocks keys 
while
+ * that update is still propagating to the OS, container images, or root CAs
+ * bundled in Python packages.  The threshold for adding an entry is very high.
+  *
+ * See https://github.com/python/cpython/issues/160040 for the eMudhra 
discussion.
+ *
+ * To block another key, add the SHA-256 digest bytes of its SPKI:
+ *
+ *     $ openssl x509 -in cert.pem -noout -pubkey | openssl pkey -pubin 
-outform DER | openssl dgst -sha256 -binary | xxd -i
+ */
+#ifndef Py_SSL_DISTRUSTED_SPKI_H
+#define Py_SSL_DISTRUSTED_SPKI_H
+
+#include "openssl/sha.h"
+
+static const unsigned char _distrusted_spki[][SHA256_DIGEST_LENGTH] = {
+    /* Compromised eMudhra intermediates, 
https://emudhra.we-have-a-problem.com/ */
+    /* CN=EM OV TLS CA - G3B,O=eMudhra BV,C=NL */
+    {
+        0x43, 0xbf, 0xfa, 0x19, 0xd6, 0xef, 0x65, 0x14, 0x51, 0xf6, 0x84, 0x1e,
+        0x01, 0x99, 0x31, 0xe9, 0x6f, 0x6f, 0x2e, 0xae, 0xff, 0x28, 0xab, 0x4b,
+        0x6f, 0x1c, 0xe4, 0x57, 0x6d, 0xe2, 0x71, 0x7b,
+    },
+    /* CN=EM DV TLS CA - G3C,O=eMudhra Inc,C=US */
+    {
+        0x49, 0xdd, 0x35, 0x3d, 0xc2, 0xb9, 0x19, 0xa3, 0x77, 0x06, 0x45, 0x11,
+        0x7d, 0x0e, 0x60, 0xc1, 0x61, 0x15, 0xe7, 0xd1, 0xbc, 0x25, 0x9e, 0xef,
+        0xe7, 0x34, 0xb0, 0x0e, 0xa4, 0x34, 0xfe, 0x5f,
+    },
+    /* CN=EM EV TLS CA - G3C,O=eMudhra Inc,C=US */
+    {
+        0x5c, 0x06, 0x60, 0xa2, 0xb1, 0x64, 0x91, 0x0d, 0x8f, 0xf5, 0x89, 0xc1,
+        0x8e, 0x14, 0x55, 0x2e, 0x9e, 0x0e, 0xc2, 0x37, 0xdc, 0xef, 0xe2, 0xcb,
+        0x73, 0x6d, 0x41, 0xa4, 0x81, 0x1f, 0x81, 0xa0,
+    },
+    /* CN=EM OV TLS CA - G3C,O=eMudhra Inc,C=US */
+    {
+        0x63, 0xec, 0xef, 0xee, 0x43, 0x1d, 0x49, 0x52, 0x3b, 0xc1, 0x13, 0x6c,
+        0x4e, 0x72, 0xd0, 0x20, 0x6a, 0x47, 0x70, 0x86, 0xca, 0xf6, 0x06, 0xcd,
+        0x5c, 0xf0, 0x25, 0x68, 0x6c, 0x10, 0x9c, 0xf8,
+    },
+    /* CN=EM OV CS CA - G3C,O=eMudhra Inc,C=US */
+    {
+        0x92, 0xc0, 0x63, 0xd6, 0x7e, 0x8f, 0x14, 0xdb, 0x71, 0xd2, 0x88, 0xe1,
+        0x12, 0x66, 0xff, 0xda, 0xf6, 0x9c, 0xbf, 0x11, 0x5f, 0x7b, 0xab, 0x14,
+        0xf3, 0xbc, 0x2f, 0x68, 0xd2, 0x5e, 0x0e, 0x30,
+    },
+    /* CN=EM EV TLS CA - G3B,O=eMudhra BV,C=NL */
+    {
+        0xe0, 0x25, 0x4c, 0x7f, 0xcd, 0xc2, 0x65, 0xe2, 0xf0, 0xf8, 0xa4, 0x28,
+        0xda, 0xbb, 0x75, 0xab, 0x4a, 0xe0, 0x39, 0xb4, 0x87, 0x2d, 0x62, 0xf5,
+        0xee, 0x57, 0xf0, 0x87, 0x7d, 0x5a, 0x8a, 0x41,
+    },
+    /* CN=EM DV TLS CA - G3B,O=eMudhra BV,C=NL */
+    {
+        0xeb, 0xed, 0x64, 0x75, 0xc2, 0xc2, 0xd8, 0x0f, 0xcb, 0x19, 0x9f, 0x3f,
+        0xe8, 0xdd, 0x6e, 0x6b, 0x3c, 0xe6, 0x0b, 0x77, 0xeb, 0x15, 0xd4, 0xe4,
+        0x2c, 0x3d, 0xec, 0x0c, 0x6f, 0x5b, 0x51, 0x05,
+    },
+    /* CN=EM OV CS CA - G3B,O=eMudhra BV,C=NL */
+    {
+        0xec, 0x4f, 0xfa, 0x1a, 0xd0, 0xac, 0x88, 0x08, 0x13, 0x14, 0xb7, 0xaa,
+        0xff, 0xef, 0x6a, 0x91, 0xf9, 0x7c, 0xd5, 0x72, 0x91, 0xbb, 0x5b, 0xa6,
+        0x86, 0xc5, 0x41, 0x5f, 0x5e, 0xb4, 0xf6, 0xb6,
+    },
+};
+
+#endif /* !Py_SSL_DISTRUSTED_SPKI_H */

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.40TU0c/_old  2026-11-09 17:45:36.035509994 +0100
+++ /var/tmp/diff_new_pack.40TU0c/_new  2026-11-09 17:45:36.041510244 +0100
@@ -1,6 +1,6 @@
-mtime: 1792174715
-commit: a7ee42b1813f51faee6a656ff7a10dd201bb468219fa308d9741430d54f8844f
+mtime: 1794237305
+commit: fde74c86307c5936d62ae926ec7306121f0156bb8b65965c2688f89a8231b7dd
 url: https://src.opensuse.org/python-interpreters/python310
-revision: a7ee42b1813f51faee6a656ff7a10dd201bb468219fa308d9741430d54f8844f
+revision: fde74c86307c5936d62ae926ec7306121f0156bb8b65965c2688f89a8231b7dd
 projectscmsync: https://src.opensuse.org/python-interpreters/_ObsPrj
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-11-09 16:15:05.000000000 +0100
@@ -0,0 +1,7 @@
+.osc
+*.obscpio
+*.osc
+_build.*
+.pbuild
+*.rej
+python310*-build/

Reply via email to