aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/landlock/landlock.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/landlock/landlock.go')
-rw-r--r--internal/landlock/landlock.go259
1 files changed, 259 insertions, 0 deletions
diff --git a/internal/landlock/landlock.go b/internal/landlock/landlock.go
new file mode 100644
index 00000000..b65e3ebe
--- /dev/null
+++ b/internal/landlock/landlock.go
@@ -0,0 +1,259 @@
+package landlock
+
+import (
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "hakurei.app/ext"
+)
+
+// include/uapi/linux/landlock.h
+
+const (
+ LANDLOCK_CREATE_RULESET_VERSION = 1 << iota
+)
+
+// AccessFS is bitmask of handled filesystem actions.
+type AccessFS uint64
+
+const (
+ LANDLOCK_ACCESS_FS_EXECUTE AccessFS = 1 << iota
+ LANDLOCK_ACCESS_FS_WRITE_FILE
+ LANDLOCK_ACCESS_FS_READ_FILE
+ LANDLOCK_ACCESS_FS_READ_DIR
+ LANDLOCK_ACCESS_FS_REMOVE_DIR
+ LANDLOCK_ACCESS_FS_REMOVE_FILE
+ LANDLOCK_ACCESS_FS_MAKE_CHAR
+ LANDLOCK_ACCESS_FS_MAKE_DIR
+ LANDLOCK_ACCESS_FS_MAKE_REG
+ LANDLOCK_ACCESS_FS_MAKE_SOCK
+ LANDLOCK_ACCESS_FS_MAKE_FIFO
+ LANDLOCK_ACCESS_FS_MAKE_BLOCK
+ LANDLOCK_ACCESS_FS_MAKE_SYM
+ LANDLOCK_ACCESS_FS_REFER
+ LANDLOCK_ACCESS_FS_TRUNCATE
+ LANDLOCK_ACCESS_FS_IOCTL_DEV
+
+ _LANDLOCK_ACCESS_FS_DELIM
+)
+
+// String returns a space-separated string of [AccessFS] flags.
+func (f AccessFS) String() string {
+ switch f {
+ case LANDLOCK_ACCESS_FS_EXECUTE:
+ return "execute"
+
+ case LANDLOCK_ACCESS_FS_WRITE_FILE:
+ return "write_file"
+
+ case LANDLOCK_ACCESS_FS_READ_FILE:
+ return "read_file"
+
+ case LANDLOCK_ACCESS_FS_READ_DIR:
+ return "read_dir"
+
+ case LANDLOCK_ACCESS_FS_REMOVE_DIR:
+ return "remove_dir"
+
+ case LANDLOCK_ACCESS_FS_REMOVE_FILE:
+ return "remove_file"
+
+ case LANDLOCK_ACCESS_FS_MAKE_CHAR:
+ return "make_char"
+
+ case LANDLOCK_ACCESS_FS_MAKE_DIR:
+ return "make_dir"
+
+ case LANDLOCK_ACCESS_FS_MAKE_REG:
+ return "make_reg"
+
+ case LANDLOCK_ACCESS_FS_MAKE_SOCK:
+ return "make_sock"
+
+ case LANDLOCK_ACCESS_FS_MAKE_FIFO:
+ return "make_fifo"
+
+ case LANDLOCK_ACCESS_FS_MAKE_BLOCK:
+ return "make_block"
+
+ case LANDLOCK_ACCESS_FS_MAKE_SYM:
+ return "make_sym"
+
+ case LANDLOCK_ACCESS_FS_REFER:
+ return "fs_refer"
+
+ case LANDLOCK_ACCESS_FS_TRUNCATE:
+ return "fs_truncate"
+
+ case LANDLOCK_ACCESS_FS_IOCTL_DEV:
+ return "fs_ioctl_dev"
+
+ default:
+ var c []AccessFS
+ for i := AccessFS(1); i < _LANDLOCK_ACCESS_FS_DELIM; i <<= 1 {
+ if f&i != 0 {
+ c = append(c, i)
+ }
+ }
+ if len(c) == 0 {
+ return "NULL"
+ }
+ s := make([]string, len(c))
+ for i, v := range c {
+ s[i] = v.String()
+ }
+ return strings.Join(s, " ")
+ }
+}
+
+// AccessNet is bitmask of handled network actions.
+type AccessNet uint64
+
+const (
+ LANDLOCK_ACCESS_NET_BIND_TCP AccessNet = 1 << iota
+ LANDLOCK_ACCESS_NET_CONNECT_TCP
+
+ _LANDLOCK_ACCESS_NET_DELIM
+)
+
+// String returns a space-separated string of [AccessNet] flags.
+func (f AccessNet) String() string {
+ switch f {
+ case LANDLOCK_ACCESS_NET_BIND_TCP:
+ return "bind_tcp"
+
+ case LANDLOCK_ACCESS_NET_CONNECT_TCP:
+ return "connect_tcp"
+
+ default:
+ var c []AccessNet
+ for i := AccessNet(1); i < _LANDLOCK_ACCESS_NET_DELIM; i <<= 1 {
+ if f&i != 0 {
+ c = append(c, i)
+ }
+ }
+ if len(c) == 0 {
+ return "NULL"
+ }
+ s := make([]string, len(c))
+ for i, v := range c {
+ s[i] = v.String()
+ }
+ return strings.Join(s, " ")
+ }
+}
+
+// Scope is bitmask of scopes restricting a Landlock domain from accessing outside resources.
+type Scope uint64
+
+const (
+ LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET Scope = 1 << iota
+ LANDLOCK_SCOPE_SIGNAL
+
+ _LANDLOCK_SCOPE_DELIM
+)
+
+// String returns a space-separated string of [Scope] flags.
+func (f Scope) String() string {
+ switch f {
+ case LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET:
+ return "abstract_unix_socket"
+
+ case LANDLOCK_SCOPE_SIGNAL:
+ return "signal"
+
+ default:
+ var c []Scope
+ for i := Scope(1); i < _LANDLOCK_SCOPE_DELIM; i <<= 1 {
+ if f&i != 0 {
+ c = append(c, i)
+ }
+ }
+ if len(c) == 0 {
+ return "NULL"
+ }
+ s := make([]string, len(c))
+ for i, v := range c {
+ s[i] = v.String()
+ }
+ return strings.Join(s, " ")
+ }
+}
+
+// RulesetAttr is equivalent to struct landlock_ruleset_attr.
+type RulesetAttr struct {
+ // Bitmask of handled filesystem actions.
+ HandledAccessFS AccessFS
+ // Bitmask of handled network actions.
+ HandledAccessNet AccessNet
+ // Bitmask of scopes restricting a Landlock domain from accessing outside
+ // resources (e.g. IPCs).
+ Scoped Scope
+}
+
+// String returns a user-facing description of [RulesetAttr].
+func (rulesetAttr *RulesetAttr) String() string {
+ if rulesetAttr == nil {
+ return "NULL"
+ }
+ elems := make([]string, 0, 3)
+ if rulesetAttr.HandledAccessFS > 0 {
+ elems = append(elems, "fs: "+rulesetAttr.HandledAccessFS.String())
+ }
+ if rulesetAttr.HandledAccessNet > 0 {
+ elems = append(elems, "net: "+rulesetAttr.HandledAccessNet.String())
+ }
+ if rulesetAttr.Scoped > 0 {
+ elems = append(elems, "scoped: "+rulesetAttr.Scoped.String())
+ }
+ if len(elems) == 0 {
+ return "0"
+ }
+ return strings.Join(elems, ", ")
+}
+
+// Create loads the ruleset into the kernel.
+func (rulesetAttr *RulesetAttr) Create(flags uintptr) (fd int, err error) {
+ var pointer, size uintptr
+ // NULL needed for abi version
+ if rulesetAttr != nil {
+ pointer = uintptr(unsafe.Pointer(rulesetAttr))
+ size = unsafe.Sizeof(*rulesetAttr)
+ }
+
+ rulesetFd, _, errno := syscall.Syscall(
+ ext.SYS_LANDLOCK_CREATE_RULESET,
+ pointer, size,
+ flags,
+ )
+ fd = int(rulesetFd)
+ err = errno
+ if fd < 0 {
+ return
+ }
+
+ if rulesetAttr != nil { // not a fd otherwise
+ syscall.CloseOnExec(fd)
+ }
+ return fd, nil
+}
+
+// GetABI returns the ABI version supported by the kernel.
+func GetABI() (int, error) {
+ return (*RulesetAttr)(nil).Create(LANDLOCK_CREATE_RULESET_VERSION)
+}
+
+// RestrictSelf applies a loaded ruleset to the calling thread.
+func RestrictSelf(rulesetFd int, flags uintptr) error {
+ r, _, errno := syscall.Syscall(
+ ext.SYS_LANDLOCK_RESTRICT_SELF,
+ uintptr(rulesetFd),
+ flags,
+ 0,
+ )
+ if r != 0 {
+ return errno
+ }
+ return nil
+}