summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--recipes-containers/cri-o/cri-o_git.bb1
-rw-r--r--recipes-containers/cri-o/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch191
2 files changed, 192 insertions, 0 deletions
diff --git a/recipes-containers/cri-o/cri-o_git.bb b/recipes-containers/cri-o/cri-o_git.bb
index d74a17fc..429c49a7 100644
--- a/recipes-containers/cri-o/cri-o_git.bb
+++ b/recipes-containers/cri-o/cri-o_git.bb
@@ -19,6 +19,7 @@ SRC_URI = "\
19 git://github.com/kubernetes-sigs/cri-o.git;branch=release-1.23;name=cri-o;protocol=https \ 19 git://github.com/kubernetes-sigs/cri-o.git;branch=release-1.23;name=cri-o;protocol=https \
20 file://0001-Makefile-force-symlinks.patch \ 20 file://0001-Makefile-force-symlinks.patch \
21 file://crio.conf \ 21 file://crio.conf \
22 file://0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch;patchdir=src/import/vendor/github.com/containers/storage \
22 " 23 "
23 24
24# Apache-2.0 for docker 25# Apache-2.0 for docker
diff --git a/recipes-containers/cri-o/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch b/recipes-containers/cri-o/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch
new file mode 100644
index 00000000..1cb7eb0e
--- /dev/null
+++ b/recipes-containers/cri-o/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch
@@ -0,0 +1,191 @@
1From 03aff6270b389f27bc1edc4985dab753a38e7c7b 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
21Upstream-Status: Backport [0dc4fc9bb826e08b6e25af0af6a296ac172b5e15]
22
23CVE: CVE-2024-9676
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 523c92dc8..c234414ef 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 "os"
43 "os/user"
44- "path/filepath"
45 "strconv"
46
47 drivers "github.com/containers/storage/drivers"
48 "github.com/containers/storage/pkg/idtools"
49 "github.com/containers/storage/pkg/unshare"
50 "github.com/containers/storage/types"
51+ securejoin "github.com/cyphar/filepath-securejoin"
52 libcontainerUser "github.com/opencontainers/runc/libcontainer/user"
53 "github.com/pkg/errors"
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