diff --git a/helper/bwrap/builder.go b/helper/bwrap/builder.go index 312295d..d196bc0 100644 --- a/helper/bwrap/builder.go +++ b/helper/bwrap/builder.go @@ -1,6 +1,8 @@ package bwrap -import "os" +import ( + "os" +) /* Bind binds mount src on host to dest in sandbox. @@ -61,6 +63,29 @@ func (c *Config) Bind(src, dest string, opts ...bool) *Config { } } +// Write copy from FD to destination DEST +// (--file FD DEST) +func (c *Config) Write(dest string, payload []byte) *Config { + c.Filesystem = append(c.Filesystem, &DataConfig{Dest: dest, Data: payload, Type: DataWrite}) + return c +} + +/* +CopyBind copy from FD to file which is readonly bind-mounted on DEST +(--ro-bind-data FD DEST) + +CopyBind(dest, payload, true) copy from FD to file which is bind-mounted on DEST +(--bind-data FD DEST) +*/ +func (c *Config) CopyBind(dest string, payload []byte, opts ...bool) *Config { + t := DataROBind + if len(opts) > 0 && opts[0] { + t = DataBind + } + c.Filesystem = append(c.Filesystem, &DataConfig{Dest: dest, Data: payload, Type: t}) + return c +} + // Dir create dir in sandbox // (--dir DEST) func (c *Config) Dir(dest string) *Config { diff --git a/helper/bwrap/config.go b/helper/bwrap/config.go index b9fa0c1..fdda14a 100644 --- a/helper/bwrap/config.go +++ b/helper/bwrap/config.go @@ -71,9 +71,6 @@ type Config struct { --ro-bind-fd FD DEST Bind open directory or path fd read-only on DEST --exec-label LABEL Exec label for the sandbox --file-label LABEL File label for temporary sandbox content - --file FD DEST Copy from FD to destination DEST - --bind-data FD DEST Copy from FD to file which is bind-mounted on DEST - --ro-bind-data FD DEST Copy from FD to file which is readonly bind-mounted on DEST --add-seccomp-fd FD Load and use seccomp rules from FD (repeatable) --block-fd FD Block on FD until some data to read is available --userns-block-fd FD Block on FD until the user namespace is ready diff --git a/helper/bwrap/config_test.go b/helper/bwrap/config_test.go index 64d783c..bbd5f5e 100644 --- a/helper/bwrap/config_test.go +++ b/helper/bwrap/config_test.go @@ -21,15 +21,14 @@ func TestConfig_Args(t *testing.T) { want []string }{ { - name: "bind", - conf: (new(bwrap.Config)). - Bind("/etc", "/.fortify/etc"). - Bind("/etc", "/.fortify/etc", true). - Bind("/run", "/.fortify/run", false, true). - Bind("/sys/devices", "/.fortify/sys/devices", true, true). - Bind("/dev/dri", "/.fortify/dev/dri", false, true, true). - Bind("/dev/dri", "/.fortify/dev/dri", true, true, true), - want: []string{ + "bind", (new(bwrap.Config)). + Bind("/etc", "/.fortify/etc"). + Bind("/etc", "/.fortify/etc", true). + Bind("/run", "/.fortify/run", false, true). + Bind("/sys/devices", "/.fortify/sys/devices", true, true). + Bind("/dev/dri", "/.fortify/dev/dri", false, true, true). + Bind("/dev/dri", "/.fortify/dev/dri", true, true, true), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // Bind("/etc", "/.fortify/etc") @@ -47,14 +46,13 @@ func TestConfig_Args(t *testing.T) { }, }, { - name: "dir remount-ro proc dev mqueue", - conf: (new(bwrap.Config)). - Dir("/.fortify"). - RemountRO("/home"). - Procfs("/proc"). - DevTmpfs("/dev"). - Mqueue("/dev/mqueue"), - want: []string{ + "dir remount-ro proc dev mqueue", (new(bwrap.Config)). + Dir("/.fortify"). + RemountRO("/home"). + Procfs("/proc"). + DevTmpfs("/dev"). + Mqueue("/dev/mqueue"), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // Dir("/.fortify") @@ -70,11 +68,10 @@ func TestConfig_Args(t *testing.T) { }, }, { - name: "tmpfs", - conf: (new(bwrap.Config)). - Tmpfs("/run/user", 8192). - Tmpfs("/run/dbus", 8192, 0755), - want: []string{ + "tmpfs", (new(bwrap.Config)). + Tmpfs("/run/user", 8192). + Tmpfs("/run/dbus", 8192, 0755), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // Tmpfs("/run/user", 8192) @@ -84,11 +81,10 @@ func TestConfig_Args(t *testing.T) { }, }, { - name: "symlink", - conf: (new(bwrap.Config)). - Symlink("/.fortify/sbin/init", "/sbin/init"). - Symlink("/.fortify/sbin/init", "/sbin/init", 0755), - want: []string{ + "symlink", (new(bwrap.Config)). + Symlink("/.fortify/sbin/init", "/sbin/init"). + Symlink("/.fortify/sbin/init", "/sbin/init", 0755), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // Symlink("/.fortify/sbin/init", "/sbin/init") @@ -98,12 +94,11 @@ func TestConfig_Args(t *testing.T) { }, }, { - name: "overlayfs", - conf: (new(bwrap.Config)). - Overlay("/etc", "/etc"). - Join("/.fortify/bin", "/bin", "/usr/bin", "/usr/local/bin"). - Persist("/nix", "/data/data/org.chromium.Chromium/overlay/rwsrc", "/data/data/org.chromium.Chromium/workdir", "/data/app/org.chromium.Chromium/nix"), - want: []string{ + "overlayfs", (new(bwrap.Config)). + Overlay("/etc", "/etc"). + Join("/.fortify/bin", "/bin", "/usr/bin", "/usr/local/bin"). + Persist("/nix", "/data/data/org.chromium.Chromium/overlay/rwsrc", "/data/data/org.chromium.Chromium/workdir", "/data/app/org.chromium.Chromium/nix"), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // Overlay("/etc", "/etc") @@ -117,23 +112,37 @@ func TestConfig_Args(t *testing.T) { }, }, { - name: "unshare", - conf: &bwrap.Config{Unshare: &bwrap.UnshareConfig{ - User: false, - IPC: false, - PID: false, - Net: false, - UTS: false, - CGroup: false, - }}, - want: []string{"--disable-userns", "--assert-userns-disabled"}, + "copy", (new(bwrap.Config)). + Write("/.fortify/version", make([]byte, 8)). + CopyBind("/etc/group", make([]byte, 8)). + CopyBind("/etc/passwd", make([]byte, 8), true), + []string{ + "--unshare-all", "--unshare-user", + "--disable-userns", "--assert-userns-disabled", + // Write("/.fortify/version", make([]byte, 8)) + "--file", "3", "/.fortify/version", + // CopyBind("/etc/group", make([]byte, 8)) + "--ro-bind-data", "4", "/etc/group", + // CopyBind("/etc/passwd", make([]byte, 8), true) + "--bind-data", "5", "/etc/passwd", + }, }, { - name: "uid gid sync", - conf: (new(bwrap.Config)). - SetUID(1971). - SetGID(100), - want: []string{ + "unshare", &bwrap.Config{Unshare: &bwrap.UnshareConfig{ + User: false, + IPC: false, + PID: false, + Net: false, + UTS: false, + CGroup: false, + }}, + []string{"--disable-userns", "--assert-userns-disabled"}, + }, + { + "uid gid sync", (new(bwrap.Config)). + SetUID(1971). + SetGID(100), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // SetUID(1971) @@ -143,17 +152,16 @@ func TestConfig_Args(t *testing.T) { }, }, { - name: "hostname chdir setenv unsetenv lockfile chmod syscall", - conf: &bwrap.Config{ - Hostname: "fortify", - Chdir: "/.fortify", - SetEnv: map[string]string{"FORTIFY_INIT": "/.fortify/sbin/init"}, - UnsetEnv: []string{"HOME", "HOST"}, - LockFile: []string{"/.fortify/lock"}, - Syscall: new(bwrap.SyscallPolicy), - Chmod: map[string]os.FileMode{"/.fortify/sbin/init": 0755}, - }, - want: []string{ + "hostname chdir setenv unsetenv lockfile chmod syscall", &bwrap.Config{ + Hostname: "fortify", + Chdir: "/.fortify", + SetEnv: map[string]string{"FORTIFY_INIT": "/.fortify/sbin/init"}, + UnsetEnv: []string{"HOME", "HOST"}, + LockFile: []string{"/.fortify/lock"}, + Syscall: new(bwrap.SyscallPolicy), + Chmod: map[string]os.FileMode{"/.fortify/sbin/init": 0755}, + }, + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", // Hostname: "fortify" @@ -175,31 +183,30 @@ func TestConfig_Args(t *testing.T) { }, { - name: "xdg-dbus-proxy constraint sample", - conf: (&bwrap.Config{Clearenv: true, DieWithParent: true}). - Symlink("usr/bin", "/bin"). - Symlink("var/home", "/home"). - Symlink("usr/lib", "/lib"). - Symlink("usr/lib64", "/lib64"). - Symlink("run/media", "/media"). - Symlink("var/mnt", "/mnt"). - Symlink("var/opt", "/opt"). - Symlink("sysroot/ostree", "/ostree"). - Symlink("var/roothome", "/root"). - Symlink("usr/sbin", "/sbin"). - Symlink("var/srv", "/srv"). - Bind("/run", "/run", false, true). - Bind("/tmp", "/tmp", false, true). - Bind("/var", "/var", false, true). - Bind("/run/user/1971/.dbus-proxy/", "/run/user/1971/.dbus-proxy/", false, true). - Bind("/boot", "/boot"). - Bind("/dev", "/dev"). - Bind("/proc", "/proc"). - Bind("/sys", "/sys"). - Bind("/sysroot", "/sysroot"). - Bind("/usr", "/usr"). - Bind("/etc", "/etc"), - want: []string{ + "xdg-dbus-proxy constraint sample", (&bwrap.Config{Clearenv: true, DieWithParent: true}). + Symlink("usr/bin", "/bin"). + Symlink("var/home", "/home"). + Symlink("usr/lib", "/lib"). + Symlink("usr/lib64", "/lib64"). + Symlink("run/media", "/media"). + Symlink("var/mnt", "/mnt"). + Symlink("var/opt", "/opt"). + Symlink("sysroot/ostree", "/ostree"). + Symlink("var/roothome", "/root"). + Symlink("usr/sbin", "/sbin"). + Symlink("var/srv", "/srv"). + Bind("/run", "/run", false, true). + Bind("/tmp", "/tmp", false, true). + Bind("/var", "/var", false, true). + Bind("/run/user/1971/.dbus-proxy/", "/run/user/1971/.dbus-proxy/", false, true). + Bind("/boot", "/boot"). + Bind("/dev", "/dev"). + Bind("/proc", "/proc"). + Bind("/sys", "/sys"). + Bind("/sysroot", "/sysroot"). + Bind("/usr", "/usr"). + Bind("/etc", "/etc"), + []string{ "--unshare-all", "--unshare-user", "--disable-userns", "--assert-userns-disabled", "--clearenv", "--die-with-parent", diff --git a/helper/bwrap/sequential.go b/helper/bwrap/sequential.go index fb97e65..53ab8aa 100644 --- a/helper/bwrap/sequential.go +++ b/helper/bwrap/sequential.go @@ -2,14 +2,19 @@ package bwrap import ( "encoding/gob" + "fmt" + "io" "os" "strconv" + + "git.gensokyo.uk/security/fortify/helper/proc" ) func init() { gob.Register(new(PermConfig[SymlinkConfig])) gob.Register(new(PermConfig[*TmpfsConfig])) gob.Register(new(OverlayConfig)) + gob.Register(new(DataConfig)) } type PositionalArg int @@ -44,6 +49,10 @@ const ( SyncFd Seccomp + + File + BindData + ROBindData ) var positionalArgs = [...]string{ @@ -74,6 +83,10 @@ var positionalArgs = [...]string{ SyncFd: "--sync-fd", Seccomp: "--seccomp", + + File: "--file", + BindData: "--bind-data", + ROBindData: "--ro-bind-data", } type PermConfig[T FSBuilder] struct { @@ -202,12 +215,59 @@ func (s SymlinkConfig) Append(args *[]string) { *args = append(*args, Symlink.St type ChmodConfig map[string]os.FileMode -func (c ChmodConfig) Len() int { - return len(c) -} - +func (c ChmodConfig) Len() int { return len(c) } func (c ChmodConfig) Append(args *[]string) { for path, mode := range c { *args = append(*args, Chmod.String(), strconv.FormatInt(int64(mode), 8), path) } } + +const ( + DataWrite = iota + DataBind + DataROBind +) + +type DataConfig struct { + Dest string `json:"dest"` + Data []byte `json:"data,omitempty"` + Type int `json:"type"` + proc.File +} + +func (d *DataConfig) Path() string { return d.Dest } +func (d *DataConfig) Len() int { + if d == nil || d.Data == nil { + return 0 + } + return 3 +} +func (d *DataConfig) Init(fd uintptr, v **os.File) uintptr { + if d.File != nil { + panic("file initialised twice") + } + d.File = proc.NewWriterTo(d) + return d.File.Init(fd, v) +} +func (d *DataConfig) WriteTo(w io.Writer) (int64, error) { + n, err := w.Write(d.Data) + return int64(n), err +} +func (d *DataConfig) Append(args *[]string) { + if d == nil || d.Data == nil { + return + } + var a PositionalArg + switch d.Type { + case DataWrite: + a = File + case DataBind: + a = BindData + case DataROBind: + a = ROBindData + default: + panic(fmt.Sprintf("invalid type %d", a)) + } + + *args = append(*args, a.String(), strconv.Itoa(int(d.Fd())), d.Dest) +}