summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--recipes-containers/buildah/buildah_git.bb1
-rw-r--r--recipes-containers/buildah/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch191
2 files changed, 192 insertions, 0 deletions
diff --git a/recipes-containers/buildah/buildah_git.bb b/recipes-containers/buildah/buildah_git.bb
index 1c33291a..45d7b31d 100644
--- a/recipes-containers/buildah/buildah_git.bb
+++ b/recipes-containers/buildah/buildah_git.bb
@@ -27,6 +27,7 @@ GOBUILDFLAGS += "-mod vendor"
27 27
28SRC_URI = " \ 28SRC_URI = " \
29 git://github.com/containers/buildah;branch=release-1.26;name=buildah;protocol=https \ 29 git://github.com/containers/buildah;branch=release-1.26;name=buildah;protocol=https \
30 file://0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch;patchdir=src/github.com/containers/buildah/vendor/github.com/containers/storage \
30 " 31 "
31 32
32DEPENDS = "libdevmapper btrfs-tools gpgme" 33DEPENDS = "libdevmapper btrfs-tools gpgme"
diff --git a/recipes-containers/buildah/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch b/recipes-containers/buildah/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch
new file mode 100644
index 00000000..d2e3dbbd
--- /dev/null
+++ b/recipes-containers/buildah/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch
@@ -0,0 +1,191 @@
1From b92233f24f22bc509e2f9fad2dc67f49e482f363 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 [0dc4fc9bb826e08b6e25af0af6a296ac172b5e15]
24
25Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
26---
27 userns.go | 93 ++++++++++++++++++++++++++++++-------------
28 userns_unsupported.go | 14 +++++++
29 2 files changed, 80 insertions(+), 27 deletions(-)
30 create mode 100644 userns_unsupported.go
31
32diff --git a/userns.go b/userns.go
33index e0e530275..bea2a9520 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@@ -78,43 +81,63 @@ func (s *store) getAvailableIDs() (*idSet, *idSet, error) {
60 return u, g, nil
61 }
62
63+const nobodyUser = 65534
64 // parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and
65 // /etc/group files.
66 func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 {
67+ var (
68+ passwd *os.File
69+ group *os.File
70+ size int
71+ err error
72+ )
73 if passwdFile == "" {
74- passwdFile = filepath.Join(containerMount, "etc/passwd")
75- }
76- if groupFile == "" {
77- groupFile = filepath.Join(groupFile, "etc/group")
78+ passwd, err = secureOpen(containerMount, "/etc/passwd")
79+ } else {
80+ // User-specified override from a volume. Will not be in
81+ // container root.
82+ passwd, err = os.Open(passwdFile)
83 }
84-
85- size := 0
86-
87- users, err := libcontainerUser.ParsePasswdFile(passwdFile)
88 if err == nil {
89- for _, u := range users {
90- // Skip the "nobody" user otherwise we end up with 65536
91- // ids with most images
92- if u.Name == "nobody" {
93- continue
94- }
95- if u.Uid > size {
96- size = u.Uid
97- }
98- if u.Gid > size {
99- size = u.Gid
100+ defer passwd.Close()
101+
102+ users, err := libcontainerUser.ParsePasswd(passwd)
103+ if err == nil {
104+ for _, u := range users {
105+ // Skip the "nobody" user otherwise we end up with 65536
106+ // ids with most images
107+ if u.Name == "nobody" || u.Name == "nogroup" {
108+ continue
109+ }
110+ if u.Uid > size && u.Uid != nobodyUser {
111+ size = u.Uid + 1
112+ }
113+ if u.Gid > size && u.Gid != nobodyUser {
114+ size = u.Gid + 1
115+ }
116 }
117 }
118 }
119
120- groups, err := libcontainerUser.ParseGroupFile(groupFile)
121+ if groupFile == "" {
122+ group, err = secureOpen(containerMount, "/etc/group")
123+ } else {
124+ // User-specified override from a volume. Will not be in
125+ // container root.
126+ group, err = os.Open(groupFile)
127+ }
128 if err == nil {
129- for _, g := range groups {
130- if g.Name == "nobody" {
131- continue
132- }
133- if g.Gid > size {
134- size = g.Gid
135+ defer group.Close()
136+
137+ groups, err := libcontainerUser.ParseGroup(group)
138+ if err == nil {
139+ for _, g := range groups {
140+ if g.Name == "nobody" || g.Name == "nogroup" {
141+ continue
142+ }
143+ if g.Gid > size && g.Gid != nobodyUser {
144+ size = g.Gid + 1
145+ }
146 }
147 }
148 }
149@@ -300,3 +323,19 @@ func getAutoUserNSIDMappings(
150 gidMap := append(availableGIDs.zip(requestedContainerGIDs), additionalGIDMappings...)
151 return uidMap, gidMap, nil
152 }
153+
154+// Securely open (read-only) a file in a container mount.
155+func secureOpen(containerMount, file string) (*os.File, error) {
156+ filePath, err := securejoin.SecureJoin(containerMount, file)
157+ if err != nil {
158+ return nil, err
159+ }
160+
161+ flags := unix.O_PATH | unix.O_CLOEXEC | unix.O_RDONLY
162+ fileHandle, err := os.OpenFile(filePath, flags, 0)
163+ if err != nil {
164+ return nil, err
165+ }
166+
167+ return fileHandle, nil
168+}
169diff --git a/userns_unsupported.go b/userns_unsupported.go
170new file mode 100644
171index 000000000..e37c18fe4
172--- /dev/null
173+++ b/userns_unsupported.go
174@@ -0,0 +1,14 @@
175+//go:build !linux
176+
177+package storage
178+
179+import (
180+ "errors"
181+
182+ "github.com/containers/storage/pkg/idtools"
183+ "github.com/containers/storage/types"
184+)
185+
186+func (s *store) getAutoUserNS(_ *types.AutoUserNsOptions, _ *Image, _ rwLayerStore, _ []roLayerStore) ([]idtools.IDMap, []idtools.IDMap, error) {
187+ return nil, nil, errors.New("user namespaces are not supported on this platform")
188+}
189--
1902.25.1
191