From 082fa5e918ca396fbf2a444417b9bf1e58d65779 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Tue, 21 Mar 2023 06:49:24 -0600 Subject: [PATCH] Add initial bits for landlock. Currently not working fully. --- go.mod | 5 ++- go.sum | 9 ++++- protect.go | 6 +-- protect_linux.go | 76 ++++++++++++++++++++++++++++++++++++ protect_linux_ro_dir_test.go | 53 +++++++++++++++++++++++++ protect_stubs.go | 4 +- 6 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 protect_linux.go create mode 100644 protect_linux_ro_dir_test.go diff --git a/go.mod b/go.mod index fdc49c8..230e0a0 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module suah.dev/protect go 1.14 -require golang.org/x/sys v0.4.0 +require ( + github.com/landlock-lsm/go-landlock v0.0.0-20230225094210-7a98d7db83f2 + golang.org/x/sys v0.6.0 +) diff --git a/go.sum b/go.sum index c2a6782..6fd3043 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,7 @@ -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/landlock-lsm/go-landlock v0.0.0-20230225094210-7a98d7db83f2 h1:kTSOM+yiVubrJQI/LJ67EGxYqrqC0C5VkfRurbFg7J4= +github.com/landlock-lsm/go-landlock v0.0.0-20230225094210-7a98d7db83f2/go.mod h1:oCxtVqzP6dNPgAQK+4okeQk9BcxjkttF8MG4DmoT6Sk= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.66 h1:ikIhPzfkSSAEwBOU+2DWhoF+xnGUhvlMTfQjBVhvzQY= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.66/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= diff --git a/protect.go b/protect.go index b674dc3..e40ae8a 100644 --- a/protect.go +++ b/protect.go @@ -12,8 +12,8 @@ import ( "strings" ) -// Unveil is a wrapper for OpenBSD's unveil(2). unveil can be used to limit -// a processes view of the filesystem. +// Unveil is a wrapper for OpenBSD's unveil(2) and Linux's LandLock. Both of +// which are used to limit a processes view of the filesystem. // // The first call to Unveil removes a processes visibility to everything // except 'path'. Any subsequent calls expand the view to contain those @@ -45,7 +45,7 @@ func UnveilSet(set map[string]string, block bool) error { // UnveilBlock locks the Unveil'd paths. Preventing further changes to a // processes filesystem view. // -// On non-OpenBSD machines this call is a noop. +// On non-OpenBSD,Linux machines this call is a noop. func UnveilBlock() error { return unveilBlock() } diff --git a/protect_linux.go b/protect_linux.go new file mode 100644 index 0000000..f1bb598 --- /dev/null +++ b/protect_linux.go @@ -0,0 +1,76 @@ +//go:build linux +// +build linux + +package protect + +import ( + "log" + "os" + + "github.com/landlock-lsm/go-landlock/landlock" +) + +type lands []landlock.PathOpt + +var landToLock lands + +func (l lands) landAdd(path, flags string) error { + s, err := os.Stat(path) + if err != nil { + return err + } + + switch mode := s.Mode(); { + case mode.IsDir(): + log.Println("directory", path) + switch flags { + case "r": + l = append(l, landlock.RODirs(path)) + case "w": + l = append(l, landlock.RWDirs(path)) + case "rw": + l = append(l, landlock.RWDirs(path)) + } + default: + log.Println("file", path) + switch flags { + case "r": + log.Println("READ ONLY") + l = append(l, landlock.ROFiles(path)) + case "w": + log.Println("WRITE") + l = append(l, landlock.RWFiles(path)) + case "rw": + log.Println("WRITE") + l = append(l, landlock.RWFiles(path)) + } + } + + return nil +} + +func (l *lands) landWalk() []landlock.PathOpt { + return *l +} + +func unveil(path string, flags string) error { + if path == "" { + err := landlock.V3.BestEffort().RestrictPaths() + if err != nil { + return landlock.V2.BestEffort().RestrictPaths() + } + } + return landToLock.landAdd(path, flags) +} + +func unveilBlock() error { + err := landlock.V3.RestrictPaths(landToLock.landWalk()...) + if err != nil { + return landlock.V2.RestrictPaths(landToLock.landWalk()...) + } + return err +} + +func pledge(promises string) error { + return nil +} diff --git a/protect_linux_ro_dir_test.go b/protect_linux_ro_dir_test.go new file mode 100644 index 0000000..50023bf --- /dev/null +++ b/protect_linux_ro_dir_test.go @@ -0,0 +1,53 @@ +package protect + +import ( + "os" + "runtime" + "testing" +) + +/* +FIXME +func TestLandlockFileWrite(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("Not running on Linux... skipping landlock test") + } + + f, err := os.CreateTemp("", "landlockTest") + if err != nil { + t.Fatal(err) + } + defer os.Remove(f.Name()) + + unveil("/tmp", "r") + err = unveilBlock() + if err != nil { + t.Fatal(err) + } + + if c, err := f.Write([]byte("badbeef")); err == nil { + t.Fatalf("wrote %d bytes to %q when I shouldn't have been able too\n", c, f.Name()) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } +} +*/ + +func TestLandlockRO(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("Not running on Linux... skipping landlock test") + } + + unveil("/tmp", "r") + err := unveilBlock() + if err != nil { + t.Fatal(err) + } + + f, err := os.CreateTemp("", "landlockTest") + if err == nil { + t.Fatalf("should not have been able to create %q, but was able to do so\n", f.Name()) + } +} diff --git a/protect_stubs.go b/protect_stubs.go index 3a99262..6239fa9 100644 --- a/protect_stubs.go +++ b/protect_stubs.go @@ -1,5 +1,5 @@ -//go:build !openbsd -// +build !openbsd +//go:build !openbsd && !linux +// +build !openbsd,!linux package protect