diff options
Diffstat (limited to 'meta')
32 files changed, 2306 insertions, 21 deletions
diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 713a7fc651..ca0416d1c7 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass | |||
| @@ -26,6 +26,7 @@ SPDX_TOOL_VERSION ??= "1.0" | |||
| 26 | SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" | 26 | SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" |
| 27 | 27 | ||
| 28 | SPDX_INCLUDE_SOURCES ??= "0" | 28 | SPDX_INCLUDE_SOURCES ??= "0" |
| 29 | SPDX_INCLUDE_COMPILED_SOURCES ??= "0" | ||
| 29 | 30 | ||
| 30 | SPDX_UUID_NAMESPACE ??= "sbom.openembedded.org" | 31 | SPDX_UUID_NAMESPACE ??= "sbom.openembedded.org" |
| 31 | SPDX_NAMESPACE_PREFIX ??= "http://spdx.org/spdxdocs" | 32 | SPDX_NAMESPACE_PREFIX ??= "http://spdx.org/spdxdocs" |
| @@ -40,6 +41,8 @@ SPDX_MULTILIB_SSTATE_ARCHS ??= "${SSTATE_ARCHS}" | |||
| 40 | python () { | 41 | python () { |
| 41 | from oe.cve_check import extend_cve_status | 42 | from oe.cve_check import extend_cve_status |
| 42 | extend_cve_status(d) | 43 | extend_cve_status(d) |
| 44 | if d.getVar("SPDX_INCLUDE_COMPILED_SOURCES") == "1": | ||
| 45 | d.setVar("SPDX_INCLUDE_SOURCES", "1") | ||
| 43 | } | 46 | } |
| 44 | 47 | ||
| 45 | def create_spdx_source_deps(d): | 48 | def create_spdx_source_deps(d): |
diff --git a/meta/lib/oe/lsb.py b/meta/lib/oe/lsb.py index 3ec03e5042..1fc3b968a0 100644 --- a/meta/lib/oe/lsb.py +++ b/meta/lib/oe/lsb.py | |||
| @@ -16,7 +16,7 @@ def get_os_release(): | |||
| 16 | key, val = line.rstrip().split('=', 1) | 16 | key, val = line.rstrip().split('=', 1) |
| 17 | except ValueError: | 17 | except ValueError: |
| 18 | continue | 18 | continue |
| 19 | data[key.strip()] = val.strip('"') | 19 | data[key.strip()] = val.strip('"\'') |
| 20 | return data | 20 | return data |
| 21 | 21 | ||
| 22 | def release_dict_osr(): | 22 | def release_dict_osr(): |
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index a8970dcca0..9c422d1757 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py | |||
| @@ -145,6 +145,8 @@ def add_package_files( | |||
| 145 | ignore_dirs=[], | 145 | ignore_dirs=[], |
| 146 | ignore_top_level_dirs=[], | 146 | ignore_top_level_dirs=[], |
| 147 | ): | 147 | ): |
| 148 | import oe.spdx | ||
| 149 | |||
| 148 | source_date_epoch = d.getVar("SOURCE_DATE_EPOCH") | 150 | source_date_epoch = d.getVar("SOURCE_DATE_EPOCH") |
| 149 | if source_date_epoch: | 151 | if source_date_epoch: |
| 150 | source_date_epoch = int(source_date_epoch) | 152 | source_date_epoch = int(source_date_epoch) |
| @@ -156,6 +158,11 @@ def add_package_files( | |||
| 156 | bb.note(f"Skip {topdir}") | 158 | bb.note(f"Skip {topdir}") |
| 157 | return spdx_files | 159 | return spdx_files |
| 158 | 160 | ||
| 161 | check_compiled_sources = d.getVar("SPDX_INCLUDE_COMPILED_SOURCES") == "1" | ||
| 162 | if check_compiled_sources: | ||
| 163 | compiled_sources, types = oe.spdx.get_compiled_sources(d) | ||
| 164 | bb.debug(1, f"Total compiled files: {len(compiled_sources)}") | ||
| 165 | |||
| 159 | for subdir, dirs, files in os.walk(topdir, onerror=walk_error): | 166 | for subdir, dirs, files in os.walk(topdir, onerror=walk_error): |
| 160 | dirs[:] = [d for d in dirs if d not in ignore_dirs] | 167 | dirs[:] = [d for d in dirs if d not in ignore_dirs] |
| 161 | if subdir == str(topdir): | 168 | if subdir == str(topdir): |
| @@ -171,6 +178,11 @@ def add_package_files( | |||
| 171 | filename = str(filepath.relative_to(topdir)) | 178 | filename = str(filepath.relative_to(topdir)) |
| 172 | file_purposes = get_purposes(filepath) | 179 | file_purposes = get_purposes(filepath) |
| 173 | 180 | ||
| 181 | # Check if file is compiled | ||
| 182 | if check_compiled_sources: | ||
| 183 | if not oe.spdx.is_compiled_source(filename, compiled_sources, types): | ||
| 184 | continue | ||
| 185 | |||
| 174 | spdx_file = objset.new_file( | 186 | spdx_file = objset.new_file( |
| 175 | get_spdxid(file_counter), | 187 | get_spdxid(file_counter), |
| 176 | filename, | 188 | filename, |
diff --git a/meta/recipes-connectivity/inetutils/inetutils/CVE-2026-28372.patch b/meta/recipes-connectivity/inetutils/inetutils/CVE-2026-28372.patch new file mode 100644 index 0000000000..4e6bf0c87c --- /dev/null +++ b/meta/recipes-connectivity/inetutils/inetutils/CVE-2026-28372.patch | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | From 4db2f19f4caac03c7f4da6363c140bd70df31386 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Erik Auerswald <auerswal@unix-ag.uni-kl.de> | ||
| 3 | Date: Sun, 15 Feb 2026 15:38:50 +0100 | ||
| 4 | Subject: [PATCH] telnetd: don't allow systemd service credentials | ||
| 5 | |||
| 6 | The login(1) implementation of util-linux added support for | ||
| 7 | systemd service credentials in release 2.40. This allows to | ||
| 8 | bypass authentication by specifying a directory name in the | ||
| 9 | environment variable CREDENTIALS_DIRECTORY. If this directory | ||
| 10 | contains a file named 'login.noauth' with the content of 'yes', | ||
| 11 | login(1) skips authentication. | ||
| 12 | |||
| 13 | GNU Inetutils telnetd supports to set arbitrary environment | ||
| 14 | variables using the 'Environment' and 'New Environment' | ||
| 15 | Telnet options. This allows specifying a directory containing | ||
| 16 | 'login.noauth'. A local user can create such a directory | ||
| 17 | and file, and, e.g., specify the user name 'root' to escalate | ||
| 18 | privileges. | ||
| 19 | |||
| 20 | This problem was reported by Ron Ben Yizhak in | ||
| 21 | <https://lists.gnu.org/archive/html/bug-inetutils/2026-02/msg00000.html>. | ||
| 22 | |||
| 23 | This commit clears CREDENTIALS_DIRECTORY from the environment | ||
| 24 | before executing login(1) to implement a simple fix that can | ||
| 25 | be backported easily. | ||
| 26 | |||
| 27 | * NEWS.md: Mention fix. | ||
| 28 | * THANKS: Mention Ron Ben Yizhak. | ||
| 29 | * telnetd/pty.c: Clear CREDENTIALS_DIRECTORY from the environment | ||
| 30 | before executing 'login'. | ||
| 31 | |||
| 32 | CVE: CVE-2026-28372 | ||
| 33 | Upstream-Status: Backport [https://cgit.git.savannah.gnu.org/cgit/inetutils.git/commit/?id=4db2f19f4caac03c7f4da6363c140bd70df31386] | ||
| 34 | Signed-off-by: Peter Marko <peter.marko@siemens.com> | ||
| 35 | --- | ||
| 36 | NEWS | 5 +++++ | ||
| 37 | THANKS | 1 + | ||
| 38 | telnetd/pty.c | 8 ++++++++ | ||
| 39 | 3 files changed, 14 insertions(+) | ||
| 40 | |||
| 41 | diff --git a/NEWS b/NEWS | ||
| 42 | index 877ca53b..f5172a71 100644 | ||
| 43 | --- a/NEWS | ||
| 44 | +++ b/NEWS | ||
| 45 | @@ -1,5 +1,10 @@ | ||
| 46 | GNU inetutils NEWS -- history of user-visible changes. | ||
| 47 | |||
| 48 | +** Prevent privilege escalation via telnetd abusing systemd service | ||
| 49 | +credentials support added to the login(1) implementation of util-linux | ||
| 50 | +in release 2.40. Reported by Ron Ben Yizhak in | ||
| 51 | +<https://lists.gnu.org/archive/html/bug-inetutils/2026-02/msg00000.html>. | ||
| 52 | + | ||
| 53 | * Noteworthy changes in release 2.5 (2023-12-29) [stable] | ||
| 54 | |||
| 55 | ** ftpd, rcp, rlogin, rsh, rshd, uucpd | ||
| 56 | diff --git a/THANKS b/THANKS | ||
| 57 | index 8d1d3dbb..ef5f6063 100644 | ||
| 58 | --- a/THANKS | ||
| 59 | +++ b/THANKS | ||
| 60 | @@ -9,6 +9,7 @@ In particular: | ||
| 61 | NIIBE Yutaka (Security fixes & making talk finally work) | ||
| 62 | Nathan Neulinger (tftpd) | ||
| 63 | Thomas Bushnell (sockaddr sin_len field) | ||
| 64 | + Ron Ben Yizhak (reported privilege escalation via telnetd) | ||
| 65 | |||
| 66 | Please see version control logs and ChangeLog.? for full credits. | ||
| 67 | |||
| 68 | diff --git a/telnetd/pty.c b/telnetd/pty.c | ||
| 69 | index c727e7be..f3518049 100644 | ||
| 70 | --- a/telnetd/pty.c | ||
| 71 | +++ b/telnetd/pty.c | ||
| 72 | @@ -130,6 +130,14 @@ start_login (char *host, int autologin, char *name) | ||
| 73 | if (!cmd) | ||
| 74 | fatal (net, "can't expand login command line"); | ||
| 75 | argcv_get (cmd, "", &argc, &argv); | ||
| 76 | + | ||
| 77 | + /* util-linux's "login" introduced an authentication bypass method | ||
| 78 | + * via environment variable "CREDENTIALS_DIRECTORY" in version 2.40. | ||
| 79 | + * Clear it from the environment before executing "login" to prevent | ||
| 80 | + * abuse via Telnet. | ||
| 81 | + */ | ||
| 82 | + unsetenv ("CREDENTIALS_DIRECTORY"); | ||
| 83 | + | ||
| 84 | execv (argv[0], argv); | ||
| 85 | syslog (LOG_ERR, "%s: %m\n", cmd); | ||
| 86 | fatalperror (net, cmd); | ||
diff --git a/meta/recipes-connectivity/inetutils/inetutils/CVE-2026-32746.patch b/meta/recipes-connectivity/inetutils/inetutils/CVE-2026-32746.patch new file mode 100644 index 0000000000..0e55f3f0a4 --- /dev/null +++ b/meta/recipes-connectivity/inetutils/inetutils/CVE-2026-32746.patch | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | From 6864598a29b652a6b69a958f5cd1318aa2b258af Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Collin Funk <collin.funk1@gmail.com> | ||
| 3 | Date: Wed, 11 Mar 2026 23:06:46 -0700 | ||
| 4 | Subject: [PATCH] telnetd: fix stack buffer overflow processing SLC suboption triplets | ||
| 5 | |||
| 6 | Previously a client could write past the end of an internal buffer using | ||
| 7 | an SLC suboption with many triplets using function octets greater than | ||
| 8 | 18, possibly leading to remote code execution. Reported by Adiel Sol, | ||
| 9 | Arad Inbar, Erez Cohen, Nir Somech, Ben Grinberg, Daniel Lubel at DREAM | ||
| 10 | Security Research Team at: | ||
| 11 | <https://lists.gnu.org/r/bug-inetutils/2026-03/msg00031.html>. | ||
| 12 | |||
| 13 | * telnetd/slc.c (add_slc): Return early if writing the tuple would lead | ||
| 14 | us to writing past the end of the buffer. | ||
| 15 | * NEWS.md: Mention the fix. | ||
| 16 | |||
| 17 | Upstream-Status: Backport [https://cgit.git.savannah.gnu.org/cgit/inetutils.git/commit/?id=6864598a29b652a6b69a958f5cd1318aa2b258af] | ||
| 18 | CVE: CVE-2026-32746 | ||
| 19 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 20 | --- | ||
| 21 | telnetd/slc.c | 3 +++ | ||
| 22 | 1 file changed, 3 insertions(+) | ||
| 23 | |||
| 24 | diff --git a/telnetd/slc.c b/telnetd/slc.c | ||
| 25 | index b3cc117..9d6bad1 100644 | ||
| 26 | --- a/telnetd/slc.c | ||
| 27 | +++ b/telnetd/slc.c | ||
| 28 | @@ -162,6 +162,9 @@ get_slc_defaults (void) | ||
| 29 | void | ||
| 30 | add_slc (register char func, register char flag, register cc_t val) | ||
| 31 | { | ||
| 32 | + /* Do nothing if the entire triplet cannot fit in the buffer. */ | ||
| 33 | + if (slcbuf + sizeof slcbuf - slcptr <= 6) | ||
| 34 | + return; | ||
| 35 | |||
| 36 | if ((*slcptr++ = (unsigned char) func) == 0xff) | ||
| 37 | *slcptr++ = 0xff; | ||
| 38 | -- | ||
| 39 | 2.43.0 | ||
| 40 | |||
diff --git a/meta/recipes-connectivity/inetutils/inetutils_2.5.bb b/meta/recipes-connectivity/inetutils/inetutils_2.5.bb index 486878022f..29ff62379d 100644 --- a/meta/recipes-connectivity/inetutils/inetutils_2.5.bb +++ b/meta/recipes-connectivity/inetutils/inetutils_2.5.bb | |||
| @@ -20,6 +20,8 @@ SRC_URI = "${GNU_MIRROR}/inetutils/inetutils-${PV}.tar.xz \ | |||
| 20 | file://tftpd.xinetd.inetutils \ | 20 | file://tftpd.xinetd.inetutils \ |
| 21 | file://CVE-2026-24061-1.patch \ | 21 | file://CVE-2026-24061-1.patch \ |
| 22 | file://CVE-2026-24061-2.patch \ | 22 | file://CVE-2026-24061-2.patch \ |
| 23 | file://CVE-2026-28372.patch \ | ||
| 24 | file://CVE-2026-32746.patch \ | ||
| 23 | " | 25 | " |
| 24 | 26 | ||
| 25 | inherit autotools gettext update-alternatives texinfo | 27 | inherit autotools gettext update-alternatives texinfo |
diff --git a/meta/recipes-core/images/build-appliance-image_15.0.0.bb b/meta/recipes-core/images/build-appliance-image_15.0.0.bb index c970020b02..85521b6026 100644 --- a/meta/recipes-core/images/build-appliance-image_15.0.0.bb +++ b/meta/recipes-core/images/build-appliance-image_15.0.0.bb | |||
| @@ -26,7 +26,7 @@ inherit core-image setuptools3 features_check | |||
| 26 | 26 | ||
| 27 | REQUIRED_DISTRO_FEATURES += "xattr" | 27 | REQUIRED_DISTRO_FEATURES += "xattr" |
| 28 | 28 | ||
| 29 | SRCREV ?= "06210079b2e10d6d3fb943afe87864267e329821" | 29 | SRCREV ?= "f4877d8e682ed22e339fe6c07f3ffa28e50c7b98" |
| 30 | SRC_URI = "git://git.yoctoproject.org/poky;branch=scarthgap \ | 30 | SRC_URI = "git://git.yoctoproject.org/poky;branch=scarthgap \ |
| 31 | file://Yocto_Build_Appliance.vmx \ | 31 | file://Yocto_Build_Appliance.vmx \ |
| 32 | file://Yocto_Build_Appliance.vmxf \ | 32 | file://Yocto_Build_Appliance.vmxf \ |
diff --git a/meta/recipes-core/systemd/systemd-systemctl/systemctl b/meta/recipes-core/systemd/systemd-systemctl/systemctl index 2229bc7b6d..b9e04a9070 100755 --- a/meta/recipes-core/systemd/systemd-systemctl/systemctl +++ b/meta/recipes-core/systemd/systemd-systemctl/systemctl | |||
| @@ -202,7 +202,8 @@ class SystemdUnit(): | |||
| 202 | try: | 202 | try: |
| 203 | for dependent in config.get('Install', prop): | 203 | for dependent in config.get('Install', prop): |
| 204 | # expand any %i to instance (ignoring escape sequence %%) | 204 | # expand any %i to instance (ignoring escape sequence %%) |
| 205 | dependent = re.sub("([^%](%%)*)%i", "\\g<1>{}".format(instance), dependent) | 205 | if instance is not None: |
| 206 | dependent = re.sub("([^%](%%)*)%i", "\\g<1>{}".format(re.escape(instance)), dependent) | ||
| 206 | wants = systemdir / "{}.{}".format(dependent, dirstem) / service | 207 | wants = systemdir / "{}.{}".format(dependent, dirstem) / service |
| 207 | add_link(wants, target) | 208 | add_link(wants, target) |
| 208 | 209 | ||
| @@ -212,13 +213,13 @@ class SystemdUnit(): | |||
| 212 | def enable(self, units_enabled=[]): | 213 | def enable(self, units_enabled=[]): |
| 213 | # if we're enabling an instance, first extract the actual instance | 214 | # if we're enabling an instance, first extract the actual instance |
| 214 | # then figure out what the template unit is | 215 | # then figure out what the template unit is |
| 215 | template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", self.unit) | 216 | template = re.match(r"[^@]+@(?P<instance>.*)\.", self.unit) |
| 216 | instance_unit_name = None | 217 | instance_unit_name = None |
| 217 | if template: | 218 | if template: |
| 218 | instance = template.group('instance') | 219 | instance = template.group('instance') |
| 219 | if instance != "": | 220 | if instance != "": |
| 220 | instance_unit_name = self.unit | 221 | instance_unit_name = self.unit |
| 221 | unit = re.sub(r"@[^\.]*\.", "@.", self.unit, 1) | 222 | unit = re.sub(r"@{}\.".format(re.escape(instance)), "@.", self.unit, 1) |
| 222 | else: | 223 | else: |
| 223 | instance = None | 224 | instance = None |
| 224 | unit = self.unit | 225 | unit = self.unit |
diff --git a/meta/recipes-devtools/go/go/CVE-2025-61726.patch b/meta/recipes-devtools/go/go/CVE-2025-61726.patch index ab053ff55c..bdd10bc933 100644 --- a/meta/recipes-devtools/go/go/CVE-2025-61726.patch +++ b/meta/recipes-devtools/go/go/CVE-2025-61726.patch | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | From 85050ca6146f3edb50ded0a352ab9edbd635effc Mon Sep 17 00:00:00 2001 | 1 | From bf06767a9ac737387eee77c7eedd67c65e853ac2 Mon Sep 17 00:00:00 2001 |
| 2 | From: Damien Neil <dneil@google.com> | 2 | From: Damien Neil <dneil@google.com> |
| 3 | Date: Mon, 3 Nov 2025 14:28:47 -0800 | 3 | Date: Mon, 3 Nov 2025 14:28:47 -0800 |
| 4 | Subject: [PATCH] [release-branch.go1.24] net/url: add urlmaxqueryparams | 4 | Subject: [PATCH] [release-branch.go1.24] net/url: add urlmaxqueryparams |
| @@ -36,6 +36,7 @@ Reviewed-by: Junyang Shao <shaojunyang@google.com> | |||
| 36 | TryBot-Bypass: Michael Pratt <mpratt@google.com> | 36 | TryBot-Bypass: Michael Pratt <mpratt@google.com> |
| 37 | (cherry picked from commit 85c794ddce26a092b0ea68d0fca79028b5069d5a) | 37 | (cherry picked from commit 85c794ddce26a092b0ea68d0fca79028b5069d5a) |
| 38 | Signed-off-by: Deepak Rathore <deeratho@cisco.com> | 38 | Signed-off-by: Deepak Rathore <deeratho@cisco.com> |
| 39 | Signed-off-by: Eduardo Ferreira <eduardo.barbosa@toradex.com> | ||
| 39 | --- | 40 | --- |
| 40 | doc/godebug.md | 7 +++++ | 41 | doc/godebug.md | 7 +++++ |
| 41 | src/internal/godebugs/table.go | 1 + | 42 | src/internal/godebugs/table.go | 1 + |
| @@ -45,7 +46,7 @@ Signed-off-by: Deepak Rathore <deeratho@cisco.com> | |||
| 45 | 5 files changed, 85 insertions(+) | 46 | 5 files changed, 85 insertions(+) |
| 46 | 47 | ||
| 47 | diff --git a/doc/godebug.md b/doc/godebug.md | 48 | diff --git a/doc/godebug.md b/doc/godebug.md |
| 48 | index ae4f0576b4..635597ea42 100644 | 49 | index ae4f057..635597e 100644 |
| 49 | --- a/doc/godebug.md | 50 | --- a/doc/godebug.md |
| 50 | +++ b/doc/godebug.md | 51 | +++ b/doc/godebug.md |
| 51 | @@ -126,6 +126,13 @@ for example, | 52 | @@ -126,6 +126,13 @@ for example, |
| @@ -63,19 +64,19 @@ index ae4f0576b4..635597ea42 100644 | |||
| 63 | to concerns around VCS injection attacks. This behavior can be renabled with the | 64 | to concerns around VCS injection attacks. This behavior can be renabled with the |
| 64 | setting `allowmultiplevcs=1`. | 65 | setting `allowmultiplevcs=1`. |
| 65 | diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go | 66 | diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go |
| 66 | index 33dcd81fc3..4ae043053c 100644 | 67 | index 33dcd81..7178df6 100644 |
| 67 | --- a/src/internal/godebugs/table.go | 68 | --- a/src/internal/godebugs/table.go |
| 68 | +++ b/src/internal/godebugs/table.go | 69 | +++ b/src/internal/godebugs/table.go |
| 69 | @@ -52,6 +52,7 @@ var All = []Info{ | 70 | @@ -51,6 +51,7 @@ var All = []Info{ |
| 71 | {Name: "tlsmaxrsasize", Package: "crypto/tls"}, | ||
| 70 | {Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"}, | 72 | {Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"}, |
| 71 | {Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"}, | 73 | {Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"}, |
| 72 | {Name: "x509sha1", Package: "crypto/x509"}, | ||
| 73 | + {Name: "urlmaxqueryparams", Package: "net/url", Changed: 24, Old: "0"}, | 74 | + {Name: "urlmaxqueryparams", Package: "net/url", Changed: 24, Old: "0"}, |
| 75 | {Name: "x509sha1", Package: "crypto/x509"}, | ||
| 74 | {Name: "x509usefallbackroots", Package: "crypto/x509"}, | 76 | {Name: "x509usefallbackroots", Package: "crypto/x509"}, |
| 75 | {Name: "x509usepolicies", Package: "crypto/x509"}, | 77 | {Name: "x509usepolicies", Package: "crypto/x509"}, |
| 76 | {Name: "zipinsecurepath", Package: "archive/zip"}, | ||
| 77 | diff --git a/src/net/url/url.go b/src/net/url/url.go | 78 | diff --git a/src/net/url/url.go b/src/net/url/url.go |
| 78 | index d2ae03232f..5219e3c130 100644 | 79 | index d2ae032..cdca468 100644 |
| 79 | --- a/src/net/url/url.go | 80 | --- a/src/net/url/url.go |
| 80 | +++ b/src/net/url/url.go | 81 | +++ b/src/net/url/url.go |
| 81 | @@ -13,6 +13,7 @@ package url | 82 | @@ -13,6 +13,7 @@ package url |
| @@ -118,7 +119,7 @@ index d2ae03232f..5219e3c130 100644 | |||
| 118 | var key string | 119 | var key string |
| 119 | key, query, _ = strings.Cut(query, "&") | 120 | key, query, _ = strings.Cut(query, "&") |
| 120 | diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go | 121 | diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go |
| 121 | index fef236e40a..b2f8bd95fc 100644 | 122 | index fef236e..b2f8bd9 100644 |
| 122 | --- a/src/net/url/url_test.go | 123 | --- a/src/net/url/url_test.go |
| 123 | +++ b/src/net/url/url_test.go | 124 | +++ b/src/net/url/url_test.go |
| 124 | @@ -1488,6 +1488,54 @@ func TestParseQuery(t *testing.T) { | 125 | @@ -1488,6 +1488,54 @@ func TestParseQuery(t *testing.T) { |
| @@ -177,7 +178,7 @@ index fef236e40a..b2f8bd95fc 100644 | |||
| 177 | url *URL | 178 | url *URL |
| 178 | out string | 179 | out string |
| 179 | diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go | 180 | diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go |
| 180 | index 517ec0e0a4..335f7873b3 100644 | 181 | index 517ec0e..88d6d8c 100644 |
| 181 | --- a/src/runtime/metrics/doc.go | 182 | --- a/src/runtime/metrics/doc.go |
| 182 | +++ b/src/runtime/metrics/doc.go | 183 | +++ b/src/runtime/metrics/doc.go |
| 183 | @@ -328,6 +328,11 @@ Below is the full list of supported metrics, ordered lexicographically. | 184 | @@ -328,6 +328,11 @@ Below is the full list of supported metrics, ordered lexicographically. |
| @@ -193,4 +194,4 @@ index 517ec0e0a4..335f7873b3 100644 | |||
| 193 | The number of non-default behaviors executed by the crypto/x509 | 194 | The number of non-default behaviors executed by the crypto/x509 |
| 194 | package due to a non-default GODEBUG=x509sha1=... setting. | 195 | package due to a non-default GODEBUG=x509sha1=... setting. |
| 195 | -- | 196 | -- |
| 196 | 2.35.6 | 197 | 2.34.1 |
diff --git a/meta/recipes-devtools/python/python3-cryptography/CVE-2026-26007.patch b/meta/recipes-devtools/python/python3-cryptography/CVE-2026-26007.patch new file mode 100644 index 0000000000..fb76bbfca3 --- /dev/null +++ b/meta/recipes-devtools/python/python3-cryptography/CVE-2026-26007.patch | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | From 42c914929b52eb16421a4ef1f7e09c8f9fdab7db Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Paul Kehrer <paul.l.kehrer@gmail.com> | ||
| 3 | Date: Wed, 18 Mar 2026 16:01:03 +0900 | ||
| 4 | Subject: [PATCH] EC check key on cofactor > 1 | ||
| 5 | |||
| 6 | An attacker could create a malicious public key that reveals portions of | ||
| 7 | your private key when using certain uncommon elliptic curves (binary | ||
| 8 | curves). This version now includes additional security checks to | ||
| 9 | prevent this attack. This issue only affects binary elliptic curves, | ||
| 10 | which are rarely used in real-world applications. Credit to **XlabAI | ||
| 11 | Team of Tencent Xuanwu Lab and Atuin Automated Vulnerability Discovery | ||
| 12 | Engine** for reporting the issue. **CVE-2026-26007** | ||
| 13 | |||
| 14 | This is a partial backport of upstream commit | ||
| 15 | 0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c, to only include what's | ||
| 16 | relevant for CVE-2026-26007. | ||
| 17 | |||
| 18 | CVE: CVE-2026-26007 | ||
| 19 | |||
| 20 | Origin: backport, https://github.com/pyca/cryptography/commit/0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c | ||
| 21 | Reference: https://salsa.debian.org/python-team/packages/python-cryptography/-/commit/464e7ca3b0b4493d5906d0c3685de71fda770c59 | ||
| 22 | |||
| 23 | Signed-off-by: Nguyen Dat Tho <tho3.nguyen@lge.com> | ||
| 24 | Signed-off-by: Paul Kehrer <paul.l.kehrer@gmail.com> | ||
| 25 | Co-authored-by: Alex Gaynor <alex.gaynor@gmail.com> | ||
| 26 | |||
| 27 | Upstream-Status: Backport [Backport from https://github.com/pyca/cryptography/commit/0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c] | ||
| 28 | --- | ||
| 29 | src/rust/src/backend/ec.rs | 39 ++++++++++++++++++++---------- | ||
| 30 | tests/hazmat/primitives/test_ec.py | 37 ++++++++++++++++++++++++++++ | ||
| 31 | 2 files changed, 63 insertions(+), 13 deletions(-) | ||
| 32 | |||
| 33 | diff --git a/src/rust/src/backend/ec.rs b/src/rust/src/backend/ec.rs | ||
| 34 | index 6a224b49f..27fced086 100644 | ||
| 35 | --- a/src/rust/src/backend/ec.rs | ||
| 36 | +++ b/src/rust/src/backend/ec.rs | ||
| 37 | @@ -155,12 +155,9 @@ pub(crate) fn public_key_from_pkey( | ||
| 38 | ) -> CryptographyResult<ECPublicKey> { | ||
| 39 | let ec = pkey.ec_key()?; | ||
| 40 | let curve = py_curve_from_curve(py, ec.group())?; | ||
| 41 | - check_key_infinity(&ec)?; | ||
| 42 | - Ok(ECPublicKey { | ||
| 43 | - pkey: pkey.to_owned(), | ||
| 44 | - curve: curve.into(), | ||
| 45 | - }) | ||
| 46 | + ECPublicKey::new(pkey.to_owned(), curve.into()) | ||
| 47 | } | ||
| 48 | + | ||
| 49 | #[pyo3::prelude::pyfunction] | ||
| 50 | fn generate_private_key( | ||
| 51 | py: pyo3::Python<'_>, | ||
| 52 | @@ -215,10 +212,7 @@ fn from_public_bytes( | ||
| 53 | let ec = openssl::ec::EcKey::from_public_key(&curve, &point)?; | ||
| 54 | let pkey = openssl::pkey::PKey::from_ec_key(ec)?; | ||
| 55 | |||
| 56 | - Ok(ECPublicKey { | ||
| 57 | - pkey, | ||
| 58 | - curve: py_curve.into(), | ||
| 59 | - }) | ||
| 60 | + ECPublicKey::new(pkey, py_curve.into()) | ||
| 61 | } | ||
| 62 | |||
| 63 | #[pyo3::prelude::pymethods] | ||
| 64 | @@ -357,6 +351,28 @@ impl ECPrivateKey { | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | +impl ECPublicKey { | ||
| 69 | + fn new( | ||
| 70 | + pkey: openssl::pkey::PKey<openssl::pkey::Public>, | ||
| 71 | + curve: pyo3::Py<pyo3::PyAny>, | ||
| 72 | + ) -> CryptographyResult<ECPublicKey> { | ||
| 73 | + let ec = pkey.ec_key()?; | ||
| 74 | + check_key_infinity(&ec)?; | ||
| 75 | + let mut bn_ctx = openssl::bn::BigNumContext::new()?; | ||
| 76 | + let mut cofactor = openssl::bn::BigNum::new()?; | ||
| 77 | + ec.group().cofactor(&mut cofactor, &mut bn_ctx)?; | ||
| 78 | + let one = openssl::bn::BigNum::from_u32(1)?; | ||
| 79 | + if cofactor != one { | ||
| 80 | + ec.check_key().map_err(|_| { | ||
| 81 | + pyo3::exceptions::PyValueError::new_err( | ||
| 82 | + "Invalid EC key (key out of range, infinity, etc.)", | ||
| 83 | + ) | ||
| 84 | + })?; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + Ok(ECPublicKey { pkey, curve }) | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | #[pyo3::prelude::pymethods] | ||
| 91 | impl ECPublicKey { | ||
| 92 | #[getter] | ||
| 93 | @@ -591,10 +607,7 @@ impl EllipticCurvePublicNumbers { | ||
| 94 | |||
| 95 | let pkey = openssl::pkey::PKey::from_ec_key(public_key)?; | ||
| 96 | |||
| 97 | - Ok(ECPublicKey { | ||
| 98 | - pkey, | ||
| 99 | - curve: self.curve.clone_ref(py), | ||
| 100 | - }) | ||
| 101 | + ECPublicKey::new(pkey, self.curve.clone_ref(py)) | ||
| 102 | } | ||
| 103 | |||
| 104 | fn __eq__( | ||
| 105 | diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py | ||
| 106 | index 334e76dcc..f7f2242f6 100644 | ||
| 107 | --- a/tests/hazmat/primitives/test_ec.py | ||
| 108 | +++ b/tests/hazmat/primitives/test_ec.py | ||
| 109 | @@ -1340,3 +1340,40 @@ class TestECDH: | ||
| 110 | |||
| 111 | with pytest.raises(ValueError): | ||
| 112 | key.exchange(ec.ECDH(), public_key) | ||
| 113 | + | ||
| 114 | + | ||
| 115 | +def test_invalid_sect_public_keys(backend): | ||
| 116 | + _skip_curve_unsupported(backend, ec.SECT571K1()) | ||
| 117 | + public_numbers = ec.EllipticCurvePublicNumbers(1, 1, ec.SECT571K1()) | ||
| 118 | + with pytest.raises(ValueError): | ||
| 119 | + public_numbers.public_key() | ||
| 120 | + | ||
| 121 | + point = binascii.unhexlify( | ||
| 122 | + b"0400000000000000000000000000000000000000000000000000000000000000000" | ||
| 123 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 124 | + b"0000000000010000000000000000000000000000000000000000000000000000000" | ||
| 125 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 126 | + b"0000000000000000000001" | ||
| 127 | + ) | ||
| 128 | + with pytest.raises(ValueError): | ||
| 129 | + ec.EllipticCurvePublicKey.from_encoded_point(ec.SECT571K1(), point) | ||
| 130 | + | ||
| 131 | + der = binascii.unhexlify( | ||
| 132 | + b"3081a7301006072a8648ce3d020106052b810400260381920004000000000000000" | ||
| 133 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 134 | + b"0000000000000000000000000000000000000000000000000000000000000100000" | ||
| 135 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 136 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 137 | + b"00001" | ||
| 138 | + ) | ||
| 139 | + with pytest.raises(ValueError): | ||
| 140 | + serialization.load_der_public_key(der) | ||
| 141 | + | ||
| 142 | + pem = textwrap.dedent("""-----BEGIN PUBLIC KEY----- | ||
| 143 | + MIGnMBAGByqGSM49AgEGBSuBBAAmA4GSAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
| 144 | + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
| 145 | + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
| 146 | + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE= | ||
| 147 | + -----END PUBLIC KEY-----""").encode() | ||
| 148 | + with pytest.raises(ValueError): | ||
| 149 | + serialization.load_pem_public_key(pem) | ||
diff --git a/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb b/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb index 732f925d92..c4573fa689 100644 --- a/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb +++ b/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb | |||
| @@ -11,6 +11,7 @@ LDSHARED += "-pthread" | |||
| 11 | SRC_URI[sha256sum] = "6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1" | 11 | SRC_URI[sha256sum] = "6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1" |
| 12 | 12 | ||
| 13 | SRC_URI += "file://0001-pyproject.toml-remove-benchmark-disable-option.patch \ | 13 | SRC_URI += "file://0001-pyproject.toml-remove-benchmark-disable-option.patch \ |
| 14 | file://CVE-2026-26007.patch \ | ||
| 14 | file://check-memfree.py \ | 15 | file://check-memfree.py \ |
| 15 | file://run-ptest \ | 16 | file://run-ptest \ |
| 16 | " | 17 | " |
diff --git a/meta/recipes-devtools/python/python3-pip/CVE-2026-1703.patch b/meta/recipes-devtools/python/python3-pip/CVE-2026-1703.patch new file mode 100644 index 0000000000..1470b7c541 --- /dev/null +++ b/meta/recipes-devtools/python/python3-pip/CVE-2026-1703.patch | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | From 4c651b70d60ed91b13663bcda9b3ed41748d0124 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Seth Michael Larson <seth@python.org> | ||
| 3 | Date: Fri, 30 Jan 2026 09:49:11 -0600 | ||
| 4 | Subject: [PATCH] Use os.path.commonpath() instead of commonprefix() | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/pypa/pip/commit/4c651b70d60ed91b13663bcda9b3ed41748d0124] | ||
| 7 | CVE: CVE-2026-1703 | ||
| 8 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 9 | --- | ||
| 10 | news/+1ee322a1.bugfix.rst | 1 + | ||
| 11 | src/pip/_internal/utils/unpacking.py | 2 +- | ||
| 12 | 2 files changed, 2 insertions(+), 1 deletion(-) | ||
| 13 | create mode 100644 news/+1ee322a1.bugfix.rst | ||
| 14 | |||
| 15 | diff --git a/news/+1ee322a1.bugfix.rst b/news/+1ee322a1.bugfix.rst | ||
| 16 | new file mode 100644 | ||
| 17 | index 0000000..edb1b32 | ||
| 18 | --- /dev/null | ||
| 19 | +++ b/news/+1ee322a1.bugfix.rst | ||
| 20 | @@ -0,0 +1 @@ | ||
| 21 | +Use a path-segment prefix comparison, not char-by-char. | ||
| 22 | diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py | ||
| 23 | index 78b5c13..0b26525 100644 | ||
| 24 | --- a/src/pip/_internal/utils/unpacking.py | ||
| 25 | +++ b/src/pip/_internal/utils/unpacking.py | ||
| 26 | @@ -81,7 +81,7 @@ def is_within_directory(directory: str, target: str) -> bool: | ||
| 27 | abs_directory = os.path.abspath(directory) | ||
| 28 | abs_target = os.path.abspath(target) | ||
| 29 | |||
| 30 | - prefix = os.path.commonprefix([abs_directory, abs_target]) | ||
| 31 | + prefix = os.path.commonpath([abs_directory, abs_target]) | ||
| 32 | return prefix == abs_directory | ||
| 33 | |||
| 34 | |||
| 35 | -- | ||
| 36 | 2.43.0 | ||
| 37 | |||
diff --git a/meta/recipes-devtools/python/python3-pip_24.0.bb b/meta/recipes-devtools/python/python3-pip_24.0.bb index be4a29500a..cf123a5d23 100644 --- a/meta/recipes-devtools/python/python3-pip_24.0.bb +++ b/meta/recipes-devtools/python/python3-pip_24.0.bb | |||
| @@ -31,7 +31,9 @@ LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=63ec52baf95163b597008bb46db68030 \ | |||
| 31 | 31 | ||
| 32 | inherit pypi python_setuptools_build_meta | 32 | inherit pypi python_setuptools_build_meta |
| 33 | 33 | ||
| 34 | SRC_URI += "file://no_shebang_mangling.patch" | 34 | SRC_URI += "file://no_shebang_mangling.patch \ |
| 35 | file://CVE-2026-1703.patch \ | ||
| 36 | " | ||
| 35 | 37 | ||
| 36 | SRC_URI[sha256sum] = "ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2" | 38 | SRC_URI[sha256sum] = "ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2" |
| 37 | 39 | ||
| @@ -39,6 +41,15 @@ do_install:append() { | |||
| 39 | rm -f ${D}/${bindir}/pip | 41 | rm -f ${D}/${bindir}/pip |
| 40 | } | 42 | } |
| 41 | 43 | ||
| 44 | do_install:append(){ | ||
| 45 | # pip vendors distlib which ships Windows launcher templates (*.exe). | ||
| 46 | # Keep them only when building for a Windows (mingw) host. | ||
| 47 | case "${HOST_OS}" in | ||
| 48 | mingw32|mingw64) ;; | ||
| 49 | *) rm -f ${D}${PYTHON_SITEPACKAGES_DIR}/pip/_vendor/distlib/*.exe ;; | ||
| 50 | esac | ||
| 51 | } | ||
| 52 | |||
| 42 | RDEPENDS:${PN} = "\ | 53 | RDEPENDS:${PN} = "\ |
| 43 | python3-compile \ | 54 | python3-compile \ |
| 44 | python3-io \ | 55 | python3-io \ |
diff --git a/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27448.patch b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27448.patch new file mode 100644 index 0000000000..87f46b4cb0 --- /dev/null +++ b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27448.patch | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | From d41a814759a9fb49584ca8ab3f7295de49a85aa0 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Gaynor <alex.gaynor@gmail.com> | ||
| 3 | Date: Mon, 16 Feb 2026 21:04:37 -0500 | ||
| 4 | Subject: [PATCH] Handle exceptions in set_tlsext_servername_callback callbacks | ||
| 5 | (#1478) | ||
| 6 | |||
| 7 | When the servername callback raises an exception, call sys.excepthook | ||
| 8 | with the exception info and return SSL_TLSEXT_ERR_ALERT_FATAL to abort | ||
| 9 | the handshake. Previously, exceptions would propagate uncaught through | ||
| 10 | the CFFI callback boundary. | ||
| 11 | |||
| 12 | https://claude.ai/code/session_01P7y1XmWkdtC5UcmZwGDvGi | ||
| 13 | |||
| 14 | Co-authored-by: Claude <noreply@anthropic.com> | ||
| 15 | |||
| 16 | Upstream-Status: Backport [https://github.com/pyca/pyopenssl/commit/d41a814759a9fb49584ca8ab3f7295de49a85aa0] | ||
| 17 | CVE: CVE-2026-27448 | ||
| 18 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 19 | --- | ||
| 20 | CHANGELOG.rst | 1 + | ||
| 21 | src/OpenSSL/SSL.py | 7 ++++++- | ||
| 22 | tests/test_ssl.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++ | ||
| 23 | 3 files changed, 57 insertions(+), 1 deletion(-) | ||
| 24 | |||
| 25 | diff --git a/CHANGELOG.rst b/CHANGELOG.rst | ||
| 26 | index 6e23770..12e60e4 100644 | ||
| 27 | --- a/CHANGELOG.rst | ||
| 28 | +++ b/CHANGELOG.rst | ||
| 29 | @@ -18,6 +18,7 @@ Changes: | ||
| 30 | |||
| 31 | - Added ``OpenSSL.SSL.Connection.get_selected_srtp_profile`` to determine which SRTP profile was negotiated. | ||
| 32 | `#1279 <https://github.com/pyca/pyopenssl/pull/1279>`_. | ||
| 33 | +- ``Context.set_tlsext_servername_callback`` now handles exceptions raised in the callback by calling ``sys.excepthook`` and returning a fatal TLS alert. Previously, exceptions were silently swallowed and the handshake would proceed as if the callback had succeeded. | ||
| 34 | |||
| 35 | 23.3.0 (2023-10-25) | ||
| 36 | ------------------- | ||
| 37 | diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py | ||
| 38 | index 4db5240..a6263c4 100644 | ||
| 39 | --- a/src/OpenSSL/SSL.py | ||
| 40 | +++ b/src/OpenSSL/SSL.py | ||
| 41 | @@ -1,5 +1,6 @@ | ||
| 42 | import os | ||
| 43 | import socket | ||
| 44 | +import sys | ||
| 45 | import typing | ||
| 46 | from errno import errorcode | ||
| 47 | from functools import partial, wraps | ||
| 48 | @@ -1567,7 +1568,11 @@ class Context: | ||
| 49 | |||
| 50 | @wraps(callback) | ||
| 51 | def wrapper(ssl, alert, arg): | ||
| 52 | - callback(Connection._reverse_mapping[ssl]) | ||
| 53 | + try: | ||
| 54 | + callback(Connection._reverse_mapping[ssl]) | ||
| 55 | + except Exception: | ||
| 56 | + sys.excepthook(*sys.exc_info()) | ||
| 57 | + return _lib.SSL_TLSEXT_ERR_ALERT_FATAL | ||
| 58 | return 0 | ||
| 59 | |||
| 60 | self._tlsext_servername_callback = _ffi.callback( | ||
| 61 | diff --git a/tests/test_ssl.py b/tests/test_ssl.py | ||
| 62 | index ca5bf83..55489b9 100644 | ||
| 63 | --- a/tests/test_ssl.py | ||
| 64 | +++ b/tests/test_ssl.py | ||
| 65 | @@ -1855,6 +1855,56 @@ class TestServerNameCallback: | ||
| 66 | |||
| 67 | assert args == [(server, b"foo1.example.com")] | ||
| 68 | |||
| 69 | + def test_servername_callback_exception( | ||
| 70 | + self, monkeypatch: pytest.MonkeyPatch | ||
| 71 | + ) -> None: | ||
| 72 | + """ | ||
| 73 | + When the callback passed to `Context.set_tlsext_servername_callback` | ||
| 74 | + raises an exception, ``sys.excepthook`` is called with the exception | ||
| 75 | + and the handshake fails with an ``Error``. | ||
| 76 | + """ | ||
| 77 | + exc = TypeError("server name callback failed") | ||
| 78 | + | ||
| 79 | + def servername(conn: Connection) -> None: | ||
| 80 | + raise exc | ||
| 81 | + | ||
| 82 | + excepthook_calls: list[ | ||
| 83 | + tuple[type[BaseException], BaseException, object] | ||
| 84 | + ] = [] | ||
| 85 | + | ||
| 86 | + def custom_excepthook( | ||
| 87 | + exc_type: type[BaseException], | ||
| 88 | + exc_value: BaseException, | ||
| 89 | + exc_tb: object, | ||
| 90 | + ) -> None: | ||
| 91 | + excepthook_calls.append((exc_type, exc_value, exc_tb)) | ||
| 92 | + | ||
| 93 | + context = Context(SSLv23_METHOD) | ||
| 94 | + context.set_tlsext_servername_callback(servername) | ||
| 95 | + | ||
| 96 | + # Necessary to actually accept the connection | ||
| 97 | + context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) | ||
| 98 | + context.use_certificate( | ||
| 99 | + load_certificate(FILETYPE_PEM, server_cert_pem) | ||
| 100 | + ) | ||
| 101 | + | ||
| 102 | + # Do a little connection to trigger the logic | ||
| 103 | + server = Connection(context, None) | ||
| 104 | + server.set_accept_state() | ||
| 105 | + | ||
| 106 | + client = Connection(Context(SSLv23_METHOD), None) | ||
| 107 | + client.set_connect_state() | ||
| 108 | + client.set_tlsext_host_name(b"foo1.example.com") | ||
| 109 | + | ||
| 110 | + monkeypatch.setattr(sys, "excepthook", custom_excepthook) | ||
| 111 | + with pytest.raises(Error): | ||
| 112 | + interact_in_memory(server, client) | ||
| 113 | + | ||
| 114 | + assert len(excepthook_calls) == 1 | ||
| 115 | + assert excepthook_calls[0][0] is TypeError | ||
| 116 | + assert excepthook_calls[0][1] is exc | ||
| 117 | + assert excepthook_calls[0][2] is not None | ||
| 118 | + | ||
| 119 | |||
| 120 | class TestApplicationLayerProtoNegotiation: | ||
| 121 | """ | ||
| 122 | -- | ||
| 123 | 2.43.0 | ||
| 124 | |||
diff --git a/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27459.patch b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27459.patch new file mode 100644 index 0000000000..f75540f96e --- /dev/null +++ b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27459.patch | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | From 57f09bb4bb051d3bc2a1abd36e9525313d5cd408 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Gaynor <alex.gaynor@gmail.com> | ||
| 3 | Date: Wed, 18 Feb 2026 07:46:15 -0500 | ||
| 4 | Subject: [PATCH] Fix buffer overflow in DTLS cookie generation callback | ||
| 5 | (#1479) | ||
| 6 | |||
| 7 | The cookie generate callback copied user-returned bytes into a | ||
| 8 | fixed-size native buffer without enforcing a maximum length. A | ||
| 9 | callback returning more than DTLS1_COOKIE_LENGTH bytes would overflow | ||
| 10 | the OpenSSL-provided buffer, corrupting adjacent memory. | ||
| 11 | |||
| 12 | Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> | ||
| 13 | |||
| 14 | Upstream-Status: Backport [https://github.com/pyca/pyopenssl/commit/57f09bb4bb051d3bc2a1abd36e9525313d5cd408] | ||
| 15 | CVE: CVE-2026-27459 | ||
| 16 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 17 | --- | ||
| 18 | CHANGELOG.rst | 1 + | ||
| 19 | src/OpenSSL/SSL.py | 7 +++++++ | ||
| 20 | tests/test_ssl.py | 38 ++++++++++++++++++++++++++++++++++++++ | ||
| 21 | 3 files changed, 46 insertions(+) | ||
| 22 | |||
| 23 | diff --git a/CHANGELOG.rst b/CHANGELOG.rst | ||
| 24 | index 12e60e4..6041fdc 100644 | ||
| 25 | --- a/CHANGELOG.rst | ||
| 26 | +++ b/CHANGELOG.rst | ||
| 27 | @@ -16,6 +16,7 @@ Deprecations: | ||
| 28 | Changes: | ||
| 29 | ^^^^^^^^ | ||
| 30 | |||
| 31 | +- Properly raise an error if a DTLS cookie callback returned a cookie longer than ``DTLS1_COOKIE_LENGTH`` bytes. Previously this would result in a buffer-overflow. | ||
| 32 | - Added ``OpenSSL.SSL.Connection.get_selected_srtp_profile`` to determine which SRTP profile was negotiated. | ||
| 33 | `#1279 <https://github.com/pyca/pyopenssl/pull/1279>`_. | ||
| 34 | - ``Context.set_tlsext_servername_callback`` now handles exceptions raised in the callback by calling ``sys.excepthook`` and returning a fatal TLS alert. Previously, exceptions were silently swallowed and the handshake would proceed as if the callback had succeeded. | ||
| 35 | diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py | ||
| 36 | index a6263c4..2e4da78 100644 | ||
| 37 | --- a/src/OpenSSL/SSL.py | ||
| 38 | +++ b/src/OpenSSL/SSL.py | ||
| 39 | @@ -691,11 +691,18 @@ class _CookieGenerateCallbackHelper(_CallbackExceptionHelper): | ||
| 40 | def __init__(self, callback): | ||
| 41 | _CallbackExceptionHelper.__init__(self) | ||
| 42 | |||
| 43 | + max_cookie_len = getattr(_lib, "DTLS1_COOKIE_LENGTH", 255) | ||
| 44 | + | ||
| 45 | @wraps(callback) | ||
| 46 | def wrapper(ssl, out, outlen): | ||
| 47 | try: | ||
| 48 | conn = Connection._reverse_mapping[ssl] | ||
| 49 | cookie = callback(conn) | ||
| 50 | + if len(cookie) > max_cookie_len: | ||
| 51 | + raise ValueError( | ||
| 52 | + f"Cookie too long (got {len(cookie)} bytes, " | ||
| 53 | + f"max {max_cookie_len})" | ||
| 54 | + ) | ||
| 55 | out[0 : len(cookie)] = cookie | ||
| 56 | outlen[0] = len(cookie) | ||
| 57 | return 1 | ||
| 58 | diff --git a/tests/test_ssl.py b/tests/test_ssl.py | ||
| 59 | index 55489b9..683e368 100644 | ||
| 60 | --- a/tests/test_ssl.py | ||
| 61 | +++ b/tests/test_ssl.py | ||
| 62 | @@ -4560,6 +4560,44 @@ class TestDTLS: | ||
| 63 | def test_it_works_with_srtp(self): | ||
| 64 | self._test_handshake_and_data(srtp_profile=b"SRTP_AES128_CM_SHA1_80") | ||
| 65 | |||
| 66 | + def test_cookie_generate_too_long(self) -> None: | ||
| 67 | + s_ctx = Context(DTLS_METHOD) | ||
| 68 | + | ||
| 69 | + def generate_cookie(ssl: Connection) -> bytes: | ||
| 70 | + return b"\x00" * 256 | ||
| 71 | + | ||
| 72 | + def verify_cookie(ssl: Connection, cookie: bytes) -> bool: | ||
| 73 | + return True | ||
| 74 | + | ||
| 75 | + s_ctx.set_cookie_generate_callback(generate_cookie) | ||
| 76 | + s_ctx.set_cookie_verify_callback(verify_cookie) | ||
| 77 | + s_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) | ||
| 78 | + s_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem)) | ||
| 79 | + s_ctx.set_options(OP_NO_QUERY_MTU) | ||
| 80 | + s = Connection(s_ctx) | ||
| 81 | + s.set_accept_state() | ||
| 82 | + | ||
| 83 | + c_ctx = Context(DTLS_METHOD) | ||
| 84 | + c_ctx.set_options(OP_NO_QUERY_MTU) | ||
| 85 | + c = Connection(c_ctx) | ||
| 86 | + c.set_connect_state() | ||
| 87 | + | ||
| 88 | + c.set_ciphertext_mtu(1500) | ||
| 89 | + s.set_ciphertext_mtu(1500) | ||
| 90 | + | ||
| 91 | + # Client sends ClientHello | ||
| 92 | + try: | ||
| 93 | + c.do_handshake() | ||
| 94 | + except SSL.WantReadError: | ||
| 95 | + pass | ||
| 96 | + chunk = c.bio_read(self.LARGE_BUFFER) | ||
| 97 | + s.bio_write(chunk) | ||
| 98 | + | ||
| 99 | + # Server tries DTLSv1_listen, which triggers cookie generation. | ||
| 100 | + # The oversized cookie should raise ValueError. | ||
| 101 | + with pytest.raises(ValueError, match="Cookie too long"): | ||
| 102 | + s.DTLSv1_listen() | ||
| 103 | + | ||
| 104 | def test_timeout(self, monkeypatch): | ||
| 105 | c_ctx = Context(DTLS_METHOD) | ||
| 106 | c = Connection(c_ctx) | ||
| 107 | -- | ||
| 108 | 2.43.0 | ||
| 109 | |||
diff --git a/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb b/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb index 116f214bfa..94a70aa17d 100644 --- a/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb +++ b/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb | |||
| @@ -10,6 +10,11 @@ SRC_URI[sha256sum] = "6aa33039a93fffa4563e655b61d11364d01264be8ccb49906101e02a33 | |||
| 10 | PYPI_PACKAGE = "pyOpenSSL" | 10 | PYPI_PACKAGE = "pyOpenSSL" |
| 11 | inherit pypi setuptools3 | 11 | inherit pypi setuptools3 |
| 12 | 12 | ||
| 13 | SRC_URI += " \ | ||
| 14 | file://CVE-2026-27448.patch \ | ||
| 15 | file://CVE-2026-27459.patch \ | ||
| 16 | " | ||
| 17 | |||
| 13 | PACKAGES =+ "${PN}-tests" | 18 | PACKAGES =+ "${PN}-tests" |
| 14 | FILES:${PN}-tests = "${libdir}/${PYTHON_DIR}/site-packages/OpenSSL/test" | 19 | FILES:${PN}-tests = "${libdir}/${PYTHON_DIR}/site-packages/OpenSSL/test" |
| 15 | 20 | ||
diff --git a/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb b/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb index 46b2f0ab00..00f83056db 100644 --- a/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb +++ b/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb | |||
| @@ -19,6 +19,15 @@ SRC_URI += " \ | |||
| 19 | 19 | ||
| 20 | SRC_URI[sha256sum] = "5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8" | 20 | SRC_URI[sha256sum] = "5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8" |
| 21 | 21 | ||
| 22 | do_install:append() { | ||
| 23 | # setuptools ships Windows launcher executables (cli*.exe, gui*.exe). | ||
| 24 | # Keep them only when building for a Windows (mingw) host. | ||
| 25 | case "${HOST_OS}" in | ||
| 26 | mingw32|mingw64) ;; | ||
| 27 | *) rm -f ${D}${PYTHON_SITEPACKAGES_DIR}/setuptools/*.exe ;; | ||
| 28 | esac | ||
| 29 | } | ||
| 30 | |||
| 22 | DEPENDS += "python3" | 31 | DEPENDS += "python3" |
| 23 | 32 | ||
| 24 | RDEPENDS:${PN} = "\ | 33 | RDEPENDS:${PN} = "\ |
diff --git a/meta/recipes-extended/timezone/timezone.inc b/meta/recipes-extended/timezone/timezone.inc index f21bedf4fc..35f22d5a15 100644 --- a/meta/recipes-extended/timezone/timezone.inc +++ b/meta/recipes-extended/timezone/timezone.inc | |||
| @@ -6,7 +6,7 @@ SECTION = "base" | |||
| 6 | LICENSE = "PD & BSD-3-Clause" | 6 | LICENSE = "PD & BSD-3-Clause" |
| 7 | LIC_FILES_CHKSUM = "file://LICENSE;md5=c679c9d6b02bc2757b3eaf8f53c43fba" | 7 | LIC_FILES_CHKSUM = "file://LICENSE;md5=c679c9d6b02bc2757b3eaf8f53c43fba" |
| 8 | 8 | ||
| 9 | PV = "2025b" | 9 | PV = "2025c" |
| 10 | 10 | ||
| 11 | SRC_URI =" http://www.iana.org/time-zones/repository/releases/tzcode${PV}.tar.gz;name=tzcode;subdir=tz \ | 11 | SRC_URI =" http://www.iana.org/time-zones/repository/releases/tzcode${PV}.tar.gz;name=tzcode;subdir=tz \ |
| 12 | http://www.iana.org/time-zones/repository/releases/tzdata${PV}.tar.gz;name=tzdata;subdir=tz \ | 12 | http://www.iana.org/time-zones/repository/releases/tzdata${PV}.tar.gz;name=tzdata;subdir=tz \ |
| @@ -16,5 +16,5 @@ S = "${WORKDIR}/tz" | |||
| 16 | 16 | ||
| 17 | UPSTREAM_CHECK_URI = "http://www.iana.org/time-zones" | 17 | UPSTREAM_CHECK_URI = "http://www.iana.org/time-zones" |
| 18 | 18 | ||
| 19 | SRC_URI[tzcode.sha256sum] = "05f8fedb3525ee70d49c87d3fae78a8a0dbae4fe87aa565c65cda9948ae135ec" | 19 | SRC_URI[tzcode.sha256sum] = "697ebe6625444aef5080f58e49d03424bbb52e08bf483d3ddb5acf10cbd15740" |
| 20 | SRC_URI[tzdata.sha256sum] = "11810413345fc7805017e27ea9fa4885fd74cd61b2911711ad038f5d28d71474" | 20 | SRC_URI[tzdata.sha256sum] = "4aa79e4effee53fc4029ffe5f6ebe97937282ebcdf386d5d2da91ce84142f957" |
diff --git a/meta/recipes-graphics/freetype/freetype/CVE-2026-23865.patch b/meta/recipes-graphics/freetype/freetype/CVE-2026-23865.patch new file mode 100644 index 0000000000..aa0d4326f8 --- /dev/null +++ b/meta/recipes-graphics/freetype/freetype/CVE-2026-23865.patch | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | From fc85a255849229c024c8e65f536fe1875d84841c Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Werner Lemberg <wl@gnu.org> | ||
| 3 | Date: Sat, 3 Jan 2026 08:07:57 +0100 | ||
| 4 | Subject: [PATCH] [ttgxvar] Check for overflow in array size computation. | ||
| 5 | |||
| 6 | Problem reported and analyzed by povcfe <povcfe2sec@gmail.com>. | ||
| 7 | |||
| 8 | Fixes issue #1382. | ||
| 9 | |||
| 10 | * src/truetype/ttgxvar.c (tt_var_load_item_variation_store): Do it. | ||
| 11 | |||
| 12 | Upstream-Status: Backport [https://gitlab.com/freetype/freetype/-/commit/fc85a255849229c024c8e65f536fe1875d84841c] | ||
| 13 | CVE: CVE-2026-23865 | ||
| 14 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 15 | --- | ||
| 16 | src/truetype/ttgxvar.c | 15 ++++++++++++++- | ||
| 17 | 1 file changed, 14 insertions(+), 1 deletion(-) | ||
| 18 | |||
| 19 | diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c | ||
| 20 | index 2ff40c9e8..96ddc04c8 100644 | ||
| 21 | --- a/src/truetype/ttgxvar.c | ||
| 22 | +++ b/src/truetype/ttgxvar.c | ||
| 23 | @@ -628,6 +628,7 @@ | ||
| 24 | FT_UShort word_delta_count; | ||
| 25 | FT_UInt region_idx_count; | ||
| 26 | FT_UInt per_region_size; | ||
| 27 | + FT_UInt delta_set_size; | ||
| 28 | |||
| 29 | |||
| 30 | if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) ) | ||
| 31 | @@ -697,7 +698,19 @@ | ||
| 32 | if ( long_words ) | ||
| 33 | per_region_size *= 2; | ||
| 34 | |||
| 35 | - if ( FT_NEW_ARRAY( varData->deltaSet, per_region_size * item_count ) ) | ||
| 36 | + /* Check for overflow (we actually test whether the */ | ||
| 37 | + /* multiplication of two unsigned values wraps around). */ | ||
| 38 | + delta_set_size = per_region_size * item_count; | ||
| 39 | + if ( per_region_size && | ||
| 40 | + delta_set_size / per_region_size != item_count ) | ||
| 41 | + { | ||
| 42 | + FT_TRACE2(( "tt_var_load_item_variation_store:" | ||
| 43 | + " bad delta set array size\n" )); | ||
| 44 | + error = FT_THROW( Array_Too_Large ); | ||
| 45 | + goto Exit; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + if ( FT_NEW_ARRAY( varData->deltaSet, delta_set_size ) ) | ||
| 49 | goto Exit; | ||
| 50 | if ( FT_Stream_Read( stream, | ||
| 51 | varData->deltaSet, | ||
| 52 | -- | ||
| 53 | GitLab | ||
| 54 | |||
diff --git a/meta/recipes-graphics/freetype/freetype_2.13.2.bb b/meta/recipes-graphics/freetype/freetype_2.13.2.bb index ce7a615a3c..e053fef3b5 100644 --- a/meta/recipes-graphics/freetype/freetype_2.13.2.bb +++ b/meta/recipes-graphics/freetype/freetype_2.13.2.bb | |||
| @@ -15,6 +15,7 @@ LIC_FILES_CHKSUM = "file://LICENSE.TXT;md5=843b6efc16f6b1652ec97f89d5a516c0 \ | |||
| 15 | 15 | ||
| 16 | SRC_URI = "${SAVANNAH_NONGNU_MIRROR}/${BPN}/${BP}.tar.xz \ | 16 | SRC_URI = "${SAVANNAH_NONGNU_MIRROR}/${BPN}/${BP}.tar.xz \ |
| 17 | file://CVE-2025-27363.patch \ | 17 | file://CVE-2025-27363.patch \ |
| 18 | file://CVE-2026-23865.patch \ | ||
| 18 | " | 19 | " |
| 19 | SRC_URI[sha256sum] = "12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d" | 20 | SRC_URI[sha256sum] = "12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d" |
| 20 | 21 | ||
diff --git a/meta/recipes-kernel/wireless-regdb/wireless-regdb_2025.10.07.bb b/meta/recipes-kernel/wireless-regdb/wireless-regdb_2026.02.04.bb index 68ae3b0464..2f7c816043 100644 --- a/meta/recipes-kernel/wireless-regdb/wireless-regdb_2025.10.07.bb +++ b/meta/recipes-kernel/wireless-regdb/wireless-regdb_2026.02.04.bb | |||
| @@ -5,7 +5,7 @@ LICENSE = "ISC" | |||
| 5 | LIC_FILES_CHKSUM = "file://LICENSE;md5=07c4f6dea3845b02a18dc00c8c87699c" | 5 | LIC_FILES_CHKSUM = "file://LICENSE;md5=07c4f6dea3845b02a18dc00c8c87699c" |
| 6 | 6 | ||
| 7 | SRC_URI = "https://www.kernel.org/pub/software/network/${BPN}/${BP}.tar.xz" | 7 | SRC_URI = "https://www.kernel.org/pub/software/network/${BPN}/${BP}.tar.xz" |
| 8 | SRC_URI[sha256sum] = "d4c872a44154604c869f5851f7d21d818d492835d370af7f58de8847973801c3" | 8 | SRC_URI[sha256sum] = "0ff48a5cd9e9cfe8e815a24e023734919e9a3b7ad2f039243ad121cf5aabf6c6" |
| 9 | 9 | ||
| 10 | inherit bin_package allarch | 10 | inherit bin_package allarch |
| 11 | 11 | ||
diff --git a/meta/recipes-multimedia/libtiff/tiff_4.6.0.bb b/meta/recipes-multimedia/libtiff/tiff_4.6.0.bb index 777783d7cc..07540692fc 100644 --- a/meta/recipes-multimedia/libtiff/tiff_4.6.0.bb +++ b/meta/recipes-multimedia/libtiff/tiff_4.6.0.bb | |||
| @@ -29,7 +29,7 @@ CVE_STATUS[CVE-2015-7313] = "fixed-version: Tested with check from https://secur | |||
| 29 | CVE_STATUS[CVE-2023-3164] = "cpe-incorrect: Issue only affects the tiffcrop tool not compiled by default since 4.6.0" | 29 | CVE_STATUS[CVE-2023-3164] = "cpe-incorrect: Issue only affects the tiffcrop tool not compiled by default since 4.6.0" |
| 30 | 30 | ||
| 31 | CVE_STATUS_GROUPS += "CVE_STATUS_REMOVED_TOOLS" | 31 | CVE_STATUS_GROUPS += "CVE_STATUS_REMOVED_TOOLS" |
| 32 | CVE_STATUS_REMOVED_TOOLS = "CVE-2024-13978 CVE-2025-8176 CVE-2025-8177 CVE-2025-8534 CVE-2025-8851 CVE-2025-8961" | 32 | CVE_STATUS_REMOVED_TOOLS = "CVE-2024-13978 CVE-2025-8176 CVE-2025-8177 CVE-2025-8534 CVE-2025-8851 CVE-2025-8961 CVE-2025-61143 CVE-2025-61144 CVE-2025-61145" |
| 33 | CVE_STATUS_REMOVED_TOOLS[status] = "cpe-incorrect: tools affected by these CVEs are not present in this release" | 33 | CVE_STATUS_REMOVED_TOOLS[status] = "cpe-incorrect: tools affected by these CVEs are not present in this release" |
| 34 | 34 | ||
| 35 | inherit autotools multilib_header | 35 | inherit autotools multilib_header |
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-1.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-1.patch new file mode 100644 index 0000000000..ae52a43a2c --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-1.patch | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | From 0b2377dfccd99be641bf3f1a0de9f0dc8dc0d4b1 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Mon, 26 Jan 2026 19:02:27 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: use actual zeroes in universal exclude | ||
| 5 | IP NC | ||
| 6 | |||
| 7 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 8 | |||
| 9 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/0b2377dfccd99be641bf3f1a0de9f0dc8dc0d4b1] | ||
| 10 | CVE: CVE-2025-14831 | ||
| 11 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 12 | --- | ||
| 13 | lib/x509/name_constraints.c | 9 +++++---- | ||
| 14 | 1 file changed, 5 insertions(+), 4 deletions(-) | ||
| 15 | |||
| 16 | --- a/lib/x509/name_constraints.c | ||
| 17 | +++ b/lib/x509/name_constraints.c | ||
| 18 | @@ -61,7 +61,7 @@ struct gnutls_name_constraints_st { | ||
| 19 | |||
| 20 | static struct name_constraints_node_st * | ||
| 21 | name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type, | ||
| 22 | - unsigned char *data, unsigned int size); | ||
| 23 | + const unsigned char *data, unsigned int size); | ||
| 24 | |||
| 25 | static int | ||
| 26 | name_constraints_node_list_add(struct name_constraints_node_list_st *list, | ||
| 27 | @@ -285,7 +285,7 @@ static void name_constraints_node_free(s | ||
| 28 | -*/ | ||
| 29 | static struct name_constraints_node_st * | ||
| 30 | name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type, | ||
| 31 | - unsigned char *data, unsigned int size) | ||
| 32 | + const unsigned char *data, unsigned int size) | ||
| 33 | { | ||
| 34 | struct name_constraints_node_st *tmp; | ||
| 35 | int ret; | ||
| 36 | @@ -339,6 +339,7 @@ static int name_constraints_node_list_in | ||
| 37 | struct name_constraints_node_list_st removed = { .data = NULL, | ||
| 38 | .size = 0, | ||
| 39 | .capacity = 0 }; | ||
| 40 | + static const unsigned char universal_ip[32] = { 0 }; | ||
| 41 | |||
| 42 | /* temporary array to see, if we need to add universal excluded constraints | ||
| 43 | * (see phase 3 for details) | ||
| 44 | @@ -471,7 +472,7 @@ static int name_constraints_node_list_in | ||
| 45 | case GNUTLS_SAN_IPADDRESS: | ||
| 46 | // add universal restricted range for IPv4 | ||
| 47 | tmp = name_constraints_node_new( | ||
| 48 | - nc, GNUTLS_SAN_IPADDRESS, NULL, 8); | ||
| 49 | + nc, GNUTLS_SAN_IPADDRESS, universal_ip, 8); | ||
| 50 | if (tmp == NULL) { | ||
| 51 | gnutls_assert(); | ||
| 52 | ret = GNUTLS_E_MEMORY_ERROR; | ||
| 53 | @@ -484,7 +485,7 @@ static int name_constraints_node_list_in | ||
| 54 | } | ||
| 55 | // add universal restricted range for IPv6 | ||
| 56 | tmp = name_constraints_node_new( | ||
| 57 | - nc, GNUTLS_SAN_IPADDRESS, NULL, 32); | ||
| 58 | + nc, GNUTLS_SAN_IPADDRESS, universal_ip, 32); | ||
| 59 | if (tmp == NULL) { | ||
| 60 | gnutls_assert(); | ||
| 61 | ret = GNUTLS_E_MEMORY_ERROR; | ||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-2.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-2.patch new file mode 100644 index 0000000000..0d34032554 --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-2.patch | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | From 85d6348a30c74d4ee3710e0f4652f634eaad6914 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Mon, 26 Jan 2026 19:10:58 +0100 | ||
| 4 | Subject: [PATCH] tests/name-constraints-ip: stop swallowing errors... | ||
| 5 | |||
| 6 | ... now when it started to pass | ||
| 7 | |||
| 8 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 9 | |||
| 10 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/85d6348a30c74d4ee3710e0f4652f634eaad6914] | ||
| 11 | CVE: CVE-2025-14831 | ||
| 12 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 13 | --- | ||
| 14 | tests/name-constraints-ip.c | 2 +- | ||
| 15 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 16 | |||
| 17 | diff --git a/tests/name-constraints-ip.c b/tests/name-constraints-ip.c | ||
| 18 | index 7a196088dc..a0cf172b7f 100644 | ||
| 19 | --- a/tests/name-constraints-ip.c | ||
| 20 | +++ b/tests/name-constraints-ip.c | ||
| 21 | @@ -772,5 +772,5 @@ int main(int argc, char **argv) | ||
| 22 | cmocka_unit_test_setup_teardown( | ||
| 23 | check_ipv4v6_single_constraint_each, setup, teardown) | ||
| 24 | }; | ||
| 25 | - cmocka_run_group_tests(tests, NULL, NULL); | ||
| 26 | + return cmocka_run_group_tests(tests, NULL, NULL); | ||
| 27 | } | ||
| 28 | -- | ||
| 29 | GitLab | ||
| 30 | |||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-3.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-3.patch new file mode 100644 index 0000000000..ed4a7da3c7 --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-3.patch | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | From c28475413f82e1f34295d5c039f0c0a4ca2ee526 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Mon, 26 Jan 2026 20:14:33 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: reject some malformed domain names | ||
| 5 | |||
| 6 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 7 | |||
| 8 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/c28475413f82e1f34295d5c039f0c0a4ca2ee526] | ||
| 9 | CVE: CVE-2025-14831 | ||
| 10 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 11 | --- | ||
| 12 | lib/x509/name_constraints.c | 17 +++++++++++++++++ | ||
| 13 | 1 file changed, 17 insertions(+) | ||
| 14 | |||
| 15 | diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c | ||
| 16 | index d07482e3c9..9783d92851 100644 | ||
| 17 | --- a/lib/x509/name_constraints.c | ||
| 18 | +++ b/lib/x509/name_constraints.c | ||
| 19 | @@ -159,6 +159,23 @@ static int validate_name_constraints_node(gnutls_x509_subject_alt_name_t type, | ||
| 20 | return gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR); | ||
| 21 | } | ||
| 22 | |||
| 23 | + /* Validate DNS names and email addresses for malformed input */ | ||
| 24 | + if (type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME) { | ||
| 25 | + unsigned int i; | ||
| 26 | + if (name->size == 0) | ||
| 27 | + return GNUTLS_E_SUCCESS; | ||
| 28 | + | ||
| 29 | + /* reject names with consecutive dots... */ | ||
| 30 | + for (i = 0; i + 1 < name->size; i++) { | ||
| 31 | + if (name->data[i] == '.' && name->data[i + 1] == '.') | ||
| 32 | + return gnutls_assert_val( | ||
| 33 | + GNUTLS_E_ILLEGAL_PARAMETER); | ||
| 34 | + } | ||
| 35 | + /* ... or names consisting exclusively of dots */ | ||
| 36 | + if (name->size == 1 && name->data[0] == '.') | ||
| 37 | + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | return GNUTLS_E_SUCCESS; | ||
| 41 | } | ||
| 42 | |||
| 43 | -- | ||
| 44 | GitLab | ||
| 45 | |||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-4.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-4.patch new file mode 100644 index 0000000000..99ec9c5e9a --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-4.patch | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | From 6db7da7fcfe230f445b1edbb56e2a8346120c891 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Thu, 5 Feb 2026 13:22:10 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: name_constraints_node_add_{new,copy} | ||
| 5 | |||
| 6 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 7 | |||
| 8 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/6db7da7fcfe230f445b1edbb56e2a8346120c891] | ||
| 9 | CVE: CVE-2025-14831 | ||
| 10 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 11 | --- | ||
| 12 | lib/x509/name_constraints.c | 112 ++++++++++++++++-------------------- | ||
| 13 | 1 file changed, 51 insertions(+), 61 deletions(-) | ||
| 14 | |||
| 15 | --- a/lib/x509/name_constraints.c | ||
| 16 | +++ b/lib/x509/name_constraints.c | ||
| 17 | @@ -86,6 +86,38 @@ name_constraints_node_list_add(struct na | ||
| 18 | return 0; | ||
| 19 | } | ||
| 20 | |||
| 21 | +static int | ||
| 22 | +name_constraints_node_add_new(gnutls_x509_name_constraints_t nc, | ||
| 23 | + struct name_constraints_node_list_st *list, | ||
| 24 | + unsigned type, const unsigned char *data, | ||
| 25 | + unsigned int size) | ||
| 26 | +{ | ||
| 27 | + struct name_constraints_node_st *node; | ||
| 28 | + int ret; | ||
| 29 | + node = name_constraints_node_new(nc, type, data, size); | ||
| 30 | + if (node == NULL) { | ||
| 31 | + gnutls_assert(); | ||
| 32 | + return GNUTLS_E_MEMORY_ERROR; | ||
| 33 | + } | ||
| 34 | + ret = name_constraints_node_list_add(list, node); | ||
| 35 | + if (ret < 0) { | ||
| 36 | + gnutls_assert(); | ||
| 37 | + return ret; | ||
| 38 | + } | ||
| 39 | + return GNUTLS_E_SUCCESS; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +static int | ||
| 43 | +name_constraints_node_add_copy(gnutls_x509_name_constraints_t nc, | ||
| 44 | + struct name_constraints_node_list_st *dest, | ||
| 45 | + const struct name_constraints_node_st *src) | ||
| 46 | +{ | ||
| 47 | + if (!src) | ||
| 48 | + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); | ||
| 49 | + return name_constraints_node_add_new(nc, dest, src->type, | ||
| 50 | + src->name.data, src->name.size); | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | // for documentation see the implementation | ||
| 54 | static int name_constraints_intersect_nodes( | ||
| 55 | gnutls_x509_name_constraints_t nc, | ||
| 56 | @@ -188,7 +220,6 @@ static int extract_name_constraints(gnut | ||
| 57 | unsigned indx; | ||
| 58 | gnutls_datum_t tmp = { NULL, 0 }; | ||
| 59 | unsigned int type; | ||
| 60 | - struct name_constraints_node_st *node; | ||
| 61 | |||
| 62 | for (indx = 1;; indx++) { | ||
| 63 | snprintf(tmpstr, sizeof(tmpstr), "%s.?%u.base", vstr, indx); | ||
| 64 | @@ -231,15 +262,9 @@ static int extract_name_constraints(gnut | ||
| 65 | goto cleanup; | ||
| 66 | } | ||
| 67 | |||
| 68 | - node = name_constraints_node_new(nc, type, tmp.data, tmp.size); | ||
| 69 | + ret = name_constraints_node_add_new(nc, nodes, type, tmp.data, | ||
| 70 | + tmp.size); | ||
| 71 | _gnutls_free_datum(&tmp); | ||
| 72 | - if (node == NULL) { | ||
| 73 | - gnutls_assert(); | ||
| 74 | - ret = GNUTLS_E_MEMORY_ERROR; | ||
| 75 | - goto cleanup; | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - ret = name_constraints_node_list_add(nodes, node); | ||
| 79 | if (ret < 0) { | ||
| 80 | gnutls_assert(); | ||
| 81 | goto cleanup; | ||
| 82 | @@ -459,14 +484,7 @@ static int name_constraints_node_list_in | ||
| 83 | // Beware: also copies nodes other than DNS, email, IP, | ||
| 84 | // since their counterpart may have been moved in phase 1. | ||
| 85 | if (!used) { | ||
| 86 | - tmp = name_constraints_node_new( | ||
| 87 | - nc, t2->type, t2->name.data, t2->name.size); | ||
| 88 | - if (tmp == NULL) { | ||
| 89 | - gnutls_assert(); | ||
| 90 | - ret = GNUTLS_E_MEMORY_ERROR; | ||
| 91 | - goto cleanup; | ||
| 92 | - } | ||
| 93 | - ret = name_constraints_node_list_add(permitted, tmp); | ||
| 94 | + ret = name_constraints_node_add_copy(nc, permitted, t2); | ||
| 95 | if (ret < 0) { | ||
| 96 | gnutls_assert(); | ||
| 97 | goto cleanup; | ||
| 98 | @@ -488,27 +506,17 @@ static int name_constraints_node_list_in | ||
| 99 | switch (type) { | ||
| 100 | case GNUTLS_SAN_IPADDRESS: | ||
| 101 | // add universal restricted range for IPv4 | ||
| 102 | - tmp = name_constraints_node_new( | ||
| 103 | - nc, GNUTLS_SAN_IPADDRESS, universal_ip, 8); | ||
| 104 | - if (tmp == NULL) { | ||
| 105 | - gnutls_assert(); | ||
| 106 | - ret = GNUTLS_E_MEMORY_ERROR; | ||
| 107 | - goto cleanup; | ||
| 108 | - } | ||
| 109 | - ret = name_constraints_node_list_add(excluded, tmp); | ||
| 110 | + ret = name_constraints_node_add_new( | ||
| 111 | + nc, excluded, GNUTLS_SAN_IPADDRESS, | ||
| 112 | + universal_ip, 8); | ||
| 113 | if (ret < 0) { | ||
| 114 | gnutls_assert(); | ||
| 115 | goto cleanup; | ||
| 116 | } | ||
| 117 | // add universal restricted range for IPv6 | ||
| 118 | - tmp = name_constraints_node_new( | ||
| 119 | - nc, GNUTLS_SAN_IPADDRESS, universal_ip, 32); | ||
| 120 | - if (tmp == NULL) { | ||
| 121 | - gnutls_assert(); | ||
| 122 | - ret = GNUTLS_E_MEMORY_ERROR; | ||
| 123 | - goto cleanup; | ||
| 124 | - } | ||
| 125 | - ret = name_constraints_node_list_add(excluded, tmp); | ||
| 126 | + ret = name_constraints_node_add_new( | ||
| 127 | + nc, excluded, GNUTLS_SAN_IPADDRESS, | ||
| 128 | + universal_ip, 32); | ||
| 129 | if (ret < 0) { | ||
| 130 | gnutls_assert(); | ||
| 131 | goto cleanup; | ||
| 132 | @@ -516,13 +524,8 @@ static int name_constraints_node_list_in | ||
| 133 | break; | ||
| 134 | case GNUTLS_SAN_DNSNAME: | ||
| 135 | case GNUTLS_SAN_RFC822NAME: | ||
| 136 | - tmp = name_constraints_node_new(nc, type, NULL, 0); | ||
| 137 | - if (tmp == NULL) { | ||
| 138 | - gnutls_assert(); | ||
| 139 | - ret = GNUTLS_E_MEMORY_ERROR; | ||
| 140 | - goto cleanup; | ||
| 141 | - } | ||
| 142 | - ret = name_constraints_node_list_add(excluded, tmp); | ||
| 143 | + ret = name_constraints_node_add_new(nc, excluded, type, | ||
| 144 | + NULL, 0); | ||
| 145 | if (ret < 0) { | ||
| 146 | gnutls_assert(); | ||
| 147 | goto cleanup; | ||
| 148 | @@ -544,20 +547,13 @@ static int name_constraints_node_list_co | ||
| 149 | struct name_constraints_node_list_st *nodes, | ||
| 150 | const struct name_constraints_node_list_st *nodes2) | ||
| 151 | { | ||
| 152 | + int ret; | ||
| 153 | + | ||
| 154 | for (size_t i = 0; i < nodes2->size; i++) { | ||
| 155 | - const struct name_constraints_node_st *node = nodes2->data[i]; | ||
| 156 | - struct name_constraints_node_st *tmp; | ||
| 157 | - int ret; | ||
| 158 | - | ||
| 159 | - tmp = name_constraints_node_new(nc, node->type, node->name.data, | ||
| 160 | - node->name.size); | ||
| 161 | - if (tmp == NULL) { | ||
| 162 | - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); | ||
| 163 | - } | ||
| 164 | - ret = name_constraints_node_list_add(nodes, tmp); | ||
| 165 | + ret = name_constraints_node_add_copy(nc, nodes, | ||
| 166 | + nodes2->data[i]); | ||
| 167 | if (ret < 0) { | ||
| 168 | - name_constraints_node_free(tmp); | ||
| 169 | - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); | ||
| 170 | + return gnutls_assert_val(ret); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | @@ -687,7 +683,6 @@ static int name_constraints_add(gnutls_x | ||
| 175 | gnutls_x509_subject_alt_name_t type, | ||
| 176 | const gnutls_datum_t *name, unsigned permitted) | ||
| 177 | { | ||
| 178 | - struct name_constraints_node_st *tmp; | ||
| 179 | struct name_constraints_node_list_st *nodes; | ||
| 180 | int ret; | ||
| 181 | |||
| 182 | @@ -697,15 +692,10 @@ static int name_constraints_add(gnutls_x | ||
| 183 | |||
| 184 | nodes = permitted ? &nc->permitted : &nc->excluded; | ||
| 185 | |||
| 186 | - tmp = name_constraints_node_new(nc, type, name->data, name->size); | ||
| 187 | - if (tmp == NULL) | ||
| 188 | - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); | ||
| 189 | - | ||
| 190 | - ret = name_constraints_node_list_add(nodes, tmp); | ||
| 191 | - if (ret < 0) { | ||
| 192 | - name_constraints_node_free(tmp); | ||
| 193 | + ret = name_constraints_node_add_new(nc, nodes, type, name->data, | ||
| 194 | + name->size); | ||
| 195 | + if (ret < 0) | ||
| 196 | return gnutls_assert_val(ret); | ||
| 197 | - } | ||
| 198 | |||
| 199 | return 0; | ||
| 200 | } | ||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-5.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-5.patch new file mode 100644 index 0000000000..7c5ffdf6d8 --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-5.patch | |||
| @@ -0,0 +1,500 @@ | |||
| 1 | From 094accd3ebec17ead6c391757eaa18763b72d83f Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Mon, 26 Jan 2026 20:16:36 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: introduce a rich comparator | ||
| 5 | |||
| 6 | These are preparatory changes before implementing N * log N intersection | ||
| 7 | over sorted lists of constraints. | ||
| 8 | |||
| 9 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 10 | |||
| 11 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/094accd3ebec17ead6c391757eaa18763b72d83f] | ||
| 12 | CVE: CVE-2025-14831 | ||
| 13 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 14 | --- | ||
| 15 | lib/x509/name_constraints.c | 411 ++++++++++++++++++++++++++++-------- | ||
| 16 | 1 file changed, 320 insertions(+), 91 deletions(-) | ||
| 17 | |||
| 18 | --- a/lib/x509/name_constraints.c | ||
| 19 | +++ b/lib/x509/name_constraints.c | ||
| 20 | @@ -39,6 +39,9 @@ | ||
| 21 | #include "ip.h" | ||
| 22 | #include "ip-in-cidr.h" | ||
| 23 | #include "intprops.h" | ||
| 24 | +#include "minmax.h" | ||
| 25 | + | ||
| 26 | +#include <string.h> | ||
| 27 | |||
| 28 | #define MAX_NC_CHECKS (1 << 20) | ||
| 29 | |||
| 30 | @@ -63,6 +66,282 @@ static struct name_constraints_node_st * | ||
| 31 | name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type, | ||
| 32 | const unsigned char *data, unsigned int size); | ||
| 33 | |||
| 34 | +/* An enum for "rich" comparisons that not only let us sort name constraints, | ||
| 35 | + * children-before-parent, but also subsume them during intersection. */ | ||
| 36 | +enum name_constraint_relation { | ||
| 37 | + NC_SORTS_BEFORE = -2, /* unrelated constraints */ | ||
| 38 | + NC_INCLUDED_BY = -1, /* nc1 is included by nc2 / children sort first */ | ||
| 39 | + NC_EQUAL = 0, /* exact match */ | ||
| 40 | + NC_INCLUDES = 1, /* nc1 includes nc2 / parents sort last */ | ||
| 41 | + NC_SORTS_AFTER = 2 /* unrelated constraints */ | ||
| 42 | +}; | ||
| 43 | + | ||
| 44 | +/* A helper to compare just a pair of strings with this rich comparison */ | ||
| 45 | +static enum name_constraint_relation | ||
| 46 | +compare_strings(const void *n1, size_t n1_len, const void *n2, size_t n2_len) | ||
| 47 | +{ | ||
| 48 | + int r = memcmp(n1, n2, MIN(n1_len, n2_len)); | ||
| 49 | + if (r < 0) | ||
| 50 | + return NC_SORTS_BEFORE; | ||
| 51 | + if (r > 0) | ||
| 52 | + return NC_SORTS_AFTER; | ||
| 53 | + if (n1_len < n2_len) | ||
| 54 | + return NC_SORTS_BEFORE; | ||
| 55 | + if (n1_len > n2_len) | ||
| 56 | + return NC_SORTS_AFTER; | ||
| 57 | + return NC_EQUAL; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +/* Rich-compare DNS names. Example order/relationships: | ||
| 61 | + * z.x.a INCLUDED_BY x.a BEFORE y.a INCLUDED_BY a BEFORE x.b BEFORE y.b */ | ||
| 62 | +static enum name_constraint_relation compare_dns_names(const gnutls_datum_t *n1, | ||
| 63 | + const gnutls_datum_t *n2) | ||
| 64 | +{ | ||
| 65 | + enum name_constraint_relation rel; | ||
| 66 | + unsigned int i, j, i_end, j_end; | ||
| 67 | + | ||
| 68 | + /* start from the end of each name */ | ||
| 69 | + i = i_end = n1->size; | ||
| 70 | + j = j_end = n2->size; | ||
| 71 | + | ||
| 72 | + /* skip the trailing dots for the comparison */ | ||
| 73 | + while (i && n1->data[i - 1] == '.') | ||
| 74 | + i_end = i = i - 1; | ||
| 75 | + while (j && n2->data[j - 1] == '.') | ||
| 76 | + j_end = j = j - 1; | ||
| 77 | + | ||
| 78 | + while (1) { | ||
| 79 | + // rewind back to beginning or an after-dot position | ||
| 80 | + while (i && n1->data[i - 1] != '.') | ||
| 81 | + i--; | ||
| 82 | + while (j && n2->data[j - 1] != '.') | ||
| 83 | + j--; | ||
| 84 | + | ||
| 85 | + rel = compare_strings(&n1->data[i], i_end - i, &n2->data[j], | ||
| 86 | + j_end - j); | ||
| 87 | + if (rel == NC_SORTS_BEFORE) /* x.a BEFORE y.a */ | ||
| 88 | + return NC_SORTS_BEFORE; | ||
| 89 | + if (rel == NC_SORTS_AFTER) /* y.a AFTER x.a */ | ||
| 90 | + return NC_SORTS_AFTER; | ||
| 91 | + if (!i && j) /* x.a INCLUDES z.x.a */ | ||
| 92 | + return NC_INCLUDES; | ||
| 93 | + if (i && !j) /* z.x.a INCLUDED_BY x.a */ | ||
| 94 | + return NC_INCLUDED_BY; | ||
| 95 | + | ||
| 96 | + if (!i && !j) /* r == 0, we ran out of components to compare */ | ||
| 97 | + return NC_EQUAL; | ||
| 98 | + /* r == 0, i && j: step back past a dot and keep comparing */ | ||
| 99 | + i_end = i = i - 1; | ||
| 100 | + j_end = j = j - 1; | ||
| 101 | + | ||
| 102 | + /* support for non-standard ".gr INCLUDES example.gr" [1] */ | ||
| 103 | + if (!i && j) /* .a INCLUDES x.a */ | ||
| 104 | + return NC_INCLUDES; | ||
| 105 | + if (i && !j) /* x.a INCLUDED_BY .a */ | ||
| 106 | + return NC_INCLUDED_BY; | ||
| 107 | + } | ||
| 108 | +} | ||
| 109 | +/* [1] https://mailarchive.ietf.org/arch/msg/saag/Bw6PtreW0G7aEG7SikfzKHES4VA */ | ||
| 110 | + | ||
| 111 | +/* Rich-compare email name constraints. Example order/relationships: | ||
| 112 | + * z@x.a INCLUDED_BY x.a BEFORE y.a INCLUDED_BY a BEFORE x@b BEFORE y@b */ | ||
| 113 | +static enum name_constraint_relation compare_emails(const gnutls_datum_t *n1, | ||
| 114 | + const gnutls_datum_t *n2) | ||
| 115 | +{ | ||
| 116 | + enum name_constraint_relation domains_rel; | ||
| 117 | + unsigned int i, j, i_end, j_end; | ||
| 118 | + gnutls_datum_t d1, d2; /* borrow from n1 and n2 */ | ||
| 119 | + | ||
| 120 | + /* start from the end of each name */ | ||
| 121 | + i = i_end = n1->size; | ||
| 122 | + j = j_end = n2->size; | ||
| 123 | + | ||
| 124 | + /* rewind to @s to look for domains */ | ||
| 125 | + while (i && n1->data[i - 1] != '@') | ||
| 126 | + i--; | ||
| 127 | + d1.size = i_end - i; | ||
| 128 | + d1.data = &n1->data[i]; | ||
| 129 | + while (j && n2->data[j - 1] != '@') | ||
| 130 | + j--; | ||
| 131 | + d2.size = j_end - j; | ||
| 132 | + d2.data = &n2->data[j]; | ||
| 133 | + | ||
| 134 | + domains_rel = compare_dns_names(&d1, &d2); | ||
| 135 | + | ||
| 136 | + /* email constraint semantics differ from DNS | ||
| 137 | + * DNS: x.a INCLUDED_BY a | ||
| 138 | + * Email: x.a INCLUDED_BY .a BEFORE a */ | ||
| 139 | + if (domains_rel == NC_INCLUDED_BY || domains_rel == NC_INCLUDES) { | ||
| 140 | + bool d1_has_dot = (d1.size > 0 && d1.data[0] == '.'); | ||
| 141 | + bool d2_has_dot = (d2.size > 0 && d2.data[0] == '.'); | ||
| 142 | + /* a constraint without a dot is exact, excluding subdomains */ | ||
| 143 | + if (!d2_has_dot && domains_rel == NC_INCLUDED_BY) | ||
| 144 | + domains_rel = NC_SORTS_BEFORE; /* x.a BEFORE a */ | ||
| 145 | + if (!d1_has_dot && domains_rel == NC_INCLUDES) | ||
| 146 | + domains_rel = NC_SORTS_AFTER; /* a AFTER x.a */ | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + if (!i && !j) { /* both are domains-only */ | ||
| 150 | + return domains_rel; | ||
| 151 | + } else if (i && !j) { /* n1 is email, n2 is domain */ | ||
| 152 | + switch (domains_rel) { | ||
| 153 | + case NC_SORTS_AFTER: | ||
| 154 | + return NC_SORTS_AFTER; | ||
| 155 | + case NC_SORTS_BEFORE: | ||
| 156 | + return NC_SORTS_BEFORE; | ||
| 157 | + case NC_INCLUDES: /* n2 is more specific, a@x.a AFTER z.x.a */ | ||
| 158 | + return NC_SORTS_AFTER; | ||
| 159 | + case NC_EQUAL: /* subdomains match, z@x.a INCLUDED_BY x.a */ | ||
| 160 | + case NC_INCLUDED_BY: /* n1 is more specific */ | ||
| 161 | + return NC_INCLUDED_BY; | ||
| 162 | + } | ||
| 163 | + } else if (!i && j) { /* n1 is domain, n2 is email */ | ||
| 164 | + switch (domains_rel) { | ||
| 165 | + case NC_SORTS_AFTER: | ||
| 166 | + return NC_SORTS_AFTER; | ||
| 167 | + case NC_SORTS_BEFORE: | ||
| 168 | + return NC_SORTS_BEFORE; | ||
| 169 | + case NC_INCLUDES: /* n2 is more specific, a AFTER z@x.a */ | ||
| 170 | + return NC_SORTS_AFTER; | ||
| 171 | + case NC_EQUAL: /* subdomains match, x.a INCLUDES z@x.a */ | ||
| 172 | + return NC_INCLUDES; | ||
| 173 | + case NC_INCLUDED_BY: /* n1 is more specific, x.a BEFORE z@a */ | ||
| 174 | + return NC_SORTS_BEFORE; | ||
| 175 | + } | ||
| 176 | + } else if (i && j) { /* both are emails */ | ||
| 177 | + switch (domains_rel) { | ||
| 178 | + case NC_SORTS_AFTER: | ||
| 179 | + return NC_SORTS_AFTER; | ||
| 180 | + case NC_SORTS_BEFORE: | ||
| 181 | + return NC_SORTS_BEFORE; | ||
| 182 | + case NC_INCLUDES: // n2 is more specific | ||
| 183 | + return NC_SORTS_AFTER; | ||
| 184 | + case NC_INCLUDED_BY: // n1 is more specific | ||
| 185 | + return NC_SORTS_BEFORE; | ||
| 186 | + case NC_EQUAL: // only case when we need to look before the @ | ||
| 187 | + break; // see below for readability | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + /* i && j, both are emails, domain names match, compare up to @ */ | ||
| 192 | + return compare_strings(n1->data, i - 1, n2->data, j - 1); | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +/* Rich-compare IP address constraints. Example order/relationships: | ||
| 196 | + * 10.0.0.0/24 INCLUDED_BY 10.0.0.0/16 BEFORE 1::1/128 INCLUDED_BY 1::1/127 */ | ||
| 197 | +static enum name_constraint_relation compare_ip_ncs(const gnutls_datum_t *n1, | ||
| 198 | + const gnutls_datum_t *n2) | ||
| 199 | +{ | ||
| 200 | + unsigned int len, i; | ||
| 201 | + int r; | ||
| 202 | + const unsigned char *ip1, *ip2, *mask1, *mask2; | ||
| 203 | + unsigned char masked11[16], masked22[16], masked12[16], masked21[16]; | ||
| 204 | + | ||
| 205 | + if (n1->size < n2->size) | ||
| 206 | + return NC_SORTS_BEFORE; | ||
| 207 | + if (n1->size > n2->size) | ||
| 208 | + return NC_SORTS_AFTER; | ||
| 209 | + len = n1->size / 2; /* 4 for IPv4, 16 for IPv6 */ | ||
| 210 | + | ||
| 211 | + /* data is a concatenation of prefix and mask */ | ||
| 212 | + ip1 = n1->data; | ||
| 213 | + ip2 = n2->data; | ||
| 214 | + mask1 = n1->data + len; | ||
| 215 | + mask2 = n2->data + len; | ||
| 216 | + for (i = 0; i < len; i++) { | ||
| 217 | + masked11[i] = ip1[i] & mask1[i]; | ||
| 218 | + masked22[i] = ip2[i] & mask2[i]; | ||
| 219 | + masked12[i] = ip1[i] & mask2[i]; | ||
| 220 | + masked21[i] = ip2[i] & mask1[i]; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + r = memcmp(mask1, mask2, len); | ||
| 224 | + if (r < 0 && !memcmp(masked11, masked21, len)) /* prefix1 < prefix2 */ | ||
| 225 | + return NC_INCLUDES; /* ip1 & mask1 == ip2 & mask1 */ | ||
| 226 | + if (r > 0 && !memcmp(masked12, masked22, len)) /* prefix1 > prefix2 */ | ||
| 227 | + return NC_INCLUDED_BY; /* ip1 & mask2 == ip2 & mask2 */ | ||
| 228 | + | ||
| 229 | + r = memcmp(masked11, masked22, len); | ||
| 230 | + if (r < 0) | ||
| 231 | + return NC_SORTS_BEFORE; | ||
| 232 | + else if (r > 0) | ||
| 233 | + return NC_SORTS_AFTER; | ||
| 234 | + return NC_EQUAL; | ||
| 235 | +} | ||
| 236 | + | ||
| 237 | +static inline bool is_supported_type(unsigned type) | ||
| 238 | +{ | ||
| 239 | + return type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME || | ||
| 240 | + type == GNUTLS_SAN_IPADDRESS; | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +/* Universal comparison for name constraint nodes. | ||
| 244 | + * Unsupported types sort before supported types to allow early handling. | ||
| 245 | + * NULL represents end-of-list and sorts after everything else. */ | ||
| 246 | +static enum name_constraint_relation | ||
| 247 | +compare_name_constraint_nodes(const struct name_constraints_node_st *n1, | ||
| 248 | + const struct name_constraints_node_st *n2) | ||
| 249 | +{ | ||
| 250 | + bool n1_supported, n2_supported; | ||
| 251 | + | ||
| 252 | + if (!n1 && !n2) | ||
| 253 | + return NC_EQUAL; | ||
| 254 | + if (!n1) | ||
| 255 | + return NC_SORTS_AFTER; | ||
| 256 | + if (!n2) | ||
| 257 | + return NC_SORTS_BEFORE; | ||
| 258 | + | ||
| 259 | + n1_supported = is_supported_type(n1->type); | ||
| 260 | + n2_supported = is_supported_type(n2->type); | ||
| 261 | + | ||
| 262 | + /* unsupported types bubble up (sort first). intersect relies on this */ | ||
| 263 | + if (!n1_supported && n2_supported) | ||
| 264 | + return NC_SORTS_BEFORE; | ||
| 265 | + if (n1_supported && !n2_supported) | ||
| 266 | + return NC_SORTS_AFTER; | ||
| 267 | + | ||
| 268 | + /* next, sort by type */ | ||
| 269 | + if (n1->type < n2->type) | ||
| 270 | + return NC_SORTS_BEFORE; | ||
| 271 | + if (n1->type > n2->type) | ||
| 272 | + return NC_SORTS_AFTER; | ||
| 273 | + | ||
| 274 | + /* now look deeper */ | ||
| 275 | + switch (n1->type) { | ||
| 276 | + case GNUTLS_SAN_DNSNAME: | ||
| 277 | + return compare_dns_names(&n1->name, &n2->name); | ||
| 278 | + case GNUTLS_SAN_RFC822NAME: | ||
| 279 | + return compare_emails(&n1->name, &n2->name); | ||
| 280 | + case GNUTLS_SAN_IPADDRESS: | ||
| 281 | + return compare_ip_ncs(&n1->name, &n2->name); | ||
| 282 | + default: | ||
| 283 | + /* unsupported types: stable lexicographic order */ | ||
| 284 | + return compare_strings(n1->name.data, n1->name.size, | ||
| 285 | + n2->name.data, n2->name.size); | ||
| 286 | + } | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +/* qsort-compatible wrapper */ | ||
| 290 | +static int compare_name_constraint_nodes_qsort(const void *a, const void *b) | ||
| 291 | +{ | ||
| 292 | + const struct name_constraints_node_st *const *n1 = a; | ||
| 293 | + const struct name_constraints_node_st *const *n2 = b; | ||
| 294 | + enum name_constraint_relation rel; | ||
| 295 | + | ||
| 296 | + rel = compare_name_constraint_nodes(*n1, *n2); | ||
| 297 | + switch (rel) { | ||
| 298 | + case NC_SORTS_BEFORE: | ||
| 299 | + case NC_INCLUDED_BY: | ||
| 300 | + return -1; | ||
| 301 | + case NC_SORTS_AFTER: | ||
| 302 | + case NC_INCLUDES: | ||
| 303 | + return 1; | ||
| 304 | + case NC_EQUAL: | ||
| 305 | + default: | ||
| 306 | + return 0; | ||
| 307 | + } | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | static int | ||
| 311 | name_constraints_node_list_add(struct name_constraints_node_list_st *list, | ||
| 312 | struct name_constraints_node_st *node) | ||
| 313 | @@ -420,9 +699,7 @@ static int name_constraints_node_list_in | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | - if (found != NULL && (t->type == GNUTLS_SAN_DNSNAME || | ||
| 318 | - t->type == GNUTLS_SAN_RFC822NAME || | ||
| 319 | - t->type == GNUTLS_SAN_IPADDRESS)) { | ||
| 320 | + if (found != NULL && is_supported_type(t->type)) { | ||
| 321 | /* move node from PERMITTED to REMOVED */ | ||
| 322 | ret = name_constraints_node_list_add(&removed, t); | ||
| 323 | if (ret < 0) { | ||
| 324 | @@ -824,61 +1101,14 @@ cleanup: | ||
| 325 | return ret; | ||
| 326 | } | ||
| 327 | |||
| 328 | -static unsigned ends_with(const gnutls_datum_t *str, | ||
| 329 | - const gnutls_datum_t *suffix) | ||
| 330 | -{ | ||
| 331 | - unsigned char *tree; | ||
| 332 | - unsigned int treelen; | ||
| 333 | - | ||
| 334 | - if (suffix->size >= str->size) | ||
| 335 | - return 0; | ||
| 336 | - | ||
| 337 | - tree = suffix->data; | ||
| 338 | - treelen = suffix->size; | ||
| 339 | - if ((treelen > 0) && (tree[0] == '.')) { | ||
| 340 | - tree++; | ||
| 341 | - treelen--; | ||
| 342 | - } | ||
| 343 | - | ||
| 344 | - if (memcmp(str->data + str->size - treelen, tree, treelen) == 0 && | ||
| 345 | - str->data[str->size - treelen - 1] == '.') | ||
| 346 | - return 1; /* match */ | ||
| 347 | - | ||
| 348 | - return 0; | ||
| 349 | -} | ||
| 350 | - | ||
| 351 | -static unsigned email_ends_with(const gnutls_datum_t *str, | ||
| 352 | - const gnutls_datum_t *suffix) | ||
| 353 | -{ | ||
| 354 | - if (suffix->size >= str->size) { | ||
| 355 | - return 0; | ||
| 356 | - } | ||
| 357 | - | ||
| 358 | - if (suffix->size > 0 && memcmp(str->data + str->size - suffix->size, | ||
| 359 | - suffix->data, suffix->size) != 0) { | ||
| 360 | - return 0; | ||
| 361 | - } | ||
| 362 | - | ||
| 363 | - if (suffix->size > 1 && suffix->data[0] == '.') { /* .domain.com */ | ||
| 364 | - return 1; /* match */ | ||
| 365 | - } else if (str->data[str->size - suffix->size - 1] == '@') { | ||
| 366 | - return 1; /* match */ | ||
| 367 | - } | ||
| 368 | - | ||
| 369 | - return 0; | ||
| 370 | -} | ||
| 371 | - | ||
| 372 | static unsigned dnsname_matches(const gnutls_datum_t *name, | ||
| 373 | const gnutls_datum_t *suffix) | ||
| 374 | { | ||
| 375 | _gnutls_hard_log("matching %.*s with DNS constraint %.*s\n", name->size, | ||
| 376 | name->data, suffix->size, suffix->data); | ||
| 377 | |||
| 378 | - if (suffix->size == name->size && | ||
| 379 | - memcmp(suffix->data, name->data, suffix->size) == 0) | ||
| 380 | - return 1; /* match */ | ||
| 381 | - | ||
| 382 | - return ends_with(name, suffix); | ||
| 383 | + enum name_constraint_relation rel = compare_dns_names(name, suffix); | ||
| 384 | + return rel == NC_EQUAL || rel == NC_INCLUDED_BY; | ||
| 385 | } | ||
| 386 | |||
| 387 | static unsigned email_matches(const gnutls_datum_t *name, | ||
| 388 | @@ -887,11 +1117,8 @@ static unsigned email_matches(const gnut | ||
| 389 | _gnutls_hard_log("matching %.*s with e-mail constraint %.*s\n", | ||
| 390 | name->size, name->data, suffix->size, suffix->data); | ||
| 391 | |||
| 392 | - if (suffix->size == name->size && | ||
| 393 | - memcmp(suffix->data, name->data, suffix->size) == 0) | ||
| 394 | - return 1; /* match */ | ||
| 395 | - | ||
| 396 | - return email_ends_with(name, suffix); | ||
| 397 | + enum name_constraint_relation rel = compare_emails(name, suffix); | ||
| 398 | + return rel == NC_EQUAL || rel == NC_INCLUDED_BY; | ||
| 399 | } | ||
| 400 | |||
| 401 | /*- | ||
| 402 | @@ -915,8 +1142,7 @@ static int name_constraints_intersect_no | ||
| 403 | // presume empty intersection | ||
| 404 | struct name_constraints_node_st *intersection = NULL; | ||
| 405 | const struct name_constraints_node_st *to_copy = NULL; | ||
| 406 | - unsigned iplength = 0; | ||
| 407 | - unsigned byte; | ||
| 408 | + enum name_constraint_relation rel; | ||
| 409 | |||
| 410 | *_intersection = NULL; | ||
| 411 | |||
| 412 | @@ -925,32 +1151,49 @@ static int name_constraints_intersect_no | ||
| 413 | } | ||
| 414 | switch (node1->type) { | ||
| 415 | case GNUTLS_SAN_DNSNAME: | ||
| 416 | - if (!dnsname_matches(&node2->name, &node1->name)) | ||
| 417 | + rel = compare_dns_names(&node1->name, &node2->name); | ||
| 418 | + switch (rel) { | ||
| 419 | + case NC_EQUAL: // equal means doesn't matter which one | ||
| 420 | + case NC_INCLUDES: // node2 is more specific | ||
| 421 | + to_copy = node2; | ||
| 422 | + break; | ||
| 423 | + case NC_INCLUDED_BY: // node1 is more specific | ||
| 424 | + to_copy = node1; | ||
| 425 | + break; | ||
| 426 | + case NC_SORTS_BEFORE: // no intersection | ||
| 427 | + case NC_SORTS_AFTER: // no intersection | ||
| 428 | return GNUTLS_E_SUCCESS; | ||
| 429 | - to_copy = node2; | ||
| 430 | + } | ||
| 431 | break; | ||
| 432 | case GNUTLS_SAN_RFC822NAME: | ||
| 433 | - if (!email_matches(&node2->name, &node1->name)) | ||
| 434 | + rel = compare_emails(&node1->name, &node2->name); | ||
| 435 | + switch (rel) { | ||
| 436 | + case NC_EQUAL: // equal means doesn't matter which one | ||
| 437 | + case NC_INCLUDES: // node2 is more specific | ||
| 438 | + to_copy = node2; | ||
| 439 | + break; | ||
| 440 | + case NC_INCLUDED_BY: // node1 is more specific | ||
| 441 | + to_copy = node1; | ||
| 442 | + break; | ||
| 443 | + case NC_SORTS_BEFORE: // no intersection | ||
| 444 | + case NC_SORTS_AFTER: // no intersection | ||
| 445 | return GNUTLS_E_SUCCESS; | ||
| 446 | - to_copy = node2; | ||
| 447 | + } | ||
| 448 | break; | ||
| 449 | case GNUTLS_SAN_IPADDRESS: | ||
| 450 | - if (node1->name.size != node2->name.size) | ||
| 451 | + rel = compare_ip_ncs(&node1->name, &node2->name); | ||
| 452 | + switch (rel) { | ||
| 453 | + case NC_EQUAL: // equal means doesn't matter which one | ||
| 454 | + case NC_INCLUDES: // node2 is more specific | ||
| 455 | + to_copy = node2; | ||
| 456 | + break; | ||
| 457 | + case NC_INCLUDED_BY: // node1 is more specific | ||
| 458 | + to_copy = node1; | ||
| 459 | + break; | ||
| 460 | + case NC_SORTS_BEFORE: // no intersection | ||
| 461 | + case NC_SORTS_AFTER: // no intersection | ||
| 462 | return GNUTLS_E_SUCCESS; | ||
| 463 | - iplength = node1->name.size / 2; | ||
| 464 | - for (byte = 0; byte < iplength; byte++) { | ||
| 465 | - if (((node1->name.data[byte] ^ | ||
| 466 | - node2->name.data[byte]) // XOR of addresses | ||
| 467 | - & node1->name.data[byte + | ||
| 468 | - iplength] // AND mask from nc1 | ||
| 469 | - & node2->name.data[byte + | ||
| 470 | - iplength]) // AND mask from nc2 | ||
| 471 | - != 0) { | ||
| 472 | - // CIDRS do not intersect | ||
| 473 | - return GNUTLS_E_SUCCESS; | ||
| 474 | - } | ||
| 475 | } | ||
| 476 | - to_copy = node2; | ||
| 477 | break; | ||
| 478 | default: | ||
| 479 | // for other types, we don't know how to do the intersection, assume empty | ||
| 480 | @@ -967,20 +1210,6 @@ static int name_constraints_intersect_no | ||
| 481 | intersection = *_intersection; | ||
| 482 | |||
| 483 | assert(intersection->name.data != NULL); | ||
| 484 | - | ||
| 485 | - if (intersection->type == GNUTLS_SAN_IPADDRESS) { | ||
| 486 | - // make sure both IP addresses are correctly masked | ||
| 487 | - _gnutls_mask_ip(intersection->name.data, | ||
| 488 | - intersection->name.data + iplength, | ||
| 489 | - iplength); | ||
| 490 | - _gnutls_mask_ip(node1->name.data, | ||
| 491 | - node1->name.data + iplength, iplength); | ||
| 492 | - // update intersection, if necessary (we already know one is subset of other) | ||
| 493 | - for (byte = 0; byte < 2 * iplength; byte++) { | ||
| 494 | - intersection->name.data[byte] |= | ||
| 495 | - node1->name.data[byte]; | ||
| 496 | - } | ||
| 497 | - } | ||
| 498 | } | ||
| 499 | |||
| 500 | return GNUTLS_E_SUCCESS; | ||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-6.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-6.patch new file mode 100644 index 0000000000..6dc599dd9f --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-6.patch | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | From bc62fbb946085527b4b1c02f337dd10c68c54690 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Wed, 4 Feb 2026 09:09:46 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: add sorted_view in preparation... | ||
| 5 | |||
| 6 | ... for actually using it later for performance gains. | ||
| 7 | |||
| 8 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 9 | |||
| 10 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/bc62fbb946085527b4b1c02f337dd10c68c54690] | ||
| 11 | CVE: CVE-2025-14831 | ||
| 12 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 13 | --- | ||
| 14 | lib/x509/name_constraints.c | 62 ++++++++++++++++++++++++++++++------- | ||
| 15 | 1 file changed, 51 insertions(+), 11 deletions(-) | ||
| 16 | |||
| 17 | --- a/lib/x509/name_constraints.c | ||
| 18 | +++ b/lib/x509/name_constraints.c | ||
| 19 | @@ -54,6 +54,9 @@ struct name_constraints_node_list_st { | ||
| 20 | struct name_constraints_node_st **data; | ||
| 21 | size_t size; | ||
| 22 | size_t capacity; | ||
| 23 | + /* sorted-on-demand view, valid only when dirty == false */ | ||
| 24 | + bool dirty; | ||
| 25 | + struct name_constraints_node_st **sorted_view; | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct gnutls_name_constraints_st { | ||
| 29 | @@ -342,6 +345,37 @@ static int compare_name_constraint_nodes | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | +/* Bring the sorted view up to date with the list data; clear the dirty flag. */ | ||
| 34 | +static int ensure_sorted(struct name_constraints_node_list_st *list) | ||
| 35 | +{ | ||
| 36 | + struct name_constraints_node_st **new_data; | ||
| 37 | + | ||
| 38 | + if (!list->dirty) | ||
| 39 | + return GNUTLS_E_SUCCESS; | ||
| 40 | + if (!list->size) { | ||
| 41 | + list->dirty = false; | ||
| 42 | + return GNUTLS_E_SUCCESS; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + /* reallocate sorted view to match current size */ | ||
| 46 | + new_data = | ||
| 47 | + _gnutls_reallocarray(list->sorted_view, list->size, | ||
| 48 | + sizeof(struct name_constraints_node_st *)); | ||
| 49 | + if (!new_data) | ||
| 50 | + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); | ||
| 51 | + list->sorted_view = new_data; | ||
| 52 | + | ||
| 53 | + /* copy pointers and sort in-place */ | ||
| 54 | + memcpy(list->sorted_view, list->data, | ||
| 55 | + list->size * sizeof(struct name_constraints_node_st *)); | ||
| 56 | + qsort(list->sorted_view, list->size, | ||
| 57 | + sizeof(struct name_constraints_node_st *), | ||
| 58 | + compare_name_constraint_nodes_qsort); | ||
| 59 | + | ||
| 60 | + list->dirty = false; | ||
| 61 | + return GNUTLS_E_SUCCESS; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | static int | ||
| 65 | name_constraints_node_list_add(struct name_constraints_node_list_st *list, | ||
| 66 | struct name_constraints_node_st *node) | ||
| 67 | @@ -361,10 +395,23 @@ name_constraints_node_list_add(struct na | ||
| 68 | list->capacity = new_capacity; | ||
| 69 | list->data = new_data; | ||
| 70 | } | ||
| 71 | + list->dirty = true; | ||
| 72 | list->data[list->size++] = node; | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | +static void | ||
| 77 | +name_constraints_node_list_clear(struct name_constraints_node_list_st *list) | ||
| 78 | +{ | ||
| 79 | + gnutls_free(list->data); | ||
| 80 | + gnutls_free(list->sorted_view); | ||
| 81 | + list->data = NULL; | ||
| 82 | + list->sorted_view = NULL; | ||
| 83 | + list->capacity = 0; | ||
| 84 | + list->size = 0; | ||
| 85 | + list->dirty = false; | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | static int | ||
| 89 | name_constraints_node_add_new(gnutls_x509_name_constraints_t nc, | ||
| 90 | struct name_constraints_node_list_st *list, | ||
| 91 | @@ -711,6 +758,7 @@ static int name_constraints_node_list_in | ||
| 92 | permitted->data[i] = | ||
| 93 | permitted->data[permitted->size - 1]; | ||
| 94 | permitted->size--; | ||
| 95 | + permitted->dirty = true; | ||
| 96 | continue; | ||
| 97 | } | ||
| 98 | i++; | ||
| 99 | @@ -905,17 +953,9 @@ void _gnutls_x509_name_constraints_clear | ||
| 100 | struct name_constraints_node_st *node = nc->nodes.data[i]; | ||
| 101 | name_constraints_node_free(node); | ||
| 102 | } | ||
| 103 | - gnutls_free(nc->nodes.data); | ||
| 104 | - nc->nodes.capacity = 0; | ||
| 105 | - nc->nodes.size = 0; | ||
| 106 | - | ||
| 107 | - gnutls_free(nc->permitted.data); | ||
| 108 | - nc->permitted.capacity = 0; | ||
| 109 | - nc->permitted.size = 0; | ||
| 110 | - | ||
| 111 | - gnutls_free(nc->excluded.data); | ||
| 112 | - nc->excluded.capacity = 0; | ||
| 113 | - nc->excluded.size = 0; | ||
| 114 | + name_constraints_node_list_clear(&nc->nodes); | ||
| 115 | + name_constraints_node_list_clear(&nc->permitted); | ||
| 116 | + name_constraints_node_list_clear(&nc->excluded); | ||
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-7.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-7.patch new file mode 100644 index 0000000000..846862007f --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-7.patch | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | From 80db5e90fa18d3e34bb91dd027bdf76d31e93dcd Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Wed, 4 Feb 2026 13:30:08 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: implement | ||
| 5 | name_constraints_node_list_union | ||
| 6 | |||
| 7 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 8 | |||
| 9 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/80db5e90fa18d3e34bb91dd027bdf76d31e93dcd] | ||
| 10 | CVE: CVE-2025-14831 | ||
| 11 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 12 | --- | ||
| 13 | lib/x509/name_constraints.c | 98 ++++++++++++++++++++++++++++++++----- | ||
| 14 | 1 file changed, 86 insertions(+), 12 deletions(-) | ||
| 15 | |||
| 16 | --- a/lib/x509/name_constraints.c | ||
| 17 | +++ b/lib/x509/name_constraints.c | ||
| 18 | @@ -41,6 +41,7 @@ | ||
| 19 | #include "intprops.h" | ||
| 20 | #include "minmax.h" | ||
| 21 | |||
| 22 | +#include <assert.h> | ||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | #define MAX_NC_CHECKS (1 << 20) | ||
| 26 | @@ -867,22 +868,95 @@ cleanup: | ||
| 27 | return ret; | ||
| 28 | } | ||
| 29 | |||
| 30 | -static int name_constraints_node_list_concat( | ||
| 31 | - gnutls_x509_name_constraints_t nc, | ||
| 32 | - struct name_constraints_node_list_st *nodes, | ||
| 33 | - const struct name_constraints_node_list_st *nodes2) | ||
| 34 | +static int | ||
| 35 | +name_constraints_node_list_union(gnutls_x509_name_constraints_t nc, | ||
| 36 | + struct name_constraints_node_list_st *nodes, | ||
| 37 | + struct name_constraints_node_list_st *nodes2) | ||
| 38 | { | ||
| 39 | int ret; | ||
| 40 | + size_t i = 0, j = 0; | ||
| 41 | + struct name_constraints_node_st *nc1; | ||
| 42 | + const struct name_constraints_node_st *nc2; | ||
| 43 | + enum name_constraint_relation rel; | ||
| 44 | + struct name_constraints_node_list_st result = { 0 }; | ||
| 45 | + | ||
| 46 | + if (nodes2->size == 0) /* nothing to do */ | ||
| 47 | + return GNUTLS_E_SUCCESS; | ||
| 48 | + | ||
| 49 | + ret = ensure_sorted(nodes); | ||
| 50 | + if (ret < 0) { | ||
| 51 | + gnutls_assert(); | ||
| 52 | + goto cleanup; | ||
| 53 | + } | ||
| 54 | + ret = ensure_sorted(nodes2); | ||
| 55 | + if (ret < 0) { | ||
| 56 | + gnutls_assert(); | ||
| 57 | + goto cleanup; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /* traverse both lists in a single pass and merge them w/o duplicates */ | ||
| 61 | + while (i < nodes->size || j < nodes2->size) { | ||
| 62 | + nc1 = (i < nodes->size) ? nodes->sorted_view[i] : NULL; | ||
| 63 | + nc2 = (j < nodes2->size) ? nodes2->sorted_view[j] : NULL; | ||
| 64 | |||
| 65 | - for (size_t i = 0; i < nodes2->size; i++) { | ||
| 66 | - ret = name_constraints_node_add_copy(nc, nodes, | ||
| 67 | - nodes2->data[i]); | ||
| 68 | + rel = compare_name_constraint_nodes(nc1, nc2); | ||
| 69 | + switch (rel) { | ||
| 70 | + case NC_SORTS_BEFORE: | ||
| 71 | + assert(nc1 != NULL); /* comparator-guaranteed */ | ||
| 72 | + ret = name_constraints_node_list_add(&result, nc1); | ||
| 73 | + i++; | ||
| 74 | + break; | ||
| 75 | + case NC_SORTS_AFTER: | ||
| 76 | + assert(nc2 != NULL); /* comparator-guaranteed */ | ||
| 77 | + ret = name_constraints_node_add_copy(nc, &result, nc2); | ||
| 78 | + j++; | ||
| 79 | + break; | ||
| 80 | + case NC_INCLUDES: /* nc1 is broader, shallow-copy it */ | ||
| 81 | + assert(nc1 != NULL && nc2 != NULL); /* comparator */ | ||
| 82 | + ret = name_constraints_node_list_add(&result, nc1); | ||
| 83 | + i++; | ||
| 84 | + j++; | ||
| 85 | + break; | ||
| 86 | + case NC_INCLUDED_BY: /* nc2 is broader, deep-copy it */ | ||
| 87 | + assert(nc1 != NULL && nc2 != NULL); /* comparator */ | ||
| 88 | + ret = name_constraints_node_add_copy(nc, &result, nc2); | ||
| 89 | + i++; | ||
| 90 | + j++; | ||
| 91 | + break; | ||
| 92 | + case NC_EQUAL: | ||
| 93 | + assert(nc1 != NULL && nc2 != NULL); /* loop condition */ | ||
| 94 | + ret = name_constraints_node_list_add(&result, nc1); | ||
| 95 | + i++; | ||
| 96 | + j++; | ||
| 97 | + break; | ||
| 98 | + } | ||
| 99 | if (ret < 0) { | ||
| 100 | - return gnutls_assert_val(ret); | ||
| 101 | + gnutls_assert(); | ||
| 102 | + goto cleanup; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | - return 0; | ||
| 107 | + gnutls_free(nodes->data); | ||
| 108 | + gnutls_free(nodes->sorted_view); | ||
| 109 | + nodes->data = result.data; | ||
| 110 | + nodes->sorted_view = NULL; | ||
| 111 | + nodes->size = result.size; | ||
| 112 | + nodes->capacity = result.capacity; | ||
| 113 | + nodes->dirty = true; | ||
| 114 | + /* since we know it's sorted, populate sorted_view almost for free */ | ||
| 115 | + nodes->sorted_view = gnutls_calloc( | ||
| 116 | + nodes->size, sizeof(struct name_constraints_node_st *)); | ||
| 117 | + if (!nodes->sorted_view) | ||
| 118 | + return GNUTLS_E_SUCCESS; /* we tried, no harm done */ | ||
| 119 | + memcpy(nodes->sorted_view, nodes->data, | ||
| 120 | + nodes->size * sizeof(struct name_constraints_node_st *)); | ||
| 121 | + nodes->dirty = false; | ||
| 122 | + | ||
| 123 | + result.data = NULL; | ||
| 124 | + return GNUTLS_E_SUCCESS; | ||
| 125 | +cleanup: | ||
| 126 | + name_constraints_node_list_clear(&result); | ||
| 127 | + return gnutls_assert_val(ret); | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | @@ -1023,7 +1097,7 @@ static int name_constraints_add(gnutls_x | ||
| 132 | * @nc2: The name constraints to be merged with | ||
| 133 | * | ||
| 134 | * This function will merge the provided name constraints structures | ||
| 135 | - * as per RFC5280 p6.1.4. That is, the excluded constraints will be appended, | ||
| 136 | + * as per RFC5280 p6.1.4. That is, the excluded constraints will be unioned, | ||
| 137 | * and permitted will be intersected. The intersection assumes that @nc | ||
| 138 | * is the root CA constraints. | ||
| 139 | * | ||
| 140 | @@ -1045,8 +1119,8 @@ int _gnutls_x509_name_constraints_merge( | ||
| 141 | return ret; | ||
| 142 | } | ||
| 143 | |||
| 144 | - ret = name_constraints_node_list_concat(nc, &nc->excluded, | ||
| 145 | - &nc2->excluded); | ||
| 146 | + ret = name_constraints_node_list_union(nc, &nc->excluded, | ||
| 147 | + &nc2->excluded); | ||
| 148 | if (ret < 0) { | ||
| 149 | gnutls_assert(); | ||
| 150 | return ret; | ||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-8.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-8.patch new file mode 100644 index 0000000000..9beca76a35 --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-8.patch | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | From d0ac999620c8c0aeb6939e1e92d884ca8e40b759 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Wed, 4 Feb 2026 18:31:37 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: make types_with_empty_intersection a | ||
| 5 | bitmask | ||
| 6 | |||
| 7 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 8 | |||
| 9 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/d0ac999620c8c0aeb6939e1e92d884ca8e40b759] | ||
| 10 | CVE: CVE-2025-14831 | ||
| 11 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 12 | --- | ||
| 13 | lib/x509/name_constraints.c | 39 +++++++++++++++++++++++++++---------- | ||
| 14 | 1 file changed, 29 insertions(+), 10 deletions(-) | ||
| 15 | |||
| 16 | --- a/lib/x509/name_constraints.c | ||
| 17 | +++ b/lib/x509/name_constraints.c | ||
| 18 | @@ -275,6 +275,7 @@ static enum name_constraint_relation com | ||
| 19 | |||
| 20 | static inline bool is_supported_type(unsigned type) | ||
| 21 | { | ||
| 22 | + /* all of these should be under GNUTLS_SAN_MAX (intersect bitmasks) */ | ||
| 23 | return type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME || | ||
| 24 | type == GNUTLS_SAN_IPADDRESS; | ||
| 25 | } | ||
| 26 | @@ -683,6 +684,21 @@ name_constraints_node_new(gnutls_x509_na | ||
| 27 | return tmp; | ||
| 28 | } | ||
| 29 | |||
| 30 | +static int | ||
| 31 | +name_constraints_node_list_union(gnutls_x509_name_constraints_t nc, | ||
| 32 | + struct name_constraints_node_list_st *nodes, | ||
| 33 | + struct name_constraints_node_list_st *nodes2); | ||
| 34 | + | ||
| 35 | +#define type_bitmask_t uint8_t /* increase if GNUTLS_SAN_MAX grows */ | ||
| 36 | +#define type_bitmask_set(mask, t) ((mask) |= (1u << (t))) | ||
| 37 | +#define type_bitmask_clr(mask, t) ((mask) &= ~(1u << (t))) | ||
| 38 | +#define type_bitmask_in(mask, t) ((mask) & (1u << (t))) | ||
| 39 | +/* C99-compatible compile-time assertions; gnutls_int.h undefines verify */ | ||
| 40 | +typedef char assert_san_max[(GNUTLS_SAN_MAX < 8) ? 1 : -1]; | ||
| 41 | +typedef char assert_dnsname[(GNUTLS_SAN_DNSNAME <= GNUTLS_SAN_MAX) ? 1 : -1]; | ||
| 42 | +typedef char assert_rfc822[(GNUTLS_SAN_RFC822NAME <= GNUTLS_SAN_MAX) ? 1 : -1]; | ||
| 43 | +typedef char assert_ipaddr[(GNUTLS_SAN_IPADDRESS <= GNUTLS_SAN_MAX) ? 1 : -1]; | ||
| 44 | + | ||
| 45 | /*- | ||
| 46 | * @brief name_constraints_node_list_intersect: | ||
| 47 | * @nc: %gnutls_x509_name_constraints_t | ||
| 48 | @@ -710,12 +726,9 @@ static int name_constraints_node_list_in | ||
| 49 | .capacity = 0 }; | ||
| 50 | static const unsigned char universal_ip[32] = { 0 }; | ||
| 51 | |||
| 52 | - /* temporary array to see, if we need to add universal excluded constraints | ||
| 53 | - * (see phase 3 for details) | ||
| 54 | - * indexed directly by (gnutls_x509_subject_alt_name_t enum - 1) */ | ||
| 55 | - unsigned char types_with_empty_intersection[GNUTLS_SAN_MAX]; | ||
| 56 | - memset(types_with_empty_intersection, 0, | ||
| 57 | - sizeof(types_with_empty_intersection)); | ||
| 58 | + /* bitmask to see if we need to add universal excluded constraints | ||
| 59 | + * (see phase 3 for details) */ | ||
| 60 | + type_bitmask_t types_with_empty_intersection = 0; | ||
| 61 | |||
| 62 | if (permitted->size == 0 || permitted2->size == 0) | ||
| 63 | return 0; | ||
| 64 | @@ -741,7 +754,8 @@ static int name_constraints_node_list_in | ||
| 65 | // note the possibility of empty intersection for this type | ||
| 66 | // if we add something to the intersection in phase 2, | ||
| 67 | // we will reset this flag back to 0 then | ||
| 68 | - types_with_empty_intersection[t->type - 1] = 1; | ||
| 69 | + type_bitmask_set(types_with_empty_intersection, | ||
| 70 | + t->type); | ||
| 71 | found = t2; | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | @@ -795,8 +809,8 @@ static int name_constraints_node_list_in | ||
| 75 | GNUTLS_E_INTERNAL_ERROR); | ||
| 76 | } | ||
| 77 | // we will not add universal excluded constraint for this type | ||
| 78 | - types_with_empty_intersection[tmp->type - 1] = | ||
| 79 | - 0; | ||
| 80 | + type_bitmask_clr(types_with_empty_intersection, | ||
| 81 | + tmp->type); | ||
| 82 | // add intersection node to PERMITTED | ||
| 83 | ret = name_constraints_node_list_add(permitted, | ||
| 84 | tmp); | ||
| 85 | @@ -824,7 +838,7 @@ static int name_constraints_node_list_in | ||
| 86 | * excluded constraint with universal wildcard | ||
| 87 | * (since the intersection of permitted is now empty). */ | ||
| 88 | for (type = 1; type <= GNUTLS_SAN_MAX; type++) { | ||
| 89 | - if (types_with_empty_intersection[type - 1] == 0) | ||
| 90 | + if (!type_bitmask_in(types_with_empty_intersection, type)) | ||
| 91 | continue; | ||
| 92 | _gnutls_hard_log( | ||
| 93 | "Adding universal excluded name constraint for type %d.\n", | ||
| 94 | @@ -868,6 +882,11 @@ cleanup: | ||
| 95 | return ret; | ||
| 96 | } | ||
| 97 | |||
| 98 | +#undef type_bitmask_t | ||
| 99 | +#undef type_bitmask_set | ||
| 100 | +#undef type_bitmask_clr | ||
| 101 | +#undef type_bitmask_in | ||
| 102 | + | ||
| 103 | static int | ||
| 104 | name_constraints_node_list_union(gnutls_x509_name_constraints_t nc, | ||
| 105 | struct name_constraints_node_list_st *nodes, | ||
diff --git a/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-9.patch b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-9.patch new file mode 100644 index 0000000000..eefc1b76e0 --- /dev/null +++ b/meta/recipes-support/gnutls/gnutls/CVE-2025-14831-9.patch | |||
| @@ -0,0 +1,421 @@ | |||
| 1 | From d6054f0016db05fb5c82177ddbd0a4e8331059a1 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 3 | Date: Wed, 4 Feb 2026 20:03:49 +0100 | ||
| 4 | Subject: [PATCH] x509/name_constraints: name_constraints_node_list_intersect | ||
| 5 | over sorted | ||
| 6 | |||
| 7 | Fixes: #1773 | ||
| 8 | Fixes: GNUTLS-SA-2026-02-09-2 | ||
| 9 | Fixes: CVE-2025-14831 | ||
| 10 | |||
| 11 | Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com> | ||
| 12 | |||
| 13 | Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/d6054f0016db05fb5c82177ddbd0a4e8331059a1] | ||
| 14 | CVE: CVE-2025-14831 | ||
| 15 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 16 | --- | ||
| 17 | lib/x509/name_constraints.c | 347 ++++++++++++++---------------------- | ||
| 18 | 1 file changed, 135 insertions(+), 212 deletions(-) | ||
| 19 | |||
| 20 | diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c | ||
| 21 | index d1006eb..04722bd 100644 | ||
| 22 | --- a/lib/x509/name_constraints.c | ||
| 23 | +++ b/lib/x509/name_constraints.c | ||
| 24 | @@ -446,13 +446,6 @@ name_constraints_node_add_copy(gnutls_x509_name_constraints_t nc, | ||
| 25 | src->name.data, src->name.size); | ||
| 26 | } | ||
| 27 | |||
| 28 | -// for documentation see the implementation | ||
| 29 | -static int name_constraints_intersect_nodes( | ||
| 30 | - gnutls_x509_name_constraints_t nc, | ||
| 31 | - const struct name_constraints_node_st *node1, | ||
| 32 | - const struct name_constraints_node_st *node2, | ||
| 33 | - struct name_constraints_node_st **intersection); | ||
| 34 | - | ||
| 35 | /*- | ||
| 36 | * _gnutls_x509_name_constraints_is_empty: | ||
| 37 | * @nc: name constraints structure | ||
| 38 | @@ -716,129 +709,143 @@ typedef char assert_ipaddr[(GNUTLS_SAN_IPADDRESS <= GNUTLS_SAN_MAX) ? 1 : -1]; | ||
| 39 | static int name_constraints_node_list_intersect( | ||
| 40 | gnutls_x509_name_constraints_t nc, | ||
| 41 | struct name_constraints_node_list_st *permitted, | ||
| 42 | - const struct name_constraints_node_list_st *permitted2, | ||
| 43 | + struct name_constraints_node_list_st *permitted2, | ||
| 44 | struct name_constraints_node_list_st *excluded) | ||
| 45 | { | ||
| 46 | - struct name_constraints_node_st *tmp; | ||
| 47 | - int ret, type, used; | ||
| 48 | - struct name_constraints_node_list_st removed = { .data = NULL, | ||
| 49 | - .size = 0, | ||
| 50 | - .capacity = 0 }; | ||
| 51 | + struct name_constraints_node_st *nc1, *nc2; | ||
| 52 | + struct name_constraints_node_list_st result = { 0 }; | ||
| 53 | + struct name_constraints_node_list_st unsupp2 = { 0 }; | ||
| 54 | + enum name_constraint_relation rel; | ||
| 55 | + unsigned type; | ||
| 56 | + int ret = GNUTLS_E_SUCCESS; | ||
| 57 | + size_t i, j, p1_unsupp = 0, p2_unsupp = 0; | ||
| 58 | + type_bitmask_t universal_exclude_needed = 0; | ||
| 59 | + type_bitmask_t types_in_p1 = 0, types_in_p2 = 0; | ||
| 60 | static const unsigned char universal_ip[32] = { 0 }; | ||
| 61 | |||
| 62 | - /* bitmask to see if we need to add universal excluded constraints | ||
| 63 | - * (see phase 3 for details) */ | ||
| 64 | - type_bitmask_t types_with_empty_intersection = 0; | ||
| 65 | - | ||
| 66 | if (permitted->size == 0 || permitted2->size == 0) | ||
| 67 | - return 0; | ||
| 68 | + return GNUTLS_E_SUCCESS; | ||
| 69 | |||
| 70 | - /* Phase 1 | ||
| 71 | - * For each name in PERMITTED, if a PERMITTED2 does not contain a name | ||
| 72 | - * with the same type, move the original name to REMOVED. | ||
| 73 | - * Do this also for node of unknown type (not DNS, email, IP) */ | ||
| 74 | - for (size_t i = 0; i < permitted->size;) { | ||
| 75 | - struct name_constraints_node_st *t = permitted->data[i]; | ||
| 76 | - const struct name_constraints_node_st *found = NULL; | ||
| 77 | - | ||
| 78 | - for (size_t j = 0; j < permitted2->size; j++) { | ||
| 79 | - const struct name_constraints_node_st *t2 = | ||
| 80 | - permitted2->data[j]; | ||
| 81 | - if (t->type == t2->type) { | ||
| 82 | - // check bounds (we will use 't->type' as index) | ||
| 83 | - if (t->type > GNUTLS_SAN_MAX || t->type == 0) { | ||
| 84 | - gnutls_assert(); | ||
| 85 | - ret = GNUTLS_E_INTERNAL_ERROR; | ||
| 86 | - goto cleanup; | ||
| 87 | - } | ||
| 88 | - // note the possibility of empty intersection for this type | ||
| 89 | - // if we add something to the intersection in phase 2, | ||
| 90 | - // we will reset this flag back to 0 then | ||
| 91 | - type_bitmask_set(types_with_empty_intersection, | ||
| 92 | - t->type); | ||
| 93 | - found = t2; | ||
| 94 | - break; | ||
| 95 | - } | ||
| 96 | + /* make sorted views of the arrays */ | ||
| 97 | + ret = ensure_sorted(permitted); | ||
| 98 | + if (ret < 0) { | ||
| 99 | + gnutls_assert(); | ||
| 100 | + goto cleanup; | ||
| 101 | + } | ||
| 102 | + ret = ensure_sorted(permitted2); | ||
| 103 | + if (ret < 0) { | ||
| 104 | + gnutls_assert(); | ||
| 105 | + goto cleanup; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /* deal with the leading unsupported types first: count, then union */ | ||
| 109 | + while (p1_unsupp < permitted->size && | ||
| 110 | + !is_supported_type(permitted->sorted_view[p1_unsupp]->type)) | ||
| 111 | + p1_unsupp++; | ||
| 112 | + while (p2_unsupp < permitted2->size && | ||
| 113 | + !is_supported_type(permitted2->sorted_view[p2_unsupp]->type)) | ||
| 114 | + p2_unsupp++; | ||
| 115 | + if (p1_unsupp) { /* copy p1 unsupported type pointers into result */ | ||
| 116 | + result.data = gnutls_calloc( | ||
| 117 | + p1_unsupp, sizeof(struct name_constraints_node_st *)); | ||
| 118 | + if (!result.data) { | ||
| 119 | + ret = GNUTLS_E_MEMORY_ERROR; | ||
| 120 | + gnutls_assert(); | ||
| 121 | + goto cleanup; | ||
| 122 | } | ||
| 123 | + memcpy(result.data, permitted->sorted_view, | ||
| 124 | + p1_unsupp * sizeof(struct name_constraints_node_st *)); | ||
| 125 | + result.size = result.capacity = p1_unsupp; | ||
| 126 | + result.dirty = true; | ||
| 127 | + } | ||
| 128 | + if (p2_unsupp) { /* union will make deep copies from p2 */ | ||
| 129 | + unsupp2.data = permitted2->sorted_view; /* so, just alias */ | ||
| 130 | + unsupp2.size = unsupp2.capacity = p2_unsupp; | ||
| 131 | + unsupp2.dirty = false; /* we know it's sorted */ | ||
| 132 | + unsupp2.sorted_view = permitted2->sorted_view; | ||
| 133 | + ret = name_constraints_node_list_union(nc, &result, &unsupp2); | ||
| 134 | + if (ret < 0) { | ||
| 135 | + gnutls_assert(); | ||
| 136 | + goto cleanup; | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | |||
| 140 | - if (found != NULL && is_supported_type(t->type)) { | ||
| 141 | - /* move node from PERMITTED to REMOVED */ | ||
| 142 | - ret = name_constraints_node_list_add(&removed, t); | ||
| 143 | - if (ret < 0) { | ||
| 144 | - gnutls_assert(); | ||
| 145 | - goto cleanup; | ||
| 146 | - } | ||
| 147 | - /* remove node by swapping */ | ||
| 148 | - if (i < permitted->size - 1) | ||
| 149 | - permitted->data[i] = | ||
| 150 | - permitted->data[permitted->size - 1]; | ||
| 151 | - permitted->size--; | ||
| 152 | - permitted->dirty = true; | ||
| 153 | - continue; | ||
| 154 | + /* with that out of the way, pre-compute the supported types we have */ | ||
| 155 | + for (i = p1_unsupp; i < permitted->size; i++) { | ||
| 156 | + type = permitted->sorted_view[i]->type; | ||
| 157 | + if (type < 1 || type > GNUTLS_SAN_MAX) { | ||
| 158 | + ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); | ||
| 159 | + goto cleanup; | ||
| 160 | + } | ||
| 161 | + type_bitmask_set(types_in_p1, type); | ||
| 162 | + } | ||
| 163 | + for (j = p2_unsupp; j < permitted2->size; j++) { | ||
| 164 | + type = permitted2->sorted_view[j]->type; | ||
| 165 | + if (type < 1 || type > GNUTLS_SAN_MAX) { | ||
| 166 | + ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); | ||
| 167 | + goto cleanup; | ||
| 168 | } | ||
| 169 | - i++; | ||
| 170 | + type_bitmask_set(types_in_p2, type); | ||
| 171 | } | ||
| 172 | + /* universal excludes might be needed for types intersecting to empty */ | ||
| 173 | + universal_exclude_needed = types_in_p1 & types_in_p2; | ||
| 174 | + | ||
| 175 | + /* go through supported type NCs and intersect in a single pass */ | ||
| 176 | + i = p1_unsupp; | ||
| 177 | + j = p2_unsupp; | ||
| 178 | + while (i < permitted->size || j < permitted2->size) { | ||
| 179 | + nc1 = (i < permitted->size) ? permitted->sorted_view[i] : NULL; | ||
| 180 | + nc2 = (j < permitted2->size) ? permitted2->sorted_view[j] : | ||
| 181 | + NULL; | ||
| 182 | + rel = compare_name_constraint_nodes(nc1, nc2); | ||
| 183 | |||
| 184 | - /* Phase 2 | ||
| 185 | - * iterate through all combinations from PERMITTED2 and PERMITTED | ||
| 186 | - * and create intersections of nodes with same type */ | ||
| 187 | - for (size_t i = 0; i < permitted2->size; i++) { | ||
| 188 | - const struct name_constraints_node_st *t2 = permitted2->data[i]; | ||
| 189 | - | ||
| 190 | - // current PERMITTED2 node has not yet been used for any intersection | ||
| 191 | - // (and is not in REMOVED either) | ||
| 192 | - used = 0; | ||
| 193 | - for (size_t j = 0; j < removed.size; j++) { | ||
| 194 | - const struct name_constraints_node_st *t = | ||
| 195 | - removed.data[j]; | ||
| 196 | - // save intersection of name constraints into tmp | ||
| 197 | - ret = name_constraints_intersect_nodes(nc, t, t2, &tmp); | ||
| 198 | - if (ret < 0) { | ||
| 199 | - gnutls_assert(); | ||
| 200 | - goto cleanup; | ||
| 201 | - } | ||
| 202 | - used = 1; | ||
| 203 | - // if intersection is not empty | ||
| 204 | - if (tmp != | ||
| 205 | - NULL) { // intersection for this type is not empty | ||
| 206 | - // check bounds | ||
| 207 | - if (tmp->type > GNUTLS_SAN_MAX || | ||
| 208 | - tmp->type == 0) { | ||
| 209 | - gnutls_free(tmp); | ||
| 210 | - return gnutls_assert_val( | ||
| 211 | - GNUTLS_E_INTERNAL_ERROR); | ||
| 212 | - } | ||
| 213 | - // we will not add universal excluded constraint for this type | ||
| 214 | - type_bitmask_clr(types_with_empty_intersection, | ||
| 215 | - tmp->type); | ||
| 216 | - // add intersection node to PERMITTED | ||
| 217 | - ret = name_constraints_node_list_add(permitted, | ||
| 218 | - tmp); | ||
| 219 | - if (ret < 0) { | ||
| 220 | - gnutls_assert(); | ||
| 221 | - goto cleanup; | ||
| 222 | - } | ||
| 223 | - } | ||
| 224 | + switch (rel) { | ||
| 225 | + case NC_SORTS_BEFORE: | ||
| 226 | + assert(nc1 != NULL); /* comparator-guaranteed */ | ||
| 227 | + /* if nothing to intersect with, shallow-copy nc1 */ | ||
| 228 | + if (!type_bitmask_in(types_in_p2, nc1->type)) | ||
| 229 | + ret = name_constraints_node_list_add(&result, | ||
| 230 | + nc1); | ||
| 231 | + i++; /* otherwise skip nc1 */ | ||
| 232 | + break; | ||
| 233 | + case NC_SORTS_AFTER: | ||
| 234 | + assert(nc2 != NULL); /* comparator-guaranteed */ | ||
| 235 | + /* if nothing to intersect with, deep-copy nc2 */ | ||
| 236 | + if (!type_bitmask_in(types_in_p1, nc2->type)) | ||
| 237 | + ret = name_constraints_node_add_copy( | ||
| 238 | + nc, &result, nc2); | ||
| 239 | + j++; /* otherwise skip nc2 */ | ||
| 240 | + break; | ||
| 241 | + case NC_INCLUDED_BY: /* add nc1, shallow-copy */ | ||
| 242 | + assert(nc1 != NULL && nc2 != NULL); /* comparator */ | ||
| 243 | + type_bitmask_clr(universal_exclude_needed, nc1->type); | ||
| 244 | + ret = name_constraints_node_list_add(&result, nc1); | ||
| 245 | + i++; | ||
| 246 | + break; | ||
| 247 | + case NC_INCLUDES: /* pick nc2, deep-copy */ | ||
| 248 | + assert(nc1 != NULL && nc2 != NULL); /* comparator */ | ||
| 249 | + type_bitmask_clr(universal_exclude_needed, nc2->type); | ||
| 250 | + ret = name_constraints_node_add_copy(nc, &result, nc2); | ||
| 251 | + j++; | ||
| 252 | + break; | ||
| 253 | + case NC_EQUAL: /* pick whichever: nc1, shallow-copy */ | ||
| 254 | + assert(nc1 != NULL && nc2 != NULL); /* loop condition */ | ||
| 255 | + type_bitmask_clr(universal_exclude_needed, nc1->type); | ||
| 256 | + ret = name_constraints_node_list_add(&result, nc1); | ||
| 257 | + i++; | ||
| 258 | + j++; | ||
| 259 | + break; | ||
| 260 | } | ||
| 261 | - // if the node from PERMITTED2 was not used for intersection, copy it to DEST | ||
| 262 | - // Beware: also copies nodes other than DNS, email, IP, | ||
| 263 | - // since their counterpart may have been moved in phase 1. | ||
| 264 | - if (!used) { | ||
| 265 | - ret = name_constraints_node_add_copy(nc, permitted, t2); | ||
| 266 | - if (ret < 0) { | ||
| 267 | - gnutls_assert(); | ||
| 268 | - goto cleanup; | ||
| 269 | - } | ||
| 270 | + if (ret < 0) { | ||
| 271 | + gnutls_assert(); | ||
| 272 | + goto cleanup; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | - /* Phase 3 | ||
| 277 | - * For each type: If we have empty permitted name constraints now | ||
| 278 | - * and we didn't have at the beginning, we have to add a new | ||
| 279 | - * excluded constraint with universal wildcard | ||
| 280 | - * (since the intersection of permitted is now empty). */ | ||
| 281 | + /* finishing touch: add universal excluded constraints for types where | ||
| 282 | + * both lists had constraints, but all intersections ended up empty */ | ||
| 283 | for (type = 1; type <= GNUTLS_SAN_MAX; type++) { | ||
| 284 | - if (!type_bitmask_in(types_with_empty_intersection, type)) | ||
| 285 | + if (!type_bitmask_in(universal_exclude_needed, type)) | ||
| 286 | continue; | ||
| 287 | _gnutls_hard_log( | ||
| 288 | "Adding universal excluded name constraint for type %d.\n", | ||
| 289 | @@ -871,14 +878,24 @@ static int name_constraints_node_list_intersect( | ||
| 290 | goto cleanup; | ||
| 291 | } | ||
| 292 | break; | ||
| 293 | - default: // do nothing, at least one node was already moved in phase 1 | ||
| 294 | - break; | ||
| 295 | + default: /* unsupported type; should be unreacheable */ | ||
| 296 | + ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); | ||
| 297 | + goto cleanup; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | - ret = GNUTLS_E_SUCCESS; | ||
| 301 | |||
| 302 | + gnutls_free(permitted->data); | ||
| 303 | + gnutls_free(permitted->sorted_view); | ||
| 304 | + permitted->data = result.data; | ||
| 305 | + permitted->sorted_view = NULL; | ||
| 306 | + permitted->size = result.size; | ||
| 307 | + permitted->capacity = result.capacity; | ||
| 308 | + permitted->dirty = true; | ||
| 309 | + | ||
| 310 | + result.data = NULL; | ||
| 311 | + ret = GNUTLS_E_SUCCESS; | ||
| 312 | cleanup: | ||
| 313 | - gnutls_free(removed.data); | ||
| 314 | + name_constraints_node_list_clear(&result); | ||
| 315 | return ret; | ||
| 316 | } | ||
| 317 | |||
| 318 | @@ -1254,100 +1271,6 @@ static unsigned email_matches(const gnutls_datum_t *name, | ||
| 319 | return rel == NC_EQUAL || rel == NC_INCLUDED_BY; | ||
| 320 | } | ||
| 321 | |||
| 322 | -/*- | ||
| 323 | - * name_constraints_intersect_nodes: | ||
| 324 | - * @nc1: name constraints node 1 | ||
| 325 | - * @nc2: name constraints node 2 | ||
| 326 | - * @_intersection: newly allocated node with intersected constraints, | ||
| 327 | - * NULL if the intersection is empty | ||
| 328 | - * | ||
| 329 | - * Inspect 2 name constraints nodes (of possibly different types) and allocate | ||
| 330 | - * a new node with intersection of given constraints. | ||
| 331 | - * | ||
| 332 | - * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. | ||
| 333 | - -*/ | ||
| 334 | -static int name_constraints_intersect_nodes( | ||
| 335 | - gnutls_x509_name_constraints_t nc, | ||
| 336 | - const struct name_constraints_node_st *node1, | ||
| 337 | - const struct name_constraints_node_st *node2, | ||
| 338 | - struct name_constraints_node_st **_intersection) | ||
| 339 | -{ | ||
| 340 | - // presume empty intersection | ||
| 341 | - struct name_constraints_node_st *intersection = NULL; | ||
| 342 | - const struct name_constraints_node_st *to_copy = NULL; | ||
| 343 | - enum name_constraint_relation rel; | ||
| 344 | - | ||
| 345 | - *_intersection = NULL; | ||
| 346 | - | ||
| 347 | - if (node1->type != node2->type) { | ||
| 348 | - return GNUTLS_E_SUCCESS; | ||
| 349 | - } | ||
| 350 | - switch (node1->type) { | ||
| 351 | - case GNUTLS_SAN_DNSNAME: | ||
| 352 | - rel = compare_dns_names(&node1->name, &node2->name); | ||
| 353 | - switch (rel) { | ||
| 354 | - case NC_EQUAL: // equal means doesn't matter which one | ||
| 355 | - case NC_INCLUDES: // node2 is more specific | ||
| 356 | - to_copy = node2; | ||
| 357 | - break; | ||
| 358 | - case NC_INCLUDED_BY: // node1 is more specific | ||
| 359 | - to_copy = node1; | ||
| 360 | - break; | ||
| 361 | - case NC_SORTS_BEFORE: // no intersection | ||
| 362 | - case NC_SORTS_AFTER: // no intersection | ||
| 363 | - return GNUTLS_E_SUCCESS; | ||
| 364 | - } | ||
| 365 | - break; | ||
| 366 | - case GNUTLS_SAN_RFC822NAME: | ||
| 367 | - rel = compare_emails(&node1->name, &node2->name); | ||
| 368 | - switch (rel) { | ||
| 369 | - case NC_EQUAL: // equal means doesn't matter which one | ||
| 370 | - case NC_INCLUDES: // node2 is more specific | ||
| 371 | - to_copy = node2; | ||
| 372 | - break; | ||
| 373 | - case NC_INCLUDED_BY: // node1 is more specific | ||
| 374 | - to_copy = node1; | ||
| 375 | - break; | ||
| 376 | - case NC_SORTS_BEFORE: // no intersection | ||
| 377 | - case NC_SORTS_AFTER: // no intersection | ||
| 378 | - return GNUTLS_E_SUCCESS; | ||
| 379 | - } | ||
| 380 | - break; | ||
| 381 | - case GNUTLS_SAN_IPADDRESS: | ||
| 382 | - rel = compare_ip_ncs(&node1->name, &node2->name); | ||
| 383 | - switch (rel) { | ||
| 384 | - case NC_EQUAL: // equal means doesn't matter which one | ||
| 385 | - case NC_INCLUDES: // node2 is more specific | ||
| 386 | - to_copy = node2; | ||
| 387 | - break; | ||
| 388 | - case NC_INCLUDED_BY: // node1 is more specific | ||
| 389 | - to_copy = node1; | ||
| 390 | - break; | ||
| 391 | - case NC_SORTS_BEFORE: // no intersection | ||
| 392 | - case NC_SORTS_AFTER: // no intersection | ||
| 393 | - return GNUTLS_E_SUCCESS; | ||
| 394 | - } | ||
| 395 | - break; | ||
| 396 | - default: | ||
| 397 | - // for other types, we don't know how to do the intersection, assume empty | ||
| 398 | - return GNUTLS_E_SUCCESS; | ||
| 399 | - } | ||
| 400 | - | ||
| 401 | - // copy existing node if applicable | ||
| 402 | - if (to_copy != NULL) { | ||
| 403 | - *_intersection = name_constraints_node_new(nc, to_copy->type, | ||
| 404 | - to_copy->name.data, | ||
| 405 | - to_copy->name.size); | ||
| 406 | - if (*_intersection == NULL) | ||
| 407 | - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); | ||
| 408 | - intersection = *_intersection; | ||
| 409 | - | ||
| 410 | - assert(intersection->name.data != NULL); | ||
| 411 | - } | ||
| 412 | - | ||
| 413 | - return GNUTLS_E_SUCCESS; | ||
| 414 | -} | ||
| 415 | - | ||
| 416 | /* | ||
| 417 | * Returns: true if the certification is acceptable, and false otherwise. | ||
| 418 | */ | ||
| 419 | -- | ||
| 420 | 2.43.0 | ||
| 421 | |||
diff --git a/meta/recipes-support/gnutls/gnutls_3.8.4.bb b/meta/recipes-support/gnutls/gnutls_3.8.4.bb index 026ae650f6..ccb6a2b4b2 100644 --- a/meta/recipes-support/gnutls/gnutls_3.8.4.bb +++ b/meta/recipes-support/gnutls/gnutls_3.8.4.bb | |||
| @@ -34,6 +34,15 @@ SRC_URI = "https://www.gnupg.org/ftp/gcrypt/gnutls/v${SHRT_VER}/gnutls-${PV}.tar | |||
| 34 | file://CVE-2025-32990.patch \ | 34 | file://CVE-2025-32990.patch \ |
| 35 | file://CVE-2025-6395.patch \ | 35 | file://CVE-2025-6395.patch \ |
| 36 | file://CVE-2025-9820.patch \ | 36 | file://CVE-2025-9820.patch \ |
| 37 | file://CVE-2025-14831-1.patch \ | ||
| 38 | file://CVE-2025-14831-2.patch \ | ||
| 39 | file://CVE-2025-14831-3.patch \ | ||
| 40 | file://CVE-2025-14831-4.patch \ | ||
| 41 | file://CVE-2025-14831-5.patch \ | ||
| 42 | file://CVE-2025-14831-6.patch \ | ||
| 43 | file://CVE-2025-14831-7.patch \ | ||
| 44 | file://CVE-2025-14831-8.patch \ | ||
| 45 | file://CVE-2025-14831-9.patch \ | ||
| 37 | " | 46 | " |
| 38 | 47 | ||
| 39 | SRC_URI[sha256sum] = "2bea4e154794f3f00180fa2a5c51fe8b005ac7a31cd58bd44cdfa7f36ebc3a9b" | 48 | SRC_URI[sha256sum] = "2bea4e154794f3f00180fa2a5c51fe8b005ac7a31cd58bd44cdfa7f36ebc3a9b" |
