Skip to content

Commit

Permalink
WIP: Support for composite rules
Browse files Browse the repository at this point in the history
Fixes issue #25
  • Loading branch information
gnoack committed Oct 13, 2024
1 parent affa120 commit 7c8ab09
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 0 deletions.
46 changes: 46 additions & 0 deletions landlock/composite_opt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package landlock

type compositeRule struct {
rules []Rule
}

func (c *compositeRule) compatibleWithConfig(cfg Config) bool {
for _, r := range c.rules {
if !r.compatibleWithConfig(cfg) {
return false
}
}
return true
}

func (c *compositeRule) downgrade(cfg Config) (out Rule, ok bool) {
cr := new(compositeRule)
for _, r := range c.rules {
r, ok := r.downgrade(cfg)
if !ok {
return nil, false
}
cr.rules = append(cr.rules, r)
}
return cr, true
}

func (c *compositeRule) addToRuleset(rulesetFD int, cfg Config) error {
for _, r := range c.rules {
err := r.addToRuleset(rulesetFD, cfg)
if err != nil {
return err
}
}
return nil
}

// CompositeRule returns a rule composed of sub-rules.
//
// A composite rule passed to [Restrict] behaves the same as passing
// all sub-rules individually. Composite rules are not strictly
// necessary in Go-Landlock, but useful for building libraries of
// re-usable Landlock rules.
func CompositeRule(rules ...Rule) Rule {
return &compositeRule{rules: rules}
}
32 changes: 32 additions & 0 deletions landlock/llrules/experimental.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Package llrules implements commonly used groups of Landlock rules.
//
// This package is *experimental*.
package llrules

import "github.com/landlock-lsm/go-landlock/landlock"

func DNS() landlock.Rule {
// UDP is not restrictable yet, but it can be added here once
// Landlock can do that.
return landlock.CompositeRule(landlock.ConnectTCP(53), dnsFiles())
}

func dnsFiles() landlock.Rule {
return landlock.ROFiles(
"/etc/hosts",
"/etc/resolv.conf",
).IgnoreIfMissing()
}

func SharedLibraries() landlock.Rule {
// XXX: How does the linker look up this list of paths?
// XXX: Use more specific rulesets.
return landlock.RODirs(
"/lib",
"/lib32",
"/lib64",
"/usr/lib",
"/usr/lib32",
"/usr/lib64",
).IgnoreIfMissing()
}
47 changes: 47 additions & 0 deletions landlock/llrules/llrules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package llrules_test

import (
"context"
"net"
"testing"

"github.com/landlock-lsm/go-landlock/landlock"
"github.com/landlock-lsm/go-landlock/landlock/llrules"
"github.com/landlock-lsm/go-landlock/landlock/lltest"
)

func TestDNSOverTCP(t *testing.T) {
lltest.RunInSubprocess(t, func() {
err := landlock.V5.BestEffort().Restrict(llrules.DNS())
if err != nil {
t.Fatalf("Enabling Landlock: %v", err)
}

r := net.Resolver{
PreferGo: true,
}
_, err = r.LookupHost(context.Background(), "localhost")
if err != nil {
t.Errorf("Unexpected DNS error: %v", err)
}
})
}

func TestDNSOverTCP_fail(t *testing.T) {
lltest.RequireABI(t, 1)

lltest.RunInSubprocess(t, func() {
err := landlock.V5.BestEffort().Restrict()
if err != nil {
t.Fatalf("Enabling Landlock: %v", err)
}

r := net.Resolver{
PreferGo: true,
}
_, err = r.LookupHost(context.Background(), "localhost")
if err == nil {
t.Errorf("Expected DNS error, but got success")
}
})
}
49 changes: 49 additions & 0 deletions landlock/rule_composite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package landlock

import "fmt"

type RuleGroup struct {
rules []Rule
}

// GroupRules groups the given rules into a single Rule value.
// The result behaves the same in a Landlock restriction call
// as listing all of the individual rules separately.
func GroupRules(rules ...Rule) RuleGroup {
return RuleGroup{rules: rules}
}

func (g RuleGroup) compatibleWithConfig(c Config) bool {
for _, r := range g.rules {
if !r.compatibleWithConfig(c) {
return false
}
}
return true
}

func (g RuleGroup) downgrade(c Config) (out Rule, ok bool) {
rs := make([]Rule, 0, len(g.rules))
for _, r := range g.rules {
r, ok := r.downgrade(c)
if !ok {
return GroupRules(), false
}
rs = append(rs, r)
}
return GroupRules(rs...), true
}

func (g RuleGroup) addToRuleset(rulesetFD int, c Config) error {
for _, r := range g.rules {
err := r.addToRuleset(rulesetFD, c)
if err != nil {
return err
}
}
return nil
}

func (g RuleGroup) String() string {
return fmt.Sprintf("rules: %v", g.rules)
}

0 comments on commit 7c8ab09

Please sign in to comment.