Tentative: whitelist files passed as arguments inside of the sandbox

master
xSmurf 10 years ago
parent 7f9c8b1c1c
commit 3c920c4fc1

@ -0,0 +1,18 @@
package main
import (
"runtime"
"github.com/subgraph/oz/oz-mount"
)
func init() {
runtime.LockOSThread()
runtime.GOMAXPROCS(1)
}
func main() {
defer runtime.UnlockOSThread()
mount.Main(mount.MOUNT)
}

@ -0,0 +1,18 @@
package main
import (
"runtime"
"github.com/subgraph/oz/oz-mount"
)
func init() {
runtime.LockOSThread()
runtime.GOMAXPROCS(1)
}
func main() {
defer runtime.UnlockOSThread()
mount.Main(mount.UMOUNT)
}

@ -160,6 +160,23 @@ func (fs *Filesystem) bind(from string, to string, flags int, u *user.User) erro
return bindMount(src, to, mntflags) return bindMount(src, to, mntflags)
} }
func (fs *Filesystem) UnbindPath(to string) (error) {
to = path.Join(fs.Root(), to)
_, err := os.Stat(to)
if err != nil {
fs.log.Warning("Target (%s) does not exist, ignoring", to)
return nil
}
// XXX
if err := syscall.Unmount(to, syscall.MNT_DETACH/* | syscall.MNT_FORCE*/); err != nil {
return err
}
return os.Remove(to)
}
func readSourceInfo(src string, cancreate bool, u *user.User) (os.FileInfo, error) { func readSourceInfo(src string, cancreate bool, u *user.User) (os.FileInfo, error) {
if fi, err := os.Stat(src); err == nil { if fi, err := os.Stat(src); err == nil {
return fi, nil return fi, nil

@ -174,7 +174,7 @@ func (d *daemonState) handleListProfiles(msg *ListProfilesMsg, m *ipc.Message) e
} }
func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error {
d.Debug("Launch message received: %+v", msg) d.Debug("Launch message received. Path: %s Name: %s Pwd: %s Args: %+v", msg.Path, msg.Name, msg.Pwd, msg.Args)
p, err := d.getProfileFromLaunchMsg(msg) p, err := d.getProfileFromLaunchMsg(msg)
if err != nil { if err != nil {
return m.Respond(&ErrorMsg{err.Error()}) return m.Respond(&ErrorMsg{err.Error()})
@ -187,7 +187,7 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error {
return m.Respond(&ErrorMsg{errmsg}) return m.Respond(&ErrorMsg{errmsg})
} else { } else {
d.Info("Found running sandbox for `%s`, running program there", p.Name) d.Info("Found running sandbox for `%s`, running program there", p.Name)
sbox.launchProgram(msg.Path, msg.Pwd, msg.Args, d.log) sbox.launchProgram(d.config.PrefixPath, msg.Path, msg.Pwd, msg.Args, d.log)
} }
} else { } else {
d.Debug("Would launch %s", p.Name) d.Debug("Would launch %s", p.Name)

@ -7,6 +7,9 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"strconv"
"strings"
"sync" "sync"
"syscall" "syscall"
@ -179,7 +182,7 @@ func (d *daemonState) launch(p *oz.Profile, msg *LaunchMsg, uid, gid uint32, log
go func() { go func() {
sbox.ready.Wait() sbox.ready.Wait()
wgNet.Wait() wgNet.Wait()
go sbox.launchProgram(msg.Path, msg.Pwd, msg.Args, log) go sbox.launchProgram(d.config.PrefixPath, msg.Path, msg.Pwd, msg.Args, log)
}() }()
} }
@ -195,26 +198,39 @@ func (d *daemonState) launch(p *oz.Profile, msg *LaunchMsg, uid, gid uint32, log
return sbox, nil return sbox, nil
} }
func (sbox *Sandbox) launchProgram(cpath, pwd string, args []string, log *logging.Logger) { func (sbox *Sandbox) launchProgram(binpath, cpath, pwd string, args []string, log *logging.Logger) {
/*
if sbox.profile.AllowFiles { if sbox.profile.AllowFiles {
sbox.whitelistArgumentFiles(binpath, pwd, args, log)
}
err := ozinit.RunProgram(sbox.addr, cpath, pwd, args)
if err != nil {
log.Error("start shell command failed: %v", err)
}
}
func (sbox *Sandbox) whitelistArgumentFiles(binpath, pwd string, args []string, log *logging.Logger) {
var files []string
for _, fpath := range args { for _, fpath := range args {
if _, err := os.Stat(fpath); err == nil { if _, err := os.Stat(fpath); err == nil {
if filepath.IsAbs(fpath) == false { if filepath.IsAbs(fpath) == false {
fpath = path.Join(pwd, fpath) fpath = path.Join(pwd, fpath)
} }
log.Info("Adding file `%s` to sandbox `%s`.", fpath, sbox.profile.Name) if !strings.HasPrefix(fpath, "/home/") {
if err := sbox.fs.AddBindWhitelist(fpath, fpath, false); err != nil { continue
log.Warning("Error adding file `%s`!", fpath)
}
} }
log.Notice("Adding file `%s` to sandbox `%s`.", fpath, sbox.profile.Name)
files = append(files, fpath)
} }
} }
*/ if len(files) > 0 {
pmnt := path.Join(binpath, "bin", "oz-mount")
err := ozinit.RunProgram(sbox.addr, cpath, pwd, args) cmnt := exec.Command(pmnt, files...)
cmnt.Env = []string{"_OZ_NSPID=" + strconv.Itoa(sbox.init.Process.Pid)}
pout, err := cmnt.CombinedOutput();
if err != nil { if err != nil {
log.Error("start shell command failed: %v", err) log.Warning("Unable to bind files to sandbox: %v", err)
log.Warning("%s", string(pout))
}
} }
} }

@ -0,0 +1,128 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sched.h>
#include <sys/types.h>
int enter_mount_namespace(void) {
if (geteuid() != 0) {
fprintf(stderr, "E Must run as root\n");
return -1;
}
// Do some minimal verification to check that oz-daemon is the parent
pid_t ppid = getppid();
//ppid = 10252;
if (checkProcessName(ppid, "oz-daemon") != 0) {
fprintf(stderr, "E unable to verify that oz-daemon is parent\n");
return -1;
}
// Parse namespace pid from environment
char *envv, *envvend;
long nspid;
envv = getenv("_OZ_NSPID");
if (envv == NULL) {
fprintf(stderr, "E unable to get namespace pid from environment\n");
return -1;
}
errno = 0;
nspid = strtol(envv, &envvend, 10);
if ((errno == ERANGE && (nspid == LONG_MAX || nspid == LONG_MIN))
|| (errno != 0 && nspid == 0)) {
fprintf(stderr, "E unable to parse namespace pid from environment\n");
return -1;
}
if (envvend == envv || nspid < 0) {
fprintf(stderr, "E unable to parse namespace pid from environment\n");
return -1;
}
// Verify that the target is an instance of oz-init
if (checkProcessName(nspid, "oz-init") != 0) {
fprintf(stderr, "E unable to verify that oz-init is the target\n");
return -1;
}
char nspath[PATH_MAX];
if (snprintf(nspath, PATH_MAX-1, "/proc/%ld/ns", nspid) < 0) {
fprintf(stderr, "E unable to parse namespace path `/proc/%ld/ns`\n", nspid);
return -1;
}
printf("D Opening: %s\n", nspath);
// Start opening the namespace
struct stat st;
int tfd, fd;
tfd = open(nspath, O_DIRECTORY | O_RDONLY);
if (tfd == -1) {
fprintf(stderr, "E failed to open child namespace\n");
return -1;
}
// Symlinks on all namespaces exist for dead processes, but they can't be opened
if (fstatat(tfd, "mnt", &st, AT_SYMLINK_NOFOLLOW) == -1) {
if (errno == ENOENT) {
fprintf(stderr, "E failed to open child namespace\n");
return -1;
}
}
fd = openat(tfd, "mnt", O_RDONLY);
if (fd == -1) {
fprintf(stderr, "E failed to open child mount namespace: %s\n", nspath);
return -1;
}
// Set the namespace.
if (setns(fd, 0) == -1) {
fprintf(stderr, "E failed to setns for: %s\n", nspath);
return -1;
}
close(fd);
}
int checkProcessName(pid_t pid, char *pname) {
FILE *fp;
char *line = NULL;
char pproc[PATH_MAX];
char cline[PATH_MAX];
size_t len = 0;
ssize_t read;
if (snprintf(cline, PATH_MAX-1, "Name: %s\n", pname) < 0) {
return -1;
}
if (snprintf(pproc, PATH_MAX-1, "/proc/%ld/status", pid) < 0) {
return -1;
}
fp = fopen(pproc, "r");
if (fp == NULL) {
return -1;
}
int retval = -1;
while (retval == -1 && (read = getline(&line, &len, fp)) != -1) {
if (hasPrefix(line, "Name:") >= 0) {
retval = strcmp(cline, line);
break;
}
}
fclose(fp);
if (line) {
free(line);
}
return retval;
}
int hasPrefix(const char *str, const char *pre) {
size_t lenpre = strlen(pre),
lenstr = strlen(str);
return lenstr < lenpre ? -1 : strncmp(pre, str, lenpre);
}

@ -0,0 +1,102 @@
// +build linux,!gccgo
package mount
// extern int enter_mount_namespace(void);
/*
#include <stdlib.h>
__attribute__((constructor)) void init(void) {
if (enter_mount_namespace() < 0) {
exit(EXIT_FAILURE);
}
}
*/
import "C"
import (
"os"
"path"
"strings"
"github.com/subgraph/oz"
"github.com/subgraph/oz/fs"
"github.com/op/go-logging"
)
const (
MOUNT = 1 << iota
UMOUNT
)
func Main(mode int) {
log := createLogger()
config, err := loadConfig()
if err != nil {
log.Error("Could not load configuration: %s\n", oz.DefaultConfigPath, err)
os.Exit(1)
}
fsys := fs.NewFilesystem(config, log)
for fii, fpath := range os.Args {
if fii == 0 {
continue
}
if !strings.HasPrefix(fpath, "/home/") {
log.Warning("Ignored `%s`, only files inside of home are permitted!", fpath)
continue
}
switch mode {
case MOUNT:
mount(fpath, fsys, log)
case UMOUNT:
unmount(fpath, fsys, log)
}
}
os.Exit(0)
}
func mount(fpath string, fsys *fs.Filesystem, log *logging.Logger) {
if _, err := os.Stat(fpath); err == nil {
//log.Notice("Adding file `%s`.", fpath)
if err := fsys.BindPath(fpath, fs.BindCanCreate, nil); err != nil {
log.Error("%v while adding `%s`!", err, fpath)
os.Exit(1)
}
}
}
func unmount(fpath string, fsys *fs.Filesystem, log *logging.Logger) {
sbpath := path.Join(fsys.Root(), fpath)
if _, err := os.Stat(sbpath); err == nil {
//log.Notice("Removing file `%s`.", fpath)
if err := fsys.UnbindPath(fpath); err != nil {
log.Error("%v while removing `%s`!", err, fpath)
os.Exit(1)
}
} else {
log.Error("%v error while removing `%s`!", err, fpath)
}
}
func createLogger() *logging.Logger {
l := logging.MustGetLogger("oz-init")
be := logging.NewLogBackend(os.Stderr, "", 0)
f := logging.MustStringFormatter("%{level:.1s} %{message}")
fbe := logging.NewBackendFormatter(be, f)
logging.SetBackend(fbe)
return l
}
func loadConfig() (*oz.Config, error) {
config, err := oz.LoadConfig(oz.DefaultConfigPath)
if err != nil {
if os.IsNotExist(err) {
config = oz.NewDefaultConfig()
} else {
return nil, err
}
}
return config, nil
}
Loading…
Cancel
Save