From f0c9dc00f27437ab21a246e8730142b50c5b7b46 Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Wed, 22 Jan 2025 00:28:46 -0800 Subject: cri-o: fix CVE-2024-9676 Backport patch to fix CVE-2024-9676. Signed-off-by: Chen Qi Signed-off-by: Bruce Ashfield --- recipes-containers/cri-o/cri-o_git.bb | 1 + ...join.SecureJoin-when-forming-userns-paths.patch | 191 +++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 recipes-containers/cri-o/files/0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch 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 = "\ git://github.com/kubernetes-sigs/cri-o.git;branch=release-1.23;name=cri-o;protocol=https \ file://0001-Makefile-force-symlinks.patch \ file://crio.conf \ + file://0001-Use-securejoin.SecureJoin-when-forming-userns-paths.patch;patchdir=src/import/vendor/github.com/containers/storage \ " # 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 @@ +From 03aff6270b389f27bc1edc4985dab753a38e7c7b Mon Sep 17 00:00:00 2001 +From: Matt Heon +Date: Wed, 9 Oct 2024 09:54:22 -0400 +Subject: [PATCH] Use securejoin.SecureJoin when forming userns paths + +We need to read /etc/passwd and /etc/group in the container to +get an idea of how many UIDs and GIDs we need to allocate for a +user namespace when `--userns=auto` is specified. We were forming +paths for these using filepath.Join, which is not safe for paths +within a container, resulting in this CVE allowing crafted +symlinks in the container to access paths on the host instead. + +Cherry-pick conflict fixed for v1.51 branch, and converted to use +the old securejoin API (securejoin.SecureJoin and then os.Open) +as this branch is too old to have the new API. + +Addresses CVE-2024-9676 + +Signed-off-by: Matt Heon + +Upstream-Status: Backport [0dc4fc9bb826e08b6e25af0af6a296ac172b5e15] + +CVE: CVE-2024-9676 + +Signed-off-by: Chen Qi +--- + userns.go | 93 ++++++++++++++++++++++++++++++------------- + userns_unsupported.go | 14 +++++++ + 2 files changed, 80 insertions(+), 27 deletions(-) + create mode 100644 userns_unsupported.go + +diff --git a/userns.go b/userns.go +index 523c92dc8..c234414ef 100644 +--- a/userns.go ++++ b/userns.go +@@ -1,18 +1,21 @@ ++//go:build linux ++ + package storage + + import ( + "os" + "os/user" +- "path/filepath" + "strconv" + + drivers "github.com/containers/storage/drivers" + "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/pkg/unshare" + "github.com/containers/storage/types" ++ securejoin "github.com/cyphar/filepath-securejoin" + libcontainerUser "github.com/opencontainers/runc/libcontainer/user" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ++ "golang.org/x/sys/unix" + ) + + // getAdditionalSubIDs looks up the additional IDs configured for +@@ -78,43 +81,63 @@ func (s *store) getAvailableIDs() (*idSet, *idSet, error) { + return u, g, nil + } + ++const nobodyUser = 65534 + // parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and + // /etc/group files. + func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 { ++ var ( ++ passwd *os.File ++ group *os.File ++ size int ++ err error ++ ) + if passwdFile == "" { +- passwdFile = filepath.Join(containerMount, "etc/passwd") +- } +- if groupFile == "" { +- groupFile = filepath.Join(groupFile, "etc/group") ++ passwd, err = secureOpen(containerMount, "/etc/passwd") ++ } else { ++ // User-specified override from a volume. Will not be in ++ // container root. ++ passwd, err = os.Open(passwdFile) + } +- +- size := 0 +- +- users, err := libcontainerUser.ParsePasswdFile(passwdFile) + if err == nil { +- for _, u := range users { +- // Skip the "nobody" user otherwise we end up with 65536 +- // ids with most images +- if u.Name == "nobody" { +- continue +- } +- if u.Uid > size { +- size = u.Uid +- } +- if u.Gid > size { +- size = u.Gid ++ defer passwd.Close() ++ ++ users, err := libcontainerUser.ParsePasswd(passwd) ++ if err == nil { ++ for _, u := range users { ++ // Skip the "nobody" user otherwise we end up with 65536 ++ // ids with most images ++ if u.Name == "nobody" || u.Name == "nogroup" { ++ continue ++ } ++ if u.Uid > size && u.Uid != nobodyUser { ++ size = u.Uid + 1 ++ } ++ if u.Gid > size && u.Gid != nobodyUser { ++ size = u.Gid + 1 ++ } + } + } + } + +- groups, err := libcontainerUser.ParseGroupFile(groupFile) ++ if groupFile == "" { ++ group, err = secureOpen(containerMount, "/etc/group") ++ } else { ++ // User-specified override from a volume. Will not be in ++ // container root. ++ group, err = os.Open(groupFile) ++ } + if err == nil { +- for _, g := range groups { +- if g.Name == "nobody" { +- continue +- } +- if g.Gid > size { +- size = g.Gid ++ defer group.Close() ++ ++ groups, err := libcontainerUser.ParseGroup(group) ++ if err == nil { ++ for _, g := range groups { ++ if g.Name == "nobody" || g.Name == "nogroup" { ++ continue ++ } ++ if g.Gid > size && g.Gid != nobodyUser { ++ size = g.Gid + 1 ++ } + } + } + } +@@ -300,3 +323,19 @@ func getAutoUserNSIDMappings( + gidMap := append(availableGIDs.zip(requestedContainerGIDs), additionalGIDMappings...) + return uidMap, gidMap, nil + } ++ ++// Securely open (read-only) a file in a container mount. ++func secureOpen(containerMount, file string) (*os.File, error) { ++ filePath, err := securejoin.SecureJoin(containerMount, file) ++ if err != nil { ++ return nil, err ++ } ++ ++ flags := unix.O_PATH | unix.O_CLOEXEC | unix.O_RDONLY ++ fileHandle, err := os.OpenFile(filePath, flags, 0) ++ if err != nil { ++ return nil, err ++ } ++ ++ return fileHandle, nil ++} +diff --git a/userns_unsupported.go b/userns_unsupported.go +new file mode 100644 +index 000000000..e37c18fe4 +--- /dev/null ++++ b/userns_unsupported.go +@@ -0,0 +1,14 @@ ++//go:build !linux ++ ++package storage ++ ++import ( ++ "errors" ++ ++ "github.com/containers/storage/pkg/idtools" ++ "github.com/containers/storage/types" ++) ++ ++func (s *store) getAutoUserNS(_ *types.AutoUserNsOptions, _ *Image, _ rwLayerStore, _ []roLayerStore) ([]idtools.IDMap, []idtools.IDMap, error) { ++ return nil, nil, errors.New("user namespaces are not supported on this platform") ++} +-- +2.25.1 + -- cgit v1.2.3-54-g00ecf