rule_change_profile.bend

bend-osdev-apparmor@0.1.0.0 · 210 lines · 24 definitions

1# bend-osdev-apparmor: change_profile rules (apparmor.d(5), "CHANGE_PROFILE
2# RULE", "change_profile rules"): `change_profile [[safe|unsafe] EXEC_COND]
3# [-> PROFILE NAME]`, which profiles a task may move to through the
4# change_profile API, optionally only when it executes a matching program.
5# safe (the default) and unsafe say whether the environment is scrubbed; an
6# exec mode needs an exec condition. One permission bit: change.
7import Base
8import bend-osdev-apparmor@0.1.0.0/text.bend as X
9import bend-osdev-apparmor@0.1.0.0/glob.bend as G
10import bend-osdev-apparmor@0.1.0.0/perm.bend as P
11import bend-osdev-apparmor@0.1.0.0/token.bend as T
12import bend-osdev-apparmor@0.1.0.0/error.bend as E
13import bend-osdev-apparmor@0.1.0.0/rule.bend as R
14
15# exec: None for any (or no) exec; target: None for every profile
16type AaChangeRule is Data:
17  AaChangeRule{qual: P.AaQual, exec: Maybe<&2, G.AaGlob>, safe: Bool, target: Maybe<&2, G.AaGlob>}
18
19# a change to the profile named target, now (exec None) or when the program
20# exec is executed
21type AaChangeQuery is Data:
22  AaChangeQuery{exec: Maybe<&2, List<&2, U32>>, target: List<&2, U32>}
23
24# the builder: mode word seen (0 none, 1 safe, 2 unsafe), exec condition,
25# arrow seen, target
26type AaChB is Data:
27  AaChB{mode: Nat, exec: Maybe<&2, G.AaGlob>, arrow: Bool, target: Maybe<&2, G.AaGlob>}
28
29def AaChange.is_path(t: T.AaTerm) -> Bool:
30  match t:
31    case T.AaTmWord{+w}:
32      Bool.or(U32.is_eq(X.AaText.head(w), 47), U32.is_eq(X.AaText.head(w), 123))
33    case T.AaTmQuoted{q}:
34      True{}
35    case _:
36      False{}
37
38def AaChB.set_target(b: AaChB, g: G.AaGlob, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
39  match b:
40    case AaChB{m, e, ar, tg}:
41      match ar:
42        case False{}:
43          Fail{E.AaSrc.syntax(src)}
44        case True{}:
45          match tg:
46            case None{}:
47              Done{AaChB{m, e, True{}, Some{g}}}
48            case Some{h}:
49              Fail{E.AaSrc.syntax(src)}
50
51def AaChB.set_exec(b: AaChB, g: G.AaGlob, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
52  match b:
53    case AaChB{m, e, ar, tg}:
54      match e:
55        case None{}:
56          match ar:
57            case False{}:
58              Done{AaChB{m, Some{g}, False{}, tg}}
59            case True{}:
60              Fail{E.AaSrc.syntax(src)}
61        case Some{h}:
62          Fail{E.AaSrc.syntax(src)}
63
64# a pattern: the target after the arrow, else the exec condition
65def AaChB.glob(b: AaChB, g: G.AaGlob, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
66  match b:
67    case AaChB{m, e, +ar, tg}:
68      match ar:
69        case True{}:
70          AaChB.set_target(AaChB{m, e, ar, tg}, g, src)
71        case False{}:
72          AaChB.set_exec(AaChB{m, e, ar, tg}, g, src)
73
74def AaChB.arrow(b: AaChB, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
75  match b:
76    case AaChB{m, e, ar, tg}:
77      match ar:
78        case True{}:
79          Fail{E.AaSrc.syntax(src)}
80        case False{}:
81          Done{AaChB{m, e, True{}, tg}}
82
83# safe or unsafe, before anything else
84def AaChB.mode(b: AaChB, n: Nat, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
85  match b:
86    case AaChB{m, e, ar, tg}:
87      match m:
88        case 0n:
89          match e:
90            case None{}:
91              match ar:
92                case False{}:
93                  Done{AaChB{n, None{}, False{}, tg}}
94                case True{}:
95                  Fail{E.AaSrc.syntax(src)}
96            case Some{h}:
97              Fail{E.AaSrc.syntax(src)}
98        case 1n+k:
99          Fail{E.AaSrc.syntax(src)}
100
101def AaChB.term.glob(r: Result<&2, &2, E.AaError, G.AaGlob>, b: AaChB, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
102  match r:
103    case Fail{e}:
104      Fail{e}
105    case Done{g}:
106      AaChB.glob(b, g, src)
107
108def AaChB.step(t: T.AaTerm, +b: AaChB, +src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
109  match t:
110    case T.AaTmWord{+w}:
111      Bool.pick(Result<&2, &2, E.AaError, AaChB>, X.AaText.is(w, "safe"), AaChB.mode(b, 1n, src), Bool.pick(Result<&2, &2, E.AaError, AaChB>, X.AaText.is(w, "unsafe"), AaChB.mode(b, 2n, src), AaChB.term.glob(R.AaRule.glob_text(w, src), b, src)))
112    case T.AaTmQuoted{q}:
113      AaChB.term.glob(R.AaRule.glob_text(q, src), b, src)
114    case T.AaTmArrow{}:
115      AaChB.arrow(b, src)
116    case _:
117      Fail{E.AaSrc.syntax(src)}
118
119def AaChB.run(ts: List<&2, T.AaTerm>, b: AaChB, +src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChB>:
120  match ts:
121    case Nil{}:
122      Done{b}
123    case Con{t, rest}:
124      do Result<&2, &2, E.AaError, AaChB>:
125        b2 : AaChB <- AaChB.step(t, b, src)
126        AaChB.run(rest, b2, src)
127
128def AaChange.is_some(m: Maybe<&2, G.AaGlob>) -> Bool:
129  match m:
130    case None{}:
131      False{}
132    case Some{g}:
133      True{}
134
135# an exec mode needs an exec condition; an arrow needs a target
136def AaChB.finish(q: P.AaQual, b: AaChB, src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChangeRule>:
137  match b:
138    case AaChB{+m, +e, ar, +tg}:
139      +bad = Bool.or(Bool.and(Nat.is_gt(m, 0n), Bool.not(AaChange.is_some(e))), Bool.and(ar, Bool.not(AaChange.is_some(tg))))
140      Bool.pick(Result<&2, &2, E.AaError, AaChangeRule>, bad, Fail{E.AaSrc.syntax(src)}, Done{AaChangeRule{q, e, Bool.not(Nat.is_eq(m, 2n)), tg}})
141
142def AaChange.parse.q(r: Result<&2, &2, E.AaError, P.AaQual>, ts: List<&2, T.AaTerm>, +src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChangeRule>:
143  match r:
144    case Fail{e}:
145      Fail{e}
146    case Done{q}:
147      do Result<&2, &2, E.AaError, AaChangeRule>:
148        b : AaChB <- AaChB.run(ts, AaChB{0n, None{}, False{}, None{}}, src)
149        AaChB.finish(q, b, src)
150
151# the terms after `change_profile`
152def AaChange.parse(ts: List<&2, T.AaTerm>, q: P.AaQual, +src: E.AaSrc) -> Result<&2, &2, E.AaError, AaChangeRule>:
153  AaChange.parse.q(R.AaRule.no_owner(q, src), ts, src)
154
155# Matching: the target matches, and a rule with an exec condition applies
156# only to a change at the exec of a matching program
157
158def AaChange.exec_ok(m: Maybe<&2, G.AaGlob>, x: Maybe<&2, List<&2, U32>>) -> Bool:
159  match m:
160    case None{}:
161      True{}
162    case Some{g}:
163      match x:
164        case None{}:
165          False{}
166        case Some{p}:
167          G.AaGlob.matches(g, p)
168
169def AaChangeRule.matches(r: AaChangeRule, q: AaChangeQuery) -> Bool:
170  match r:
171    case AaChangeRule{qu, e, s, tg}:
172      match q:
173        case AaChangeQuery{qe, qt}:
174          Bool.and(AaChange.exec_ok(e, qe), R.AaRule.opt_match(tg, qt))
175
176def AaChangeRule.matching.go(hit: Bool, r: AaChangeRule, rest: List<&2, AaChangeRule>) -> List<&2, AaChangeRule>:
177  match hit:
178    case True{}:
179      Con{r, rest}
180    case False{}:
181      rest
182
183def AaChangeRule.matching(rs: List<&2, AaChangeRule>, +q: AaChangeQuery) -> List<&2, AaChangeRule>:
184  match rs:
185    case Nil{}:
186      Nil{}
187    case Con{+r, t}:
188      AaChangeRule.matching.go(AaChangeRule.matches(r, q), r, AaChangeRule.matching(t, q))
189
190def AaChangeRule.grants.of(rs: List<&2, AaChangeRule>) -> List<&2, P.AaGrant>:
191  match rs:
192    case Nil{}:
193      Nil{}
194    case Con{r, t}:
195      match r:
196        case AaChangeRule{qu, e, s, tg}:
197          Con{P.AaGrant.of(qu, P.AaPerms.bit(0n)), AaChangeRule.grants.of(t)}
198
199def AaChangeRule.grants(rs: List<&2, AaChangeRule>, +q: AaChangeQuery) -> List<&2, P.AaGrant>:
200  AaChangeRule.grants.of(AaChangeRule.matching(rs, q))
201
202def AaChangeRule.append(xs: List<&2, AaChangeRule>, ys: List<&2, AaChangeRule>) -> List<&2, AaChangeRule>:
203  match xs:
204    case Nil{}:
205      ys
206    case Con{h, t}:
207      Con{h, AaChangeRule.append(t, ys)}
208
209def AaChangeQuery.req() -> P.AaPerms:
210  P.AaPerms.bit(0n)