From cb9f5ac65dc00dc8a7f859bc422483950d180e83 Mon Sep 17 00:00:00 2001
From: Aleksa Sarai <asarai@suse.de>
Date: Mon, 30 Sep 2019 00:35:33 +1000
Subject: [PATCH] CVE-2019-16884

This is a backport of the following patches:

  * e12201c719ac ("vendor: update github.com/opencontainers/selinux")
  * 5db97bbdef9f ("*: verify that operations on /proc/... are on procfs")

SUSE-Bugs: CVE-2019-16884 bsc#1152308
Signed-off-by: Aleksa Sarai <asarai@suse.de>
---
 libcontainer/apparmor/apparmor.go             | 10 ++++-
 libcontainer/utils/utils_unix.go              | 44 ++++++++++++++-----
 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 ++++++
 6 files changed, 100 insertions(+), 20 deletions(-)

diff --git a/libcontainer/apparmor/apparmor.go b/libcontainer/apparmor/apparmor.go
index 7fff0627fa1b..debfc1e489ed 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/<tid>/ 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 c96088988a6d..1576f2d4ab63 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/vendor.conf b/vendor.conf
index e3f8e6d7ea06..a29764cd73c3 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -6,7 +6,7 @@ github.com/opencontainers/runtime-spec  29686dbc5559d93fb1ef402eeda3e35c38d75af4
 # Core libcontainer functionality.
 github.com/checkpoint-restore/go-criu   17b0214f6c48980c45dc47ecb0cfd6d9e02df723 # v3.11
 github.com/mrunalp/fileutils            7d4729fb36185a7c1719923406c9d40e54fb93c7
-github.com/opencontainers/selinux       3a1f366feb7aecbf7a0e71ac4cea88b31597de9e # v1.2.2
+github.com/opencontainers/selinux       5215b1806f52b1fcc2070a8826c542c9d33cd3cf # v1.3.0 (+ CVE-2019-16884)
 github.com/seccomp/libseccomp-golang    689e3c1541a84461afc49c1c87352a6cedf72e9c # v0.9.1
 github.com/sirupsen/logrus              8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1
 github.com/syndtr/gocapability          d98352740cb2c55f81556b63d4a1ec64c5a319c2
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 1eb9a6bf2527..2730fcf4a9ad 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 d7786c33c197..8cdf1b054ace 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 79b005d194c9..0c2e1cd38e79 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 {
-- 
2.23.0

