1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2 | /* |
3 | * AppArmor security module |
4 | * |
5 | * This file contains AppArmor policy definitions. |
6 | * |
7 | * Copyright (C) 1998-2008 Novell/SUSE |
8 | * Copyright 2009-2010 Canonical Ltd. |
9 | */ |
10 | |
11 | #ifndef __AA_POLICY_H |
12 | #define __AA_POLICY_H |
13 | |
14 | #include <linux/capability.h> |
15 | #include <linux/cred.h> |
16 | #include <linux/kref.h> |
17 | #include <linux/rhashtable.h> |
18 | #include <linux/sched.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/socket.h> |
21 | |
22 | #include "apparmor.h" |
23 | #include "audit.h" |
24 | #include "capability.h" |
25 | #include "domain.h" |
26 | #include "file.h" |
27 | #include "lib.h" |
28 | #include "label.h" |
29 | #include "net.h" |
30 | #include "perms.h" |
31 | #include "resource.h" |
32 | |
33 | |
34 | struct aa_ns; |
35 | |
36 | extern int unprivileged_userns_apparmor_policy; |
37 | extern int aa_unprivileged_unconfined_restricted; |
38 | |
39 | extern const char *const aa_profile_mode_names[]; |
40 | #define APPARMOR_MODE_NAMES_MAX_INDEX 4 |
41 | |
42 | #define PROFILE_MODE(_profile, _mode) \ |
43 | ((aa_g_profile_mode == (_mode)) || \ |
44 | ((_profile)->mode == (_mode))) |
45 | |
46 | #define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN) |
47 | |
48 | #define USER_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_USER) |
49 | |
50 | #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL) |
51 | |
52 | #define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT) |
53 | |
54 | #define CHECK_DEBUG1(_profile) ((_profile)->label.flags & FLAG_DEBUG1) |
55 | |
56 | #define CHECK_DEBUG2(_profile) ((_profile)->label.flags & FLAG_DEBUG2) |
57 | |
58 | #define profile_is_stale(_profile) (label_is_stale(&(_profile)->label)) |
59 | |
60 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) |
61 | |
62 | /* |
63 | * FIXME: currently need a clean way to replace and remove profiles as a |
64 | * set. It should be done at the namespace level. |
65 | * Either, with a set of profiles loaded at the namespace level or via |
66 | * a mark and remove marked interface. |
67 | */ |
68 | enum profile_mode { |
69 | APPARMOR_ENFORCE, /* enforce access rules */ |
70 | APPARMOR_COMPLAIN, /* allow and log access violations */ |
71 | APPARMOR_KILL, /* kill task on access violation */ |
72 | APPARMOR_UNCONFINED, /* profile set to unconfined */ |
73 | APPARMOR_USER, /* modified complain mode to userspace */ |
74 | }; |
75 | |
76 | |
77 | /* struct aa_policydb - match engine for a policy |
78 | * count: refcount for the pdb |
79 | * dfa: dfa pattern match |
80 | * perms: table of permissions |
81 | * strs: table of strings, index by x |
82 | * start: set of start states for the different classes of data |
83 | */ |
84 | struct aa_policydb { |
85 | struct kref count; |
86 | struct aa_dfa *dfa; |
87 | struct { |
88 | struct aa_perms *perms; |
89 | u32 size; |
90 | }; |
91 | struct aa_str_table trans; |
92 | aa_state_t start[AA_CLASS_LAST + 1]; |
93 | }; |
94 | |
95 | extern struct aa_policydb *nullpdb; |
96 | |
97 | struct aa_policydb *aa_alloc_pdb(gfp_t gfp); |
98 | void aa_pdb_free_kref(struct kref *kref); |
99 | |
100 | /** |
101 | * aa_get_pdb - increment refcount on @pdb |
102 | * @pdb: policydb (MAYBE NULL) |
103 | * |
104 | * Returns: pointer to @pdb if @pdb is NULL will return NULL |
105 | * Requires: @pdb must be held with valid refcount when called |
106 | */ |
107 | static inline struct aa_policydb *aa_get_pdb(struct aa_policydb *pdb) |
108 | { |
109 | if (pdb) |
110 | kref_get(kref: &(pdb->count)); |
111 | |
112 | return pdb; |
113 | } |
114 | |
115 | /** |
116 | * aa_put_pdb - put a pdb refcount |
117 | * @pdb: pdb to put refcount (MAYBE NULL) |
118 | * |
119 | * Requires: if @pdb != NULL that a valid refcount be held |
120 | */ |
121 | static inline void aa_put_pdb(struct aa_policydb *pdb) |
122 | { |
123 | if (pdb) |
124 | kref_put(kref: &pdb->count, release: aa_pdb_free_kref); |
125 | } |
126 | |
127 | static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy, |
128 | aa_state_t state) |
129 | { |
130 | unsigned int index = ACCEPT_TABLE(policy->dfa)[state]; |
131 | |
132 | if (!(policy->perms)) |
133 | return &default_perms; |
134 | |
135 | return &(policy->perms[index]); |
136 | } |
137 | |
138 | |
139 | /* struct aa_data - generic data structure |
140 | * key: name for retrieving this data |
141 | * size: size of data in bytes |
142 | * data: binary data |
143 | * head: reserved for rhashtable |
144 | */ |
145 | struct aa_data { |
146 | char *key; |
147 | u32 size; |
148 | char *data; |
149 | struct rhash_head head; |
150 | }; |
151 | |
152 | /* struct aa_ruleset - data covering mediation rules |
153 | * @list: list the rule is on |
154 | * @size: the memory consumed by this ruleset |
155 | * @policy: general match rules governing policy |
156 | * @file: The set of rules governing basic file access and domain transitions |
157 | * @caps: capabilities for the profile |
158 | * @rlimits: rlimits for the profile |
159 | * @secmark_count: number of secmark entries |
160 | * @secmark: secmark label match info |
161 | */ |
162 | struct aa_ruleset { |
163 | struct list_head list; |
164 | |
165 | int size; |
166 | |
167 | /* TODO: merge policy and file */ |
168 | struct aa_policydb *policy; |
169 | struct aa_policydb *file; |
170 | struct aa_caps caps; |
171 | |
172 | struct aa_rlimit rlimits; |
173 | |
174 | int secmark_count; |
175 | struct aa_secmark *secmark; |
176 | }; |
177 | |
178 | /* struct aa_attachment - data and rules for a profiles attachment |
179 | * @list: |
180 | * @xmatch_str: human readable attachment string |
181 | * @xmatch: optional extended matching for unconfined executables names |
182 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority |
183 | * @xattr_count: number of xattrs in table |
184 | * @xattrs: table of xattrs |
185 | */ |
186 | struct aa_attachment { |
187 | const char *xmatch_str; |
188 | struct aa_policydb *xmatch; |
189 | unsigned int xmatch_len; |
190 | int xattr_count; |
191 | char **xattrs; |
192 | }; |
193 | |
194 | /* struct aa_profile - basic confinement data |
195 | * @base - base components of the profile (name, refcount, lists, lock ...) |
196 | * @label - label this profile is an extension of |
197 | * @parent: parent of profile |
198 | * @ns: namespace the profile is in |
199 | * @rename: optional profile name that this profile renamed |
200 | * |
201 | * @audit: the auditing mode of the profile |
202 | * @mode: the enforcement mode of the profile |
203 | * @path_flags: flags controlling path generation behavior |
204 | * @disconnected: what to prepend if attach_disconnected is specified |
205 | * @attach: attachment rules for the profile |
206 | * @rules: rules to be enforced |
207 | * |
208 | * @dents: dentries for the profiles file entries in apparmorfs |
209 | * @dirname: name of the profile dir in apparmorfs |
210 | * @data: hashtable for free-form policy aa_data |
211 | * |
212 | * The AppArmor profile contains the basic confinement data. Each profile |
213 | * has a name, and exists in a namespace. The @name and @exec_match are |
214 | * used to determine profile attachment against unconfined tasks. All other |
215 | * attachments are determined by profile X transition rules. |
216 | * |
217 | * Profiles have a hierarchy where hats and children profiles keep |
218 | * a reference to their parent. |
219 | * |
220 | * Profile names can not begin with a : and can not contain the \0 |
221 | * character. If a profile name begins with / it will be considered when |
222 | * determining profile attachment on "unconfined" tasks. |
223 | */ |
224 | struct aa_profile { |
225 | struct aa_policy base; |
226 | struct aa_profile __rcu *parent; |
227 | |
228 | struct aa_ns *ns; |
229 | const char *rename; |
230 | |
231 | enum audit_mode audit; |
232 | long mode; |
233 | u32 path_flags; |
234 | const char *disconnected; |
235 | |
236 | struct aa_attachment attach; |
237 | struct list_head rules; |
238 | |
239 | struct aa_loaddata *rawdata; |
240 | unsigned char *hash; |
241 | char *dirname; |
242 | struct dentry *dents[AAFS_PROF_SIZEOF]; |
243 | struct rhashtable *data; |
244 | struct aa_label label; |
245 | }; |
246 | |
247 | extern enum profile_mode aa_g_profile_mode; |
248 | |
249 | #define AA_MAY_LOAD_POLICY AA_MAY_APPEND |
250 | #define AA_MAY_REPLACE_POLICY AA_MAY_WRITE |
251 | #define AA_MAY_REMOVE_POLICY AA_MAY_DELETE |
252 | |
253 | #define profiles_ns(P) ((P)->ns) |
254 | #define name_is_shared(A, B) ((A)->hname && (A)->hname == (B)->hname) |
255 | |
256 | struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp); |
257 | struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy, |
258 | gfp_t gfp); |
259 | struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, |
260 | gfp_t gfp); |
261 | struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat, |
262 | const char *base, gfp_t gfp); |
263 | void aa_free_profile(struct aa_profile *profile); |
264 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); |
265 | struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, |
266 | size_t n); |
267 | struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); |
268 | struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, |
269 | const char *fqname, size_t n); |
270 | |
271 | ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label, |
272 | u32 mask, struct aa_loaddata *udata); |
273 | ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label, |
274 | char *name, size_t size); |
275 | void __aa_profile_list_release(struct list_head *head); |
276 | |
277 | #define profile_unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) |
278 | |
279 | /** |
280 | * aa_get_newest_profile - simple wrapper fn to wrap the label version |
281 | * @p: profile (NOT NULL) |
282 | * |
283 | * Returns refcount to newest version of the profile (maybe @p) |
284 | * |
285 | * Requires: @p must be held with a valid refcount |
286 | */ |
287 | static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) |
288 | { |
289 | return labels_profile(aa_get_newest_label(&p->label)); |
290 | } |
291 | |
292 | static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules, |
293 | unsigned char class) |
294 | { |
295 | if (class <= AA_CLASS_LAST) |
296 | return rules->policy->start[class]; |
297 | else |
298 | return aa_dfa_match_len(dfa: rules->policy->dfa, |
299 | start: rules->policy->start[0], str: &class, len: 1); |
300 | } |
301 | |
302 | static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF) |
303 | { |
304 | aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET); |
305 | __be16 be_af = cpu_to_be16(AF); |
306 | |
307 | if (!state) |
308 | return DFA_NOMATCH; |
309 | return aa_dfa_match_len(dfa: rules->policy->dfa, start: state, str: (char *) &be_af, len: 2); |
310 | } |
311 | |
312 | static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head, |
313 | unsigned char class) |
314 | { |
315 | struct aa_ruleset *rule; |
316 | |
317 | /* TODO: change to list walk */ |
318 | rule = list_first_entry(head, typeof(*rule), list); |
319 | return RULE_MEDIATES(rules: rule, class); |
320 | } |
321 | |
322 | /** |
323 | * aa_get_profile - increment refcount on profile @p |
324 | * @p: profile (MAYBE NULL) |
325 | * |
326 | * Returns: pointer to @p if @p is NULL will return NULL |
327 | * Requires: @p must be held with valid refcount when called |
328 | */ |
329 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) |
330 | { |
331 | if (p) |
332 | kref_get(kref: &(p->label.count)); |
333 | |
334 | return p; |
335 | } |
336 | |
337 | /** |
338 | * aa_get_profile_not0 - increment refcount on profile @p found via lookup |
339 | * @p: profile (MAYBE NULL) |
340 | * |
341 | * Returns: pointer to @p if @p is NULL will return NULL |
342 | * Requires: @p must be held with valid refcount when called |
343 | */ |
344 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) |
345 | { |
346 | if (p && kref_get_unless_zero(kref: &p->label.count)) |
347 | return p; |
348 | |
349 | return NULL; |
350 | } |
351 | |
352 | /** |
353 | * aa_get_profile_rcu - increment a refcount profile that can be replaced |
354 | * @p: pointer to profile that can be replaced (NOT NULL) |
355 | * |
356 | * Returns: pointer to a refcounted profile. |
357 | * else NULL if no profile |
358 | */ |
359 | static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) |
360 | { |
361 | struct aa_profile *c; |
362 | |
363 | rcu_read_lock(); |
364 | do { |
365 | c = rcu_dereference(*p); |
366 | } while (c && !kref_get_unless_zero(kref: &c->label.count)); |
367 | rcu_read_unlock(); |
368 | |
369 | return c; |
370 | } |
371 | |
372 | /** |
373 | * aa_put_profile - decrement refcount on profile @p |
374 | * @p: profile (MAYBE NULL) |
375 | */ |
376 | static inline void aa_put_profile(struct aa_profile *p) |
377 | { |
378 | if (p) |
379 | kref_put(kref: &p->label.count, release: aa_label_kref); |
380 | } |
381 | |
382 | static inline int AUDIT_MODE(struct aa_profile *profile) |
383 | { |
384 | if (aa_g_audit != AUDIT_NORMAL) |
385 | return aa_g_audit; |
386 | |
387 | return profile->audit; |
388 | } |
389 | |
390 | bool aa_policy_view_capable(const struct cred *subj_cred, |
391 | struct aa_label *label, struct aa_ns *ns); |
392 | bool aa_policy_admin_capable(const struct cred *subj_cred, |
393 | struct aa_label *label, struct aa_ns *ns); |
394 | int aa_may_manage_policy(const struct cred *subj_cred, |
395 | struct aa_label *label, struct aa_ns *ns, |
396 | u32 mask); |
397 | bool aa_current_policy_view_capable(struct aa_ns *ns); |
398 | bool aa_current_policy_admin_capable(struct aa_ns *ns); |
399 | |
400 | #endif /* __AA_POLICY_H */ |
401 | |