carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux && rootrunner
2
3package host
4
5import (
6 "encoding/json"
7 "os"
8 "path/filepath"
9 "strings"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/release"
13 "github.com/carlosframework/platform/internal/rootstate"
14 "golang.org/x/sys/unix"
15)
16
17const generationDeviceRebootDir = "/var/lib/carlos-rootrunner-p3-devices"
18
19func TestRootRebootGenerationDevicesPhase1(t *testing.T) {
20 if os.Geteuid() != 0 {
21 t.Fatal("root required")
22 }
23 if err := os.Mkdir(generationDeviceRebootDir, 0755); err != nil {
24 t.Fatal(err)
25 }
26 tree := filepath.Join(generationDeviceRebootDir, "tree")
27 if err := os.Mkdir(tree, 0755); err != nil {
28 t.Fatal(err)
29 }
30 path := filepath.Join(tree, "app")
31 if err := os.WriteFile(path, []byte("retained executable"), 0700); err != nil {
32 t.Fatal(err)
33 }
34 f, err := rootstate.OpenTrustedFile(path)
35 if err != nil {
36 t.Fatal(err)
37 }
38 defer f.Close()
39 d, err := os.Open(tree)
40 if err != nil {
41 t.Fatal(err)
42 }
43 defer d.Close()
44 id, err := rootstate.Identity(f)
45 if err != nil {
46 t.Fatal(err)
47 }
48 tid, err := rootstate.Identity(d)
49 if err != nil {
50 t.Fatal(err)
51 }
52 persistent, err := rootstate.ObjectIdentity(f)
53 if err != nil {
54 t.Fatal(err)
55 }
56 tp, err := rootstate.ObjectIdentity(d)
57 if err != nil {
58 t.Fatal(err)
59 }
60 b := release.GenerationBinding{Path: path, Tree: tree, Executable: id, TreeIdentity: tid, ExecutableProof: persistent, TreeProof: tp}
61 s, err := rootstate.Open(filepath.Join(generationDeviceRebootDir, "authority"), true, false)
62 if err != nil {
63 t.Fatal(err)
64 }
65 defer s.Close()
66 raw, _ := json.Marshal(b)
67 if err = s.Write("binding.json", raw); err != nil {
68 t.Fatal(err)
69 }
70 t.Logf("boot=%s device=%d mount=%d", persistent.Boot, id.Device, persistent.Mount)
71}
72
73func TestRootRebootGenerationDevicesPhase2(t *testing.T) {
74 defer os.RemoveAll(generationDeviceRebootDir)
75 s, err := rootstate.Open(filepath.Join(generationDeviceRebootDir, "authority"), false, false)
76 if err != nil {
77 t.Fatal(err)
78 }
79 defer s.Close()
80 raw, err := s.Read("binding.json")
81 if err != nil {
82 t.Fatal(err)
83 }
84 var b release.GenerationBinding
85 if err = json.Unmarshal(raw, &b); err != nil {
86 t.Fatal(err)
87 }
88 boot, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
89 if err != nil || strings.TrimSpace(string(boot)) == b.ExecutableProof.Boot {
90 t.Fatal("actual reboot required", err)
91 }
92 current, err := generationCurrentFiles(b, rootstate.OpenTrustedFile)
93 if err != nil {
94 t.Fatal("retained identity across boot", err)
95 }
96 t.Logf("prior-device=%d current-device=%d", b.Executable.Device, current.Executable.Device)
97 // The reboot need not renumber this particular instance. Exercise that
98 // branch explicitly while retaining the actual previous boot's handles.
99 changed := b
100 changed.Executable.Device++
101 changed.TreeIdentity.Device++
102 changed.ExecutableProof.Device++
103 changed.ExecutableProof.Mount++
104 changed.TreeProof.Device++
105 changed.TreeProof.Mount++
106 if _, err = generationCurrentFiles(changed, rootstate.OpenTrustedFile); err != nil {
107 t.Fatal("boot-local numbers persisted as authority", err)
108 }
109 foreign := b
110 foreign.ExecutableProof.Filesystem = "different-filesystem"
111 if _, err = generationCurrentFiles(foreign, rootstate.OpenTrustedFile); err == nil {
112 t.Fatal("foreign filesystem accepted")
113 }
114 if err = unix.Mount(b.Tree, b.Tree, "", unix.MS_BIND, ""); err != nil {
115 t.Fatal(err)
116 }
117 if _, err = generationCurrentFiles(b, rootstate.OpenTrustedFile); err == nil {
118 t.Fatal("substituted mount accepted")
119 }
120 if err = unix.Unmount(b.Tree, 0); err != nil {
121 t.Fatal(err)
122 }
123 if err = os.Rename(b.Path, b.Path+"-old"); err != nil {
124 t.Fatal(err)
125 }
126 if err = os.WriteFile(b.Path, []byte("retained executable"), 0700); err != nil {
127 t.Fatal(err)
128 }
129 if _, err = generationCurrentFiles(b, rootstate.OpenTrustedFile); err == nil {
130 t.Fatal("substituted inode accepted")
131 }
132}
133