apparmor.bend

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

1# bend-osdev-apparmor: AppArmor policy, as a pure model: loading profiles
2# from text (apparmor.d(5)) and answering what they allow.
3#
4# Aa.load reads a policy from a file map (each file a name and its text, as
5# Octets) and the name of the file to start from. The queries name a
6# profile by its full name (a, a//child, a//^hat is a//hat) and answer an
7# AaDecision (eval.bend), or None when the policy has no such profile;
8# Aa.exec answers where an exec goes (transitions.bend).
9#
10# The files below this one work on byte lists (List<&2, U32>, what
11# Octets.to_list gives), which is what their laws are stated over.
12import Base
13import bend-osdev-bytes@0.1.0.0/bytes.bend as B
14import bend-osdev-apparmor@0.1.0.0/error.bend as E
15import bend-osdev-apparmor@0.1.0.0/perm.bend as P
16import bend-osdev-apparmor@0.1.0.0/options.bend as O
17import bend-osdev-apparmor@0.1.0.0/profile.bend as PR
18import bend-osdev-apparmor@0.1.0.0/parse.bend as S
19import bend-osdev-apparmor@0.1.0.0/eval.bend as V
20import bend-osdev-apparmor@0.1.0.0/transitions.bend as TR
21import bend-osdev-apparmor@0.1.0.0/rule_file.bend as F
22import bend-osdev-apparmor@0.1.0.0/rule_capability.bend as RC
23import bend-osdev-apparmor@0.1.0.0/rule_network.bend as RN
24import bend-osdev-apparmor@0.1.0.0/rule_signal.bend as RS
25import bend-osdev-apparmor@0.1.0.0/rule_ptrace.bend as RP
26import bend-osdev-apparmor@0.1.0.0/rule_mount.bend as RM
27import bend-osdev-apparmor@0.1.0.0/rule_dbus.bend as RD
28import bend-osdev-apparmor@0.1.0.0/rule_change_profile.bend as RX
29
30# a file of the policy: its name as includes name it (abstractions/base
31# for <abstractions/base>), and its text
32type AaSource is Data:
33  AaSource{name: B.Octets, text: B.Octets}
34
35def Aa.files(xs: List<&2, AaSource>) -> List<&2, S.AaSrcFile>:
36  match xs:
37    case Nil{}:
38      Nil{}
39    case Con{x, t}:
40      match x:
41        case AaSource{n, s}:
42          Con{S.AaSrcFile{B.Octets.to_list(n), B.Octets.to_list(s)}, Aa.files(t)}
43
44# the policy the file main defines, with its includes taken from sources
45def Aa.load(opts: O.AaOptions, sources: List<&2, AaSource>, main: B.Octets) -> Result<&2, &2, E.AaError, PR.AaPolicy>:
46  S.AaParse.load(opts, Aa.files(sources), B.Octets.to_list(main))
47
48def Aa.load_default(sources: List<&2, AaSource>, main: B.Octets) -> Result<&2, &2, E.AaError, PR.AaPolicy>:
49  Aa.load(O.AaOptions.default(), sources, main)
50
51def Aa.profile(pol: PR.AaPolicy, name: B.Octets) -> Maybe<&2, PR.AaProfile>:
52  PR.AaPolicy.lookup(pol, B.Octets.to_list(name))
53
54def Aa.on.file(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: F.AaFileQuery) -> Maybe<&2, V.AaDecision>:
55  match m:
56    case None{}:
57      None{}
58    case Some{p}:
59      Some{V.AaEval.file(opts, p, q)}
60
61# may the profile access path with the permissions req (owner: the task owns the file)?
62def Aa.file(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, path: B.Octets, owner: Bool, req: P.AaPerms) -> Maybe<&2, V.AaDecision>:
63  Aa.on.file(Aa.profile(pol, profile), opts, F.AaFileQuery{B.Octets.to_list(path), owner, req})
64
65def Aa.on.capability(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RC.AaCapQuery) -> Maybe<&2, V.AaDecision>:
66  match m:
67    case None{}:
68      None{}
69    case Some{p}:
70      Some{V.AaEval.capability(opts, p, q)}
71
72# may the profile use capability number cap?
73def Aa.capability(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, cap: U32) -> Maybe<&2, V.AaDecision>:
74  Aa.on.capability(Aa.profile(pol, profile), opts, RC.AaCapQuery{cap})
75
76def Aa.on.network(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RN.AaNetQuery) -> Maybe<&2, V.AaDecision>:
77  match m:
78    case None{}:
79      None{}
80    case Some{p}:
81      Some{V.AaEval.network(opts, p, q)}
82
83# may the profile use a socket of this domain, type and protocol (names: inet, stream, tcp)?
84def Aa.network(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, domain: B.Octets, stype: B.Octets, proto: B.Octets) -> Maybe<&2, V.AaDecision>:
85  Aa.on.network(Aa.profile(pol, profile), opts, RN.AaNetQuery{B.Octets.to_list(domain), B.Octets.to_list(stype), B.Octets.to_list(proto)})
86
87def Aa.on.signal(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RS.AaSignalQuery) -> Maybe<&2, V.AaDecision>:
88  match m:
89    case None{}:
90      None{}
91    case Some{p}:
92      Some{V.AaEval.signal(opts, p, q)}
93
94# may the profile send (receive) the signal named sig to (from) the peer profile?
95def Aa.signal(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, req: P.AaPerms, sig: B.Octets, peer: B.Octets) -> Maybe<&2, V.AaDecision>:
96  Aa.on.signal(Aa.profile(pol, profile), opts, RS.AaSignalQuery{req, B.Octets.to_list(sig), B.Octets.to_list(peer)})
97
98def Aa.on.ptrace(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RP.AaPtraceQuery) -> Maybe<&2, V.AaDecision>:
99  match m:
100    case None{}:
101      None{}
102    case Some{p}:
103      Some{V.AaEval.ptrace(opts, p, q)}
104
105def Aa.ptrace(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, req: P.AaPerms, peer: B.Octets) -> Maybe<&2, V.AaDecision>:
106  Aa.on.ptrace(Aa.profile(pol, profile), opts, RP.AaPtraceQuery{req, B.Octets.to_list(peer)})
107
108def Aa.on.mount(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RM.AaMountQuery) -> Maybe<&2, V.AaDecision>:
109  match m:
110    case None{}:
111      None{}
112    case Some{p}:
113      Some{V.AaEval.mount(opts, p, q)}
114
115def Aa.mount(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, q: RM.AaMountQuery) -> Maybe<&2, V.AaDecision>:
116  Aa.on.mount(Aa.profile(pol, profile), opts, q)
117
118def Aa.on.dbus(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RD.AaDbusQuery) -> Maybe<&2, V.AaDecision>:
119  match m:
120    case None{}:
121      None{}
122    case Some{p}:
123      Some{V.AaEval.dbus(opts, p, q)}
124
125def Aa.dbus(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, q: RD.AaDbusQuery) -> Maybe<&2, V.AaDecision>:
126  Aa.on.dbus(Aa.profile(pol, profile), opts, q)
127
128def Aa.on.change(m: Maybe<&2, PR.AaProfile>, opts: O.AaOptions, q: RX.AaChangeQuery) -> Maybe<&2, V.AaDecision>:
129  match m:
130    case None{}:
131      None{}
132    case Some{p}:
133      Some{V.AaEval.change_profile(opts, p, q)}
134
135def Aa.opt_list(m: Maybe<&2, B.Octets>) -> Maybe<&2, List<&2, U32>>:
136  match m:
137    case None{}:
138      None{}
139    case Some{o}:
140      Some{B.Octets.to_list(o)}
141
142# may the profile change_profile to target (now: exec None; at the exec of a program: Some)?
143def Aa.change_profile(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, exec: Maybe<&2, B.Octets>, target: B.Octets) -> Maybe<&2, V.AaDecision>:
144  Aa.on.change(Aa.profile(pol, profile), opts, RX.AaChangeQuery{Aa.opt_list(exec), B.Octets.to_list(target)})
145
146# where an exec of path by a task confined by profile goes
147def Aa.exec(opts: O.AaOptions, pol: PR.AaPolicy, profile: B.Octets, path: B.Octets, owner: Bool) -> TR.AaExecResult:
148  TR.AaTrans.exec(opts, pol, B.Octets.to_list(profile), owner, B.Octets.to_list(path))
149
150# where an exec of path by an unconfined task goes
151def Aa.exec_unconfined(pol: PR.AaPolicy, path: B.Octets) -> TR.AaExecResult:
152  TR.AaTrans.exec_unconfined(pol, B.Octets.to_list(path))