From 6c28b7232cdb0c8d9137ad04c2dbdd5cd46afa08f74796c5da302855507f0b6a Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Fri, 13 Jan 2017 17:01:54 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/Virtualization:containers/runc?expand=0&rev=36 --- CVE-2016-8649.patch | 225 +++++++++++++++++++++++++++++++++++ _service | 2 +- runc-git.50a19c6.tar.xz | 3 + runc-git.f59ba3cdd76f.tar.xz | 3 - runc.changes | 7 ++ runc.spec | 8 +- 6 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 CVE-2016-8649.patch create mode 100644 runc-git.50a19c6.tar.xz delete mode 100644 runc-git.f59ba3cdd76f.tar.xz diff --git a/CVE-2016-8649.patch b/CVE-2016-8649.patch new file mode 100644 index 0000000..a7598ff --- /dev/null +++ b/CVE-2016-8649.patch @@ -0,0 +1,225 @@ +From 9729fc3127c5e27d59a0126f52e91c61037173ae Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Tue, 29 Nov 2016 01:25:06 +1100 +Subject: [PATCH] libcontainer: init: only pass stateDirFd when creating a + container + +If we pass a file descriptor to the host filesystem while joining a +container, there is a race condition where a process inside the +container can ptrace(2) the joining process and stop it from closing its +file descriptor to the stateDirFd. Then the process can access the *host* +filesystem from that file descriptor. + +To fix this, don't open or pass the stateDirFd to the init process +unless we're creating a new container. A proper fix for this would be to +remove the need for even passing around directory file descriptors +(which are quite dangerous in the context of mount namespaces). + +SUSE: This is an additional patch which was the original "fix" for the + CVE. It was agreed that the patch was too complicated to be used + as a fix for a security issue -- but it will be upstreamed once + the embargo passes. + +Fixes: CVE-2016-8649 +Signed-off-by: Aleksa Sarai +--- + libcontainer/container_linux.go | 30 ++++++++++++++++++------------ + libcontainer/factory_linux.go | 37 ++++++++++++++++++++++--------------- + libcontainer/init_linux.go | 3 +-- + libcontainer/process_linux.go | 2 -- + libcontainer/setns_init_linux.go | 7 +------ + 5 files changed, 42 insertions(+), 37 deletions(-) + +diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go +index 29c8b3437be3..4110af6fd89d 100644 +--- a/libcontainer/container_linux.go ++++ b/libcontainer/container_linux.go +@@ -294,21 +294,29 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces + if err != nil { + return nil, newSystemErrorWithCause(err, "creating new init pipe") + } +- rootDir, err := os.Open(c.root) +- if err != nil { +- return nil, err +- } +- cmd, err := c.commandTemplate(p, childPipe, rootDir) ++ cmd, err := c.commandTemplate(p, childPipe) + if err != nil { + return nil, newSystemErrorWithCause(err, "creating new command template") + } + if !doInit { +- return c.newSetnsProcess(p, cmd, parentPipe, childPipe, rootDir) ++ return c.newSetnsProcess(p, cmd, parentPipe, childPipe) + } ++ ++ // We only set up rootDir if we're not doing a `runc exec`. The reason for ++ // this is to avoid cases where a racing, unprivileged process inside the ++ // container can get access to the statedir file descriptor (which would ++ // allow for container rootfs escape). ++ rootDir, err := os.Open(c.root) ++ if err != nil { ++ return nil, err ++ } ++ cmd.ExtraFiles = append(cmd.ExtraFiles, rootDir) ++ cmd.Env = append(cmd.Env, ++ fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1)) + return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir) + } + +-func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File) (*exec.Cmd, error) { ++func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) { + cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...) + cmd.Stdin = p.Stdin + cmd.Stdout = p.Stdout +@@ -317,10 +325,9 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } +- cmd.ExtraFiles = append(p.ExtraFiles, childPipe, rootDir) ++ cmd.ExtraFiles = append(p.ExtraFiles, childPipe) + cmd.Env = append(cmd.Env, +- fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-2), +- fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1)) ++ fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1)) + // NOTE: when running a container with no PID namespace and the parent process spawning the container is + // PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason + // even with the parent still running. +@@ -357,7 +364,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c + }, nil + } + +-func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*setnsProcess, error) { ++func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*setnsProcess, error) { + cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns)) + state, err := c.currentState() + if err != nil { +@@ -378,7 +385,6 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, + config: c.newInitConfig(p), + process: p, + bootstrapData: data, +- rootDir: rootDir, + }, nil + } + +diff --git a/libcontainer/factory_linux.go b/libcontainer/factory_linux.go +index cc336c147fb9..e3478d5b50e3 100644 +--- a/libcontainer/factory_linux.go ++++ b/libcontainer/factory_linux.go +@@ -222,27 +222,34 @@ func (l *LinuxFactory) Type() string { + // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state + // This is a low level implementation detail of the reexec and should not be consumed externally + func (l *LinuxFactory) StartInitialization() (err error) { +- var pipefd, rootfd int +- for _, pair := range []struct { +- k string +- v *int +- }{ +- {"_LIBCONTAINER_INITPIPE", &pipefd}, +- {"_LIBCONTAINER_STATEDIR", &rootfd}, +- } { +- +- s := os.Getenv(pair.k) ++ var ( ++ pipefd, rootfd int ++ envInitPipe = os.Getenv("_LIBCONTAINER_INITPIPE") ++ envStateDir = os.Getenv("_LIBCONTAINER_STATEDIR") ++ ) + +- i, err := strconv.Atoi(s) +- if err != nil { +- return fmt.Errorf("unable to convert %s=%s to int", pair.k, s) +- } +- *pair.v = i ++ // Get the INITPIPE. ++ pipefd, err = strconv.Atoi(envInitPipe) ++ if err != nil { ++ return fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE=%s to int: %s", envInitPipe, err) + } ++ + var ( + pipe = os.NewFile(uintptr(pipefd), "pipe") + it = initType(os.Getenv("_LIBCONTAINER_INITTYPE")) + ) ++ ++ // Only get the STATEDIR if we're an init process. It's a bit late to do ++ // anything about this if we've already brought in an fd (a racing process ++ // could've opened the fd by now because we're in the PID namespace). ++ rootfd = -1 ++ if it == initStandard { ++ rootfd, err = strconv.Atoi(envStateDir) ++ if err != nil { ++ return fmt.Errorf("unable to convert _LIBCONTAINER_STATEDIR=%s to int: %s", envStateDir, err) ++ } ++ } ++ + // clear the current process's environment to clean any libcontainer + // specific env vars. + os.Clearenv() +diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go +index 4043d51c0bd0..b1e6762ecdf3 100644 +--- a/libcontainer/init_linux.go ++++ b/libcontainer/init_linux.go +@@ -77,8 +77,7 @@ func newContainerInit(t initType, pipe *os.File, stateDirFD int) (initer, error) + switch t { + case initSetns: + return &linuxSetnsInit{ +- config: config, +- stateDirFD: stateDirFD, ++ config: config, + }, nil + case initStandard: + return &linuxStandardInit{ +diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go +index 5b81317fd711..7b90c6b8faae 100644 +--- a/libcontainer/process_linux.go ++++ b/libcontainer/process_linux.go +@@ -51,7 +51,6 @@ type setnsProcess struct { + fds []string + process *Process + bootstrapData io.Reader +- rootDir *os.File + } + + func (p *setnsProcess) startTime() (string, error) { +@@ -70,7 +69,6 @@ func (p *setnsProcess) start() (err error) { + defer p.parentPipe.Close() + err = p.cmd.Start() + p.childPipe.Close() +- p.rootDir.Close() + if err != nil { + return newSystemErrorWithCause(err, "starting setns process") + } +diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go +index 7f5f182402cc..2a8f34528179 100644 +--- a/libcontainer/setns_init_linux.go ++++ b/libcontainer/setns_init_linux.go +@@ -5,7 +5,6 @@ package libcontainer + import ( + "fmt" + "os" +- "syscall" + + "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/opencontainers/runc/libcontainer/keys" +@@ -17,8 +16,7 @@ import ( + // linuxSetnsInit performs the container's initialization for running a new process + // inside an existing container. + type linuxSetnsInit struct { +- config *initConfig +- stateDirFD int ++ config *initConfig + } + + func (l *linuxSetnsInit) getSessionRingName() string { +@@ -51,8 +49,5 @@ func (l *linuxSetnsInit) Init() error { + if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil { + return err + } +- // close the statedir fd before exec because the kernel resets dumpable in the wrong order +- // https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1290-L1318 +- syscall.Close(l.stateDirFD) + return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) + } +-- +2.11.0 + diff --git a/_service b/_service index 08404c0..a835680 100644 --- a/_service +++ b/_service @@ -8,7 +8,7 @@ git runc git.%h - f59ba3cdd76fdc08c004f42aa915996f6f420899 + 50a19c6ff828 .git diff --git a/runc-git.50a19c6.tar.xz b/runc-git.50a19c6.tar.xz new file mode 100644 index 0000000..903c79c --- /dev/null +++ b/runc-git.50a19c6.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:971d6eaeb495b0d3368004521dc73fba670d4e0e3fd51408d450cd5bdc393dcf +size 406228 diff --git a/runc-git.f59ba3cdd76f.tar.xz b/runc-git.f59ba3cdd76f.tar.xz deleted file mode 100644 index 53976c3..0000000 --- a/runc-git.f59ba3cdd76f.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a82fddecb61fbef51d9dc1de2db9688c0936ea48ad7f023c65d8341aea8d2421 -size 406472 diff --git a/runc.changes b/runc.changes index 1ad7f3c..c723aeb 100644 --- a/runc.changes +++ b/runc.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Jan 13 13:58:33 UTC 2017 - jmassaguerpla@suse.com + +- fix CVE-2016-9962 bsc#1012568 and applying the patch + CVE-2016-9962.patch, because 1.12.6 partially fixes it (it contains + the first patch attached in bsc#1012568) + ------------------------------------------------------------------- Mon Dec 19 12:49:38 UTC 2016 - jmassaguerpla@suse.com diff --git a/runc.spec b/runc.spec index 5022aff..f7e7703 100644 --- a/runc.spec +++ b/runc.spec @@ -1,7 +1,7 @@ # # spec file for package runc # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -40,13 +40,13 @@ # FIX-OPENSUSE: This will be removed as soon as we move Docker's runC fork into # a separate package. This whole versioning mess is caused by # Docker vendoring non-releases of runC. -%define git_version f59ba3cdd76f +%define git_version 50a19c6 # How to get the git_revision # git clone ${url}.git runc-upstream # cd runc-upstream # git checkout $git_version # git_revision=r$(git rev-list HEAD | wc -l) -%define git_revision r2818 +%define git_revision r2819 %define version_unconverted %{git_version} Name: runc @@ -57,6 +57,7 @@ License: Apache-2.0 Group: System/Management Url: https://github.com/opencontainers/runc Source: %{name}-git.%{git_version}.tar.xz +Patch0: CVE-2016-8649.patch BuildRequires: fdupes %ifarch %go_arches BuildRequires: go >= 1.5 @@ -102,6 +103,7 @@ Test package for runc. It contains the source code and the tests. %prep %setup -q -n %{name}-git.%{git_version} +%patch0 -p1 %build # Do not use symlinks. If you want to run the unit tests for this package at