From e20be3ea0ab179973c4759607375b9742a06f16a Mon Sep 17 00:00:00 2001 From: xSmurf Date: Tue, 9 Jun 2015 03:08:50 +0000 Subject: [PATCH] Added tentative restricted /dev using mknod, and configuration to disable the feature --- config.go | 2 ++ fs/cleanup.go | 9 ++++- fs/fs.go | 4 ++- fs/ozinit.go | 29 +++++++++++----- fs/setup.go | 82 +++++++++++++++++++++++++++++++++++++++++++-- oz-daemon/daemon.go | 2 +- oz-daemon/launch.go | 2 +- oz-init/init.go | 2 +- 8 files changed, 117 insertions(+), 15 deletions(-) diff --git a/config.go b/config.go index 3f11029..6f6dfcb 100644 --- a/config.go +++ b/config.go @@ -11,6 +11,7 @@ type Config struct { SandboxPath string `json:"sandbox_path"` BridgeMACAddr string `json:"bridge_mac"` NMIgnoreFile string `json:"nm_ignore_file"` + UseFullDev bool `json:"use_full_dev"` AllowRootShell bool `json:"allow_root_shell"` LogXpra bool `json:"log_xpra"` } @@ -24,6 +25,7 @@ func NewDefaultConfig() *Config { SandboxPath: "/srv/oz", NMIgnoreFile: "/etc/NetworkManager/conf.d/oz.conf", BridgeMACAddr: "6A:A8:2E:56:E8:9C", + UseFullDev: false, AllowRootShell: false, LogXpra: false, } diff --git a/fs/cleanup.go b/fs/cleanup.go index 3e6efe3..20fdbe1 100644 --- a/fs/cleanup.go +++ b/fs/cleanup.go @@ -2,11 +2,14 @@ package fs import ( "errors" - "github.com/op/go-logging" "io/ioutil" + "os" "sort" "strings" "syscall" + + // External + "github.com/op/go-logging" ) func (fs *Filesystem) Cleanup() error { @@ -36,6 +39,10 @@ func (mnts mountEntries) unmountAll(log *logging.Logger) (bool, error) { reterr := error(nil) atLeastOne := false for _, m := range mnts { + log.Debug("Unmounting mountpoint: %s", m.dir) + if _, err := os.Stat(m.dir); os.IsNotExist(err) { + continue + } if err := syscall.Unmount(m.dir, 0); err != nil { log.Warning("Failed to unmount mountpoint %s: %v", m.dir, err) reterr = err diff --git a/fs/fs.go b/fs/fs.go index 11bbb38..2dc68e3 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -28,6 +28,7 @@ type Filesystem struct { userID string noDefaults bool noSysAndProc bool + fullDevices bool whitelist []*mountItem blacklist []*mountItem } @@ -75,7 +76,7 @@ func (fs *Filesystem) newItem(path, target string, readonly bool) (*mountItem, e }, nil } -func NewFromProfile(profile *oz.Profile, user *user.User, basePath string, log *logging.Logger) *Filesystem { +func NewFromProfile(profile *oz.Profile, user *user.User, basePath string, UseFullDev bool, log *logging.Logger) *Filesystem { fs := NewFilesystem(profile.Name, user, basePath, log) for _, wl := range profile.Whitelist { fs.addWhitelist(wl.Path, wl.Path, wl.ReadOnly) @@ -85,6 +86,7 @@ func NewFromProfile(profile *oz.Profile, user *user.User, basePath string, log * } fs.noDefaults = profile.NoDefaults fs.noSysAndProc = profile.NoSysProc + fs.fullDevices = UseFullDev if profile.XServer.Enabled { fs.xpra = path.Join(user.HomeDir, ".Xoz", profile.Name) } diff --git a/fs/ozinit.go b/fs/ozinit.go index 3e4350d..583328c 100644 --- a/fs/ozinit.go +++ b/fs/ozinit.go @@ -24,20 +24,24 @@ func (fs *Filesystem) OzInit() error { } func (fs *Filesystem) ozinitMountDev() error { - flags := uintptr(syscall.MS_NOSUID | syscall.MS_REC | syscall.MS_NOEXEC) - if err := syscall.Mount("none", "/dev", "devtmpfs", flags, ""); err != nil { - fs.log.Warning("Failed to mount devtmpfs: %v", err) - return err - } + if fs.fullDevices { + flags := uintptr(syscall.MS_NOSUID | syscall.MS_REC | syscall.MS_NOEXEC) + if err := syscall.Mount("none", "/dev", "devtmpfs", flags, ""); err != nil { + fs.log.Warning("Failed to mount devtmpfs: %v", err) + return err + } - if err := mountSpecial("/dev/shm", "tmpfs"); err != nil { - fs.log.Warning("Failed to mount shm directory: %v", err) - return err + if err := mountSpecial("/dev/shm", "tmpfs"); err != nil { + fs.log.Warning("Failed to mount shm directory: %v", err) + return err + } } + if err := mountSpecial("/dev/pts", "devpts"); err != nil { fs.log.Warning("Failed to mount pts directory: %v", err) return err } + return nil } @@ -87,6 +91,15 @@ func (fs *Filesystem) ozinitCreateSymlinks() error { return err } } + + if fs.fullDevices == false { + for _, sl := range deviceSymlinks { + if err := syscall.Symlink(sl[0], sl[1]); err != nil { + return err + } + } + } + return nil } diff --git a/fs/setup.go b/fs/setup.go index af0cfd4..5bdc553 100644 --- a/fs/setup.go +++ b/fs/setup.go @@ -18,9 +18,10 @@ var basicEmptyDirs = []string{ "/sbin", "/var", "/var/lib", "/var/cache", "/home", "/boot", "/tmp", "/run", "/run/user", - "/run/shm", "/run/lock", "/root", + "/run/lock", "/root", "/opt", "/srv", "/dev", "/proc", "/sys", "/mnt", "/media", + //"/run/shm", } var basicBlacklist = []string{ @@ -37,6 +38,47 @@ var basicSymlinks = [][2]string{ {"/run", "/var/run"}, {"/tmp", "/var/tmp"}, {"/run/lock", "/var/lock"}, + // Devices +} + +var deviceSymlinks = [][2]string{ + {"/proc/self/fd", "/dev/fd"}, + {"/proc/self/fd/2", "/dev/stderr"}, + {"/proc/self/fd/0", "/dev/stdin"}, + {"/proc/self/fd/1", "/dev/stdout"}, + {"/dev/pts/ptmx", "/dev/ptmx"}, + {"/dev/shm", "/run/shm"}, +} + +type fsDeviceDefinition struct { + path string + mode uint32 + dev int + perm uint32 +} + +const ugorw = syscall.S_IRUSR|syscall.S_IWUSR | syscall.S_IRGRP|syscall.S_IWGRP | syscall.S_IROTH|syscall.S_IWOTH +const urwgr = syscall.S_IRUSR|syscall.S_IWUSR | syscall.S_IRGRP +const urw = syscall.S_IRUSR|syscall.S_IWUSR + +var basicDevices = []fsDeviceDefinition{ + {path: "/dev/full", mode: syscall.S_IFCHR|ugorw, dev: _makedev(1, 7), perm: 0666}, + {path: "/dev/null", mode: syscall.S_IFCHR|ugorw, dev: _makedev(1, 3), perm: 0666}, + {path: "/dev/random", mode: syscall.S_IFCHR|ugorw, dev: _makedev(1, 8), perm: 0666}, + + {path: "/dev/console", mode: syscall.S_IFCHR|urw, dev: _makedev(5, 1), perm: 0600}, + {path: "/dev/tty", mode: syscall.S_IFCHR|ugorw, dev: _makedev(5, 0), perm: 0666}, + {path: "/dev/tty1", mode: syscall.S_IFREG|urwgr, dev: 0, perm: 0640}, + {path: "/dev/tty2", mode: syscall.S_IFREG|urwgr, dev: 0, perm: 0640}, + {path: "/dev/tty3", mode: syscall.S_IFREG|urwgr, dev: 0, perm: 0640}, + {path: "/dev/tty4", mode: syscall.S_IFREG|urwgr, dev: 0, perm: 0640}, + + {path: "/dev/urandom", mode: syscall.S_IFCHR|ugorw, dev: _makedev(1, 9), perm: 0666}, + {path: "/dev/zero", mode: syscall.S_IFCHR|ugorw, dev: _makedev(1, 5), perm: 0666}, +} + +func _makedev(x, y int) int { + return (((x)<<8) | (y)) } func (fs *Filesystem) Setup(profilesPath string) error { @@ -68,6 +110,12 @@ func (fs *Filesystem) Setup(profilesPath string) error { if err := fs.setupChroot(); err != nil { return err } + if fs.fullDevices == false { + if err := fs.setupDev(); err != nil { + return err + } + } + return fs.setupMountItems() } @@ -104,14 +152,17 @@ func (fs *Filesystem) setupRootfs() error { if err := syscall.Mount(fs.base, fs.base, "tmpfs", flags, data); err != nil { return fmt.Errorf("failed to create base tmpfs at %s: %v", fs.base, err) } +/* +// Currently unused // create extra directories - extra := []string{"sockets"} + extra := []string{"sockets", "dev"} for _, sub := range extra { d := path.Join(fs.base, sub) if err := os.Mkdir(d, 0755); err != nil { return fmt.Errorf("unable to create directory (%s): %v", d, err) } } +*/ return nil } @@ -132,6 +183,33 @@ func (fs *Filesystem) setupChroot() error { return setupTmp(fs.root) } +func (fs *Filesystem) setupDev() error { + devPath := path.Join(fs.root, "dev") + flags := uintptr(syscall.MS_NOSUID | syscall.MS_NOEXEC) + if err := syscall.Mount("none", devPath, "tmpfs", flags, ""); err != nil { + fs.log.Warning("Failed to mount devtmpfs: %v", err) + return err + } + + for _, dev := range basicDevices { + path := path.Join(fs.root, dev.path) + if err := syscall.Mknod(path, dev.mode, dev.dev); err != nil { + return fmt.Errorf("Failed to mknod device %s: %+v", path, err) + } + if err := os.Chmod(path, os.FileMode(dev.perm)); err != nil { + return fmt.Errorf("Unable to set permissions for device %s: %+v", dev.path, err) + } + } + + shmPath := path.Join(devPath, "shm") + if err := mountSpecial(shmPath, "tmpfs"); err != nil { + fs.log.Warning("Failed to mount shm directory: %v", err) + return err + } + + return nil +} + func bindBasicDirectories(root string, dirs []string) error { for _, src := range dirs { st, err := os.Lstat(src) diff --git a/oz-daemon/daemon.go b/oz-daemon/daemon.go index 6f3f156..7e13bed 100644 --- a/oz-daemon/daemon.go +++ b/oz-daemon/daemon.go @@ -170,7 +170,7 @@ func (d *daemonState) handleClean(clean *CleanMsg, msg *ipc.Message) error { } // XXX u, _ := user.Current() - fs := fs.NewFromProfile(p, u, d.config.SandboxPath, d.log) + fs := fs.NewFromProfile(p, u, d.config.SandboxPath, d.config.UseFullDev, d.log) if err := fs.Cleanup(); err != nil { return msg.Respond(&ErrorMsg{err.Error()}) } diff --git a/oz-daemon/launch.go b/oz-daemon/launch.go index 6b38e80..fac8e91 100644 --- a/oz-daemon/launch.go +++ b/oz-daemon/launch.go @@ -85,7 +85,7 @@ func (d *daemonState) launch(p *oz.Profile, uid, gid uint32, log *logging.Logger if err != nil { return nil, fmt.Errorf("failed to lookup user for uid=%d: %v", uid, err) } - fs := fs.NewFromProfile(p, u, d.config.SandboxPath, d.log) + fs := fs.NewFromProfile(p, u, d.config.SandboxPath, d.config.UseFullDev, d.log) if err := fs.Setup(d.config.ProfileDir); err != nil { return nil, err } diff --git a/oz-init/init.go b/oz-init/init.go index 668b21e..dac4631 100644 --- a/oz-init/init.go +++ b/oz-init/init.go @@ -132,7 +132,7 @@ func parseArgs() *initState { gid: gid, user: u, display: display, - fs: fs.NewFromProfile(p, u, config.SandboxPath, log), + fs: fs.NewFromProfile(p, u, config.SandboxPath, config.UseFullDev, log), network: stn, } }