summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen Qi <Qi.Chen@windriver.com>2025-01-21 18:27:13 -0800
committerBruce Ashfield <bruce.ashfield@gmail.com>2025-01-30 18:53:26 +0000
commit7bea34aac15da6b694b03dbcc2190a4d9a34bcda (patch)
tree1a8ff50d85d9c85c233b84418c90d5782bf75c61
parent49ac2c4e935a2298f571e7457737e17d3a29d5e3 (diff)
downloadmeta-virtualization-7bea34aac15da6b694b03dbcc2190a4d9a34bcda.tar.gz
buildah: fix CVE-2024-9676
Backport patch to fix CVE-2024-9676. Signed-off-by: Chen Qi <Qi.Chen@windriver.com> Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--recipes-containers/buildah/buildah/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch187
-rw-r--r--recipes-containers/buildah/buildah_git.bb1
2 files changed, 188 insertions, 0 deletions
diff --git a/recipes-containers/buildah/buildah/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch b/recipes-containers/buildah/buildah/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch
new file mode 100644
index 00000000..73040e82
--- /dev/null
+++ b/recipes-containers/buildah/buildah/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch
@@ -0,0 +1,187 @@
1From 854570c44c219c2b92b03b36b7a2069a32e2c08a Mon Sep 17 00:00:00 2001
2From: Matt Heon <mheon@redhat.com>
3Date: Wed, 9 Oct 2024 09:54:22 -0400
4Subject: [PATCH] Use securejoin.SecureJoin when forming userns paths
5
6We need to read /etc/passwd and /etc/group in the container to
7get an idea of how many UIDs and GIDs we need to allocate for a
8user namespace when `--userns=auto` is specified. We were forming
9paths for these using filepath.Join, which is not safe for paths
10within a container, resulting in this CVE allowing crafted
11symlinks in the container to access paths on the host instead.
12
13Cherry-pick conflict fixed for v1.51 branch, and converted to use
14the old securejoin API (securejoin.SecureJoin and then os.Open)
15as this branch is too old to have the new API.
16
17Addresses CVE-2024-9676
18
19Signed-off-by: Matt Heon <mheon@redhat.com>
20
21CVE: CVE-2024-9676
22
23Upstream-Status: Backport [854570c44c219c2b92b03b36b7a2069a32e2c08a]
24
25Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
26---
27 userns.go | 92 ++++++++++++++++++++++++++++++-------------
28 userns_unsupported.go | 14 +++++++
29 2 files changed, 79 insertions(+), 27 deletions(-)
30 create mode 100644 userns_unsupported.go
31
32diff --git a/userns.go b/userns.go
33index 32ae830be..2c855da7c 100644
34--- a/userns.go
35+++ b/userns.go
36@@ -1,18 +1,21 @@
37+//go:build linux
38+
39 package storage
40
41 import (
42 "fmt"
43 "os"
44 "os/user"
45- "path/filepath"
46 "strconv"
47
48 drivers "github.com/containers/storage/drivers"
49 "github.com/containers/storage/pkg/idtools"
50 "github.com/containers/storage/pkg/unshare"
51 "github.com/containers/storage/types"
52+ securejoin "github.com/cyphar/filepath-securejoin"
53 libcontainerUser "github.com/opencontainers/runc/libcontainer/user"
54 "github.com/sirupsen/logrus"
55+ "golang.org/x/sys/unix"
56 )
57
58 // getAdditionalSubIDs looks up the additional IDs configured for
59@@ -85,40 +88,59 @@ const nobodyUser = 65534
60 // parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and
61 // /etc/group files.
62 func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 {
63+ var (
64+ passwd *os.File
65+ group *os.File
66+ size int
67+ err error
68+ )
69 if passwdFile == "" {
70- passwdFile = filepath.Join(containerMount, "etc/passwd")
71- }
72- if groupFile == "" {
73- groupFile = filepath.Join(groupFile, "etc/group")
74+ passwd, err = secureOpen(containerMount, "/etc/passwd")
75+ } else {
76+ // User-specified override from a volume. Will not be in
77+ // container root.
78+ passwd, err = os.Open(passwdFile)
79 }
80-
81- size := 0
82-
83- users, err := libcontainerUser.ParsePasswdFile(passwdFile)
84 if err == nil {
85- for _, u := range users {
86- // Skip the "nobody" user otherwise we end up with 65536
87- // ids with most images
88- if u.Name == "nobody" {
89- continue
90- }
91- if u.Uid > size && u.Uid != nobodyUser {
92- size = u.Uid
93- }
94- if u.Gid > size && u.Gid != nobodyUser {
95- size = u.Gid
96+ defer passwd.Close()
97+
98+ users, err := libcontainerUser.ParsePasswd(passwd)
99+ if err == nil {
100+ for _, u := range users {
101+ // Skip the "nobody" user otherwise we end up with 65536
102+ // ids with most images
103+ if u.Name == "nobody" || u.Name == "nogroup" {
104+ continue
105+ }
106+ if u.Uid > size && u.Uid != nobodyUser {
107+ size = u.Uid + 1
108+ }
109+ if u.Gid > size && u.Gid != nobodyUser {
110+ size = u.Gid + 1
111+ }
112 }
113 }
114 }
115
116- groups, err := libcontainerUser.ParseGroupFile(groupFile)
117+ if groupFile == "" {
118+ group, err = secureOpen(containerMount, "/etc/group")
119+ } else {
120+ // User-specified override from a volume. Will not be in
121+ // container root.
122+ group, err = os.Open(groupFile)
123+ }
124 if err == nil {
125- for _, g := range groups {
126- if g.Name == "nobody" {
127- continue
128- }
129- if g.Gid > size && g.Gid != nobodyUser {
130- size = g.Gid
131+ defer group.Close()
132+
133+ groups, err := libcontainerUser.ParseGroup(group)
134+ if err == nil {
135+ for _, g := range groups {
136+ if g.Name == "nobody" || g.Name == "nogroup" {
137+ continue
138+ }
139+ if g.Gid > size && g.Gid != nobodyUser {
140+ size = g.Gid + 1
141+ }
142 }
143 }
144 }
145@@ -309,3 +331,19 @@ func getAutoUserNSIDMappings(
146 gidMap := append(availableGIDs.zip(requestedContainerGIDs), additionalGIDMappings...)
147 return uidMap, gidMap, nil
148 }
149+
150+// Securely open (read-only) a file in a container mount.
151+func secureOpen(containerMount, file string) (*os.File, error) {
152+ filePath, err := securejoin.SecureJoin(containerMount, file)
153+ if err != nil {
154+ return nil, err
155+ }
156+
157+ flags := unix.O_PATH | unix.O_CLOEXEC | unix.O_RDONLY
158+ fileHandle, err := os.OpenFile(filePath, flags, 0)
159+ if err != nil {
160+ return nil, err
161+ }
162+
163+ return fileHandle, nil
164+}
165diff --git a/userns_unsupported.go b/userns_unsupported.go
166new file mode 100644
167index 000000000..e37c18fe4
168--- /dev/null
169+++ b/userns_unsupported.go
170@@ -0,0 +1,14 @@
171+//go:build !linux
172+
173+package storage
174+
175+import (
176+ "errors"
177+
178+ "github.com/containers/storage/pkg/idtools"
179+ "github.com/containers/storage/types"
180+)
181+
182+func (s *store) getAutoUserNS(_ *types.AutoUserNsOptions, _ *Image, _ rwLayerStore, _ []roLayerStore) ([]idtools.IDMap, []idtools.IDMap, error) {
183+ return nil, nil, errors.New("user namespaces are not supported on this platform")
184+}
185--
1862.25.1
187
diff --git a/recipes-containers/buildah/buildah_git.bb b/recipes-containers/buildah/buildah_git.bb
index 90ffc238..288a1cb0 100644
--- a/recipes-containers/buildah/buildah_git.bb
+++ b/recipes-containers/buildah/buildah_git.bb
@@ -33,6 +33,7 @@ SRCREV_storage = "246ba3062e8b551026aef2708eee747014ce5c52"
33 33
34SRC_URI = " \ 34SRC_URI = " \
35 git://github.com/containers/buildah;branch=release-1.34;name=buildah;protocol=https \ 35 git://github.com/containers/buildah;branch=release-1.34;name=buildah;protocol=https \
36 file://0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch;patchdir=src/github.com/containers/buildah/vendor/github.com/containers/storage \
36 " 37 "
37 38
38DEPENDS = "libdevmapper btrfs-tools gpgme" 39DEPENDS = "libdevmapper btrfs-tools gpgme"