mirror of
git://git.yoctoproject.org/meta-virtualization.git
synced 2025-10-22 23:13:04 +02:00
containerd-opencontainers: fix CVE-2024-40635
Upstream-Status: Backport from 9639b96255
Signed-off-by: Theo GAIGE <tgaige.opensource@witekio.com>
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
This commit is contained in:
parent
5429fc0f5c
commit
16155ae737
|
@ -0,0 +1,180 @@
|
||||||
|
From 6727fad7cc608f47304c073d758a04ca516e08fd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Craig Ingram <Cjingram@google.com>
|
||||||
|
Date: Fri, 7 Mar 2025 13:27:58 +0000
|
||||||
|
Subject: [PATCH] validate uid/gid
|
||||||
|
|
||||||
|
Upstream-Status: Backport [https://github.com/containerd/containerd/commit/9639b9625554183d0c4d8d072dccb84fedd2320f]
|
||||||
|
CVE: CVE-2024-40635
|
||||||
|
|
||||||
|
Signed-off-by: Theo GAIGE <tgaige.opensource@witekio.com>
|
||||||
|
---
|
||||||
|
oci/spec_opts.go | 24 ++++++++--
|
||||||
|
oci/spec_opts_linux_test.go | 92 +++++++++++++++++++++++++++++++++++++
|
||||||
|
2 files changed, 112 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/oci/spec_opts.go b/oci/spec_opts.go
|
||||||
|
index 3330ad108..1f10b24c6 100644
|
||||||
|
--- a/oci/spec_opts.go
|
||||||
|
+++ b/oci/spec_opts.go
|
||||||
|
@@ -22,6 +22,7 @@ import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
+ "math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
@@ -536,6 +537,20 @@ func WithUser(userstr string) SpecOpts {
|
||||||
|
defer ensureAdditionalGids(s)
|
||||||
|
setProcess(s)
|
||||||
|
s.Process.User.AdditionalGids = nil
|
||||||
|
+ // While the Linux kernel allows the max UID to be MaxUint32 - 2,
|
||||||
|
+ // and the OCI Runtime Spec has no definition about the max UID,
|
||||||
|
+ // the runc implementation is known to require the UID to be <= MaxInt32.
|
||||||
|
+ //
|
||||||
|
+ // containerd follows runc's limitation here.
|
||||||
|
+ //
|
||||||
|
+ // In future we may relax this limitation to allow MaxUint32 - 2,
|
||||||
|
+ // or, amend the OCI Runtime Spec to codify the implementation limitation.
|
||||||
|
+ const (
|
||||||
|
+ minUserID = 0
|
||||||
|
+ maxUserID = math.MaxInt32
|
||||||
|
+ minGroupID = 0
|
||||||
|
+ maxGroupID = math.MaxInt32
|
||||||
|
+ )
|
||||||
|
|
||||||
|
// For LCOW it's a bit harder to confirm that the user actually exists on the host as a rootfs isn't
|
||||||
|
// mounted on the host and shared into the guest, but rather the rootfs is constructed entirely in the
|
||||||
|
@@ -552,8 +567,8 @@ func WithUser(userstr string) SpecOpts {
|
||||||
|
switch len(parts) {
|
||||||
|
case 1:
|
||||||
|
v, err := strconv.Atoi(parts[0])
|
||||||
|
- if err != nil {
|
||||||
|
- // if we cannot parse as a uint they try to see if it is a username
|
||||||
|
+ if err != nil || v < minUserID || v > maxUserID {
|
||||||
|
+ // if we cannot parse as an int32 then try to see if it is a username
|
||||||
|
return WithUsername(userstr)(ctx, client, c, s)
|
||||||
|
}
|
||||||
|
return WithUserID(uint32(v))(ctx, client, c, s)
|
||||||
|
@@ -564,12 +579,13 @@ func WithUser(userstr string) SpecOpts {
|
||||||
|
)
|
||||||
|
var uid, gid uint32
|
||||||
|
v, err := strconv.Atoi(parts[0])
|
||||||
|
- if err != nil {
|
||||||
|
+ if err != nil || v < minUserID || v > maxUserID {
|
||||||
|
username = parts[0]
|
||||||
|
} else {
|
||||||
|
uid = uint32(v)
|
||||||
|
}
|
||||||
|
- if v, err = strconv.Atoi(parts[1]); err != nil {
|
||||||
|
+ v, err = strconv.Atoi(parts[1])
|
||||||
|
+ if err != nil || v < minGroupID || v > maxGroupID {
|
||||||
|
groupname = parts[1]
|
||||||
|
} else {
|
||||||
|
gid = uint32(v)
|
||||||
|
diff --git a/oci/spec_opts_linux_test.go b/oci/spec_opts_linux_test.go
|
||||||
|
index 904edcb42..f3be5ef27 100644
|
||||||
|
--- a/oci/spec_opts_linux_test.go
|
||||||
|
+++ b/oci/spec_opts_linux_test.go
|
||||||
|
@@ -31,6 +31,98 @@ import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
+// nolint:gosec
|
||||||
|
+func TestWithUser(t *testing.T) {
|
||||||
|
+ t.Parallel()
|
||||||
|
+
|
||||||
|
+ expectedPasswd := `root:x:0:0:root:/root:/bin/ash
|
||||||
|
+guest:x:405:100:guest:/dev/null:/sbin/nologin
|
||||||
|
+`
|
||||||
|
+ expectedGroup := `root:x:0:root
|
||||||
|
+bin:x:1:root,bin,daemon
|
||||||
|
+daemon:x:2:root,bin,daemon
|
||||||
|
+sys:x:3:root,bin,adm
|
||||||
|
+guest:x:100:guest
|
||||||
|
+`
|
||||||
|
+ td := t.TempDir()
|
||||||
|
+ apply := fstest.Apply(
|
||||||
|
+ fstest.CreateDir("/etc", 0777),
|
||||||
|
+ fstest.CreateFile("/etc/passwd", []byte(expectedPasswd), 0777),
|
||||||
|
+ fstest.CreateFile("/etc/group", []byte(expectedGroup), 0777),
|
||||||
|
+ )
|
||||||
|
+ if err := apply.Apply(td); err != nil {
|
||||||
|
+ t.Fatalf("failed to apply: %v", err)
|
||||||
|
+ }
|
||||||
|
+ c := containers.Container{ID: t.Name()}
|
||||||
|
+ testCases := []struct {
|
||||||
|
+ user string
|
||||||
|
+ expectedUID uint32
|
||||||
|
+ expectedGID uint32
|
||||||
|
+ err string
|
||||||
|
+ }{
|
||||||
|
+ {
|
||||||
|
+ user: "0",
|
||||||
|
+ expectedUID: 0,
|
||||||
|
+ expectedGID: 0,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "root:root",
|
||||||
|
+ expectedUID: 0,
|
||||||
|
+ expectedGID: 0,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "guest",
|
||||||
|
+ expectedUID: 405,
|
||||||
|
+ expectedGID: 100,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "guest:guest",
|
||||||
|
+ expectedUID: 405,
|
||||||
|
+ expectedGID: 100,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "guest:nobody",
|
||||||
|
+ err: "no groups found",
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "405:100",
|
||||||
|
+ expectedUID: 405,
|
||||||
|
+ expectedGID: 100,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "405:2147483648",
|
||||||
|
+ err: "no groups found",
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "-1000",
|
||||||
|
+ err: "no users found",
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ user: "2147483648",
|
||||||
|
+ err: "no users found",
|
||||||
|
+ },
|
||||||
|
+ }
|
||||||
|
+ for _, testCase := range testCases {
|
||||||
|
+ testCase := testCase
|
||||||
|
+ t.Run(testCase.user, func(t *testing.T) {
|
||||||
|
+ t.Parallel()
|
||||||
|
+ s := Spec{
|
||||||
|
+ Version: specs.Version,
|
||||||
|
+ Root: &specs.Root{
|
||||||
|
+ Path: td,
|
||||||
|
+ },
|
||||||
|
+ Linux: &specs.Linux{},
|
||||||
|
+ }
|
||||||
|
+ err := WithUser(testCase.user)(context.Background(), nil, &c, &s)
|
||||||
|
+ if err != nil {
|
||||||
|
+ assert.EqualError(t, err, testCase.err)
|
||||||
|
+ }
|
||||||
|
+ assert.Equal(t, testCase.expectedUID, s.Process.User.UID)
|
||||||
|
+ assert.Equal(t, testCase.expectedGID, s.Process.User.GID)
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
// nolint:gosec
|
||||||
|
func TestWithUserID(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
|
@ -9,6 +9,7 @@ SRCREV = "1e1ea6e986c6c86565bc33d52e34b81b3e2bc71f"
|
||||||
SRC_URI = "git://github.com/containerd/containerd;branch=release/1.6;protocol=https;destsuffix=git/src/github.com/containerd/containerd \
|
SRC_URI = "git://github.com/containerd/containerd;branch=release/1.6;protocol=https;destsuffix=git/src/github.com/containerd/containerd \
|
||||||
file://0001-Makefile-allow-GO_BUILD_FLAGS-to-be-externally-speci.patch \
|
file://0001-Makefile-allow-GO_BUILD_FLAGS-to-be-externally-speci.patch \
|
||||||
file://0001-build-don-t-use-gcflags-to-define-trimpath.patch \
|
file://0001-build-don-t-use-gcflags-to-define-trimpath.patch \
|
||||||
|
file://CVE-2024-40635.patch \
|
||||||
"
|
"
|
||||||
|
|
||||||
# Apache-2.0 for containerd
|
# Apache-2.0 for containerd
|
||||||
|
|
Loading…
Reference in New Issue
Block a user