返回 DeepSeek-Reasonix
acl_test.go
根目录 / internal / packagegrant / acl_test.go
1 package packagegrant
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "errors"
7 "testing"
8 )
9
10 const aceTypeAccessDenied = 0x1
11
12 // sid encodes S-1-<authority>-<subs...> the way Windows lays one out in an ACE.
13 func sid(authority byte, subs ...uint32) []byte {
14 b := []byte{1, byte(len(subs)), 0, 0, 0, 0, 0, authority}
15 for _, s := range subs {
16 b = binary.LittleEndian.AppendUint32(b, s)
17 }
18 return b
19 }
20
21 func ace(kind, flags byte, s []byte) []byte {
22 b := []byte{kind, flags, 0, 0}
23 b = binary.LittleEndian.AppendUint32(b, 0x1200a9)
24 b = append(b, s...)
25 binary.LittleEndian.PutUint16(b[2:4], uint16(len(b)))
26 return b
27 }
28
29 func acl(aces ...[]byte) []byte {
30 b := make([]byte, aclHeaderSize)
31 b[0] = 2
32 for _, a := range aces {
33 b = append(b, a...)
34 }
35 binary.LittleEndian.PutUint16(b[2:4], uint16(len(b)))
36 binary.LittleEndian.PutUint16(b[4:6], uint16(len(aces)))
37 return b
38 }
39
40 var (
41 user = sid(5, 21, 1111111111, 2222222222, 3333333333, 1001)
42 system = sid(5, 18)
43 allPackages = sid(15, 2, 1)
44 allRestrict = sid(15, 2, 2)
45 capability = sid(15, 3, 1024, 1, 2, 3, 4, 5, 6, 7)
46 livePackage = sid(15, 2, 1658335055, 2853777128, 3014724382, 1760870658, 2056114279, 2893136470, 138490047)
47 shortPackage = sid(15, 2, 999, 999, 999)
48 )
49
50 func TestOnlySpecificPackageAllowEntriesAreRemoved(t *testing.T) {
51 keep := [][]byte{
52 ace(aceTypeAccessAllowed, 0, user),
53 ace(aceTypeAccessAllowed, 0, system),
54 ace(aceTypeAccessAllowed, 0, allPackages),
55 ace(aceTypeAccessAllowed, 0, allRestrict),
56 ace(aceTypeAccessAllowed, 0, capability),
57 ace(aceTypeAccessDenied, 0, livePackage),
58 }
59 for name, grant := range map[string][]byte{
60 "a package whose SID has the full hash": ace(aceTypeAccessAllowed, 0, livePackage),
61 "a package SID of any other length": ace(aceTypeAccessAllowed, 0, shortPackage),
62 "a callback allow entry": ace(aceTypeAccessAllowedCallback, 0, livePackage),
63 } {
64 in := acl(append(append([][]byte{}, keep...), grant)...)
65 out, protect, changed, err := rewrite(in, false)
66 if err != nil || !changed || protect {
67 t.Fatalf("%s: changed=%v protect=%v err=%v", name, changed, protect, err)
68 }
69 if want := acl(keep...); !bytes.Equal(out, want) {
70 t.Fatalf("%s: kept entries differ\n got %x\nwant %x", name, out, want)
71 }
72 }
73 }
74
75 // A pass narrows access: a deny naming a package stays, and so does every
76 // entry the judgement has no reason to read.
77 func TestAnACLWithoutPackageGrantsIsLeftAlone(t *testing.T) {
78 in := acl(
79 ace(aceTypeAccessDenied, 0, livePackage),
80 ace(aceTypeAccessAllowed, aceFlagInherited, allPackages),
81 ace(aceTypeAccessAllowed, 0, capability),
82 ace(0x5, 0, livePackage),
83 )
84 out, protect, changed, err := rewrite(in, true)
85 if err != nil || changed || protect || out != nil {
86 t.Fatalf("out=%x protect=%v changed=%v err=%v", out, protect, changed, err)
87 }
88 }
89
90 func TestAnInheritedGrantProtectsTheObjectAndKeepsWhatItInherited(t *testing.T) {
91 in := acl(
92 ace(aceTypeAccessAllowed, 0, user),
93 ace(aceTypeAccessAllowed, aceFlagInherited|0x3, livePackage),
94 ace(aceTypeAccessAllowed, aceFlagInherited|0x3, system),
95 )
96 out, protect, changed, err := rewrite(in, false)
97 if err != nil || !changed || !protect {
98 t.Fatalf("changed=%v protect=%v err=%v", changed, protect, err)
99 }
100 want := acl(ace(aceTypeAccessAllowed, 0, user), ace(aceTypeAccessAllowed, 0x3, system))
101 if !bytes.Equal(out, want) {
102 t.Fatalf("inherited entries were not kept as the object's own\n got %x\nwant %x", out, want)
103 }
104 }
105
106 func TestAProtectedDACLStaysProtected(t *testing.T) {
107 in := acl(ace(aceTypeAccessAllowed, 0, user), ace(aceTypeAccessAllowed, 0, livePackage))
108 if _, protect, changed, err := rewrite(in, true); err != nil || !changed || !protect {
109 t.Fatalf("changed=%v protect=%v err=%v", changed, protect, err)
110 }
111 }
112
113 func TestSizesThatDoNotAccountForTheBytesAreRefused(t *testing.T) {
114 good := acl(ace(aceTypeAccessAllowed, 0, livePackage))
115 cases := map[string][]byte{
116 "shorter than a header": good[:4],
117 "size past the buffer": func() []byte { b := bytes.Clone(good); binary.LittleEndian.PutUint16(b[2:4], 0xffff); return b }(),
118 "count past the size": func() []byte { b := bytes.Clone(good); binary.LittleEndian.PutUint16(b[4:6], 2); return b }(),
119 "entry past the size": func() []byte { b := bytes.Clone(good); binary.LittleEndian.PutUint16(b[10:12], 0xff); return b }(),
120 "entry shorter than its header": func() []byte {
121 b := bytes.Clone(good)
122 binary.LittleEndian.PutUint16(b[10:12], 2)
123 return b
124 }(),
125 }
126 for name, in := range cases {
127 if _, _, _, err := rewrite(in, false); !errors.Is(err, ErrMalformed) {
128 t.Errorf("%s: err=%v, want ErrMalformed", name, err)
129 }
130 }
131 }
132
133 func TestATruncatedSIDIsNeverAPackage(t *testing.T) {
134 entry := ace(aceTypeAccessAllowed, 0, livePackage)
135 truncated := entry[:aceHeaderSize+aceMaskSize+sidMinSize+4]
136 binary.LittleEndian.PutUint16(truncated[2:4], uint16(len(truncated)))
137 if grantsSpecificPackage(truncated) {
138 t.Fatal("a SID shorter than its sub-authority count was read as a package")
139 }
140 }
141
141 lines GO