]>
Commit | Line | Data |
---|---|---|
2dfbb274 | 1 | From 05bf1eb7276886a3eda0588a8e012b558b693e96 Mon Sep 17 00:00:00 2001 |
76514441 | 2 | From: John Johansen <john.johansen@canonical.com> |
0fa3db16 | 3 | Date: Thu, 22 Jul 2010 02:32:02 -0700 |
2dfbb274 AM |
4 | Subject: [PATCH] UBUNTU: SAUCE: AppArmor: Add profile introspection file to |
5 | interface | |
1e8b8f9b AM |
6 | |
7 | Add the dynamic profiles file to the interace, to allow load policy | |
8 | introspection. | |
9 | ||
10 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
11 | Acked-by: Kees Cook <kees@ubuntu.com> | |
1e8b8f9b | 12 | --- |
2dfbb274 AM |
13 | security/apparmor/Kconfig | 9 ++ |
14 | security/apparmor/apparmorfs.c | 231 ++++++++++++++++++++++++++++++++++++++++ | |
15 | 2 files changed, 240 insertions(+), 0 deletions(-) | |
1e8b8f9b | 16 | |
2dfbb274 AM |
17 | diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig |
18 | index 9b9013b..51ebf96 100644 | |
19 | --- a/security/apparmor/Kconfig | |
20 | +++ b/security/apparmor/Kconfig | |
21 | @@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE | |
22 | boot. | |
23 | ||
24 | If you are unsure how to answer this question, answer 1. | |
25 | + | |
26 | +config SECURITY_APPARMOR_COMPAT_24 | |
27 | + bool "Enable AppArmor 2.4 compatability" | |
28 | + depends on SECURITY_APPARMOR | |
29 | + default y | |
30 | + help | |
31 | + This option enables compatability with AppArmor 2.4. It is | |
32 | + recommended if compatability with older versions of AppArmor | |
33 | + is desired. | |
1e8b8f9b | 34 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c |
2dfbb274 | 35 | index 16c15ec..42b7c9f 100644 |
1e8b8f9b AM |
36 | --- a/security/apparmor/apparmorfs.c |
37 | +++ b/security/apparmor/apparmorfs.c | |
2dfbb274 | 38 | @@ -182,6 +182,234 @@ const struct file_operations aa_fs_seq_file_ops = { |
1e8b8f9b AM |
39 | .release = single_release, |
40 | }; | |
41 | ||
2dfbb274 | 42 | +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 |
1e8b8f9b AM |
43 | +/** |
44 | + * __next_namespace - find the next namespace to list | |
45 | + * @root: root namespace to stop search at (NOT NULL) | |
46 | + * @ns: current ns position (NOT NULL) | |
47 | + * | |
48 | + * Find the next namespace from @ns under @root and handle all locking needed | |
49 | + * while switching current namespace. | |
50 | + * | |
51 | + * Returns: next namespace or NULL if at last namespace under @root | |
52 | + * NOTE: will not unlock root->lock | |
53 | + */ | |
54 | +static struct aa_namespace *__next_namespace(struct aa_namespace *root, | |
55 | + struct aa_namespace *ns) | |
56 | +{ | |
57 | + struct aa_namespace *parent; | |
58 | + | |
59 | + /* is next namespace a child */ | |
60 | + if (!list_empty(&ns->sub_ns)) { | |
61 | + struct aa_namespace *next; | |
62 | + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); | |
63 | + read_lock(&next->lock); | |
64 | + return next; | |
65 | + } | |
66 | + | |
67 | + /* check if the next ns is a sibling, parent, gp, .. */ | |
68 | + parent = ns->parent; | |
69 | + while (parent) { | |
70 | + read_unlock(&ns->lock); | |
71 | + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { | |
72 | + read_lock(&ns->lock); | |
73 | + return ns; | |
74 | + } | |
75 | + if (parent == root) | |
76 | + return NULL; | |
77 | + ns = parent; | |
78 | + parent = parent->parent; | |
79 | + } | |
80 | + | |
81 | + return NULL; | |
82 | +} | |
83 | + | |
84 | +/** | |
85 | + * __first_profile - find the first profile in a namespace | |
86 | + * @root: namespace that is root of profiles being displayed (NOT NULL) | |
87 | + * @ns: namespace to start in (NOT NULL) | |
88 | + * | |
89 | + * Returns: unrefcounted profile or NULL if no profile | |
90 | + */ | |
91 | +static struct aa_profile *__first_profile(struct aa_namespace *root, | |
92 | + struct aa_namespace *ns) | |
93 | +{ | |
94 | + for ( ; ns; ns = __next_namespace(root, ns)) { | |
95 | + if (!list_empty(&ns->base.profiles)) | |
96 | + return list_first_entry(&ns->base.profiles, | |
97 | + struct aa_profile, base.list); | |
98 | + } | |
99 | + return NULL; | |
100 | +} | |
101 | + | |
102 | +/** | |
103 | + * __next_profile - step to the next profile in a profile tree | |
104 | + * @profile: current profile in tree (NOT NULL) | |
105 | + * | |
106 | + * Perform a depth first taversal on the profile tree in a namespace | |
107 | + * | |
108 | + * Returns: next profile or NULL if done | |
109 | + * Requires: profile->ns.lock to be held | |
110 | + */ | |
111 | +static struct aa_profile *__next_profile(struct aa_profile *p) | |
112 | +{ | |
113 | + struct aa_profile *parent; | |
114 | + struct aa_namespace *ns = p->ns; | |
115 | + | |
116 | + /* is next profile a child */ | |
117 | + if (!list_empty(&p->base.profiles)) | |
118 | + return list_first_entry(&p->base.profiles, typeof(*p), | |
119 | + base.list); | |
120 | + | |
121 | + /* is next profile a sibling, parent sibling, gp, subling, .. */ | |
122 | + parent = p->parent; | |
123 | + while (parent) { | |
124 | + list_for_each_entry_continue(p, &parent->base.profiles, | |
125 | + base.list) | |
126 | + return p; | |
127 | + p = parent; | |
128 | + parent = parent->parent; | |
129 | + } | |
130 | + | |
131 | + /* is next another profile in the namespace */ | |
132 | + list_for_each_entry_continue(p, &ns->base.profiles, base.list) | |
133 | + return p; | |
134 | + | |
135 | + return NULL; | |
136 | +} | |
137 | + | |
138 | +/** | |
139 | + * next_profile - step to the next profile in where ever it may be | |
140 | + * @root: root namespace (NOT NULL) | |
141 | + * @profile: current profile (NOT NULL) | |
142 | + * | |
143 | + * Returns: next profile or NULL if there isn't one | |
144 | + */ | |
145 | +static struct aa_profile *next_profile(struct aa_namespace *root, | |
146 | + struct aa_profile *profile) | |
147 | +{ | |
148 | + struct aa_profile *next = __next_profile(profile); | |
149 | + if (next) | |
150 | + return next; | |
151 | + | |
152 | + /* finished all profiles in namespace move to next namespace */ | |
153 | + return __first_profile(root, __next_namespace(root, profile->ns)); | |
154 | +} | |
155 | + | |
156 | +/** | |
157 | + * p_start - start a depth first traversal of profile tree | |
158 | + * @f: seq_file to fill | |
159 | + * @pos: current position | |
160 | + * | |
161 | + * Returns: first profile under current namespace or NULL if none found | |
162 | + * | |
163 | + * acquires first ns->lock | |
164 | + */ | |
165 | +static void *p_start(struct seq_file *f, loff_t *pos) | |
166 | + __acquires(root->lock) | |
167 | +{ | |
168 | + struct aa_profile *profile = NULL; | |
169 | + struct aa_namespace *root = aa_current_profile()->ns; | |
170 | + loff_t l = *pos; | |
171 | + f->private = aa_get_namespace(root); | |
172 | + | |
173 | + | |
174 | + /* find the first profile */ | |
175 | + read_lock(&root->lock); | |
176 | + profile = __first_profile(root, root); | |
177 | + | |
178 | + /* skip to position */ | |
179 | + for (; profile && l > 0; l--) | |
180 | + profile = next_profile(root, profile); | |
181 | + | |
182 | + return profile; | |
183 | +} | |
184 | + | |
185 | +/** | |
186 | + * p_next - read the next profile entry | |
187 | + * @f: seq_file to fill | |
188 | + * @p: profile previously returned | |
189 | + * @pos: current position | |
190 | + * | |
191 | + * Returns: next profile after @p or NULL if none | |
192 | + * | |
193 | + * may acquire/release locks in namespace tree as necessary | |
194 | + */ | |
195 | +static void *p_next(struct seq_file *f, void *p, loff_t *pos) | |
196 | +{ | |
197 | + struct aa_profile *profile = p; | |
198 | + struct aa_namespace *root = f->private; | |
199 | + (*pos)++; | |
200 | + | |
201 | + return next_profile(root, profile); | |
202 | +} | |
203 | + | |
204 | +/** | |
205 | + * p_stop - stop depth first traversal | |
206 | + * @f: seq_file we are filling | |
207 | + * @p: the last profile writen | |
208 | + * | |
209 | + * Release all locking done by p_start/p_next on namespace tree | |
210 | + */ | |
211 | +static void p_stop(struct seq_file *f, void *p) | |
212 | + __releases(root->lock) | |
213 | +{ | |
214 | + struct aa_profile *profile = p; | |
215 | + struct aa_namespace *root = f->private, *ns; | |
216 | + | |
217 | + if (profile) { | |
218 | + for (ns = profile->ns; ns && ns != root; ns = ns->parent) | |
219 | + read_unlock(&ns->lock); | |
220 | + } | |
221 | + read_unlock(&root->lock); | |
222 | + aa_put_namespace(root); | |
223 | +} | |
224 | + | |
225 | +/** | |
226 | + * seq_show_profile - show a profile entry | |
227 | + * @f: seq_file to file | |
228 | + * @p: current position (profile) (NOT NULL) | |
229 | + * | |
230 | + * Returns: error on failure | |
231 | + */ | |
232 | +static int seq_show_profile(struct seq_file *f, void *p) | |
233 | +{ | |
234 | + struct aa_profile *profile = (struct aa_profile *)p; | |
235 | + struct aa_namespace *root = f->private; | |
236 | + | |
237 | + if (profile->ns != root) | |
238 | + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); | |
239 | + seq_printf(f, "%s (%s)\n", profile->base.hname, | |
240 | + COMPLAIN_MODE(profile) ? "complain" : "enforce"); | |
241 | + | |
242 | + return 0; | |
243 | +} | |
244 | + | |
245 | +static const struct seq_operations aa_fs_profiles_op = { | |
246 | + .start = p_start, | |
247 | + .next = p_next, | |
248 | + .stop = p_stop, | |
249 | + .show = seq_show_profile, | |
250 | +}; | |
251 | + | |
252 | +static int profiles_open(struct inode *inode, struct file *file) | |
253 | +{ | |
254 | + return seq_open(file, &aa_fs_profiles_op); | |
255 | +} | |
256 | + | |
257 | +static int profiles_release(struct inode *inode, struct file *file) | |
258 | +{ | |
259 | + return seq_release(inode, file); | |
260 | +} | |
261 | + | |
262 | +const struct file_operations aa_fs_profiles_fops = { | |
263 | + .open = profiles_open, | |
264 | + .read = seq_read, | |
265 | + .llseek = seq_lseek, | |
266 | + .release = profiles_release, | |
267 | +}; | |
2dfbb274 | 268 | +#endif /* CONFIG_SECURITY_APPARMOR_COMPAT_24 */ |
1e8b8f9b AM |
269 | + |
270 | /** Base file system setup **/ | |
271 | ||
272 | static struct aa_fs_entry aa_fs_entry_file[] = { | |
2dfbb274 | 273 | @@ -210,6 +438,9 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { |
1e8b8f9b AM |
274 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), |
275 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), | |
276 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), | |
2dfbb274 | 277 | +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 |
1e8b8f9b | 278 | + AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), |
2dfbb274 | 279 | +#endif |
1e8b8f9b AM |
280 | AA_FS_DIR("features", aa_fs_entry_features), |
281 | { } | |
282 | }; | |
0fa3db16 | 283 | -- |
2dfbb274 | 284 | 1.7.6.5 |
1e8b8f9b | 285 | |
2dfbb274 | 286 | From 4facdf9db37c12ff655c91270d9030e2ed805ca2 Mon Sep 17 00:00:00 2001 |
0fa3db16 AM |
287 | From: John Johansen <john.johansen@canonical.com> |
288 | Date: Mon, 4 Oct 2010 15:03:36 -0700 | |
2dfbb274 | 289 | Subject: [PATCH] UBUNTU: SAUCE: AppArmor: basic networking rules |
1e8b8f9b AM |
290 | |
291 | Base support for network mediation. | |
76514441 AM |
292 | |
293 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
294 | --- | |
0fa3db16 AM |
295 | security/apparmor/.gitignore | 2 +- |
296 | security/apparmor/Makefile | 42 +++++++++- | |
297 | security/apparmor/apparmorfs.c | 1 + | |
298 | security/apparmor/include/audit.h | 4 + | |
299 | security/apparmor/include/net.h | 44 ++++++++++ | |
300 | security/apparmor/include/policy.h | 3 + | |
301 | security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ | |
302 | security/apparmor/net.c | 162 ++++++++++++++++++++++++++++++++++++ | |
303 | security/apparmor/policy.c | 1 + | |
304 | security/apparmor/policy_unpack.c | 46 ++++++++++ | |
305 | 10 files changed, 414 insertions(+), 3 deletions(-) | |
306 | create mode 100644 security/apparmor/include/net.h | |
307 | create mode 100644 security/apparmor/net.c | |
76514441 | 308 | |
fc63ffa9 | 309 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
1e8b8f9b | 310 | index 806bd19..19daa85 100644 |
fc63ffa9 AM |
311 | --- a/security/apparmor/Makefile |
312 | +++ b/security/apparmor/Makefile | |
948a1326 | 313 | @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
ceaf2cfb | 314 | |
fc63ffa9 AM |
315 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
316 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | |
317 | - resource.o sid.o file.o | |
318 | + resource.o sid.o file.o net.o | |
ceaf2cfb | 319 | |
948a1326 | 320 | -clean-files := capability_names.h rlim_names.h |
1e8b8f9b | 321 | +clean-files := capability_names.h rlim_names.h net_names.h |
ceaf2cfb | 322 | |
ceaf2cfb | 323 | |
948a1326 | 324 | # Build a lower case string table of capability names |
1e8b8f9b AM |
325 | @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ |
326 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ | |
948a1326 JR |
327 | echo "};" >> $@ |
328 | ||
1e8b8f9b | 329 | +# Build a lower case string table of address family names |
948a1326 | 330 | +# Transform lines from |
1e8b8f9b AM |
331 | +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ |
332 | +# #define AF_INET 2 /* Internet IP Protocol */ | |
333 | +# to | |
334 | +# [1] = "local", | |
335 | +# [2] = "inet", | |
336 | +# | |
337 | +# and build the securityfs entries for the mapping. | |
338 | +# Transforms lines from | |
339 | +# #define AF_INET 2 /* Internet IP Protocol */ | |
948a1326 | 340 | +# to |
1e8b8f9b | 341 | +# #define AA_FS_AF_MASK "local inet" |
ceaf2cfb | 342 | +quiet_cmd_make-af = GEN $@ |
948a1326 | 343 | +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ |
1e8b8f9b AM |
344 | + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ |
345 | + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | |
346 | + echo "};" >> $@ ;\ | |
347 | + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ | |
348 | + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ | |
349 | + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | |
350 | + | |
351 | +# Build a lower case string table of sock type names | |
352 | +# Transform lines from | |
353 | +# SOCK_STREAM = 1, | |
354 | +# to | |
355 | +# [1] = "stream", | |
356 | +quiet_cmd_make-sock = GEN $@ | |
357 | +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ | |
358 | + sed $^ >>$@ -r -n \ | |
359 | + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | |
948a1326 | 360 | + echo "};" >> $@ |
1e8b8f9b AM |
361 | |
362 | # Build a lower case string table of rlimit names. | |
363 | # Transforms lines from | |
364 | @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ | |
365 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | |
366 | ||
fc63ffa9 | 367 | $(obj)/capability.o : $(obj)/capability_names.h |
1e8b8f9b | 368 | +$(obj)/net.o : $(obj)/net_names.h |
fc63ffa9 | 369 | $(obj)/resource.o : $(obj)/rlim_names.h |
1e8b8f9b AM |
370 | $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ |
371 | $(src)/Makefile | |
372 | @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ | |
373 | $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \ | |
374 | $(src)/Makefile | |
948a1326 | 375 | $(call cmd,make-rlim) |
1e8b8f9b AM |
376 | +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ |
377 | + $(srctree)/include/linux/net.h \ | |
378 | + $(src)/Makefile | |
948a1326 | 379 | + $(call cmd,make-af) |
1e8b8f9b AM |
380 | + $(call cmd,make-sock) |
381 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
2dfbb274 | 382 | index 42b7c9f..114fb23 100644 |
1e8b8f9b AM |
383 | --- a/security/apparmor/apparmorfs.c |
384 | +++ b/security/apparmor/apparmorfs.c | |
2dfbb274 | 385 | @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { |
1e8b8f9b AM |
386 | static struct aa_fs_entry aa_fs_entry_features[] = { |
387 | AA_FS_DIR("domain", aa_fs_entry_domain), | |
388 | AA_FS_DIR("file", aa_fs_entry_file), | |
389 | + AA_FS_DIR("network", aa_fs_entry_network), | |
390 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
391 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
392 | { } | |
393 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h | |
2dfbb274 | 394 | index 4b7e189..17734f9 100644 |
1e8b8f9b AM |
395 | --- a/security/apparmor/include/audit.h |
396 | +++ b/security/apparmor/include/audit.h | |
2dfbb274 | 397 | @@ -127,6 +127,10 @@ struct apparmor_audit_data { |
1e8b8f9b AM |
398 | u32 denied; |
399 | uid_t ouid; | |
400 | } fs; | |
401 | + struct { | |
402 | + int type, protocol; | |
403 | + struct sock *sk; | |
404 | + } net; | |
405 | }; | |
406 | }; | |
407 | ||
fc63ffa9 | 408 | diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h |
ceaf2cfb | 409 | new file mode 100644 |
1e8b8f9b | 410 | index 0000000..cb8a121 |
ceaf2cfb | 411 | --- /dev/null |
fc63ffa9 | 412 | +++ b/security/apparmor/include/net.h |
1e8b8f9b | 413 | @@ -0,0 +1,44 @@ |
9474138d AM |
414 | +/* |
415 | + * AppArmor security module | |
416 | + * | |
fc63ffa9 | 417 | + * This file contains AppArmor network mediation definitions. |
9474138d AM |
418 | + * |
419 | + * Copyright (C) 1998-2008 Novell/SUSE | |
1e8b8f9b | 420 | + * Copyright 2009-2012 Canonical Ltd. |
9474138d AM |
421 | + * |
422 | + * This program is free software; you can redistribute it and/or | |
423 | + * modify it under the terms of the GNU General Public License as | |
424 | + * published by the Free Software Foundation, version 2 of the | |
425 | + * License. | |
426 | + */ | |
427 | + | |
fc63ffa9 AM |
428 | +#ifndef __AA_NET_H |
429 | +#define __AA_NET_H | |
2380c486 | 430 | + |
fc63ffa9 | 431 | +#include <net/sock.h> |
9474138d | 432 | + |
1e8b8f9b AM |
433 | +#include "apparmorfs.h" |
434 | + | |
fc63ffa9 AM |
435 | +/* struct aa_net - network confinement data |
436 | + * @allowed: basic network families permissions | |
437 | + * @audit_network: which network permissions to force audit | |
438 | + * @quiet_network: which network permissions to quiet rejects | |
439 | + */ | |
440 | +struct aa_net { | |
441 | + u16 allow[AF_MAX]; | |
442 | + u16 audit[AF_MAX]; | |
443 | + u16 quiet[AF_MAX]; | |
444 | +}; | |
ceaf2cfb | 445 | + |
1e8b8f9b AM |
446 | +extern struct aa_fs_entry aa_fs_entry_network[]; |
447 | + | |
fc63ffa9 AM |
448 | +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, |
449 | + int type, int protocol, struct sock *sk); | |
450 | +extern int aa_revalidate_sk(int op, struct sock *sk); | |
ceaf2cfb | 451 | + |
fc63ffa9 AM |
452 | +static inline void aa_free_net_rules(struct aa_net *new) |
453 | +{ | |
454 | + /* NOP */ | |
ceaf2cfb AM |
455 | +} |
456 | + | |
fc63ffa9 AM |
457 | +#endif /* __AA_NET_H */ |
458 | diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h | |
1e8b8f9b | 459 | index bda4569..eb13a73 100644 |
fc63ffa9 AM |
460 | --- a/security/apparmor/include/policy.h |
461 | +++ b/security/apparmor/include/policy.h | |
462 | @@ -27,6 +27,7 @@ | |
463 | #include "capability.h" | |
464 | #include "domain.h" | |
465 | #include "file.h" | |
466 | +#include "net.h" | |
467 | #include "resource.h" | |
468 | ||
1e8b8f9b AM |
469 | extern const char *const profile_mode_names[]; |
470 | @@ -157,6 +158,7 @@ struct aa_policydb { | |
471 | * @policy: general match rules governing policy | |
fc63ffa9 AM |
472 | * @file: The set of rules governing basic file access and domain transitions |
473 | * @caps: capabilities for the profile | |
474 | + * @net: network controls for the profile | |
475 | * @rlimits: rlimits for the profile | |
476 | * | |
477 | * The AppArmor profile contains the basic confinement data. Each profile | |
1e8b8f9b AM |
478 | @@ -194,6 +196,7 @@ struct aa_profile { |
479 | struct aa_policydb policy; | |
fc63ffa9 AM |
480 | struct aa_file_rules file; |
481 | struct aa_caps caps; | |
482 | + struct aa_net net; | |
483 | struct aa_rlimit rlimits; | |
484 | }; | |
485 | ||
486 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
2dfbb274 | 487 | index 8ea39aa..f628734 100644 |
fc63ffa9 AM |
488 | --- a/security/apparmor/lsm.c |
489 | +++ b/security/apparmor/lsm.c | |
948a1326 | 490 | @@ -32,6 +32,7 @@ |
fc63ffa9 AM |
491 | #include "include/context.h" |
492 | #include "include/file.h" | |
493 | #include "include/ipc.h" | |
494 | +#include "include/net.h" | |
495 | #include "include/path.h" | |
496 | #include "include/policy.h" | |
497 | #include "include/procattr.h" | |
2dfbb274 | 498 | @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, |
fc63ffa9 AM |
499 | return error; |
500 | } | |
501 | ||
502 | +static int apparmor_socket_create(int family, int type, int protocol, int kern) | |
ceaf2cfb | 503 | +{ |
fc63ffa9 AM |
504 | + struct aa_profile *profile; |
505 | + int error = 0; | |
ceaf2cfb | 506 | + |
fc63ffa9 AM |
507 | + if (kern) |
508 | + return 0; | |
ceaf2cfb | 509 | + |
fc63ffa9 AM |
510 | + profile = __aa_current_profile(); |
511 | + if (!unconfined(profile)) | |
512 | + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, | |
513 | + NULL); | |
514 | + return error; | |
515 | +} | |
ceaf2cfb | 516 | + |
fc63ffa9 AM |
517 | +static int apparmor_socket_bind(struct socket *sock, |
518 | + struct sockaddr *address, int addrlen) | |
9474138d | 519 | +{ |
fc63ffa9 | 520 | + struct sock *sk = sock->sk; |
9474138d | 521 | + |
fc63ffa9 AM |
522 | + return aa_revalidate_sk(OP_BIND, sk); |
523 | +} | |
ceaf2cfb | 524 | + |
fc63ffa9 AM |
525 | +static int apparmor_socket_connect(struct socket *sock, |
526 | + struct sockaddr *address, int addrlen) | |
527 | +{ | |
528 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 529 | + |
fc63ffa9 | 530 | + return aa_revalidate_sk(OP_CONNECT, sk); |
9474138d AM |
531 | +} |
532 | + | |
fc63ffa9 | 533 | +static int apparmor_socket_listen(struct socket *sock, int backlog) |
9474138d | 534 | +{ |
fc63ffa9 AM |
535 | + struct sock *sk = sock->sk; |
536 | + | |
537 | + return aa_revalidate_sk(OP_LISTEN, sk); | |
9474138d AM |
538 | +} |
539 | + | |
fc63ffa9 | 540 | +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) |
9474138d | 541 | +{ |
fc63ffa9 | 542 | + struct sock *sk = sock->sk; |
9474138d | 543 | + |
fc63ffa9 AM |
544 | + return aa_revalidate_sk(OP_ACCEPT, sk); |
545 | +} | |
9474138d | 546 | + |
fc63ffa9 AM |
547 | +static int apparmor_socket_sendmsg(struct socket *sock, |
548 | + struct msghdr *msg, int size) | |
76514441 | 549 | +{ |
fc63ffa9 | 550 | + struct sock *sk = sock->sk; |
76514441 | 551 | + |
fc63ffa9 | 552 | + return aa_revalidate_sk(OP_SENDMSG, sk); |
76514441 AM |
553 | +} |
554 | + | |
fc63ffa9 AM |
555 | +static int apparmor_socket_recvmsg(struct socket *sock, |
556 | + struct msghdr *msg, int size, int flags) | |
557 | +{ | |
558 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 559 | + |
fc63ffa9 AM |
560 | + return aa_revalidate_sk(OP_RECVMSG, sk); |
561 | +} | |
ceaf2cfb | 562 | + |
fc63ffa9 AM |
563 | +static int apparmor_socket_getsockname(struct socket *sock) |
564 | +{ | |
565 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 566 | + |
fc63ffa9 | 567 | + return aa_revalidate_sk(OP_GETSOCKNAME, sk); |
9474138d AM |
568 | +} |
569 | + | |
fc63ffa9 | 570 | +static int apparmor_socket_getpeername(struct socket *sock) |
9474138d | 571 | +{ |
fc63ffa9 | 572 | + struct sock *sk = sock->sk; |
9474138d | 573 | + |
fc63ffa9 AM |
574 | + return aa_revalidate_sk(OP_GETPEERNAME, sk); |
575 | +} | |
ceaf2cfb | 576 | + |
fc63ffa9 AM |
577 | +static int apparmor_socket_getsockopt(struct socket *sock, int level, |
578 | + int optname) | |
579 | +{ | |
580 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 581 | + |
fc63ffa9 AM |
582 | + return aa_revalidate_sk(OP_GETSOCKOPT, sk); |
583 | +} | |
ceaf2cfb | 584 | + |
fc63ffa9 AM |
585 | +static int apparmor_socket_setsockopt(struct socket *sock, int level, |
586 | + int optname) | |
587 | +{ | |
588 | + struct sock *sk = sock->sk; | |
2380c486 | 589 | + |
fc63ffa9 AM |
590 | + return aa_revalidate_sk(OP_SETSOCKOPT, sk); |
591 | +} | |
ceaf2cfb | 592 | + |
fc63ffa9 AM |
593 | +static int apparmor_socket_shutdown(struct socket *sock, int how) |
594 | +{ | |
595 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 596 | + |
fc63ffa9 | 597 | + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); |
ceaf2cfb | 598 | +} |
948a1326 | 599 | + |
1e8b8f9b AM |
600 | static struct security_operations apparmor_ops = { |
601 | .name = "apparmor", | |
602 | ||
2dfbb274 | 603 | @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = { |
5d26c04f AM |
604 | .getprocattr = apparmor_getprocattr, |
605 | .setprocattr = apparmor_setprocattr, | |
606 | ||
607 | + .socket_create = apparmor_socket_create, | |
608 | + .socket_bind = apparmor_socket_bind, | |
609 | + .socket_connect = apparmor_socket_connect, | |
610 | + .socket_listen = apparmor_socket_listen, | |
611 | + .socket_accept = apparmor_socket_accept, | |
612 | + .socket_sendmsg = apparmor_socket_sendmsg, | |
613 | + .socket_recvmsg = apparmor_socket_recvmsg, | |
614 | + .socket_getsockname = apparmor_socket_getsockname, | |
615 | + .socket_getpeername = apparmor_socket_getpeername, | |
616 | + .socket_getsockopt = apparmor_socket_getsockopt, | |
617 | + .socket_setsockopt = apparmor_socket_setsockopt, | |
618 | + .socket_shutdown = apparmor_socket_shutdown, | |
619 | + | |
620 | .cred_alloc_blank = apparmor_cred_alloc_blank, | |
621 | .cred_free = apparmor_cred_free, | |
622 | .cred_prepare = apparmor_cred_prepare, | |
fc63ffa9 | 623 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c |
ceaf2cfb | 624 | new file mode 100644 |
2dfbb274 | 625 | index 0000000..003dd18 |
ceaf2cfb | 626 | --- /dev/null |
fc63ffa9 | 627 | +++ b/security/apparmor/net.c |
1e8b8f9b | 628 | @@ -0,0 +1,162 @@ |
2380c486 | 629 | +/* |
9474138d AM |
630 | + * AppArmor security module |
631 | + * | |
fc63ffa9 | 632 | + * This file contains AppArmor network mediation |
2380c486 | 633 | + * |
9474138d | 634 | + * Copyright (C) 1998-2008 Novell/SUSE |
1e8b8f9b | 635 | + * Copyright 2009-2012 Canonical Ltd. |
2380c486 | 636 | + * |
9474138d AM |
637 | + * This program is free software; you can redistribute it and/or |
638 | + * modify it under the terms of the GNU General Public License as | |
639 | + * published by the Free Software Foundation, version 2 of the | |
640 | + * License. | |
2380c486 JR |
641 | + */ |
642 | + | |
9474138d AM |
643 | +#include "include/apparmor.h" |
644 | +#include "include/audit.h" | |
645 | +#include "include/context.h" | |
fc63ffa9 | 646 | +#include "include/net.h" |
9474138d | 647 | +#include "include/policy.h" |
2380c486 | 648 | + |
1e8b8f9b AM |
649 | +#include "net_names.h" |
650 | + | |
651 | +struct aa_fs_entry aa_fs_entry_network[] = { | |
652 | + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), | |
653 | + { } | |
9474138d AM |
654 | +}; |
655 | + | |
fc63ffa9 | 656 | +/* audit callback for net specific fields */ |
76514441 | 657 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 658 | +{ |
76514441 | 659 | + struct common_audit_data *sa = va; |
9474138d | 660 | + |
fc63ffa9 | 661 | + audit_log_format(ab, " family="); |
1e8b8f9b AM |
662 | + if (address_family_names[sa->u.net->family]) { |
663 | + audit_log_string(ab, address_family_names[sa->u.net->family]); | |
fc63ffa9 | 664 | + } else { |
1e8b8f9b | 665 | + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); |
fc63ffa9 | 666 | + } |
fc63ffa9 | 667 | + audit_log_format(ab, " sock_type="); |
1e8b8f9b AM |
668 | + if (sock_type_names[sa->aad->net.type]) { |
669 | + audit_log_string(ab, sock_type_names[sa->aad->net.type]); | |
fc63ffa9 | 670 | + } else { |
1e8b8f9b | 671 | + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); |
fc63ffa9 | 672 | + } |
1e8b8f9b | 673 | + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); |
2380c486 JR |
674 | +} |
675 | + | |
676 | +/** | |
fc63ffa9 AM |
677 | + * audit_net - audit network access |
678 | + * @profile: profile being enforced (NOT NULL) | |
679 | + * @op: operation being checked | |
680 | + * @family: network family | |
681 | + * @type: network type | |
682 | + * @protocol: network protocol | |
683 | + * @sk: socket auditing is being applied to | |
684 | + * @error: error code for failure else 0 | |
ceaf2cfb | 685 | + * |
fc63ffa9 | 686 | + * Returns: %0 or sa->error else other errorcode on failure |
2380c486 | 687 | + */ |
fc63ffa9 AM |
688 | +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, |
689 | + int protocol, struct sock *sk, int error) | |
2380c486 | 690 | +{ |
fc63ffa9 AM |
691 | + int audit_type = AUDIT_APPARMOR_AUTO; |
692 | + struct common_audit_data sa; | |
1e8b8f9b AM |
693 | + struct apparmor_audit_data aad = { }; |
694 | + struct lsm_network_audit net = { }; | |
fc63ffa9 | 695 | + if (sk) { |
0c3ec466 | 696 | + sa.type = LSM_AUDIT_DATA_NET; |
fc63ffa9 | 697 | + } else { |
0c3ec466 | 698 | + sa.type = LSM_AUDIT_DATA_NONE; |
2380c486 | 699 | + } |
fc63ffa9 | 700 | + /* todo fill in socket addr info */ |
1e8b8f9b AM |
701 | + sa.aad = &aad; |
702 | + sa.u.net = &net; | |
703 | + sa.aad->op = op, | |
704 | + sa.u.net->family = family; | |
705 | + sa.u.net->sk = sk; | |
706 | + sa.aad->net.type = type; | |
707 | + sa.aad->net.protocol = protocol; | |
708 | + sa.aad->error = error; | |
709 | + | |
710 | + if (likely(!sa.aad->error)) { | |
711 | + u16 audit_mask = profile->net.audit[sa.u.net->family]; | |
fc63ffa9 | 712 | + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && |
1e8b8f9b | 713 | + !(1 << sa.aad->net.type & audit_mask))) |
ceaf2cfb | 714 | + return 0; |
fc63ffa9 AM |
715 | + audit_type = AUDIT_APPARMOR_AUDIT; |
716 | + } else { | |
1e8b8f9b | 717 | + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; |
fc63ffa9 | 718 | + u16 kill_mask = 0; |
1e8b8f9b | 719 | + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; |
9474138d | 720 | + |
fc63ffa9 AM |
721 | + if (denied & kill_mask) |
722 | + audit_type = AUDIT_APPARMOR_KILL; | |
9474138d | 723 | + |
fc63ffa9 AM |
724 | + if ((denied & quiet_mask) && |
725 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
726 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
1e8b8f9b | 727 | + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; |
9474138d AM |
728 | + } |
729 | + | |
fc63ffa9 | 730 | + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); |
ceaf2cfb AM |
731 | +} |
732 | + | |
2380c486 | 733 | +/** |
fc63ffa9 AM |
734 | + * aa_net_perm - very course network access check |
735 | + * @op: operation being checked | |
736 | + * @profile: profile being enforced (NOT NULL) | |
737 | + * @family: network family | |
738 | + * @type: network type | |
739 | + * @protocol: network protocol | |
2380c486 | 740 | + * |
fc63ffa9 | 741 | + * Returns: %0 else error if permission denied |
2380c486 | 742 | + */ |
fc63ffa9 AM |
743 | +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, |
744 | + int protocol, struct sock *sk) | |
2380c486 | 745 | +{ |
fc63ffa9 AM |
746 | + u16 family_mask; |
747 | + int error; | |
ceaf2cfb | 748 | + |
fc63ffa9 AM |
749 | + if ((family < 0) || (family >= AF_MAX)) |
750 | + return -EINVAL; | |
ceaf2cfb | 751 | + |
fc63ffa9 AM |
752 | + if ((type < 0) || (type >= SOCK_MAX)) |
753 | + return -EINVAL; | |
76514441 | 754 | + |
fc63ffa9 AM |
755 | + /* unix domain and netlink sockets are handled by ipc */ |
756 | + if (family == AF_UNIX || family == AF_NETLINK) | |
757 | + return 0; | |
76514441 | 758 | + |
fc63ffa9 | 759 | + family_mask = profile->net.allow[family]; |
2380c486 | 760 | + |
fc63ffa9 | 761 | + error = (family_mask & (1 << type)) ? 0 : -EACCES; |
ceaf2cfb | 762 | + |
fc63ffa9 | 763 | + return audit_net(profile, op, family, type, protocol, sk, error); |
2380c486 JR |
764 | +} |
765 | + | |
76514441 | 766 | +/** |
fc63ffa9 AM |
767 | + * aa_revalidate_sk - Revalidate access to a sock |
768 | + * @op: operation being checked | |
769 | + * @sk: sock being revalidated (NOT NULL) | |
76514441 | 770 | + * |
fc63ffa9 | 771 | + * Returns: %0 else error if permission denied |
76514441 | 772 | + */ |
fc63ffa9 | 773 | +int aa_revalidate_sk(int op, struct sock *sk) |
2380c486 | 774 | +{ |
fc63ffa9 AM |
775 | + struct aa_profile *profile; |
776 | + int error = 0; | |
2380c486 | 777 | + |
fc63ffa9 AM |
778 | + /* aa_revalidate_sk should not be called from interrupt context |
779 | + * don't mediate these calls as they are not task related | |
780 | + */ | |
781 | + if (in_interrupt()) | |
782 | + return 0; | |
ceaf2cfb | 783 | + |
fc63ffa9 AM |
784 | + profile = __aa_current_profile(); |
785 | + if (!unconfined(profile)) | |
786 | + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, | |
787 | + sk->sk_protocol, sk); | |
2380c486 | 788 | + |
fc63ffa9 | 789 | + return error; |
2380c486 | 790 | +} |
fc63ffa9 | 791 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c |
2dfbb274 | 792 | index cf5fd22..27c8161 100644 |
fc63ffa9 AM |
793 | --- a/security/apparmor/policy.c |
794 | +++ b/security/apparmor/policy.c | |
795 | @@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile) | |
796 | ||
797 | aa_free_file_rules(&profile->file); | |
798 | aa_free_cap_rules(&profile->caps); | |
799 | + aa_free_net_rules(&profile->net); | |
800 | aa_free_rlimit_rules(&profile->rlimits); | |
801 | ||
802 | aa_free_sid(profile->sid); | |
803 | diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c | |
2dfbb274 | 804 | index 329b1fd..1b90dfa 100644 |
fc63ffa9 AM |
805 | --- a/security/apparmor/policy_unpack.c |
806 | +++ b/security/apparmor/policy_unpack.c | |
1e8b8f9b | 807 | @@ -193,6 +193,19 @@ fail: |
fc63ffa9 AM |
808 | return 0; |
809 | } | |
810 | ||
811 | +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) | |
2380c486 | 812 | +{ |
fc63ffa9 AM |
813 | + if (unpack_nameX(e, AA_U16, name)) { |
814 | + if (!inbounds(e, sizeof(u16))) | |
815 | + return 0; | |
816 | + if (data) | |
817 | + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); | |
818 | + e->pos += sizeof(u16); | |
819 | + return 1; | |
2380c486 | 820 | + } |
2380c486 JR |
821 | + return 0; |
822 | +} | |
823 | + | |
fc63ffa9 AM |
824 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
825 | { | |
826 | if (unpack_nameX(e, AA_U32, name)) { | |
1e8b8f9b | 827 | @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) |
fc63ffa9 AM |
828 | { |
829 | struct aa_profile *profile = NULL; | |
830 | const char *name = NULL; | |
2380c486 | 831 | + size_t size = 0; |
1e8b8f9b | 832 | int i, error = -EPROTO; |
fc63ffa9 AM |
833 | kernel_cap_t tmpcap; |
834 | u32 tmp; | |
1e8b8f9b | 835 | @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) |
fc63ffa9 AM |
836 | if (!unpack_rlimits(e, profile)) |
837 | goto fail; | |
838 | ||
ceaf2cfb | 839 | + size = unpack_array(e, "net_allowed_af"); |
2380c486 | 840 | + if (size) { |
2380c486 JR |
841 | + |
842 | + for (i = 0; i < size; i++) { | |
fc63ffa9 AM |
843 | + /* discard extraneous rules that this kernel will |
844 | + * never request | |
845 | + */ | |
1efb82ae | 846 | + if (i >= AF_MAX) { |
fc63ffa9 AM |
847 | + u16 tmp; |
848 | + if (!unpack_u16(e, &tmp, NULL) || | |
849 | + !unpack_u16(e, &tmp, NULL) || | |
850 | + !unpack_u16(e, &tmp, NULL)) | |
851 | + goto fail; | |
852 | + continue; | |
853 | + } | |
76514441 | 854 | + if (!unpack_u16(e, &profile->net.allow[i], NULL)) |
2380c486 | 855 | + goto fail; |
ceaf2cfb | 856 | + if (!unpack_u16(e, &profile->net.audit[i], NULL)) |
2380c486 | 857 | + goto fail; |
ceaf2cfb | 858 | + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) |
2380c486 JR |
859 | + goto fail; |
860 | + } | |
ceaf2cfb | 861 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
2380c486 | 862 | + goto fail; |
2380c486 | 863 | + } |
1e8b8f9b AM |
864 | + /* |
865 | + * allow unix domain and netlink sockets they are handled | |
866 | + * by IPC | |
867 | + */ | |
76514441 AM |
868 | + profile->net.allow[AF_UNIX] = 0xffff; |
869 | + profile->net.allow[AF_NETLINK] = 0xffff; | |
2380c486 | 870 | + |
1e8b8f9b AM |
871 | if (unpack_nameX(e, AA_STRUCT, "policydb")) { |
872 | /* generic policy dfa - optional and may be NULL */ | |
873 | profile->policy.dfa = unpack_dfa(e); | |
0fa3db16 | 874 | -- |
2dfbb274 | 875 | 1.7.7.6 |
1e8b8f9b | 876 | |
2dfbb274 | 877 | From 888a3d71db1ffd3a19d9f621b07e60c4ab9e1c44 Mon Sep 17 00:00:00 2001 |
0fa3db16 AM |
878 | From: John Johansen <john.johansen@canonical.com> |
879 | Date: Wed, 16 May 2012 10:58:05 -0700 | |
2dfbb274 | 880 | Subject: [PATCH] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount |
1e8b8f9b AM |
881 | |
882 | Add the ability for apparmor to do mediation of mount operations. Mount | |
883 | rules require an updated apparmor_parser (2.8 series) for policy compilation. | |
884 | ||
885 | The basic form of the rules are. | |
886 | ||
887 | [audit] [deny] mount [conds]* [device] [ -> [conds] path], | |
888 | [audit] [deny] remount [conds]* [path], | |
889 | [audit] [deny] umount [conds]* [path], | |
890 | [audit] [deny] pivotroot [oldroot=<value>] <path> | |
891 | ||
892 | remount is just a short cut for mount options=remount | |
893 | ||
894 | where [conds] can be | |
895 | fstype=<expr> | |
896 | options=<expr> | |
897 | ||
898 | Example mount commands | |
899 | mount, # allow all mounts, but not umount or pivotroot | |
900 | ||
901 | mount fstype=procfs, # allow mounting procfs anywhere | |
902 | ||
903 | mount options=(bind, ro) /foo -> /bar, # readonly bind mount | |
904 | ||
905 | mount /dev/sda -> /mnt, | |
906 | ||
907 | mount /dev/sd** -> /mnt/**, | |
908 | ||
909 | mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ | |
910 | ||
911 | umount, | |
912 | ||
913 | umount /m*, | |
914 | ||
915 | See the apparmor userspace for full documentation | |
fc63ffa9 AM |
916 | |
917 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
1e8b8f9b | 918 | Acked-by: Kees Cook <kees@ubuntu.com> |
fc63ffa9 | 919 | --- |
0fa3db16 AM |
920 | security/apparmor/Makefile | 2 +- |
921 | security/apparmor/apparmorfs.c | 13 + | |
922 | security/apparmor/audit.c | 4 + | |
923 | security/apparmor/domain.c | 2 +- | |
924 | security/apparmor/include/apparmor.h | 3 +- | |
925 | security/apparmor/include/audit.h | 11 + | |
926 | security/apparmor/include/domain.h | 2 + | |
927 | security/apparmor/include/mount.h | 54 +++ | |
928 | security/apparmor/lsm.c | 59 ++++ | |
929 | security/apparmor/mount.c | 620 ++++++++++++++++++++++++++++++++++ | |
930 | 10 files changed, 767 insertions(+), 3 deletions(-) | |
931 | create mode 100644 security/apparmor/include/mount.h | |
932 | create mode 100644 security/apparmor/mount.c | |
fc63ffa9 | 933 | |
fc63ffa9 | 934 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
1e8b8f9b | 935 | index 19daa85..63e0a4c 100644 |
fc63ffa9 AM |
936 | --- a/security/apparmor/Makefile |
937 | +++ b/security/apparmor/Makefile | |
1e8b8f9b AM |
938 | @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
939 | ||
948a1326 | 940 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
fc63ffa9 | 941 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
1e8b8f9b AM |
942 | - resource.o sid.o file.o net.o |
943 | + resource.o sid.o file.o net.o mount.o | |
fc63ffa9 | 944 | |
1e8b8f9b | 945 | clean-files := capability_names.h rlim_names.h net_names.h |
948a1326 | 946 | |
1e8b8f9b | 947 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c |
2dfbb274 | 948 | index 114fb23..ee77ec9 100644 |
1e8b8f9b AM |
949 | --- a/security/apparmor/apparmorfs.c |
950 | +++ b/security/apparmor/apparmorfs.c | |
2dfbb274 | 951 | @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { |
1e8b8f9b AM |
952 | { } |
953 | }; | |
954 | ||
955 | +static struct aa_fs_entry aa_fs_entry_mount[] = { | |
956 | + AA_FS_FILE_STRING("mask", "mount umount"), | |
957 | + { } | |
958 | +}; | |
959 | + | |
960 | +static struct aa_fs_entry aa_fs_entry_namespaces[] = { | |
961 | + AA_FS_FILE_BOOLEAN("profile", 1), | |
962 | + AA_FS_FILE_BOOLEAN("pivot_root", 1), | |
963 | + { } | |
964 | +}; | |
965 | + | |
966 | static struct aa_fs_entry aa_fs_entry_features[] = { | |
967 | AA_FS_DIR("domain", aa_fs_entry_domain), | |
968 | AA_FS_DIR("file", aa_fs_entry_file), | |
969 | AA_FS_DIR("network", aa_fs_entry_network), | |
970 | + AA_FS_DIR("mount", aa_fs_entry_mount), | |
971 | + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), | |
972 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
973 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
974 | { } | |
975 | diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c | |
2dfbb274 | 976 | index 3ae28db..e267963 100644 |
1e8b8f9b AM |
977 | --- a/security/apparmor/audit.c |
978 | +++ b/security/apparmor/audit.c | |
979 | @@ -44,6 +44,10 @@ const char *const op_table[] = { | |
980 | "file_mmap", | |
981 | "file_mprotect", | |
982 | ||
983 | + "pivotroot", | |
984 | + "mount", | |
985 | + "umount", | |
986 | + | |
987 | "create", | |
988 | "post_create", | |
989 | "bind", | |
990 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c | |
2dfbb274 | 991 | index b81ea10..afa8671 100644 |
1e8b8f9b AM |
992 | --- a/security/apparmor/domain.c |
993 | +++ b/security/apparmor/domain.c | |
994 | @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) | |
995 | * | |
996 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | |
997 | */ | |
998 | -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
999 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
1000 | { | |
1001 | struct aa_profile *new_profile = NULL; | |
1002 | struct aa_namespace *ns = profile->ns; | |
1003 | diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h | |
1004 | index 40aedd9..e243d96 100644 | |
1005 | --- a/security/apparmor/include/apparmor.h | |
1006 | +++ b/security/apparmor/include/apparmor.h | |
1007 | @@ -29,8 +29,9 @@ | |
1008 | #define AA_CLASS_NET 4 | |
1009 | #define AA_CLASS_RLIMITS 5 | |
1010 | #define AA_CLASS_DOMAIN 6 | |
1011 | +#define AA_CLASS_MOUNT 7 | |
1012 | ||
1013 | -#define AA_CLASS_LAST AA_CLASS_DOMAIN | |
1014 | +#define AA_CLASS_LAST AA_CLASS_MOUNT | |
1015 | ||
1016 | /* Control parameters settable through module/boot flags */ | |
1017 | extern enum audit_mode aa_g_audit; | |
1018 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h | |
2dfbb274 | 1019 | index 17734f9..66a738c 100644 |
1e8b8f9b AM |
1020 | --- a/security/apparmor/include/audit.h |
1021 | +++ b/security/apparmor/include/audit.h | |
1022 | @@ -73,6 +73,10 @@ enum aa_ops { | |
1023 | OP_FMMAP, | |
1024 | OP_FMPROT, | |
1025 | ||
1026 | + OP_PIVOTROOT, | |
1027 | + OP_MOUNT, | |
1028 | + OP_UMOUNT, | |
1029 | + | |
1030 | OP_CREATE, | |
1031 | OP_POST_CREATE, | |
1032 | OP_BIND, | |
2dfbb274 | 1033 | @@ -122,6 +126,13 @@ struct apparmor_audit_data { |
1e8b8f9b AM |
1034 | unsigned long max; |
1035 | } rlim; | |
1036 | struct { | |
1037 | + const char *src_name; | |
1038 | + const char *type; | |
1039 | + const char *trans; | |
1040 | + const char *data; | |
1041 | + unsigned long flags; | |
1042 | + } mnt; | |
1043 | + struct { | |
1044 | const char *target; | |
1045 | u32 request; | |
1046 | u32 denied; | |
1047 | diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h | |
1048 | index de04464..a3f70c5 100644 | |
1049 | --- a/security/apparmor/include/domain.h | |
1050 | +++ b/security/apparmor/include/domain.h | |
1051 | @@ -23,6 +23,8 @@ struct aa_domain { | |
1052 | char **table; | |
1053 | }; | |
1054 | ||
1055 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); | |
1056 | + | |
1057 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | |
1058 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | |
1059 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | |
1060 | diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h | |
ceaf2cfb | 1061 | new file mode 100644 |
1e8b8f9b | 1062 | index 0000000..bc17a53 |
ceaf2cfb | 1063 | --- /dev/null |
1e8b8f9b AM |
1064 | +++ b/security/apparmor/include/mount.h |
1065 | @@ -0,0 +1,54 @@ | |
2380c486 | 1066 | +/* |
9474138d AM |
1067 | + * AppArmor security module |
1068 | + * | |
1e8b8f9b | 1069 | + * This file contains AppArmor file mediation function definitions. |
2380c486 | 1070 | + * |
1e8b8f9b | 1071 | + * Copyright 2012 Canonical Ltd. |
2380c486 | 1072 | + * |
9474138d AM |
1073 | + * This program is free software; you can redistribute it and/or |
1074 | + * modify it under the terms of the GNU General Public License as | |
1075 | + * published by the Free Software Foundation, version 2 of the | |
1076 | + * License. | |
2380c486 JR |
1077 | + */ |
1078 | + | |
1e8b8f9b AM |
1079 | +#ifndef __AA_MOUNT_H |
1080 | +#define __AA_MOUNT_H | |
fc63ffa9 | 1081 | + |
1e8b8f9b AM |
1082 | +#include <linux/fs.h> |
1083 | +#include <linux/path.h> | |
2380c486 | 1084 | + |
1e8b8f9b AM |
1085 | +#include "domain.h" |
1086 | +#include "policy.h" | |
76514441 | 1087 | + |
1e8b8f9b AM |
1088 | +/* mount perms */ |
1089 | +#define AA_MAY_PIVOTROOT 0x01 | |
1090 | +#define AA_MAY_MOUNT 0x02 | |
1091 | +#define AA_MAY_UMOUNT 0x04 | |
1092 | +#define AA_AUDIT_DATA 0x40 | |
1093 | +#define AA_CONT_MATCH 0x40 | |
fc63ffa9 | 1094 | + |
1e8b8f9b | 1095 | +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) |
fc63ffa9 | 1096 | + |
1e8b8f9b AM |
1097 | +int aa_remount(struct aa_profile *profile, struct path *path, |
1098 | + unsigned long flags, void *data); | |
fc63ffa9 | 1099 | + |
1e8b8f9b AM |
1100 | +int aa_bind_mount(struct aa_profile *profile, struct path *path, |
1101 | + const char *old_name, unsigned long flags); | |
fc63ffa9 | 1102 | + |
fc63ffa9 | 1103 | + |
1e8b8f9b AM |
1104 | +int aa_mount_change_type(struct aa_profile *profile, struct path *path, |
1105 | + unsigned long flags); | |
fc63ffa9 | 1106 | + |
1e8b8f9b AM |
1107 | +int aa_move_mount(struct aa_profile *profile, struct path *path, |
1108 | + const char *old_name); | |
1109 | + | |
1110 | +int aa_new_mount(struct aa_profile *profile, const char *dev_name, | |
1111 | + struct path *path, const char *type, unsigned long flags, | |
1112 | + void *data); | |
1113 | + | |
1114 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); | |
1115 | + | |
1116 | +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, | |
1117 | + struct path *new_path); | |
1118 | + | |
1119 | +#endif /* __AA_MOUNT_H */ | |
1120 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
2dfbb274 | 1121 | index f628734..65ff9e4 100644 |
1e8b8f9b AM |
1122 | --- a/security/apparmor/lsm.c |
1123 | +++ b/security/apparmor/lsm.c | |
1124 | @@ -36,6 +36,7 @@ | |
1125 | #include "include/path.h" | |
1126 | #include "include/policy.h" | |
1127 | #include "include/procattr.h" | |
1128 | +#include "include/mount.h" | |
1129 | ||
1130 | /* Flag indicating whether initialization completed */ | |
1131 | int apparmor_initialized __initdata; | |
2dfbb274 | 1132 | @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, |
1e8b8f9b AM |
1133 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); |
1134 | } | |
1135 | ||
1136 | +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, | |
1137 | + unsigned long flags, void *data) | |
2380c486 | 1138 | +{ |
1e8b8f9b AM |
1139 | + struct aa_profile *profile; |
1140 | + int error = 0; | |
76514441 | 1141 | + |
1e8b8f9b AM |
1142 | + /* Discard magic */ |
1143 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
1144 | + flags &= ~MS_MGC_MSK; | |
2380c486 | 1145 | + |
1e8b8f9b AM |
1146 | + flags &= ~AA_MS_IGNORE_MASK; |
1147 | + | |
1148 | + profile = __aa_current_profile(); | |
1149 | + if (!unconfined(profile)) { | |
1150 | + if (flags & MS_REMOUNT) | |
1151 | + error = aa_remount(profile, path, flags, data); | |
1152 | + else if (flags & MS_BIND) | |
1153 | + error = aa_bind_mount(profile, path, dev_name, flags); | |
1154 | + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
1155 | + MS_UNBINDABLE)) | |
1156 | + error = aa_mount_change_type(profile, path, flags); | |
1157 | + else if (flags & MS_MOVE) | |
1158 | + error = aa_move_mount(profile, path, dev_name); | |
1159 | + else | |
1160 | + error = aa_new_mount(profile, dev_name, path, type, | |
1161 | + flags, data); | |
2380c486 | 1162 | + } |
1e8b8f9b AM |
1163 | + return error; |
1164 | +} | |
2380c486 | 1165 | + |
1e8b8f9b AM |
1166 | +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) |
1167 | +{ | |
1168 | + struct aa_profile *profile; | |
1169 | + int error = 0; | |
1170 | + | |
1171 | + profile = __aa_current_profile(); | |
1172 | + if (!unconfined(profile)) | |
1173 | + error = aa_umount(profile, mnt, flags); | |
1174 | + | |
1175 | + return error; | |
2380c486 JR |
1176 | +} |
1177 | + | |
1e8b8f9b | 1178 | +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) |
2380c486 | 1179 | +{ |
1e8b8f9b AM |
1180 | + struct aa_profile *profile; |
1181 | + int error = 0; | |
1182 | + | |
1183 | + profile = __aa_current_profile(); | |
1184 | + if (!unconfined(profile)) | |
1185 | + error = aa_pivotroot(profile, old_path, new_path); | |
1186 | + | |
1187 | + return error; | |
2380c486 JR |
1188 | +} |
1189 | + | |
1e8b8f9b AM |
1190 | static int apparmor_getprocattr(struct task_struct *task, char *name, |
1191 | char **value) | |
1192 | { | |
2dfbb274 | 1193 | @@ -721,6 +776,10 @@ static struct security_operations apparmor_ops = { |
1e8b8f9b AM |
1194 | .capget = apparmor_capget, |
1195 | .capable = apparmor_capable, | |
1196 | ||
1197 | + .sb_mount = apparmor_sb_mount, | |
1198 | + .sb_umount = apparmor_sb_umount, | |
1199 | + .sb_pivotroot = apparmor_sb_pivotroot, | |
1200 | + | |
1201 | .path_link = apparmor_path_link, | |
1202 | .path_unlink = apparmor_path_unlink, | |
1203 | .path_symlink = apparmor_path_symlink, | |
1204 | diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c | |
1205 | new file mode 100644 | |
2dfbb274 | 1206 | index 0000000..478aa4d |
1e8b8f9b AM |
1207 | --- /dev/null |
1208 | +++ b/security/apparmor/mount.c | |
1209 | @@ -0,0 +1,620 @@ | |
1210 | +/* | |
1211 | + * AppArmor security module | |
fc63ffa9 | 1212 | + * |
1e8b8f9b | 1213 | + * This file contains AppArmor mediation of files |
ceaf2cfb | 1214 | + * |
1e8b8f9b AM |
1215 | + * Copyright (C) 1998-2008 Novell/SUSE |
1216 | + * Copyright 2009-2012 Canonical Ltd. | |
1217 | + * | |
1218 | + * This program is free software; you can redistribute it and/or | |
1219 | + * modify it under the terms of the GNU General Public License as | |
1220 | + * published by the Free Software Foundation, version 2 of the | |
1221 | + * License. | |
ceaf2cfb | 1222 | + */ |
2380c486 | 1223 | + |
1e8b8f9b AM |
1224 | +#include <linux/fs.h> |
1225 | +#include <linux/mount.h> | |
1226 | +#include <linux/namei.h> | |
2380c486 | 1227 | + |
1e8b8f9b AM |
1228 | +#include "include/apparmor.h" |
1229 | +#include "include/audit.h" | |
1230 | +#include "include/context.h" | |
1231 | +#include "include/domain.h" | |
1232 | +#include "include/file.h" | |
1233 | +#include "include/match.h" | |
1234 | +#include "include/mount.h" | |
1235 | +#include "include/path.h" | |
1236 | +#include "include/policy.h" | |
ceaf2cfb | 1237 | + |
2380c486 | 1238 | + |
1e8b8f9b AM |
1239 | +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) |
1240 | +{ | |
1241 | + if (flags & MS_RDONLY) | |
1242 | + audit_log_format(ab, "ro"); | |
1243 | + else | |
1244 | + audit_log_format(ab, "rw"); | |
1245 | + if (flags & MS_NOSUID) | |
1246 | + audit_log_format(ab, ", nosuid"); | |
1247 | + if (flags & MS_NODEV) | |
1248 | + audit_log_format(ab, ", nodev"); | |
1249 | + if (flags & MS_NOEXEC) | |
1250 | + audit_log_format(ab, ", noexec"); | |
1251 | + if (flags & MS_SYNCHRONOUS) | |
1252 | + audit_log_format(ab, ", sync"); | |
1253 | + if (flags & MS_REMOUNT) | |
1254 | + audit_log_format(ab, ", remount"); | |
1255 | + if (flags & MS_MANDLOCK) | |
1256 | + audit_log_format(ab, ", mand"); | |
1257 | + if (flags & MS_DIRSYNC) | |
1258 | + audit_log_format(ab, ", dirsync"); | |
1259 | + if (flags & MS_NOATIME) | |
1260 | + audit_log_format(ab, ", noatime"); | |
1261 | + if (flags & MS_NODIRATIME) | |
1262 | + audit_log_format(ab, ", nodiratime"); | |
1263 | + if (flags & MS_BIND) | |
1264 | + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); | |
1265 | + if (flags & MS_MOVE) | |
1266 | + audit_log_format(ab, ", move"); | |
1267 | + if (flags & MS_SILENT) | |
1268 | + audit_log_format(ab, ", silent"); | |
1269 | + if (flags & MS_POSIXACL) | |
1270 | + audit_log_format(ab, ", acl"); | |
1271 | + if (flags & MS_UNBINDABLE) | |
1272 | + audit_log_format(ab, flags & MS_REC ? ", runbindable" : | |
1273 | + ", unbindable"); | |
1274 | + if (flags & MS_PRIVATE) | |
1275 | + audit_log_format(ab, flags & MS_REC ? ", rprivate" : | |
1276 | + ", private"); | |
1277 | + if (flags & MS_SLAVE) | |
1278 | + audit_log_format(ab, flags & MS_REC ? ", rslave" : | |
1279 | + ", slave"); | |
1280 | + if (flags & MS_SHARED) | |
1281 | + audit_log_format(ab, flags & MS_REC ? ", rshared" : | |
1282 | + ", shared"); | |
1283 | + if (flags & MS_RELATIME) | |
1284 | + audit_log_format(ab, ", relatime"); | |
1285 | + if (flags & MS_I_VERSION) | |
1286 | + audit_log_format(ab, ", iversion"); | |
1287 | + if (flags & MS_STRICTATIME) | |
1288 | + audit_log_format(ab, ", strictatime"); | |
1289 | + if (flags & MS_NOUSER) | |
1290 | + audit_log_format(ab, ", nouser"); | |
2380c486 JR |
1291 | +} |
1292 | + | |
ceaf2cfb | 1293 | +/** |
1e8b8f9b AM |
1294 | + * audit_cb - call back for mount specific audit fields |
1295 | + * @ab: audit_buffer (NOT NULL) | |
1296 | + * @va: audit struct to audit values of (NOT NULL) | |
ceaf2cfb | 1297 | + */ |
1e8b8f9b | 1298 | +static void audit_cb(struct audit_buffer *ab, void *va) |
2380c486 | 1299 | +{ |
1e8b8f9b | 1300 | + struct common_audit_data *sa = va; |
9474138d | 1301 | + |
1e8b8f9b AM |
1302 | + if (sa->aad->mnt.type) { |
1303 | + audit_log_format(ab, " fstype="); | |
1304 | + audit_log_untrustedstring(ab, sa->aad->mnt.type); | |
1305 | + } | |
1306 | + if (sa->aad->mnt.src_name) { | |
1307 | + audit_log_format(ab, " srcname="); | |
1308 | + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); | |
1309 | + } | |
1310 | + if (sa->aad->mnt.trans) { | |
1311 | + audit_log_format(ab, " trans="); | |
1312 | + audit_log_untrustedstring(ab, sa->aad->mnt.trans); | |
1313 | + } | |
1314 | + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { | |
1315 | + audit_log_format(ab, " flags=\""); | |
1316 | + audit_mnt_flags(ab, sa->aad->mnt.flags); | |
1317 | + audit_log_format(ab, "\""); | |
1318 | + } | |
1319 | + if (sa->aad->mnt.data) { | |
1320 | + audit_log_format(ab, " options="); | |
1321 | + audit_log_untrustedstring(ab, sa->aad->mnt.data); | |
1322 | + } | |
9474138d | 1323 | +} |
2380c486 | 1324 | + |
fc63ffa9 | 1325 | +/** |
1e8b8f9b AM |
1326 | + * audit_mount - handle the auditing of mount operations |
1327 | + * @profile: the profile being enforced (NOT NULL) | |
1328 | + * @gfp: allocation flags | |
1329 | + * @op: operation being mediated (NOT NULL) | |
1330 | + * @name: name of object being mediated (MAYBE NULL) | |
1331 | + * @src_name: src_name of object being mediated (MAYBE_NULL) | |
1332 | + * @type: type of filesystem (MAYBE_NULL) | |
1333 | + * @trans: name of trans (MAYBE NULL) | |
1334 | + * @flags: filesystem idependent mount flags | |
1335 | + * @data: filesystem mount flags | |
1336 | + * @request: permissions requested | |
1337 | + * @perms: the permissions computed for the request (NOT NULL) | |
1338 | + * @info: extra information message (MAYBE NULL) | |
1339 | + * @error: 0 if operation allowed else failure error code | |
9474138d | 1340 | + * |
1e8b8f9b | 1341 | + * Returns: %0 or error on failure |
76514441 | 1342 | + */ |
1e8b8f9b AM |
1343 | +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, |
1344 | + const char *name, const char *src_name, | |
1345 | + const char *type, const char *trans, | |
1346 | + unsigned long flags, const void *data, u32 request, | |
1347 | + struct file_perms *perms, const char *info, int error) | |
2380c486 | 1348 | +{ |
1e8b8f9b | 1349 | + int audit_type = AUDIT_APPARMOR_AUTO; |
2dfbb274 | 1350 | + struct common_audit_data sa = { }; |
1e8b8f9b | 1351 | + struct apparmor_audit_data aad = { }; |
9474138d | 1352 | + |
1e8b8f9b AM |
1353 | + if (likely(!error)) { |
1354 | + u32 mask = perms->audit; | |
9474138d | 1355 | + |
1e8b8f9b AM |
1356 | + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
1357 | + mask = 0xffff; | |
76514441 | 1358 | + |
1e8b8f9b AM |
1359 | + /* mask off perms that are not being force audited */ |
1360 | + request &= mask; | |
fc63ffa9 | 1361 | + |
1e8b8f9b AM |
1362 | + if (likely(!request)) |
1363 | + return 0; | |
1364 | + audit_type = AUDIT_APPARMOR_AUDIT; | |
1365 | + } else { | |
1366 | + /* only report permissions that were denied */ | |
1367 | + request = request & ~perms->allow; | |
1368 | + | |
1369 | + if (request & perms->kill) | |
1370 | + audit_type = AUDIT_APPARMOR_KILL; | |
1371 | + | |
1372 | + /* quiet known rejects, assumes quiet and kill do not overlap */ | |
1373 | + if ((request & perms->quiet) && | |
1374 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
1375 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
1376 | + request &= ~perms->quiet; | |
1377 | + | |
1378 | + if (!request) | |
1379 | + return COMPLAIN_MODE(profile) ? | |
1380 | + complain_error(error) : error; | |
1381 | + } | |
1382 | + | |
0c3ec466 | 1383 | + sa.type = LSM_AUDIT_DATA_NONE; |
1e8b8f9b AM |
1384 | + sa.aad = &aad; |
1385 | + sa.aad->op = op; | |
1386 | + sa.aad->name = name; | |
1387 | + sa.aad->mnt.src_name = src_name; | |
1388 | + sa.aad->mnt.type = type; | |
1389 | + sa.aad->mnt.trans = trans; | |
1390 | + sa.aad->mnt.flags = flags; | |
1391 | + if (data && (perms->audit & AA_AUDIT_DATA)) | |
1392 | + sa.aad->mnt.data = data; | |
1393 | + sa.aad->info = info; | |
1394 | + sa.aad->error = error; | |
1395 | + | |
1396 | + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); | |
76514441 AM |
1397 | +} |
1398 | + | |
1399 | +/** | |
1e8b8f9b AM |
1400 | + * match_mnt_flags - Do an ordered match on mount flags |
1401 | + * @dfa: dfa to match against | |
1402 | + * @state: state to start in | |
1403 | + * @flags: mount flags to match against | |
76514441 | 1404 | + * |
1e8b8f9b AM |
1405 | + * Mount flags are encoded as an ordered match. This is done instead of |
1406 | + * checking against a simple bitmask, to allow for logical operations | |
1407 | + * on the flags. | |
76514441 | 1408 | + * |
1e8b8f9b | 1409 | + * Returns: next state after flags match |
76514441 | 1410 | + */ |
1e8b8f9b AM |
1411 | +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, |
1412 | + unsigned long flags) | |
9474138d | 1413 | +{ |
1e8b8f9b | 1414 | + unsigned int i; |
fc63ffa9 | 1415 | + |
1e8b8f9b AM |
1416 | + for (i = 0; i <= 31 ; ++i) { |
1417 | + if ((1 << i) & flags) | |
1418 | + state = aa_dfa_next(dfa, state, i + 1); | |
1419 | + } | |
1420 | + | |
1421 | + return state; | |
9474138d AM |
1422 | +} |
1423 | + | |
1424 | +/** | |
1e8b8f9b AM |
1425 | + * compute_mnt_perms - compute mount permission associated with @state |
1426 | + * @dfa: dfa to match against (NOT NULL) | |
1427 | + * @state: state match finished in | |
ceaf2cfb | 1428 | + * |
1e8b8f9b | 1429 | + * Returns: mount permissions |
9474138d | 1430 | + */ |
1e8b8f9b AM |
1431 | +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, |
1432 | + unsigned int state) | |
9474138d | 1433 | +{ |
1e8b8f9b | 1434 | + struct file_perms perms; |
2380c486 | 1435 | + |
1e8b8f9b AM |
1436 | + perms.kill = 0; |
1437 | + perms.allow = dfa_user_allow(dfa, state); | |
1438 | + perms.audit = dfa_user_audit(dfa, state); | |
1439 | + perms.quiet = dfa_user_quiet(dfa, state); | |
1440 | + perms.xindex = dfa_user_xindex(dfa, state); | |
1441 | + | |
1442 | + return perms; | |
1443 | +} | |
1444 | + | |
1445 | +static const char const *mnt_info_table[] = { | |
1446 | + "match succeeded", | |
1447 | + "failed mntpnt match", | |
1448 | + "failed srcname match", | |
1449 | + "failed type match", | |
1450 | + "failed flags match", | |
1451 | + "failed data match" | |
1452 | +}; | |
1453 | + | |
1454 | +/* | |
1455 | + * Returns 0 on success else element that match failed in, this is the | |
1456 | + * index into the mnt_info_table above | |
1457 | + */ | |
1458 | +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, | |
1459 | + const char *mntpnt, const char *devname, | |
1460 | + const char *type, unsigned long flags, | |
1461 | + void *data, bool binary, struct file_perms *perms) | |
1462 | +{ | |
1463 | + unsigned int state; | |
1464 | + | |
1465 | + state = aa_dfa_match(dfa, start, mntpnt); | |
1466 | + state = aa_dfa_null_transition(dfa, state); | |
1467 | + if (!state) | |
1468 | + return 1; | |
1469 | + | |
1470 | + if (devname) | |
1471 | + state = aa_dfa_match(dfa, state, devname); | |
1472 | + state = aa_dfa_null_transition(dfa, state); | |
1473 | + if (!state) | |
1474 | + return 2; | |
1475 | + | |
1476 | + if (type) | |
1477 | + state = aa_dfa_match(dfa, state, type); | |
1478 | + state = aa_dfa_null_transition(dfa, state); | |
1479 | + if (!state) | |
1480 | + return 3; | |
1481 | + | |
1482 | + state = match_mnt_flags(dfa, state, flags); | |
1483 | + if (!state) | |
1484 | + return 4; | |
1485 | + *perms = compute_mnt_perms(dfa, state); | |
1486 | + if (perms->allow & AA_MAY_MOUNT) | |
1487 | + return 0; | |
1488 | + | |
1489 | + /* only match data if not binary and the DFA flags data is expected */ | |
1490 | + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { | |
1491 | + state = aa_dfa_null_transition(dfa, state); | |
1492 | + if (!state) | |
1493 | + return 4; | |
1494 | + | |
1495 | + state = aa_dfa_match(dfa, state, data); | |
1496 | + if (!state) | |
1497 | + return 5; | |
1498 | + *perms = compute_mnt_perms(dfa, state); | |
1499 | + if (perms->allow & AA_MAY_MOUNT) | |
1500 | + return 0; | |
fc63ffa9 | 1501 | + } |
1e8b8f9b AM |
1502 | + |
1503 | + /* failed at end of flags match */ | |
1504 | + return 4; | |
9474138d AM |
1505 | +} |
1506 | + | |
ceaf2cfb | 1507 | +/** |
1e8b8f9b AM |
1508 | + * match_mnt - handle path matching for mount |
1509 | + * @profile: the confining profile | |
1510 | + * @mntpnt: string for the mntpnt (NOT NULL) | |
1511 | + * @devname: string for the devname/src_name (MAYBE NULL) | |
1512 | + * @type: string for the dev type (MAYBE NULL) | |
1513 | + * @flags: mount flags to match | |
1514 | + * @data: fs mount data (MAYBE NULL) | |
1515 | + * @binary: whether @data is binary | |
1516 | + * @perms: Returns: permission found by the match | |
1517 | + * @info: Returns: infomation string about the match for logging | |
fc63ffa9 | 1518 | + * |
1e8b8f9b | 1519 | + * Returns: 0 on success else error |
ceaf2cfb | 1520 | + */ |
1e8b8f9b AM |
1521 | +static int match_mnt(struct aa_profile *profile, const char *mntpnt, |
1522 | + const char *devname, const char *type, | |
1523 | + unsigned long flags, void *data, bool binary, | |
1524 | + struct file_perms *perms, const char **info) | |
9474138d | 1525 | +{ |
1e8b8f9b | 1526 | + int pos; |
2380c486 | 1527 | + |
1e8b8f9b AM |
1528 | + if (!profile->policy.dfa) |
1529 | + return -EACCES; | |
1530 | + | |
1531 | + pos = do_match_mnt(profile->policy.dfa, | |
1532 | + profile->policy.start[AA_CLASS_MOUNT], | |
1533 | + mntpnt, devname, type, flags, data, binary, perms); | |
1534 | + if (pos) { | |
1535 | + *info = mnt_info_table[pos]; | |
1536 | + return -EACCES; | |
1537 | + } | |
2380c486 | 1538 | + |
fc63ffa9 | 1539 | + return 0; |
9474138d | 1540 | +} |
9474138d | 1541 | + |
1e8b8f9b AM |
1542 | +static int path_flags(struct aa_profile *profile, struct path *path) |
1543 | +{ | |
1544 | + return profile->path_flags | | |
1545 | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; | |
1546 | +} | |
9474138d | 1547 | + |
1e8b8f9b AM |
1548 | +int aa_remount(struct aa_profile *profile, struct path *path, |
1549 | + unsigned long flags, void *data) | |
9474138d | 1550 | +{ |
1e8b8f9b AM |
1551 | + struct file_perms perms = { }; |
1552 | + const char *name, *info = NULL; | |
1553 | + char *buffer = NULL; | |
1554 | + int binary, error; | |
1555 | + | |
1556 | + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; | |
1557 | + | |
1558 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1559 | + &info); | |
1560 | + if (error) | |
1561 | + goto audit; | |
1562 | + | |
1563 | + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, | |
1564 | + &perms, &info); | |
1565 | + | |
1566 | +audit: | |
1567 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
1568 | + NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
1569 | + error); | |
1570 | + kfree(buffer); | |
1571 | + | |
1572 | + return error; | |
9474138d AM |
1573 | +} |
1574 | + | |
1e8b8f9b AM |
1575 | +int aa_bind_mount(struct aa_profile *profile, struct path *path, |
1576 | + const char *dev_name, unsigned long flags) | |
9474138d | 1577 | +{ |
1e8b8f9b AM |
1578 | + struct file_perms perms = { }; |
1579 | + char *buffer = NULL, *old_buffer = NULL; | |
1580 | + const char *name, *old_name = NULL, *info = NULL; | |
1581 | + struct path old_path; | |
1582 | + int error; | |
1583 | + | |
1584 | + if (!dev_name || !*dev_name) | |
1585 | + return -EINVAL; | |
1586 | + | |
1587 | + flags &= MS_REC | MS_BIND; | |
1588 | + | |
1589 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1590 | + &info); | |
1591 | + if (error) | |
1592 | + goto audit; | |
1593 | + | |
1594 | + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); | |
1595 | + if (error) | |
1596 | + goto audit; | |
1597 | + | |
1598 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
1599 | + &old_buffer, &old_name, &info); | |
1600 | + path_put(&old_path); | |
1601 | + if (error) | |
1602 | + goto audit; | |
1603 | + | |
1604 | + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, | |
1605 | + &perms, &info); | |
1606 | + | |
1607 | +audit: | |
1608 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
1609 | + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, | |
1610 | + info, error); | |
1611 | + kfree(buffer); | |
1612 | + kfree(old_buffer); | |
1613 | + | |
1614 | + return error; | |
9474138d | 1615 | +} |
fc63ffa9 | 1616 | + |
1e8b8f9b AM |
1617 | +int aa_mount_change_type(struct aa_profile *profile, struct path *path, |
1618 | + unsigned long flags) | |
1619 | +{ | |
1620 | + struct file_perms perms = { }; | |
1621 | + char *buffer = NULL; | |
1622 | + const char *name, *info = NULL; | |
1623 | + int error; | |
1624 | + | |
1625 | + /* These are the flags allowed by do_change_type() */ | |
1626 | + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
1627 | + MS_UNBINDABLE); | |
1628 | + | |
1629 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1630 | + &info); | |
fc63ffa9 | 1631 | + if (error) |
1e8b8f9b AM |
1632 | + goto audit; |
1633 | + | |
1634 | + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, | |
1635 | + &info); | |
1636 | + | |
1637 | +audit: | |
1638 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
1639 | + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, | |
1640 | + error); | |
1641 | + kfree(buffer); | |
1642 | + | |
1643 | + return error; | |
1644 | +} | |
1645 | + | |
1646 | +int aa_move_mount(struct aa_profile *profile, struct path *path, | |
1647 | + const char *orig_name) | |
1648 | +{ | |
1649 | + struct file_perms perms = { }; | |
1650 | + char *buffer = NULL, *old_buffer = NULL; | |
1651 | + const char *name, *old_name = NULL, *info = NULL; | |
1652 | + struct path old_path; | |
1653 | + int error; | |
1654 | + | |
1655 | + if (!orig_name || !*orig_name) | |
1656 | + return -EINVAL; | |
1657 | + | |
1658 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1659 | + &info); | |
fc63ffa9 | 1660 | + if (error) |
1e8b8f9b AM |
1661 | + goto audit; |
1662 | + | |
1663 | + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); | |
fc63ffa9 | 1664 | + if (error) |
1e8b8f9b AM |
1665 | + goto audit; |
1666 | + | |
1667 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
1668 | + &old_buffer, &old_name, &info); | |
1669 | + path_put(&old_path); | |
1670 | + if (error) | |
1671 | + goto audit; | |
1672 | + | |
1673 | + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, | |
1674 | + &perms, &info); | |
1675 | + | |
1676 | +audit: | |
1677 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
1678 | + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, | |
1679 | + info, error); | |
1680 | + kfree(buffer); | |
1681 | + kfree(old_buffer); | |
1682 | + | |
1683 | + return error; | |
1684 | +} | |
1685 | + | |
1686 | +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, | |
1687 | + struct path *path, const char *type, unsigned long flags, | |
1688 | + void *data) | |
1689 | +{ | |
1690 | + struct file_perms perms = { }; | |
1691 | + char *buffer = NULL, *dev_buffer = NULL; | |
1692 | + const char *name = NULL, *dev_name = NULL, *info = NULL; | |
1693 | + int binary = 1; | |
1694 | + int error; | |
1695 | + | |
1696 | + dev_name = orig_dev_name; | |
1697 | + if (type) { | |
1698 | + int requires_dev; | |
1699 | + struct file_system_type *fstype = get_fs_type(type); | |
1700 | + if (!fstype) | |
1701 | + return -ENODEV; | |
1702 | + | |
1703 | + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; | |
1704 | + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; | |
1705 | + put_filesystem(fstype); | |
1706 | + | |
1707 | + if (requires_dev) { | |
1708 | + struct path dev_path; | |
1709 | + | |
1710 | + if (!dev_name || !*dev_name) { | |
1711 | + error = -ENOENT; | |
1712 | + goto out; | |
1713 | + } | |
1714 | + | |
1715 | + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); | |
1716 | + if (error) | |
1717 | + goto audit; | |
1718 | + | |
1719 | + error = aa_path_name(&dev_path, | |
1720 | + path_flags(profile, &dev_path), | |
1721 | + &dev_buffer, &dev_name, &info); | |
1722 | + path_put(&dev_path); | |
1723 | + if (error) | |
1724 | + goto audit; | |
1725 | + } | |
1726 | + } | |
1727 | + | |
1728 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1729 | + &info); | |
1730 | + if (error) | |
1731 | + goto audit; | |
1732 | + | |
1733 | + error = match_mnt(profile, name, dev_name, type, flags, data, binary, | |
1734 | + &perms, &info); | |
1735 | + | |
1736 | +audit: | |
1737 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, | |
1738 | + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
1739 | + error); | |
1740 | + kfree(buffer); | |
1741 | + kfree(dev_buffer); | |
1742 | + | |
1743 | +out: | |
1744 | + return error; | |
1745 | + | |
1746 | +} | |
1747 | + | |
1748 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) | |
1749 | +{ | |
1750 | + struct file_perms perms = { }; | |
1751 | + char *buffer = NULL; | |
1752 | + const char *name, *info = NULL; | |
1753 | + int error; | |
1754 | + | |
1755 | + struct path path = { mnt, mnt->mnt_root }; | |
1756 | + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, | |
1757 | + &info); | |
1758 | + if (error) | |
1759 | + goto audit; | |
1760 | + | |
1761 | + if (!error && profile->policy.dfa) { | |
1762 | + unsigned int state; | |
1763 | + state = aa_dfa_match(profile->policy.dfa, | |
1764 | + profile->policy.start[AA_CLASS_MOUNT], | |
1765 | + name); | |
1766 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
1767 | + } | |
1768 | + | |
1769 | + if (AA_MAY_UMOUNT & ~perms.allow) | |
1770 | + error = -EACCES; | |
1771 | + | |
1772 | +audit: | |
1773 | + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, | |
1774 | + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); | |
1775 | + kfree(buffer); | |
1776 | + | |
1777 | + return error; | |
1778 | +} | |
1779 | + | |
1780 | +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, | |
1781 | + struct path *new_path) | |
1782 | +{ | |
1783 | + struct file_perms perms = { }; | |
1784 | + struct aa_profile *target = NULL; | |
1785 | + char *old_buffer = NULL, *new_buffer = NULL; | |
1786 | + const char *old_name, *new_name = NULL, *info = NULL; | |
1787 | + int error; | |
1788 | + | |
1789 | + error = aa_path_name(old_path, path_flags(profile, old_path), | |
1790 | + &old_buffer, &old_name, &info); | |
1791 | + if (error) | |
1792 | + goto audit; | |
1793 | + | |
1794 | + error = aa_path_name(new_path, path_flags(profile, new_path), | |
1795 | + &new_buffer, &new_name, &info); | |
1796 | + if (error) | |
1797 | + goto audit; | |
1798 | + | |
1799 | + if (profile->policy.dfa) { | |
1800 | + unsigned int state; | |
1801 | + state = aa_dfa_match(profile->policy.dfa, | |
1802 | + profile->policy.start[AA_CLASS_MOUNT], | |
1803 | + new_name); | |
1804 | + state = aa_dfa_null_transition(profile->policy.dfa, state); | |
1805 | + state = aa_dfa_match(profile->policy.dfa, state, old_name); | |
1806 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
1807 | + } | |
1808 | + | |
1809 | + if (AA_MAY_PIVOTROOT & perms.allow) { | |
1810 | + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { | |
1811 | + target = x_table_lookup(profile, perms.xindex); | |
1812 | + if (!target) | |
1813 | + error = -ENOENT; | |
1814 | + else | |
1815 | + error = aa_replace_current_profile(target); | |
1816 | + } | |
1817 | + } else | |
1818 | + error = -EACCES; | |
1819 | + | |
1820 | +audit: | |
1821 | + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, | |
1822 | + old_name, NULL, target ? target->base.name : NULL, | |
1823 | + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); | |
1824 | + aa_put_profile(target); | |
1825 | + kfree(old_buffer); | |
1826 | + kfree(new_buffer); | |
1827 | + | |
1828 | + return error; | |
1829 | +} | |
0fa3db16 | 1830 | -- |
2dfbb274 | 1831 | 1.7.7.6 |
0fa3db16 | 1832 |