| 1 | //go:build linux |
| 2 | |
| 3 | package host |
| 4 | |
| 5 | import ( |
| 6 | "crypto/sha256" |
| 7 | "encoding/hex" |
| 8 | "fmt" |
| 9 | "os" |
| 10 | "path/filepath" |
| 11 | "testing" |
| 12 | |
| 13 | "github.com/carlosframework/platform/internal/hostbox" |
| 14 | "github.com/carlosframework/platform/internal/hostguard" |
| 15 | "github.com/carlosframework/platform/internal/provision" |
| 16 | "golang.org/x/sys/unix" |
| 17 | ) |
| 18 | |
| 19 | func TestServingParkSourceUsesRootInventory(t *testing.T) { |
| 20 | t.Parallel() |
| 21 | for _, attack := range []string{"none", "unmaterialized", "duplicate", "owner", "revision", "enrollment", "record", "stamp", "foreign", "missing", "flat", "symlink", "replacement"} { |
| 22 | t.Run(attack, func(t *testing.T) { |
| 23 | f, _, _, inputs := servingRetirementFixture(t) |
| 24 | o := servingAdmit(t, f, &inputs, "G") |
| 25 | h := o.Claim.Holder |
| 26 | actor := hostbox.Actor{Box: "box-1", Region: "ie", Enrollment: "enrollment", Revision: 7, Binding: "7"} |
| 27 | root := hostguard.RootInstallation{Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Migration: 1, InventoryRevision: 3, Account: h.Account, App: h.App, Record: h.Key, Box: actor.Box, Enrollment: actor.Enrollment} |
| 28 | d, err := os.Open(filepath.Dir(inputs.DBPath)) |
| 29 | if err != nil { |
| 30 | t.Fatal(err) |
| 31 | } |
| 32 | id, err := servingParkDirID(d) |
| 33 | d.Close() |
| 34 | if err != nil { |
| 35 | t.Fatal(err) |
| 36 | } |
| 37 | owner := sha256.Sum256([]byte(fmt.Sprintf("%s\x00%s\x00%d\x00%s", o.Host, h.Incarnation, h.Stamp, actor.Box))) |
| 38 | in := hostbox.StopInventory{Complete: true, Host: o.Host, Incarnation: h.Incarnation, InstallationStamp: h.Stamp, Box: actor.Box, Enrollment: actor.Enrollment, DirectoryRevision: "7", Entries: []hostbox.StopInventoryEntry{{Kind: "path", ManagedRoot: "data", RelativePath: "instances/" + provision.InstanceID(o.Host), ArtifactID: "state", Ownership: hex.EncodeToString(owner[:]), Materialization: "materialized", Device: id.Device, Inode: id.Inode, Type: unix.S_IFDIR}}} |
| 39 | switch attack { |
| 40 | case "unmaterialized": |
| 41 | in.Entries[0].Materialization = "intended" |
| 42 | case "duplicate": |
| 43 | in.Entries = append(in.Entries, in.Entries[0]) |
| 44 | case "owner": |
| 45 | in.Entries[0].Ownership = "forged" |
| 46 | case "revision": |
| 47 | root.InventoryRevision++ |
| 48 | case "enrollment": |
| 49 | actor.Enrollment = "foreign" |
| 50 | case "record": |
| 51 | root.Record = "foreign" |
| 52 | case "stamp": |
| 53 | root.Stamp++ |
| 54 | case "foreign": |
| 55 | in.Host = "other.example" |
| 56 | case "missing": |
| 57 | in.Entries = nil |
| 58 | case "flat": |
| 59 | in.Entries[0].RelativePath = "state" |
| 60 | case "symlink", "replacement": |
| 61 | dir := filepath.Dir(inputs.DBPath) |
| 62 | if err := os.Rename(dir, dir+".original"); err != nil { |
| 63 | t.Fatal(err) |
| 64 | } |
| 65 | if attack == "symlink" { |
| 66 | err = os.Symlink(dir+".original", dir) |
| 67 | } else { |
| 68 | err = os.Mkdir(dir, 0700) |
| 69 | } |
| 70 | if err != nil { |
| 71 | t.Fatal(err) |
| 72 | } |
| 73 | } |
| 74 | got, err := servingParkStateDirectory(f.guard.DataDir, o, root, in, 3, actor) |
| 75 | if attack == "none" { |
| 76 | if err != nil || got != id { |
| 77 | t.Fatalf("source: %+v %v", got, err) |
| 78 | } |
| 79 | } else if err == nil { |
| 80 | t.Fatal("unproved source accepted") |
| 81 | } |
| 82 | }) |
| 83 | } |
| 84 | } |
| 85 | |