diff options
author | Chen Qi <Qi.Chen@windriver.com> | 2025-01-21 18:27:13 -0800 |
---|---|---|
committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2025-01-30 18:53:26 +0000 |
commit | 7bea34aac15da6b694b03dbcc2190a4d9a34bcda (patch) | |
tree | 1a8ff50d85d9c85c233b84418c90d5782bf75c61 | |
parent | 49ac2c4e935a2298f571e7457737e17d3a29d5e3 (diff) | |
download | meta-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.patch | 187 | ||||
-rw-r--r-- | recipes-containers/buildah/buildah_git.bb | 1 |
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 @@ | |||
1 | From 854570c44c219c2b92b03b36b7a2069a32e2c08a Mon Sep 17 00:00:00 2001 | ||
2 | From: Matt Heon <mheon@redhat.com> | ||
3 | Date: Wed, 9 Oct 2024 09:54:22 -0400 | ||
4 | Subject: [PATCH] Use securejoin.SecureJoin when forming userns paths | ||
5 | |||
6 | We need to read /etc/passwd and /etc/group in the container to | ||
7 | get an idea of how many UIDs and GIDs we need to allocate for a | ||
8 | user namespace when `--userns=auto` is specified. We were forming | ||
9 | paths for these using filepath.Join, which is not safe for paths | ||
10 | within a container, resulting in this CVE allowing crafted | ||
11 | symlinks in the container to access paths on the host instead. | ||
12 | |||
13 | Cherry-pick conflict fixed for v1.51 branch, and converted to use | ||
14 | the old securejoin API (securejoin.SecureJoin and then os.Open) | ||
15 | as this branch is too old to have the new API. | ||
16 | |||
17 | Addresses CVE-2024-9676 | ||
18 | |||
19 | Signed-off-by: Matt Heon <mheon@redhat.com> | ||
20 | |||
21 | CVE: CVE-2024-9676 | ||
22 | |||
23 | Upstream-Status: Backport [854570c44c219c2b92b03b36b7a2069a32e2c08a] | ||
24 | |||
25 | Signed-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 | |||
32 | diff --git a/userns.go b/userns.go | ||
33 | index 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 | +} | ||
165 | diff --git a/userns_unsupported.go b/userns_unsupported.go | ||
166 | new file mode 100644 | ||
167 | index 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 | -- | ||
186 | 2.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 | ||
34 | SRC_URI = " \ | 34 | SRC_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 | ||
38 | DEPENDS = "libdevmapper btrfs-tools gpgme" | 39 | DEPENDS = "libdevmapper btrfs-tools gpgme" |