Hello community, here is the log from the commit of package runc for openSUSE:Factory checked in at 2017-01-23 11:36:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/runc (Old) and /work/SRC/openSUSE:Factory/.runc.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "runc" Changes: -------- --- /work/SRC/openSUSE:Factory/runc/runc.changes 2017-01-10 10:44:32.474394530 +0100 +++ /work/SRC/openSUSE:Factory/.runc.new/runc.changes 2017-01-23 11:36:54.941912764 +0100 @@ -1,0 +2,7 @@ +Fri Jan 13 13:58:33 UTC 2017 - [email protected] + +- 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) + +------------------------------------------------------------------- Old: ---- runc-git.f59ba3cdd76f.tar.xz New: ---- CVE-2016-9962.patch runc-git.50a19c6.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ runc.spec ++++++ --- /var/tmp/diff_new_pack.1y6yTQ/_old 2017-01-23 11:36:55.509832222 +0100 +++ /var/tmp/diff_new_pack.1y6yTQ/_new 2017-01-23 11:36:55.513831656 +0100 @@ -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 @@ Group: System/Management Url: https://github.com/opencontainers/runc Source: %{name}-git.%{git_version}.tar.xz +Patch0: CVE-2016-9962.patch BuildRequires: fdupes %ifarch %go_arches BuildRequires: go >= 1.5 @@ -102,6 +103,7 @@ %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 ++++++ CVE-2016-9962.patch ++++++ >From 9729fc3127c5e27d59a0126f52e91c61037173ae Mon Sep 17 00:00:00 2001 From: Aleksa Sarai <[email protected]> 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-9962 Signed-off-by: Aleksa Sarai <[email protected]> --- 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 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.1y6yTQ/_old 2017-01-23 11:36:55.561824849 +0100 +++ /var/tmp/diff_new_pack.1y6yTQ/_new 2017-01-23 11:36:55.561824849 +0100 @@ -8,7 +8,7 @@ <param name="scm">git</param> <param name="filename">runc</param> <param name="versionformat">git.%h</param> - <param name="revision">f59ba3cdd76fdc08c004f42aa915996f6f420899</param> + <param name="revision">50a19c6ff828</param> <param name="exclude">.git</param> </service> <service name="recompress" mode="disabled"> ++++++ runc-git.f59ba3cdd76f.tar.xz -> runc-git.50a19c6.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-git.f59ba3cdd76f/libcontainer/init_linux.go new/runc-git.50a19c6/libcontainer/init_linux.go --- old/runc-git.f59ba3cdd76f/libcontainer/init_linux.go 2016-10-21 00:22:03.000000000 +0200 +++ new/runc-git.50a19c6/libcontainer/init_linux.go 2016-12-17 18:01:09.000000000 +0100 @@ -77,7 +77,8 @@ switch t { case initSetns: return &linuxSetnsInit{ - config: config, + config: config, + stateDirFD: stateDirFD, }, nil case initStandard: return &linuxStandardInit{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-git.f59ba3cdd76f/libcontainer/nsenter/nsexec.c new/runc-git.50a19c6/libcontainer/nsenter/nsexec.c --- old/runc-git.f59ba3cdd76f/libcontainer/nsenter/nsexec.c 2016-10-21 00:22:03.000000000 +0200 +++ new/runc-git.50a19c6/libcontainer/nsenter/nsexec.c 2016-12-17 18:01:09.000000000 +0100 @@ -408,6 +408,11 @@ if (pipenum == -1) return; + /* make the process non-dumpable */ + if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) != 0) { + bail("failed to set process as non-dumpable"); + } + /* Parse all of the netlink configuration. */ nl_parse(pipenum, &config); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-git.f59ba3cdd76f/libcontainer/setns_init_linux.go new/runc-git.50a19c6/libcontainer/setns_init_linux.go --- old/runc-git.f59ba3cdd76f/libcontainer/setns_init_linux.go 2016-10-21 00:22:03.000000000 +0200 +++ new/runc-git.50a19c6/libcontainer/setns_init_linux.go 2016-12-17 18:01:09.000000000 +0100 @@ -5,6 +5,7 @@ import ( "fmt" "os" + "syscall" "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/keys" @@ -16,7 +17,8 @@ // linuxSetnsInit performs the container's initialization for running a new process // inside an existing container. type linuxSetnsInit struct { - config *initConfig + config *initConfig + stateDirFD int } func (l *linuxSetnsInit) getSessionRingName() string { @@ -49,5 +51,8 @@ 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()) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-git.f59ba3cdd76f/libcontainer/standard_init_linux.go new/runc-git.50a19c6/libcontainer/standard_init_linux.go --- old/runc-git.f59ba3cdd76f/libcontainer/standard_init_linux.go 2016-10-21 00:22:03.000000000 +0200 +++ new/runc-git.50a19c6/libcontainer/standard_init_linux.go 2016-12-17 18:01:09.000000000 +0100 @@ -171,6 +171,9 @@ return newSystemErrorWithCause(err, "init seccomp") } } + // 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) if err := syscall.Exec(name, l.config.Args[0:], os.Environ()); err != nil { return newSystemErrorWithCause(err, "exec user process") }
