Skip to content

Commit c8bd474

Browse files
authored
Merge pull request #9304 from mheon/100_cve_2021_20188
[v1.0] Correct handling of capabilities
2 parents 900d033 + 69daa67 commit c8bd474

File tree

3 files changed

+132
-48
lines changed

3 files changed

+132
-48
lines changed

libpod/container_api.go

+3-21
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package libpod
22

33
import (
44
"context"
5-
"fmt"
65
"io/ioutil"
76
"os"
87
"strconv"
@@ -11,9 +10,7 @@ import (
1110

1211
"github.com/containers/libpod/libpod/driver"
1312
"github.com/containers/libpod/pkg/inspect"
14-
"github.com/containers/libpod/pkg/lookup"
1513
"github.com/containers/storage/pkg/stringid"
16-
"github.com/docker/docker/daemon/caps"
1714
"github.com/pkg/errors"
1815
"github.com/sirupsen/logrus"
1916
"k8s.io/apimachinery/pkg/util/wait"
@@ -263,8 +260,6 @@ func (c *Container) Kill(signal uint) error {
263260
// TODO allow specifying streams to attach to
264261
// TODO investigate allowing exec without attaching
265262
func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string) error {
266-
var capList []string
267-
268263
locked := false
269264
if !c.batched {
270265
locked = true
@@ -287,22 +282,8 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
287282
if conState != ContainerStateRunning {
288283
return errors.Wrapf(ErrCtrStateInvalid, "cannot exec into container that is not running")
289284
}
290-
if privileged || c.config.Privileged {
291-
capList = caps.GetAllCapabilities()
292-
}
293285

294-
// If user was set, look it up in the container to get a UID to use on
295-
// the host
296-
hostUser := ""
297-
if user != "" {
298-
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, nil)
299-
if err != nil {
300-
return err
301-
}
302-
303-
// runc expects user formatted as uid:gid
304-
hostUser = fmt.Sprintf("%d:%d", execUser.Uid, execUser.Gid)
305-
}
286+
isPrivileged := privileged || c.config.Privileged
306287

307288
// Generate exec session ID
308289
// Ensure we don't conflict with an existing session ID
@@ -324,10 +305,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
324305

325306
logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID)
326307

327-
execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID)
308+
execCmd, processFile, err := c.runtime.ociRuntime.execContainer(c, cmd, env, tty, workDir, user, sessionID, isPrivileged)
328309
if err != nil {
329310
return errors.Wrapf(err, "error exec %s", c.ID())
330311
}
312+
defer os.Remove(processFile)
331313
chWait := make(chan error)
332314
go func() {
333315
chWait <- execCmd.Wait()

libpod/oci.go

+121-27
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ import (
1515
"syscall"
1616
"time"
1717

18+
"github.com/containers/libpod/pkg/lookup"
1819
"github.com/containers/libpod/pkg/rootless"
1920
"github.com/containers/libpod/pkg/util"
2021
"github.com/coreos/go-systemd/activation"
2122
"github.com/cri-o/ocicni/pkg/ocicni"
23+
"github.com/docker/docker/daemon/caps"
2224
spec "github.com/opencontainers/runtime-spec/specs-go"
2325
"github.com/opencontainers/selinux/go-selinux"
2426
"github.com/opencontainers/selinux/go-selinux/label"
@@ -735,18 +737,23 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error {
735737
// TODO: Add --detach support
736738
// TODO: Convert to use conmon
737739
// TODO: add --pid-file and use that to generate exec session tracking
738-
func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string) (*exec.Cmd, error) {
740+
func (r *OCIRuntime) execContainer(c *Container, cmd, env []string, tty bool, cwd, user, sessionID string, privileged bool) (*exec.Cmd, string, error) {
739741
if len(cmd) == 0 {
740-
return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
742+
return nil, "", errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
741743
}
742744

743745
if sessionID == "" {
744-
return nil, errors.Wrapf(ErrEmptyID, "must provide a session ID for exec")
746+
return nil, "", errors.Wrapf(ErrEmptyID, "must provide a session ID for exec")
745747
}
746748

747749
runtimeDir, err := util.GetRootlessRuntimeDir()
748750
if err != nil {
749-
return nil, err
751+
return nil, "", err
752+
}
753+
754+
processFile, err := prepareProcessExec(c, cmd, env, tty, cwd, user, sessionID, privileged)
755+
if err != nil {
756+
return nil, "", err
750757
}
751758

752759
args := []string{}
@@ -756,34 +763,14 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
756763

757764
args = append(args, "exec")
758765

759-
if cwd != "" {
760-
args = append(args, "--cwd", cwd)
761-
}
766+
args = append(args, "--process", processFile)
762767

763768
args = append(args, "--pid-file", c.execPidPath(sessionID))
764769

765-
if tty {
766-
args = append(args, "--tty")
767-
} else {
768-
args = append(args, "--tty=false")
769-
}
770-
771-
if user != "" {
772-
args = append(args, "--user", user)
773-
}
774-
775770
if c.config.Spec.Process.NoNewPrivileges {
776771
args = append(args, "--no-new-privs")
777772
}
778773

779-
for _, cap := range capAdd {
780-
args = append(args, "--cap", cap)
781-
}
782-
783-
for _, envVar := range env {
784-
args = append(args, "--env", envVar)
785-
}
786-
787774
// Append container ID and command
788775
args = append(args, c.ID())
789776
args = append(args, cmd...)
@@ -797,10 +784,10 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
797784
execCmd.Env = append(execCmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
798785

799786
if err := execCmd.Start(); err != nil {
800-
return nil, errors.Wrapf(err, "cannot start container %s", c.ID())
787+
return nil, "", errors.Wrapf(err, "cannot start container %s", c.ID())
801788
}
802789

803-
return execCmd, nil
790+
return execCmd, processFile, nil
804791
}
805792

806793
// execStopContainer stops all active exec sessions in a container
@@ -892,3 +879,110 @@ func (r *OCIRuntime) checkpointContainer(ctr *Container, options ContainerCheckp
892879
args = append(args, ctr.ID())
893880
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
894881
}
882+
883+
// prepareProcessExec returns the path of the process.json used in runc exec -p.
884+
// Returns path to the created exec process file. This will need to be removed
885+
// by the caller when they're done, best effort.
886+
func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, sessionID string, privileged bool) (string, error) {
887+
filename := filepath.Join(c.bundlePath(), fmt.Sprintf("exec-process-%s", sessionID))
888+
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0600)
889+
if err != nil {
890+
return "", err
891+
}
892+
defer f.Close()
893+
894+
pspec := c.config.Spec.Process
895+
pspec.SelinuxLabel = c.config.ProcessLabel
896+
pspec.Args = cmd
897+
// We need to default this to false else it will inherit terminal as true
898+
// from the container.
899+
pspec.Terminal = false
900+
if tty {
901+
pspec.Terminal = true
902+
}
903+
if len(env) > 0 {
904+
pspec.Env = append(pspec.Env, env...)
905+
}
906+
907+
if cwd != "" {
908+
pspec.Cwd = cwd
909+
910+
}
911+
912+
var addGroups []string
913+
var sgids []uint32
914+
915+
// if the user is empty, we should inherit the user that the container is currently running with
916+
if user == "" {
917+
user = c.config.User
918+
addGroups = c.config.Groups
919+
}
920+
921+
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, nil)
922+
if err != nil {
923+
return "", err
924+
}
925+
926+
if len(addGroups) > 0 {
927+
sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, nil)
928+
if err != nil {
929+
return "", errors.Wrapf(err, "error looking up supplemental groups for container %s exec session %s", c.ID(), sessionID)
930+
}
931+
}
932+
933+
// If user was set, look it up in the container to get a UID to use on
934+
// the host
935+
if user != "" || len(sgids) > 0 {
936+
if user != "" {
937+
for _, sgid := range execUser.Sgids {
938+
sgids = append(sgids, uint32(sgid))
939+
}
940+
}
941+
processUser := spec.User{
942+
UID: uint32(execUser.Uid),
943+
GID: uint32(execUser.Gid),
944+
AdditionalGids: sgids,
945+
}
946+
947+
pspec.User = processUser
948+
}
949+
950+
allCaps := caps.GetAllCapabilities()
951+
pspec.Capabilities.Effective = []string{}
952+
if privileged {
953+
pspec.Capabilities.Bounding = allCaps
954+
} else {
955+
pspec.Capabilities.Bounding = []string{}
956+
}
957+
pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
958+
if execUser.Uid == 0 {
959+
pspec.Capabilities.Effective = pspec.Capabilities.Bounding
960+
pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
961+
pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
962+
} else {
963+
pspec.Capabilities.Permitted = pspec.Capabilities.Effective
964+
pspec.Capabilities.Ambient = pspec.Capabilities.Effective
965+
}
966+
967+
hasHomeSet := false
968+
for _, s := range pspec.Env {
969+
if strings.HasPrefix(s, "HOME=") {
970+
hasHomeSet = true
971+
break
972+
}
973+
}
974+
if !hasHomeSet {
975+
pspec.Env = append(pspec.Env, fmt.Sprintf("HOME=%s", execUser.Home))
976+
}
977+
978+
processJSON, err := json.Marshal(pspec)
979+
if err != nil {
980+
return "", err
981+
}
982+
983+
if err := ioutil.WriteFile(filename, processJSON, 0644); err != nil {
984+
return "", err
985+
}
986+
987+
return filename, nil
988+
}

pkg/spec/spec.go

+8
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,14 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
325325
}
326326
} else {
327327
g.SetupPrivileged(true)
328+
if config.User != "" {
329+
user := strings.SplitN(config.User, ":", 2)[0]
330+
if user != "root" && user != "0" {
331+
g.Spec().Process.Capabilities.Effective = []string{}
332+
g.Spec().Process.Capabilities.Permitted = []string{}
333+
g.Spec().Process.Capabilities.Ambient = []string{}
334+
}
335+
}
328336
}
329337

330338
// HANDLE SECCOMP

0 commit comments

Comments
 (0)