1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2 | /* |
3 | * AppArmor security module |
4 | * |
5 | * This file contains AppArmor lib definitions |
6 | * |
7 | * 2017 Canonical Ltd. |
8 | */ |
9 | |
10 | #ifndef __AA_LIB_H |
11 | #define __AA_LIB_H |
12 | |
13 | #include <linux/slab.h> |
14 | #include <linux/fs.h> |
15 | #include <linux/lsm_hooks.h> |
16 | |
17 | #include "match.h" |
18 | |
19 | extern struct aa_dfa *stacksplitdfa; |
20 | |
21 | /* |
22 | * DEBUG remains global (no per profile flag) since it is mostly used in sysctl |
23 | * which is not related to profile accesses. |
24 | */ |
25 | |
26 | #define DEBUG_ON (aa_g_debug) |
27 | /* |
28 | * split individual debug cases out in preparation for finer grained |
29 | * debug controls in the future. |
30 | */ |
31 | #define AA_DEBUG_LABEL DEBUG_ON |
32 | #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) |
33 | #define AA_DEBUG(fmt, args...) \ |
34 | do { \ |
35 | if (DEBUG_ON) \ |
36 | pr_debug_ratelimited("AppArmor: " fmt, ##args); \ |
37 | } while (0) |
38 | |
39 | #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) |
40 | |
41 | #define AA_BUG(X, args...) \ |
42 | do { \ |
43 | _Pragma("GCC diagnostic ignored \"-Wformat-zero-length\""); \ |
44 | AA_BUG_FMT((X), "" args); \ |
45 | _Pragma("GCC diagnostic warning \"-Wformat-zero-length\""); \ |
46 | } while (0) |
47 | #ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS |
48 | #define AA_BUG_FMT(X, fmt, args...) \ |
49 | WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) |
50 | #else |
51 | #define AA_BUG_FMT(X, fmt, args...) no_printk(fmt, ##args) |
52 | #endif |
53 | |
54 | #define AA_ERROR(fmt, args...) \ |
55 | pr_err_ratelimited("AppArmor: " fmt, ##args) |
56 | |
57 | /* Flag indicating whether initialization completed */ |
58 | extern int apparmor_initialized; |
59 | |
60 | /* fn's in lib */ |
61 | const char *skipn_spaces(const char *str, size_t n); |
62 | char *aa_split_fqname(char *args, char **ns_name); |
63 | const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, |
64 | size_t *ns_len); |
65 | void aa_info_message(const char *str); |
66 | |
67 | /* Security blob offsets */ |
68 | extern struct lsm_blob_sizes apparmor_blob_sizes; |
69 | |
70 | /** |
71 | * aa_strneq - compare null terminated @str to a non null terminated substring |
72 | * @str: a null terminated string |
73 | * @sub: a substring, not necessarily null terminated |
74 | * @len: length of @sub to compare |
75 | * |
76 | * The @str string must be full consumed for this to be considered a match |
77 | */ |
78 | static inline bool aa_strneq(const char *str, const char *sub, int len) |
79 | { |
80 | return !strncmp(str, sub, len) && !str[len]; |
81 | } |
82 | |
83 | /** |
84 | * aa_dfa_null_transition - step to next state after null character |
85 | * @dfa: the dfa to match against |
86 | * @start: the state of the dfa to start matching in |
87 | * |
88 | * aa_dfa_null_transition transitions to the next state after a null |
89 | * character which is not used in standard matching and is only |
90 | * used to separate pairs. |
91 | */ |
92 | static inline aa_state_t aa_dfa_null_transition(struct aa_dfa *dfa, |
93 | aa_state_t start) |
94 | { |
95 | /* the null transition only needs the string's null terminator byte */ |
96 | return aa_dfa_next(dfa, state: start, c: 0); |
97 | } |
98 | |
99 | static inline bool path_mediated_fs(struct dentry *dentry) |
100 | { |
101 | return !(dentry->d_sb->s_flags & SB_NOUSER); |
102 | } |
103 | |
104 | struct aa_str_table { |
105 | int size; |
106 | char **table; |
107 | }; |
108 | |
109 | void aa_free_str_table(struct aa_str_table *table); |
110 | |
111 | struct counted_str { |
112 | struct kref count; |
113 | char name[]; |
114 | }; |
115 | |
116 | #define str_to_counted(str) \ |
117 | ((struct counted_str *)(str - offsetof(struct counted_str, name))) |
118 | |
119 | #define __counted /* atm just a notation */ |
120 | |
121 | void aa_str_kref(struct kref *kref); |
122 | char *aa_str_alloc(int size, gfp_t gfp); |
123 | |
124 | |
125 | static inline __counted char *aa_get_str(__counted char *str) |
126 | { |
127 | if (str) |
128 | kref_get(kref: &(str_to_counted(str)->count)); |
129 | |
130 | return str; |
131 | } |
132 | |
133 | static inline void aa_put_str(__counted char *str) |
134 | { |
135 | if (str) |
136 | kref_put(kref: &str_to_counted(str)->count, release: aa_str_kref); |
137 | } |
138 | |
139 | |
140 | /* struct aa_policy - common part of both namespaces and profiles |
141 | * @name: name of the object |
142 | * @hname - The hierarchical name |
143 | * @list: list policy object is on |
144 | * @profiles: head of the profiles list contained in the object |
145 | */ |
146 | struct aa_policy { |
147 | const char *name; |
148 | __counted char *hname; |
149 | struct list_head list; |
150 | struct list_head profiles; |
151 | }; |
152 | |
153 | /** |
154 | * basename - find the last component of an hname |
155 | * @name: hname to find the base profile name component of (NOT NULL) |
156 | * |
157 | * Returns: the tail (base profile name) name component of an hname |
158 | */ |
159 | static inline const char *basename(const char *hname) |
160 | { |
161 | char *split; |
162 | |
163 | hname = strim((char *)hname); |
164 | for (split = strstr(hname, "//" ); split; split = strstr(hname, "//" )) |
165 | hname = split + 2; |
166 | |
167 | return hname; |
168 | } |
169 | |
170 | /** |
171 | * __policy_find - find a policy by @name on a policy list |
172 | * @head: list to search (NOT NULL) |
173 | * @name: name to search for (NOT NULL) |
174 | * |
175 | * Requires: rcu_read_lock be held |
176 | * |
177 | * Returns: unrefcounted policy that match @name or NULL if not found |
178 | */ |
179 | static inline struct aa_policy *__policy_find(struct list_head *head, |
180 | const char *name) |
181 | { |
182 | struct aa_policy *policy; |
183 | |
184 | list_for_each_entry_rcu(policy, head, list) { |
185 | if (!strcmp(policy->name, name)) |
186 | return policy; |
187 | } |
188 | return NULL; |
189 | } |
190 | |
191 | /** |
192 | * __policy_strn_find - find a policy that's name matches @len chars of @str |
193 | * @head: list to search (NOT NULL) |
194 | * @str: string to search for (NOT NULL) |
195 | * @len: length of match required |
196 | * |
197 | * Requires: rcu_read_lock be held |
198 | * |
199 | * Returns: unrefcounted policy that match @str or NULL if not found |
200 | * |
201 | * if @len == strlen(@strlen) then this is equiv to __policy_find |
202 | * other wise it allows searching for policy by a partial match of name |
203 | */ |
204 | static inline struct aa_policy *__policy_strn_find(struct list_head *head, |
205 | const char *str, int len) |
206 | { |
207 | struct aa_policy *policy; |
208 | |
209 | list_for_each_entry_rcu(policy, head, list) { |
210 | if (aa_strneq(str: policy->name, sub: str, len)) |
211 | return policy; |
212 | } |
213 | |
214 | return NULL; |
215 | } |
216 | |
217 | bool aa_policy_init(struct aa_policy *policy, const char *prefix, |
218 | const char *name, gfp_t gfp); |
219 | void aa_policy_destroy(struct aa_policy *policy); |
220 | |
221 | |
222 | /* |
223 | * fn_label_build - abstract out the build of a label transition |
224 | * @L: label the transition is being computed for |
225 | * @P: profile parameter derived from L by this macro, can be passed to FN |
226 | * @GFP: memory allocation type to use |
227 | * @FN: fn to call for each profile transition. @P is set to the profile |
228 | * |
229 | * Returns: new label on success |
230 | * ERR_PTR if build @FN fails |
231 | * NULL if label_build fails due to low memory conditions |
232 | * |
233 | * @FN must return a label or ERR_PTR on failure. NULL is not allowed |
234 | */ |
235 | #define fn_label_build(L, P, GFP, FN) \ |
236 | ({ \ |
237 | __label__ __do_cleanup, __done; \ |
238 | struct aa_label *__new_; \ |
239 | \ |
240 | if ((L)->size > 1) { \ |
241 | /* TODO: add cache of transitions already done */ \ |
242 | struct label_it __i; \ |
243 | int __j, __k, __count; \ |
244 | DEFINE_VEC(label, __lvec); \ |
245 | DEFINE_VEC(profile, __pvec); \ |
246 | if (vec_setup(label, __lvec, (L)->size, (GFP))) { \ |
247 | __new_ = NULL; \ |
248 | goto __done; \ |
249 | } \ |
250 | __j = 0; \ |
251 | label_for_each(__i, (L), (P)) { \ |
252 | __new_ = (FN); \ |
253 | AA_BUG(!__new_); \ |
254 | if (IS_ERR(__new_)) \ |
255 | goto __do_cleanup; \ |
256 | __lvec[__j++] = __new_; \ |
257 | } \ |
258 | for (__j = __count = 0; __j < (L)->size; __j++) \ |
259 | __count += __lvec[__j]->size; \ |
260 | if (!vec_setup(profile, __pvec, __count, (GFP))) { \ |
261 | for (__j = __k = 0; __j < (L)->size; __j++) { \ |
262 | label_for_each(__i, __lvec[__j], (P)) \ |
263 | __pvec[__k++] = aa_get_profile(P); \ |
264 | } \ |
265 | __count -= aa_vec_unique(__pvec, __count, 0); \ |
266 | if (__count > 1) { \ |
267 | __new_ = aa_vec_find_or_create_label(__pvec,\ |
268 | __count, (GFP)); \ |
269 | /* only fails if out of Mem */ \ |
270 | if (!__new_) \ |
271 | __new_ = NULL; \ |
272 | } else \ |
273 | __new_ = aa_get_label(&__pvec[0]->label); \ |
274 | vec_cleanup(profile, __pvec, __count); \ |
275 | } else \ |
276 | __new_ = NULL; \ |
277 | __do_cleanup: \ |
278 | vec_cleanup(label, __lvec, (L)->size); \ |
279 | } else { \ |
280 | (P) = labels_profile(L); \ |
281 | __new_ = (FN); \ |
282 | } \ |
283 | __done: \ |
284 | if (!__new_) \ |
285 | AA_DEBUG("label build failed\n"); \ |
286 | (__new_); \ |
287 | }) |
288 | |
289 | |
290 | #define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \ |
291 | ({ \ |
292 | struct aa_label *__new; \ |
293 | if ((P)->ns != (NS)) \ |
294 | __new = (OTHER_FN); \ |
295 | else \ |
296 | __new = (NS_FN); \ |
297 | (__new); \ |
298 | }) |
299 | |
300 | #define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \ |
301 | ({ \ |
302 | fn_label_build((L), (P), (GFP), \ |
303 | __fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \ |
304 | }) |
305 | |
306 | #endif /* __AA_LIB_H */ |
307 | |