Move user home detection to its own library.

PiperOrigin-RevId: 307977689
This commit is contained in:
Nicolas Lacasse
2020-04-22 22:18:21 -07:00
committed by gVisor bot
parent 0c586946ea
commit e69a871c7b
5 changed files with 114 additions and 135 deletions
+34
View File
@@ -0,0 +1,34 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
go_library(
name = "user",
srcs = ["user.go"],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/fspath",
"//pkg/sentry/fs",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
"//pkg/usermem",
],
)
go_test(
name = "user_test",
size = "small",
srcs = ["user_test.go"],
library = ":user",
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/sentry/fs",
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/contexttest",
"//pkg/usermem",
],
)
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package boot
package user
import (
"bufio"
@@ -136,10 +136,10 @@ func getExecUserHomeVFS2(ctx context.Context, mns *vfs.MountNamespace, uid auth.
return homeDir, nil
}
// maybeAddExecUserHome returns a new slice with the HOME enviroment variable
// MaybeAddExecUserHome returns a new slice with the HOME enviroment variable
// set if the slice does not already contain it, otherwise it returns the
// original slice unmodified.
func maybeAddExecUserHome(ctx context.Context, mns *fs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) {
func MaybeAddExecUserHome(ctx context.Context, mns *fs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) {
// Check if the envv already contains HOME.
for _, env := range envv {
if strings.HasPrefix(env, "HOME=") {
@@ -159,7 +159,10 @@ func maybeAddExecUserHome(ctx context.Context, mns *fs.MountNamespace, uid auth.
return append(envv, "HOME="+homeDir), nil
}
func maybeAddExecUserHomeVFS2(ctx context.Context, vmns *vfs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) {
// MaybeAddExecUserHomeVFS2 returns a new slice with the HOME enviroment
// variable set if the slice does not already contain it, otherwise it returns
// the original slice unmodified.
func MaybeAddExecUserHomeVFS2(ctx context.Context, vmns *vfs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) {
// Check if the envv already contains HOME.
for _, env := range envv {
if strings.HasPrefix(env, "HOME=") {
@@ -12,167 +12,111 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package boot
package user
import (
"io/ioutil"
"os"
"path/filepath"
"fmt"
"strings"
"syscall"
"testing"
specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/contexttest"
"gvisor.dev/gvisor/pkg/usermem"
)
func setupTempDir() (string, error) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "exec-user-test")
if err != nil {
return "", err
// createEtcPasswd creates /etc/passwd with the given contents and mode. If
// mode is empty, then no file will be created. If mode is not a regular file
// mode, then contents is ignored.
func createEtcPasswd(ctx context.Context, root *fs.Dirent, contents string, mode linux.FileMode) error {
if err := root.CreateDirectory(ctx, root, "etc", fs.FilePermsFromMode(0755)); err != nil {
return err
}
return tmpDir, nil
}
func setupPasswd(contents string, perms os.FileMode) func() (string, error) {
return func() (string, error) {
tmpDir, err := setupTempDir()
etc, err := root.Walk(ctx, root, "etc")
if err != nil {
return err
}
defer etc.DecRef()
switch mode.FileType() {
case 0:
// Don't create anything.
return nil
case linux.S_IFREG:
passwd, err := etc.Create(ctx, root, "passwd", fs.FileFlags{Write: true}, fs.FilePermsFromMode(mode))
if err != nil {
return "", err
return err
}
if err := os.Mkdir(filepath.Join(tmpDir, "etc"), 0777); err != nil {
return "", err
defer passwd.DecRef()
if _, err := passwd.Writev(ctx, usermem.BytesIOSequence([]byte(contents))); err != nil {
return err
}
f, err := os.Create(filepath.Join(tmpDir, "etc", "passwd"))
if err != nil {
return "", err
}
defer f.Close()
_, err = f.WriteString(contents)
if err != nil {
return "", err
}
err = f.Chmod(perms)
if err != nil {
return "", err
}
return tmpDir, nil
return nil
case linux.S_IFDIR:
return etc.CreateDirectory(ctx, root, "passwd", fs.FilePermsFromMode(mode))
case linux.S_IFIFO:
return etc.CreateFifo(ctx, root, "passwd", fs.FilePermsFromMode(mode))
default:
return fmt.Errorf("unknown file type %x", mode.FileType())
}
}
// TestGetExecUserHome tests the getExecUserHome function.
func TestGetExecUserHome(t *testing.T) {
tests := map[string]struct {
uid auth.KUID
createRoot func() (string, error)
expected string
uid auth.KUID
passwdContents string
passwdMode linux.FileMode
expected string
}{
"success": {
uid: 1000,
createRoot: setupPasswd("adin::1000:1111::/home/adin:/bin/sh", 0666),
expected: "/home/adin",
},
"no_passwd": {
uid: 1000,
createRoot: setupTempDir,
expected: "/",
uid: 1000,
passwdContents: "adin::1000:1111::/home/adin:/bin/sh",
passwdMode: linux.S_IFREG | 0666,
expected: "/home/adin",
},
"no_perms": {
uid: 1000,
createRoot: setupPasswd("adin::1000:1111::/home/adin:/bin/sh", 0000),
expected: "/",
uid: 1000,
passwdContents: "adin::1000:1111::/home/adin:/bin/sh",
passwdMode: linux.S_IFREG,
expected: "/",
},
"no_passwd": {
uid: 1000,
expected: "/",
},
"directory": {
uid: 1000,
createRoot: func() (string, error) {
tmpDir, err := setupTempDir()
if err != nil {
return "", err
}
if err := os.Mkdir(filepath.Join(tmpDir, "etc"), 0777); err != nil {
return "", err
}
if err := syscall.Mkdir(filepath.Join(tmpDir, "etc", "passwd"), 0666); err != nil {
return "", err
}
return tmpDir, nil
},
expected: "/",
uid: 1000,
passwdMode: linux.S_IFDIR | 0666,
expected: "/",
},
// Currently we don't allow named pipes.
"named_pipe": {
uid: 1000,
createRoot: func() (string, error) {
tmpDir, err := setupTempDir()
if err != nil {
return "", err
}
if err := os.Mkdir(filepath.Join(tmpDir, "etc"), 0777); err != nil {
return "", err
}
if err := syscall.Mkfifo(filepath.Join(tmpDir, "etc", "passwd"), 0666); err != nil {
return "", err
}
return tmpDir, nil
},
expected: "/",
uid: 1000,
passwdMode: linux.S_IFIFO | 0666,
expected: "/",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
tmpDir, err := tc.createRoot()
if err != nil {
t.Fatalf("failed to create root dir: %v", err)
}
sandEnd, cleanup, err := startGofer(tmpDir)
if err != nil {
t.Fatalf("failed to create gofer: %v", err)
}
defer cleanup()
ctx := contexttest.Context(t)
conf := &Config{
RootDir: "unused_root_dir",
Network: NetworkNone,
DisableSeccomp: true,
}
msrc := fs.NewPseudoMountSource(ctx)
rootInode := tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc)
spec := &specs.Spec{
Root: &specs.Root{
Path: tmpDir,
Readonly: true,
},
// Add /proc mount as tmpfs to avoid needing a kernel.
Mounts: []specs.Mount{
{
Destination: "/proc",
Type: "tmpfs",
},
},
}
mntr := newContainerMounter(spec, []int{sandEnd}, nil, &podMountHints{})
mns, err := mntr.createMountNamespace(ctx, conf)
mns, err := fs.NewMountNamespace(ctx, rootInode)
if err != nil {
t.Fatalf("failed to create mount namespace: %v", err)
t.Fatalf("NewMountNamespace failed: %v", err)
}
ctx = fs.WithRoot(ctx, mns.Root())
if err := mntr.mountSubmounts(ctx, conf, mns); err != nil {
t.Fatalf("failed to create mount namespace: %v", err)
defer mns.DecRef()
root := mns.Root()
defer root.DecRef()
ctx = fs.WithRoot(ctx, root)
if err := createEtcPasswd(ctx, root, tc.passwdContents, tc.passwdMode); err != nil {
t.Fatalf("createEtcPasswd failed: %v", err)
}
got, err := getExecUserHome(ctx, mns, tc.uid)
+1 -4
View File
@@ -20,7 +20,6 @@ go_library(
"loader_arm64.go",
"network.go",
"strace.go",
"user.go",
"vfs.go",
],
visibility = [
@@ -52,6 +51,7 @@ go_library(
"//pkg/sentry/fs/sys",
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/fs/tty",
"//pkg/sentry/fs/user",
"//pkg/sentry/fsimpl/devtmpfs",
"//pkg/sentry/fsimpl/gofer",
"//pkg/sentry/fsimpl/host",
@@ -97,7 +97,6 @@ go_library(
"//pkg/tcpip/transport/tcp",
"//pkg/tcpip/transport/udp",
"//pkg/urpc",
"//pkg/usermem",
"//runsc/boot/filter",
"//runsc/boot/platforms",
"//runsc/boot/pprof",
@@ -115,7 +114,6 @@ go_test(
"compat_test.go",
"fs_test.go",
"loader_test.go",
"user_test.go",
],
library = ":boot",
deps = [
@@ -125,7 +123,6 @@ go_test(
"//pkg/sentry/contexttest",
"//pkg/sentry/fs",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sync",
"//pkg/unet",
"//runsc/fsgofer",
+4 -3
View File
@@ -35,6 +35,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/control"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/host"
"gvisor.dev/gvisor/pkg/sentry/fs/user"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -550,11 +551,11 @@ func (l *Loader) run() error {
// Add the HOME enviroment variable if it is not already set.
var envv []string
if kernel.VFS2Enabled {
envv, err = maybeAddExecUserHomeVFS2(ctx, l.rootProcArgs.MountNamespaceVFS2,
envv, err = user.MaybeAddExecUserHomeVFS2(ctx, l.rootProcArgs.MountNamespaceVFS2,
l.rootProcArgs.Credentials.RealKUID, l.rootProcArgs.Envv)
} else {
envv, err = maybeAddExecUserHome(ctx, l.rootProcArgs.MountNamespace,
envv, err = user.MaybeAddExecUserHome(ctx, l.rootProcArgs.MountNamespace,
l.rootProcArgs.Credentials.RealKUID, l.rootProcArgs.Envv)
}
if err != nil {
@@ -860,7 +861,7 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) {
root := args.MountNamespace.Root()
defer root.DecRef()
ctx := fs.WithRoot(l.k.SupervisorContext(), root)
envv, err := maybeAddExecUserHome(ctx, args.MountNamespace, args.KUID, args.Envv)
envv, err := user.MaybeAddExecUserHome(ctx, args.MountNamespace, args.KUID, args.Envv)
if err != nil {
return 0, err
}