From 219b6c99e0fed25f8dff6999f02854d66d42532b Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 22 Sep 2015 11:12:55 -0700 Subject: [PATCH] Ignore changing /dev/null permissions if used in STDIO Whenever dev/null is used as one of the main processes STDIO, do not try to change the permissions on it via fchown because we should not do it in the first place and also this will fail if the container is supposed to be readonly. Signed-off-by: Michael Crosby --- libcontainer/init_linux.go | 42 +++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index 0433913a..ddb11865 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -187,16 +187,10 @@ func setupUser(config *initConfig) error { return err } } - // change the permissions on the STDIO of the current process so that when the user - // is changed for the container, it's STDIO of the process matches the user. - for _, fd := range []uintptr{ - os.Stdin.Fd(), - os.Stderr.Fd(), - os.Stdout.Fd(), - } { - if err := syscall.Fchown(int(fd), execUser.Uid, execUser.Gid); err != nil { - return err - } + // before we change to the container's user make sure that the processes STDIO + // is correctly owned by the user that we are switching to. + if err := fixStdioPermissions(execUser); err != nil { + return err } suppGroups := append(execUser.Sgids, addGroups...) if err := syscall.Setgroups(suppGroups); err != nil { @@ -218,6 +212,34 @@ func setupUser(config *initConfig) error { return nil } +// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user. +// The ownership needs to match because it is created outside of the container and needs to be +// localized. +func fixStdioPermissions(u *user.ExecUser) error { + var null syscall.Stat_t + if err := syscall.Stat("/dev/null", &null); err != nil { + return err + } + for _, fd := range []uintptr{ + os.Stdin.Fd(), + os.Stderr.Fd(), + os.Stdout.Fd(), + } { + var s syscall.Stat_t + if err := syscall.Fstat(int(fd), &s); err != nil { + return err + } + // skip chown of /dev/null if it was used as one of the STDIO fds. + if s.Rdev == null.Rdev { + continue + } + if err := syscall.Fchown(int(fd), u.Uid, u.Gid); err != nil { + return err + } + } + return nil +} + // setupNetwork sets up and initializes any network interface inside the container. func setupNetwork(config *initConfig) error { for _, config := range config.Networks {