diff --git a/SOURCES/9aef50441511f0e9954d31d5ae84429040032e7c.patch b/SOURCES/9aef50441511f0e9954d31d5ae84429040032e7c.patch new file mode 100644 index 0000000..b077fb5 --- /dev/null +++ b/SOURCES/9aef50441511f0e9954d31d5ae84429040032e7c.patch @@ -0,0 +1,179 @@ +From 9aef50441511f0e9954d31d5ae84429040032e7c Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Mon, 30 Sep 2019 00:35:33 +1000 +Subject: [PATCH] vendor: update github.com/opencontainers/selinux + +This is a bump to v1.3.0, plus the necessary CVE-2019-16884 mitigation. + +Signed-off-by: Aleksa Sarai +--- + vendor.conf | 2 +- + .../selinux/go-selinux/label/label_selinux.go | 18 ++++++---- + .../selinux/go-selinux/selinux_linux.go | 33 +++++++++++++++++++ + .../selinux/go-selinux/selinux_stub.go | 13 ++++++++ + 4 files changed, 58 insertions(+), 8 deletions(-) + +diff --git a/vendor.conf b/vendor.conf +index e3f8e6d7e..a29764cd7 100644 +--- a/vendor.conf ++++ b/vendor.conf +@@ -5,7 +5,7 @@ + # Core libcontainer functionality. + github.com/checkpoint-restore/go-criu v3.11 + github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08 +-github.com/opencontainers/selinux v1.2.2 ++github.com/opencontainers/selinux 5215b1806f52b1fcc2070a8826c542c9d33cd3cf # v1.3.0 (+ CVE-2019-16884) + github.com/seccomp/libseccomp-golang 84e90a91acea0f4e51e62bc1a75de18b1fc0790f + github.com/sirupsen/logrus a3f95b5c423586578a4e099b11a46c2479628cac + github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 +diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +index 1eb9a6bf2..2730fcf4a 100644 +--- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go ++++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +@@ -13,11 +13,12 @@ import ( + + // Valid Label Options + var validOptions = map[string]bool{ +- "disable": true, +- "type": true, +- "user": true, +- "role": true, +- "level": true, ++ "disable": true, ++ "type": true, ++ "filetype": true, ++ "user": true, ++ "role": true, ++ "level": true, + } + + var ErrIncompatibleLabel = fmt.Errorf("Bad SELinux option z and Z can not be used together") +@@ -51,13 +52,16 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) { + return "", mountLabel, nil + } + if i := strings.Index(opt, ":"); i == -1 { +- return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type' followed by ':' and a value", opt) ++ return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt) + } + con := strings.SplitN(opt, ":", 2) + if !validOptions[con[0]] { +- return "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type'", con[0]) ++ return "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) + + } ++ if con[0] == "filetype" { ++ mcon["type"] = con[1] ++ } + pcon[con[0]] = con[1] + if con[0] == "level" || con[0] == "user" { + mcon[con[0]] = con[1] +diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +index d7786c33c..8cdf1b054 100644 +--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go ++++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +@@ -18,6 +18,8 @@ import ( + "strings" + "sync" + "syscall" ++ ++ "golang.org/x/sys/unix" + ) + + const ( +@@ -252,6 +254,12 @@ func getSELinuxPolicyRoot() string { + return filepath.Join(selinuxDir, readConfig(selinuxTypeTag)) + } + ++func isProcHandle(fh *os.File) (bool, error) { ++ var buf unix.Statfs_t ++ err := unix.Fstatfs(int(fh.Fd()), &buf) ++ return buf.Type == unix.PROC_SUPER_MAGIC, err ++} ++ + func readCon(fpath string) (string, error) { + if fpath == "" { + return "", ErrEmptyPath +@@ -263,6 +271,12 @@ func readCon(fpath string) (string, error) { + } + defer in.Close() + ++ if ok, err := isProcHandle(in); err != nil { ++ return "", err ++ } else if !ok { ++ return "", fmt.Errorf("%s not on procfs", fpath) ++ } ++ + var retval string + if _, err := fmt.Fscanf(in, "%s", &retval); err != nil { + return "", err +@@ -345,6 +359,12 @@ func writeCon(fpath string, val string) error { + } + defer out.Close() + ++ if ok, err := isProcHandle(out); err != nil { ++ return err ++ } else if !ok { ++ return fmt.Errorf("%s not on procfs", fpath) ++ } ++ + if val != "" { + _, err = out.Write([]byte(val)) + } else { +@@ -392,6 +412,14 @@ func SetExecLabel(label string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label) + } + ++/* ++SetTaskLabel sets the SELinux label for the current thread, or an error. ++This requires the dyntransition permission. ++*/ ++func SetTaskLabel(label string) error { ++ return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()), label) ++} ++ + // SetSocketLabel takes a process label and tells the kernel to assign the + // label to the next socket that gets created + func SetSocketLabel(label string) error { +@@ -403,6 +431,11 @@ func SocketLabel() (string, error) { + return readCon(fmt.Sprintf("/proc/self/task/%d/attr/sockcreate", syscall.Gettid())) + } + ++// PeerLabel retrieves the label of the client on the other side of a socket ++func PeerLabel(fd uintptr) (string, error) { ++ return unix.GetsockoptString(int(fd), syscall.SOL_SOCKET, syscall.SO_PEERSEC) ++} ++ + // SetKeyLabel takes a process label and tells the kernel to assign the + // label to the next kernel keyring that gets created + func SetKeyLabel(label string) error { +diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go +index 79b005d19..0c2e1cd38 100644 +--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go ++++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go +@@ -96,6 +96,14 @@ func SetExecLabel(label string) error { + return nil + } + ++/* ++SetTaskLabel sets the SELinux label for the current thread, or an error. ++This requires the dyntransition permission. ++*/ ++func SetTaskLabel(label string) error { ++ return nil ++} ++ + /* + SetSocketLabel sets the SELinux label that the kernel will use for any programs + that are executed by the current process thread, or an error. +@@ -109,6 +117,11 @@ func SocketLabel() (string, error) { + return "", nil + } + ++// PeerLabel retrieves the label of the client on the other side of a socket ++func PeerLabel(fd uintptr) (string, error) { ++ return "", nil ++} ++ + // SetKeyLabel takes a process label and tells the kernel to assign the + // label to the next kernel keyring that gets created + func SetKeyLabel(label string) error { diff --git a/SOURCES/d463f6485b809b5ea738f84e05ff5b456058a184.patch b/SOURCES/d463f6485b809b5ea738f84e05ff5b456058a184.patch new file mode 100644 index 0000000..a751010 --- /dev/null +++ b/SOURCES/d463f6485b809b5ea738f84e05ff5b456058a184.patch @@ -0,0 +1,140 @@ +From d463f6485b809b5ea738f84e05ff5b456058a184 Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Fri, 27 Sep 2019 12:01:07 +1000 +Subject: [PATCH] *: verify that operations on /proc/... are on procfs + +This is an additional mitigation for CVE-2019-16884. The primary problem +is that Docker can be coerced into bind-mounting a file system on top of +/proc (resulting in label-related writes to /proc no longer happening). + +While we are working on mitigations against permitting the mounts, this +helps avoid our code from being tricked into writing to non-procfs +files. This is not a perfect solution (after all, there might be a +bind-mount of a different procfs file over the target) but in order to +exploit that you would need to be able to tweak a config.json pretty +specifically (which thankfully Docker doesn't allow). + +Specifically this stops AppArmor from not labeling a process silently +due to /proc/self/attr/... being incorrectly set, and stops any +accidental fd leaks because /proc/self/fd/... is not real. + +Signed-off-by: Aleksa Sarai +--- + libcontainer/apparmor/apparmor.go | 10 +++++-- + libcontainer/utils/utils_unix.go | 44 ++++++++++++++++++++++++------- + 2 files changed, 42 insertions(+), 12 deletions(-) + +diff --git a/libcontainer/apparmor/apparmor.go b/libcontainer/apparmor/apparmor.go +index 7fff0627f..debfc1e48 100644 +--- a/libcontainer/apparmor/apparmor.go ++++ b/libcontainer/apparmor/apparmor.go +@@ -6,6 +6,8 @@ import ( + "fmt" + "io/ioutil" + "os" ++ ++ "github.com/opencontainers/runc/libcontainer/utils" + ) + + // IsEnabled returns true if apparmor is enabled for the host. +@@ -19,7 +21,7 @@ func IsEnabled() bool { + return false + } + +-func setprocattr(attr, value string) error { ++func setProcAttr(attr, value string) error { + // Under AppArmor you can only change your own attr, so use /proc/self/ + // instead of /proc// like libapparmor does + path := fmt.Sprintf("/proc/self/attr/%s", attr) +@@ -30,6 +32,10 @@ func setprocattr(attr, value string) error { + } + defer f.Close() + ++ if err := utils.EnsureProcHandle(f); err != nil { ++ return err ++ } ++ + _, err = fmt.Fprintf(f, "%s", value) + return err + } +@@ -37,7 +43,7 @@ func setprocattr(attr, value string) error { + // changeOnExec reimplements aa_change_onexec from libapparmor in Go + func changeOnExec(name string) error { + value := "exec " + name +- if err := setprocattr("exec", value); err != nil { ++ if err := setProcAttr("exec", value); err != nil { + return fmt.Errorf("apparmor failed to apply profile: %s", err) + } + return nil +diff --git a/libcontainer/utils/utils_unix.go b/libcontainer/utils/utils_unix.go +index c96088988..1576f2d4a 100644 +--- a/libcontainer/utils/utils_unix.go ++++ b/libcontainer/utils/utils_unix.go +@@ -3,33 +3,57 @@ + package utils + + import ( +- "io/ioutil" ++ "fmt" + "os" + "strconv" + + "golang.org/x/sys/unix" + ) + ++// EnsureProcHandle returns whether or not the given file handle is on procfs. ++func EnsureProcHandle(fh *os.File) error { ++ var buf unix.Statfs_t ++ if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil { ++ return fmt.Errorf("ensure %s is on procfs: %v", fh.Name(), err) ++ } ++ if buf.Type != unix.PROC_SUPER_MAGIC { ++ return fmt.Errorf("%s is not on procfs", fh.Name()) ++ } ++ return nil ++} ++ ++// CloseExecFrom applies O_CLOEXEC to all file descriptors currently open for ++// the process (except for those below the given fd value). + func CloseExecFrom(minFd int) error { +- fdList, err := ioutil.ReadDir("/proc/self/fd") ++ fdDir, err := os.Open("/proc/self/fd") ++ if err != nil { ++ return err ++ } ++ defer fdDir.Close() ++ ++ if err := EnsureProcHandle(fdDir); err != nil { ++ return err ++ } ++ ++ fdList, err := fdDir.Readdirnames(-1) + if err != nil { + return err + } +- for _, fi := range fdList { +- fd, err := strconv.Atoi(fi.Name()) ++ for _, fdStr := range fdList { ++ fd, err := strconv.Atoi(fdStr) ++ // Ignore non-numeric file names. + if err != nil { +- // ignore non-numeric file names + continue + } +- ++ // Ignore descriptors lower than our specified minimum. + if fd < minFd { +- // ignore descriptors lower than our specified minimum + continue + } +- +- // intentionally ignore errors from unix.CloseOnExec ++ // Intentionally ignore errors from unix.CloseOnExec -- the cases where ++ // this might fail are basically file descriptors that have already ++ // been closed (including and especially the one that was created when ++ // ioutil.ReadDir did the "opendir" syscall). + unix.CloseOnExec(fd) +- // the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall) + } + return nil + } diff --git a/SPECS/runc.spec b/SPECS/runc.spec index 95e95f3..7cf6021 100644 --- a/SPECS/runc.spec +++ b/SPECS/runc.spec @@ -26,7 +26,7 @@ go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" - Name: %{repo} Version: 1.0.0 -Release: 60.rc8%{?dist} +Release: 61.rc8%{?dist} Summary: CLI for running Open Containers ExcludeArch: %{ix86} License: ASL 2.0 @@ -34,6 +34,8 @@ URL: %{git0} Source0: %{git0}/archive/%{commit0}/%{name}-%{shortcommit0}.tar.gz Source1: 99-containers.conf Patch0: 1807.patch +Patch1: https://github.com/opencontainers/runc/commit/9aef50441511f0e9954d31d5ae84429040032e7c.patch +Patch2: https://github.com/opencontainers/runc/commit/d463f6485b809b5ea738f84e05ff5b456058a184.patch # If go_compiler is not set to 1, there is no virtual provide. Use golang instead. BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} >= 1.6.2 BuildRequires: git @@ -91,6 +93,10 @@ install -p -m 0644 contrib/completions/bash/%{name} %{buildroot}%{_datadir}/bash %{_datadir}/bash-completion/completions/%{name} %changelog +* Mon Nov 25 2019 Jindrich Novy - 1.0.0-61.rc8 +- backport patches for CVE-2019-16884 from upstream +- Resolves: #1764182 + * Mon Jun 17 2019 Lokesh Mandvekar - 1.0.0-60.rc8 - Resolves: #1721247 - enable fips mode