From 9258eac0727f6c6323459c0b0d32924fc1091428 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 3 Feb 2022 17:23:13 -0800 Subject: [PATCH] libct/start: use execabs for newuidmap lookup Since we are looking up the path to newuidmap/newgidmap in one context, and executing those in another (libct/nsenter), it might make sense to use a stricter rules for looking up path to those binaries. Practically it means that if someone wants to use custom newuidmap and newgidmap binaries from $PATH, it would be impossible to use these from the current directory by means of PATH=.:$PATH; instead one would have to do something like PATH=$(pwd):$PATH. See https://go.dev/blog/path-security for background. Signed-off-by: Kir Kolyshkin --- libcontainer/container_linux.go | 5 +- vendor/golang.org/x/sys/execabs/execabs.go | 102 +++++++++++++++++++++ vendor/modules.txt | 1 + 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 vendor/golang.org/x/sys/execabs/execabs.go diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index b285a31753d..4fe133d0072 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -23,6 +23,7 @@ import ( "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink/nl" + "golang.org/x/sys/execabs" "golang.org/x/sys/unix" "google.golang.org/protobuf/proto" @@ -2150,7 +2151,7 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na // We resolve the paths for new{u,g}idmap from // the context of runc to avoid doing a path // lookup in the nsexec context. - if path, err := exec.LookPath("newuidmap"); err == nil { + if path, err := execabs.LookPath("newuidmap"); err == nil { r.AddData(&Bytemsg{ Type: UidmapPathAttr, Value: []byte(path), @@ -2178,7 +2179,7 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na Value: b, }) if c.config.RootlessEUID { - if path, err := exec.LookPath("newgidmap"); err == nil { + if path, err := execabs.LookPath("newgidmap"); err == nil { r.AddData(&Bytemsg{ Type: GidmapPathAttr, Value: []byte(path), diff --git a/vendor/golang.org/x/sys/execabs/execabs.go b/vendor/golang.org/x/sys/execabs/execabs.go new file mode 100644 index 00000000000..78192498db0 --- /dev/null +++ b/vendor/golang.org/x/sys/execabs/execabs.go @@ -0,0 +1,102 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package execabs is a drop-in replacement for os/exec +// that requires PATH lookups to find absolute paths. +// That is, execabs.Command("cmd") runs the same PATH lookup +// as exec.Command("cmd"), but if the result is a path +// which is relative, the Run and Start methods will report +// an error instead of running the executable. +// +// See https://blog.golang.org/path-security for more information +// about when it may be necessary or appropriate to use this package. +package execabs + +import ( + "context" + "fmt" + "os/exec" + "path/filepath" + "reflect" + "unsafe" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +// It is an alias for exec.ErrNotFound. +var ErrNotFound = exec.ErrNotFound + +// Cmd represents an external command being prepared or run. +// It is an alias for exec.Cmd. +type Cmd = exec.Cmd + +// Error is returned by LookPath when it fails to classify a file as an executable. +// It is an alias for exec.Error. +type Error = exec.Error + +// An ExitError reports an unsuccessful exit by a command. +// It is an alias for exec.ExitError. +type ExitError = exec.ExitError + +func relError(file, path string) error { + return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path) +} + +// LookPath searches for an executable named file in the directories +// named by the PATH environment variable. If file contains a slash, +// it is tried directly and the PATH is not consulted. The result will be +// an absolute path. +// +// LookPath differs from exec.LookPath in its handling of PATH lookups, +// which are used for file names without slashes. If exec.LookPath's +// PATH lookup would have returned an executable from the current directory, +// LookPath instead returns an error. +func LookPath(file string) (string, error) { + path, err := exec.LookPath(file) + if err != nil { + return "", err + } + if filepath.Base(file) == file && !filepath.IsAbs(path) { + return "", relError(file, path) + } + return path, nil +} + +func fixCmd(name string, cmd *exec.Cmd) { + if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) { + // exec.Command was called with a bare binary name and + // exec.LookPath returned a path which is not absolute. + // Set cmd.lookPathErr and clear cmd.Path so that it + // cannot be run. + lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer())) + if *lookPathErr == nil { + *lookPathErr = relError(name, cmd.Path) + } + cmd.Path = "" + } +} + +// CommandContext is like Command but includes a context. +// +// The provided context is used to kill the process (by calling os.Process.Kill) +// if the context becomes done before the command completes on its own. +func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd { + cmd := exec.CommandContext(ctx, name, arg...) + fixCmd(name, cmd) + return cmd + +} + +// Command returns the Cmd struct to execute the named program with the given arguments. +// See exec.Command for most details. +// +// Command differs from exec.Command in its handling of PATH lookups, +// which are used when the program name contains no slashes. +// If exec.Command would have returned an exec.Cmd configured to run an +// executable from the current directory, Command instead +// returns an exec.Cmd that will return an error from Start or Run. +func Command(name string, arg ...string) *exec.Cmd { + cmd := exec.Command(name, arg...) + fixCmd(name, cmd) + return cmd +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e6cfb193e46..17a6794f60d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -72,6 +72,7 @@ github.com/vishvananda/netns golang.org/x/net/bpf # golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c ## explicit +golang.org/x/sys/execabs golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix golang.org/x/sys/windows