]>
Commit | Line | Data |
---|---|---|
76514441 AM |
1 | From 3f980257e048429a1f0a5dbce0b027a93c0781cc Mon Sep 17 00:00:00 2001 |
2 | From: John Johansen <john.johansen@canonical.com> | |
3 | Date: Wed, 4 Aug 2010 04:42:50 -0700 | |
4 | Subject: [PATCH] AppArmor: security module v2.6 + compat patches as of 29-07-2010 (security-next) | |
5 | ||
6 | AppArmor v2.6 module as synced to security-next 29-07-2010 backported to | |
7 | 2.6.35 + AppArmor 2.4 compatibility patches. | |
8 | ||
9 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
10 | --- | |
11 | Documentation/apparmor.txt | 40 + | |
12 | Documentation/kernel-parameters.txt | 8 + | |
13 | MAINTAINERS | 8 + | |
14 | include/linux/lsm_audit.h | 31 + | |
15 | security/Kconfig | 6 + | |
16 | security/Makefile | 2 + | |
17 | security/apparmor/.gitignore | 5 + | |
18 | security/apparmor/Kconfig | 40 + | |
19 | security/apparmor/Makefile | 30 + | |
20 | security/apparmor/apparmorfs-24.c | 287 +++++++ | |
21 | security/apparmor/apparmorfs.c | 253 ++++++ | |
22 | security/apparmor/audit.c | 215 ++++++ | |
23 | security/apparmor/capability.c | 141 ++++ | |
24 | security/apparmor/context.c | 216 ++++++ | |
25 | security/apparmor/domain.c | 823 ++++++++++++++++++++ | |
26 | security/apparmor/file.c | 457 +++++++++++ | |
27 | security/apparmor/include/apparmor.h | 92 +++ | |
28 | security/apparmor/include/apparmorfs.h | 26 + | |
29 | security/apparmor/include/audit.h | 123 +++ | |
30 | security/apparmor/include/capability.h | 45 ++ | |
31 | security/apparmor/include/context.h | 154 ++++ | |
32 | security/apparmor/include/domain.h | 36 + | |
33 | security/apparmor/include/file.h | 217 ++++++ | |
34 | security/apparmor/include/ipc.h | 28 + | |
35 | security/apparmor/include/match.h | 132 ++++ | |
36 | security/apparmor/include/net.h | 40 + | |
37 | security/apparmor/include/path.h | 31 + | |
38 | security/apparmor/include/policy.h | 308 ++++++++ | |
39 | security/apparmor/include/policy_unpack.h | 20 + | |
40 | security/apparmor/include/procattr.h | 26 + | |
41 | security/apparmor/include/resource.h | 46 ++ | |
42 | security/apparmor/include/sid.h | 24 + | |
43 | security/apparmor/ipc.c | 114 +++ | |
44 | security/apparmor/lib.c | 133 ++++ | |
45 | security/apparmor/lsm.c | 1051 +++++++++++++++++++++++++ | |
46 | security/apparmor/match.c | 370 +++++++++ | |
47 | security/apparmor/net.c | 169 ++++ | |
48 | security/apparmor/path.c | 235 ++++++ | |
49 | security/apparmor/policy.c | 1185 +++++++++++++++++++++++++++++ | |
50 | security/apparmor/policy_unpack.c | 740 ++++++++++++++++++ | |
51 | security/apparmor/policy_unpack.c.rej | 11 + | |
52 | security/apparmor/procattr.c | 170 ++++ | |
53 | security/apparmor/resource.c | 134 ++++ | |
54 | security/apparmor/sid.c | 55 ++ | |
55 | 44 files changed, 8277 insertions(+), 0 deletions(-) | |
56 | create mode 100644 Documentation/apparmor.txt | |
57 | create mode 100644 security/apparmor/.gitignore | |
58 | create mode 100644 security/apparmor/Kconfig | |
59 | create mode 100644 security/apparmor/Makefile | |
60 | create mode 100644 security/apparmor/apparmorfs-24.c | |
61 | create mode 100644 security/apparmor/apparmorfs.c | |
62 | create mode 100644 security/apparmor/audit.c | |
63 | create mode 100644 security/apparmor/capability.c | |
64 | create mode 100644 security/apparmor/context.c | |
65 | create mode 100644 security/apparmor/domain.c | |
66 | create mode 100644 security/apparmor/file.c | |
67 | create mode 100644 security/apparmor/include/apparmor.h | |
68 | create mode 100644 security/apparmor/include/apparmorfs.h | |
69 | create mode 100644 security/apparmor/include/audit.h | |
70 | create mode 100644 security/apparmor/include/capability.h | |
71 | create mode 100644 security/apparmor/include/context.h | |
72 | create mode 100644 security/apparmor/include/domain.h | |
73 | create mode 100644 security/apparmor/include/file.h | |
74 | create mode 100644 security/apparmor/include/ipc.h | |
75 | create mode 100644 security/apparmor/include/match.h | |
76 | create mode 100644 security/apparmor/include/net.h | |
77 | create mode 100644 security/apparmor/include/path.h | |
78 | create mode 100644 security/apparmor/include/policy.h | |
79 | create mode 100644 security/apparmor/include/policy_unpack.h | |
80 | create mode 100644 security/apparmor/include/procattr.h | |
81 | create mode 100644 security/apparmor/include/resource.h | |
82 | create mode 100644 security/apparmor/include/sid.h | |
83 | create mode 100644 security/apparmor/ipc.c | |
84 | create mode 100644 security/apparmor/lib.c | |
85 | create mode 100644 security/apparmor/lsm.c | |
86 | create mode 100644 security/apparmor/match.c | |
87 | create mode 100644 security/apparmor/net.c | |
88 | create mode 100644 security/apparmor/path.c | |
89 | create mode 100644 security/apparmor/policy.c | |
90 | create mode 100644 security/apparmor/policy_unpack.c | |
91 | create mode 100644 security/apparmor/policy_unpack.c.rej | |
92 | create mode 100644 security/apparmor/procattr.c | |
93 | create mode 100644 security/apparmor/resource.c | |
94 | create mode 100644 security/apparmor/sid.c | |
95 | ||
96 | diff --git a/Documentation/apparmor.txt b/Documentation/apparmor.txt | |
97 | new file mode 100644 | |
98 | index 0000000..6240438 | |
99 | --- /dev/null | |
100 | +++ b/Documentation/apparmor.txt | |
101 | @@ -0,0 +1,40 @@ | |
102 | +--- What is AppArmor? --- | |
103 | + | |
104 | +AppArmor is MAC style security extension for the Linux kernel. It implements | |
105 | +a task centered policy, with task "profiles" being created and loaded | |
106 | +from user space. Tasks on the system that do not have a profile defined for | |
107 | +them run in an unconfined state which is equivalent to standard Linux DAC | |
108 | +permissions. | |
109 | + | |
110 | +--- How to enable/disable --- | |
111 | + | |
112 | +set CONFIG_SECURITY_APPARMOR=y | |
113 | + | |
114 | +If AppArmor should be selected as the default security module then | |
115 | + set CONFIG_DEFAULT_SECURITY="apparmor" | |
116 | + and CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 | |
117 | + | |
118 | +Build the kernel | |
119 | + | |
120 | +If AppArmor is not the default security module it can be enabled by passing | |
121 | +security=apparmor on the kernel's command line. | |
122 | + | |
123 | +If AppArmor is the default security module it can be disabled by passing | |
124 | +apparmor=0, security=XXXX (where XXX is valid security module), on the | |
125 | +kernel's command line | |
126 | + | |
127 | +For AppArmor to enforce any restrictions beyond standard Linux DAC permissions | |
128 | +policy must be loaded into the kernel from user space (see the Documentation | |
129 | +and tools links). | |
130 | + | |
131 | +--- Documentation --- | |
132 | + | |
133 | +Documentation can be found on the wiki. | |
134 | + | |
135 | +--- Links --- | |
136 | + | |
137 | +Mailing List - apparmor@lists.ubuntu.com | |
138 | +Wiki - http://apparmor.wiki.kernel.org/ | |
139 | +User space tools - https://launchpad.net/apparmor | |
140 | +Kernel module - git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git | |
141 | + | |
142 | diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt | |
143 | index 2b2407d..b61f89f 100644 | |
144 | --- a/Documentation/kernel-parameters.txt | |
145 | +++ b/Documentation/kernel-parameters.txt | |
146 | @@ -93,6 +93,7 @@ parameter is applicable: | |
147 | Documentation/scsi/. | |
148 | SECURITY Different security models are enabled. | |
149 | SELINUX SELinux support is enabled. | |
150 | + APPARMOR AppArmor support is enabled. | |
151 | SERIAL Serial support is enabled. | |
152 | SH SuperH architecture is enabled. | |
153 | SMP The kernel is an SMP kernel. | |
154 | @@ -2312,6 +2313,13 @@ and is between 256 and 4096 characters. It is defined in the file | |
155 | If enabled at boot time, /selinux/disable can be used | |
156 | later to disable prior to initial policy load. | |
157 | ||
158 | + apparmor= [APPARMOR] Disable or enable AppArmor at boot time | |
159 | + Format: { "0" | "1" } | |
160 | + See security/apparmor/Kconfig help text | |
161 | + 0 -- disable. | |
162 | + 1 -- enable. | |
163 | + Default value is set via kernel config option. | |
164 | + | |
165 | serialnumber [BUGS=X86-32] | |
2380c486 | 166 | |
76514441 AM |
167 | shapers= [NET] |
168 | diff --git a/MAINTAINERS b/MAINTAINERS | |
169 | index 02f75fc..a8d5851 100644 | |
170 | --- a/MAINTAINERS | |
171 | +++ b/MAINTAINERS | |
172 | @@ -5061,6 +5061,14 @@ S: Supported | |
173 | F: include/linux/selinux* | |
174 | F: security/selinux/ | |
175 | ||
176 | +APPARMOR SECURITY MODULE | |
177 | +M: John Johansen <john.johansen@canonical.com> | |
178 | +L: apparmor@lists.ubuntu.com (subscribers-only, general discussion) | |
179 | +W: apparmor.wiki.kernel.org | |
180 | +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git | |
181 | +S: Supported | |
182 | +F: security/apparmor/ | |
183 | + | |
184 | SENSABLE PHANTOM | |
185 | M: Jiri Slaby <jirislaby@gmail.com> | |
186 | S: Maintained | |
187 | diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h | |
188 | index 6907251..3474e45 100644 | |
189 | --- a/include/linux/lsm_audit.h | |
190 | +++ b/include/linux/lsm_audit.h | |
191 | @@ -94,6 +94,37 @@ struct common_audit_data { | |
192 | int result; | |
193 | } selinux_audit_data; | |
194 | #endif | |
195 | +#ifdef CONFIG_SECURITY_APPARMOR | |
196 | + struct { | |
197 | + int error; | |
198 | + int op; | |
199 | + int type; | |
200 | + void *profile; | |
201 | + const char *name; | |
202 | + const char *info; | |
203 | + union { | |
204 | + void *target; | |
205 | + struct { | |
206 | + long pos; | |
207 | + void *target; | |
208 | + } iface; | |
209 | + struct { | |
210 | + int rlim; | |
211 | + unsigned long max; | |
212 | + } rlim; | |
213 | + struct { | |
214 | + const char *target; | |
215 | + u32 request; | |
216 | + u32 denied; | |
217 | + uid_t ouid; | |
218 | + } fs; | |
219 | + struct { | |
220 | + int type, protocol; | |
221 | + struct sock *sk; | |
222 | + } net; | |
223 | + }; | |
224 | + } apparmor_audit_data; | |
225 | +#endif | |
226 | }; | |
227 | /* these callback will be implemented by a specific LSM */ | |
228 | void (*lsm_pre_audit)(struct audit_buffer *, void *); | |
ceaf2cfb AM |
229 | diff --git a/security/Kconfig b/security/Kconfig |
230 | index 226b955..bd72ae6 100644 | |
231 | --- a/security/Kconfig | |
232 | +++ b/security/Kconfig | |
233 | @@ -140,6 +140,7 @@ config LSM_MMAP_MIN_ADDR | |
0eaba94d AM |
234 | source security/selinux/Kconfig |
235 | source security/smack/Kconfig | |
236 | source security/tomoyo/Kconfig | |
237 | +source security/apparmor/Kconfig | |
238 | ||
239 | source security/integrity/ima/Kconfig | |
240 | ||
ceaf2cfb AM |
241 | @@ -148,6 +149,7 @@ choice |
242 | default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX | |
243 | default DEFAULT_SECURITY_SMACK if SECURITY_SMACK | |
244 | default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO | |
245 | + default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR | |
246 | default DEFAULT_SECURITY_DAC | |
247 | ||
248 | help | |
249 | @@ -163,6 +165,9 @@ choice | |
250 | config DEFAULT_SECURITY_TOMOYO | |
251 | bool "TOMOYO" if SECURITY_TOMOYO=y | |
252 | ||
253 | + config DEFAULT_SECURITY_APPARMOR | |
254 | + bool "AppArmor" if SECURITY_APPARMOR=y | |
255 | + | |
256 | config DEFAULT_SECURITY_DAC | |
257 | bool "Unix Discretionary Access Controls" | |
258 | ||
259 | @@ -173,6 +178,7 @@ config DEFAULT_SECURITY | |
260 | default "selinux" if DEFAULT_SECURITY_SELINUX | |
261 | default "smack" if DEFAULT_SECURITY_SMACK | |
262 | default "tomoyo" if DEFAULT_SECURITY_TOMOYO | |
263 | + default "apparmor" if DEFAULT_SECURITY_APPARMOR | |
264 | default "" if DEFAULT_SECURITY_DAC | |
265 | ||
266 | endmenu | |
267 | diff --git a/security/Makefile b/security/Makefile | |
268 | index da20a19..8bb0fe9 100644 | |
269 | --- a/security/Makefile | |
270 | +++ b/security/Makefile | |
271 | @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/ | |
272 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux | |
273 | subdir-$(CONFIG_SECURITY_SMACK) += smack | |
274 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo | |
275 | +subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor | |
276 | ||
277 | # always enable default capabilities | |
278 | obj-y += commoncap.o | |
279 | @@ -19,6 +20,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o | |
280 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o | |
281 | obj-$(CONFIG_AUDIT) += lsm_audit.o | |
282 | obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o | |
283 | +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o | |
284 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | |
285 | ||
286 | # Object integrity file lists | |
287 | diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore | |
288 | new file mode 100644 | |
289 | index 0000000..0a0a99f | |
290 | --- /dev/null | |
291 | +++ b/security/apparmor/.gitignore | |
292 | @@ -0,0 +1,5 @@ | |
293 | +# | |
294 | +# Generated include files | |
295 | +# | |
296 | +af_names.h | |
297 | +capability_names.h | |
298 | diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig | |
299 | new file mode 100644 | |
76514441 | 300 | index 0000000..fdf3022 |
ceaf2cfb AM |
301 | --- /dev/null |
302 | +++ b/security/apparmor/Kconfig | |
303 | @@ -0,0 +1,40 @@ | |
304 | +config SECURITY_APPARMOR | |
305 | + bool "AppArmor support" | |
306 | + depends on SECURITY | |
307 | + select AUDIT | |
308 | + select SECURITY_PATH | |
309 | + select SECURITYFS | |
310 | + select SECURITY_NETWORK | |
311 | + default n | |
312 | + help | |
313 | + This enables the AppArmor security module. | |
314 | + Required userspace tools (if they are not included in your | |
315 | + distribution) and further information may be found at | |
76514441 | 316 | + http://apparmor.wiki.kernel.org |
ceaf2cfb AM |
317 | + |
318 | + If you are unsure how to answer this question, answer N. | |
319 | + | |
320 | +config SECURITY_APPARMOR_BOOTPARAM_VALUE | |
321 | + int "AppArmor boot parameter default value" | |
322 | + depends on SECURITY_APPARMOR | |
323 | + range 0 1 | |
324 | + default 1 | |
325 | + help | |
326 | + This option sets the default value for the kernel parameter | |
327 | + 'apparmor', which allows AppArmor to be enabled or disabled | |
328 | + at boot. If this option is set to 0 (zero), the AppArmor | |
329 | + kernel parameter will default to 0, disabling AppArmor at | |
76514441 | 330 | + boot. If this option is set to 1 (one), the AppArmor |
ceaf2cfb | 331 | + kernel parameter will default to 1, enabling AppArmor at |
76514441 | 332 | + boot. |
ceaf2cfb AM |
333 | + |
334 | + If you are unsure how to answer this question, answer 1. | |
335 | + | |
336 | +config SECURITY_APPARMOR_COMPAT_24 | |
337 | + bool "Enable AppArmor 2.4 compatability" | |
338 | + depends on SECURITY_APPARMOR | |
339 | + default y | |
340 | + help | |
341 | + This option enables compatability with AppArmor 2.4. It is | |
342 | + recommended if compatability with older versions of AppArmor | |
343 | + is desired. | |
344 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile | |
345 | new file mode 100644 | |
76514441 | 346 | index 0000000..e5e8968 |
ceaf2cfb AM |
347 | --- /dev/null |
348 | +++ b/security/apparmor/Makefile | |
76514441 | 349 | @@ -0,0 +1,30 @@ |
ceaf2cfb AM |
350 | +# Makefile for AppArmor Linux Security Module |
351 | +# | |
352 | +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |
353 | + | |
354 | +apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | |
355 | + path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | |
356 | + resource.o sid.o file.o net.o | |
357 | + | |
358 | +apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o | |
359 | + | |
360 | +clean-files: capability_names.h af_names.h | |
361 | + | |
362 | +quiet_cmd_make-caps = GEN $@ | |
363 | +cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ | |
364 | + | |
365 | +quiet_cmd_make-af = GEN $@ | |
366 | +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ | |
367 | + | |
76514441 AM |
368 | +quiet_cmd_make-rlim = GEN $@ |
369 | +cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ | |
370 | + | |
ceaf2cfb AM |
371 | +$(obj)/capability.o : $(obj)/capability_names.h |
372 | +$(obj)/net.o : $(obj)/af_names.h | |
76514441 | 373 | +$(obj)/resource.o : $(obj)/rlim_names.h |
ceaf2cfb AM |
374 | +$(obj)/capability_names.h : $(srctree)/include/linux/capability.h |
375 | + $(call cmd,make-caps) | |
376 | +$(obj)/af_names.h : $(srctree)/include/linux/socket.h | |
377 | + $(call cmd,make-af) | |
76514441 AM |
378 | +$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h |
379 | + $(call cmd,make-rlim) | |
ceaf2cfb AM |
380 | diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c |
381 | new file mode 100644 | |
76514441 | 382 | index 0000000..dc8c744 |
ceaf2cfb AM |
383 | --- /dev/null |
384 | +++ b/security/apparmor/apparmorfs-24.c | |
76514441 | 385 | @@ -0,0 +1,287 @@ |
9474138d AM |
386 | +/* |
387 | + * AppArmor security module | |
388 | + * | |
ceaf2cfb | 389 | + * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions |
9474138d AM |
390 | + * |
391 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 392 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
393 | + * |
394 | + * This program is free software; you can redistribute it and/or | |
395 | + * modify it under the terms of the GNU General Public License as | |
396 | + * published by the Free Software Foundation, version 2 of the | |
397 | + * License. | |
ceaf2cfb AM |
398 | + * |
399 | + * | |
400 | + * This file contain functions providing an interface for <= AppArmor 2.4 | |
401 | + * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24 | |
402 | + * being set (see Makefile). | |
9474138d AM |
403 | + */ |
404 | + | |
405 | +#include <linux/security.h> | |
406 | +#include <linux/vmalloc.h> | |
407 | +#include <linux/module.h> | |
408 | +#include <linux/seq_file.h> | |
409 | +#include <linux/uaccess.h> | |
410 | +#include <linux/namei.h> | |
2380c486 | 411 | + |
9474138d AM |
412 | +#include "include/apparmor.h" |
413 | +#include "include/audit.h" | |
414 | +#include "include/context.h" | |
415 | +#include "include/policy.h" | |
9474138d | 416 | + |
ceaf2cfb AM |
417 | + |
418 | +/* apparmor/matching */ | |
419 | +static ssize_t aa_matching_read(struct file *file, char __user *buf, | |
420 | + size_t size, loff_t *ppos) | |
421 | +{ | |
422 | + const char matching[] = "pattern=aadfa audit perms=crwxamlk/ " | |
423 | + "user::other"; | |
424 | + | |
425 | + return simple_read_from_buffer(buf, size, ppos, matching, | |
426 | + sizeof(matching) - 1); | |
427 | +} | |
428 | + | |
429 | +const struct file_operations aa_fs_matching_fops = { | |
430 | + .read = aa_matching_read, | |
431 | +}; | |
432 | + | |
433 | +/* apparmor/features */ | |
434 | +static ssize_t aa_features_read(struct file *file, char __user *buf, | |
435 | + size_t size, loff_t *ppos) | |
436 | +{ | |
437 | + const char features[] = "file=3.1 capability=2.0 network=1.0 " | |
438 | + "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1"; | |
439 | + | |
440 | + return simple_read_from_buffer(buf, size, ppos, features, | |
441 | + sizeof(features) - 1); | |
442 | +} | |
443 | + | |
444 | +const struct file_operations aa_fs_features_fops = { | |
445 | + .read = aa_features_read, | |
446 | +}; | |
ceaf2cfb AM |
447 | + |
448 | +/** | |
449 | + * __next_namespace - find the next namespace to list | |
450 | + * @root: root namespace to stop search at (NOT NULL) | |
451 | + * @ns: current ns position (NOT NULL) | |
452 | + * | |
453 | + * Find the next namespace from @ns under @root and handle all locking needed | |
454 | + * while switching current namespace. | |
455 | + * | |
456 | + * Returns: next namespace or NULL if at last namespace under @root | |
457 | + * NOTE: will not unlock root->lock | |
458 | + */ | |
459 | +static struct aa_namespace *__next_namespace(struct aa_namespace *root, | |
460 | + struct aa_namespace *ns) | |
9474138d | 461 | +{ |
ceaf2cfb | 462 | + struct aa_namespace *parent; |
9474138d | 463 | + |
ceaf2cfb AM |
464 | + /* is next namespace a child */ |
465 | + if (!list_empty(&ns->sub_ns)) { | |
466 | + struct aa_namespace *next; | |
467 | + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); | |
468 | + read_lock(&next->lock); | |
469 | + return next; | |
470 | + } | |
471 | + | |
472 | + /* check if the next ns is a sibling, parent, gp, .. */ | |
473 | + parent = ns->parent; | |
474 | + while (parent) { | |
475 | + read_unlock(&ns->lock); | |
476 | + list_for_each_entry_continue(ns, &parent->sub_ns, base.list) { | |
477 | + read_lock(&ns->lock); | |
478 | + return ns; | |
479 | + } | |
480 | + if (parent == root) | |
481 | + return NULL; | |
482 | + ns = parent; | |
483 | + parent = parent->parent; | |
484 | + } | |
485 | + | |
486 | + return NULL; | |
9474138d AM |
487 | +} |
488 | + | |
ceaf2cfb AM |
489 | +/** |
490 | + * __first_profile - find the first profile in a namespace | |
491 | + * @root: namespace that is root of profiles being displayed (NOT NULL) | |
492 | + * @ns: namespace to start in (NOT NULL) | |
493 | + * | |
494 | + * Returns: unrefcounted profile or NULL if no profile | |
495 | + */ | |
76514441 AM |
496 | +static struct aa_profile *__first_profile(struct aa_namespace *root, |
497 | + struct aa_namespace *ns) | |
9474138d | 498 | +{ |
ceaf2cfb AM |
499 | + for ( ; ns; ns = __next_namespace(root, ns)) { |
500 | + if (!list_empty(&ns->base.profiles)) | |
501 | + return list_first_entry(&ns->base.profiles, | |
502 | + struct aa_profile, base.list); | |
9474138d | 503 | + } |
ceaf2cfb | 504 | + return NULL; |
9474138d AM |
505 | +} |
506 | + | |
ceaf2cfb AM |
507 | +/** |
508 | + * __next_profile - step to the next profile in a profile tree | |
509 | + * @profile: current profile in tree (NOT NULL) | |
510 | + * | |
511 | + * Perform a depth first taversal on the profile tree in a namespace | |
512 | + * | |
513 | + * Returns: next profile or NULL if done | |
514 | + * Requires: profile->ns.lock to be held | |
515 | + */ | |
516 | +static struct aa_profile *__next_profile(struct aa_profile *p) | |
9474138d | 517 | +{ |
ceaf2cfb AM |
518 | + struct aa_profile *parent; |
519 | + struct aa_namespace *ns = p->ns; | |
9474138d | 520 | + |
ceaf2cfb AM |
521 | + /* is next profile a child */ |
522 | + if (!list_empty(&p->base.profiles)) | |
523 | + return list_first_entry(&p->base.profiles, typeof(*p), | |
524 | + base.list); | |
9474138d | 525 | + |
ceaf2cfb AM |
526 | + /* is next profile a sibling, parent sibling, gp, subling, .. */ |
527 | + parent = p->parent; | |
528 | + while (parent) { | |
529 | + list_for_each_entry_continue(p, &parent->base.profiles, | |
530 | + base.list) | |
531 | + return p; | |
532 | + p = parent; | |
533 | + parent = parent->parent; | |
534 | + } | |
9474138d | 535 | + |
ceaf2cfb AM |
536 | + /* is next another profile in the namespace */ |
537 | + list_for_each_entry_continue(p, &ns->base.profiles, base.list) | |
538 | + return p; | |
9474138d | 539 | + |
ceaf2cfb | 540 | + return NULL; |
9474138d AM |
541 | +} |
542 | + | |
ceaf2cfb AM |
543 | +/** |
544 | + * next_profile - step to the next profile in where ever it may be | |
545 | + * @root: root namespace (NOT NULL) | |
546 | + * @profile: current profile (NOT NULL) | |
547 | + * | |
548 | + * Returns: next profile or NULL if there isn't one | |
549 | + */ | |
550 | +static struct aa_profile *next_profile(struct aa_namespace *root, | |
551 | + struct aa_profile *profile) | |
9474138d | 552 | +{ |
ceaf2cfb AM |
553 | + struct aa_profile *next = __next_profile(profile); |
554 | + if (next) | |
555 | + return next; | |
9474138d | 556 | + |
ceaf2cfb AM |
557 | + /* finished all profiles in namespace move to next namespace */ |
558 | + return __first_profile(root, __next_namespace(root, profile->ns)); | |
559 | +} | |
9474138d | 560 | + |
ceaf2cfb AM |
561 | +/** |
562 | + * p_start - start a depth first traversal of profile tree | |
563 | + * @f: seq_file to fill | |
564 | + * @pos: current position | |
565 | + * | |
566 | + * Returns: first profile under current namespace or NULL if none found | |
567 | + * | |
568 | + * acquires first ns->lock | |
569 | + */ | |
570 | +static void *p_start(struct seq_file *f, loff_t *pos) | |
571 | + __acquires(root->lock) | |
9474138d | 572 | +{ |
ceaf2cfb AM |
573 | + struct aa_profile *profile = NULL; |
574 | + struct aa_namespace *root = aa_current_profile()->ns; | |
575 | + loff_t l = *pos; | |
576 | + f->private = aa_get_namespace(root); | |
9474138d | 577 | + |
9474138d | 578 | + |
ceaf2cfb AM |
579 | + /* find the first profile */ |
580 | + read_lock(&root->lock); | |
581 | + profile = __first_profile(root, root); | |
9474138d | 582 | + |
ceaf2cfb AM |
583 | + /* skip to position */ |
584 | + for (; profile && l > 0; l--) | |
585 | + profile = next_profile(root, profile); | |
9474138d | 586 | + |
ceaf2cfb | 587 | + return profile; |
9474138d AM |
588 | +} |
589 | + | |
ceaf2cfb AM |
590 | +/** |
591 | + * p_next - read the next profile entry | |
592 | + * @f: seq_file to fill | |
593 | + * @p: profile previously returned | |
594 | + * @pos: current position | |
595 | + * | |
596 | + * Returns: next profile after @p or NULL if none | |
597 | + * | |
598 | + * may acquire/release locks in namespace tree as necessary | |
599 | + */ | |
600 | +static void *p_next(struct seq_file *f, void *p, loff_t *pos) | |
9474138d | 601 | +{ |
ceaf2cfb AM |
602 | + struct aa_profile *profile = p; |
603 | + struct aa_namespace *root = f->private; | |
604 | + (*pos)++; | |
9474138d | 605 | + |
ceaf2cfb AM |
606 | + return next_profile(root, profile); |
607 | +} | |
9474138d | 608 | + |
ceaf2cfb AM |
609 | +/** |
610 | + * p_stop - stop depth first traversal | |
611 | + * @f: seq_file we are filling | |
612 | + * @p: the last profile writen | |
613 | + * | |
614 | + * Release all locking done by p_start/p_next on namespace tree | |
615 | + */ | |
616 | +static void p_stop(struct seq_file *f, void *p) | |
617 | + __releases(root->lock) | |
618 | +{ | |
619 | + struct aa_profile *profile = p; | |
620 | + struct aa_namespace *root = f->private, *ns; | |
9474138d | 621 | + |
ceaf2cfb AM |
622 | + if (profile) { |
623 | + for (ns = profile->ns; ns && ns != root; ns = ns->parent) | |
624 | + read_unlock(&ns->lock); | |
625 | + } | |
626 | + read_unlock(&root->lock); | |
627 | + aa_put_namespace(root); | |
9474138d AM |
628 | +} |
629 | + | |
ceaf2cfb | 630 | +/** |
76514441 | 631 | + * seq_show_profile - show a profile entry |
ceaf2cfb AM |
632 | + * @f: seq_file to file |
633 | + * @p: current position (profile) (NOT NULL) | |
634 | + * | |
635 | + * Returns: error on failure | |
636 | + */ | |
637 | +static int seq_show_profile(struct seq_file *f, void *p) | |
9474138d | 638 | +{ |
ceaf2cfb AM |
639 | + struct aa_profile *profile = (struct aa_profile *)p; |
640 | + struct aa_namespace *root = f->private; | |
9474138d | 641 | + |
ceaf2cfb | 642 | + if (profile->ns != root) |
76514441 | 643 | + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); |
ceaf2cfb AM |
644 | + seq_printf(f, "%s (%s)\n", profile->base.hname, |
645 | + COMPLAIN_MODE(profile) ? "complain" : "enforce"); | |
9474138d | 646 | + |
ceaf2cfb | 647 | + return 0; |
9474138d AM |
648 | +} |
649 | + | |
ceaf2cfb AM |
650 | +static const struct seq_operations aa_fs_profiles_op = { |
651 | + .start = p_start, | |
652 | + .next = p_next, | |
653 | + .stop = p_stop, | |
654 | + .show = seq_show_profile, | |
9474138d AM |
655 | +}; |
656 | + | |
76514441 AM |
657 | +static int profiles_open(struct inode *inode, struct file *file) |
658 | +{ | |
659 | + return seq_open(file, &aa_fs_profiles_op); | |
660 | +} | |
661 | + | |
662 | +static int profiles_release(struct inode *inode, struct file *file) | |
663 | +{ | |
664 | + return seq_release(inode, file); | |
665 | +} | |
666 | + | |
667 | +const struct file_operations aa_fs_profiles_fops = { | |
668 | + .open = profiles_open, | |
669 | + .read = seq_read, | |
670 | + .llseek = seq_lseek, | |
671 | + .release = profiles_release, | |
672 | +}; | |
673 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
674 | new file mode 100644 | |
675 | index 0000000..0e27449 | |
676 | --- /dev/null | |
677 | +++ b/security/apparmor/apparmorfs.c | |
678 | @@ -0,0 +1,253 @@ | |
679 | +/* | |
680 | + * AppArmor security module | |
681 | + * | |
682 | + * This file contains AppArmor /sys/kernel/security/apparmor interface functions | |
683 | + * | |
684 | + * Copyright (C) 1998-2008 Novell/SUSE | |
685 | + * Copyright 2009-2010 Canonical Ltd. | |
686 | + * | |
687 | + * This program is free software; you can redistribute it and/or | |
688 | + * modify it under the terms of the GNU General Public License as | |
689 | + * published by the Free Software Foundation, version 2 of the | |
690 | + * License. | |
691 | + */ | |
692 | + | |
693 | +#include <linux/security.h> | |
694 | +#include <linux/vmalloc.h> | |
695 | +#include <linux/module.h> | |
696 | +#include <linux/seq_file.h> | |
697 | +#include <linux/uaccess.h> | |
698 | +#include <linux/namei.h> | |
699 | + | |
700 | +#include "include/apparmor.h" | |
701 | +#include "include/apparmorfs.h" | |
702 | +#include "include/audit.h" | |
703 | +#include "include/context.h" | |
704 | +#include "include/policy.h" | |
705 | + | |
706 | +/** | |
707 | + * aa_simple_write_to_buffer - common routine for getting policy from user | |
708 | + * @op: operation doing the user buffer copy | |
709 | + * @userbuf: user buffer to copy data from (NOT NULL) | |
710 | + * @alloc_size: size of user buffer | |
711 | + * @copy_size: size of data to copy from user buffer | |
712 | + * @pos: position write is at in the file (NOT NULL) | |
713 | + * | |
714 | + * Returns: kernel buffer containing copy of user buffer data or an | |
715 | + * ERR_PTR on failure. | |
716 | + */ | |
717 | +static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, | |
718 | + size_t alloc_size, size_t copy_size, | |
719 | + loff_t *pos) | |
720 | +{ | |
721 | + char *data; | |
722 | + | |
723 | + if (*pos != 0) | |
724 | + /* only writes from pos 0, that is complete writes */ | |
725 | + return ERR_PTR(-ESPIPE); | |
726 | + | |
727 | + /* | |
728 | + * Don't allow profile load/replace/remove from profiles that don't | |
729 | + * have CAP_MAC_ADMIN | |
730 | + */ | |
731 | + if (!aa_may_manage_policy(op)) | |
732 | + return ERR_PTR(-EACCES); | |
733 | + | |
734 | + /* freed by caller to simple_write_to_buffer */ | |
735 | + data = kvmalloc(alloc_size); | |
736 | + if (data == NULL) | |
737 | + return ERR_PTR(-ENOMEM); | |
738 | + | |
739 | + if (copy_from_user(data, userbuf, copy_size)) { | |
740 | + kvfree(data); | |
741 | + return ERR_PTR(-EFAULT); | |
742 | + } | |
743 | + | |
744 | + return data; | |
745 | +} | |
746 | + | |
747 | + | |
748 | +/* .load file hook fn to load policy */ | |
749 | +static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, | |
750 | + loff_t *pos) | |
751 | +{ | |
752 | + char *data; | |
753 | + ssize_t error; | |
754 | + | |
755 | + data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); | |
756 | + | |
757 | + error = PTR_ERR(data); | |
758 | + if (!IS_ERR(data)) { | |
759 | + error = aa_replace_profiles(data, size, PROF_ADD); | |
760 | + kvfree(data); | |
761 | + } | |
762 | + | |
763 | + return error; | |
764 | +} | |
765 | + | |
766 | +static const struct file_operations aa_fs_profile_load = { | |
767 | + .write = profile_load | |
768 | +}; | |
769 | + | |
770 | +/* .replace file hook fn to load and/or replace policy */ | |
771 | +static ssize_t profile_replace(struct file *f, const char __user *buf, | |
772 | + size_t size, loff_t *pos) | |
773 | +{ | |
774 | + char *data; | |
775 | + ssize_t error; | |
776 | + | |
777 | + data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); | |
778 | + error = PTR_ERR(data); | |
779 | + if (!IS_ERR(data)) { | |
780 | + error = aa_replace_profiles(data, size, PROF_REPLACE); | |
781 | + kvfree(data); | |
782 | + } | |
783 | + | |
784 | + return error; | |
785 | +} | |
786 | + | |
787 | +static const struct file_operations aa_fs_profile_replace = { | |
788 | + .write = profile_replace | |
789 | +}; | |
790 | + | |
791 | +/* .remove file hook fn to remove loaded policy */ | |
792 | +static ssize_t profile_remove(struct file *f, const char __user *buf, | |
793 | + size_t size, loff_t *pos) | |
794 | +{ | |
795 | + char *data; | |
796 | + ssize_t error; | |
797 | + | |
798 | + /* | |
799 | + * aa_remove_profile needs a null terminated string so 1 extra | |
800 | + * byte is allocated and the copied data is null terminated. | |
801 | + */ | |
802 | + data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); | |
9474138d | 803 | + |
76514441 AM |
804 | + error = PTR_ERR(data); |
805 | + if (!IS_ERR(data)) { | |
806 | + data[size] = 0; | |
807 | + error = aa_remove_profiles(data, size); | |
808 | + kvfree(data); | |
809 | + } | |
810 | + | |
811 | + return error; | |
9474138d AM |
812 | +} |
813 | + | |
76514441 AM |
814 | +static const struct file_operations aa_fs_profile_remove = { |
815 | + .write = profile_remove | |
9474138d AM |
816 | +}; |
817 | + | |
ceaf2cfb AM |
818 | +/** Base file system setup **/ |
819 | + | |
76514441 | 820 | +static struct dentry *aa_fs_dentry __initdata; |
9474138d | 821 | + |
76514441 | 822 | +static void __init aafs_remove(const char *name) |
9474138d AM |
823 | +{ |
824 | + struct dentry *dentry; | |
825 | + | |
ceaf2cfb | 826 | + dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); |
9474138d AM |
827 | + if (!IS_ERR(dentry)) { |
828 | + securityfs_remove(dentry); | |
829 | + dput(dentry); | |
830 | + } | |
831 | +} | |
832 | + | |
ceaf2cfb AM |
833 | +/** |
834 | + * aafs_create - create an entry in the apparmor filesystem | |
76514441 | 835 | + * @name: name of the entry (NOT NULL) |
ceaf2cfb | 836 | + * @mask: file permission mask of the file |
76514441 | 837 | + * @fops: file operations for the file (NOT NULL) |
ceaf2cfb AM |
838 | + * |
839 | + * Used aafs_remove to remove entries created with this fn. | |
840 | + */ | |
76514441 AM |
841 | +static int __init aafs_create(const char *name, int mask, |
842 | + const struct file_operations *fops) | |
9474138d AM |
843 | +{ |
844 | + struct dentry *dentry; | |
845 | + | |
ceaf2cfb | 846 | + dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, |
9474138d AM |
847 | + NULL, fops); |
848 | + | |
849 | + return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; | |
850 | +} | |
851 | + | |
ceaf2cfb AM |
852 | +/** |
853 | + * aa_destroy_aafs - cleanup and free aafs | |
854 | + * | |
855 | + * releases dentries allocated by aa_create_aafs | |
856 | + */ | |
76514441 | 857 | +void __init aa_destroy_aafs(void) |
9474138d | 858 | +{ |
ceaf2cfb | 859 | + if (aa_fs_dentry) { |
9474138d AM |
860 | + aafs_remove(".remove"); |
861 | + aafs_remove(".replace"); | |
862 | + aafs_remove(".load"); | |
ceaf2cfb | 863 | +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 |
76514441 | 864 | + aafs_remove("profiles"); |
9474138d AM |
865 | + aafs_remove("matching"); |
866 | + aafs_remove("features"); | |
ceaf2cfb AM |
867 | +#endif |
868 | + securityfs_remove(aa_fs_dentry); | |
869 | + aa_fs_dentry = NULL; | |
9474138d AM |
870 | + } |
871 | +} | |
872 | + | |
ceaf2cfb AM |
873 | +/** |
874 | + * aa_create_aafs - create the apparmor security filesystem | |
875 | + * | |
876 | + * dentries created here are released by aa_destroy_aafs | |
877 | + * | |
878 | + * Returns: error on failure | |
879 | + */ | |
76514441 | 880 | +int __init aa_create_aafs(void) |
9474138d AM |
881 | +{ |
882 | + int error; | |
883 | + | |
884 | + if (!apparmor_initialized) | |
885 | + return 0; | |
886 | + | |
ceaf2cfb | 887 | + if (aa_fs_dentry) { |
9474138d AM |
888 | + AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); |
889 | + return -EEXIST; | |
890 | + } | |
891 | + | |
ceaf2cfb AM |
892 | + aa_fs_dentry = securityfs_create_dir("apparmor", NULL); |
893 | + if (IS_ERR(aa_fs_dentry)) { | |
894 | + error = PTR_ERR(aa_fs_dentry); | |
895 | + aa_fs_dentry = NULL; | |
9474138d AM |
896 | + goto error; |
897 | + } | |
ceaf2cfb AM |
898 | +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 |
899 | + error = aafs_create("matching", 0444, &aa_fs_matching_fops); | |
9474138d AM |
900 | + if (error) |
901 | + goto error; | |
ceaf2cfb | 902 | + error = aafs_create("features", 0444, &aa_fs_features_fops); |
9474138d AM |
903 | + if (error) |
904 | + goto error; | |
ceaf2cfb AM |
905 | +#endif |
906 | + error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); | |
9474138d AM |
907 | + if (error) |
908 | + goto error; | |
ceaf2cfb | 909 | + error = aafs_create(".load", 0640, &aa_fs_profile_load); |
9474138d AM |
910 | + if (error) |
911 | + goto error; | |
ceaf2cfb | 912 | + error = aafs_create(".replace", 0640, &aa_fs_profile_replace); |
9474138d AM |
913 | + if (error) |
914 | + goto error; | |
ceaf2cfb | 915 | + error = aafs_create(".remove", 0640, &aa_fs_profile_remove); |
9474138d AM |
916 | + if (error) |
917 | + goto error; | |
918 | + | |
919 | + /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | |
920 | + | |
921 | + /* Report that AppArmor fs is enabled */ | |
ceaf2cfb | 922 | + aa_info_message("AppArmor Filesystem Enabled"); |
9474138d AM |
923 | + return 0; |
924 | + | |
925 | +error: | |
ceaf2cfb | 926 | + aa_destroy_aafs(); |
9474138d | 927 | + AA_ERROR("Error creating AppArmor securityfs\n"); |
9474138d AM |
928 | + return error; |
929 | +} | |
930 | + | |
ceaf2cfb AM |
931 | +fs_initcall(aa_create_aafs); |
932 | diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c | |
933 | new file mode 100644 | |
76514441 | 934 | index 0000000..96502b2 |
ceaf2cfb AM |
935 | --- /dev/null |
936 | +++ b/security/apparmor/audit.c | |
76514441 | 937 | @@ -0,0 +1,215 @@ |
9474138d AM |
938 | +/* |
939 | + * AppArmor security module | |
940 | + * | |
941 | + * This file contains AppArmor auditing functions | |
942 | + * | |
943 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 944 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
945 | + * |
946 | + * This program is free software; you can redistribute it and/or | |
947 | + * modify it under the terms of the GNU General Public License as | |
948 | + * published by the Free Software Foundation, version 2 of the | |
949 | + * License. | |
950 | + */ | |
951 | + | |
952 | +#include <linux/audit.h> | |
953 | +#include <linux/socket.h> | |
954 | + | |
955 | +#include "include/apparmor.h" | |
956 | +#include "include/audit.h" | |
957 | +#include "include/policy.h" | |
958 | + | |
76514441 AM |
959 | +const char *op_table[] = { |
960 | + "null", | |
961 | + | |
962 | + "sysctl", | |
963 | + "capable", | |
964 | + | |
965 | + "unlink", | |
966 | + "mkdir", | |
967 | + "rmdir", | |
968 | + "mknod", | |
969 | + "truncate", | |
970 | + "link", | |
971 | + "symlink", | |
972 | + "rename_src", | |
973 | + "rename_dest", | |
974 | + "chmod", | |
975 | + "chown", | |
976 | + "getattr", | |
977 | + "open", | |
978 | + | |
979 | + "file_perm", | |
980 | + "file_lock", | |
981 | + "file_mmap", | |
982 | + "file_mprotect", | |
983 | + | |
984 | + "create", | |
985 | + "post_create", | |
986 | + "bind", | |
987 | + "connect", | |
988 | + "listen", | |
989 | + "accept", | |
990 | + "sendmsg", | |
991 | + "recvmsg", | |
992 | + "getsockname", | |
993 | + "getpeername", | |
994 | + "getsockopt", | |
995 | + "setsockopt", | |
996 | + "socket_shutdown", | |
997 | + | |
998 | + "ptrace", | |
999 | + | |
1000 | + "exec", | |
1001 | + "change_hat", | |
1002 | + "change_profile", | |
1003 | + "change_onexec", | |
1004 | + | |
1005 | + "setprocattr", | |
1006 | + "setrlimit", | |
1007 | + | |
1008 | + "profile_replace", | |
1009 | + "profile_load", | |
1010 | + "profile_remove" | |
1011 | +}; | |
1012 | + | |
9474138d AM |
1013 | +const char *audit_mode_names[] = { |
1014 | + "normal", | |
1015 | + "quiet_denied", | |
ceaf2cfb | 1016 | + "quiet", |
9474138d AM |
1017 | + "noquiet", |
1018 | + "all" | |
1019 | +}; | |
1020 | + | |
ceaf2cfb | 1021 | +static char *aa_audit_type[] = { |
76514441 AM |
1022 | + "AUDIT", |
1023 | + "ALLOWED", | |
1024 | + "DENIED", | |
1025 | + "HINT", | |
1026 | + "STATUS", | |
1027 | + "ERROR", | |
1028 | + "KILLED" | |
9474138d AM |
1029 | +}; |
1030 | + | |
1031 | +/* | |
ceaf2cfb AM |
1032 | + * Currently AppArmor auditing is fed straight into the audit framework. |
1033 | + * | |
9474138d | 1034 | + * TODO: |
ceaf2cfb AM |
1035 | + * netlink interface for complain mode |
1036 | + * user auditing, - send user auditing to netlink interface | |
9474138d AM |
1037 | + * system control of whether user audit messages go to system log |
1038 | + */ | |
ceaf2cfb AM |
1039 | + |
1040 | +/** | |
76514441 AM |
1041 | + * audit_base - core AppArmor function. |
1042 | + * @ab: audit buffer to fill (NOT NULL) | |
1043 | + * @ca: audit structure containing data to audit (NOT NULL) | |
ceaf2cfb | 1044 | + * |
76514441 | 1045 | + * Record common AppArmor audit data from @sa |
ceaf2cfb | 1046 | + */ |
76514441 | 1047 | +static void audit_pre(struct audit_buffer *ab, void *ca) |
9474138d | 1048 | +{ |
76514441 AM |
1049 | + struct common_audit_data *sa = ca; |
1050 | + struct task_struct *tsk = sa->tsk ? sa->tsk : current; | |
9474138d | 1051 | + |
ceaf2cfb | 1052 | + if (aa_g_audit_header) { |
76514441 AM |
1053 | + audit_log_format(ab, "apparmor="); |
1054 | + audit_log_string(ab, aa_audit_type[sa->aad.type]); | |
ceaf2cfb | 1055 | + } |
9474138d | 1056 | + |
76514441 | 1057 | + if (sa->aad.op) { |
ceaf2cfb | 1058 | + audit_log_format(ab, " operation="); |
76514441 | 1059 | + audit_log_string(ab, op_table[sa->aad.op]); |
ceaf2cfb | 1060 | + } |
9474138d | 1061 | + |
76514441 | 1062 | + if (sa->aad.info) { |
ceaf2cfb | 1063 | + audit_log_format(ab, " info="); |
76514441 AM |
1064 | + audit_log_string(ab, sa->aad.info); |
1065 | + if (sa->aad.error) | |
1066 | + audit_log_format(ab, " error=%d", sa->aad.error); | |
9474138d AM |
1067 | + } |
1068 | + | |
76514441 AM |
1069 | + if (sa->aad.profile) { |
1070 | + struct aa_profile *profile = sa->aad.profile; | |
ceaf2cfb AM |
1071 | + pid_t pid; |
1072 | + rcu_read_lock(); | |
76514441 | 1073 | + pid = tsk->real_parent->pid; |
ceaf2cfb | 1074 | + rcu_read_unlock(); |
9474138d | 1075 | + audit_log_format(ab, " parent=%d", pid); |
ceaf2cfb | 1076 | + if (profile->ns != root_ns) { |
9474138d | 1077 | + audit_log_format(ab, " namespace="); |
ceaf2cfb | 1078 | + audit_log_untrustedstring(ab, profile->ns->base.hname); |
9474138d | 1079 | + } |
76514441 AM |
1080 | + audit_log_format(ab, " profile="); |
1081 | + audit_log_untrustedstring(ab, profile->base.hname); | |
9474138d AM |
1082 | + } |
1083 | + | |
76514441 AM |
1084 | + if (sa->aad.name) { |
1085 | + audit_log_format(ab, " name="); | |
1086 | + audit_log_untrustedstring(ab, sa->aad.name); | |
1087 | + } | |
1088 | +} | |
9474138d | 1089 | + |
76514441 AM |
1090 | +/** |
1091 | + * aa_audit_msg - Log a message to the audit subsystem | |
1092 | + * @sa: audit event structure (NOT NULL) | |
1093 | + * @cb: optional callback fn for type specific fields (MAYBE NULL) | |
1094 | + */ | |
1095 | +void aa_audit_msg(int type, struct common_audit_data *sa, | |
1096 | + void (*cb) (struct audit_buffer *, void *)) | |
1097 | +{ | |
1098 | + sa->aad.type = type; | |
1099 | + sa->lsm_pre_audit = audit_pre; | |
1100 | + sa->lsm_post_audit = cb; | |
1101 | + common_lsm_audit(sa); | |
9474138d AM |
1102 | +} |
1103 | + | |
1104 | +/** | |
76514441 | 1105 | + * aa_audit - Log a profile based audit event to the audit subsystem |
9474138d | 1106 | + * @type: audit type for the message |
76514441 AM |
1107 | + * @profile: profile to check against (NOT NULL) |
1108 | + * @gfp: allocation flags to use | |
1109 | + * @sa: audit event (NOT NULL) | |
1110 | + * @cb: optional callback fn for type specific fields (MAYBE NULL) | |
ceaf2cfb AM |
1111 | + * |
1112 | + * Handle default message switching based off of audit mode flags | |
1113 | + * | |
1114 | + * Returns: error on failure | |
9474138d | 1115 | + */ |
76514441 AM |
1116 | +int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, |
1117 | + struct common_audit_data *sa, | |
1118 | + void (*cb) (struct audit_buffer *, void *)) | |
9474138d | 1119 | +{ |
76514441 | 1120 | + BUG_ON(!profile); |
9474138d AM |
1121 | + |
1122 | + if (type == AUDIT_APPARMOR_AUTO) { | |
76514441 | 1123 | + if (likely(!sa->aad.error)) { |
ceaf2cfb | 1124 | + if (AUDIT_MODE(profile) != AUDIT_ALL) |
5e665963 | 1125 | + return 0; |
9474138d | 1126 | + type = AUDIT_APPARMOR_AUDIT; |
ceaf2cfb | 1127 | + } else if (COMPLAIN_MODE(profile)) |
9474138d AM |
1128 | + type = AUDIT_APPARMOR_ALLOWED; |
1129 | + else | |
1130 | + type = AUDIT_APPARMOR_DENIED; | |
1131 | + } | |
ceaf2cfb | 1132 | + if (AUDIT_MODE(profile) == AUDIT_QUIET || |
9474138d | 1133 | + (type == AUDIT_APPARMOR_DENIED && |
ceaf2cfb | 1134 | + AUDIT_MODE(profile) == AUDIT_QUIET)) |
76514441 AM |
1135 | + return sa->aad.error; |
1136 | + | |
1137 | + if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | |
1138 | + type = AUDIT_APPARMOR_KILL; | |
1139 | + | |
1140 | + if (!unconfined(profile)) | |
1141 | + sa->aad.profile = profile; | |
1142 | + | |
1143 | + aa_audit_msg(type, sa, cb); | |
9474138d | 1144 | + |
76514441 AM |
1145 | + if (sa->aad.type == AUDIT_APPARMOR_KILL) |
1146 | + (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); | |
1147 | + | |
1148 | + if (sa->aad.type == AUDIT_APPARMOR_ALLOWED) | |
1149 | + return complain_error(sa->aad.error); | |
1150 | + | |
1151 | + return sa->aad.error; | |
ceaf2cfb AM |
1152 | +} |
1153 | diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c | |
1154 | new file mode 100644 | |
76514441 | 1155 | index 0000000..9982c48 |
ceaf2cfb AM |
1156 | --- /dev/null |
1157 | +++ b/security/apparmor/capability.c | |
76514441 | 1158 | @@ -0,0 +1,141 @@ |
9474138d AM |
1159 | +/* |
1160 | + * AppArmor security module | |
1161 | + * | |
1162 | + * This file contains AppArmor capability mediation functions | |
1163 | + * | |
1164 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 1165 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
1166 | + * |
1167 | + * This program is free software; you can redistribute it and/or | |
1168 | + * modify it under the terms of the GNU General Public License as | |
1169 | + * published by the Free Software Foundation, version 2 of the | |
1170 | + * License. | |
1171 | + */ | |
1172 | + | |
1173 | +#include <linux/capability.h> | |
1174 | +#include <linux/errno.h> | |
1175 | +#include <linux/gfp.h> | |
1176 | + | |
1177 | +#include "include/apparmor.h" | |
1178 | +#include "include/capability.h" | |
1179 | +#include "include/context.h" | |
1180 | +#include "include/policy.h" | |
1181 | +#include "include/audit.h" | |
1182 | + | |
1183 | +/* | |
1184 | + * Table of capability names: we generate it from capabilities.h. | |
1185 | + */ | |
1186 | +#include "capability_names.h" | |
1187 | + | |
1188 | +struct audit_cache { | |
ceaf2cfb | 1189 | + struct aa_profile *profile; |
9474138d AM |
1190 | + kernel_cap_t caps; |
1191 | +}; | |
1192 | + | |
1193 | +static DEFINE_PER_CPU(struct audit_cache, audit_cache); | |
1194 | + | |
ceaf2cfb AM |
1195 | +/** |
1196 | + * audit_cb - call back for capability components of audit struct | |
1197 | + * @ab - audit buffer (NOT NULL) | |
1198 | + * @va - audit struct to audit data from (NOT NULL) | |
1199 | + */ | |
76514441 | 1200 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 1201 | +{ |
76514441 AM |
1202 | + struct common_audit_data *sa = va; |
1203 | + audit_log_format(ab, " capname="); | |
1204 | + audit_log_untrustedstring(ab, capability_names[sa->u.cap]); | |
9474138d AM |
1205 | +} |
1206 | + | |
ceaf2cfb | 1207 | +/** |
76514441 AM |
1208 | + * audit_caps - audit a capability |
1209 | + * @profile: profile confining task (NOT NULL) | |
1210 | + * @task: task capability test was performed against (NOT NULL) | |
1211 | + * @cap: capability tested | |
1212 | + * @error: error code returned by test | |
ceaf2cfb AM |
1213 | + * |
1214 | + * Do auditing of capability and handle, audit/complain/kill modes switching | |
1215 | + * and duplicate message elimination. | |
1216 | + * | |
76514441 | 1217 | + * Returns: 0 or sa->error on success, error code on failure |
ceaf2cfb | 1218 | + */ |
76514441 AM |
1219 | +static int audit_caps(struct aa_profile *profile, struct task_struct *task, |
1220 | + int cap, int error) | |
9474138d AM |
1221 | +{ |
1222 | + struct audit_cache *ent; | |
1223 | + int type = AUDIT_APPARMOR_AUTO; | |
76514441 AM |
1224 | + struct common_audit_data sa; |
1225 | + COMMON_AUDIT_DATA_INIT(&sa, CAP); | |
1226 | + sa.tsk = task; | |
1227 | + sa.u.cap = cap; | |
1228 | + sa.aad.op = OP_CAPABLE; | |
1229 | + sa.aad.error = error; | |
1230 | + | |
1231 | + if (likely(!error)) { | |
9474138d | 1232 | + /* test if auditing is being forced */ |
ceaf2cfb | 1233 | + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && |
76514441 | 1234 | + !cap_raised(profile->caps.audit, cap))) |
9474138d | 1235 | + return 0; |
5e665963 | 1236 | + type = AUDIT_APPARMOR_AUDIT; |
76514441 AM |
1237 | + } else if (KILL_MODE(profile) || |
1238 | + cap_raised(profile->caps.kill, cap)) { | |
9474138d | 1239 | + type = AUDIT_APPARMOR_KILL; |
76514441 | 1240 | + } else if (cap_raised(profile->caps.quiet, cap) && |
ceaf2cfb AM |
1241 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && |
1242 | + AUDIT_MODE(profile) != AUDIT_ALL) { | |
9474138d | 1243 | + /* quiet auditing */ |
76514441 | 1244 | + return error; |
9474138d AM |
1245 | + } |
1246 | + | |
1247 | + /* Do simple duplicate message elimination */ | |
1248 | + ent = &get_cpu_var(audit_cache); | |
76514441 | 1249 | + if (profile == ent->profile && cap_raised(ent->caps, cap)) { |
ceaf2cfb AM |
1250 | + put_cpu_var(audit_cache); |
1251 | + if (COMPLAIN_MODE(profile)) | |
76514441 AM |
1252 | + return complain_error(error); |
1253 | + return error; | |
9474138d | 1254 | + } else { |
ceaf2cfb AM |
1255 | + aa_put_profile(ent->profile); |
1256 | + ent->profile = aa_get_profile(profile); | |
76514441 | 1257 | + cap_raise(ent->caps, cap); |
9474138d AM |
1258 | + } |
1259 | + put_cpu_var(audit_cache); | |
1260 | + | |
76514441 | 1261 | + return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); |
9474138d AM |
1262 | +} |
1263 | + | |
ceaf2cfb | 1264 | +/** |
76514441 | 1265 | + * profile_capable - test if profile allows use of capability @cap |
ceaf2cfb AM |
1266 | + * @profile: profile being enforced (NOT NULL, NOT unconfined) |
1267 | + * @cap: capability to test if allowed | |
1268 | + * | |
1269 | + * Returns: 0 if allowed else -EPERM | |
1270 | + */ | |
76514441 | 1271 | +static int profile_capable(struct aa_profile *profile, int cap) |
9474138d | 1272 | +{ |
76514441 | 1273 | + return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM; |
9474138d AM |
1274 | +} |
1275 | + | |
1276 | +/** | |
1277 | + * aa_capable - test permission to use capability | |
76514441 AM |
1278 | + * @task: task doing capability test against (NOT NULL) |
1279 | + * @profile: profile confining @task (NOT NULL) | |
9474138d AM |
1280 | + * @cap: capability to be tested |
1281 | + * @audit: whether an audit record should be generated | |
1282 | + * | |
1283 | + * Look up capability in profile capability set. | |
ceaf2cfb AM |
1284 | + * |
1285 | + * Returns: 0 on success, or else an error code. | |
9474138d AM |
1286 | + */ |
1287 | +int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | |
1288 | + int audit) | |
1289 | +{ | |
76514441 | 1290 | + int error = profile_capable(profile, cap); |
9474138d AM |
1291 | + |
1292 | + if (!audit) { | |
ceaf2cfb | 1293 | + if (COMPLAIN_MODE(profile)) |
76514441 | 1294 | + return complain_error(error); |
9474138d AM |
1295 | + return error; |
1296 | + } | |
1297 | + | |
76514441 | 1298 | + return audit_caps(profile, task, cap, error); |
9474138d | 1299 | +} |
ceaf2cfb AM |
1300 | diff --git a/security/apparmor/context.c b/security/apparmor/context.c |
1301 | new file mode 100644 | |
76514441 | 1302 | index 0000000..8a9b502 |
ceaf2cfb AM |
1303 | --- /dev/null |
1304 | +++ b/security/apparmor/context.c | |
76514441 | 1305 | @@ -0,0 +1,216 @@ |
9474138d AM |
1306 | +/* |
1307 | + * AppArmor security module | |
1308 | + * | |
1309 | + * This file contains AppArmor functions used to manipulate object security | |
1310 | + * contexts. | |
1311 | + * | |
1312 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 1313 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
1314 | + * |
1315 | + * This program is free software; you can redistribute it and/or | |
1316 | + * modify it under the terms of the GNU General Public License as | |
1317 | + * published by the Free Software Foundation, version 2 of the | |
1318 | + * License. | |
ceaf2cfb AM |
1319 | + * |
1320 | + * | |
1321 | + * AppArmor sets confinement on every task, via the the aa_task_cxt and | |
76514441 | 1322 | + * the aa_task_cxt.profile, both of which are required and are not allowed |
ceaf2cfb AM |
1323 | + * to be NULL. The aa_task_cxt is not reference counted and is unique |
1324 | + * to each cred (which is reference count). The profile pointed to by | |
1325 | + * the task_cxt is reference counted. | |
1326 | + * | |
1327 | + * TODO | |
1328 | + * If a task uses change_hat it currently does not return to the old | |
1329 | + * cred or task context but instead creates a new one. Ideally the task | |
1330 | + * should return to the previous cred if it has not been modified. | |
1331 | + * | |
9474138d AM |
1332 | + */ |
1333 | + | |
1334 | +#include "include/context.h" | |
1335 | +#include "include/policy.h" | |
1336 | + | |
ceaf2cfb | 1337 | +/** |
76514441 | 1338 | + * aa_alloc_task_context - allocate a new task_cxt |
ceaf2cfb AM |
1339 | + * @flags: gfp flags for allocation |
1340 | + * | |
1341 | + * Returns: allocated buffer or NULL on failure | |
1342 | + */ | |
1343 | +struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) | |
9474138d | 1344 | +{ |
ceaf2cfb | 1345 | + return kzalloc(sizeof(struct aa_task_cxt), flags); |
9474138d AM |
1346 | +} |
1347 | + | |
ceaf2cfb AM |
1348 | +/** |
1349 | + * aa_free_task_context - free a task_cxt | |
1350 | + * @cxt: task_cxt to free (MAYBE NULL) | |
1351 | + */ | |
1352 | +void aa_free_task_context(struct aa_task_cxt *cxt) | |
9474138d AM |
1353 | +{ |
1354 | + if (cxt) { | |
ceaf2cfb AM |
1355 | + aa_put_profile(cxt->profile); |
1356 | + aa_put_profile(cxt->previous); | |
1357 | + aa_put_profile(cxt->onexec); | |
9474138d | 1358 | + |
ceaf2cfb | 1359 | + kzfree(cxt); |
9474138d AM |
1360 | + } |
1361 | +} | |
1362 | + | |
9474138d | 1363 | +/** |
ceaf2cfb AM |
1364 | + * aa_dup_task_context - duplicate a task context, incrementing reference counts |
1365 | + * @new: a blank task context (NOT NULL) | |
1366 | + * @old: the task context to copy (NOT NULL) | |
9474138d | 1367 | + */ |
ceaf2cfb | 1368 | +void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) |
9474138d | 1369 | +{ |
ceaf2cfb AM |
1370 | + *new = *old; |
1371 | + aa_get_profile(new->profile); | |
1372 | + aa_get_profile(new->previous); | |
1373 | + aa_get_profile(new->onexec); | |
9474138d AM |
1374 | +} |
1375 | + | |
1376 | +/** | |
76514441 AM |
1377 | + * aa_replace_current_profile - replace the current tasks profiles |
1378 | + * @profile: new profile (NOT NULL) | |
9474138d | 1379 | + * |
76514441 | 1380 | + * Returns: 0 or error on failure |
9474138d | 1381 | + */ |
76514441 | 1382 | +int aa_replace_current_profile(struct aa_profile *profile) |
9474138d | 1383 | +{ |
76514441 AM |
1384 | + struct aa_task_cxt *cxt = current_cred()->security; |
1385 | + struct cred *new; | |
1386 | + BUG_ON(!profile); | |
1387 | + | |
ceaf2cfb | 1388 | + if (cxt->profile == profile) |
76514441 | 1389 | + return 0; |
9474138d | 1390 | + |
76514441 AM |
1391 | + new = prepare_creds(); |
1392 | + if (!new) | |
1393 | + return -ENOMEM; | |
1394 | + | |
1395 | + cxt = new->security; | |
ceaf2cfb AM |
1396 | + if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { |
1397 | + /* if switching to unconfined or a different profile namespace | |
1398 | + * clear out context state | |
1399 | + */ | |
1400 | + aa_put_profile(cxt->previous); | |
1401 | + aa_put_profile(cxt->onexec); | |
1402 | + cxt->previous = NULL; | |
1403 | + cxt->onexec = NULL; | |
1404 | + cxt->token = 0; | |
9474138d | 1405 | + } |
76514441 AM |
1406 | + /* be careful switching cxt->profile, when racing replacement it |
1407 | + * is possible that cxt->profile->replacedby is the reference keeping | |
1408 | + * @profile valid, so make sure to get its reference before dropping | |
1409 | + * the reference on cxt->profile */ | |
1410 | + aa_get_profile(profile); | |
ceaf2cfb | 1411 | + aa_put_profile(cxt->profile); |
76514441 | 1412 | + cxt->profile = profile; |
9474138d AM |
1413 | + |
1414 | + commit_creds(new); | |
1415 | + return 0; | |
1416 | +} | |
1417 | + | |
ceaf2cfb AM |
1418 | +/** |
1419 | + * aa_set_current_onexec - set the tasks change_profile to happen onexec | |
76514441 | 1420 | + * @profile: system profile to set at exec (MAYBE NULL to clear value) |
ceaf2cfb AM |
1421 | + * |
1422 | + * Returns: 0 or error on failure | |
1423 | + */ | |
1424 | +int aa_set_current_onexec(struct aa_profile *profile) | |
9474138d | 1425 | +{ |
ceaf2cfb | 1426 | + struct aa_task_cxt *cxt; |
9474138d AM |
1427 | + struct cred *new = prepare_creds(); |
1428 | + if (!new) | |
1429 | + return -ENOMEM; | |
1430 | + | |
1431 | + cxt = new->security; | |
76514441 | 1432 | + aa_get_profile(profile); |
ceaf2cfb | 1433 | + aa_put_profile(cxt->onexec); |
76514441 | 1434 | + cxt->onexec = profile; |
9474138d AM |
1435 | + |
1436 | + commit_creds(new); | |
1437 | + return 0; | |
1438 | +} | |
1439 | + | |
ceaf2cfb AM |
1440 | +/** |
1441 | + * aa_set_current_hat - set the current tasks hat | |
1442 | + * @profile: profile to set as the current hat (NOT NULL) | |
1443 | + * @token: token value that must be specified to change from the hat | |
1444 | + * | |
1445 | + * Do switch of tasks hat. If the task is currently in a hat | |
1446 | + * validate the token to match. | |
1447 | + * | |
1448 | + * Returns: 0 or error on failure | |
9474138d AM |
1449 | + */ |
1450 | +int aa_set_current_hat(struct aa_profile *profile, u64 token) | |
1451 | +{ | |
ceaf2cfb | 1452 | + struct aa_task_cxt *cxt; |
9474138d AM |
1453 | + struct cred *new = prepare_creds(); |
1454 | + if (!new) | |
1455 | + return -ENOMEM; | |
ceaf2cfb | 1456 | + BUG_ON(!profile); |
9474138d AM |
1457 | + |
1458 | + cxt = new->security; | |
ceaf2cfb | 1459 | + if (!cxt->previous) { |
76514441 | 1460 | + /* transfer refcount */ |
ceaf2cfb AM |
1461 | + cxt->previous = cxt->profile; |
1462 | + cxt->token = token; | |
1463 | + } else if (cxt->token == token) { | |
1464 | + aa_put_profile(cxt->profile); | |
9474138d AM |
1465 | + } else { |
1466 | + /* previous_profile && cxt->token != token */ | |
1467 | + abort_creds(new); | |
1468 | + return -EACCES; | |
1469 | + } | |
ceaf2cfb | 1470 | + cxt->profile = aa_get_profile(aa_newest_version(profile)); |
9474138d | 1471 | + /* clear exec on switching context */ |
ceaf2cfb AM |
1472 | + aa_put_profile(cxt->onexec); |
1473 | + cxt->onexec = NULL; | |
9474138d AM |
1474 | + |
1475 | + commit_creds(new); | |
1476 | + return 0; | |
1477 | +} | |
1478 | + | |
ceaf2cfb AM |
1479 | +/** |
1480 | + * aa_restore_previous_profile - exit from hat context restoring the profile | |
1481 | + * @token: the token that must be matched to exit hat context | |
1482 | + * | |
1483 | + * Attempt to return out of a hat to the previous profile. The token | |
1484 | + * must match the stored token value. | |
1485 | + * | |
1486 | + * Returns: 0 or error of failure | |
9474138d AM |
1487 | + */ |
1488 | +int aa_restore_previous_profile(u64 token) | |
1489 | +{ | |
ceaf2cfb | 1490 | + struct aa_task_cxt *cxt; |
9474138d AM |
1491 | + struct cred *new = prepare_creds(); |
1492 | + if (!new) | |
1493 | + return -ENOMEM; | |
1494 | + | |
1495 | + cxt = new->security; | |
ceaf2cfb | 1496 | + if (cxt->token != token) { |
9474138d AM |
1497 | + abort_creds(new); |
1498 | + return -EACCES; | |
1499 | + } | |
1500 | + /* ignore restores when there is no saved profile */ | |
ceaf2cfb | 1501 | + if (!cxt->previous) { |
9474138d AM |
1502 | + abort_creds(new); |
1503 | + return 0; | |
1504 | + } | |
1505 | + | |
ceaf2cfb AM |
1506 | + aa_put_profile(cxt->profile); |
1507 | + cxt->profile = aa_newest_version(cxt->previous); | |
1508 | + BUG_ON(!cxt->profile); | |
1509 | + if (unlikely(cxt->profile != cxt->previous)) { | |
1510 | + aa_get_profile(cxt->profile); | |
1511 | + aa_put_profile(cxt->previous); | |
9474138d AM |
1512 | + } |
1513 | + /* clear exec && prev information when restoring to previous context */ | |
ceaf2cfb AM |
1514 | + cxt->previous = NULL; |
1515 | + cxt->token = 0; | |
1516 | + aa_put_profile(cxt->onexec); | |
1517 | + cxt->onexec = NULL; | |
9474138d AM |
1518 | + |
1519 | + commit_creds(new); | |
1520 | + return 0; | |
1521 | +} | |
ceaf2cfb AM |
1522 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c |
1523 | new file mode 100644 | |
76514441 | 1524 | index 0000000..08bbe63 |
ceaf2cfb AM |
1525 | --- /dev/null |
1526 | +++ b/security/apparmor/domain.c | |
76514441 | 1527 | @@ -0,0 +1,823 @@ |
9474138d AM |
1528 | +/* |
1529 | + * AppArmor security module | |
1530 | + * | |
1531 | + * This file contains AppArmor policy attachment and domain transitions | |
1532 | + * | |
1533 | + * Copyright (C) 2002-2008 Novell/SUSE | |
ceaf2cfb | 1534 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
1535 | + * |
1536 | + * This program is free software; you can redistribute it and/or | |
1537 | + * modify it under the terms of the GNU General Public License as | |
1538 | + * published by the Free Software Foundation, version 2 of the | |
1539 | + * License. | |
1540 | + */ | |
1541 | + | |
1542 | +#include <linux/errno.h> | |
1543 | +#include <linux/fdtable.h> | |
1544 | +#include <linux/file.h> | |
1545 | +#include <linux/mount.h> | |
9474138d AM |
1546 | +#include <linux/syscalls.h> |
1547 | +#include <linux/tracehook.h> | |
5e665963 | 1548 | +#include <linux/personality.h> |
9474138d AM |
1549 | + |
1550 | +#include "include/audit.h" | |
1551 | +#include "include/apparmorfs.h" | |
1552 | +#include "include/context.h" | |
1553 | +#include "include/domain.h" | |
1554 | +#include "include/file.h" | |
1555 | +#include "include/ipc.h" | |
1556 | +#include "include/match.h" | |
1557 | +#include "include/path.h" | |
1558 | +#include "include/policy.h" | |
1559 | + | |
1560 | +/** | |
1561 | + * aa_free_domain_entries - free entries in a domain table | |
ceaf2cfb | 1562 | + * @domain: the domain table to free (MAYBE NULL) |
9474138d AM |
1563 | + */ |
1564 | +void aa_free_domain_entries(struct aa_domain *domain) | |
1565 | +{ | |
1566 | + int i; | |
ceaf2cfb AM |
1567 | + if (domain) { |
1568 | + if (!domain->table) | |
1569 | + return; | |
9474138d | 1570 | + |
ceaf2cfb AM |
1571 | + for (i = 0; i < domain->size; i++) |
1572 | + kzfree(domain->table[i]); | |
1573 | + kzfree(domain->table); | |
1574 | + domain->table = NULL; | |
1575 | + } | |
9474138d AM |
1576 | +} |
1577 | + | |
ceaf2cfb | 1578 | +/** |
76514441 | 1579 | + * may_change_ptraced_domain - check if can change profile on ptraced task |
ceaf2cfb AM |
1580 | + * @task: task we want to change profile of (NOT NULL) |
1581 | + * @to_profile: profile to change to (NOT NULL) | |
1582 | + * | |
1583 | + * Check if the task is ptraced and if so if the tracing task is allowed | |
9474138d | 1584 | + * to trace the new domain |
ceaf2cfb AM |
1585 | + * |
1586 | + * Returns: %0 or error if change not allowed | |
9474138d | 1587 | + */ |
76514441 AM |
1588 | +static int may_change_ptraced_domain(struct task_struct *task, |
1589 | + struct aa_profile *to_profile) | |
9474138d AM |
1590 | +{ |
1591 | + struct task_struct *tracer; | |
1592 | + struct cred *cred = NULL; | |
1593 | + struct aa_profile *tracerp = NULL; | |
1594 | + int error = 0; | |
1595 | + | |
1596 | + rcu_read_lock(); | |
1597 | + tracer = tracehook_tracer_task(task); | |
ceaf2cfb AM |
1598 | + if (tracer) { |
1599 | + /* released below */ | |
1600 | + cred = get_task_cred(tracer); | |
1601 | + tracerp = aa_cred_profile(cred); | |
1602 | + } | |
9474138d AM |
1603 | + rcu_read_unlock(); |
1604 | + | |
ceaf2cfb AM |
1605 | + /* not ptraced */ |
1606 | + if (!tracer || unconfined(tracerp)) | |
1607 | + goto out; | |
9474138d AM |
1608 | + |
1609 | + error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); | |
ceaf2cfb AM |
1610 | + |
1611 | +out: | |
1612 | + if (cred) | |
1613 | + put_cred(cred); | |
9474138d AM |
1614 | + |
1615 | + return error; | |
1616 | +} | |
1617 | + | |
1618 | +/** | |
ceaf2cfb AM |
1619 | + * change_profile_perms - find permissions for change_profile |
1620 | + * @profile: the current profile (NOT NULL) | |
1621 | + * @ns: the namespace being switched to (NOT NULL) | |
1622 | + * @name: the name of the profile to change to (NOT NULL) | |
76514441 AM |
1623 | + * @request: requested perms |
1624 | + * @start: state to start matching in | |
ceaf2cfb AM |
1625 | + * |
1626 | + * Returns: permission set | |
9474138d AM |
1627 | + */ |
1628 | +static struct file_perms change_profile_perms(struct aa_profile *profile, | |
1629 | + struct aa_namespace *ns, | |
76514441 AM |
1630 | + const char *name, u32 request, |
1631 | + unsigned int start) | |
9474138d AM |
1632 | +{ |
1633 | + struct file_perms perms; | |
ceaf2cfb | 1634 | + struct path_cond cond = { }; |
9474138d AM |
1635 | + unsigned int state; |
1636 | + | |
ceaf2cfb | 1637 | + if (unconfined(profile)) { |
76514441 | 1638 | + perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; |
9474138d | 1639 | + perms.audit = perms.quiet = perms.kill = 0; |
9474138d | 1640 | + return perms; |
5e665963 AM |
1641 | + } else if (!profile->file.dfa) { |
1642 | + return nullperms; | |
9474138d | 1643 | + } else if ((ns == profile->ns)) { |
76514441 AM |
1644 | + /* try matching against rules with out namespace prepended */ |
1645 | + aa_str_perms(profile->file.dfa, start, name, &cond, &perms); | |
1646 | + if (COMBINED_PERM_MASK(perms) & request) | |
9474138d AM |
1647 | + return perms; |
1648 | + } | |
1649 | + | |
1650 | + /* try matching with namespace name and then profile */ | |
76514441 AM |
1651 | + state = aa_dfa_match(profile->file.dfa, start, ns->base.name); |
1652 | + state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); | |
1653 | + aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | |
1654 | + | |
1655 | + return perms; | |
9474138d AM |
1656 | +} |
1657 | + | |
ceaf2cfb | 1658 | +/** |
76514441 | 1659 | + * __attach_match_ - find an attachment match |
ceaf2cfb AM |
1660 | + * @name - to match against (NOT NULL) |
1661 | + * @head - profile list to walk (NOT NULL) | |
1662 | + * | |
1663 | + * Do a linear search on the profiles in the list. There is a matching | |
76514441 | 1664 | + * preference where an exact match is preferred over a name which uses |
ceaf2cfb | 1665 | + * expressions to match, and matching expressions with the greatest |
76514441 | 1666 | + * xmatch_len are preferred. |
ceaf2cfb AM |
1667 | + * |
1668 | + * Requires: @head not be shared or have appropriate locks held | |
1669 | + * | |
1670 | + * Returns: profile or NULL if no match found | |
1671 | + */ | |
76514441 AM |
1672 | +static struct aa_profile *__attach_match(const char *name, |
1673 | + struct list_head *head) | |
ceaf2cfb AM |
1674 | +{ |
1675 | + int len = 0; | |
1676 | + struct aa_profile *profile, *candidate = NULL; | |
1677 | + | |
1678 | + list_for_each_entry(profile, head, base.list) { | |
1679 | + if (profile->flags & PFLAG_NULL) | |
1680 | + continue; | |
1681 | + if (profile->xmatch && profile->xmatch_len > len) { | |
1682 | + unsigned int state = aa_dfa_match(profile->xmatch, | |
1683 | + DFA_START, name); | |
76514441 | 1684 | + u32 perm = dfa_user_allow(profile->xmatch, state); |
ceaf2cfb AM |
1685 | + /* any accepting state means a valid match. */ |
1686 | + if (perm & MAY_EXEC) { | |
1687 | + candidate = profile; | |
1688 | + len = profile->xmatch_len; | |
1689 | + } | |
1690 | + } else if (!strcmp(profile->base.name, name)) | |
1691 | + /* exact non-re match, no more searching required */ | |
1692 | + return profile; | |
1693 | + } | |
1694 | + | |
1695 | + return candidate; | |
1696 | +} | |
1697 | + | |
1698 | +/** | |
76514441 | 1699 | + * find_attach - do attachment search for unconfined processes |
ceaf2cfb AM |
1700 | + * @ns: the current namespace (NOT NULL) |
1701 | + * @list: list to search (NOT NULL) | |
1702 | + * @name: the executable name to match against (NOT NULL) | |
1703 | + * | |
1704 | + * Returns: profile or NULL if no match found | |
1705 | + */ | |
76514441 AM |
1706 | +static struct aa_profile *find_attach(struct aa_namespace *ns, |
1707 | + struct list_head *list, const char *name) | |
ceaf2cfb AM |
1708 | +{ |
1709 | + struct aa_profile *profile; | |
1710 | + | |
1711 | + read_lock(&ns->lock); | |
76514441 | 1712 | + profile = aa_get_profile(__attach_match(name, list)); |
ceaf2cfb AM |
1713 | + read_unlock(&ns->lock); |
1714 | + | |
1715 | + return profile; | |
1716 | +} | |
1717 | + | |
1718 | +/** | |
1719 | + * separate_fqname - separate the namespace and profile names | |
1720 | + * @fqname: the fqname name to split (NOT NULL) | |
1721 | + * @ns_name: the namespace name if it exists (NOT NULL) | |
1722 | + * | |
1723 | + * This is the xtable equivalent routine of aa_split_fqname. It finds the | |
1724 | + * split in an xtable fqname which contains an embedded \0 instead of a : | |
1725 | + * if a namespace is specified. This is done so the xtable is constant and | |
76514441 | 1726 | + * isn't re-split on every lookup. |
ceaf2cfb AM |
1727 | + * |
1728 | + * Either the profile or namespace name may be optional but if the namespace | |
1729 | + * is specified the profile name termination must be present. This results | |
1730 | + * in the following possible encodings: | |
1731 | + * profile_name\0 | |
1732 | + * :ns_name\0profile_name\0 | |
1733 | + * :ns_name\0\0 | |
1734 | + * | |
76514441 | 1735 | + * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table |
ceaf2cfb AM |
1736 | + * |
1737 | + * Returns: profile name if it is specified else NULL | |
1738 | + */ | |
1739 | +static const char *separate_fqname(const char *fqname, const char **ns_name) | |
9474138d | 1740 | +{ |
ceaf2cfb AM |
1741 | + const char *name; |
1742 | + | |
1743 | + if (fqname[0] == ':') { | |
76514441 AM |
1744 | + /* In this case there is guaranteed to be two \0 terminators |
1745 | + * in the string. They are verified at load time by | |
1746 | + * by unpack_trans_table | |
1747 | + */ | |
ceaf2cfb AM |
1748 | + *ns_name = fqname + 1; /* skip : */ |
1749 | + name = *ns_name + strlen(*ns_name) + 1; | |
1750 | + if (!*name) | |
1751 | + name = NULL; | |
1752 | + } else { | |
1753 | + *ns_name = NULL; | |
1754 | + name = fqname; | |
9474138d | 1755 | + } |
ceaf2cfb AM |
1756 | + |
1757 | + return name; | |
1758 | +} | |
1759 | + | |
1760 | +static const char *next_name(int xtype, const char *name) | |
1761 | +{ | |
9474138d AM |
1762 | + return NULL; |
1763 | +} | |
1764 | + | |
ceaf2cfb | 1765 | +/** |
76514441 AM |
1766 | + * x_table_lookup - lookup an x transition name via transition table |
1767 | + * @profile: current profile (NOT NULL) | |
ceaf2cfb AM |
1768 | + * @xindex: index into x transition table |
1769 | + * | |
76514441 | 1770 | + * Returns: refcounted profile, or NULL on failure (MAYBE NULL) |
9474138d | 1771 | + */ |
76514441 | 1772 | +static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) |
9474138d AM |
1773 | +{ |
1774 | + struct aa_profile *new_profile = NULL; | |
ceaf2cfb | 1775 | + struct aa_namespace *ns = profile->ns; |
76514441 | 1776 | + u32 xtype = xindex & AA_X_TYPE_MASK; |
9474138d | 1777 | + int index = xindex & AA_X_INDEX_MASK; |
76514441 | 1778 | + const char *name; |
9474138d | 1779 | + |
76514441 AM |
1780 | + /* index is guaranteed to be in range, validated at load time */ |
1781 | + for (name = profile->file.trans.table[index]; !new_profile && name; | |
1782 | + name = next_name(xtype, name)) { | |
9474138d AM |
1783 | + struct aa_namespace *new_ns; |
1784 | + const char *xname = NULL; | |
1785 | + | |
1786 | + new_ns = NULL; | |
1787 | + if (xindex & AA_X_CHILD) { | |
ceaf2cfb | 1788 | + /* release by caller */ |
9474138d | 1789 | + new_profile = aa_find_child(profile, name); |
9474138d AM |
1790 | + continue; |
1791 | + } else if (*name == ':') { | |
1792 | + /* switching namespace */ | |
ceaf2cfb AM |
1793 | + const char *ns_name; |
1794 | + xname = name = separate_fqname(name, &ns_name); | |
1795 | + if (!xname) | |
9474138d | 1796 | + /* no name so use profile name */ |
ceaf2cfb | 1797 | + xname = profile->base.hname; |
9474138d AM |
1798 | + if (*ns_name == '@') { |
1799 | + /* TODO: variable support */ | |
1800 | + ; | |
1801 | + } | |
ceaf2cfb AM |
1802 | + /* released below */ |
1803 | + new_ns = aa_find_namespace(ns, ns_name); | |
9474138d AM |
1804 | + if (!new_ns) |
1805 | + continue; | |
1806 | + } else if (*name == '@') { | |
1807 | + /* TODO: variable support */ | |
ceaf2cfb | 1808 | + continue; |
9474138d | 1809 | + } else { |
76514441 | 1810 | + /* basic namespace lookup */ |
9474138d AM |
1811 | + xname = name; |
1812 | + } | |
1813 | + | |
ceaf2cfb | 1814 | + /* released by caller */ |
76514441 | 1815 | + new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); |
9474138d AM |
1816 | + aa_put_namespace(new_ns); |
1817 | + } | |
1818 | + | |
ceaf2cfb | 1819 | + /* released by caller */ |
9474138d AM |
1820 | + return new_profile; |
1821 | +} | |
1822 | + | |
ceaf2cfb | 1823 | +/** |
76514441 AM |
1824 | + * x_to_profile - get target profile for a given xindex |
1825 | + * @profile: current profile (NOT NULL) | |
1826 | + * @name: name to lookup (NOT NULL) | |
1827 | + * @xindex: index into x transition table | |
1828 | + * | |
1829 | + * find profile for a transition index | |
1830 | + * | |
1831 | + * Returns: refcounted profile or NULL if not found available | |
1832 | + */ | |
1833 | +static struct aa_profile *x_to_profile(struct aa_profile *profile, | |
1834 | + const char *name, u32 xindex) | |
1835 | +{ | |
1836 | + struct aa_profile *new_profile = NULL; | |
1837 | + struct aa_namespace *ns = profile->ns; | |
1838 | + u32 xtype = xindex & AA_X_TYPE_MASK; | |
1839 | + | |
1840 | + switch (xtype) { | |
1841 | + case AA_X_NONE: | |
1842 | + /* fail exec unless ix || ux fallback - handled by caller */ | |
1843 | + return NULL; | |
1844 | + case AA_X_NAME: | |
1845 | + if (xindex & AA_X_CHILD) | |
1846 | + /* released by caller */ | |
1847 | + new_profile = find_attach(ns, &profile->base.profiles, | |
1848 | + name); | |
1849 | + else | |
1850 | + /* released by caller */ | |
1851 | + new_profile = find_attach(ns, &ns->base.profiles, | |
1852 | + name); | |
1853 | + break; | |
1854 | + case AA_X_TABLE: | |
1855 | + /* released by caller */ | |
1856 | + new_profile = x_table_lookup(profile, xindex); | |
1857 | + break; | |
1858 | + } | |
1859 | + | |
1860 | + /* released by caller */ | |
1861 | + return new_profile; | |
1862 | +} | |
1863 | + | |
1864 | +/** | |
ceaf2cfb AM |
1865 | + * apparmor_bprm_set_creds - set the new creds on the bprm struct |
1866 | + * @bprm: binprm for the exec (NOT NULL) | |
1867 | + * | |
1868 | + * Returns: %0 or error on failure | |
1869 | + */ | |
9474138d AM |
1870 | +int apparmor_bprm_set_creds(struct linux_binprm *bprm) |
1871 | +{ | |
ceaf2cfb | 1872 | + struct aa_task_cxt *cxt; |
9474138d AM |
1873 | + struct aa_profile *profile, *new_profile = NULL; |
1874 | + struct aa_namespace *ns; | |
1875 | + char *buffer = NULL; | |
ceaf2cfb | 1876 | + unsigned int state; |
76514441 | 1877 | + struct file_perms perms = {}; |
ceaf2cfb AM |
1878 | + struct path_cond cond = { |
1879 | + bprm->file->f_path.dentry->d_inode->i_uid, | |
1880 | + bprm->file->f_path.dentry->d_inode->i_mode | |
1881 | + }; | |
76514441 AM |
1882 | + const char *name = NULL, *target = NULL, *info = NULL; |
1883 | + int error = cap_bprm_set_creds(bprm); | |
1884 | + if (error) | |
1885 | + return error; | |
9474138d AM |
1886 | + |
1887 | + if (bprm->cred_prepared) | |
1888 | + return 0; | |
1889 | + | |
9474138d AM |
1890 | + cxt = bprm->cred->security; |
1891 | + BUG_ON(!cxt); | |
1892 | + | |
76514441 | 1893 | + profile = aa_get_profile(aa_newest_version(cxt->profile)); |
ceaf2cfb AM |
1894 | + /* |
1895 | + * get the namespace from the replacement profile as replacement | |
1896 | + * can change the namespace | |
1897 | + */ | |
1898 | + ns = profile->ns; | |
1899 | + state = profile->file.start; | |
9474138d | 1900 | + |
76514441 AM |
1901 | + /* buffer freed below, name is pointer into buffer */ |
1902 | + error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer, | |
1903 | + &name); | |
1904 | + if (error) { | |
ceaf2cfb AM |
1905 | + if (profile->flags & |
1906 | + (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) | |
76514441 AM |
1907 | + error = 0; |
1908 | + info = "Exec failed name resolution"; | |
1909 | + name = bprm->filename; | |
9474138d AM |
1910 | + goto audit; |
1911 | + } | |
1912 | + | |
76514441 AM |
1913 | + /* Test for onexec first as onexec directives override other |
1914 | + * x transitions. | |
1915 | + */ | |
ceaf2cfb | 1916 | + if (unconfined(profile)) { |
76514441 AM |
1917 | + /* unconfined task */ |
1918 | + if (cxt->onexec) | |
1919 | + /* change_profile on exec already been granted */ | |
1920 | + new_profile = aa_get_profile(cxt->onexec); | |
1921 | + else | |
1922 | + new_profile = find_attach(ns, &ns->base.profiles, name); | |
9474138d AM |
1923 | + if (!new_profile) |
1924 | + goto cleanup; | |
1925 | + goto apply; | |
76514441 AM |
1926 | + } |
1927 | + | |
1928 | + /* find exec permissions for name */ | |
1929 | + state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | |
1930 | + if (cxt->onexec) { | |
1931 | + struct file_perms cp; | |
1932 | + info = "change_profile onexec"; | |
1933 | + if (!(perms.allow & AA_MAY_ONEXEC)) | |
1934 | + goto audit; | |
1935 | + | |
1936 | + /* test if this exec can be paired with change_profile onexec. | |
1937 | + * onexec permission is linked to exec with a standard pairing | |
1938 | + * exec\0change_profile | |
ceaf2cfb | 1939 | + */ |
76514441 AM |
1940 | + state = aa_dfa_null_transition(profile->file.dfa, state); |
1941 | + cp = change_profile_perms(profile, cxt->onexec->ns, name, | |
1942 | + AA_MAY_ONEXEC, state); | |
1943 | + | |
1944 | + if (!(cp.allow & AA_MAY_ONEXEC)) | |
1945 | + goto audit; | |
1946 | + new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | |
1947 | + goto apply; | |
1948 | + } | |
1949 | + | |
1950 | + if (perms.allow & MAY_EXEC) { | |
1951 | + /* exec permission determine how to transition */ | |
1952 | + new_profile = x_to_profile(profile, name, perms.xindex); | |
ceaf2cfb | 1953 | + if (!new_profile) { |
76514441 AM |
1954 | + if (perms.xindex & AA_X_INHERIT) { |
1955 | + /* (p|c|n)ix - don't change profile but do | |
1956 | + * use the newest version, which was picked | |
1957 | + * up above when getting profile | |
1958 | + */ | |
1959 | + info = "ix fallback"; | |
1960 | + new_profile = aa_get_profile(profile); | |
9474138d | 1961 | + goto x_clear; |
76514441 | 1962 | + } else if (perms.xindex & AA_X_UNCONFINED) { |
9474138d | 1963 | + new_profile = aa_get_profile(ns->unconfined); |
76514441 | 1964 | + info = "ux fallback"; |
9474138d | 1965 | + } else { |
76514441 AM |
1966 | + error = -ENOENT; |
1967 | + info = "profile not found"; | |
9474138d AM |
1968 | + } |
1969 | + } | |
ceaf2cfb | 1970 | + } else if (COMPLAIN_MODE(profile)) { |
76514441 | 1971 | + /* no exec permission - are we in learning mode */ |
ceaf2cfb | 1972 | + new_profile = aa_new_null_profile(profile, 0); |
ceaf2cfb | 1973 | + if (!new_profile) { |
76514441 AM |
1974 | + error = -ENOMEM; |
1975 | + info = "could not create null profile"; | |
1976 | + } else { | |
1977 | + error = -EACCES; | |
1978 | + target = new_profile->base.hname; | |
1979 | + } | |
1980 | + perms.xindex |= AA_X_UNSAFE; | |
1981 | + } else | |
1982 | + /* fail exec */ | |
1983 | + error = -EACCES; | |
9474138d AM |
1984 | + |
1985 | + if (!new_profile) | |
1986 | + goto audit; | |
1987 | + | |
9474138d AM |
1988 | + if (bprm->unsafe & LSM_UNSAFE_SHARE) { |
1989 | + /* FIXME: currently don't mediate shared state */ | |
1990 | + ; | |
1991 | + } | |
1992 | + | |
1993 | + if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | |
76514441 AM |
1994 | + error = may_change_ptraced_domain(current, new_profile); |
1995 | + if (error) { | |
1996 | + aa_put_profile(new_profile); | |
9474138d | 1997 | + goto audit; |
76514441 | 1998 | + } |
9474138d AM |
1999 | + } |
2000 | + | |
2001 | + /* Determine if secure exec is needed. | |
2002 | + * Can be at this point for the following reasons: | |
2003 | + * 1. unconfined switching to confined | |
2004 | + * 2. confined switching to different confinement | |
2005 | + * 3. confined switching to unconfined | |
2006 | + * | |
2007 | + * Cases 2 and 3 are marked as requiring secure exec | |
2008 | + * (unless policy specified "unsafe exec") | |
2009 | + * | |
2010 | + * bprm->unsafe is used to cache the AA_X_UNSAFE permission | |
2011 | + * to avoid having to recompute in secureexec | |
2012 | + */ | |
76514441 AM |
2013 | + if (!(perms.xindex & AA_X_UNSAFE)) { |
2014 | + AA_DEBUG("scrubbing environment variables for %s profile=%s\n", | |
2015 | + name, new_profile->base.hname); | |
9474138d | 2016 | + bprm->unsafe |= AA_SECURE_X_NEEDED; |
ceaf2cfb | 2017 | + } |
9474138d | 2018 | +apply: |
76514441 | 2019 | + target = new_profile->base.hname; |
9474138d AM |
2020 | + /* when transitioning profiles clear unsafe personality bits */ |
2021 | + bprm->per_clear |= PER_CLEAR_ON_SETID; | |
2022 | + | |
76514441 | 2023 | +x_clear: |
ceaf2cfb AM |
2024 | + aa_put_profile(cxt->profile); |
2025 | + /* transfer new profile reference will be released when cxt is freed */ | |
2026 | + cxt->profile = new_profile; | |
9474138d | 2027 | + |
76514441 | 2028 | + /* clear out all temporary/transitional state from the context */ |
ceaf2cfb AM |
2029 | + aa_put_profile(cxt->previous); |
2030 | + aa_put_profile(cxt->onexec); | |
2031 | + cxt->previous = NULL; | |
2032 | + cxt->onexec = NULL; | |
2033 | + cxt->token = 0; | |
9474138d AM |
2034 | + |
2035 | +audit: | |
76514441 AM |
2036 | + error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, |
2037 | + name, target, cond.uid, info, error); | |
9474138d AM |
2038 | + |
2039 | +cleanup: | |
76514441 | 2040 | + aa_put_profile(profile); |
9474138d AM |
2041 | + kfree(buffer); |
2042 | + | |
76514441 | 2043 | + return error; |
9474138d AM |
2044 | +} |
2045 | + | |
ceaf2cfb AM |
2046 | +/** |
2047 | + * apparmor_bprm_secureexec - determine if secureexec is needed | |
2048 | + * @bprm: binprm for exec (NOT NULL) | |
2049 | + * | |
2050 | + * Returns: %1 if secureexec is needed else %0 | |
2051 | + */ | |
9474138d AM |
2052 | +int apparmor_bprm_secureexec(struct linux_binprm *bprm) |
2053 | +{ | |
2054 | + int ret = cap_bprm_secureexec(bprm); | |
2055 | + | |
2056 | + /* the decision to use secure exec is computed in set_creds | |
ceaf2cfb | 2057 | + * and stored in bprm->unsafe. |
9474138d AM |
2058 | + */ |
2059 | + if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED)) | |
2060 | + ret = 1; | |
2061 | + | |
2062 | + return ret; | |
2063 | +} | |
2064 | + | |
ceaf2cfb AM |
2065 | +/** |
2066 | + * apparmor_bprm_committing_creds - do task cleanup on committing new creds | |
2067 | + * @bprm: binprm for the exec (NOT NULL) | |
9474138d | 2068 | + */ |
ceaf2cfb | 2069 | +void apparmor_bprm_committing_creds(struct linux_binprm *bprm) |
9474138d | 2070 | +{ |
ceaf2cfb AM |
2071 | + struct aa_profile *profile = __aa_current_profile(); |
2072 | + struct aa_task_cxt *new_cxt = bprm->cred->security; | |
9474138d | 2073 | + |
ceaf2cfb AM |
2074 | + /* bail out if unconfined or not changing profile */ |
2075 | + if ((new_cxt->profile == profile) || | |
2076 | + (unconfined(new_cxt->profile))) | |
2077 | + return; | |
9474138d AM |
2078 | + |
2079 | + current->pdeath_signal = 0; | |
2080 | + | |
2081 | + /* reset soft limits and set hard limits for the new profile */ | |
ceaf2cfb | 2082 | + __aa_transition_rlimits(profile, new_cxt->profile); |
2380c486 | 2083 | +} |
2380c486 | 2084 | + |
ceaf2cfb AM |
2085 | +/** |
2086 | + * apparmor_bprm_commited_cred - do cleanup after new creds committed | |
2087 | + * @bprm: binprm for the exec (NOT NULL) | |
2088 | + */ | |
9474138d AM |
2089 | +void apparmor_bprm_committed_creds(struct linux_binprm *bprm) |
2090 | +{ | |
2091 | + /* TODO: cleanup signals - ipc mediation */ | |
2092 | + return; | |
2093 | +} | |
2380c486 | 2094 | + |
ceaf2cfb AM |
2095 | +/* |
2096 | + * Functions for self directed profile change | |
2097 | + */ | |
2098 | + | |
2099 | +/** | |
2100 | + * new_compound_name - create an hname with @n2 appended to @n1 | |
2101 | + * @n1: base of hname (NOT NULL) | |
2102 | + * @n2: name to append (NOT NULL) | |
2103 | + * | |
2104 | + * Returns: new name or NULL on error | |
2105 | + */ | |
2106 | +static char *new_compound_name(const char *n1, const char *n2) | |
2107 | +{ | |
2108 | + char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); | |
2109 | + if (name) | |
2110 | + sprintf(name, "%s//%s", n1, n2); | |
2111 | + return name; | |
2112 | +} | |
2113 | + | |
9474138d AM |
2114 | +/** |
2115 | + * aa_change_hat - change hat to/from subprofile | |
76514441 | 2116 | + * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0) |
ceaf2cfb | 2117 | + * @count: number of hat names in @hats |
9474138d AM |
2118 | + * @token: magic value to validate the hat change |
2119 | + * @permtest: true if this is just a permission test | |
2120 | + * | |
ceaf2cfb AM |
2121 | + * Change to the first profile specified in @hats that exists, and store |
2122 | + * the @hat_magic in the current task context. If the count == 0 and the | |
2123 | + * @token matches that stored in the current task context, return to the | |
2124 | + * top level profile. | |
2125 | + * | |
9474138d AM |
2126 | + * Returns %0 on success, error otherwise. |
2127 | + */ | |
ceaf2cfb | 2128 | +int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) |
9474138d AM |
2129 | +{ |
2130 | + const struct cred *cred; | |
ceaf2cfb | 2131 | + struct aa_task_cxt *cxt; |
9474138d | 2132 | + struct aa_profile *profile, *previous_profile, *hat = NULL; |
0eaba94d | 2133 | + char *name = NULL; |
ceaf2cfb | 2134 | + int i; |
76514441 AM |
2135 | + struct file_perms perms = {}; |
2136 | + const char *target = NULL, *info = NULL; | |
2137 | + int error = 0; | |
2380c486 | 2138 | + |
ceaf2cfb AM |
2139 | + /* released below */ |
2140 | + cred = get_current_cred(); | |
9474138d | 2141 | + cxt = cred->security; |
ceaf2cfb AM |
2142 | + profile = aa_cred_profile(cred); |
2143 | + previous_profile = cxt->previous; | |
2380c486 | 2144 | + |
ceaf2cfb | 2145 | + if (unconfined(profile)) { |
76514441 AM |
2146 | + info = "unconfined"; |
2147 | + error = -EPERM; | |
9474138d AM |
2148 | + goto audit; |
2149 | + } | |
2380c486 | 2150 | + |
ceaf2cfb AM |
2151 | + if (count) { |
2152 | + /* attempting to change into a new hat or switch to a sibling */ | |
2153 | + struct aa_profile *root; | |
2154 | + root = PROFILE_IS_HAT(profile) ? profile->parent : profile; | |
2380c486 | 2155 | + |
ceaf2cfb AM |
2156 | + /* find first matching hat */ |
2157 | + for (i = 0; i < count && !hat; i++) | |
2158 | + /* released below */ | |
2159 | + hat = aa_find_child(root, hats[i]); | |
9474138d | 2160 | + if (!hat) { |
ceaf2cfb | 2161 | + if (!COMPLAIN_MODE(root) || permtest) { |
ceaf2cfb | 2162 | + if (list_empty(&root->base.profiles)) |
76514441 | 2163 | + error = -ECHILD; |
ceaf2cfb | 2164 | + else |
76514441 | 2165 | + error = -ENOENT; |
0eaba94d | 2166 | + goto out; |
ceaf2cfb AM |
2167 | + } |
2168 | + | |
2169 | + /* | |
2170 | + * In complain mode and failed to match any hats. | |
76514441 | 2171 | + * Audit the failure is based off of the first hat |
ceaf2cfb AM |
2172 | + * supplied. This is done due how userspace |
2173 | + * interacts with change_hat. | |
2174 | + * | |
2175 | + * TODO: Add logging of all failed hats | |
2176 | + */ | |
2177 | + | |
2178 | + /* freed below */ | |
2179 | + name = new_compound_name(root->base.hname, hats[0]); | |
76514441 | 2180 | + target = name; |
ceaf2cfb AM |
2181 | + /* released below */ |
2182 | + hat = aa_new_null_profile(profile, 1); | |
9474138d | 2183 | + if (!hat) { |
76514441 AM |
2184 | + info = "failed null profile create"; |
2185 | + error = -ENOMEM; | |
9474138d AM |
2186 | + goto audit; |
2187 | + } | |
0eaba94d | 2188 | + } else { |
76514441 | 2189 | + target = hat->base.hname; |
0eaba94d | 2190 | + if (!PROFILE_IS_HAT(hat)) { |
76514441 AM |
2191 | + info = "target not hat"; |
2192 | + error = -EPERM; | |
0eaba94d AM |
2193 | + goto audit; |
2194 | + } | |
9474138d | 2195 | + } |
2380c486 | 2196 | + |
76514441 AM |
2197 | + error = may_change_ptraced_domain(current, hat); |
2198 | + if (error) { | |
2199 | + info = "ptraced"; | |
2200 | + error = -EPERM; | |
9474138d AM |
2201 | + goto audit; |
2202 | + } | |
2203 | + | |
2204 | + if (!permtest) { | |
76514441 AM |
2205 | + error = aa_set_current_hat(hat, token); |
2206 | + if (error == -EACCES) | |
2207 | + /* kill task in case of brute force attacks */ | |
2208 | + perms.kill = AA_MAY_CHANGEHAT; | |
2209 | + else if (name && !error) | |
0eaba94d | 2210 | + /* reset error for learning of new hats */ |
76514441 | 2211 | + error = -ENOENT; |
9474138d | 2212 | + } |
0eaba94d | 2213 | + } else if (previous_profile) { |
ceaf2cfb AM |
2214 | + /* Return to saved profile. Kill task if restore fails |
2215 | + * to avoid brute force attacks | |
2216 | + */ | |
76514441 AM |
2217 | + target = previous_profile->base.hname; |
2218 | + error = aa_restore_previous_profile(token); | |
2219 | + perms.kill = AA_MAY_CHANGEHAT; | |
ceaf2cfb AM |
2220 | + } else |
2221 | + /* ignore restores when there is no saved profile */ | |
2222 | + goto out; | |
9474138d AM |
2223 | + |
2224 | +audit: | |
2225 | + if (!permtest) | |
76514441 AM |
2226 | + error = aa_audit_file(profile, &perms, GFP_KERNEL, |
2227 | + OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, | |
2228 | + target, 0, info, error); | |
9474138d | 2229 | + |
9474138d AM |
2230 | +out: |
2231 | + aa_put_profile(hat); | |
0eaba94d | 2232 | + kfree(name); |
ceaf2cfb | 2233 | + put_cred(cred); |
9474138d | 2234 | + |
76514441 | 2235 | + return error; |
9474138d AM |
2236 | +} |
2237 | + | |
2238 | +/** | |
2239 | + * aa_change_profile - perform a one-way profile transition | |
76514441 AM |
2240 | + * @ns_name: name of the profile namespace to change to (MAYBE NULL) |
2241 | + * @hname: name of profile to change to (MAYBE NULL) | |
9474138d AM |
2242 | + * @onexec: whether this transition is to take place immediately or at exec |
2243 | + * @permtest: true if this is just a permission test | |
2380c486 | 2244 | + * |
9474138d | 2245 | + * Change to new profile @name. Unlike with hats, there is no way |
76514441 AM |
2246 | + * to change back. If @name isn't specified the current profile name is |
2247 | + * used. | |
2248 | + * If @onexec then the transition is delayed until | |
9474138d | 2249 | + * the next exec. |
2380c486 | 2250 | + * |
9474138d | 2251 | + * Returns %0 on success, error otherwise. |
2380c486 | 2252 | + */ |
76514441 | 2253 | +int aa_change_profile(const char *ns_name, const char *hname, bool onexec, |
ceaf2cfb | 2254 | + bool permtest) |
9474138d AM |
2255 | +{ |
2256 | + const struct cred *cred; | |
ceaf2cfb | 2257 | + struct aa_task_cxt *cxt; |
9474138d AM |
2258 | + struct aa_profile *profile, *target = NULL; |
2259 | + struct aa_namespace *ns = NULL; | |
76514441 AM |
2260 | + struct file_perms perms = {}; |
2261 | + const char *name = NULL, *info = NULL; | |
2262 | + int op, error = 0; | |
2263 | + u32 request; | |
2380c486 | 2264 | + |
ceaf2cfb | 2265 | + if (!hname && !ns_name) |
9474138d | 2266 | + return -EINVAL; |
2380c486 | 2267 | + |
76514441 AM |
2268 | + if (onexec) { |
2269 | + request = AA_MAY_ONEXEC; | |
2270 | + op = OP_CHANGE_ONEXEC; | |
2271 | + } else { | |
2272 | + request = AA_MAY_CHANGE_PROFILE; | |
2273 | + op = OP_CHANGE_PROFILE; | |
2274 | + } | |
9474138d | 2275 | + |
ceaf2cfb | 2276 | + cred = get_current_cred(); |
9474138d | 2277 | + cxt = cred->security; |
ceaf2cfb | 2278 | + profile = aa_cred_profile(cred); |
9474138d AM |
2279 | + |
2280 | + if (ns_name) { | |
ceaf2cfb AM |
2281 | + /* released below */ |
2282 | + ns = aa_find_namespace(profile->ns, ns_name); | |
9474138d AM |
2283 | + if (!ns) { |
2284 | + /* we don't create new namespace in complain mode */ | |
76514441 AM |
2285 | + name = ns_name; |
2286 | + info = "namespace not found"; | |
2287 | + error = -ENOENT; | |
9474138d AM |
2288 | + goto audit; |
2289 | + } | |
76514441 | 2290 | + } else |
ceaf2cfb AM |
2291 | + /* released below */ |
2292 | + ns = aa_get_namespace(profile->ns); | |
9474138d AM |
2293 | + |
2294 | + /* if the name was not specified, use the name of the current profile */ | |
ceaf2cfb AM |
2295 | + if (!hname) { |
2296 | + if (unconfined(profile)) | |
2297 | + hname = ns->unconfined->base.hname; | |
9474138d | 2298 | + else |
ceaf2cfb | 2299 | + hname = profile->base.hname; |
9474138d | 2300 | + } |
9474138d | 2301 | + |
76514441 AM |
2302 | + perms = change_profile_perms(profile, ns, hname, request, |
2303 | + profile->file.start); | |
2304 | + if (!(perms.allow & request)) { | |
2305 | + error = -EACCES; | |
9474138d AM |
2306 | + goto audit; |
2307 | + } | |
2308 | + | |
ceaf2cfb | 2309 | + /* released below */ |
76514441 | 2310 | + target = aa_lookup_profile(ns, hname); |
9474138d | 2311 | + if (!target) { |
76514441 AM |
2312 | + info = "profile not found"; |
2313 | + error = -ENOENT; | |
ceaf2cfb | 2314 | + if (permtest || !COMPLAIN_MODE(profile)) |
9474138d | 2315 | + goto audit; |
76514441 | 2316 | + /* released below */ |
ceaf2cfb AM |
2317 | + target = aa_new_null_profile(profile, 0); |
2318 | + if (!target) { | |
76514441 AM |
2319 | + info = "failed null profile create"; |
2320 | + error = -ENOMEM; | |
ceaf2cfb AM |
2321 | + goto audit; |
2322 | + } | |
9474138d | 2323 | + } |
ceaf2cfb | 2324 | + |
9474138d | 2325 | + /* check if tracing task is allowed to trace target domain */ |
76514441 AM |
2326 | + error = may_change_ptraced_domain(current, target); |
2327 | + if (error) { | |
2328 | + info = "ptrace prevents transition"; | |
9474138d AM |
2329 | + goto audit; |
2330 | + } | |
2331 | + | |
2332 | + if (permtest) | |
2333 | + goto audit; | |
2334 | + | |
2335 | + if (onexec) | |
76514441 | 2336 | + error = aa_set_current_onexec(target); |
9474138d | 2337 | + else |
76514441 | 2338 | + error = aa_replace_current_profile(target); |
2380c486 | 2339 | + |
9474138d AM |
2340 | +audit: |
2341 | + if (!permtest) | |
76514441 AM |
2342 | + error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, |
2343 | + name, hname, 0, info, error); | |
9474138d AM |
2344 | + |
2345 | + aa_put_namespace(ns); | |
2346 | + aa_put_profile(target); | |
ceaf2cfb | 2347 | + put_cred(cred); |
9474138d | 2348 | + |
76514441 | 2349 | + return error; |
9474138d | 2350 | +} |
ceaf2cfb AM |
2351 | diff --git a/security/apparmor/file.c b/security/apparmor/file.c |
2352 | new file mode 100644 | |
76514441 | 2353 | index 0000000..7312db7 |
ceaf2cfb AM |
2354 | --- /dev/null |
2355 | +++ b/security/apparmor/file.c | |
76514441 | 2356 | @@ -0,0 +1,457 @@ |
2380c486 | 2357 | +/* |
9474138d AM |
2358 | + * AppArmor security module |
2359 | + * | |
2360 | + * This file contains AppArmor mediation of files | |
2361 | + * | |
2362 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 2363 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
2364 | + * |
2365 | + * This program is free software; you can redistribute it and/or | |
2366 | + * modify it under the terms of the GNU General Public License as | |
2367 | + * published by the Free Software Foundation, version 2 of the | |
2368 | + * License. | |
2380c486 | 2369 | + */ |
2380c486 | 2370 | + |
9474138d AM |
2371 | +#include "include/apparmor.h" |
2372 | +#include "include/audit.h" | |
2373 | +#include "include/file.h" | |
2374 | +#include "include/match.h" | |
2375 | +#include "include/path.h" | |
2376 | +#include "include/policy.h" | |
2377 | + | |
2378 | +struct file_perms nullperms; | |
2379 | + | |
76514441 | 2380 | + |
ceaf2cfb | 2381 | +/** |
76514441 AM |
2382 | + * audit_file_mask - convert mask to permission string |
2383 | + * @buffer: buffer to write string to (NOT NULL) | |
ceaf2cfb | 2384 | + * @mask: permission mask to convert |
ceaf2cfb | 2385 | + */ |
76514441 | 2386 | +static void audit_file_mask(struct audit_buffer *ab, u32 mask) |
9474138d | 2387 | +{ |
76514441 AM |
2388 | + char str[10]; |
2389 | + | |
2390 | + char *m = str; | |
9474138d AM |
2391 | + |
2392 | + if (mask & AA_EXEC_MMAP) | |
2393 | + *m++ = 'm'; | |
76514441 | 2394 | + if (mask & (MAY_READ | AA_MAY_META_READ)) |
9474138d | 2395 | + *m++ = 'r'; |
76514441 AM |
2396 | + if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD | |
2397 | + AA_MAY_CHOWN)) | |
9474138d AM |
2398 | + *m++ = 'w'; |
2399 | + else if (mask & MAY_APPEND) | |
2400 | + *m++ = 'a'; | |
ceaf2cfb AM |
2401 | + if (mask & AA_MAY_CREATE) |
2402 | + *m++ = 'c'; | |
2403 | + if (mask & AA_MAY_DELETE) | |
2404 | + *m++ = 'd'; | |
9474138d AM |
2405 | + if (mask & AA_MAY_LINK) |
2406 | + *m++ = 'l'; | |
2407 | + if (mask & AA_MAY_LOCK) | |
2408 | + *m++ = 'k'; | |
ceaf2cfb | 2409 | + if (mask & MAY_EXEC) |
9474138d | 2410 | + *m++ = 'x'; |
76514441 | 2411 | + *m = '\0'; |
2380c486 | 2412 | + |
ceaf2cfb | 2413 | + audit_log_string(ab, str); |
9474138d | 2414 | +} |
2380c486 | 2415 | + |
ceaf2cfb AM |
2416 | +/** |
2417 | + * file_audit_cb - call back for file specific audit fields | |
2418 | + * @ab: audit_buffer (NOT NULL) | |
2419 | + * @va: audit struct to audit values of (NOT NULL) | |
2420 | + */ | |
76514441 | 2421 | +static void file_audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 2422 | +{ |
76514441 AM |
2423 | + struct common_audit_data *sa = va; |
2424 | + uid_t fsuid = current_fsuid(); | |
2380c486 | 2425 | + |
76514441 | 2426 | + if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { |
ceaf2cfb | 2427 | + audit_log_format(ab, " requested_mask="); |
76514441 | 2428 | + audit_file_mask(ab, sa->aad.fs.request); |
ceaf2cfb | 2429 | + } |
76514441 | 2430 | + if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) { |
ceaf2cfb | 2431 | + audit_log_format(ab, " denied_mask="); |
76514441 | 2432 | + audit_file_mask(ab, sa->aad.fs.denied); |
ceaf2cfb | 2433 | + } |
76514441 | 2434 | + if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { |
9474138d | 2435 | + audit_log_format(ab, " fsuid=%d", fsuid); |
76514441 | 2436 | + audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid); |
9474138d | 2437 | + } |
2380c486 | 2438 | + |
76514441 AM |
2439 | + if (sa->aad.fs.target) { |
2440 | + audit_log_format(ab, " target="); | |
2441 | + audit_log_untrustedstring(ab, sa->aad.fs.target); | |
9474138d AM |
2442 | + } |
2443 | +} | |
2380c486 | 2444 | + |
ceaf2cfb AM |
2445 | +/** |
2446 | + * aa_audit_file - handle the auditing of file operations | |
2447 | + * @profile: the profile being enforced (NOT NULL) | |
76514441 AM |
2448 | + * @perms: the permissions computed for the request (NOT NULL) |
2449 | + * @gfp: allocation flags | |
2450 | + * @op: operation being mediated | |
2451 | + * @request: permissions requested | |
2452 | + * @name: name of object being mediated (MAYBE NULL) | |
2453 | + * @target: name of target (MAYBE NULL) | |
2454 | + * @ouid: object uid | |
2455 | + * @info: extra information message (MAYBE NULL) | |
2456 | + * @error: 0 if operation allowed else failure error code | |
ceaf2cfb AM |
2457 | + * |
2458 | + * Returns: %0 or error on failure | |
2459 | + */ | |
76514441 AM |
2460 | +int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, |
2461 | + gfp_t gfp, int op, u32 request, const char *name, | |
2462 | + const char *target, uid_t ouid, const char *info, int error) | |
9474138d AM |
2463 | +{ |
2464 | + int type = AUDIT_APPARMOR_AUTO; | |
76514441 AM |
2465 | + struct common_audit_data sa; |
2466 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
2467 | + sa.aad.op = op, | |
2468 | + sa.aad.fs.request = request; | |
2469 | + sa.aad.name = name; | |
2470 | + sa.aad.fs.target = target; | |
2471 | + sa.aad.fs.ouid = ouid; | |
2472 | + sa.aad.info = info; | |
2473 | + sa.aad.error = error; | |
2474 | + | |
2475 | + if (likely(!sa.aad.error)) { | |
2476 | + u32 mask = perms->audit; | |
2380c486 | 2477 | + |
ceaf2cfb | 2478 | + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
9474138d | 2479 | + mask = 0xffff; |
2380c486 | 2480 | + |
9474138d | 2481 | + /* mask off perms that are not being force audited */ |
76514441 | 2482 | + sa.aad.fs.request &= mask; |
2380c486 | 2483 | + |
76514441 | 2484 | + if (likely(!sa.aad.fs.request)) |
9474138d | 2485 | + return 0; |
5e665963 | 2486 | + type = AUDIT_APPARMOR_AUDIT; |
9474138d | 2487 | + } else { |
ceaf2cfb | 2488 | + /* only report permissions that were denied */ |
76514441 | 2489 | + sa.aad.fs.request = sa.aad.fs.request & ~perms->allow; |
2380c486 | 2490 | + |
76514441 | 2491 | + if (sa.aad.fs.request & perms->kill) |
9474138d | 2492 | + type = AUDIT_APPARMOR_KILL; |
2380c486 | 2493 | + |
ceaf2cfb | 2494 | + /* quiet known rejects, assumes quiet and kill do not overlap */ |
76514441 | 2495 | + if ((sa.aad.fs.request & perms->quiet) && |
ceaf2cfb AM |
2496 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && |
2497 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
76514441 | 2498 | + sa.aad.fs.request &= ~perms->quiet; |
2380c486 | 2499 | + |
76514441 AM |
2500 | + if (!sa.aad.fs.request) |
2501 | + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; | |
9474138d | 2502 | + } |
76514441 AM |
2503 | + |
2504 | + sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow; | |
2505 | + return aa_audit(type, profile, gfp, &sa, file_audit_cb); | |
2506 | +} | |
2507 | + | |
2508 | +/** | |
2509 | + * map_old_perms - map old file perms layout to the new layout | |
2510 | + * @old: permission set in old mapping | |
2511 | + * | |
2512 | + * Returns: new permission mapping | |
2513 | + */ | |
2514 | +static u32 map_old_perms(u32 old) | |
2515 | +{ | |
2516 | + u32 new = old & 0xf; | |
2517 | + if (old & MAY_READ) | |
2518 | + new |= AA_MAY_META_READ; | |
2519 | + if (old & MAY_WRITE) | |
2520 | + new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE | | |
2521 | + AA_MAY_CHMOD | AA_MAY_CHOWN; | |
2522 | + if (old & 0x10) | |
2523 | + new |= AA_MAY_LINK; | |
2524 | + /* the old mapping lock and link_subset flags where overlaid | |
2525 | + * and use was determined by part of a pair that they were in | |
2526 | + */ | |
2527 | + if (old & 0x20) | |
2528 | + new |= AA_MAY_LOCK | AA_LINK_SUBSET; | |
2529 | + if (old & 0x40) /* AA_EXEC_MMAP */ | |
2530 | + new |= AA_EXEC_MMAP; | |
2531 | + | |
2532 | + new |= AA_MAY_META_READ; | |
2533 | + | |
2534 | + return new; | |
9474138d | 2535 | +} |
2380c486 | 2536 | + |
ceaf2cfb | 2537 | +/** |
76514441 | 2538 | + * compute_perms - convert dfa compressed perms to internal perms |
ceaf2cfb AM |
2539 | + * @dfa: dfa to compute perms for (NOT NULL) |
2540 | + * @state: state in dfa | |
2541 | + * @cond: conditions to consider (NOT NULL) | |
2542 | + * | |
2543 | + * TODO: convert from dfa + state to permission entry, do computation conversion | |
2544 | + * at load time. | |
2545 | + * | |
2546 | + * Returns: computed permission set | |
2547 | + */ | |
76514441 AM |
2548 | +static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, |
2549 | + struct path_cond *cond) | |
9474138d AM |
2550 | +{ |
2551 | + struct file_perms perms; | |
2380c486 | 2552 | + |
ceaf2cfb AM |
2553 | + /* FIXME: change over to new dfa format |
2554 | + * currently file perms are encoded in the dfa, new format | |
2555 | + * splits the permissions from the dfa. This mapping can be | |
2556 | + * done at profile load | |
2557 | + */ | |
9474138d | 2558 | + perms.kill = 0; |
2380c486 | 2559 | + |
9474138d | 2560 | + if (current_fsuid() == cond->uid) { |
76514441 AM |
2561 | + perms.allow = map_old_perms(dfa_user_allow(dfa, state)); |
2562 | + perms.audit = map_old_perms(dfa_user_audit(dfa, state)); | |
2563 | + perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); | |
9474138d AM |
2564 | + perms.xindex = dfa_user_xindex(dfa, state); |
2565 | + } else { | |
76514441 AM |
2566 | + perms.allow = map_old_perms(dfa_other_allow(dfa, state)); |
2567 | + perms.audit = map_old_perms(dfa_other_audit(dfa, state)); | |
2568 | + perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); | |
9474138d AM |
2569 | + perms.xindex = dfa_other_xindex(dfa, state); |
2570 | + } | |
2380c486 | 2571 | + |
9474138d | 2572 | + /* change_profile wasn't determined by ownership in old mapping */ |
5e665963 | 2573 | + if (ACCEPT_TABLE(dfa)[state] & 0x80000000) |
76514441 | 2574 | + perms.allow |= AA_MAY_CHANGE_PROFILE; |
2380c486 | 2575 | + |
9474138d AM |
2576 | + return perms; |
2577 | +} | |
2380c486 | 2578 | + |
ceaf2cfb AM |
2579 | +/** |
2580 | + * aa_str_perms - find permission that match @name | |
76514441 | 2581 | + * @dfa: to match against (MAYBE NULL) |
ceaf2cfb AM |
2582 | + * @state: state to start matching in |
2583 | + * @name: string to match against dfa (NOT NULL) | |
2584 | + * @cond: conditions to consider for permission set computation (NOT NULL) | |
76514441 | 2585 | + * @perms: Returns - the permissions found when matching @name |
ceaf2cfb | 2586 | + * |
76514441 | 2587 | + * Returns: the final state in @dfa when beginning @start and walking @name |
ceaf2cfb | 2588 | + */ |
76514441 AM |
2589 | +unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, |
2590 | + const char *name, struct path_cond *cond, | |
2591 | + struct file_perms *perms) | |
9474138d AM |
2592 | +{ |
2593 | + unsigned int state; | |
76514441 AM |
2594 | + if (!dfa) { |
2595 | + *perms = nullperms; | |
2596 | + return DFA_NOMATCH; | |
2597 | + } | |
2380c486 | 2598 | + |
9474138d | 2599 | + state = aa_dfa_match(dfa, start, name); |
76514441 | 2600 | + *perms = compute_perms(dfa, state, cond); |
2380c486 | 2601 | + |
76514441 | 2602 | + return state; |
9474138d | 2603 | +} |
2380c486 | 2604 | + |
ceaf2cfb | 2605 | +/** |
76514441 AM |
2606 | + * is_deleted - test if a file has been completely unlinked |
2607 | + * @dentry: dentry of file to test for deletion (NOT NULL) | |
ceaf2cfb | 2608 | + * |
76514441 | 2609 | + * Returns: %1 if deleted else %0 |
ceaf2cfb | 2610 | + */ |
76514441 | 2611 | +static inline bool is_deleted(struct dentry *dentry) |
9474138d | 2612 | +{ |
76514441 AM |
2613 | + if (d_unlinked(dentry) && dentry->d_inode->i_nlink == 0) |
2614 | + return 1; | |
2615 | + return 0; | |
9474138d | 2616 | +} |
2380c486 | 2617 | + |
ceaf2cfb AM |
2618 | +/** |
2619 | + * aa_path_perm - do permissions check & audit for @path | |
76514441 | 2620 | + * @op: operation being checked |
ceaf2cfb | 2621 | + * @profile: profile being enforced (NOT NULL) |
ceaf2cfb | 2622 | + * @path: path to check permissions of (NOT NULL) |
76514441 | 2623 | + * @flags: any additional path flags beyond what the profile specifies |
ceaf2cfb AM |
2624 | + * @request: requested permissions |
2625 | + * @cond: conditional info for this request (NOT NULL) | |
2626 | + * | |
2627 | + * Returns: %0 else error if access denied or other error | |
2628 | + */ | |
76514441 AM |
2629 | +int aa_path_perm(int op, struct aa_profile *profile, struct path *path, |
2630 | + int flags, u32 request, struct path_cond *cond) | |
9474138d | 2631 | +{ |
76514441 AM |
2632 | + char *buffer = NULL; |
2633 | + struct file_perms perms = {}; | |
2634 | + const char *name, *info = NULL; | |
2635 | + int error; | |
2636 | + | |
2637 | + flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); | |
2638 | + error = aa_get_name(path, flags, &buffer, &name); | |
2639 | + if (error) { | |
2640 | + if (error == -ENOENT && is_deleted(path->dentry)) { | |
2641 | + /* Access to open files that are deleted are | |
2642 | + * give a pass (implicit delegation) | |
2643 | + */ | |
2644 | + error = 0; | |
2645 | + perms.allow = request; | |
2646 | + } else if (error == -ENOENT) | |
2647 | + info = "Failed name lookup - deleted entry"; | |
2648 | + else if (error == -ESTALE) | |
2649 | + info = "Failed name lookup - disconnected path"; | |
2650 | + else if (error == -ENAMETOOLONG) | |
2651 | + info = "Failed name lookup - name too long"; | |
9474138d | 2652 | + else |
76514441 | 2653 | + info = "Failed name lookup"; |
9474138d | 2654 | + } else { |
76514441 AM |
2655 | + aa_str_perms(profile->file.dfa, profile->file.start, name, cond, |
2656 | + &perms); | |
2657 | + if (request & ~perms.allow) | |
2658 | + error = -EACCES; | |
9474138d | 2659 | + } |
76514441 AM |
2660 | + error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, |
2661 | + NULL, cond->uid, info, error); | |
9474138d | 2662 | + kfree(buffer); |
2380c486 | 2663 | + |
76514441 | 2664 | + return error; |
9474138d | 2665 | +} |
2380c486 | 2666 | + |
ceaf2cfb AM |
2667 | +/** |
2668 | + * xindex_is_subset - helper for aa_path_link | |
2669 | + * @link: link permission set | |
2670 | + * @target: target permission set | |
2671 | + * | |
2672 | + * test target x permissions are equal OR a subset of link x permissions | |
2673 | + * this is done as part of the subset test, where a hardlink must have | |
2674 | + * a subset of permissions that the target has. | |
2675 | + * | |
2676 | + * Returns: %1 if subset else %0 | |
2677 | + */ | |
76514441 | 2678 | +static inline bool xindex_is_subset(u32 link, u32 target) |
ceaf2cfb AM |
2679 | +{ |
2680 | + if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || | |
2681 | + ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) | |
2682 | + return 0; | |
2683 | + | |
2684 | + return 1; | |
2685 | +} | |
2686 | + | |
2687 | +/** | |
2688 | + * aa_path_link - Handle hard link permission check | |
2689 | + * @profile: the profile being enforced (NOT NULL) | |
2690 | + * @old_dentry: the target dentry (NOT NULL) | |
2691 | + * @new_dir: directory the new link will be created in (NOT NULL) | |
2692 | + * @new_dentry: the link being created (NOT NULL) | |
2693 | + * | |
2694 | + * Handle the permission test for a link & target pair. Permission | |
2695 | + * is encoded as a pair where the link permission is determined | |
2696 | + * first, and if allowed, the target is tested. The target test | |
2697 | + * is done from the point of the link match (not start of DFA) | |
2698 | + * making the target permission dependent on the link permission match. | |
2699 | + * | |
2700 | + * The subset test if required forces that permissions granted | |
2701 | + * on link are a subset of the permission granted to target. | |
2702 | + * | |
2703 | + * Returns: %0 if allowed else error | |
2704 | + */ | |
9474138d AM |
2705 | +int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, |
2706 | + struct path *new_dir, struct dentry *new_dentry) | |
2707 | +{ | |
2708 | + struct path link = { new_dir->mnt, new_dentry }; | |
2709 | + struct path target = { new_dir->mnt, old_dentry }; | |
ceaf2cfb AM |
2710 | + struct path_cond cond = { |
2711 | + old_dentry->d_inode->i_uid, | |
2712 | + old_dentry->d_inode->i_mode | |
2713 | + }; | |
9474138d | 2714 | + char *buffer = NULL, *buffer2 = NULL; |
76514441 AM |
2715 | + const char *lname, *tname = NULL, *info = NULL; |
2716 | + struct file_perms lperms, perms; | |
2717 | + u32 request = AA_MAY_LINK; | |
9474138d | 2718 | + unsigned int state; |
76514441 AM |
2719 | + int error; |
2720 | + | |
2721 | + lperms = nullperms; | |
2380c486 | 2722 | + |
ceaf2cfb | 2723 | + /* buffer freed below, lname is pointer in buffer */ |
76514441 AM |
2724 | + error = aa_get_name(&link, profile->path_flags, &buffer, &lname); |
2725 | + if (error) | |
9474138d | 2726 | + goto audit; |
2380c486 | 2727 | + |
ceaf2cfb | 2728 | + /* buffer2 freed below, tname is pointer in buffer2 */ |
76514441 AM |
2729 | + error = aa_get_name(&target, profile->path_flags, &buffer2, &tname); |
2730 | + if (error) | |
9474138d | 2731 | + goto audit; |
2380c486 | 2732 | + |
76514441 | 2733 | + error = -EACCES; |
ceaf2cfb | 2734 | + /* aa_str_perms - handles the case of the dfa being NULL */ |
76514441 AM |
2735 | + state = aa_str_perms(profile->file.dfa, profile->file.start, lname, |
2736 | + &cond, &lperms); | |
2380c486 | 2737 | + |
76514441 | 2738 | + if (!(lperms.allow & AA_MAY_LINK)) |
9474138d | 2739 | + goto audit; |
2380c486 | 2740 | + |
9474138d | 2741 | + /* test to see if target can be paired with link */ |
76514441 AM |
2742 | + state = aa_dfa_null_transition(profile->file.dfa, state); |
2743 | + aa_str_perms(profile->file.dfa, state, tname, &cond, &perms); | |
2744 | + | |
2745 | + /* force audit/quiet masks for link are stored in the second entry | |
2746 | + * in the link pair. | |
2747 | + */ | |
2748 | + lperms.audit = perms.audit; | |
2749 | + lperms.quiet = perms.quiet; | |
2750 | + lperms.kill = perms.kill; | |
2751 | + | |
2752 | + if (!(perms.allow & AA_MAY_LINK)) { | |
2753 | + info = "target restricted"; | |
9474138d AM |
2754 | + goto audit; |
2755 | + } | |
2380c486 | 2756 | + |
9474138d | 2757 | + /* done if link subset test is not required */ |
76514441 | 2758 | + if (!(perms.allow & AA_LINK_SUBSET)) |
ceaf2cfb | 2759 | + goto done_tests; |
2380c486 | 2760 | + |
9474138d AM |
2761 | + /* Do link perm subset test requiring allowed permission on link are a |
2762 | + * subset of the allowed permissions on target. | |
2763 | + */ | |
76514441 AM |
2764 | + aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond, |
2765 | + &perms); | |
2380c486 | 2766 | + |
9474138d | 2767 | + /* AA_MAY_LINK is not considered in the subset test */ |
76514441 AM |
2768 | + request = lperms.allow & ~AA_MAY_LINK; |
2769 | + lperms.allow &= perms.allow | AA_MAY_LINK; | |
9474138d | 2770 | + |
76514441 AM |
2771 | + request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); |
2772 | + if (request & ~lperms.allow) { | |
ceaf2cfb | 2773 | + goto audit; |
76514441 AM |
2774 | + } else if ((lperms.allow & MAY_EXEC) && |
2775 | + !xindex_is_subset(lperms.xindex, perms.xindex)) { | |
2776 | + lperms.allow &= ~MAY_EXEC; | |
2777 | + request |= MAY_EXEC; | |
2778 | + info = "link not subset of target"; | |
ceaf2cfb | 2779 | + goto audit; |
9474138d | 2780 | + } |
2380c486 | 2781 | + |
ceaf2cfb | 2782 | +done_tests: |
76514441 | 2783 | + error = 0; |
ceaf2cfb | 2784 | + |
9474138d | 2785 | +audit: |
76514441 AM |
2786 | + error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, |
2787 | + lname, tname, cond.uid, info, error); | |
9474138d AM |
2788 | + kfree(buffer); |
2789 | + kfree(buffer2); | |
2380c486 | 2790 | + |
76514441 | 2791 | + return error; |
9474138d | 2792 | +} |
2380c486 | 2793 | + |
ceaf2cfb | 2794 | +/** |
76514441 AM |
2795 | + * aa_file_perm - do permission revalidation check & audit for @file |
2796 | + * @op: operation being checked | |
ceaf2cfb | 2797 | + * @profile: profile being enforced (NOT NULL) |
76514441 | 2798 | + * @file: file to revalidate access permissions on (NOT NULL) |
ceaf2cfb | 2799 | + * @request: requested permissions |
ceaf2cfb | 2800 | + * |
76514441 | 2801 | + * Returns: %0 if access allowed else error |
ceaf2cfb | 2802 | + */ |
76514441 AM |
2803 | +int aa_file_perm(int op, struct aa_profile *profile, struct file *file, |
2804 | + u32 request) | |
9474138d | 2805 | +{ |
ceaf2cfb AM |
2806 | + struct path_cond cond = { |
2807 | + .uid = file->f_path.dentry->d_inode->i_uid, | |
2808 | + .mode = file->f_path.dentry->d_inode->i_mode | |
2809 | + }; | |
2380c486 | 2810 | + |
76514441 AM |
2811 | + return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED, |
2812 | + request, &cond); | |
2380c486 | 2813 | +} |
ceaf2cfb AM |
2814 | diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h |
2815 | new file mode 100644 | |
76514441 | 2816 | index 0000000..38ccaea |
ceaf2cfb AM |
2817 | --- /dev/null |
2818 | +++ b/security/apparmor/include/apparmor.h | |
76514441 | 2819 | @@ -0,0 +1,92 @@ |
9474138d AM |
2820 | +/* |
2821 | + * AppArmor security module | |
2822 | + * | |
2823 | + * This file contains AppArmor basic global and lib definitions | |
2824 | + * | |
2825 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 2826 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
2827 | + * |
2828 | + * This program is free software; you can redistribute it and/or | |
2829 | + * modify it under the terms of the GNU General Public License as | |
2830 | + * published by the Free Software Foundation, version 2 of the | |
2831 | + * License. | |
2832 | + */ | |
2380c486 | 2833 | + |
9474138d AM |
2834 | +#ifndef __APPARMOR_H |
2835 | +#define __APPARMOR_H | |
2380c486 | 2836 | + |
9474138d | 2837 | +#include <linux/fs.h> |
2380c486 | 2838 | + |
ceaf2cfb AM |
2839 | +#include "match.h" |
2840 | + | |
76514441 | 2841 | +/* Control parameters settable through module/boot flags */ |
ceaf2cfb AM |
2842 | +extern enum audit_mode aa_g_audit; |
2843 | +extern int aa_g_audit_header; | |
2844 | +extern int aa_g_debug; | |
2845 | +extern int aa_g_lock_policy; | |
2846 | +extern int aa_g_logsyscall; | |
2847 | +extern int aa_g_paranoid_load; | |
2848 | +extern unsigned int aa_g_path_max; | |
2380c486 | 2849 | + |
9474138d AM |
2850 | +/* |
2851 | + * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | |
2852 | + * which is not related to profile accesses. | |
2853 | + */ | |
2380c486 | 2854 | + |
9474138d AM |
2855 | +#define AA_DEBUG(fmt, args...) \ |
2856 | + do { \ | |
ceaf2cfb | 2857 | + if (aa_g_debug && printk_ratelimit()) \ |
9474138d AM |
2858 | + printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ |
2859 | + } while (0) | |
2380c486 | 2860 | + |
9474138d AM |
2861 | +#define AA_ERROR(fmt, args...) \ |
2862 | + do { \ | |
2863 | + if (printk_ratelimit()) \ | |
2864 | + printk(KERN_ERR "AppArmor: " fmt, ##args); \ | |
2865 | + } while (0) | |
2866 | + | |
2867 | +/* Flag indicating whether initialization completed */ | |
76514441 | 2868 | +extern int apparmor_initialized __initdata; |
2380c486 | 2869 | + |
9474138d | 2870 | +/* fn's in lib */ |
ceaf2cfb | 2871 | +char *aa_split_fqname(char *args, char **ns_name); |
ceaf2cfb | 2872 | +void aa_info_message(const char *str); |
76514441 AM |
2873 | +void *kvmalloc(size_t size); |
2874 | +void kvfree(void *buffer); | |
2875 | + | |
2876 | + | |
2877 | +/** | |
2878 | + * aa_strneq - compare null terminated @str to a non null terminated substring | |
2879 | + * @str: a null terminated string | |
2880 | + * @sub: a substring, not necessarily null terminated | |
2881 | + * @len: length of @sub to compare | |
2882 | + * | |
2883 | + * The @str string must be full consumed for this to be considered a match | |
2884 | + */ | |
2885 | +static inline bool aa_strneq(const char *str, const char *sub, int len) | |
2886 | +{ | |
2887 | + return !strncmp(str, sub, len) && !str[len]; | |
2888 | +} | |
ceaf2cfb AM |
2889 | + |
2890 | +/** | |
2891 | + * aa_dfa_null_transition - step to next state after null character | |
2892 | + * @dfa: the dfa to match against | |
2893 | + * @start: the state of the dfa to start matching in | |
ceaf2cfb AM |
2894 | + * |
2895 | + * aa_dfa_null_transition transitions to the next state after a null | |
2896 | + * character which is not used in standard matching and is only | |
76514441 | 2897 | + * used to separate pairs. |
ceaf2cfb AM |
2898 | + */ |
2899 | +static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | |
76514441 | 2900 | + unsigned int start) |
ceaf2cfb | 2901 | +{ |
76514441 AM |
2902 | + /* the null transition only needs the string's null terminator byte */ |
2903 | + return aa_dfa_match_len(dfa, start, "", 1); | |
ceaf2cfb | 2904 | +} |
2380c486 | 2905 | + |
ceaf2cfb | 2906 | +static inline bool mediated_filesystem(struct inode *inode) |
2380c486 | 2907 | +{ |
9474138d | 2908 | + return !(inode->i_sb->s_flags & MS_NOUSER); |
2380c486 JR |
2909 | +} |
2910 | + | |
ceaf2cfb AM |
2911 | +#endif /* __APPARMOR_H */ |
2912 | diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h | |
2913 | new file mode 100644 | |
76514441 | 2914 | index 0000000..14f955c |
ceaf2cfb AM |
2915 | --- /dev/null |
2916 | +++ b/security/apparmor/include/apparmorfs.h | |
76514441 | 2917 | @@ -0,0 +1,26 @@ |
ceaf2cfb AM |
2918 | +/* |
2919 | + * AppArmor security module | |
2920 | + * | |
2921 | + * This file contains AppArmor filesystem definitions. | |
2922 | + * | |
2923 | + * Copyright (C) 1998-2008 Novell/SUSE | |
2924 | + * Copyright 2009-2010 Canonical Ltd. | |
2925 | + * | |
2926 | + * This program is free software; you can redistribute it and/or | |
2927 | + * modify it under the terms of the GNU General Public License as | |
2928 | + * published by the Free Software Foundation, version 2 of the | |
2929 | + * License. | |
2930 | + */ | |
2931 | + | |
2932 | +#ifndef __AA_APPARMORFS_H | |
2933 | +#define __AA_APPARMORFS_H | |
2934 | + | |
76514441 | 2935 | +extern void __init aa_destroy_aafs(void); |
ceaf2cfb AM |
2936 | + |
2937 | +#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 | |
2938 | +extern const struct file_operations aa_fs_matching_fops; | |
2939 | +extern const struct file_operations aa_fs_features_fops; | |
76514441 | 2940 | +extern const struct file_operations aa_fs_profiles_fops; |
ceaf2cfb | 2941 | +#endif |
2380c486 | 2942 | + |
ceaf2cfb AM |
2943 | +#endif /* __AA_APPARMORFS_H */ |
2944 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h | |
2945 | new file mode 100644 | |
76514441 | 2946 | index 0000000..1951786 |
ceaf2cfb AM |
2947 | --- /dev/null |
2948 | +++ b/security/apparmor/include/audit.h | |
76514441 | 2949 | @@ -0,0 +1,123 @@ |
9474138d AM |
2950 | +/* |
2951 | + * AppArmor security module | |
2952 | + * | |
2953 | + * This file contains AppArmor auditing function definitions. | |
2954 | + * | |
2955 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 2956 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
2957 | + * |
2958 | + * This program is free software; you can redistribute it and/or | |
2959 | + * modify it under the terms of the GNU General Public License as | |
2960 | + * published by the Free Software Foundation, version 2 of the | |
2961 | + * License. | |
2962 | + */ | |
2380c486 | 2963 | + |
9474138d AM |
2964 | +#ifndef __AA_AUDIT_H |
2965 | +#define __AA_AUDIT_H | |
2380c486 | 2966 | + |
9474138d AM |
2967 | +#include <linux/audit.h> |
2968 | +#include <linux/fs.h> | |
76514441 | 2969 | +#include <linux/lsm_audit.h> |
9474138d AM |
2970 | +#include <linux/sched.h> |
2971 | +#include <linux/slab.h> | |
2380c486 | 2972 | + |
76514441 AM |
2973 | +#include "file.h" |
2974 | + | |
9474138d | 2975 | +struct aa_profile; |
2380c486 | 2976 | + |
9474138d AM |
2977 | +extern const char *audit_mode_names[]; |
2978 | +#define AUDIT_MAX_INDEX 5 | |
2380c486 | 2979 | + |
9474138d | 2980 | +#define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ |
2380c486 | 2981 | + |
9474138d AM |
2982 | +enum audit_mode { |
2983 | + AUDIT_NORMAL, /* follow normal auditing of accesses */ | |
2984 | + AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | |
2985 | + AUDIT_QUIET, /* quiet all messages */ | |
2986 | + AUDIT_NOQUIET, /* do not quiet audit messages */ | |
2987 | + AUDIT_ALL /* audit all accesses */ | |
2380c486 JR |
2988 | +}; |
2989 | + | |
76514441 AM |
2990 | +enum audit_type { |
2991 | + AUDIT_APPARMOR_AUDIT, | |
2992 | + AUDIT_APPARMOR_ALLOWED, | |
2993 | + AUDIT_APPARMOR_DENIED, | |
2994 | + AUDIT_APPARMOR_HINT, | |
2995 | + AUDIT_APPARMOR_STATUS, | |
2996 | + AUDIT_APPARMOR_ERROR, | |
2997 | + AUDIT_APPARMOR_KILL | |
9474138d | 2998 | +}; |
2380c486 | 2999 | + |
76514441 AM |
3000 | +extern const char *op_table[]; |
3001 | +enum aa_ops { | |
3002 | + OP_NULL, | |
3003 | + | |
3004 | + OP_SYSCTL, | |
3005 | + OP_CAPABLE, | |
3006 | + | |
3007 | + OP_UNLINK, | |
3008 | + OP_MKDIR, | |
3009 | + OP_RMDIR, | |
3010 | + OP_MKNOD, | |
3011 | + OP_TRUNC, | |
3012 | + OP_LINK, | |
3013 | + OP_SYMLINK, | |
3014 | + OP_RENAME_SRC, | |
3015 | + OP_RENAME_DEST, | |
3016 | + OP_CHMOD, | |
3017 | + OP_CHOWN, | |
3018 | + OP_GETATTR, | |
3019 | + OP_OPEN, | |
3020 | + | |
3021 | + OP_FPERM, | |
3022 | + OP_FLOCK, | |
3023 | + OP_FMMAP, | |
3024 | + OP_FMPROT, | |
3025 | + | |
3026 | + OP_CREATE, | |
3027 | + OP_POST_CREATE, | |
3028 | + OP_BIND, | |
3029 | + OP_CONNECT, | |
3030 | + OP_LISTEN, | |
3031 | + OP_ACCEPT, | |
3032 | + OP_SENDMSG, | |
3033 | + OP_RECVMSG, | |
3034 | + OP_GETSOCKNAME, | |
3035 | + OP_GETPEERNAME, | |
3036 | + OP_GETSOCKOPT, | |
3037 | + OP_SETSOCKOPT, | |
3038 | + OP_SOCK_SHUTDOWN, | |
3039 | + | |
3040 | + OP_PTRACE, | |
3041 | + | |
3042 | + OP_EXEC, | |
3043 | + OP_CHANGE_HAT, | |
3044 | + OP_CHANGE_PROFILE, | |
3045 | + OP_CHANGE_ONEXEC, | |
3046 | + | |
3047 | + OP_SETPROCATTR, | |
3048 | + OP_SETRLIMIT, | |
3049 | + | |
3050 | + OP_PROF_REPL, | |
3051 | + OP_PROF_LOAD, | |
3052 | + OP_PROF_RM, | |
3053 | +}; | |
3054 | + | |
3055 | + | |
3056 | +/* define a short hand for apparmor_audit_data portion of common_audit_data */ | |
3057 | +#define aad apparmor_audit_data | |
3058 | + | |
3059 | +void aa_audit_msg(int type, struct common_audit_data *sa, | |
3060 | + void (*cb) (struct audit_buffer *, void *)); | |
3061 | +int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | |
3062 | + struct common_audit_data *sa, | |
3063 | + void (*cb) (struct audit_buffer *, void *)); | |
3064 | + | |
3065 | +static inline int complain_error(int error) | |
3066 | +{ | |
3067 | + if (error == -EPERM || error == -EACCES) | |
3068 | + return 0; | |
3069 | + return error; | |
3070 | +} | |
ceaf2cfb AM |
3071 | + |
3072 | +#endif /* __AA_AUDIT_H */ | |
3073 | diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h | |
3074 | new file mode 100644 | |
76514441 | 3075 | index 0000000..c24d295 |
ceaf2cfb AM |
3076 | --- /dev/null |
3077 | +++ b/security/apparmor/include/capability.h | |
9474138d AM |
3078 | @@ -0,0 +1,45 @@ |
3079 | +/* | |
3080 | + * AppArmor security module | |
3081 | + * | |
3082 | + * This file contains AppArmor capability mediation definitions. | |
3083 | + * | |
3084 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 3085 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3086 | + * |
3087 | + * This program is free software; you can redistribute it and/or | |
3088 | + * modify it under the terms of the GNU General Public License as | |
3089 | + * published by the Free Software Foundation, version 2 of the | |
3090 | + * License. | |
3091 | + */ | |
3092 | + | |
3093 | +#ifndef __AA_CAPABILITY_H | |
3094 | +#define __AA_CAPABILITY_H | |
3095 | + | |
3096 | +#include <linux/sched.h> | |
3097 | + | |
3098 | +struct aa_profile; | |
3099 | + | |
3100 | +/* aa_caps - confinement data for capabilities | |
ceaf2cfb AM |
3101 | + * @allowed: capabilities mask |
3102 | + * @audit: caps that are to be audited | |
3103 | + * @quiet: caps that should not be audited | |
3104 | + * @kill: caps that when requested will result in the task being killed | |
3105 | + * @extended: caps that are subject finer grained mediation | |
9474138d AM |
3106 | + */ |
3107 | +struct aa_caps { | |
76514441 | 3108 | + kernel_cap_t allow; |
9474138d AM |
3109 | + kernel_cap_t audit; |
3110 | + kernel_cap_t quiet; | |
3111 | + kernel_cap_t kill; | |
ceaf2cfb | 3112 | + kernel_cap_t extended; |
2380c486 JR |
3113 | +}; |
3114 | + | |
9474138d AM |
3115 | +int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, |
3116 | + int audit); | |
2380c486 | 3117 | + |
9474138d | 3118 | +static inline void aa_free_cap_rules(struct aa_caps *caps) |
2380c486 | 3119 | +{ |
9474138d | 3120 | + /* NOP */ |
2380c486 JR |
3121 | +} |
3122 | + | |
ceaf2cfb AM |
3123 | +#endif /* __AA_CAPBILITY_H */ |
3124 | diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h | |
3125 | new file mode 100644 | |
76514441 | 3126 | index 0000000..a9cbee4 |
ceaf2cfb AM |
3127 | --- /dev/null |
3128 | +++ b/security/apparmor/include/context.h | |
3129 | @@ -0,0 +1,154 @@ | |
9474138d AM |
3130 | +/* |
3131 | + * AppArmor security module | |
3132 | + * | |
3133 | + * This file contains AppArmor contexts used to associate "labels" to objects. | |
3134 | + * | |
3135 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 3136 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3137 | + * |
3138 | + * This program is free software; you can redistribute it and/or | |
3139 | + * modify it under the terms of the GNU General Public License as | |
3140 | + * published by the Free Software Foundation, version 2 of the | |
3141 | + * License. | |
3142 | + */ | |
2380c486 | 3143 | + |
9474138d AM |
3144 | +#ifndef __AA_CONTEXT_H |
3145 | +#define __AA_CONTEXT_H | |
2380c486 | 3146 | + |
9474138d AM |
3147 | +#include <linux/cred.h> |
3148 | +#include <linux/slab.h> | |
3149 | +#include <linux/sched.h> | |
2380c486 | 3150 | + |
9474138d AM |
3151 | +#include "policy.h" |
3152 | + | |
9474138d | 3153 | +/* struct aa_file_cxt - the AppArmor context the file was opened in |
9474138d | 3154 | + * @perms: the permission the file was opened with |
76514441 AM |
3155 | + * |
3156 | + * The file_cxt could currently be directly stored in file->f_security | |
3157 | + * as the profile reference is now stored in the f_cred. However the | |
3158 | + * cxt struct will expand in the future so we keep the struct. | |
9474138d AM |
3159 | + */ |
3160 | +struct aa_file_cxt { | |
76514441 | 3161 | + u16 allow; |
9474138d AM |
3162 | +}; |
3163 | + | |
ceaf2cfb AM |
3164 | +/** |
3165 | + * aa_alloc_file_context - allocate file_cxt | |
3166 | + * @gfp: gfp flags for allocation | |
3167 | + * | |
3168 | + * Returns: file_cxt or NULL on failure | |
3169 | + */ | |
9474138d | 3170 | +static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) |
2380c486 | 3171 | +{ |
9474138d | 3172 | + return kzalloc(sizeof(struct aa_file_cxt), gfp); |
2380c486 JR |
3173 | +} |
3174 | + | |
ceaf2cfb AM |
3175 | +/** |
3176 | + * aa_free_file_context - free a file_cxt | |
3177 | + * @cxt: file_cxt to free (MAYBE_NULL) | |
3178 | + */ | |
9474138d | 3179 | +static inline void aa_free_file_context(struct aa_file_cxt *cxt) |
2380c486 | 3180 | +{ |
76514441 | 3181 | + if (cxt) |
ceaf2cfb | 3182 | + kzfree(cxt); |
9474138d | 3183 | +} |
2380c486 | 3184 | + |
ceaf2cfb AM |
3185 | +/** |
3186 | + * struct aa_task_cxt - primary label for confined tasks | |
3187 | + * @profile: the current profile (NOT NULL) | |
3188 | + * @exec: profile to transition to on next exec (MAYBE NULL) | |
3189 | + * @previous: profile the task may return to (MAYBE NULL) | |
9474138d | 3190 | + * @token: magic value the task must know for returning to @previous_profile |
2380c486 | 3191 | + * |
9474138d AM |
3192 | + * Contains the task's current profile (which could change due to |
3193 | + * change_hat). Plus the hat_magic needed during change_hat. | |
ceaf2cfb AM |
3194 | + * |
3195 | + * TODO: make so a task can be confined by a stack of contexts | |
2380c486 | 3196 | + */ |
ceaf2cfb | 3197 | +struct aa_task_cxt { |
9474138d AM |
3198 | + struct aa_profile *profile; |
3199 | + struct aa_profile *onexec; | |
3200 | + struct aa_profile *previous; | |
3201 | + u64 token; | |
3202 | +}; | |
2380c486 | 3203 | + |
ceaf2cfb AM |
3204 | +struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); |
3205 | +void aa_free_task_context(struct aa_task_cxt *cxt); | |
3206 | +void aa_dup_task_context(struct aa_task_cxt *new, | |
3207 | + const struct aa_task_cxt *old); | |
76514441 | 3208 | +int aa_replace_current_profile(struct aa_profile *profile); |
ceaf2cfb | 3209 | +int aa_set_current_onexec(struct aa_profile *profile); |
9474138d AM |
3210 | +int aa_set_current_hat(struct aa_profile *profile, u64 token); |
3211 | +int aa_restore_previous_profile(u64 cookie); | |
2380c486 | 3212 | + |
9474138d AM |
3213 | +/** |
3214 | + * __aa_task_is_confined - determine if @task has any confinement | |
ceaf2cfb | 3215 | + * @task: task to check confinement of (NOT NULL) |
9474138d | 3216 | + * |
ceaf2cfb | 3217 | + * If @task != current needs to be called in RCU safe critical section |
9474138d | 3218 | + */ |
ceaf2cfb | 3219 | +static inline bool __aa_task_is_confined(struct task_struct *task) |
2380c486 | 3220 | +{ |
ceaf2cfb | 3221 | + struct aa_task_cxt *cxt = __task_cred(task)->security; |
2380c486 | 3222 | + |
ceaf2cfb AM |
3223 | + BUG_ON(!cxt || !cxt->profile); |
3224 | + if (unconfined(aa_newest_version(cxt->profile))) | |
3225 | + return 0; | |
2380c486 | 3226 | + |
ceaf2cfb | 3227 | + return 1; |
2380c486 JR |
3228 | +} |
3229 | + | |
ceaf2cfb AM |
3230 | +/** |
3231 | + * aa_cred_profile - obtain cred's profiles | |
3232 | + * @cred: cred to obtain profiles from (NOT NULL) | |
3233 | + * | |
3234 | + * Returns: confining profile | |
3235 | + * | |
3236 | + * does NOT increment reference count | |
3237 | + */ | |
3238 | +static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | |
2380c486 | 3239 | +{ |
ceaf2cfb AM |
3240 | + struct aa_task_cxt *cxt = cred->security; |
3241 | + BUG_ON(!cxt || !cxt->profile); | |
3242 | + return aa_newest_version(cxt->profile); | |
9474138d | 3243 | +} |
2380c486 | 3244 | + |
ceaf2cfb AM |
3245 | +/** |
3246 | + * __aa_current_profile - find the current tasks confining profile | |
3247 | + * | |
3248 | + * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | |
3249 | + * | |
3250 | + * This fn will not update the tasks cred to the most up to date version | |
3251 | + * of the profile so it is safe to call when inside of locks. | |
3252 | + */ | |
3253 | +static inline struct aa_profile *__aa_current_profile(void) | |
2380c486 | 3254 | +{ |
ceaf2cfb | 3255 | + return aa_cred_profile(current_cred()); |
2380c486 JR |
3256 | +} |
3257 | + | |
ceaf2cfb AM |
3258 | +/** |
3259 | + * aa_current_profile - find the current tasks confining profile and do updates | |
3260 | + * | |
3261 | + * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | |
3262 | + * | |
3263 | + * This fn will update the tasks cred structure if the profile has been | |
3264 | + * replaced. Not safe to call inside locks | |
3265 | + */ | |
9474138d | 3266 | +static inline struct aa_profile *aa_current_profile(void) |
2380c486 | 3267 | +{ |
ceaf2cfb AM |
3268 | + const struct aa_task_cxt *cxt = current_cred()->security; |
3269 | + struct aa_profile *profile; | |
3270 | + BUG_ON(!cxt || !cxt->profile); | |
2380c486 | 3271 | + |
ceaf2cfb AM |
3272 | + profile = aa_newest_version(cxt->profile); |
3273 | + /* | |
3274 | + * Whether or not replacement succeeds, use newest profile so | |
3275 | + * there is no need to update it after replacement. | |
3276 | + */ | |
3277 | + if (unlikely((cxt->profile != profile))) | |
76514441 | 3278 | + aa_replace_current_profile(profile); |
2380c486 | 3279 | + |
ceaf2cfb AM |
3280 | + return profile; |
3281 | +} | |
9474138d | 3282 | + |
ceaf2cfb AM |
3283 | +#endif /* __AA_CONTEXT_H */ |
3284 | diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h | |
3285 | new file mode 100644 | |
76514441 | 3286 | index 0000000..de04464 |
ceaf2cfb AM |
3287 | --- /dev/null |
3288 | +++ b/security/apparmor/include/domain.h | |
3289 | @@ -0,0 +1,36 @@ | |
9474138d AM |
3290 | +/* |
3291 | + * AppArmor security module | |
3292 | + * | |
3293 | + * This file contains AppArmor security domain transition function definitions. | |
3294 | + * | |
3295 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 3296 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3297 | + * |
3298 | + * This program is free software; you can redistribute it and/or | |
3299 | + * modify it under the terms of the GNU General Public License as | |
3300 | + * published by the Free Software Foundation, version 2 of the | |
3301 | + * License. | |
2380c486 | 3302 | + */ |
2380c486 | 3303 | + |
9474138d AM |
3304 | +#include <linux/binfmts.h> |
3305 | +#include <linux/types.h> | |
3306 | + | |
3307 | +#ifndef __AA_DOMAIN_H | |
3308 | +#define __AA_DOMAIN_H | |
3309 | + | |
3310 | +struct aa_domain { | |
3311 | + int size; | |
3312 | + char **table; | |
3313 | +}; | |
3314 | + | |
3315 | +int apparmor_bprm_set_creds(struct linux_binprm *bprm); | |
3316 | +int apparmor_bprm_secureexec(struct linux_binprm *bprm); | |
ceaf2cfb | 3317 | +void apparmor_bprm_committing_creds(struct linux_binprm *bprm); |
9474138d AM |
3318 | +void apparmor_bprm_committed_creds(struct linux_binprm *bprm); |
3319 | + | |
3320 | +void aa_free_domain_entries(struct aa_domain *domain); | |
ceaf2cfb | 3321 | +int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); |
76514441 | 3322 | +int aa_change_profile(const char *ns_name, const char *name, bool onexec, |
ceaf2cfb AM |
3323 | + bool permtest); |
3324 | + | |
3325 | +#endif /* __AA_DOMAIN_H */ | |
3326 | diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h | |
3327 | new file mode 100644 | |
76514441 | 3328 | index 0000000..be36fea |
ceaf2cfb AM |
3329 | --- /dev/null |
3330 | +++ b/security/apparmor/include/file.h | |
76514441 | 3331 | @@ -0,0 +1,217 @@ |
9474138d AM |
3332 | +/* |
3333 | + * AppArmor security module | |
3334 | + * | |
3335 | + * This file contains AppArmor file mediation function definitions. | |
3336 | + * | |
3337 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 3338 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3339 | + * |
3340 | + * This program is free software; you can redistribute it and/or | |
3341 | + * modify it under the terms of the GNU General Public License as | |
3342 | + * published by the Free Software Foundation, version 2 of the | |
3343 | + * License. | |
3344 | + */ | |
3345 | + | |
3346 | +#ifndef __AA_FILE_H | |
3347 | +#define __AA_FILE_H | |
3348 | + | |
3349 | +#include <linux/path.h> | |
3350 | + | |
9474138d AM |
3351 | +#include "domain.h" |
3352 | +#include "match.h" | |
3353 | + | |
3354 | +struct aa_profile; | |
3355 | + | |
3356 | +/* | |
3357 | + * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags | |
3358 | + * for profile permissions | |
3359 | + */ | |
76514441 AM |
3360 | +#define AA_MAY_CREATE 0x0010 |
3361 | +#define AA_MAY_DELETE 0x0020 | |
3362 | +#define AA_MAY_META_WRITE 0x0040 | |
3363 | +#define AA_MAY_META_READ 0x0080 | |
9474138d | 3364 | + |
76514441 AM |
3365 | +#define AA_MAY_CHMOD 0x0100 |
3366 | +#define AA_MAY_CHOWN 0x0200 | |
3367 | +#define AA_MAY_LOCK 0x0400 | |
3368 | +#define AA_EXEC_MMAP 0x0800 | |
2380c486 | 3369 | + |
76514441 AM |
3370 | +#define AA_MAY_LINK 0x1000 |
3371 | +#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ | |
3372 | +#define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ | |
3373 | +#define AA_MAY_CHANGE_PROFILE 0x80000000 | |
3374 | +#define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ | |
2380c486 | 3375 | + |
9474138d | 3376 | +#define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ |
76514441 AM |
3377 | + AA_MAY_CREATE | AA_MAY_DELETE | \ |
3378 | + AA_MAY_META_READ | AA_MAY_META_WRITE | \ | |
3379 | + AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ | |
3380 | + AA_EXEC_MMAP | AA_MAY_LINK) | |
2380c486 | 3381 | + |
9474138d AM |
3382 | +/* |
3383 | + * The xindex is broken into 3 parts | |
3384 | + * - index - an index into either the exec name table or the variable table | |
3385 | + * - exec type - which determines how the executable name and index are used | |
3386 | + * - flags - which modify how the destination name is applied | |
3387 | + */ | |
3388 | +#define AA_X_INDEX_MASK 0x03ff | |
2380c486 | 3389 | + |
9474138d | 3390 | +#define AA_X_TYPE_MASK 0x0c00 |
ceaf2cfb | 3391 | +#define AA_X_TYPE_SHIFT 10 |
9474138d AM |
3392 | +#define AA_X_NONE 0x0000 |
3393 | +#define AA_X_NAME 0x0400 /* use executable name px */ | |
3394 | +#define AA_X_TABLE 0x0800 /* use a specified name ->n# */ | |
2380c486 | 3395 | + |
9474138d AM |
3396 | +#define AA_X_UNSAFE 0x1000 |
3397 | +#define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ | |
3398 | +#define AA_X_INHERIT 0x4000 | |
3399 | +#define AA_X_UNCONFINED 0x8000 | |
2380c486 | 3400 | + |
9474138d AM |
3401 | +/* AA_SECURE_X_NEEDED - is passed in the bprm->unsafe field */ |
3402 | +#define AA_SECURE_X_NEEDED 0x8000 | |
2380c486 | 3403 | + |
76514441 | 3404 | +/* need to make conditional which ones are being set */ |
9474138d AM |
3405 | +struct path_cond { |
3406 | + uid_t uid; | |
3407 | + umode_t mode; | |
3408 | +}; | |
2380c486 | 3409 | + |
76514441 AM |
3410 | +/* struct file_perms - file permission |
3411 | + * @allow: mask of permissions that are allowed | |
9474138d AM |
3412 | + * @audit: mask of permissions to force an audit message for |
3413 | + * @quiet: mask of permissions to quiet audit messages for | |
3414 | + * @kill: mask of permissions that when matched will kill the task | |
76514441 | 3415 | + * @xindex: exec transition index if @allow contains MAY_EXEC |
2380c486 | 3416 | + * |
9474138d | 3417 | + * The @audit and @queit mask should be mutually exclusive. |
2380c486 | 3418 | + */ |
9474138d | 3419 | +struct file_perms { |
76514441 AM |
3420 | + u32 allow; |
3421 | + u32 audit; | |
3422 | + u32 quiet; | |
3423 | + u32 kill; | |
9474138d | 3424 | + u16 xindex; |
9474138d | 3425 | +}; |
2380c486 | 3426 | + |
9474138d | 3427 | +extern struct file_perms nullperms; |
2380c486 | 3428 | + |
76514441 | 3429 | +#define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) |
2380c486 | 3430 | + |
9474138d AM |
3431 | +/* FIXME: split perms from dfa and match this to description |
3432 | + * also add delegation info. | |
2380c486 | 3433 | + */ |
9474138d AM |
3434 | +static inline u16 dfa_map_xindex(u16 mask) |
3435 | +{ | |
3436 | + u16 old_index = (mask >> 10) & 0xf; | |
3437 | + u16 index = 0; | |
3438 | + | |
9474138d AM |
3439 | + if (mask & 0x100) |
3440 | + index |= AA_X_UNSAFE; | |
3441 | + if (mask & 0x200) | |
3442 | + index |= AA_X_INHERIT; | |
5e665963 AM |
3443 | + if (mask & 0x80) |
3444 | + index |= AA_X_UNCONFINED; | |
9474138d AM |
3445 | + |
3446 | + if (old_index == 1) { | |
3447 | + index |= AA_X_UNCONFINED; | |
3448 | + } else if (old_index == 2) { | |
3449 | + index |= AA_X_NAME; | |
3450 | + } else if (old_index == 3) { | |
3451 | + index |= AA_X_NAME | AA_X_CHILD; | |
2380c486 | 3452 | + } else { |
9474138d AM |
3453 | + index |= AA_X_TABLE; |
3454 | + index |= old_index - 4; | |
2380c486 | 3455 | + } |
9474138d AM |
3456 | + |
3457 | + return index; | |
2380c486 JR |
3458 | +} |
3459 | + | |
9474138d AM |
3460 | +/* |
3461 | + * map old dfa inline permissions to new format | |
3462 | + */ | |
76514441 AM |
3463 | +#define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ |
3464 | + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | |
ceaf2cfb AM |
3465 | +#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) |
3466 | +#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) | |
9474138d AM |
3467 | +#define dfa_user_xindex(dfa, state) \ |
3468 | + (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) | |
3469 | + | |
76514441 AM |
3470 | +#define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ |
3471 | + 0x7f) | \ | |
3472 | + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | |
ceaf2cfb AM |
3473 | +#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) |
3474 | +#define dfa_other_quiet(dfa, state) \ | |
3475 | + ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) | |
9474138d AM |
3476 | +#define dfa_other_xindex(dfa, state) \ |
3477 | + dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) | |
3478 | + | |
76514441 AM |
3479 | +int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, |
3480 | + gfp_t gfp, int op, u32 request, const char *name, | |
3481 | + const char *target, uid_t ouid, const char *info, int error); | |
9474138d | 3482 | + |
2380c486 | 3483 | +/** |
9474138d AM |
3484 | + * struct aa_file_rules - components used for file rule permissions |
3485 | + * @dfa: dfa to match path names and conditionals against | |
3486 | + * @perms: permission table indexed by the matched state accept entry of @dfa | |
3487 | + * @trans: transition table for indexed by named x transitions | |
3488 | + * | |
3489 | + * File permission are determined by matching a path against @dfa and then | |
3490 | + * then using the value of the accept entry for the matching state as | |
3491 | + * an index into @perms. If a named exec transition is required it is | |
3492 | + * looked up in the transition table. | |
2380c486 | 3493 | + */ |
9474138d | 3494 | +struct aa_file_rules { |
ceaf2cfb | 3495 | + unsigned int start; |
9474138d AM |
3496 | + struct aa_dfa *dfa; |
3497 | + /* struct perms perms; */ | |
3498 | + struct aa_domain trans; | |
3499 | + /* TODO: add delegate table */ | |
3500 | +}; | |
3501 | + | |
76514441 AM |
3502 | +unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, |
3503 | + const char *name, struct path_cond *cond, | |
3504 | + struct file_perms *perms); | |
2380c486 | 3505 | + |
76514441 AM |
3506 | +int aa_path_perm(int op, struct aa_profile *profile, struct path *path, |
3507 | + int flags, u32 request, struct path_cond *cond); | |
9474138d AM |
3508 | + |
3509 | +int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | |
3510 | + struct path *new_dir, struct dentry *new_dentry); | |
3511 | + | |
76514441 AM |
3512 | +int aa_file_perm(int op, struct aa_profile *profile, struct file *file, |
3513 | + u32 request); | |
9474138d | 3514 | + |
9474138d | 3515 | +static inline void aa_free_file_rules(struct aa_file_rules *rules) |
2380c486 | 3516 | +{ |
ceaf2cfb | 3517 | + aa_put_dfa(rules->dfa); |
9474138d | 3518 | + aa_free_domain_entries(&rules->trans); |
2380c486 JR |
3519 | +} |
3520 | + | |
9474138d AM |
3521 | +#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) |
3522 | + | |
3523 | +/* from namei.c */ | |
9474138d | 3524 | +#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) |
ceaf2cfb AM |
3525 | + |
3526 | +/** | |
3527 | + * aa_map_file_perms - map file flags to AppArmor permissions | |
3528 | + * @file: open file to map flags to AppArmor permissions | |
3529 | + * | |
3530 | + * Returns: apparmor permission set for the file | |
9474138d | 3531 | + */ |
76514441 | 3532 | +static inline u32 aa_map_file_to_perms(struct file *file) |
2380c486 | 3533 | +{ |
9474138d | 3534 | + int flags = MAP_OPEN_FLAGS(file->f_flags); |
76514441 | 3535 | + u32 perms = ACC_FMODE(file->f_mode); |
9474138d AM |
3536 | + |
3537 | + if ((flags & O_APPEND) && (perms & MAY_WRITE)) | |
3538 | + perms = (perms & ~MAY_WRITE) | MAY_APPEND; | |
3539 | + /* trunc implies write permission */ | |
3540 | + if (flags & O_TRUNC) | |
3541 | + perms |= MAY_WRITE; | |
3542 | + if (flags & O_CREAT) | |
3543 | + perms |= AA_MAY_CREATE; | |
3544 | + | |
3545 | + return perms; | |
2380c486 JR |
3546 | +} |
3547 | + | |
ceaf2cfb AM |
3548 | +#endif /* __AA_FILE_H */ |
3549 | diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h | |
3550 | new file mode 100644 | |
3551 | index 0000000..aeda0fb | |
3552 | --- /dev/null | |
3553 | +++ b/security/apparmor/include/ipc.h | |
9474138d | 3554 | @@ -0,0 +1,28 @@ |
2380c486 | 3555 | +/* |
9474138d | 3556 | + * AppArmor security module |
2380c486 | 3557 | + * |
9474138d | 3558 | + * This file contains AppArmor ipc mediation function definitions. |
2380c486 | 3559 | + * |
9474138d | 3560 | + * Copyright (C) 1998-2008 Novell/SUSE |
ceaf2cfb | 3561 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3562 | + * |
3563 | + * This program is free software; you can redistribute it and/or | |
3564 | + * modify it under the terms of the GNU General Public License as | |
3565 | + * published by the Free Software Foundation, version 2 of the | |
3566 | + * License. | |
2380c486 JR |
3567 | + */ |
3568 | + | |
9474138d AM |
3569 | +#ifndef __AA_IPC_H |
3570 | +#define __AA_IPC_H | |
2380c486 | 3571 | + |
9474138d | 3572 | +#include <linux/sched.h> |
2380c486 | 3573 | + |
9474138d | 3574 | +struct aa_profile; |
2380c486 | 3575 | + |
9474138d AM |
3576 | +int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, |
3577 | + struct aa_profile *tracee, unsigned int mode); | |
2380c486 | 3578 | + |
9474138d AM |
3579 | +int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, |
3580 | + unsigned int mode); | |
2380c486 | 3581 | + |
ceaf2cfb AM |
3582 | +#endif /* __AA_IPC_H */ |
3583 | diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h | |
3584 | new file mode 100644 | |
76514441 | 3585 | index 0000000..734a6d3 |
ceaf2cfb AM |
3586 | --- /dev/null |
3587 | +++ b/security/apparmor/include/match.h | |
76514441 | 3588 | @@ -0,0 +1,132 @@ |
9474138d AM |
3589 | +/* |
3590 | + * AppArmor security module | |
3591 | + * | |
3592 | + * This file contains AppArmor policy dfa matching engine definitions. | |
3593 | + * | |
3594 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 3595 | + * Copyright 2009-2010 Canonical Ltd. |
2380c486 | 3596 | + * |
9474138d AM |
3597 | + * This program is free software; you can redistribute it and/or |
3598 | + * modify it under the terms of the GNU General Public License as | |
3599 | + * published by the Free Software Foundation, version 2 of the | |
3600 | + * License. | |
2380c486 | 3601 | + */ |
2380c486 | 3602 | + |
9474138d AM |
3603 | +#ifndef __AA_MATCH_H |
3604 | +#define __AA_MATCH_H | |
2380c486 | 3605 | + |
76514441 AM |
3606 | +#include <linux/workqueue.h> |
3607 | + | |
9474138d AM |
3608 | +#define DFA_NOMATCH 0 |
3609 | +#define DFA_START 1 | |
3610 | + | |
3611 | +#define DFA_VALID_PERM_MASK 0xffffffff | |
3612 | +#define DFA_VALID_PERM2_MASK 0xffffffff | |
2380c486 | 3613 | + |
2380c486 | 3614 | +/** |
9474138d AM |
3615 | + * The format used for transition tables is based on the GNU flex table |
3616 | + * file format (--tables-file option; see Table File Format in the flex | |
3617 | + * info pages and the flex sources for documentation). The magic number | |
3618 | + * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because | |
3619 | + * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used | |
3620 | + * slightly differently (see the apparmor-parser package). | |
2380c486 | 3621 | + */ |
2380c486 | 3622 | + |
9474138d | 3623 | +#define YYTH_MAGIC 0x1B5E783D |
ceaf2cfb | 3624 | +#define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ |
2380c486 | 3625 | + |
9474138d | 3626 | +struct table_set_header { |
ceaf2cfb AM |
3627 | + u32 th_magic; /* YYTH_MAGIC */ |
3628 | + u32 th_hsize; | |
3629 | + u32 th_ssize; | |
3630 | + u16 th_flags; | |
3631 | + char th_version[]; | |
9474138d | 3632 | +}; |
2380c486 | 3633 | + |
ceaf2cfb AM |
3634 | +/* The YYTD_ID are one less than flex table mappings. The flex id |
3635 | + * has 1 subtracted at table load time, this allows us to directly use the | |
3636 | + * ID's as indexes. | |
3637 | + */ | |
3638 | +#define YYTD_ID_ACCEPT 0 | |
3639 | +#define YYTD_ID_BASE 1 | |
3640 | +#define YYTD_ID_CHK 2 | |
3641 | +#define YYTD_ID_DEF 3 | |
3642 | +#define YYTD_ID_EC 4 | |
3643 | +#define YYTD_ID_META 5 | |
3644 | +#define YYTD_ID_ACCEPT2 6 | |
3645 | +#define YYTD_ID_NXT 7 | |
3646 | +#define YYTD_ID_TSIZE 8 | |
2380c486 | 3647 | + |
9474138d AM |
3648 | +#define YYTD_DATA8 1 |
3649 | +#define YYTD_DATA16 2 | |
3650 | +#define YYTD_DATA32 4 | |
ceaf2cfb AM |
3651 | +#define YYTD_DATA64 8 |
3652 | + | |
3653 | +/* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the | |
3654 | + * first flags | |
3655 | + */ | |
3656 | +#define ACCEPT1_FLAGS(X) ((X) & 0x3f) | |
3657 | +#define ACCEPT2_FLAGS(X) ACCEPT1_FLAGS((X) >> YYTD_ID_ACCEPT2) | |
3658 | +#define TO_ACCEPT1_FLAG(X) ACCEPT1_FLAGS(X) | |
3659 | +#define TO_ACCEPT2_FLAG(X) (ACCEPT1_FLAGS(X) << YYTD_ID_ACCEPT2) | |
3660 | +#define DFA_FLAG_VERIFY_STATES 0x1000 | |
2380c486 | 3661 | + |
9474138d | 3662 | +struct table_header { |
ceaf2cfb AM |
3663 | + u16 td_id; |
3664 | + u16 td_flags; | |
3665 | + u32 td_hilen; | |
3666 | + u32 td_lolen; | |
3667 | + char td_data[]; | |
9474138d | 3668 | +}; |
2380c486 | 3669 | + |
ceaf2cfb AM |
3670 | +#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) |
3671 | +#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) | |
3672 | +#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) | |
3673 | +#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) | |
3674 | +#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) | |
3675 | +#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) | |
3676 | +#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) | |
2380c486 | 3677 | + |
9474138d | 3678 | +struct aa_dfa { |
ceaf2cfb AM |
3679 | + struct kref count; |
3680 | + u16 flags; | |
3681 | + struct table_header *tables[YYTD_ID_TSIZE]; | |
9474138d | 3682 | +}; |
2380c486 | 3683 | + |
9474138d | 3684 | +#define byte_to_byte(X) (X) |
2380c486 | 3685 | + |
9474138d AM |
3686 | +#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ |
3687 | + do { \ | |
3688 | + typeof(LEN) __i; \ | |
3689 | + TYPE *__t = (TYPE *) TABLE; \ | |
3690 | + TYPE *__b = (TYPE *) BLOB; \ | |
3691 | + for (__i = 0; __i < LEN; __i++) { \ | |
3692 | + __t[__i] = NTOHX(__b[__i]); \ | |
3693 | + } \ | |
3694 | + } while (0) | |
2380c486 | 3695 | + |
9474138d | 3696 | +static inline size_t table_size(size_t len, size_t el_size) |
2380c486 | 3697 | +{ |
9474138d | 3698 | + return ALIGN(sizeof(struct table_header) + len * el_size, 8); |
2380c486 JR |
3699 | +} |
3700 | + | |
ceaf2cfb | 3701 | +struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); |
9474138d AM |
3702 | +unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, |
3703 | + const char *str, int len); | |
3704 | +unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | |
3705 | + const char *str); | |
ceaf2cfb AM |
3706 | +void aa_dfa_free_kref(struct kref *kref); |
3707 | + | |
3708 | +/** | |
3709 | + * aa_put_dfa - put a dfa refcount | |
3710 | + * @dfa: dfa to put refcount (MAYBE NULL) | |
3711 | + * | |
76514441 | 3712 | + * Requires: if @dfa != NULL that a valid refcount be held |
ceaf2cfb AM |
3713 | + */ |
3714 | +static inline void aa_put_dfa(struct aa_dfa *dfa) | |
3715 | +{ | |
3716 | + if (dfa) | |
3717 | + kref_put(&dfa->count, aa_dfa_free_kref); | |
3718 | +} | |
9474138d AM |
3719 | + |
3720 | +#endif /* __AA_MATCH_H */ | |
ceaf2cfb AM |
3721 | diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h |
3722 | new file mode 100644 | |
76514441 | 3723 | index 0000000..3c7d599 |
ceaf2cfb AM |
3724 | --- /dev/null |
3725 | +++ b/security/apparmor/include/net.h | |
9474138d | 3726 | @@ -0,0 +1,40 @@ |
2380c486 | 3727 | +/* |
9474138d AM |
3728 | + * AppArmor security module |
3729 | + * | |
3730 | + * This file contains AppArmor network mediation definitions. | |
2380c486 | 3731 | + * |
9474138d | 3732 | + * Copyright (C) 1998-2008 Novell/SUSE |
ceaf2cfb | 3733 | + * Copyright 2009-2010 Canonical Ltd. |
2380c486 | 3734 | + * |
9474138d AM |
3735 | + * This program is free software; you can redistribute it and/or |
3736 | + * modify it under the terms of the GNU General Public License as | |
3737 | + * published by the Free Software Foundation, version 2 of the | |
3738 | + * License. | |
2380c486 | 3739 | + */ |
2380c486 | 3740 | + |
9474138d AM |
3741 | +#ifndef __AA_NET_H |
3742 | +#define __AA_NET_H | |
2380c486 | 3743 | + |
9474138d | 3744 | +#include <net/sock.h> |
2380c486 | 3745 | + |
9474138d AM |
3746 | +/* struct aa_net - network confinement data |
3747 | + * @allowed: basic network families permissions | |
3748 | + * @audit_network: which network permissions to force audit | |
3749 | + * @quiet_network: which network permissions to quiet rejects | |
3750 | + */ | |
3751 | +struct aa_net { | |
76514441 | 3752 | + u16 allow[AF_MAX]; |
9474138d AM |
3753 | + u16 audit[AF_MAX]; |
3754 | + u16 quiet[AF_MAX]; | |
3755 | +}; | |
2380c486 | 3756 | + |
76514441 AM |
3757 | +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, |
3758 | + int type, int protocol, struct sock *sk); | |
3759 | +extern int aa_revalidate_sk(int op, struct sock *sk); | |
2380c486 | 3760 | + |
9474138d AM |
3761 | +static inline void aa_free_net_rules(struct aa_net *new) |
3762 | +{ | |
3763 | + /* NOP */ | |
3764 | +} | |
2380c486 | 3765 | + |
ceaf2cfb AM |
3766 | +#endif /* __AA_NET_H */ |
3767 | diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h | |
3768 | new file mode 100644 | |
76514441 | 3769 | index 0000000..27b327a |
ceaf2cfb AM |
3770 | --- /dev/null |
3771 | +++ b/security/apparmor/include/path.h | |
3772 | @@ -0,0 +1,31 @@ | |
9474138d AM |
3773 | +/* |
3774 | + * AppArmor security module | |
3775 | + * | |
3776 | + * This file contains AppArmor basic path manipulation function definitions. | |
3777 | + * | |
3778 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 3779 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3780 | + * |
3781 | + * This program is free software; you can redistribute it and/or | |
3782 | + * modify it under the terms of the GNU General Public License as | |
3783 | + * published by the Free Software Foundation, version 2 of the | |
3784 | + * License. | |
3785 | + */ | |
2380c486 | 3786 | + |
9474138d AM |
3787 | +#ifndef __AA_PATH_H |
3788 | +#define __AA_PATH_H | |
3789 | + | |
ceaf2cfb AM |
3790 | + |
3791 | +enum path_flags { | |
3792 | + PATH_IS_DIR = 0x1, /* path is a directory */ | |
3793 | + PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ | |
3794 | + PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ | |
3795 | + PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ | |
3796 | + | |
76514441 | 3797 | + PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ |
ceaf2cfb AM |
3798 | + PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ |
3799 | +}; | |
3800 | + | |
76514441 | 3801 | +int aa_get_name(struct path *path, int flags, char **buffer, const char **name); |
9474138d | 3802 | + |
ceaf2cfb AM |
3803 | +#endif /* __AA_PATH_H */ |
3804 | diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h | |
3805 | new file mode 100644 | |
76514441 | 3806 | index 0000000..6776929 |
ceaf2cfb AM |
3807 | --- /dev/null |
3808 | +++ b/security/apparmor/include/policy.h | |
76514441 | 3809 | @@ -0,0 +1,308 @@ |
9474138d AM |
3810 | +/* |
3811 | + * AppArmor security module | |
3812 | + * | |
3813 | + * This file contains AppArmor policy definitions. | |
2380c486 | 3814 | + * |
9474138d | 3815 | + * Copyright (C) 1998-2008 Novell/SUSE |
ceaf2cfb | 3816 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
3817 | + * |
3818 | + * This program is free software; you can redistribute it and/or | |
3819 | + * modify it under the terms of the GNU General Public License as | |
3820 | + * published by the Free Software Foundation, version 2 of the | |
3821 | + * License. | |
2380c486 | 3822 | + */ |
2380c486 | 3823 | + |
9474138d AM |
3824 | +#ifndef __AA_POLICY_H |
3825 | +#define __AA_POLICY_H | |
2380c486 | 3826 | + |
9474138d AM |
3827 | +#include <linux/capability.h> |
3828 | +#include <linux/cred.h> | |
3829 | +#include <linux/kref.h> | |
3830 | +#include <linux/sched.h> | |
3831 | +#include <linux/slab.h> | |
3832 | +#include <linux/socket.h> | |
2380c486 | 3833 | + |
9474138d AM |
3834 | +#include "apparmor.h" |
3835 | +#include "audit.h" | |
3836 | +#include "capability.h" | |
3837 | +#include "domain.h" | |
3838 | +#include "file.h" | |
3839 | +#include "net.h" | |
3840 | +#include "resource.h" | |
2380c486 | 3841 | + |
9474138d AM |
3842 | +extern const char *profile_mode_names[]; |
3843 | +#define APPARMOR_NAMES_MAX_INDEX 3 | |
2380c486 | 3844 | + |
76514441 AM |
3845 | +#define COMPLAIN_MODE(_profile) \ |
3846 | + ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ | |
3847 | + ((_profile)->mode == APPARMOR_COMPLAIN)) | |
2380c486 | 3848 | + |
76514441 AM |
3849 | +#define KILL_MODE(_profile) \ |
3850 | + ((aa_g_profile_mode == APPARMOR_KILL) || \ | |
3851 | + ((_profile)->mode == APPARMOR_KILL)) | |
2380c486 | 3852 | + |
76514441 | 3853 | +#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) |
2380c486 | 3854 | + |
9474138d AM |
3855 | +/* |
3856 | + * FIXME: currently need a clean way to replace and remove profiles as a | |
3857 | + * set. It should be done at the namespace level. | |
3858 | + * Either, with a set of profiles loaded at the namespace level or via | |
3859 | + * a mark and remove marked interface. | |
3860 | + */ | |
3861 | +enum profile_mode { | |
ceaf2cfb AM |
3862 | + APPARMOR_ENFORCE, /* enforce access rules */ |
3863 | + APPARMOR_COMPLAIN, /* allow and log access violations */ | |
3864 | + APPARMOR_KILL, /* kill task on access violation */ | |
9474138d | 3865 | +}; |
2380c486 | 3866 | + |
9474138d AM |
3867 | +enum profile_flags { |
3868 | + PFLAG_HAT = 1, /* profile is a hat */ | |
76514441 | 3869 | + PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ |
9474138d AM |
3870 | + PFLAG_NULL = 4, /* profile is null learning profile */ |
3871 | + PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ | |
3872 | + PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ | |
76514441 | 3873 | + PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ |
9474138d | 3874 | + PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ |
ceaf2cfb AM |
3875 | + PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ |
3876 | + | |
76514441 | 3877 | + /* These flags must correspond with PATH_flags */ |
ceaf2cfb | 3878 | + PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ |
9474138d | 3879 | +}; |
2380c486 | 3880 | + |
9474138d | 3881 | +struct aa_profile; |
2380c486 | 3882 | + |
ceaf2cfb | 3883 | +/* struct aa_policy - common part of both namespaces and profiles |
9474138d | 3884 | + * @name: name of the object |
ceaf2cfb | 3885 | + * @hname - The hierarchical name |
9474138d | 3886 | + * @count: reference count of the obj |
ceaf2cfb | 3887 | + * @list: list policy object is on |
9474138d AM |
3888 | + * @profiles: head of the profiles list contained in the object |
3889 | + */ | |
ceaf2cfb | 3890 | +struct aa_policy { |
9474138d | 3891 | + char *name; |
ceaf2cfb | 3892 | + char *hname; |
9474138d | 3893 | + struct kref count; |
9474138d AM |
3894 | + struct list_head list; |
3895 | + struct list_head profiles; | |
3896 | +}; | |
2380c486 | 3897 | + |
9474138d AM |
3898 | +/* struct aa_ns_acct - accounting of profiles in namespace |
3899 | + * @max_size: maximum space allowed for all profiles in namespace | |
3900 | + * @max_count: maximum number of profiles that can be in this namespace | |
3901 | + * @size: current size of profiles | |
3902 | + * @count: current count of profiles (includes null profiles) | |
3903 | + */ | |
3904 | +struct aa_ns_acct { | |
3905 | + int max_size; | |
3906 | + int max_count; | |
3907 | + int size; | |
3908 | + int count; | |
3909 | +}; | |
2380c486 | 3910 | + |
9474138d | 3911 | +/* struct aa_namespace - namespace for a set of profiles |
ceaf2cfb AM |
3912 | + * @base: common policy |
3913 | + * @parent: parent of namespace | |
3914 | + * @lock: lock for modifying the object | |
9474138d | 3915 | + * @acct: accounting for the namespace |
9474138d | 3916 | + * @unconfined: special unconfined profile for the namespace |
ceaf2cfb | 3917 | + * @sub_ns: list of namespaces under the current namespace. |
9474138d AM |
3918 | + * |
3919 | + * An aa_namespace defines the set profiles that are searched to determine | |
3920 | + * which profile to attach to a task. Profiles can not be shared between | |
76514441 AM |
3921 | + * aa_namespaces and profile names within a namespace are guaranteed to be |
3922 | + * unique. When profiles in separate namespaces have the same name they | |
9474138d AM |
3923 | + * are NOT considered to be equivalent. |
3924 | + * | |
ceaf2cfb AM |
3925 | + * Namespaces are hierarchical and only namespaces and profiles below the |
3926 | + * current namespace are visible. | |
3927 | + * | |
9474138d AM |
3928 | + * Namespace names must be unique and can not contain the characters :/\0 |
3929 | + * | |
76514441 AM |
3930 | + * FIXME TODO: add vserver support of namespaces (can it all be done in |
3931 | + * userspace?) | |
9474138d AM |
3932 | + */ |
3933 | +struct aa_namespace { | |
ceaf2cfb AM |
3934 | + struct aa_policy base; |
3935 | + struct aa_namespace *parent; | |
3936 | + rwlock_t lock; | |
9474138d | 3937 | + struct aa_ns_acct acct; |
9474138d | 3938 | + struct aa_profile *unconfined; |
ceaf2cfb | 3939 | + struct list_head sub_ns; |
9474138d | 3940 | +}; |
2380c486 | 3941 | + |
9474138d | 3942 | +/* struct aa_profile - basic confinement data |
76514441 | 3943 | + * @base - base components of the profile (name, refcount, lists, lock ...) |
ceaf2cfb | 3944 | + * @parent: parent of profile |
9474138d | 3945 | + * @ns: namespace the profile is in |
76514441 AM |
3946 | + * @replacedby: is set to the profile that replaced this profile |
3947 | + * @rename: optional profile name that this profile renamed | |
9474138d | 3948 | + * @xmatch: optional extended matching for unconfined executables names |
ceaf2cfb | 3949 | + * @xmatch_len: xmatch prefix len, used to determine xmatch priority |
9474138d AM |
3950 | + * @sid: the unique security id number of this profile |
3951 | + * @audit: the auditing mode of the profile | |
3952 | + * @mode: the enforcement mode of the profile | |
3953 | + * @flags: flags controlling profile behavior | |
ceaf2cfb | 3954 | + * @path_flags: flags controlling path generation behavior |
9474138d AM |
3955 | + * @size: the memory consumed by this profiles rules |
3956 | + * @file: The set of rules governing basic file access and domain transitions | |
3957 | + * @caps: capabilities for the profile | |
3958 | + * @net: network controls for the profile | |
3959 | + * @rlimits: rlimits for the profile | |
3960 | + * | |
3961 | + * The AppArmor profile contains the basic confinement data. Each profile | |
76514441 | 3962 | + * has a name, and exists in a namespace. The @name and @exec_match are |
9474138d | 3963 | + * used to determine profile attachment against unconfined tasks. All other |
76514441 | 3964 | + * attachments are determined by profile X transition rules. |
9474138d AM |
3965 | + * |
3966 | + * The @replacedby field is write protected by the profile lock. Reads | |
3967 | + * are assumed to be atomic, and are done without locking. | |
3968 | + * | |
76514441 | 3969 | + * Profiles have a hierarchy where hats and children profiles keep |
9474138d AM |
3970 | + * a reference to their parent. |
3971 | + * | |
3972 | + * Profile names can not begin with a : and can not contain the \0 | |
3973 | + * character. If a profile name begins with / it will be considered when | |
3974 | + * determining profile attachment on "unconfined" tasks. | |
3975 | + */ | |
3976 | +struct aa_profile { | |
ceaf2cfb | 3977 | + struct aa_policy base; |
9474138d | 3978 | + struct aa_profile *parent; |
9474138d | 3979 | + |
ceaf2cfb | 3980 | + struct aa_namespace *ns; |
76514441 AM |
3981 | + struct aa_profile *replacedby; |
3982 | + const char *rename; | |
3983 | + | |
9474138d AM |
3984 | + struct aa_dfa *xmatch; |
3985 | + int xmatch_len; | |
3986 | + u32 sid; | |
3987 | + enum audit_mode audit; | |
3988 | + enum profile_mode mode; | |
3989 | + u32 flags; | |
ceaf2cfb | 3990 | + u32 path_flags; |
9474138d AM |
3991 | + int size; |
3992 | + | |
3993 | + struct aa_file_rules file; | |
3994 | + struct aa_caps caps; | |
3995 | + struct aa_net net; | |
3996 | + struct aa_rlimit rlimits; | |
3997 | +}; | |
2380c486 | 3998 | + |
ceaf2cfb AM |
3999 | +extern struct aa_namespace *root_ns; |
4000 | +extern enum profile_mode aa_g_profile_mode; | |
2380c486 | 4001 | + |
ceaf2cfb | 4002 | +void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); |
2380c486 | 4003 | + |
76514441 AM |
4004 | +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); |
4005 | +const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); | |
ceaf2cfb AM |
4006 | +int aa_alloc_root_ns(void); |
4007 | +void aa_free_root_ns(void); | |
4008 | +void aa_free_namespace_kref(struct kref *kref); | |
2380c486 | 4009 | + |
ceaf2cfb AM |
4010 | +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, |
4011 | + const char *name); | |
2380c486 | 4012 | + |
ceaf2cfb | 4013 | +static inline struct aa_policy *aa_get_common(struct aa_policy *c) |
2380c486 | 4014 | +{ |
9474138d AM |
4015 | + if (c) |
4016 | + kref_get(&c->count); | |
2380c486 | 4017 | + |
9474138d | 4018 | + return c; |
2380c486 JR |
4019 | +} |
4020 | + | |
ceaf2cfb AM |
4021 | +/** |
4022 | + * aa_get_namespace - increment references count on @ns | |
4023 | + * @ns: namespace to increment reference count of (MAYBE NULL) | |
4024 | + * | |
76514441 | 4025 | + * Returns: pointer to @ns, if @ns is NULL returns NULL |
ceaf2cfb AM |
4026 | + * Requires: @ns must be held with valid refcount when called |
4027 | + */ | |
9474138d | 4028 | +static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) |
2380c486 | 4029 | +{ |
9474138d AM |
4030 | + if (ns) |
4031 | + kref_get(&(ns->base.count)); | |
2380c486 | 4032 | + |
9474138d | 4033 | + return ns; |
2380c486 JR |
4034 | +} |
4035 | + | |
ceaf2cfb AM |
4036 | +/** |
4037 | + * aa_put_namespace - decrement refcount on @ns | |
76514441 | 4038 | + * @ns: namespace to put reference of |
ceaf2cfb | 4039 | + * |
76514441 | 4040 | + * Decrement reference count of @ns and if no longer in use free it |
ceaf2cfb | 4041 | + */ |
9474138d | 4042 | +static inline void aa_put_namespace(struct aa_namespace *ns) |
2380c486 | 4043 | +{ |
9474138d | 4044 | + if (ns) |
ceaf2cfb | 4045 | + kref_put(&ns->base.count, aa_free_namespace_kref); |
9474138d AM |
4046 | +} |
4047 | + | |
ceaf2cfb AM |
4048 | +struct aa_profile *aa_alloc_profile(const char *name); |
4049 | +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | |
4050 | +void aa_free_profile_kref(struct kref *kref); | |
9474138d | 4051 | +struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); |
76514441 | 4052 | +struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); |
9474138d | 4053 | +struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); |
ceaf2cfb | 4054 | + |
76514441 AM |
4055 | +ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); |
4056 | +ssize_t aa_remove_profiles(char *name, size_t size); | |
4057 | + | |
4058 | +#define PROF_ADD 1 | |
4059 | +#define PROF_REPLACE 0 | |
ceaf2cfb AM |
4060 | + |
4061 | +#define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | |
4062 | + | |
4063 | +/** | |
4064 | + * aa_newest_version - find the newest version of @profile | |
4065 | + * @profile: the profile to check for newer versions of (NOT NULL) | |
4066 | + * | |
4067 | + * Returns: newest version of @profile, if @profile is the newest version | |
4068 | + * return @profile. | |
4069 | + * | |
4070 | + * NOTE: the profile returned is not refcounted, The refcount on @profile | |
4071 | + * must be held until the caller decides what to do with the returned newest | |
4072 | + * version. | |
4073 | + */ | |
4074 | +static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | |
9474138d | 4075 | +{ |
76514441 AM |
4076 | + while (profile->replacedby) |
4077 | + profile = profile->replacedby; | |
ceaf2cfb | 4078 | + |
9474138d | 4079 | + return profile; |
2380c486 JR |
4080 | +} |
4081 | + | |
9474138d AM |
4082 | +/** |
4083 | + * aa_get_profile - increment refcount on profile @p | |
ceaf2cfb AM |
4084 | + * @p: profile (MAYBE NULL) |
4085 | + * | |
4086 | + * Returns: pointer to @p if @p is NULL will return NULL | |
4087 | + * Requires: @p must be held with valid refcount when called | |
9474138d AM |
4088 | + */ |
4089 | +static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | |
2380c486 | 4090 | +{ |
9474138d AM |
4091 | + if (p) |
4092 | + kref_get(&(p->base.count)); | |
2380c486 | 4093 | + |
9474138d | 4094 | + return p; |
2380c486 JR |
4095 | +} |
4096 | + | |
9474138d AM |
4097 | +/** |
4098 | + * aa_put_profile - decrement refcount on profile @p | |
ceaf2cfb | 4099 | + * @p: profile (MAYBE NULL) |
9474138d AM |
4100 | + */ |
4101 | +static inline void aa_put_profile(struct aa_profile *p) | |
2380c486 | 4102 | +{ |
9474138d | 4103 | + if (p) |
ceaf2cfb | 4104 | + kref_put(&p->base.count, aa_free_profile_kref); |
2380c486 JR |
4105 | +} |
4106 | + | |
ceaf2cfb | 4107 | +static inline int AUDIT_MODE(struct aa_profile *profile) |
2380c486 | 4108 | +{ |
ceaf2cfb AM |
4109 | + if (aa_g_audit != AUDIT_NORMAL) |
4110 | + return aa_g_audit; | |
76514441 AM |
4111 | + |
4112 | + return profile->audit; | |
2380c486 JR |
4113 | +} |
4114 | + | |
76514441 AM |
4115 | +bool aa_may_manage_policy(int op); |
4116 | + | |
ceaf2cfb AM |
4117 | +#endif /* __AA_POLICY_H */ |
4118 | diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h | |
4119 | new file mode 100644 | |
76514441 | 4120 | index 0000000..a2dccca |
ceaf2cfb AM |
4121 | --- /dev/null |
4122 | +++ b/security/apparmor/include/policy_unpack.h | |
76514441 | 4123 | @@ -0,0 +1,20 @@ |
9474138d AM |
4124 | +/* |
4125 | + * AppArmor security module | |
4126 | + * | |
4127 | + * This file contains AppArmor policy loading interface function definitions. | |
4128 | + * | |
4129 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 4130 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4131 | + * |
4132 | + * This program is free software; you can redistribute it and/or | |
4133 | + * modify it under the terms of the GNU General Public License as | |
4134 | + * published by the Free Software Foundation, version 2 of the | |
4135 | + * License. | |
4136 | + */ | |
2380c486 | 4137 | + |
9474138d AM |
4138 | +#ifndef __POLICY_INTERFACE_H |
4139 | +#define __POLICY_INTERFACE_H | |
2380c486 | 4140 | + |
76514441 | 4141 | +struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); |
2380c486 | 4142 | + |
ceaf2cfb AM |
4143 | +#endif /* __POLICY_INTERFACE_H */ |
4144 | diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h | |
4145 | new file mode 100644 | |
76514441 | 4146 | index 0000000..544aa6b |
ceaf2cfb AM |
4147 | --- /dev/null |
4148 | +++ b/security/apparmor/include/procattr.h | |
9474138d AM |
4149 | @@ -0,0 +1,26 @@ |
4150 | +/* | |
4151 | + * AppArmor security module | |
4152 | + * | |
76514441 | 4153 | + * This file contains AppArmor /proc/<pid>/attr/ interface function definitions. |
9474138d AM |
4154 | + * |
4155 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 4156 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4157 | + * |
4158 | + * This program is free software; you can redistribute it and/or | |
4159 | + * modify it under the terms of the GNU General Public License as | |
4160 | + * published by the Free Software Foundation, version 2 of the | |
4161 | + * License. | |
4162 | + */ | |
2380c486 | 4163 | + |
9474138d AM |
4164 | +#ifndef __AA_PROCATTR_H |
4165 | +#define __AA_PROCATTR_H | |
2380c486 | 4166 | + |
9474138d | 4167 | +#define AA_DO_TEST 1 |
ceaf2cfb AM |
4168 | +#define AA_ONEXEC 1 |
4169 | + | |
4170 | +int aa_getprocattr(struct aa_profile *profile, char **string); | |
4171 | +int aa_setprocattr_changehat(char *args, size_t size, int test); | |
76514441 | 4172 | +int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); |
ceaf2cfb AM |
4173 | +int aa_setprocattr_permipc(char *fqname); |
4174 | + | |
4175 | +#endif /* __AA_PROCATTR_H */ | |
4176 | diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h | |
4177 | new file mode 100644 | |
76514441 | 4178 | index 0000000..3c88be9 |
ceaf2cfb AM |
4179 | --- /dev/null |
4180 | +++ b/security/apparmor/include/resource.h | |
76514441 | 4181 | @@ -0,0 +1,46 @@ |
9474138d AM |
4182 | +/* |
4183 | + * AppArmor security module | |
4184 | + * | |
76514441 | 4185 | + * This file contains AppArmor resource limits function definitions. |
9474138d AM |
4186 | + * |
4187 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 4188 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4189 | + * |
4190 | + * This program is free software; you can redistribute it and/or | |
4191 | + * modify it under the terms of the GNU General Public License as | |
4192 | + * published by the Free Software Foundation, version 2 of the | |
4193 | + * License. | |
4194 | + */ | |
2380c486 | 4195 | + |
9474138d AM |
4196 | +#ifndef __AA_RESOURCE_H |
4197 | +#define __AA_RESOURCE_H | |
2380c486 | 4198 | + |
9474138d AM |
4199 | +#include <linux/resource.h> |
4200 | +#include <linux/sched.h> | |
2380c486 | 4201 | + |
9474138d | 4202 | +struct aa_profile; |
2380c486 | 4203 | + |
76514441 | 4204 | +/* struct aa_rlimit - rlimit settings for the profile |
9474138d AM |
4205 | + * @mask: which hard limits to set |
4206 | + * @limits: rlimit values that override task limits | |
4207 | + * | |
4208 | + * AppArmor rlimits are used to set confined task rlimits. Only the | |
4209 | + * limits specified in @mask will be controlled by apparmor. | |
4210 | + */ | |
4211 | +struct aa_rlimit { | |
4212 | + unsigned int mask; | |
4213 | + struct rlimit limits[RLIM_NLIMITS]; | |
4214 | +}; | |
2380c486 | 4215 | + |
76514441 | 4216 | +int aa_map_resource(int resource); |
9474138d AM |
4217 | +int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, |
4218 | + struct rlimit *new_rlim); | |
4219 | + | |
4220 | +void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); | |
4221 | + | |
4222 | +static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) | |
4223 | +{ | |
4224 | + /* NOP */ | |
2380c486 JR |
4225 | +} |
4226 | + | |
ceaf2cfb AM |
4227 | +#endif /* __AA_RESOURCE_H */ |
4228 | diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h | |
4229 | new file mode 100644 | |
4230 | index 0000000..020db35 | |
4231 | --- /dev/null | |
4232 | +++ b/security/apparmor/include/sid.h | |
4233 | @@ -0,0 +1,24 @@ | |
9474138d AM |
4234 | +/* |
4235 | + * AppArmor security module | |
4236 | + * | |
4237 | + * This file contains AppArmor security identifier (sid) definitions | |
4238 | + * | |
ceaf2cfb | 4239 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4240 | + * |
4241 | + * This program is free software; you can redistribute it and/or | |
4242 | + * modify it under the terms of the GNU General Public License as | |
4243 | + * published by the Free Software Foundation, version 2 of the | |
4244 | + * License. | |
4245 | + */ | |
4246 | + | |
4247 | +#ifndef __AA_SID_H | |
4248 | +#define __AA_SID_H | |
4249 | + | |
4250 | +#include <linux/types.h> | |
4251 | + | |
4252 | +struct aa_profile; | |
4253 | + | |
ceaf2cfb | 4254 | +u32 aa_alloc_sid(void); |
9474138d | 4255 | +void aa_free_sid(u32 sid); |
2380c486 | 4256 | + |
ceaf2cfb AM |
4257 | +#endif /* __AA_SID_H */ |
4258 | diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c | |
4259 | new file mode 100644 | |
76514441 | 4260 | index 0000000..9013a78 |
ceaf2cfb AM |
4261 | --- /dev/null |
4262 | +++ b/security/apparmor/ipc.c | |
76514441 | 4263 | @@ -0,0 +1,114 @@ |
9474138d AM |
4264 | +/* |
4265 | + * AppArmor security module | |
4266 | + * | |
4267 | + * This file contains AppArmor ipc mediation | |
4268 | + * | |
4269 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 4270 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4271 | + * |
4272 | + * This program is free software; you can redistribute it and/or | |
4273 | + * modify it under the terms of the GNU General Public License as | |
4274 | + * published by the Free Software Foundation, version 2 of the | |
4275 | + * License. | |
4276 | + */ | |
2380c486 | 4277 | + |
9474138d AM |
4278 | +#include <linux/gfp.h> |
4279 | +#include <linux/ptrace.h> | |
2380c486 | 4280 | + |
9474138d AM |
4281 | +#include "include/audit.h" |
4282 | +#include "include/capability.h" | |
4283 | +#include "include/context.h" | |
4284 | +#include "include/policy.h" | |
2380c486 | 4285 | + |
9474138d | 4286 | +/* call back to audit ptrace fields */ |
76514441 | 4287 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 4288 | +{ |
76514441 AM |
4289 | + struct common_audit_data *sa = va; |
4290 | + audit_log_format(ab, " target="); | |
4291 | + audit_log_untrustedstring(ab, sa->aad.target); | |
2380c486 JR |
4292 | +} |
4293 | + | |
ceaf2cfb AM |
4294 | +/** |
4295 | + * aa_audit_ptrace - do auditing for ptrace | |
4296 | + * @profile: profile being enforced (NOT NULL) | |
76514441 AM |
4297 | + * @target: profile being traced (NOT NULL) |
4298 | + * @error: error condition | |
ceaf2cfb AM |
4299 | + * |
4300 | + * Returns: %0 or error code | |
4301 | + */ | |
9474138d | 4302 | +static int aa_audit_ptrace(struct aa_profile *profile, |
76514441 | 4303 | + struct aa_profile *target, int error) |
2380c486 | 4304 | +{ |
76514441 AM |
4305 | + struct common_audit_data sa; |
4306 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
4307 | + sa.aad.op = OP_PTRACE; | |
4308 | + sa.aad.target = target; | |
4309 | + sa.aad.error = error; | |
4310 | + | |
4311 | + return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, | |
4312 | + audit_cb); | |
2380c486 JR |
4313 | +} |
4314 | + | |
ceaf2cfb AM |
4315 | +/** |
4316 | + * aa_may_ptrace - test if tracer task can trace the tracee | |
4317 | + * @tracer_task: task who will do the tracing (NOT NULL) | |
4318 | + * @tracer: profile of the task doing the tracing (NOT NULL) | |
4319 | + * @tracee: task to be traced | |
4320 | + * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH | |
4321 | + * | |
4322 | + * Returns: %0 else error code if permission denied or error | |
4323 | + */ | |
9474138d AM |
4324 | +int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, |
4325 | + struct aa_profile *tracee, unsigned int mode) | |
2380c486 | 4326 | +{ |
9474138d AM |
4327 | + /* TODO: currently only based on capability, not extended ptrace |
4328 | + * rules, | |
4329 | + * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH | |
4330 | + */ | |
4331 | + | |
76514441 | 4332 | + if (unconfined(tracer) || tracer == tracee) |
9474138d AM |
4333 | + return 0; |
4334 | + /* log this capability request */ | |
4335 | + return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); | |
2380c486 JR |
4336 | +} |
4337 | + | |
ceaf2cfb AM |
4338 | +/** |
4339 | + * aa_ptrace - do ptrace permission check and auditing | |
76514441 AM |
4340 | + * @tracer: task doing the tracing (NOT NULL) |
4341 | + * @tracee: task being traced (NOT NULL) | |
ceaf2cfb AM |
4342 | + * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH |
4343 | + * | |
4344 | + * Returns: %0 else error code if permission denied or error | |
4345 | + */ | |
9474138d AM |
4346 | +int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, |
4347 | + unsigned int mode) | |
2380c486 | 4348 | +{ |
9474138d AM |
4349 | + /* |
4350 | + * tracer can ptrace tracee when | |
4351 | + * - tracer is unconfined || | |
9474138d AM |
4352 | + * - tracer is in complain mode |
4353 | + * - tracer has rules allowing it to trace tracee currently this is: | |
4354 | + * - confined by the same profile || | |
4355 | + * - tracer profile has CAP_SYS_PTRACE | |
4356 | + */ | |
4357 | + | |
4358 | + struct aa_profile *tracer_p; | |
ceaf2cfb AM |
4359 | + /* cred released below */ |
4360 | + const struct cred *cred = get_task_cred(tracer); | |
2380c486 | 4361 | + int error = 0; |
ceaf2cfb AM |
4362 | + tracer_p = aa_cred_profile(cred); |
4363 | + | |
4364 | + if (!unconfined(tracer_p)) { | |
ceaf2cfb AM |
4365 | + /* lcred released below */ |
4366 | + struct cred *lcred = get_task_cred(tracee); | |
76514441 | 4367 | + struct aa_profile *tracee_p = aa_cred_profile(lcred); |
2380c486 | 4368 | + |
76514441 AM |
4369 | + error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); |
4370 | + error = aa_audit_ptrace(tracer_p, tracee_p, error); | |
2380c486 | 4371 | + |
ceaf2cfb | 4372 | + put_cred(lcred); |
2380c486 | 4373 | + } |
9474138d | 4374 | + put_cred(cred); |
2380c486 | 4375 | + |
2380c486 JR |
4376 | + return error; |
4377 | +} | |
ceaf2cfb AM |
4378 | diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c |
4379 | new file mode 100644 | |
76514441 | 4380 | index 0000000..6e85cdb |
ceaf2cfb AM |
4381 | --- /dev/null |
4382 | +++ b/security/apparmor/lib.c | |
76514441 | 4383 | @@ -0,0 +1,133 @@ |
9474138d AM |
4384 | +/* |
4385 | + * AppArmor security module | |
4386 | + * | |
4387 | + * This file contains basic common functions used in AppArmor | |
4388 | + * | |
4389 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 4390 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4391 | + * |
4392 | + * This program is free software; you can redistribute it and/or | |
4393 | + * modify it under the terms of the GNU General Public License as | |
4394 | + * published by the Free Software Foundation, version 2 of the | |
4395 | + * License. | |
4396 | + */ | |
2380c486 | 4397 | + |
9474138d AM |
4398 | +#include <linux/slab.h> |
4399 | +#include <linux/string.h> | |
76514441 | 4400 | +#include <linux/vmalloc.h> |
2380c486 | 4401 | + |
9474138d | 4402 | +#include "include/audit.h" |
2380c486 | 4403 | + |
2380c486 | 4404 | + |
ceaf2cfb AM |
4405 | +/** |
4406 | + * aa_split_fqname - split a fqname into a profile and namespace name | |
76514441 AM |
4407 | + * @fqname: a full qualified name in namespace profile format (NOT NULL) |
4408 | + * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) | |
ceaf2cfb AM |
4409 | + * |
4410 | + * Returns: profile name or NULL if one is not specified | |
4411 | + * | |
4412 | + * Split a namespace name from a profile name (see policy.c for naming | |
4413 | + * description). If a portion of the name is missing it returns NULL for | |
4414 | + * that portion. | |
4415 | + * | |
76514441 | 4416 | + * NOTE: may modify the @fqname string. The pointers returned point |
ceaf2cfb AM |
4417 | + * into the @fqname string. |
4418 | + */ | |
4419 | +char *aa_split_fqname(char *fqname, char **ns_name) | |
9474138d | 4420 | +{ |
ceaf2cfb | 4421 | + char *name = strim(fqname); |
2380c486 | 4422 | + |
ceaf2cfb AM |
4423 | + *ns_name = NULL; |
4424 | + if (name[0] == ':') { | |
4425 | + char *split = strchr(&name[1], ':'); | |
4426 | + if (split) { | |
4427 | + /* overwrite ':' with \0 */ | |
4428 | + *split = 0; | |
4429 | + name = skip_spaces(split + 1); | |
4430 | + } else | |
4431 | + /* a ns name without a following profile is allowed */ | |
4432 | + name = NULL; | |
4433 | + *ns_name = &name[1]; | |
2380c486 | 4434 | + } |
ceaf2cfb | 4435 | + if (name && *name == 0) |
9474138d | 4436 | + name = NULL; |
2380c486 | 4437 | + |
9474138d | 4438 | + return name; |
2380c486 JR |
4439 | +} |
4440 | + | |
9474138d | 4441 | +/** |
76514441 AM |
4442 | + * aa_info_message - log a none profile related status message |
4443 | + * @str: message to log | |
4444 | + */ | |
4445 | +void aa_info_message(const char *str) | |
4446 | +{ | |
4447 | + if (audit_enabled) { | |
4448 | + struct common_audit_data sa; | |
4449 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
4450 | + sa.aad.info = str; | |
4451 | + aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); | |
4452 | + } | |
4453 | + printk(KERN_INFO "AppArmor: %s\n", str); | |
4454 | +} | |
4455 | + | |
4456 | +/** | |
4457 | + * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc | |
4458 | + * @size: size of allocation | |
9474138d | 4459 | + * |
76514441 AM |
4460 | + * Return: allocated buffer or NULL if failed |
4461 | + * | |
4462 | + * It is possible that policy being loaded from the user is larger than | |
4463 | + * what can be allocated by kmalloc, in those cases fall back to vmalloc. | |
9474138d | 4464 | + */ |
76514441 | 4465 | +void *kvmalloc(size_t size) |
9474138d | 4466 | +{ |
76514441 AM |
4467 | + void *buffer = NULL; |
4468 | + | |
4469 | + if (size == 0) | |
4470 | + return NULL; | |
4471 | + | |
4472 | + /* do not attempt kmalloc if we need more than 16 pages at once */ | |
4473 | + if (size <= (16*PAGE_SIZE)) | |
4474 | + buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); | |
4475 | + if (!buffer) { | |
4476 | + /* see kvfree for why size must be at least work_struct size | |
4477 | + * when allocated via vmalloc | |
4478 | + */ | |
4479 | + if (size < sizeof(struct work_struct)) | |
4480 | + size = sizeof(struct work_struct); | |
4481 | + buffer = vmalloc(size); | |
4482 | + } | |
4483 | + return buffer; | |
9474138d | 4484 | +} |
2380c486 | 4485 | + |
76514441 AM |
4486 | +/** |
4487 | + * do_vfree - workqueue routine for freeing vmalloced memory | |
4488 | + * @work: data to be freed | |
4489 | + * | |
4490 | + * The work_struct is overlaid to the data being freed, as at the point | |
4491 | + * the work is scheduled the data is no longer valid, be its freeing | |
4492 | + * needs to be delayed until safe. | |
4493 | + */ | |
4494 | +static void do_vfree(struct work_struct *work) | |
9474138d | 4495 | +{ |
76514441 | 4496 | + vfree(work); |
2380c486 | 4497 | +} |
ceaf2cfb | 4498 | + |
76514441 AM |
4499 | +/** |
4500 | + * kvfree - free an allocation do by kvmalloc | |
4501 | + * @buffer: buffer to free (MAYBE_NULL) | |
4502 | + * | |
4503 | + * Free a buffer allocated by kvmalloc | |
4504 | + */ | |
4505 | +void kvfree(void *buffer) | |
4506 | +{ | |
4507 | + if (is_vmalloc_addr(buffer)) { | |
4508 | + /* Data is no longer valid so just use the allocated space | |
4509 | + * as the work_struct | |
4510 | + */ | |
4511 | + struct work_struct *work = (struct work_struct *) buffer; | |
4512 | + INIT_WORK(work, do_vfree); | |
4513 | + schedule_work(work); | |
4514 | + } else | |
4515 | + kfree(buffer); | |
4516 | +} | |
ceaf2cfb AM |
4517 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c |
4518 | new file mode 100644 | |
76514441 | 4519 | index 0000000..e8d0821 |
ceaf2cfb AM |
4520 | --- /dev/null |
4521 | +++ b/security/apparmor/lsm.c | |
76514441 | 4522 | @@ -0,0 +1,1051 @@ |
9474138d AM |
4523 | +/* |
4524 | + * AppArmor security module | |
4525 | + * | |
4526 | + * This file contains AppArmor LSM hooks. | |
4527 | + * | |
4528 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 4529 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
4530 | + * |
4531 | + * This program is free software; you can redistribute it and/or | |
4532 | + * modify it under the terms of the GNU General Public License as | |
4533 | + * published by the Free Software Foundation, version 2 of the | |
4534 | + * License. | |
4535 | + */ | |
2380c486 | 4536 | + |
9474138d AM |
4537 | +#include <linux/security.h> |
4538 | +#include <linux/moduleparam.h> | |
4539 | +#include <linux/mm.h> | |
4540 | +#include <linux/mman.h> | |
4541 | +#include <linux/mount.h> | |
4542 | +#include <linux/namei.h> | |
4543 | +#include <linux/ptrace.h> | |
4544 | +#include <linux/ctype.h> | |
4545 | +#include <linux/sysctl.h> | |
4546 | +#include <linux/audit.h> | |
4547 | +#include <net/sock.h> | |
4548 | + | |
4549 | +#include "include/apparmor.h" | |
4550 | +#include "include/apparmorfs.h" | |
4551 | +#include "include/audit.h" | |
4552 | +#include "include/capability.h" | |
4553 | +#include "include/context.h" | |
4554 | +#include "include/file.h" | |
4555 | +#include "include/ipc.h" | |
4556 | +#include "include/net.h" | |
4557 | +#include "include/path.h" | |
4558 | +#include "include/policy.h" | |
4559 | +#include "include/procattr.h" | |
4560 | + | |
4561 | +/* Flag indicating whether initialization completed */ | |
76514441 | 4562 | +int apparmor_initialized __initdata; |
9474138d | 4563 | + |
9474138d AM |
4564 | +/* |
4565 | + * LSM hook functions | |
4566 | + */ | |
4567 | + | |
4568 | +/* | |
ceaf2cfb AM |
4569 | + * free the associated aa_task_cxt and put its profiles |
4570 | + */ | |
4571 | +static void apparmor_cred_free(struct cred *cred) | |
4572 | +{ | |
4573 | + aa_free_task_context(cred->security); | |
4574 | + cred->security = NULL; | |
4575 | +} | |
4576 | + | |
4577 | +/* | |
4578 | + * allocate the apparmor part of blank credentials | |
4579 | + */ | |
4580 | +static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) | |
4581 | +{ | |
4582 | + /* freed by apparmor_cred_free */ | |
4583 | + struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | |
4584 | + if (!cxt) | |
4585 | + return -ENOMEM; | |
4586 | + | |
4587 | + cred->security = cxt; | |
4588 | + return 0; | |
4589 | +} | |
4590 | + | |
4591 | +/* | |
4592 | + * prepare new aa_task_cxt for modification by prepare_cred block | |
9474138d AM |
4593 | + */ |
4594 | +static int apparmor_cred_prepare(struct cred *new, const struct cred *old, | |
4595 | + gfp_t gfp) | |
2380c486 | 4596 | +{ |
ceaf2cfb AM |
4597 | + /* freed by apparmor_cred_free */ |
4598 | + struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | |
9474138d AM |
4599 | + if (!cxt) |
4600 | + return -ENOMEM; | |
ceaf2cfb AM |
4601 | + |
4602 | + aa_dup_task_context(cxt, old->security); | |
9474138d AM |
4603 | + new->security = cxt; |
4604 | + return 0; | |
2380c486 JR |
4605 | +} |
4606 | + | |
9474138d | 4607 | +/* |
ceaf2cfb | 4608 | + * transfer the apparmor data to a blank set of creds |
9474138d | 4609 | + */ |
ceaf2cfb | 4610 | +static void apparmor_cred_transfer(struct cred *new, const struct cred *old) |
2380c486 | 4611 | +{ |
ceaf2cfb AM |
4612 | + const struct aa_task_cxt *old_cxt = old->security; |
4613 | + struct aa_task_cxt *new_cxt = new->security; | |
2380c486 | 4614 | + |
ceaf2cfb AM |
4615 | + aa_dup_task_context(new_cxt, old_cxt); |
4616 | +} | |
9474138d | 4617 | + |
ceaf2cfb AM |
4618 | +static int apparmor_ptrace_access_check(struct task_struct *child, |
4619 | + unsigned int mode) | |
2380c486 | 4620 | +{ |
76514441 AM |
4621 | + int error = cap_ptrace_access_check(child, mode); |
4622 | + if (error) | |
4623 | + return error; | |
ceaf2cfb | 4624 | + |
9474138d | 4625 | + return aa_ptrace(current, child, mode); |
2380c486 JR |
4626 | +} |
4627 | + | |
9474138d | 4628 | +static int apparmor_ptrace_traceme(struct task_struct *parent) |
2380c486 | 4629 | +{ |
76514441 AM |
4630 | + int error = cap_ptrace_traceme(parent); |
4631 | + if (error) | |
4632 | + return error; | |
4633 | + | |
9474138d | 4634 | + return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); |
2380c486 JR |
4635 | +} |
4636 | + | |
9474138d AM |
4637 | +/* Derived from security/commoncap.c:cap_capget */ |
4638 | +static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, | |
4639 | + kernel_cap_t *inheritable, kernel_cap_t *permitted) | |
2380c486 JR |
4640 | +{ |
4641 | + struct aa_profile *profile; | |
9474138d | 4642 | + const struct cred *cred; |
2380c486 | 4643 | + |
9474138d AM |
4644 | + rcu_read_lock(); |
4645 | + cred = __task_cred(target); | |
ceaf2cfb | 4646 | + profile = aa_cred_profile(cred); |
2380c486 | 4647 | + |
ceaf2cfb | 4648 | + *effective = cred->cap_effective; |
9474138d | 4649 | + *inheritable = cred->cap_inheritable; |
ceaf2cfb | 4650 | + *permitted = cred->cap_permitted; |
2380c486 | 4651 | + |
76514441 AM |
4652 | + if (!unconfined(profile)) { |
4653 | + *effective = cap_intersect(*effective, profile->caps.allow); | |
4654 | + *permitted = cap_intersect(*permitted, profile->caps.allow); | |
4655 | + } | |
9474138d | 4656 | + rcu_read_unlock(); |
2380c486 | 4657 | + |
9474138d | 4658 | + return 0; |
2380c486 JR |
4659 | +} |
4660 | + | |
9474138d AM |
4661 | +static int apparmor_capable(struct task_struct *task, const struct cred *cred, |
4662 | + int cap, int audit) | |
2380c486 | 4663 | +{ |
9474138d AM |
4664 | + struct aa_profile *profile; |
4665 | + /* cap_capable returns 0 on success, else -EPERM */ | |
4666 | + int error = cap_capable(task, cred, cap, audit); | |
76514441 AM |
4667 | + if (!error) { |
4668 | + profile = aa_cred_profile(cred); | |
4669 | + if (!unconfined(profile)) | |
4670 | + error = aa_capable(task, profile, cap, audit); | |
9474138d | 4671 | + } |
9474138d | 4672 | + return error; |
2380c486 JR |
4673 | +} |
4674 | + | |
ceaf2cfb AM |
4675 | +/** |
4676 | + * common_perm - basic common permission check wrapper fn for paths | |
76514441 | 4677 | + * @op: operation being checked |
ceaf2cfb AM |
4678 | + * @path: path to check permission of (NOT NULL) |
4679 | + * @mask: requested permissions mask | |
4680 | + * @cond: conditional info for the permission request (NOT NULL) | |
4681 | + * | |
4682 | + * Returns: %0 else error code if error or permission denied | |
4683 | + */ | |
76514441 | 4684 | +static int common_perm(int op, struct path *path, u32 mask, |
9474138d | 4685 | + struct path_cond *cond) |
2380c486 JR |
4686 | +{ |
4687 | + struct aa_profile *profile; | |
9474138d | 4688 | + int error = 0; |
2380c486 | 4689 | + |
ceaf2cfb AM |
4690 | + profile = __aa_current_profile(); |
4691 | + if (!unconfined(profile)) | |
76514441 | 4692 | + error = aa_path_perm(op, profile, path, 0, mask, cond); |
2380c486 | 4693 | + |
9474138d | 4694 | + return error; |
2380c486 JR |
4695 | +} |
4696 | + | |
ceaf2cfb | 4697 | +/** |
76514441 AM |
4698 | + * common_perm_dir_dentry - common permission wrapper when path is dir, dentry |
4699 | + * @op: operation being checked | |
ceaf2cfb AM |
4700 | + * @dir: directory of the dentry (NOT NULL) |
4701 | + * @dentry: dentry to check (NOT NULL) | |
4702 | + * @mask: requested permissions mask | |
4703 | + * @cond: conditional info for the permission request (NOT NULL) | |
4704 | + * | |
4705 | + * Returns: %0 else error code if error or permission denied | |
4706 | + */ | |
76514441 AM |
4707 | +static int common_perm_dir_dentry(int op, struct path *dir, |
4708 | + struct dentry *dentry, u32 mask, | |
4709 | + struct path_cond *cond) | |
2380c486 | 4710 | +{ |
9474138d | 4711 | + struct path path = { dir->mnt, dentry }; |
2380c486 | 4712 | + |
9474138d | 4713 | + return common_perm(op, &path, mask, cond); |
2380c486 JR |
4714 | +} |
4715 | + | |
ceaf2cfb | 4716 | +/** |
76514441 AM |
4717 | + * common_perm_mnt_dentry - common permission wrapper when mnt, dentry |
4718 | + * @op: operation being checked | |
4719 | + * @mnt: mount point of dentry (NOT NULL) | |
4720 | + * @dentry: dentry to check (NOT NULL) | |
4721 | + * @mask: requested permissions mask | |
4722 | + * | |
4723 | + * Returns: %0 else error code if error or permission denied | |
4724 | + */ | |
4725 | +static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, | |
4726 | + struct dentry *dentry, u32 mask) | |
4727 | +{ | |
4728 | + struct path path = { mnt, dentry }; | |
4729 | + struct path_cond cond = { dentry->d_inode->i_uid, | |
4730 | + dentry->d_inode->i_mode | |
4731 | + }; | |
4732 | + | |
4733 | + return common_perm(op, &path, mask, &cond); | |
4734 | +} | |
4735 | + | |
4736 | +/** | |
ceaf2cfb | 4737 | + * common_perm_rm - common permission wrapper for operations doing rm |
76514441 | 4738 | + * @op: operation being checked |
ceaf2cfb AM |
4739 | + * @dir: directory that the dentry is in (NOT NULL) |
4740 | + * @dentry: dentry being rm'd (NOT NULL) | |
4741 | + * @mask: requested permission mask | |
4742 | + * | |
4743 | + * Returns: %0 else error code if error or permission denied | |
4744 | + */ | |
76514441 AM |
4745 | +static int common_perm_rm(int op, struct path *dir, |
4746 | + struct dentry *dentry, u32 mask) | |
2380c486 | 4747 | +{ |
9474138d | 4748 | + struct inode *inode = dentry->d_inode; |
ceaf2cfb | 4749 | + struct path_cond cond = { }; |
2380c486 | 4750 | + |
76514441 | 4751 | + if (!inode || !dir->mnt || !mediated_filesystem(inode)) |
2380c486 JR |
4752 | + return 0; |
4753 | + | |
9474138d AM |
4754 | + cond.uid = inode->i_uid; |
4755 | + cond.mode = inode->i_mode; | |
2380c486 | 4756 | + |
76514441 | 4757 | + return common_perm_dir_dentry(op, dir, dentry, mask, &cond); |
2380c486 JR |
4758 | +} |
4759 | + | |
ceaf2cfb AM |
4760 | +/** |
4761 | + * common_perm_create - common permission wrapper for operations doing create | |
76514441 | 4762 | + * @op: operation being checked |
ceaf2cfb AM |
4763 | + * @dir: directory that dentry will be created in (NOT NULL) |
4764 | + * @dentry: dentry to create (NOT NULL) | |
4765 | + * @mask: request permission mask | |
4766 | + * @mode: created file mode | |
4767 | + * | |
4768 | + * Returns: %0 else error code if error or permission denied | |
4769 | + */ | |
76514441 AM |
4770 | +static int common_perm_create(int op, struct path *dir, struct dentry *dentry, |
4771 | + u32 mask, umode_t mode) | |
2380c486 | 4772 | +{ |
9474138d AM |
4773 | + struct path_cond cond = { current_fsuid(), mode }; |
4774 | + | |
4775 | + if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode)) | |
4776 | + return 0; | |
2380c486 | 4777 | + |
76514441 | 4778 | + return common_perm_dir_dentry(op, dir, dentry, mask, &cond); |
2380c486 JR |
4779 | +} |
4780 | + | |
9474138d | 4781 | +static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) |
2380c486 | 4782 | +{ |
76514441 | 4783 | + return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE); |
2380c486 JR |
4784 | +} |
4785 | + | |
9474138d AM |
4786 | +static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, |
4787 | + int mode) | |
2380c486 | 4788 | +{ |
76514441 AM |
4789 | + return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE, |
4790 | + S_IFDIR); | |
2380c486 JR |
4791 | +} |
4792 | + | |
9474138d AM |
4793 | +static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) |
4794 | +{ | |
76514441 | 4795 | + return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE); |
9474138d AM |
4796 | +} |
4797 | + | |
4798 | +static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, | |
4799 | + int mode, unsigned int dev) | |
4800 | +{ | |
76514441 | 4801 | + return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); |
9474138d AM |
4802 | +} |
4803 | + | |
4804 | +static int apparmor_path_truncate(struct path *path, loff_t length, | |
4805 | + unsigned int time_attrs) | |
4806 | +{ | |
4807 | + struct path_cond cond = { path->dentry->d_inode->i_uid, | |
ceaf2cfb AM |
4808 | + path->dentry->d_inode->i_mode |
4809 | + }; | |
9474138d AM |
4810 | + |
4811 | + if (!path->mnt || !mediated_filesystem(path->dentry->d_inode)) | |
4812 | + return 0; | |
76514441 AM |
4813 | + |
4814 | + return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE, | |
4815 | + &cond); | |
9474138d AM |
4816 | +} |
4817 | + | |
4818 | +static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, | |
ceaf2cfb | 4819 | + const char *old_name) |
2380c486 | 4820 | +{ |
76514441 | 4821 | + return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE, |
9474138d | 4822 | + S_IFLNK); |
2380c486 JR |
4823 | +} |
4824 | + | |
9474138d AM |
4825 | +static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, |
4826 | + struct dentry *new_dentry) | |
2380c486 JR |
4827 | +{ |
4828 | + struct aa_profile *profile; | |
4829 | + int error = 0; | |
4830 | + | |
9474138d | 4831 | + if (!mediated_filesystem(old_dentry->d_inode)) |
2380c486 JR |
4832 | + return 0; |
4833 | + | |
ceaf2cfb AM |
4834 | + profile = aa_current_profile(); |
4835 | + if (!unconfined(profile)) | |
9474138d | 4836 | + error = aa_path_link(profile, old_dentry, new_dir, new_dentry); |
2380c486 JR |
4837 | + return error; |
4838 | +} | |
4839 | + | |
9474138d | 4840 | +static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, |
ceaf2cfb | 4841 | + struct path *new_dir, struct dentry *new_dentry) |
2380c486 | 4842 | +{ |
9474138d AM |
4843 | + struct aa_profile *profile; |
4844 | + int error = 0; | |
2380c486 | 4845 | + |
9474138d | 4846 | + if (!mediated_filesystem(old_dentry->d_inode)) |
2380c486 JR |
4847 | + return 0; |
4848 | + | |
ceaf2cfb AM |
4849 | + profile = aa_current_profile(); |
4850 | + if (!unconfined(profile)) { | |
9474138d AM |
4851 | + struct path old_path = { old_dir->mnt, old_dentry }; |
4852 | + struct path new_path = { new_dir->mnt, new_dentry }; | |
4853 | + struct path_cond cond = { old_dentry->d_inode->i_uid, | |
ceaf2cfb AM |
4854 | + old_dentry->d_inode->i_mode |
4855 | + }; | |
9474138d | 4856 | + |
76514441 AM |
4857 | + error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0, |
4858 | + MAY_READ | AA_MAY_META_READ | MAY_WRITE | | |
4859 | + AA_MAY_META_WRITE | AA_MAY_DELETE, | |
4860 | + &cond); | |
9474138d | 4861 | + if (!error) |
76514441 AM |
4862 | + error = aa_path_perm(OP_RENAME_DEST, profile, &new_path, |
4863 | + 0, MAY_WRITE | AA_MAY_META_WRITE | | |
4864 | + AA_MAY_CREATE, &cond); | |
ceaf2cfb AM |
4865 | + |
4866 | + } | |
4867 | + return error; | |
4868 | +} | |
4869 | + | |
4870 | +static int apparmor_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | |
4871 | + mode_t mode) | |
4872 | +{ | |
ceaf2cfb AM |
4873 | + if (!mediated_filesystem(dentry->d_inode)) |
4874 | + return 0; | |
4875 | + | |
76514441 | 4876 | + return common_perm_mnt_dentry(OP_CHMOD, mnt, dentry, AA_MAY_CHMOD); |
ceaf2cfb AM |
4877 | +} |
4878 | + | |
4879 | +static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid) | |
4880 | +{ | |
76514441 AM |
4881 | + struct path_cond cond = { path->dentry->d_inode->i_uid, |
4882 | + path->dentry->d_inode->i_mode | |
4883 | + }; | |
ceaf2cfb AM |
4884 | + |
4885 | + if (!mediated_filesystem(path->dentry->d_inode)) | |
4886 | + return 0; | |
4887 | + | |
76514441 AM |
4888 | + return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond); |
4889 | +} | |
ceaf2cfb | 4890 | + |
76514441 AM |
4891 | +static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) |
4892 | +{ | |
4893 | + if (!mediated_filesystem(dentry->d_inode)) | |
4894 | + return 0; | |
4895 | + | |
4896 | + return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry, | |
4897 | + AA_MAY_META_READ); | |
2380c486 JR |
4898 | +} |
4899 | + | |
9474138d | 4900 | +static int apparmor_dentry_open(struct file *file, const struct cred *cred) |
2380c486 | 4901 | +{ |
76514441 | 4902 | + struct aa_file_cxt *fcxt = file->f_security; |
9474138d AM |
4903 | + struct aa_profile *profile; |
4904 | + int error = 0; | |
ceaf2cfb | 4905 | + |
76514441 AM |
4906 | + if (!mediated_filesystem(file->f_path.dentry->d_inode)) |
4907 | + return 0; | |
4908 | + | |
4909 | + /* If in exec, permission is handled by bprm hooks. | |
4910 | + * Cache permissions granted by the previous exec check, with | |
4911 | + * implicit read and executable mmap which are required to | |
4912 | + * actually execute the image. | |
4913 | + */ | |
4914 | + if (current->in_execve) { | |
4915 | + fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; | |
9474138d | 4916 | + return 0; |
76514441 | 4917 | + } |
2380c486 | 4918 | + |
ceaf2cfb AM |
4919 | + profile = aa_cred_profile(cred); |
4920 | + if (!unconfined(profile)) { | |
ceaf2cfb | 4921 | + struct inode *inode = file->f_path.dentry->d_inode; |
9474138d | 4922 | + struct path_cond cond = { inode->i_uid, inode->i_mode }; |
2380c486 | 4923 | + |
76514441 | 4924 | + error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, |
9474138d | 4925 | + aa_map_file_to_perms(file), &cond); |
76514441 AM |
4926 | + /* todo cache full allowed permissions set and state */ |
4927 | + fcxt->allow = aa_map_file_to_perms(file); | |
9474138d | 4928 | + } |
2380c486 | 4929 | + |
9474138d | 4930 | + return error; |
2380c486 JR |
4931 | +} |
4932 | + | |
9474138d | 4933 | +static int apparmor_file_alloc_security(struct file *file) |
2380c486 | 4934 | +{ |
ceaf2cfb | 4935 | + /* freed by apparmor_file_free_security */ |
9474138d AM |
4936 | + file->f_security = aa_alloc_file_context(GFP_KERNEL); |
4937 | + if (!file->f_security) | |
4938 | + return -ENOMEM; | |
4939 | + return 0; | |
2380c486 | 4940 | + |
2380c486 JR |
4941 | +} |
4942 | + | |
9474138d | 4943 | +static void apparmor_file_free_security(struct file *file) |
2380c486 | 4944 | +{ |
9474138d | 4945 | + struct aa_file_cxt *cxt = file->f_security; |
2380c486 | 4946 | + |
9474138d | 4947 | + aa_free_file_context(cxt); |
2380c486 JR |
4948 | +} |
4949 | + | |
76514441 | 4950 | +static int common_file_perm(int op, struct file *file, u32 mask) |
2380c486 | 4951 | +{ |
9474138d | 4952 | + struct aa_file_cxt *fcxt = file->f_security; |
76514441 | 4953 | + struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); |
9474138d | 4954 | + int error = 0; |
2380c486 | 4955 | + |
76514441 AM |
4956 | + BUG_ON(!fprofile); |
4957 | + | |
4958 | + if (!file->f_path.mnt || | |
9474138d AM |
4959 | + !mediated_filesystem(file->f_path.dentry->d_inode)) |
4960 | + return 0; | |
4961 | + | |
ceaf2cfb AM |
4962 | + profile = __aa_current_profile(); |
4963 | + | |
76514441 AM |
4964 | + /* revalidate access, if task is unconfined, or the cached cred |
4965 | + * doesn't match or if the request is for more permissions than | |
4966 | + * was granted. | |
4967 | + * | |
4968 | + * Note: the test for !unconfined(fprofile) is to handle file | |
4969 | + * delegation from unconfined tasks | |
ceaf2cfb | 4970 | + */ |
76514441 AM |
4971 | + if (!unconfined(profile) && !unconfined(fprofile) && |
4972 | + ((fprofile != profile) || (mask & ~fcxt->allow))) | |
4973 | + error = aa_file_perm(op, profile, file, mask); | |
9474138d AM |
4974 | + |
4975 | + return error; | |
2380c486 JR |
4976 | +} |
4977 | + | |
76514441 | 4978 | +static int apparmor_file_permission(struct file *file, int mask) |
2380c486 | 4979 | +{ |
76514441 | 4980 | + return common_file_perm(OP_FPERM, file, mask); |
2380c486 JR |
4981 | +} |
4982 | + | |
9474138d | 4983 | +static int apparmor_file_lock(struct file *file, unsigned int cmd) |
2380c486 | 4984 | +{ |
76514441 | 4985 | + u32 mask = AA_MAY_LOCK; |
2380c486 | 4986 | + |
9474138d AM |
4987 | + if (cmd == F_WRLCK) |
4988 | + mask |= MAY_WRITE; | |
4989 | + | |
76514441 | 4990 | + return common_file_perm(OP_FLOCK, file, mask); |
2380c486 JR |
4991 | +} |
4992 | + | |
76514441 AM |
4993 | +static int common_mmap(int op, struct file *file, unsigned long prot, |
4994 | + unsigned long flags) | |
2380c486 | 4995 | +{ |
9474138d AM |
4996 | + struct dentry *dentry; |
4997 | + int mask = 0; | |
2380c486 | 4998 | + |
9474138d AM |
4999 | + if (!file || !file->f_security) |
5000 | + return 0; | |
5001 | + | |
5002 | + if (prot & PROT_READ) | |
5003 | + mask |= MAY_READ; | |
ceaf2cfb AM |
5004 | + /* |
5005 | + * Private mappings don't require write perms since they don't | |
5006 | + * write back to the files | |
5007 | + */ | |
9474138d AM |
5008 | + if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) |
5009 | + mask |= MAY_WRITE; | |
5010 | + if (prot & PROT_EXEC) | |
5011 | + mask |= AA_EXEC_MMAP; | |
5012 | + | |
5013 | + dentry = file->f_path.dentry; | |
76514441 | 5014 | + return common_file_perm(op, file, mask); |
2380c486 JR |
5015 | +} |
5016 | + | |
9474138d AM |
5017 | +static int apparmor_file_mmap(struct file *file, unsigned long reqprot, |
5018 | + unsigned long prot, unsigned long flags, | |
5019 | + unsigned long addr, unsigned long addr_only) | |
2380c486 | 5020 | +{ |
ceaf2cfb AM |
5021 | + int rc = 0; |
5022 | + | |
5023 | + /* do DAC check */ | |
5024 | + rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); | |
5025 | + if (rc || addr_only) | |
5026 | + return rc; | |
2380c486 | 5027 | + |
76514441 | 5028 | + return common_mmap(OP_FMMAP, file, prot, flags); |
2380c486 JR |
5029 | +} |
5030 | + | |
9474138d AM |
5031 | +static int apparmor_file_mprotect(struct vm_area_struct *vma, |
5032 | + unsigned long reqprot, unsigned long prot) | |
2380c486 | 5033 | +{ |
76514441 | 5034 | + return common_mmap(OP_FMPROT, vma->vm_file, prot, |
ceaf2cfb | 5035 | + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); |
2380c486 JR |
5036 | +} |
5037 | + | |
5038 | +static int apparmor_getprocattr(struct task_struct *task, char *name, | |
5039 | + char **value) | |
5040 | +{ | |
9474138d | 5041 | + int error = -ENOENT; |
ceaf2cfb AM |
5042 | + struct aa_profile *profile; |
5043 | + /* released below */ | |
5044 | + const struct cred *cred = get_task_cred(task); | |
5045 | + struct aa_task_cxt *cxt = cred->security; | |
5046 | + profile = aa_cred_profile(cred); | |
5047 | + | |
5048 | + if (strcmp(name, "current") == 0) | |
5049 | + error = aa_getprocattr(aa_newest_version(cxt->profile), | |
5050 | + value); | |
5051 | + else if (strcmp(name, "prev") == 0 && cxt->previous) | |
5052 | + error = aa_getprocattr(aa_newest_version(cxt->previous), | |
5053 | + value); | |
5054 | + else if (strcmp(name, "exec") == 0 && cxt->onexec) | |
5055 | + error = aa_getprocattr(aa_newest_version(cxt->onexec), | |
5056 | + value); | |
5057 | + else | |
5058 | + error = -EINVAL; | |
2380c486 | 5059 | + |
9474138d | 5060 | + put_cred(cred); |
2380c486 JR |
5061 | + |
5062 | + return error; | |
5063 | +} | |
5064 | + | |
5065 | +static int apparmor_setprocattr(struct task_struct *task, char *name, | |
5066 | + void *value, size_t size) | |
5067 | +{ | |
76514441 | 5068 | + char *command, *args = value; |
ceaf2cfb | 5069 | + size_t arg_size; |
2380c486 JR |
5070 | + int error; |
5071 | + | |
76514441 | 5072 | + if (size == 0) |
2380c486 | 5073 | + return -EINVAL; |
76514441 AM |
5074 | + /* args points to a PAGE_SIZE buffer, AppArmor requires that |
5075 | + * the buffer must be null terminated or have size <= PAGE_SIZE -1 | |
5076 | + * so that AppArmor can null terminate them | |
5077 | + */ | |
5078 | + if (args[size - 1] != '\0') { | |
5079 | + if (size == PAGE_SIZE) | |
5080 | + return -EINVAL; | |
5081 | + args[size] = '\0'; | |
5082 | + } | |
9474138d AM |
5083 | + |
5084 | + /* task can only write its own attributes */ | |
5085 | + if (current != task) | |
5086 | + return -EACCES; | |
5087 | + | |
2380c486 | 5088 | + args = value; |
ceaf2cfb | 5089 | + args = strim(args); |
2380c486 JR |
5090 | + command = strsep(&args, " "); |
5091 | + if (!args) | |
5092 | + return -EINVAL; | |
ceaf2cfb | 5093 | + args = skip_spaces(args); |
2380c486 JR |
5094 | + if (!*args) |
5095 | + return -EINVAL; | |
5096 | + | |
ceaf2cfb | 5097 | + arg_size = size - (args - (char *) value); |
9474138d AM |
5098 | + if (strcmp(name, "current") == 0) { |
5099 | + if (strcmp(command, "changehat") == 0) { | |
ceaf2cfb AM |
5100 | + error = aa_setprocattr_changehat(args, arg_size, |
5101 | + !AA_DO_TEST); | |
9474138d | 5102 | + } else if (strcmp(command, "permhat") == 0) { |
ceaf2cfb AM |
5103 | + error = aa_setprocattr_changehat(args, arg_size, |
5104 | + AA_DO_TEST); | |
9474138d | 5105 | + } else if (strcmp(command, "changeprofile") == 0) { |
ceaf2cfb | 5106 | + error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, |
9474138d AM |
5107 | + !AA_DO_TEST); |
5108 | + } else if (strcmp(command, "permprofile") == 0) { | |
ceaf2cfb | 5109 | + error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, |
9474138d AM |
5110 | + AA_DO_TEST); |
5111 | + } else if (strcmp(command, "permipc") == 0) { | |
5112 | + error = aa_setprocattr_permipc(args); | |
5113 | + } else { | |
76514441 AM |
5114 | + struct common_audit_data sa; |
5115 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
5116 | + sa.aad.op = OP_SETPROCATTR; | |
5117 | + sa.aad.info = name; | |
5118 | + sa.aad.error = -EINVAL; | |
5119 | + return aa_audit(AUDIT_APPARMOR_DENIED, NULL, GFP_KERNEL, | |
5120 | + &sa, NULL); | |
2380c486 | 5121 | + } |
9474138d | 5122 | + } else if (strcmp(name, "exec") == 0) { |
ceaf2cfb | 5123 | + error = aa_setprocattr_changeprofile(args, AA_ONEXEC, |
9474138d | 5124 | + !AA_DO_TEST); |
2380c486 | 5125 | + } else { |
9474138d | 5126 | + /* only support the "current" and "exec" process attributes */ |
2380c486 JR |
5127 | + return -EINVAL; |
5128 | + } | |
2380c486 JR |
5129 | + if (!error) |
5130 | + error = size; | |
5131 | + return error; | |
5132 | +} | |
5133 | + | |
5134 | +static int apparmor_task_setrlimit(unsigned int resource, | |
5135 | + struct rlimit *new_rlim) | |
5136 | +{ | |
ceaf2cfb | 5137 | + struct aa_profile *profile = aa_current_profile(); |
2380c486 JR |
5138 | + int error = 0; |
5139 | + | |
ceaf2cfb | 5140 | + if (!unconfined(profile)) |
2380c486 | 5141 | + error = aa_task_setrlimit(profile, resource, new_rlim); |
2380c486 JR |
5142 | + |
5143 | + return error; | |
5144 | +} | |
5145 | + | |
ceaf2cfb AM |
5146 | +static int apparmor_socket_create(int family, int type, int protocol, int kern) |
5147 | +{ | |
9474138d AM |
5148 | + struct aa_profile *profile; |
5149 | + int error = 0; | |
2380c486 | 5150 | + |
9474138d AM |
5151 | + if (kern) |
5152 | + return 0; | |
2380c486 | 5153 | + |
ceaf2cfb AM |
5154 | + profile = __aa_current_profile(); |
5155 | + if (!unconfined(profile)) | |
76514441 AM |
5156 | + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, |
5157 | + NULL); | |
9474138d | 5158 | + return error; |
2380c486 JR |
5159 | +} |
5160 | + | |
9474138d AM |
5161 | +static int apparmor_socket_bind(struct socket *sock, |
5162 | + struct sockaddr *address, int addrlen) | |
2380c486 | 5163 | +{ |
9474138d | 5164 | + struct sock *sk = sock->sk; |
2380c486 | 5165 | + |
76514441 | 5166 | + return aa_revalidate_sk(OP_BIND, sk); |
2380c486 JR |
5167 | +} |
5168 | + | |
9474138d | 5169 | +static int apparmor_socket_connect(struct socket *sock, |
ceaf2cfb | 5170 | + struct sockaddr *address, int addrlen) |
9474138d AM |
5171 | +{ |
5172 | + struct sock *sk = sock->sk; | |
2380c486 | 5173 | + |
76514441 | 5174 | + return aa_revalidate_sk(OP_CONNECT, sk); |
9474138d | 5175 | +} |
2380c486 | 5176 | + |
9474138d AM |
5177 | +static int apparmor_socket_listen(struct socket *sock, int backlog) |
5178 | +{ | |
5179 | + struct sock *sk = sock->sk; | |
2380c486 | 5180 | + |
76514441 | 5181 | + return aa_revalidate_sk(OP_LISTEN, sk); |
9474138d | 5182 | +} |
2380c486 | 5183 | + |
9474138d AM |
5184 | +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) |
5185 | +{ | |
5186 | + struct sock *sk = sock->sk; | |
2380c486 | 5187 | + |
76514441 | 5188 | + return aa_revalidate_sk(OP_ACCEPT, sk); |
9474138d | 5189 | +} |
2380c486 | 5190 | + |
9474138d | 5191 | +static int apparmor_socket_sendmsg(struct socket *sock, |
ceaf2cfb | 5192 | + struct msghdr *msg, int size) |
2380c486 | 5193 | +{ |
9474138d AM |
5194 | + struct sock *sk = sock->sk; |
5195 | + | |
76514441 | 5196 | + return aa_revalidate_sk(OP_SENDMSG, sk); |
2380c486 JR |
5197 | +} |
5198 | + | |
9474138d AM |
5199 | +static int apparmor_socket_recvmsg(struct socket *sock, |
5200 | + struct msghdr *msg, int size, int flags) | |
2380c486 | 5201 | +{ |
9474138d | 5202 | + struct sock *sk = sock->sk; |
2380c486 | 5203 | + |
76514441 | 5204 | + return aa_revalidate_sk(OP_RECVMSG, sk); |
2380c486 JR |
5205 | +} |
5206 | + | |
9474138d | 5207 | +static int apparmor_socket_getsockname(struct socket *sock) |
2380c486 | 5208 | +{ |
9474138d AM |
5209 | + struct sock *sk = sock->sk; |
5210 | + | |
76514441 | 5211 | + return aa_revalidate_sk(OP_GETSOCKNAME, sk); |
2380c486 JR |
5212 | +} |
5213 | + | |
9474138d | 5214 | +static int apparmor_socket_getpeername(struct socket *sock) |
2380c486 | 5215 | +{ |
9474138d | 5216 | + struct sock *sk = sock->sk; |
2380c486 | 5217 | + |
76514441 | 5218 | + return aa_revalidate_sk(OP_GETPEERNAME, sk); |
2380c486 JR |
5219 | +} |
5220 | + | |
9474138d | 5221 | +static int apparmor_socket_getsockopt(struct socket *sock, int level, |
ceaf2cfb | 5222 | + int optname) |
2380c486 | 5223 | +{ |
9474138d | 5224 | + struct sock *sk = sock->sk; |
2380c486 | 5225 | + |
76514441 | 5226 | + return aa_revalidate_sk(OP_GETSOCKOPT, sk); |
2380c486 JR |
5227 | +} |
5228 | + | |
9474138d | 5229 | +static int apparmor_socket_setsockopt(struct socket *sock, int level, |
ceaf2cfb | 5230 | + int optname) |
9474138d AM |
5231 | +{ |
5232 | + struct sock *sk = sock->sk; | |
2380c486 | 5233 | + |
76514441 | 5234 | + return aa_revalidate_sk(OP_SETSOCKOPT, sk); |
9474138d | 5235 | +} |
2380c486 | 5236 | + |
9474138d | 5237 | +static int apparmor_socket_shutdown(struct socket *sock, int how) |
2380c486 | 5238 | +{ |
9474138d | 5239 | + struct sock *sk = sock->sk; |
2380c486 | 5240 | + |
76514441 | 5241 | + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); |
9474138d | 5242 | +} |
2380c486 | 5243 | + |
9474138d AM |
5244 | +static struct security_operations apparmor_ops = { |
5245 | + .name = "apparmor", | |
2380c486 | 5246 | + |
ceaf2cfb | 5247 | + .ptrace_access_check = apparmor_ptrace_access_check, |
9474138d AM |
5248 | + .ptrace_traceme = apparmor_ptrace_traceme, |
5249 | + .capget = apparmor_capget, | |
9474138d | 5250 | + .capable = apparmor_capable, |
9474138d AM |
5251 | + |
5252 | + .path_link = apparmor_path_link, | |
5253 | + .path_unlink = apparmor_path_unlink, | |
5254 | + .path_symlink = apparmor_path_symlink, | |
5255 | + .path_mkdir = apparmor_path_mkdir, | |
5256 | + .path_rmdir = apparmor_path_rmdir, | |
5257 | + .path_mknod = apparmor_path_mknod, | |
5258 | + .path_rename = apparmor_path_rename, | |
ceaf2cfb AM |
5259 | + .path_chmod = apparmor_path_chmod, |
5260 | + .path_chown = apparmor_path_chown, | |
9474138d AM |
5261 | + .path_truncate = apparmor_path_truncate, |
5262 | + .dentry_open = apparmor_dentry_open, | |
76514441 | 5263 | + .inode_getattr = apparmor_inode_getattr, |
2380c486 | 5264 | + |
9474138d AM |
5265 | + .file_permission = apparmor_file_permission, |
5266 | + .file_alloc_security = apparmor_file_alloc_security, | |
5267 | + .file_free_security = apparmor_file_free_security, | |
5268 | + .file_mmap = apparmor_file_mmap, | |
5269 | + .file_mprotect = apparmor_file_mprotect, | |
5270 | + .file_lock = apparmor_file_lock, | |
2380c486 | 5271 | + |
9474138d AM |
5272 | + .getprocattr = apparmor_getprocattr, |
5273 | + .setprocattr = apparmor_setprocattr, | |
2380c486 | 5274 | + |
9474138d | 5275 | + .socket_create = apparmor_socket_create, |
9474138d AM |
5276 | + .socket_bind = apparmor_socket_bind, |
5277 | + .socket_connect = apparmor_socket_connect, | |
5278 | + .socket_listen = apparmor_socket_listen, | |
5279 | + .socket_accept = apparmor_socket_accept, | |
5280 | + .socket_sendmsg = apparmor_socket_sendmsg, | |
5281 | + .socket_recvmsg = apparmor_socket_recvmsg, | |
5282 | + .socket_getsockname = apparmor_socket_getsockname, | |
5283 | + .socket_getpeername = apparmor_socket_getpeername, | |
5284 | + .socket_getsockopt = apparmor_socket_getsockopt, | |
5285 | + .socket_setsockopt = apparmor_socket_setsockopt, | |
5286 | + .socket_shutdown = apparmor_socket_shutdown, | |
2380c486 | 5287 | + |
ceaf2cfb | 5288 | + .cred_alloc_blank = apparmor_cred_alloc_blank, |
9474138d AM |
5289 | + .cred_free = apparmor_cred_free, |
5290 | + .cred_prepare = apparmor_cred_prepare, | |
ceaf2cfb | 5291 | + .cred_transfer = apparmor_cred_transfer, |
2380c486 | 5292 | + |
9474138d | 5293 | + .bprm_set_creds = apparmor_bprm_set_creds, |
ceaf2cfb | 5294 | + .bprm_committing_creds = apparmor_bprm_committing_creds, |
9474138d AM |
5295 | + .bprm_committed_creds = apparmor_bprm_committed_creds, |
5296 | + .bprm_secureexec = apparmor_bprm_secureexec, | |
2380c486 | 5297 | + |
9474138d AM |
5298 | + .task_setrlimit = apparmor_task_setrlimit, |
5299 | +}; | |
2380c486 | 5300 | + |
9474138d AM |
5301 | +/* |
5302 | + * AppArmor sysfs module parameters | |
5303 | + */ | |
2380c486 | 5304 | + |
9474138d AM |
5305 | +static int param_set_aabool(const char *val, struct kernel_param *kp); |
5306 | +static int param_get_aabool(char *buffer, struct kernel_param *kp); | |
5307 | +#define param_check_aabool(name, p) __param_check(name, p, int) | |
2380c486 | 5308 | + |
9474138d AM |
5309 | +static int param_set_aauint(const char *val, struct kernel_param *kp); |
5310 | +static int param_get_aauint(char *buffer, struct kernel_param *kp); | |
5311 | +#define param_check_aauint(name, p) __param_check(name, p, int) | |
2380c486 | 5312 | + |
9474138d AM |
5313 | +static int param_set_aalockpolicy(const char *val, struct kernel_param *kp); |
5314 | +static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp); | |
5315 | +#define param_check_aalockpolicy(name, p) __param_check(name, p, int) | |
2380c486 | 5316 | + |
9474138d AM |
5317 | +static int param_set_audit(const char *val, struct kernel_param *kp); |
5318 | +static int param_get_audit(char *buffer, struct kernel_param *kp); | |
5319 | +#define param_check_audit(name, p) __param_check(name, p, int) | |
2380c486 | 5320 | + |
9474138d AM |
5321 | +static int param_set_mode(const char *val, struct kernel_param *kp); |
5322 | +static int param_get_mode(char *buffer, struct kernel_param *kp); | |
5323 | +#define param_check_mode(name, p) __param_check(name, p, int) | |
2380c486 | 5324 | + |
9474138d AM |
5325 | +/* Flag values, also controllable via /sys/module/apparmor/parameters |
5326 | + * We define special types as we want to do additional mediation. | |
5327 | + */ | |
2380c486 | 5328 | + |
9474138d | 5329 | +/* AppArmor global enforcement switch - complain, enforce, kill */ |
ceaf2cfb | 5330 | +enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; |
9474138d | 5331 | +module_param_call(mode, param_set_mode, param_get_mode, |
ceaf2cfb | 5332 | + &aa_g_profile_mode, S_IRUSR | S_IWUSR); |
2380c486 | 5333 | + |
9474138d | 5334 | +/* Debug mode */ |
ceaf2cfb AM |
5335 | +int aa_g_debug; |
5336 | +module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); | |
2380c486 | 5337 | + |
9474138d | 5338 | +/* Audit mode */ |
ceaf2cfb | 5339 | +enum audit_mode aa_g_audit; |
9474138d | 5340 | +module_param_call(audit, param_set_audit, param_get_audit, |
ceaf2cfb | 5341 | + &aa_g_audit, S_IRUSR | S_IWUSR); |
2380c486 | 5342 | + |
9474138d AM |
5343 | +/* Determines if audit header is included in audited messages. This |
5344 | + * provides more context if the audit daemon is not running | |
5345 | + */ | |
76514441 | 5346 | +int aa_g_audit_header = 1; |
ceaf2cfb | 5347 | +module_param_named(audit_header, aa_g_audit_header, aabool, |
9474138d | 5348 | + S_IRUSR | S_IWUSR); |
2380c486 | 5349 | + |
9474138d AM |
5350 | +/* lock out loading/removal of policy |
5351 | + * TODO: add in at boot loading of policy, which is the only way to | |
5352 | + * load policy, if lock_policy is set | |
2380c486 | 5353 | + */ |
ceaf2cfb AM |
5354 | +int aa_g_lock_policy; |
5355 | +module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy, | |
9474138d | 5356 | + S_IRUSR | S_IWUSR); |
2380c486 | 5357 | + |
9474138d | 5358 | +/* Syscall logging mode */ |
ceaf2cfb AM |
5359 | +int aa_g_logsyscall; |
5360 | +module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); | |
2380c486 | 5361 | + |
9474138d | 5362 | +/* Maximum pathname length before accesses will start getting rejected */ |
ceaf2cfb AM |
5363 | +unsigned int aa_g_path_max = 2 * PATH_MAX; |
5364 | +module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); | |
5365 | + | |
5366 | +/* Determines how paranoid loading of policy is and how much verification | |
5367 | + * on the loaded policy is done. | |
5368 | + */ | |
5369 | +int aa_g_paranoid_load = 1; | |
5370 | +module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | |
5371 | + S_IRUSR | S_IWUSR); | |
2380c486 | 5372 | + |
9474138d | 5373 | +/* Boot time disable flag */ |
9474138d | 5374 | +static unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; |
ceaf2cfb | 5375 | +module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); |
2380c486 | 5376 | + |
9474138d | 5377 | +static int __init apparmor_enabled_setup(char *str) |
2380c486 | 5378 | +{ |
ceaf2cfb AM |
5379 | + unsigned long enabled; |
5380 | + int error = strict_strtoul(str, 0, &enabled); | |
5381 | + if (!error) | |
5382 | + apparmor_enabled = enabled ? 1 : 0; | |
9474138d | 5383 | + return 1; |
2380c486 | 5384 | +} |
ceaf2cfb | 5385 | + |
9474138d | 5386 | +__setup("apparmor=", apparmor_enabled_setup); |
2380c486 | 5387 | + |
ceaf2cfb | 5388 | +/* set global flag turning off the ability to load policy */ |
9474138d | 5389 | +static int param_set_aalockpolicy(const char *val, struct kernel_param *kp) |
2380c486 | 5390 | +{ |
ceaf2cfb | 5391 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d | 5392 | + return -EPERM; |
ceaf2cfb | 5393 | + if (aa_g_lock_policy) |
9474138d AM |
5394 | + return -EACCES; |
5395 | + return param_set_bool(val, kp); | |
2380c486 JR |
5396 | +} |
5397 | + | |
9474138d | 5398 | +static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp) |
2380c486 | 5399 | +{ |
ceaf2cfb | 5400 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d AM |
5401 | + return -EPERM; |
5402 | + return param_get_bool(buffer, kp); | |
2380c486 JR |
5403 | +} |
5404 | + | |
9474138d | 5405 | +static int param_set_aabool(const char *val, struct kernel_param *kp) |
2380c486 | 5406 | +{ |
ceaf2cfb | 5407 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d AM |
5408 | + return -EPERM; |
5409 | + return param_set_bool(val, kp); | |
2380c486 JR |
5410 | +} |
5411 | + | |
9474138d | 5412 | +static int param_get_aabool(char *buffer, struct kernel_param *kp) |
2380c486 | 5413 | +{ |
ceaf2cfb | 5414 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d AM |
5415 | + return -EPERM; |
5416 | + return param_get_bool(buffer, kp); | |
2380c486 JR |
5417 | +} |
5418 | + | |
9474138d | 5419 | +static int param_set_aauint(const char *val, struct kernel_param *kp) |
2380c486 | 5420 | +{ |
ceaf2cfb | 5421 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d AM |
5422 | + return -EPERM; |
5423 | + return param_set_uint(val, kp); | |
2380c486 JR |
5424 | +} |
5425 | + | |
9474138d | 5426 | +static int param_get_aauint(char *buffer, struct kernel_param *kp) |
2380c486 | 5427 | +{ |
ceaf2cfb | 5428 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d AM |
5429 | + return -EPERM; |
5430 | + return param_get_uint(buffer, kp); | |
2380c486 JR |
5431 | +} |
5432 | + | |
9474138d AM |
5433 | +static int param_get_audit(char *buffer, struct kernel_param *kp) |
5434 | +{ | |
ceaf2cfb | 5435 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d | 5436 | + return -EPERM; |
2380c486 | 5437 | + |
9474138d AM |
5438 | + if (!apparmor_enabled) |
5439 | + return -EINVAL; | |
2380c486 | 5440 | + |
ceaf2cfb | 5441 | + return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); |
2380c486 JR |
5442 | +} |
5443 | + | |
9474138d | 5444 | +static int param_set_audit(const char *val, struct kernel_param *kp) |
2380c486 | 5445 | +{ |
9474138d | 5446 | + int i; |
ceaf2cfb | 5447 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d | 5448 | + return -EPERM; |
2380c486 | 5449 | + |
9474138d AM |
5450 | + if (!apparmor_enabled) |
5451 | + return -EINVAL; | |
2380c486 | 5452 | + |
9474138d AM |
5453 | + if (!val) |
5454 | + return -EINVAL; | |
2380c486 | 5455 | + |
9474138d AM |
5456 | + for (i = 0; i < AUDIT_MAX_INDEX; i++) { |
5457 | + if (strcmp(val, audit_mode_names[i]) == 0) { | |
ceaf2cfb | 5458 | + aa_g_audit = i; |
9474138d | 5459 | + return 0; |
2380c486 | 5460 | + } |
2380c486 | 5461 | + } |
2380c486 | 5462 | + |
9474138d | 5463 | + return -EINVAL; |
2380c486 JR |
5464 | +} |
5465 | + | |
9474138d | 5466 | +static int param_get_mode(char *buffer, struct kernel_param *kp) |
2380c486 | 5467 | +{ |
ceaf2cfb | 5468 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d | 5469 | + return -EPERM; |
2380c486 | 5470 | + |
9474138d AM |
5471 | + if (!apparmor_enabled) |
5472 | + return -EINVAL; | |
2380c486 | 5473 | + |
ceaf2cfb | 5474 | + return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); |
2380c486 JR |
5475 | +} |
5476 | + | |
9474138d | 5477 | +static int param_set_mode(const char *val, struct kernel_param *kp) |
2380c486 | 5478 | +{ |
9474138d | 5479 | + int i; |
ceaf2cfb | 5480 | + if (!capable(CAP_MAC_ADMIN)) |
9474138d | 5481 | + return -EPERM; |
2380c486 | 5482 | + |
9474138d AM |
5483 | + if (!apparmor_enabled) |
5484 | + return -EINVAL; | |
2380c486 | 5485 | + |
9474138d AM |
5486 | + if (!val) |
5487 | + return -EINVAL; | |
2380c486 | 5488 | + |
9474138d AM |
5489 | + for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { |
5490 | + if (strcmp(val, profile_mode_names[i]) == 0) { | |
ceaf2cfb | 5491 | + aa_g_profile_mode = i; |
9474138d AM |
5492 | + return 0; |
5493 | + } | |
5494 | + } | |
2380c486 | 5495 | + |
9474138d | 5496 | + return -EINVAL; |
2380c486 JR |
5497 | +} |
5498 | + | |
9474138d AM |
5499 | +/* |
5500 | + * AppArmor init functions | |
2380c486 | 5501 | + */ |
2380c486 | 5502 | + |
ceaf2cfb AM |
5503 | +/** |
5504 | + * set_init_cxt - set a task context and profile on the first task. | |
5505 | + * | |
5506 | + * TODO: allow setting an alternate profile than unconfined | |
5507 | + */ | |
5508 | +static int __init set_init_cxt(void) | |
9474138d | 5509 | +{ |
ceaf2cfb AM |
5510 | + struct cred *cred = (struct cred *)current->real_cred; |
5511 | + struct aa_task_cxt *cxt; | |
2380c486 | 5512 | + |
9474138d AM |
5513 | + cxt = aa_alloc_task_context(GFP_KERNEL); |
5514 | + if (!cxt) | |
5515 | + return -ENOMEM; | |
2380c486 | 5516 | + |
ceaf2cfb | 5517 | + cxt->profile = aa_get_profile(root_ns->unconfined); |
9474138d | 5518 | + cred->security = cxt; |
2380c486 | 5519 | + |
9474138d | 5520 | + return 0; |
2380c486 JR |
5521 | +} |
5522 | + | |
9474138d | 5523 | +static int __init apparmor_init(void) |
2380c486 | 5524 | +{ |
9474138d | 5525 | + int error; |
2380c486 | 5526 | + |
9474138d | 5527 | + if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { |
76514441 | 5528 | + aa_info_message("AppArmor disabled by boot time parameter"); |
9474138d AM |
5529 | + apparmor_enabled = 0; |
5530 | + return 0; | |
5531 | + } | |
2380c486 | 5532 | + |
ceaf2cfb | 5533 | + error = aa_alloc_root_ns(); |
9474138d AM |
5534 | + if (error) { |
5535 | + AA_ERROR("Unable to allocate default profile namespace\n"); | |
5536 | + goto alloc_out; | |
5537 | + } | |
2380c486 | 5538 | + |
9474138d AM |
5539 | + error = set_init_cxt(); |
5540 | + if (error) { | |
5541 | + AA_ERROR("Failed to set context on init task\n"); | |
ceaf2cfb | 5542 | + goto register_security_out; |
9474138d | 5543 | + } |
2380c486 | 5544 | + |
9474138d AM |
5545 | + error = register_security(&apparmor_ops); |
5546 | + if (error) { | |
5547 | + AA_ERROR("Unable to register AppArmor\n"); | |
5548 | + goto register_security_out; | |
5549 | + } | |
2380c486 | 5550 | + |
9474138d AM |
5551 | + /* Report that AppArmor successfully initialized */ |
5552 | + apparmor_initialized = 1; | |
ceaf2cfb AM |
5553 | + if (aa_g_profile_mode == APPARMOR_COMPLAIN) |
5554 | + aa_info_message("AppArmor initialized: complain mode enabled"); | |
5555 | + else if (aa_g_profile_mode == APPARMOR_KILL) | |
5556 | + aa_info_message("AppArmor initialized: kill mode enabled"); | |
9474138d | 5557 | + else |
ceaf2cfb | 5558 | + aa_info_message("AppArmor initialized"); |
2380c486 | 5559 | + |
9474138d | 5560 | + return error; |
2380c486 | 5561 | + |
9474138d | 5562 | +register_security_out: |
ceaf2cfb | 5563 | + aa_free_root_ns(); |
2380c486 | 5564 | + |
9474138d | 5565 | +alloc_out: |
ceaf2cfb | 5566 | + aa_destroy_aafs(); |
2380c486 | 5567 | + |
9474138d AM |
5568 | + apparmor_enabled = 0; |
5569 | + return error; | |
2380c486 | 5570 | + |
2380c486 JR |
5571 | +} |
5572 | + | |
9474138d | 5573 | +security_initcall(apparmor_init); |
ceaf2cfb AM |
5574 | diff --git a/security/apparmor/match.c b/security/apparmor/match.c |
5575 | new file mode 100644 | |
76514441 | 5576 | index 0000000..0248bb3 |
ceaf2cfb AM |
5577 | --- /dev/null |
5578 | +++ b/security/apparmor/match.c | |
76514441 | 5579 | @@ -0,0 +1,370 @@ |
9474138d AM |
5580 | +/* |
5581 | + * AppArmor security module | |
2380c486 | 5582 | + * |
9474138d AM |
5583 | + * This file contains AppArmor dfa based regular expression matching engine |
5584 | + * | |
5585 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 5586 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
5587 | + * |
5588 | + * This program is free software; you can redistribute it and/or | |
5589 | + * modify it under the terms of the GNU General Public License as | |
5590 | + * published by the Free Software Foundation, version 2 of the | |
5591 | + * License. | |
2380c486 | 5592 | + */ |
2380c486 | 5593 | + |
9474138d | 5594 | +#include <linux/errno.h> |
ceaf2cfb | 5595 | +#include <linux/kernel.h> |
0eaba94d | 5596 | +#include <linux/mm.h> |
ceaf2cfb | 5597 | +#include <linux/slab.h> |
0eaba94d | 5598 | +#include <linux/vmalloc.h> |
ceaf2cfb AM |
5599 | +#include <linux/err.h> |
5600 | +#include <linux/kref.h> | |
2380c486 | 5601 | + |
76514441 | 5602 | +#include "include/apparmor.h" |
9474138d | 5603 | +#include "include/match.h" |
2380c486 | 5604 | + |
ceaf2cfb | 5605 | +/** |
ceaf2cfb | 5606 | + * unpack_table - unpack a dfa table (one of accept, default, base, next check) |
76514441 | 5607 | + * @blob: data to unpack (NOT NULL) |
ceaf2cfb AM |
5608 | + * @bsize: size of blob |
5609 | + * | |
5610 | + * Returns: pointer to table else NULL on failure | |
5611 | + * | |
76514441 | 5612 | + * NOTE: must be freed by kvfree (not kmalloc) |
ceaf2cfb AM |
5613 | + */ |
5614 | +static struct table_header *unpack_table(char *blob, size_t bsize) | |
9474138d AM |
5615 | +{ |
5616 | + struct table_header *table = NULL; | |
5617 | + struct table_header th; | |
5618 | + size_t tsize; | |
2380c486 | 5619 | + |
9474138d AM |
5620 | + if (bsize < sizeof(struct table_header)) |
5621 | + goto out; | |
2380c486 | 5622 | + |
ceaf2cfb AM |
5623 | + /* loaded td_id's start at 1, subtract 1 now to avoid doing |
5624 | + * it every time we use td_id as an index | |
5625 | + */ | |
5626 | + th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; | |
9474138d AM |
5627 | + th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); |
5628 | + th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); | |
5629 | + blob += sizeof(struct table_header); | |
2380c486 | 5630 | + |
9474138d | 5631 | + if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || |
ceaf2cfb | 5632 | + th.td_flags == YYTD_DATA8)) |
9474138d | 5633 | + goto out; |
2380c486 | 5634 | + |
9474138d AM |
5635 | + tsize = table_size(th.td_lolen, th.td_flags); |
5636 | + if (bsize < tsize) | |
5637 | + goto out; | |
2380c486 | 5638 | + |
76514441 AM |
5639 | + /* Pad table allocation for next/check by 256 entries to remain |
5640 | + * backwards compatible with old (buggy) tools and remain safe without | |
5641 | + * run time checks | |
5642 | + */ | |
5643 | + if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK) | |
5644 | + tsize += 256 * th.td_flags; | |
5645 | + | |
5646 | + table = kvmalloc(tsize); | |
9474138d | 5647 | + if (table) { |
76514441 AM |
5648 | + /* ensure the pad is clear, else there will be errors */ |
5649 | + memset(table, 0, tsize); | |
9474138d AM |
5650 | + *table = th; |
5651 | + if (th.td_flags == YYTD_DATA8) | |
5652 | + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | |
5653 | + u8, byte_to_byte); | |
5654 | + else if (th.td_flags == YYTD_DATA16) | |
5655 | + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | |
5656 | + u16, be16_to_cpu); | |
ceaf2cfb | 5657 | + else if (th.td_flags == YYTD_DATA32) |
9474138d AM |
5658 | + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, |
5659 | + u32, be32_to_cpu); | |
ceaf2cfb AM |
5660 | + else |
5661 | + goto fail; | |
9474138d | 5662 | + } |
2380c486 | 5663 | + |
9474138d | 5664 | +out: |
76514441 AM |
5665 | + /* if table was vmalloced make sure the page tables are synced |
5666 | + * before it is used, as it goes live to all cpus. | |
5667 | + */ | |
5668 | + if (is_vmalloc_addr(table)) | |
5669 | + vm_unmap_aliases(); | |
9474138d | 5670 | + return table; |
ceaf2cfb | 5671 | +fail: |
76514441 | 5672 | + kvfree(table); |
ceaf2cfb AM |
5673 | + return NULL; |
5674 | +} | |
5675 | + | |
5676 | +/** | |
76514441 | 5677 | + * verify_dfa - verify that transitions and states in the tables are in bounds. |
ceaf2cfb AM |
5678 | + * @dfa: dfa to test (NOT NULL) |
5679 | + * @flags: flags controlling what type of accept table are acceptable | |
5680 | + * | |
5681 | + * Assumes dfa has gone through the first pass verification done by unpacking | |
5682 | + * NOTE: this does not valid accept table values | |
5683 | + * | |
5684 | + * Returns: %0 else error code on failure to verify | |
5685 | + */ | |
5686 | +static int verify_dfa(struct aa_dfa *dfa, int flags) | |
5687 | +{ | |
5688 | + size_t i, state_count, trans_count; | |
5689 | + int error = -EPROTO; | |
5690 | + | |
5691 | + /* check that required tables exist */ | |
5692 | + if (!(dfa->tables[YYTD_ID_DEF] && | |
5693 | + dfa->tables[YYTD_ID_BASE] && | |
5694 | + dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK])) | |
5695 | + goto out; | |
5696 | + | |
5697 | + /* accept.size == default.size == base.size */ | |
5698 | + state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; | |
5699 | + if (ACCEPT1_FLAGS(flags)) { | |
5700 | + if (!dfa->tables[YYTD_ID_ACCEPT]) | |
5701 | + goto out; | |
5702 | + if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) | |
5703 | + goto out; | |
5704 | + } | |
5705 | + if (ACCEPT2_FLAGS(flags)) { | |
5706 | + if (!dfa->tables[YYTD_ID_ACCEPT2]) | |
5707 | + goto out; | |
5708 | + if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) | |
5709 | + goto out; | |
5710 | + } | |
5711 | + if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) | |
5712 | + goto out; | |
5713 | + | |
5714 | + /* next.size == chk.size */ | |
5715 | + trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; | |
5716 | + if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) | |
5717 | + goto out; | |
5718 | + | |
5719 | + /* if equivalence classes then its table size must be 256 */ | |
5720 | + if (dfa->tables[YYTD_ID_EC] && | |
5721 | + dfa->tables[YYTD_ID_EC]->td_lolen != 256) | |
5722 | + goto out; | |
5723 | + | |
5724 | + if (flags & DFA_FLAG_VERIFY_STATES) { | |
76514441 | 5725 | + int warning = 0; |
ceaf2cfb AM |
5726 | + for (i = 0; i < state_count; i++) { |
5727 | + if (DEFAULT_TABLE(dfa)[i] >= state_count) | |
5728 | + goto out; | |
5729 | + /* TODO: do check that DEF state recursion terminates */ | |
76514441 AM |
5730 | + if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { |
5731 | + if (warning) | |
5732 | + continue; | |
5733 | + printk(KERN_WARNING "AppArmor DFA next/check " | |
5734 | + "upper bounds error fixed, upgrade " | |
5735 | + "user space tools \n"); | |
5736 | + warning = 1; | |
5737 | + } else if (BASE_TABLE(dfa)[i] >= trans_count) { | |
5738 | + printk(KERN_ERR "AppArmor DFA next/check upper " | |
5739 | + "bounds error\n"); | |
ceaf2cfb | 5740 | + goto out; |
76514441 | 5741 | + } |
ceaf2cfb AM |
5742 | + } |
5743 | + | |
5744 | + for (i = 0; i < trans_count; i++) { | |
5745 | + if (NEXT_TABLE(dfa)[i] >= state_count) | |
5746 | + goto out; | |
5747 | + if (CHECK_TABLE(dfa)[i] >= state_count) | |
5748 | + goto out; | |
5749 | + } | |
5750 | + } | |
5751 | + | |
5752 | + error = 0; | |
5753 | +out: | |
5754 | + return error; | |
5755 | +} | |
5756 | + | |
5757 | +/** | |
76514441 | 5758 | + * dfa_free - free a dfa allocated by aa_dfa_unpack |
ceaf2cfb AM |
5759 | + * @dfa: the dfa to free (MAYBE NULL) |
5760 | + * | |
5761 | + * Requires: reference count to dfa == 0 | |
5762 | + */ | |
76514441 | 5763 | +static void dfa_free(struct aa_dfa *dfa) |
ceaf2cfb AM |
5764 | +{ |
5765 | + if (dfa) { | |
5766 | + int i; | |
5767 | + | |
5768 | + for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { | |
76514441 | 5769 | + kvfree(dfa->tables[i]); |
ceaf2cfb AM |
5770 | + dfa->tables[i] = NULL; |
5771 | + } | |
76514441 | 5772 | + kfree(dfa); |
ceaf2cfb | 5773 | + } |
ceaf2cfb AM |
5774 | +} |
5775 | + | |
5776 | +/** | |
5777 | + * aa_dfa_free_kref - free aa_dfa by kref (called by aa_put_dfa) | |
5778 | + * @kr: kref callback for freeing of a dfa (NOT NULL) | |
5779 | + */ | |
5780 | +void aa_dfa_free_kref(struct kref *kref) | |
5781 | +{ | |
5782 | + struct aa_dfa *dfa = container_of(kref, struct aa_dfa, count); | |
76514441 | 5783 | + dfa_free(dfa); |
9474138d | 5784 | +} |
2380c486 | 5785 | + |
ceaf2cfb AM |
5786 | +/** |
5787 | + * aa_dfa_unpack - unpack the binary tables of a serialized dfa | |
5788 | + * @blob: aligned serialized stream of data to unpack (NOT NULL) | |
5789 | + * @size: size of data to unpack | |
5790 | + * @flags: flags controlling what type of accept tables are acceptable | |
5791 | + * | |
76514441 AM |
5792 | + * Unpack a dfa that has been serialized. To find information on the dfa |
5793 | + * format look in Documentation/apparmor.txt | |
ceaf2cfb AM |
5794 | + * Assumes the dfa @blob stream has been aligned on a 8 byte boundry |
5795 | + * | |
5796 | + * Returns: an unpacked dfa ready for matching or ERR_PTR on failure | |
5797 | + */ | |
5798 | +struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) | |
9474138d | 5799 | +{ |
ceaf2cfb | 5800 | + int hsize; |
9474138d | 5801 | + int error = -ENOMEM; |
ceaf2cfb AM |
5802 | + char *data = blob; |
5803 | + struct table_header *table = NULL; | |
5804 | + struct aa_dfa *dfa = kzalloc(sizeof(struct aa_dfa), GFP_KERNEL); | |
5805 | + if (!dfa) | |
5806 | + goto fail; | |
5807 | + | |
5808 | + kref_init(&dfa->count); | |
5809 | + | |
5810 | + error = -EPROTO; | |
2380c486 | 5811 | + |
9474138d AM |
5812 | + /* get dfa table set header */ |
5813 | + if (size < sizeof(struct table_set_header)) | |
5814 | + goto fail; | |
2380c486 | 5815 | + |
ceaf2cfb | 5816 | + if (ntohl(*(u32 *) data) != YYTH_MAGIC) |
9474138d | 5817 | + goto fail; |
2380c486 | 5818 | + |
ceaf2cfb | 5819 | + hsize = ntohl(*(u32 *) (data + 4)); |
9474138d AM |
5820 | + if (size < hsize) |
5821 | + goto fail; | |
2380c486 | 5822 | + |
ceaf2cfb AM |
5823 | + dfa->flags = ntohs(*(u16 *) (data + 12)); |
5824 | + data += hsize; | |
9474138d | 5825 | + size -= hsize; |
2380c486 | 5826 | + |
9474138d | 5827 | + while (size > 0) { |
ceaf2cfb | 5828 | + table = unpack_table(data, size); |
9474138d AM |
5829 | + if (!table) |
5830 | + goto fail; | |
2380c486 | 5831 | + |
9474138d AM |
5832 | + switch (table->td_id) { |
5833 | + case YYTD_ID_ACCEPT: | |
ceaf2cfb AM |
5834 | + if (!(table->td_flags & ACCEPT1_FLAGS(flags))) |
5835 | + goto fail; | |
5836 | + break; | |
9474138d | 5837 | + case YYTD_ID_ACCEPT2: |
ceaf2cfb AM |
5838 | + if (!(table->td_flags & ACCEPT2_FLAGS(flags))) |
5839 | + goto fail; | |
5840 | + break; | |
9474138d | 5841 | + case YYTD_ID_BASE: |
9474138d AM |
5842 | + if (table->td_flags != YYTD_DATA32) |
5843 | + goto fail; | |
5844 | + break; | |
5845 | + case YYTD_ID_DEF: | |
5846 | + case YYTD_ID_NXT: | |
5847 | + case YYTD_ID_CHK: | |
9474138d AM |
5848 | + if (table->td_flags != YYTD_DATA16) |
5849 | + goto fail; | |
5850 | + break; | |
5851 | + case YYTD_ID_EC: | |
9474138d AM |
5852 | + if (table->td_flags != YYTD_DATA8) |
5853 | + goto fail; | |
5854 | + break; | |
5855 | + default: | |
9474138d AM |
5856 | + goto fail; |
5857 | + } | |
ceaf2cfb AM |
5858 | + /* check for duplicate table entry */ |
5859 | + if (dfa->tables[table->td_id]) | |
5860 | + goto fail; | |
5861 | + dfa->tables[table->td_id] = table; | |
5862 | + data += table_size(table->td_lolen, table->td_flags); | |
9474138d | 5863 | + size -= table_size(table->td_lolen, table->td_flags); |
ceaf2cfb | 5864 | + table = NULL; |
2380c486 JR |
5865 | + } |
5866 | + | |
ceaf2cfb AM |
5867 | + error = verify_dfa(dfa, flags); |
5868 | + if (error) | |
5869 | + goto fail; | |
2380c486 | 5870 | + |
ceaf2cfb | 5871 | + return dfa; |
2380c486 | 5872 | + |
ceaf2cfb | 5873 | +fail: |
76514441 AM |
5874 | + kvfree(table); |
5875 | + dfa_free(dfa); | |
ceaf2cfb | 5876 | + return ERR_PTR(error); |
2380c486 JR |
5877 | +} |
5878 | + | |
2380c486 | 5879 | +/** |
9474138d | 5880 | + * aa_dfa_match_len - traverse @dfa to find state @str stops at |
ceaf2cfb | 5881 | + * @dfa: the dfa to match @str against (NOT NULL) |
9474138d | 5882 | + * @start: the state of the dfa to start matching in |
ceaf2cfb | 5883 | + * @str: the string of bytes to match against the dfa (NOT NULL) |
9474138d | 5884 | + * @len: length of the string of bytes to match |
2380c486 | 5885 | + * |
9474138d AM |
5886 | + * aa_dfa_match_len will match @str against the dfa and return the state it |
5887 | + * finished matching in. The final state can be used to look up the accepting | |
5888 | + * label, or as the start state of a continuing match. | |
5889 | + * | |
5890 | + * This function will happily match again the 0 byte and only finishes | |
5891 | + * when @len input is consumed. | |
ceaf2cfb AM |
5892 | + * |
5893 | + * Returns: final state reached after input is consumed | |
2380c486 | 5894 | + */ |
9474138d AM |
5895 | +unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, |
5896 | + const char *str, int len) | |
2380c486 | 5897 | +{ |
9474138d AM |
5898 | + u16 *def = DEFAULT_TABLE(dfa); |
5899 | + u32 *base = BASE_TABLE(dfa); | |
5900 | + u16 *next = NEXT_TABLE(dfa); | |
5901 | + u16 *check = CHECK_TABLE(dfa); | |
5902 | + unsigned int state = start, pos; | |
2380c486 | 5903 | + |
9474138d | 5904 | + if (state == 0) |
2380c486 | 5905 | + return 0; |
2380c486 | 5906 | + |
9474138d | 5907 | + /* current state is <state>, matching character *str */ |
ceaf2cfb AM |
5908 | + if (dfa->tables[YYTD_ID_EC]) { |
5909 | + /* Equivalence class table defined */ | |
9474138d | 5910 | + u8 *equiv = EQUIV_TABLE(dfa); |
ceaf2cfb | 5911 | + /* default is direct to next state */ |
9474138d | 5912 | + for (; len; len--) { |
ceaf2cfb | 5913 | + pos = base[state] + equiv[(u8) *str++]; |
9474138d AM |
5914 | + if (check[pos] == state) |
5915 | + state = next[pos]; | |
5916 | + else | |
5917 | + state = def[state]; | |
2380c486 JR |
5918 | + } |
5919 | + } else { | |
ceaf2cfb | 5920 | + /* default is direct to next state */ |
9474138d | 5921 | + for (; len; len--) { |
ceaf2cfb | 5922 | + pos = base[state] + (u8) *str++; |
9474138d AM |
5923 | + if (check[pos] == state) |
5924 | + state = next[pos]; | |
5925 | + else | |
5926 | + state = def[state]; | |
5927 | + } | |
2380c486 | 5928 | + } |
ceaf2cfb | 5929 | + |
9474138d | 5930 | + return state; |
2380c486 JR |
5931 | +} |
5932 | + | |
9474138d AM |
5933 | +/** |
5934 | + * aa_dfa_next_state - traverse @dfa to find state @str stops at | |
ceaf2cfb | 5935 | + * @dfa: the dfa to match @str against (NOT NULL) |
9474138d | 5936 | + * @start: the state of the dfa to start matching in |
ceaf2cfb | 5937 | + * @str: the null terminated string of bytes to match against the dfa (NOT NULL) |
9474138d AM |
5938 | + * |
5939 | + * aa_dfa_next_state will match @str against the dfa and return the state it | |
5940 | + * finished matching in. The final state can be used to look up the accepting | |
5941 | + * label, or as the start state of a continuing match. | |
ceaf2cfb AM |
5942 | + * |
5943 | + * Returns: final state reached after input is consumed | |
9474138d AM |
5944 | + */ |
5945 | +unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | |
5946 | + const char *str) | |
5947 | +{ | |
5948 | + return aa_dfa_match_len(dfa, start, str, strlen(str)); | |
2380c486 | 5949 | +} |
ceaf2cfb AM |
5950 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c |
5951 | new file mode 100644 | |
76514441 | 5952 | index 0000000..7c36e82 |
ceaf2cfb AM |
5953 | --- /dev/null |
5954 | +++ b/security/apparmor/net.c | |
76514441 | 5955 | @@ -0,0 +1,169 @@ |
9474138d AM |
5956 | +/* |
5957 | + * AppArmor security module | |
5958 | + * | |
5959 | + * This file contains AppArmor network mediation | |
5960 | + * | |
5961 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 5962 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
5963 | + * |
5964 | + * This program is free software; you can redistribute it and/or | |
5965 | + * modify it under the terms of the GNU General Public License as | |
5966 | + * published by the Free Software Foundation, version 2 of the | |
5967 | + * License. | |
5968 | + */ | |
2380c486 | 5969 | + |
9474138d AM |
5970 | +#include "include/apparmor.h" |
5971 | +#include "include/audit.h" | |
5972 | +#include "include/context.h" | |
5973 | +#include "include/net.h" | |
5974 | +#include "include/policy.h" | |
2380c486 | 5975 | + |
9474138d | 5976 | +#include "af_names.h" |
2380c486 | 5977 | + |
9474138d AM |
5978 | +static const char *sock_type_names[] = { |
5979 | + "unknown(0)", | |
5980 | + "stream", | |
5981 | + "dgram", | |
5982 | + "raw", | |
5983 | + "rdm", | |
5984 | + "seqpacket", | |
5985 | + "dccp", | |
5986 | + "unknown(7)", | |
5987 | + "unknown(8)", | |
5988 | + "unknown(9)", | |
5989 | + "packet", | |
5990 | +}; | |
2380c486 | 5991 | + |
ceaf2cfb | 5992 | +/* audit callback for net specific fields */ |
76514441 | 5993 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 5994 | +{ |
76514441 | 5995 | + struct common_audit_data *sa = va; |
2380c486 | 5996 | + |
76514441 AM |
5997 | + audit_log_format(ab, " family="); |
5998 | + if (address_family_names[sa->u.net.family]) { | |
5999 | + audit_log_string(ab, address_family_names[sa->u.net.family]); | |
6000 | + } else { | |
6001 | + audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family); | |
2380c486 | 6002 | + } |
ceaf2cfb | 6003 | + |
76514441 AM |
6004 | + audit_log_format(ab, " sock_type="); |
6005 | + if (sock_type_names[sa->aad.net.type]) { | |
6006 | + audit_log_string(ab, sock_type_names[sa->aad.net.type]); | |
6007 | + } else { | |
6008 | + audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type); | |
6009 | + } | |
6010 | + | |
6011 | + audit_log_format(ab, " protocol=%d", sa->aad.net.protocol); | |
2380c486 JR |
6012 | +} |
6013 | + | |
ceaf2cfb | 6014 | +/** |
76514441 | 6015 | + * audit_net - audit network access |
ceaf2cfb | 6016 | + * @profile: profile being enforced (NOT NULL) |
76514441 AM |
6017 | + * @op: operation being checked |
6018 | + * @family: network family | |
6019 | + * @type: network type | |
6020 | + * @protocol: network protocol | |
6021 | + * @sk: socket auditing is being applied to | |
6022 | + * @error: error code for failure else 0 | |
ceaf2cfb AM |
6023 | + * |
6024 | + * Returns: %0 or sa->error else other errorcode on failure | |
6025 | + */ | |
76514441 AM |
6026 | +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, |
6027 | + int protocol, struct sock *sk, int error) | |
2380c486 | 6028 | +{ |
76514441 AM |
6029 | + int audit_type = AUDIT_APPARMOR_AUTO; |
6030 | + struct common_audit_data sa; | |
6031 | + if (sk) { | |
6032 | + COMMON_AUDIT_DATA_INIT(&sa, NET); | |
6033 | + } else { | |
6034 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
6035 | + } | |
6036 | + /* todo fill in socket addr info */ | |
6037 | + | |
6038 | + sa.aad.op = op, | |
6039 | + sa.u.net.family = family; | |
6040 | + sa.u.net.sk = sk; | |
6041 | + sa.aad.net.type = type; | |
6042 | + sa.aad.net.protocol = protocol; | |
2380c486 | 6043 | + |
76514441 AM |
6044 | + if (likely(!sa.aad.error)) { |
6045 | + u16 audit_mask = profile->net.audit[sa.u.net.family]; | |
ceaf2cfb | 6046 | + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && |
76514441 | 6047 | + !(1 << sa.aad.net.type & audit_mask))) |
9474138d | 6048 | + return 0; |
76514441 | 6049 | + audit_type = AUDIT_APPARMOR_AUDIT; |
9474138d | 6050 | + } else { |
76514441 | 6051 | + u16 quiet_mask = profile->net.quiet[sa.u.net.family]; |
9474138d | 6052 | + u16 kill_mask = 0; |
76514441 | 6053 | + u16 denied = (1 << sa.aad.net.type) & ~quiet_mask; |
9474138d AM |
6054 | + |
6055 | + if (denied & kill_mask) | |
76514441 | 6056 | + audit_type = AUDIT_APPARMOR_KILL; |
9474138d AM |
6057 | + |
6058 | + if ((denied & quiet_mask) && | |
ceaf2cfb AM |
6059 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && |
6060 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
76514441 | 6061 | + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; |
2380c486 | 6062 | + } |
9474138d | 6063 | + |
76514441 | 6064 | + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); |
2380c486 JR |
6065 | +} |
6066 | + | |
ceaf2cfb AM |
6067 | +/** |
6068 | + * aa_net_perm - very course network access check | |
76514441 | 6069 | + * @op: operation being checked |
ceaf2cfb | 6070 | + * @profile: profile being enforced (NOT NULL) |
ceaf2cfb AM |
6071 | + * @family: network family |
6072 | + * @type: network type | |
6073 | + * @protocol: network protocol | |
6074 | + * | |
6075 | + * Returns: %0 else error if permission denied | |
6076 | + */ | |
76514441 AM |
6077 | +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, |
6078 | + int protocol, struct sock *sk) | |
2380c486 | 6079 | +{ |
9474138d | 6080 | + u16 family_mask; |
76514441 | 6081 | + int error; |
2380c486 | 6082 | + |
9474138d AM |
6083 | + if ((family < 0) || (family >= AF_MAX)) |
6084 | + return -EINVAL; | |
2380c486 | 6085 | + |
9474138d AM |
6086 | + if ((type < 0) || (type >= SOCK_MAX)) |
6087 | + return -EINVAL; | |
2380c486 | 6088 | + |
9474138d AM |
6089 | + /* unix domain and netlink sockets are handled by ipc */ |
6090 | + if (family == AF_UNIX || family == AF_NETLINK) | |
6091 | + return 0; | |
2380c486 | 6092 | + |
76514441 | 6093 | + family_mask = profile->net.allow[family]; |
2380c486 | 6094 | + |
76514441 | 6095 | + error = (family_mask & (1 << type)) ? 0 : -EACCES; |
2380c486 | 6096 | + |
76514441 | 6097 | + return audit_net(profile, op, family, type, protocol, sk, error); |
9474138d | 6098 | +} |
2380c486 | 6099 | + |
ceaf2cfb AM |
6100 | +/** |
6101 | + * aa_revalidate_sk - Revalidate access to a sock | |
76514441 | 6102 | + * @op: operation being checked |
ceaf2cfb | 6103 | + * @sk: sock being revalidated (NOT NULL) |
ceaf2cfb AM |
6104 | + * |
6105 | + * Returns: %0 else error if permission denied | |
6106 | + */ | |
76514441 | 6107 | +int aa_revalidate_sk(int op, struct sock *sk) |
9474138d AM |
6108 | +{ |
6109 | + struct aa_profile *profile; | |
9474138d | 6110 | + int error = 0; |
2380c486 | 6111 | + |
ceaf2cfb AM |
6112 | + /* aa_revalidate_sk should not be called from interrupt context |
6113 | + * don't mediate these calls as they are not task related | |
6114 | + */ | |
6115 | + if (in_interrupt()) | |
9474138d | 6116 | + return 0; |
2380c486 | 6117 | + |
ceaf2cfb AM |
6118 | + profile = __aa_current_profile(); |
6119 | + if (!unconfined(profile)) | |
76514441 AM |
6120 | + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, |
6121 | + sk->sk_protocol, sk); | |
9474138d AM |
6122 | + |
6123 | + return error; | |
6124 | +} | |
ceaf2cfb AM |
6125 | diff --git a/security/apparmor/path.c b/security/apparmor/path.c |
6126 | new file mode 100644 | |
76514441 | 6127 | index 0000000..96bab94 |
ceaf2cfb AM |
6128 | --- /dev/null |
6129 | +++ b/security/apparmor/path.c | |
76514441 | 6130 | @@ -0,0 +1,235 @@ |
9474138d AM |
6131 | +/* |
6132 | + * AppArmor security module | |
6133 | + * | |
6134 | + * This file contains AppArmor function for pathnames | |
6135 | + * | |
6136 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 6137 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
6138 | + * |
6139 | + * This program is free software; you can redistribute it and/or | |
6140 | + * modify it under the terms of the GNU General Public License as | |
6141 | + * published by the Free Software Foundation, version 2 of the | |
6142 | + * License. | |
6143 | + */ | |
2380c486 | 6144 | + |
76514441 | 6145 | +#include <linux/magic.h> |
9474138d AM |
6146 | +#include <linux/mnt_namespace.h> |
6147 | +#include <linux/mount.h> | |
6148 | +#include <linux/namei.h> | |
ceaf2cfb | 6149 | +#include <linux/nsproxy.h> |
9474138d AM |
6150 | +#include <linux/path.h> |
6151 | +#include <linux/sched.h> | |
6152 | +#include <linux/slab.h> | |
6153 | +#include <linux/fs_struct.h> | |
2380c486 | 6154 | + |
9474138d AM |
6155 | +#include "include/apparmor.h" |
6156 | +#include "include/path.h" | |
ceaf2cfb AM |
6157 | +#include "include/policy.h" |
6158 | + | |
76514441 AM |
6159 | + |
6160 | +/* modified from dcache.c */ | |
6161 | +static int prepend(char **buffer, int buflen, const char *str, int namelen) | |
6162 | +{ | |
6163 | + buflen -= namelen; | |
6164 | + if (buflen < 0) | |
6165 | + return -ENAMETOOLONG; | |
6166 | + *buffer -= namelen; | |
6167 | + memcpy(*buffer, str, namelen); | |
6168 | + return 0; | |
6169 | +} | |
6170 | + | |
6171 | +#define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) | |
6172 | + | |
ceaf2cfb AM |
6173 | +/** |
6174 | + * d_namespace_path - lookup a name associated with a given path | |
6175 | + * @path: path to lookup (NOT NULL) | |
6176 | + * @buf: buffer to store path to (NOT NULL) | |
6177 | + * @buflen: length of @buf | |
76514441 AM |
6178 | + * @name: Returns - pointer for start of path name with in @buf (NOT NULL) |
6179 | + * @flags: flags controlling path lookup | |
ceaf2cfb AM |
6180 | + * |
6181 | + * Handle path name lookup. | |
6182 | + * | |
6183 | + * Returns: %0 else error code if path lookup fails | |
6184 | + * When no error the path name is returned in @name which points to | |
6185 | + * to a position in @buf | |
6186 | + */ | |
6187 | +static int d_namespace_path(struct path *path, char *buf, int buflen, | |
6188 | + char **name, int flags) | |
6189 | +{ | |
6190 | + struct path root, tmp; | |
6191 | + char *res; | |
6192 | + int deleted, connected; | |
6193 | + int error = 0; | |
6194 | + | |
6195 | + /* Get the root we want to resolve too */ | |
6196 | + if (flags & PATH_CHROOT_REL) { | |
6197 | + /* resolve paths relative to chroot */ | |
6198 | + read_lock(¤t->fs->lock); | |
6199 | + root = current->fs->root; | |
6200 | + /* released below */ | |
6201 | + path_get(&root); | |
6202 | + read_unlock(¤t->fs->lock); | |
6203 | + } else { | |
6204 | + /* resolve paths relative to namespace */ | |
6205 | + root.mnt = current->nsproxy->mnt_ns->root; | |
6206 | + root.dentry = root.mnt->mnt_root; | |
6207 | + /* released below */ | |
6208 | + path_get(&root); | |
6209 | + } | |
6210 | + | |
6211 | + spin_lock(&dcache_lock); | |
6212 | + /* There is a race window between path lookup here and the | |
6213 | + * need to strip the " (deleted) string that __d_path applies | |
6214 | + * Detect the race and relookup the path | |
6215 | + * | |
6216 | + * The stripping of (deleted) is a hack that could be removed | |
6217 | + * with an updated __d_path | |
6218 | + */ | |
6219 | + do { | |
6220 | + tmp = root; | |
6221 | + deleted = d_unlinked(path->dentry); | |
6222 | + res = __d_path(path, &tmp, buf, buflen); | |
6223 | + | |
6224 | + } while (deleted != d_unlinked(path->dentry)); | |
6225 | + spin_unlock(&dcache_lock); | |
6226 | + | |
6227 | + *name = res; | |
6228 | + /* handle error conditions - and still allow a partial path to | |
6229 | + * be returned. | |
6230 | + */ | |
6231 | + if (IS_ERR(res)) { | |
6232 | + error = PTR_ERR(res); | |
6233 | + *name = buf; | |
6234 | + goto out; | |
6235 | + } | |
6236 | + if (deleted) { | |
6237 | + /* On some filesystems, newly allocated dentries appear to the | |
6238 | + * security_path hooks as a deleted dentry except without an | |
6239 | + * inode allocated. | |
6240 | + * | |
6241 | + * Remove the appended deleted text and return as string for | |
6242 | + * normal mediation, or auditing. The (deleted) string is | |
76514441 | 6243 | + * guaranteed to be added in this case, so just strip it. |
ceaf2cfb AM |
6244 | + */ |
6245 | + buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ | |
6246 | + | |
6247 | + if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { | |
6248 | + error = -ENOENT; | |
6249 | + goto out; | |
6250 | + } | |
6251 | + } | |
6252 | + | |
6253 | + /* Determine if the path is connected to the expected root */ | |
6254 | + connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; | |
6255 | + | |
76514441 AM |
6256 | + /* If the path is not connected, |
6257 | + * check if it is a sysctl and handle specially else remove any | |
6258 | + * leading / that __d_path may have returned. | |
ceaf2cfb AM |
6259 | + * Unless |
6260 | + * specifically directed to connect the path, | |
6261 | + * OR | |
6262 | + * if in a chroot and doing chroot relative paths and the path | |
6263 | + * resolves to the namespace root (would be connected outside | |
6264 | + * of chroot) and specifically directed to connect paths to | |
6265 | + * namespace root. | |
6266 | + */ | |
76514441 AM |
6267 | + if (!connected) { |
6268 | + /* is the disconnect path a sysctl? */ | |
6269 | + if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && | |
6270 | + strncmp(*name, "/sys/", 5) == 0) { | |
6271 | + /* TODO: convert over to using a per namespace | |
6272 | + * control instead of hard coded /proc | |
6273 | + */ | |
6274 | + error = prepend(name, *name - buf, "/proc", 5); | |
6275 | + } else if (!(flags & PATH_CONNECT_PATH) && | |
6276 | + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | |
6277 | + (tmp.mnt == current->nsproxy->mnt_ns->root && | |
6278 | + tmp.dentry == tmp.mnt->mnt_root))) { | |
6279 | + /* disconnected path, don't return pathname starting | |
6280 | + * with '/' | |
6281 | + */ | |
6282 | + error = -ESTALE; | |
6283 | + if (*res == '/') | |
6284 | + *name = res + 1; | |
6285 | + } | |
ceaf2cfb AM |
6286 | + } |
6287 | + | |
6288 | +out: | |
6289 | + path_put(&root); | |
6290 | + | |
6291 | + return error; | |
6292 | +} | |
9474138d | 6293 | + |
ceaf2cfb AM |
6294 | +/** |
6295 | + * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended | |
6296 | + * @path: path to get name for (NOT NULL) | |
6297 | + * @flags: flags controlling path lookup | |
6298 | + * @buffer: buffer to put name in (NOT NULL) | |
6299 | + * @size: size of buffer | |
76514441 | 6300 | + * @name: Returns - contains position of path name in @buffer (NOT NULL) |
ceaf2cfb AM |
6301 | + * |
6302 | + * Returns: %0 else error on failure | |
6303 | + */ | |
6304 | +static int get_name_to_buffer(struct path *path, int flags, char *buffer, | |
6305 | + int size, char **name) | |
9474138d | 6306 | +{ |
ceaf2cfb AM |
6307 | + int adjust = (flags & PATH_IS_DIR) ? 1 : 0; |
6308 | + int error = d_namespace_path(path, buffer, size - adjust, name, flags); | |
9474138d | 6309 | + |
ceaf2cfb | 6310 | + if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') |
9474138d AM |
6311 | + /* |
6312 | + * Append "/" to the pathname. The root directory is a special | |
6313 | + * case; it already ends in slash. | |
6314 | + */ | |
6315 | + strcpy(&buffer[size - 2], "/"); | |
2380c486 | 6316 | + |
2380c486 JR |
6317 | + return error; |
6318 | +} | |
6319 | + | |
6320 | +/** | |
9474138d | 6321 | + * aa_get_name - compute the pathname of a file |
ceaf2cfb | 6322 | + * @path: path the file (NOT NULL) |
76514441 | 6323 | + * @flags: flags controlling path name generation |
ceaf2cfb | 6324 | + * @buffer: buffer that aa_get_name() allocated (NOT NULL) |
76514441 | 6325 | + * @name: Returns - the generated path name if !error (NOT NULL) |
2380c486 | 6326 | + * |
ceaf2cfb | 6327 | + * @name is a pointer to the beginning of the pathname (which usually differs |
9474138d | 6328 | + * from the beginning of the buffer), or NULL. If there is an error @name |
ceaf2cfb AM |
6329 | + * may contain a partial or invalid name that can be used for audit purposes, |
6330 | + * but it can not be used for mediation. | |
6331 | + * | |
6332 | + * We need PATH_IS_DIR to indicate whether the file is a directory or not | |
6333 | + * because the file may not yet exist, and so we cannot check the inode's | |
6334 | + * file type. | |
9474138d | 6335 | + * |
ceaf2cfb | 6336 | + * Returns: %0 else error code if could retrieve name |
2380c486 | 6337 | + */ |
76514441 | 6338 | +int aa_get_name(struct path *path, int flags, char **buffer, const char **name) |
2380c486 | 6339 | +{ |
9474138d AM |
6340 | + char *buf, *str = NULL; |
6341 | + int size = 256; | |
6342 | + int error; | |
2380c486 | 6343 | + |
9474138d AM |
6344 | + *name = NULL; |
6345 | + *buffer = NULL; | |
6346 | + for (;;) { | |
ceaf2cfb | 6347 | + /* freed by caller */ |
9474138d AM |
6348 | + buf = kmalloc(size, GFP_KERNEL); |
6349 | + if (!buf) | |
6350 | + return -ENOMEM; | |
2380c486 | 6351 | + |
ceaf2cfb | 6352 | + error = get_name_to_buffer(path, flags, buf, size, &str); |
76514441 | 6353 | + if (error != -ENAMETOOLONG) |
9474138d | 6354 | + break; |
2380c486 | 6355 | + |
9474138d AM |
6356 | + kfree(buf); |
6357 | + size <<= 1; | |
ceaf2cfb | 6358 | + if (size > aa_g_path_max) |
9474138d AM |
6359 | + return -ENAMETOOLONG; |
6360 | + } | |
6361 | + *buffer = buf; | |
6362 | + *name = str; | |
2380c486 | 6363 | + |
9474138d AM |
6364 | + return error; |
6365 | +} | |
ceaf2cfb AM |
6366 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c |
6367 | new file mode 100644 | |
76514441 | 6368 | index 0000000..e3020ed |
ceaf2cfb AM |
6369 | --- /dev/null |
6370 | +++ b/security/apparmor/policy.c | |
76514441 | 6371 | @@ -0,0 +1,1185 @@ |
9474138d AM |
6372 | +/* |
6373 | + * AppArmor security module | |
6374 | + * | |
6375 | + * This file contains AppArmor policy manipulation functions | |
6376 | + * | |
6377 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 6378 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
6379 | + * |
6380 | + * This program is free software; you can redistribute it and/or | |
6381 | + * modify it under the terms of the GNU General Public License as | |
6382 | + * published by the Free Software Foundation, version 2 of the | |
6383 | + * License. | |
6384 | + * | |
6385 | + * | |
6386 | + * AppArmor policy is based around profiles, which contain the rules a | |
76514441 | 6387 | + * task is confined by. Every task in the system has a profile attached |
9474138d AM |
6388 | + * to it determined either by matching "unconfined" tasks against the |
6389 | + * visible set of profiles or by following a profiles attachment rules. | |
6390 | + * | |
ceaf2cfb AM |
6391 | + * Each profile exists in a profile namespace which is a container of |
6392 | + * visible profiles. Each namespace contains a special "unconfined" profile, | |
6393 | + * which doesn't enforce any confinement on a task beyond DAC. | |
9474138d AM |
6394 | + * |
6395 | + * Namespace and profile names can be written together in either | |
6396 | + * of two syntaxes. | |
6397 | + * :namespace:profile - used by kernel interfaces for easy detection | |
6398 | + * namespace://profile - used by policy | |
6399 | + * | |
ceaf2cfb | 6400 | + * Profile names can not start with : or @ or ^ and may not contain \0 |
76514441 | 6401 | + * |
9474138d AM |
6402 | + * Reserved profile names |
6403 | + * unconfined - special automatically generated unconfined profile | |
6404 | + * inherit - special name to indicate profile inheritance | |
76514441 | 6405 | + * null-XXXX-YYYY - special automatically generated learning profiles |
2380c486 | 6406 | + * |
ceaf2cfb | 6407 | + * Namespace names may not start with / or @ and may not contain \0 or : |
76514441 | 6408 | + * Reserved namespace names |
9474138d | 6409 | + * user-XXXX - user defined profiles |
ceaf2cfb | 6410 | + * |
76514441 | 6411 | + * a // in a profile or namespace name indicates a hierarchical name with the |
ceaf2cfb AM |
6412 | + * name before the // being the parent and the name after the child. |
6413 | + * | |
76514441 | 6414 | + * Profile and namespace hierarchies serve two different but similar purposes. |
ceaf2cfb AM |
6415 | + * The namespace contains the set of visible profiles that are considered |
6416 | + * for attachment. The hierarchy of namespaces allows for virtualizing | |
6417 | + * the namespace so that for example a chroot can have its own set of profiles | |
6418 | + * which may define some local user namespaces. | |
76514441 | 6419 | + * The profile hierarchy severs two distinct purposes, |
ceaf2cfb AM |
6420 | + * - it allows for sub profiles or hats, which allows an application to run |
6421 | + * subprograms under its own profile with different restriction than it | |
6422 | + * self, and not have it use the system profile. | |
6423 | + * eg. if a mail program starts an editor, the policy might make the | |
6424 | + * restrictions tighter on the editor tighter than the mail program, | |
6425 | + * and definitely different than general editor restrictions | |
6426 | + * - it allows for binary hierarchy of profiles, so that execution history | |
6427 | + * is preserved. This feature isn't exploited by AppArmor reference policy | |
6428 | + * but is allowed. NOTE: this is currently suboptimal because profile | |
6429 | + * aliasing is not currently implemented so that a profile for each | |
6430 | + * level must be defined. | |
6431 | + * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started | |
6432 | + * from /bin/bash | |
6433 | + * | |
76514441 AM |
6434 | + * A profile or namespace name that can contain one or more // separators |
6435 | + * is referred to as an hname (hierarchical). | |
ceaf2cfb AM |
6436 | + * eg. /bin/bash//bin/ls |
6437 | + * | |
6438 | + * An fqname is a name that may contain both namespace and profile hnames. | |
6439 | + * eg. :ns:/bin/bash//bin/ls | |
6440 | + * | |
6441 | + * NOTES: | |
6442 | + * - locking of profile lists is currently fairly coarse. All profile | |
6443 | + * lists within a namespace use the namespace lock. | |
6444 | + * FIXME: move profile lists to using rcu_lists | |
2380c486 | 6445 | + */ |
2380c486 | 6446 | + |
9474138d AM |
6447 | +#include <linux/slab.h> |
6448 | +#include <linux/spinlock.h> | |
6449 | +#include <linux/string.h> | |
6450 | + | |
6451 | +#include "include/apparmor.h" | |
6452 | +#include "include/capability.h" | |
ceaf2cfb | 6453 | +#include "include/context.h" |
9474138d AM |
6454 | +#include "include/file.h" |
6455 | +#include "include/ipc.h" | |
6456 | +#include "include/match.h" | |
ceaf2cfb | 6457 | +#include "include/path.h" |
9474138d | 6458 | +#include "include/policy.h" |
ceaf2cfb | 6459 | +#include "include/policy_unpack.h" |
9474138d AM |
6460 | +#include "include/resource.h" |
6461 | +#include "include/sid.h" | |
2380c486 | 6462 | + |
2380c486 | 6463 | + |
ceaf2cfb AM |
6464 | +/* root profile namespace */ |
6465 | +struct aa_namespace *root_ns; | |
2380c486 | 6466 | + |
9474138d AM |
6467 | +const char *profile_mode_names[] = { |
6468 | + "enforce", | |
6469 | + "complain", | |
6470 | + "kill", | |
6471 | +}; | |
2380c486 | 6472 | + |
ceaf2cfb AM |
6473 | +/** |
6474 | + * hname_tail - find the last component of an hname | |
76514441 | 6475 | + * @name: hname to find the base profile name component of (NOT NULL) |
ceaf2cfb | 6476 | + * |
76514441 | 6477 | + * Returns: the tail (base profile name) name component of an hname |
ceaf2cfb AM |
6478 | + */ |
6479 | +static const char *hname_tail(const char *hname) | |
6480 | +{ | |
6481 | + char *split; | |
6482 | + hname = strim((char *)hname); | |
6483 | + for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) | |
6484 | + hname = split + 2; | |
2380c486 | 6485 | + |
ceaf2cfb AM |
6486 | + return hname; |
6487 | +} | |
2380c486 | 6488 | + |
ceaf2cfb AM |
6489 | +/** |
6490 | + * policy_init - initialize a policy structure | |
6491 | + * @policy: policy to initialize (NOT NULL) | |
76514441 | 6492 | + * @prefix: prefix name if any is required. (MAYBE NULL) |
ceaf2cfb | 6493 | + * @name: name of the policy, init will make a copy of it (NOT NULL) |
76514441 AM |
6494 | + * |
6495 | + * Note: this fn creates a copy of strings passed in | |
6496 | + * | |
6497 | + * Returns: true if policy init successful | |
ceaf2cfb | 6498 | + */ |
76514441 AM |
6499 | +static bool policy_init(struct aa_policy *policy, const char *prefix, |
6500 | + const char *name) | |
2380c486 | 6501 | +{ |
ceaf2cfb | 6502 | + /* freed by policy_free */ |
76514441 AM |
6503 | + if (prefix) { |
6504 | + policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, | |
6505 | + GFP_KERNEL); | |
6506 | + if (policy->hname) | |
6507 | + sprintf(policy->hname, "%s//%s", prefix, name); | |
6508 | + } else | |
6509 | + policy->hname = kstrdup(name, GFP_KERNEL); | |
ceaf2cfb | 6510 | + if (!policy->hname) |
9474138d | 6511 | + return 0; |
ceaf2cfb AM |
6512 | + /* base.name is a substring of fqname */ |
6513 | + policy->name = (char *)hname_tail(policy->hname); | |
ceaf2cfb AM |
6514 | + INIT_LIST_HEAD(&policy->list); |
6515 | + INIT_LIST_HEAD(&policy->profiles); | |
6516 | + kref_init(&policy->count); | |
2380c486 | 6517 | + |
9474138d AM |
6518 | + return 1; |
6519 | +} | |
2380c486 | 6520 | + |
ceaf2cfb AM |
6521 | +/** |
6522 | + * policy_destroy - free the elements referenced by @policy | |
6523 | + * @policy: policy that is to have its elements freed (NOT NULL) | |
6524 | + */ | |
6525 | +static void policy_destroy(struct aa_policy *policy) | |
9474138d AM |
6526 | +{ |
6527 | + /* still contains profiles -- invalid */ | |
ceaf2cfb | 6528 | + if (!list_empty(&policy->profiles)) { |
9474138d AM |
6529 | + AA_ERROR("%s: internal error, " |
6530 | + "policy '%s' still contains profiles\n", | |
ceaf2cfb | 6531 | + __func__, policy->name); |
9474138d | 6532 | + BUG(); |
2380c486 | 6533 | + } |
ceaf2cfb | 6534 | + if (!list_empty(&policy->list)) { |
9474138d | 6535 | + AA_ERROR("%s: internal error, policy '%s' still on list\n", |
ceaf2cfb | 6536 | + __func__, policy->name); |
9474138d | 6537 | + BUG(); |
2380c486 JR |
6538 | + } |
6539 | + | |
ceaf2cfb AM |
6540 | + /* don't free name as its a subset of hname */ |
6541 | + kzfree(policy->hname); | |
9474138d AM |
6542 | +} |
6543 | + | |
ceaf2cfb AM |
6544 | +/** |
6545 | + * __policy_find - find a policy by @name on a policy list | |
6546 | + * @head: list to search (NOT NULL) | |
6547 | + * @name: name to search for (NOT NULL) | |
6548 | + * | |
6549 | + * Requires: correct locks for the @head list be held | |
6550 | + * | |
76514441 | 6551 | + * Returns: unrefcounted policy that match @name or NULL if not found |
ceaf2cfb AM |
6552 | + */ |
6553 | +static struct aa_policy *__policy_find(struct list_head *head, const char *name) | |
9474138d | 6554 | +{ |
ceaf2cfb | 6555 | + struct aa_policy *policy; |
9474138d | 6556 | + |
ceaf2cfb AM |
6557 | + list_for_each_entry(policy, head, list) { |
6558 | + if (!strcmp(policy->name, name)) | |
6559 | + return policy; | |
2380c486 | 6560 | + } |
9474138d AM |
6561 | + return NULL; |
6562 | +} | |
2380c486 | 6563 | + |
ceaf2cfb | 6564 | +/** |
76514441 | 6565 | + * __policy_strn_find - find a policy that's name matches @len chars of @str |
ceaf2cfb AM |
6566 | + * @head: list to search (NOT NULL) |
6567 | + * @str: string to search for (NOT NULL) | |
6568 | + * @len: length of match required | |
6569 | + * | |
6570 | + * Requires: correct locks for the @head list be held | |
6571 | + * | |
76514441 | 6572 | + * Returns: unrefcounted policy that match @str or NULL if not found |
ceaf2cfb AM |
6573 | + * |
6574 | + * if @len == strlen(@strlen) then this is equiv to __policy_find | |
6575 | + * other wise it allows searching for policy by a partial match of name | |
6576 | + */ | |
6577 | +static struct aa_policy *__policy_strn_find(struct list_head *head, | |
6578 | + const char *str, int len) | |
9474138d | 6579 | +{ |
ceaf2cfb | 6580 | + struct aa_policy *policy; |
2380c486 | 6581 | + |
ceaf2cfb AM |
6582 | + list_for_each_entry(policy, head, list) { |
6583 | + if (aa_strneq(policy->name, str, len)) | |
6584 | + return policy; | |
9474138d | 6585 | + } |
2380c486 | 6586 | + |
9474138d | 6587 | + return NULL; |
2380c486 JR |
6588 | +} |
6589 | + | |
9474138d AM |
6590 | +/* |
6591 | + * Routines for AppArmor namespaces | |
2380c486 | 6592 | + */ |
2380c486 | 6593 | + |
76514441 AM |
6594 | +static const char *hidden_ns_name = "---"; |
6595 | +/** | |
6596 | + * aa_ns_visible - test if @view is visible from @curr | |
6597 | + * @curr: namespace to treat as the parent (NOT NULL) | |
6598 | + * @view: namespace to test if visible from @curr (NOT NULL) | |
6599 | + * | |
6600 | + * Returns: true if @view is visible from @curr else false | |
6601 | + */ | |
6602 | +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) | |
6603 | +{ | |
6604 | + if (curr == view) | |
6605 | + return true; | |
6606 | + | |
6607 | + for ( ; view; view = view->parent) { | |
6608 | + if (view->parent == curr) | |
6609 | + return true; | |
6610 | + } | |
6611 | + return false; | |
6612 | +} | |
6613 | + | |
6614 | +/** | |
6615 | + * aa_na_name - Find the ns name to display for @view from @curr | |
6616 | + * @curr - current namespace (NOT NULL) | |
6617 | + * @view - namespace attempting to view (NOT NULL) | |
6618 | + * | |
6619 | + * Returns: name of @view visible from @curr | |
6620 | + */ | |
6621 | +const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) | |
6622 | +{ | |
6623 | + /* if view == curr then the namespace name isn't displayed */ | |
6624 | + if (curr == view) | |
6625 | + return ""; | |
6626 | + | |
6627 | + if (aa_ns_visible(curr, view)) { | |
6628 | + /* at this point if a ns is visible it is in a view ns | |
6629 | + * thus the curr ns.hname is a prefix of its name. | |
6630 | + * Only output the virtualized portion of the name | |
6631 | + * Add + 2 to skip over // separating curr hname prefix | |
6632 | + * from the visible tail of the views hname | |
6633 | + */ | |
6634 | + return view->base.hname + strlen(curr->base.hname) + 2; | |
6635 | + } else | |
6636 | + return hidden_ns_name; | |
6637 | +} | |
6638 | + | |
2380c486 | 6639 | +/** |
76514441 AM |
6640 | + * alloc_namespace - allocate, initialize and return a new namespace |
6641 | + * @prefix: parent namespace name (MAYBE NULL) | |
ceaf2cfb AM |
6642 | + * @name: a preallocated name (NOT NULL) |
6643 | + * | |
76514441 | 6644 | + * Returns: refcounted namespace or NULL on failure. |
2380c486 | 6645 | + */ |
76514441 AM |
6646 | +static struct aa_namespace *alloc_namespace(const char *prefix, |
6647 | + const char *name) | |
2380c486 | 6648 | +{ |
9474138d | 6649 | + struct aa_namespace *ns; |
2380c486 | 6650 | + |
9474138d AM |
6651 | + ns = kzalloc(sizeof(*ns), GFP_KERNEL); |
6652 | + AA_DEBUG("%s(%p)\n", __func__, ns); | |
6653 | + if (!ns) | |
6654 | + return NULL; | |
76514441 | 6655 | + if (!policy_init(&ns->base, prefix, name)) |
9474138d | 6656 | + goto fail_ns; |
76514441 | 6657 | + |
ceaf2cfb AM |
6658 | + INIT_LIST_HEAD(&ns->sub_ns); |
6659 | + rwlock_init(&ns->lock); | |
2380c486 | 6660 | + |
76514441 | 6661 | + /* released by free_namespace */ |
ceaf2cfb | 6662 | + ns->unconfined = aa_alloc_profile("unconfined"); |
9474138d AM |
6663 | + if (!ns->unconfined) |
6664 | + goto fail_unconfined; | |
2380c486 | 6665 | + |
ceaf2cfb | 6666 | + ns->unconfined->sid = aa_alloc_sid(); |
9474138d | 6667 | + ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | |
ceaf2cfb AM |
6668 | + PFLAG_IMMUTABLE; |
6669 | + | |
6670 | + /* | |
76514441 | 6671 | + * released by free_namespace, however __remove_namespace breaks |
ceaf2cfb AM |
6672 | + * the cyclic references (ns->unconfined, and unconfined->ns) and |
6673 | + * replaces with refs to parent namespace unconfined | |
6674 | + */ | |
9474138d | 6675 | + ns->unconfined->ns = aa_get_namespace(ns); |
2380c486 | 6676 | + |
9474138d | 6677 | + return ns; |
2380c486 | 6678 | + |
9474138d | 6679 | +fail_unconfined: |
ceaf2cfb | 6680 | + kzfree(ns->base.name); |
9474138d | 6681 | +fail_ns: |
ceaf2cfb | 6682 | + kzfree(ns); |
9474138d | 6683 | + return NULL; |
2380c486 JR |
6684 | +} |
6685 | + | |
9474138d | 6686 | +/** |
76514441 | 6687 | + * free_namespace - free a profile namespace |
ceaf2cfb | 6688 | + * @ns: the namespace to free (MAYBE NULL) |
9474138d | 6689 | + * |
ceaf2cfb AM |
6690 | + * Requires: All references to the namespace must have been put, if the |
6691 | + * namespace was referenced by a profile confining a task, | |
9474138d | 6692 | + */ |
76514441 | 6693 | +static void free_namespace(struct aa_namespace *ns) |
9474138d AM |
6694 | +{ |
6695 | + if (!ns) | |
6696 | + return; | |
2380c486 | 6697 | + |
ceaf2cfb AM |
6698 | + policy_destroy(&ns->base); |
6699 | + aa_put_namespace(ns->parent); | |
2380c486 | 6700 | + |
9474138d AM |
6701 | + if (ns->unconfined && ns->unconfined->ns == ns) |
6702 | + ns->unconfined->ns = NULL; | |
2380c486 | 6703 | + |
9474138d | 6704 | + aa_put_profile(ns->unconfined); |
ceaf2cfb | 6705 | + kzfree(ns); |
9474138d | 6706 | +} |
2380c486 | 6707 | + |
ceaf2cfb AM |
6708 | +/** |
6709 | + * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | |
6710 | + * @kr: kref callback for freeing of a namespace (NOT NULL) | |
6711 | + */ | |
6712 | +void aa_free_namespace_kref(struct kref *kref) | |
9474138d | 6713 | +{ |
76514441 | 6714 | + free_namespace(container_of(kref, struct aa_namespace, base.count)); |
2380c486 JR |
6715 | +} |
6716 | + | |
6717 | +/** | |
ceaf2cfb AM |
6718 | + * __aa_find_namespace - find a namespace on a list by @name |
6719 | + * @head: list to search for namespace on (NOT NULL) | |
6720 | + * @name: name of namespace to look for (NOT NULL) | |
2380c486 | 6721 | + * |
ceaf2cfb AM |
6722 | + * Returns: unrefcounted namespace |
6723 | + * | |
6724 | + * Requires: ns lock be held | |
2380c486 | 6725 | + */ |
ceaf2cfb AM |
6726 | +static struct aa_namespace *__aa_find_namespace(struct list_head *head, |
6727 | + const char *name) | |
2380c486 | 6728 | +{ |
ceaf2cfb | 6729 | + return (struct aa_namespace *)__policy_find(head, name); |
9474138d | 6730 | +} |
2380c486 | 6731 | + |
ceaf2cfb AM |
6732 | +/** |
6733 | + * aa_find_namespace - look up a profile namespace on the namespace list | |
6734 | + * @root: namespace to search in (NOT NULL) | |
6735 | + * @name: name of namespace to find (NOT NULL) | |
6736 | + * | |
76514441 AM |
6737 | + * Returns: a refcounted namespace on the list, or NULL if no namespace |
6738 | + * called @name exists. | |
ceaf2cfb AM |
6739 | + * |
6740 | + * refcount released by caller | |
6741 | + */ | |
6742 | +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | |
6743 | + const char *name) | |
9474138d AM |
6744 | +{ |
6745 | + struct aa_namespace *ns = NULL; | |
2380c486 | 6746 | + |
ceaf2cfb AM |
6747 | + read_lock(&root->lock); |
6748 | + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | |
6749 | + read_unlock(&root->lock); | |
2380c486 | 6750 | + |
9474138d AM |
6751 | + return ns; |
6752 | +} | |
2380c486 | 6753 | + |
9474138d AM |
6754 | +/** |
6755 | + * aa_prepare_namespace - find an existing or create a new namespace of @name | |
76514441 | 6756 | + * @name: the namespace to find or add (MAYBE NULL) |
ceaf2cfb AM |
6757 | + * |
6758 | + * Returns: refcounted namespace or NULL if failed to create one | |
9474138d | 6759 | + */ |
ceaf2cfb | 6760 | +static struct aa_namespace *aa_prepare_namespace(const char *name) |
9474138d | 6761 | +{ |
ceaf2cfb AM |
6762 | + struct aa_namespace *ns, *root; |
6763 | + | |
6764 | + root = aa_current_profile()->ns; | |
6765 | + | |
6766 | + write_lock(&root->lock); | |
6767 | + | |
6768 | + /* if name isn't specified the profile is loaded to the current ns */ | |
6769 | + if (!name) { | |
6770 | + /* released by caller */ | |
6771 | + ns = aa_get_namespace(root); | |
6772 | + goto out; | |
6773 | + } | |
6774 | + | |
6775 | + /* try and find the specified ns and if it doesn't exist create it */ | |
6776 | + /* released by caller */ | |
6777 | + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | |
9474138d | 6778 | + if (!ns) { |
76514441 | 6779 | + /* namespace not found */ |
9474138d | 6780 | + struct aa_namespace *new_ns; |
ceaf2cfb | 6781 | + write_unlock(&root->lock); |
76514441 | 6782 | + new_ns = alloc_namespace(root->base.hname, name); |
9474138d AM |
6783 | + if (!new_ns) |
6784 | + return NULL; | |
ceaf2cfb AM |
6785 | + write_lock(&root->lock); |
6786 | + /* test for race when new_ns was allocated */ | |
6787 | + ns = __aa_find_namespace(&root->sub_ns, name); | |
9474138d | 6788 | + if (!ns) { |
ceaf2cfb AM |
6789 | + /* add parent ref */ |
6790 | + new_ns->parent = aa_get_namespace(root); | |
6791 | + | |
6792 | + list_add(&new_ns->base.list, &root->sub_ns); | |
6793 | + /* add list ref */ | |
6794 | + ns = aa_get_namespace(new_ns); | |
9474138d AM |
6795 | + } else { |
6796 | + /* raced so free the new one */ | |
76514441 | 6797 | + free_namespace(new_ns); |
ceaf2cfb | 6798 | + /* get reference on namespace */ |
9474138d AM |
6799 | + aa_get_namespace(ns); |
6800 | + } | |
2380c486 | 6801 | + } |
ceaf2cfb AM |
6802 | +out: |
6803 | + write_unlock(&root->lock); | |
2380c486 | 6804 | + |
ceaf2cfb | 6805 | + /* return ref */ |
9474138d | 6806 | + return ns; |
2380c486 JR |
6807 | +} |
6808 | + | |
ceaf2cfb | 6809 | +/** |
76514441 | 6810 | + * __list_add_profile - add a profile to a list |
ceaf2cfb AM |
6811 | + * @list: list to add it to (NOT NULL) |
6812 | + * @profile: the profile to add (NOT NULL) | |
6813 | + * | |
76514441 | 6814 | + * refcount @profile, should be put by __list_remove_profile |
ceaf2cfb AM |
6815 | + * |
6816 | + * Requires: namespace lock be held, or list not be shared | |
9474138d | 6817 | + */ |
76514441 AM |
6818 | +static void __list_add_profile(struct list_head *list, |
6819 | + struct aa_profile *profile) | |
2380c486 | 6820 | +{ |
ceaf2cfb AM |
6821 | + list_add(&profile->base.list, list); |
6822 | + /* get list reference */ | |
6823 | + aa_get_profile(profile); | |
2380c486 JR |
6824 | +} |
6825 | + | |
ceaf2cfb | 6826 | +/** |
76514441 | 6827 | + * __list_remove_profile - remove a profile from the list it is on |
ceaf2cfb AM |
6828 | + * @profile: the profile to remove (NOT NULL) |
6829 | + * | |
6830 | + * remove a profile from the list, warning generally removal should | |
76514441 | 6831 | + * be done with __replace_profile as most profile removals are |
ceaf2cfb AM |
6832 | + * replacements to the unconfined profile. |
6833 | + * | |
6834 | + * put @profile list refcount | |
6835 | + * | |
6836 | + * Requires: namespace lock be held, or list not have been live | |
6837 | + */ | |
76514441 | 6838 | +static void __list_remove_profile(struct aa_profile *profile) |
2380c486 | 6839 | +{ |
9474138d AM |
6840 | + list_del_init(&profile->base.list); |
6841 | + if (!(profile->flags & PFLAG_NO_LIST_REF)) | |
ceaf2cfb | 6842 | + /* release list reference */ |
9474138d AM |
6843 | + aa_put_profile(profile); |
6844 | +} | |
2380c486 | 6845 | + |
ceaf2cfb | 6846 | +/** |
76514441 | 6847 | + * __replace_profile - replace @old with @new on a list |
ceaf2cfb | 6848 | + * @old: profile to be replaced (NOT NULL) |
76514441 | 6849 | + * @new: profile to replace @old with (NOT NULL) |
ceaf2cfb | 6850 | + * |
76514441 AM |
6851 | + * Will duplicate and refcount elements that @new inherits from @old |
6852 | + * and will inherit @old children. | |
ceaf2cfb AM |
6853 | + * |
6854 | + * refcount @new for list, put @old list refcount | |
6855 | + * | |
6856 | + * Requires: namespace list lock be held, or list not be shared | |
6857 | + */ | |
76514441 | 6858 | +static void __replace_profile(struct aa_profile *old, struct aa_profile *new) |
9474138d | 6859 | +{ |
ceaf2cfb AM |
6860 | + struct aa_policy *policy; |
6861 | + struct aa_profile *child, *tmp; | |
9474138d | 6862 | + |
ceaf2cfb AM |
6863 | + if (old->parent) |
6864 | + policy = &old->parent->base; | |
6865 | + else | |
6866 | + policy = &old->ns->base; | |
6867 | + | |
76514441 AM |
6868 | + /* released when @new is freed */ |
6869 | + new->parent = aa_get_profile(old->parent); | |
6870 | + new->ns = aa_get_namespace(old->ns); | |
6871 | + new->sid = old->sid; | |
6872 | + __list_add_profile(&policy->profiles, new); | |
ceaf2cfb AM |
6873 | + /* inherit children */ |
6874 | + list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | |
6875 | + aa_put_profile(child->parent); | |
6876 | + child->parent = aa_get_profile(new); | |
76514441 | 6877 | + /* list refcount transferred to @new*/ |
ceaf2cfb AM |
6878 | + list_move(&child->base.list, &new->base.profiles); |
6879 | + } | |
6880 | + | |
76514441 | 6881 | + /* released by free_profile */ |
ceaf2cfb | 6882 | + old->replacedby = aa_get_profile(new); |
76514441 | 6883 | + __list_remove_profile(old); |
2380c486 JR |
6884 | +} |
6885 | + | |
76514441 AM |
6886 | +static void __profile_list_release(struct list_head *head); |
6887 | + | |
2380c486 | 6888 | +/** |
76514441 AM |
6889 | + * __remove_profile - remove old profile, and children |
6890 | + * @profile: profile to be replaced (NOT NULL) | |
ceaf2cfb | 6891 | + * |
76514441 | 6892 | + * Requires: namespace list lock be held, or list not be shared |
2380c486 | 6893 | + */ |
76514441 | 6894 | +static void __remove_profile(struct aa_profile *profile) |
2380c486 | 6895 | +{ |
76514441 AM |
6896 | + /* release any children lists first */ |
6897 | + __profile_list_release(&profile->base.profiles); | |
6898 | + /* released by free_profile */ | |
6899 | + profile->replacedby = aa_get_profile(profile->ns->unconfined); | |
6900 | + __list_remove_profile(profile); | |
9474138d | 6901 | +} |
2380c486 | 6902 | + |
ceaf2cfb | 6903 | +/** |
76514441 AM |
6904 | + * __profile_list_release - remove all profiles on the list and put refs |
6905 | + * @head: list of profiles (NOT NULL) | |
ceaf2cfb AM |
6906 | + * |
6907 | + * Requires: namespace lock be held | |
6908 | + */ | |
76514441 | 6909 | +static void __profile_list_release(struct list_head *head) |
ceaf2cfb | 6910 | +{ |
76514441 AM |
6911 | + struct aa_profile *profile, *tmp; |
6912 | + list_for_each_entry_safe(profile, tmp, head, base.list) | |
6913 | + __remove_profile(profile); | |
ceaf2cfb AM |
6914 | +} |
6915 | + | |
76514441 AM |
6916 | +static void __ns_list_release(struct list_head *head); |
6917 | + | |
ceaf2cfb | 6918 | +/** |
76514441 | 6919 | + * destroy_namespace - remove everything contained by @ns |
ceaf2cfb AM |
6920 | + * @ns: namespace to have it contents removed (NOT NULL) |
6921 | + */ | |
76514441 | 6922 | +static void destroy_namespace(struct aa_namespace *ns) |
ceaf2cfb AM |
6923 | +{ |
6924 | + if (!ns) | |
6925 | + return; | |
6926 | + | |
6927 | + write_lock(&ns->lock); | |
6928 | + /* release all profiles in this namespace */ | |
76514441 | 6929 | + __profile_list_release(&ns->base.profiles); |
ceaf2cfb AM |
6930 | + |
6931 | + /* release all sub namespaces */ | |
76514441 | 6932 | + __ns_list_release(&ns->sub_ns); |
ceaf2cfb AM |
6933 | + |
6934 | + write_unlock(&ns->lock); | |
6935 | +} | |
6936 | + | |
6937 | +/** | |
76514441 | 6938 | + * __remove_namespace - remove a namespace and all its children |
ceaf2cfb | 6939 | + * @ns: namespace to be removed (NOT NULL) |
76514441 | 6940 | + * |
ceaf2cfb AM |
6941 | + * Requires: ns->parent->lock be held and ns removed from parent. |
6942 | + */ | |
76514441 | 6943 | +static void __remove_namespace(struct aa_namespace *ns) |
9474138d AM |
6944 | +{ |
6945 | + struct aa_profile *unconfined = ns->unconfined; | |
ceaf2cfb AM |
6946 | + |
6947 | + /* remove ns from namespace list */ | |
9474138d | 6948 | + list_del_init(&ns->base.list); |
2380c486 | 6949 | + |
9474138d AM |
6950 | + /* |
6951 | + * break the ns, unconfined profile cyclic reference and forward | |
ceaf2cfb AM |
6952 | + * all new unconfined profiles requests to the parent namespace |
6953 | + * This will result in all confined tasks that have a profile | |
6954 | + * being removed, inheriting the parent->unconfined profile. | |
9474138d | 6955 | + */ |
ceaf2cfb AM |
6956 | + if (ns->parent) |
6957 | + ns->unconfined = aa_get_profile(ns->parent->unconfined); | |
6958 | + | |
76514441 | 6959 | + destroy_namespace(ns); |
ceaf2cfb AM |
6960 | + |
6961 | + /* release original ns->unconfined ref */ | |
9474138d | 6962 | + aa_put_profile(unconfined); |
ceaf2cfb | 6963 | + /* release ns->base.list ref, from removal above */ |
9474138d | 6964 | + aa_put_namespace(ns); |
2380c486 JR |
6965 | +} |
6966 | + | |
6967 | +/** | |
76514441 AM |
6968 | + * __ns_list_release - remove all profile namespaces on the list put refs |
6969 | + * @head: list of profile namespaces (NOT NULL) | |
6970 | + * | |
6971 | + * Requires: namespace lock be held | |
6972 | + */ | |
6973 | +static void __ns_list_release(struct list_head *head) | |
6974 | +{ | |
6975 | + struct aa_namespace *ns, *tmp; | |
6976 | + list_for_each_entry_safe(ns, tmp, head, base.list) | |
6977 | + __remove_namespace(ns); | |
6978 | + | |
6979 | +} | |
6980 | + | |
6981 | +/** | |
ceaf2cfb AM |
6982 | + * aa_alloc_root_ns - allocate the root profile namespace |
6983 | + * | |
6984 | + * Returns: %0 on success else error | |
6985 | + * | |
2380c486 | 6986 | + */ |
ceaf2cfb | 6987 | +int __init aa_alloc_root_ns(void) |
2380c486 | 6988 | +{ |
ceaf2cfb | 6989 | + /* released by aa_free_root_ns - used as list ref*/ |
76514441 | 6990 | + root_ns = alloc_namespace(NULL, "root"); |
ceaf2cfb AM |
6991 | + if (!root_ns) |
6992 | + return -ENOMEM; | |
2380c486 | 6993 | + |
ceaf2cfb AM |
6994 | + return 0; |
6995 | +} | |
2380c486 | 6996 | + |
ceaf2cfb AM |
6997 | + /** |
6998 | + * aa_free_root_ns - free the root profile namespace | |
6999 | + */ | |
76514441 | 7000 | +void __init aa_free_root_ns(void) |
ceaf2cfb AM |
7001 | + { |
7002 | + struct aa_namespace *ns = root_ns; | |
7003 | + root_ns = NULL; | |
76514441 AM |
7004 | + |
7005 | + destroy_namespace(ns); | |
ceaf2cfb | 7006 | + aa_put_namespace(ns); |
2380c486 JR |
7007 | +} |
7008 | + | |
7009 | +/** | |
ceaf2cfb AM |
7010 | + * aa_alloc_profile - allocate, initialize and return a new profile |
7011 | + * @hname: name of the profile (NOT NULL) | |
2380c486 | 7012 | + * |
76514441 | 7013 | + * Returns: refcount profile or NULL on failure |
2380c486 | 7014 | + */ |
ceaf2cfb | 7015 | +struct aa_profile *aa_alloc_profile(const char *hname) |
2380c486 | 7016 | +{ |
9474138d AM |
7017 | + struct aa_profile *profile; |
7018 | + | |
76514441 | 7019 | + /* freed by free_profile - usually through aa_put_profile */ |
9474138d AM |
7020 | + profile = kzalloc(sizeof(*profile), GFP_KERNEL); |
7021 | + if (!profile) | |
7022 | + return NULL; | |
ceaf2cfb | 7023 | + |
76514441 | 7024 | + if (!policy_init(&profile->base, NULL, hname)) { |
ceaf2cfb | 7025 | + kzfree(profile); |
9474138d AM |
7026 | + return NULL; |
7027 | + } | |
ceaf2cfb | 7028 | + |
76514441 | 7029 | + /* refcount released by caller */ |
9474138d | 7030 | + return profile; |
2380c486 JR |
7031 | +} |
7032 | + | |
7033 | +/** | |
9474138d | 7034 | + * aa_new_null_profile - create a new null-X learning profile |
ceaf2cfb | 7035 | + * @parent: profile that caused this profile to be created (NOT NULL) |
9474138d AM |
7036 | + * @hat: true if the null- learning profile is a hat |
7037 | + * | |
7038 | + * Create a null- complain mode profile used in learning mode. The name of | |
7039 | + * the profile is unique and follows the format of parent//null-sid. | |
2380c486 | 7040 | + * |
9474138d AM |
7041 | + * null profiles are added to the profile list but the list does not |
7042 | + * hold a count on them so that they are automatically released when | |
7043 | + * not in use. | |
ceaf2cfb | 7044 | + * |
76514441 | 7045 | + * Returns: new refcounted profile else NULL on failure |
2380c486 | 7046 | + */ |
ceaf2cfb | 7047 | +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) |
2380c486 | 7048 | +{ |
9474138d AM |
7049 | + struct aa_profile *profile = NULL; |
7050 | + char *name; | |
ceaf2cfb | 7051 | + u32 sid = aa_alloc_sid(); |
9474138d | 7052 | + |
ceaf2cfb AM |
7053 | + /* freed below */ |
7054 | + name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); | |
9474138d AM |
7055 | + if (!name) |
7056 | + goto fail; | |
ceaf2cfb | 7057 | + sprintf(name, "%s//null-%x", parent->base.hname, sid); |
9474138d | 7058 | + |
ceaf2cfb | 7059 | + profile = aa_alloc_profile(name); |
9474138d AM |
7060 | + kfree(name); |
7061 | + if (!profile) | |
7062 | + goto fail; | |
7063 | + | |
ceaf2cfb | 7064 | + profile->sid = sid; |
9474138d | 7065 | + profile->mode = APPARMOR_COMPLAIN; |
76514441 | 7066 | + profile->flags = PFLAG_NULL; |
9474138d | 7067 | + if (hat) |
ceaf2cfb | 7068 | + profile->flags |= PFLAG_HAT; |
9474138d | 7069 | + |
76514441 | 7070 | + /* released on free_profile */ |
9474138d | 7071 | + profile->parent = aa_get_profile(parent); |
ceaf2cfb AM |
7072 | + profile->ns = aa_get_namespace(parent->ns); |
7073 | + | |
7074 | + write_lock(&profile->ns->lock); | |
76514441 | 7075 | + __list_add_profile(&parent->base.profiles, profile); |
ceaf2cfb AM |
7076 | + write_unlock(&profile->ns->lock); |
7077 | + | |
76514441 | 7078 | + /* refcount released by caller */ |
ceaf2cfb | 7079 | + return profile; |
9474138d | 7080 | + |
ceaf2cfb AM |
7081 | +fail: |
7082 | + aa_free_sid(sid); | |
7083 | + return NULL; | |
2380c486 JR |
7084 | +} |
7085 | + | |
9474138d | 7086 | +/** |
76514441 | 7087 | + * free_profile - free a profile |
ceaf2cfb | 7088 | + * @profile: the profile to free (MAYBE NULL) |
2380c486 | 7089 | + * |
9474138d AM |
7090 | + * Free a profile, its hats and null_profile. All references to the profile, |
7091 | + * its hats and null_profile must have been put. | |
2380c486 | 7092 | + * |
76514441 | 7093 | + * If the profile was referenced from a task context, free_profile() will |
9474138d | 7094 | + * be called from an rcu callback routine, so we must not sleep here. |
2380c486 | 7095 | + */ |
76514441 | 7096 | +static void free_profile(struct aa_profile *profile) |
9474138d AM |
7097 | +{ |
7098 | + AA_DEBUG("%s(%p)\n", __func__, profile); | |
2380c486 | 7099 | + |
9474138d AM |
7100 | + if (!profile) |
7101 | + return; | |
7102 | + | |
9474138d | 7103 | + if (!list_empty(&profile->base.list)) { |
ceaf2cfb AM |
7104 | + AA_ERROR("%s: internal error, " |
7105 | + "profile '%s' still on ns list\n", | |
7106 | + __func__, profile->base.name); | |
7107 | + BUG(); | |
9474138d AM |
7108 | + } |
7109 | + | |
ceaf2cfb AM |
7110 | + /* free children profiles */ |
7111 | + policy_destroy(&profile->base); | |
7112 | + aa_put_profile(profile->parent); | |
9474138d AM |
7113 | + |
7114 | + aa_put_namespace(profile->ns); | |
76514441 | 7115 | + kzfree(profile->rename); |
9474138d AM |
7116 | + |
7117 | + aa_free_file_rules(&profile->file); | |
7118 | + aa_free_cap_rules(&profile->caps); | |
7119 | + aa_free_net_rules(&profile->net); | |
7120 | + aa_free_rlimit_rules(&profile->rlimits); | |
7121 | + | |
7122 | + aa_free_sid(profile->sid); | |
ceaf2cfb | 7123 | + aa_put_dfa(profile->xmatch); |
9474138d | 7124 | + |
76514441 | 7125 | + aa_put_profile(profile->replacedby); |
9474138d | 7126 | + |
ceaf2cfb | 7127 | + kzfree(profile); |
9474138d AM |
7128 | +} |
7129 | + | |
ceaf2cfb AM |
7130 | +/** |
7131 | + * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) | |
7132 | + * @kr: kref callback for freeing of a profile (NOT NULL) | |
7133 | + */ | |
7134 | +void aa_free_profile_kref(struct kref *kref) | |
7135 | +{ | |
7136 | + struct aa_profile *p = container_of(kref, struct aa_profile, | |
7137 | + base.count); | |
9474138d | 7138 | + |
76514441 | 7139 | + free_profile(p); |
ceaf2cfb | 7140 | +} |
9474138d | 7141 | + |
76514441 | 7142 | +/* TODO: profile accounting - setup in remove */ |
9474138d | 7143 | + |
ceaf2cfb | 7144 | +/** |
76514441 | 7145 | + * __find_child - find a profile on @head list with a name matching @name |
ceaf2cfb AM |
7146 | + * @head: list to search (NOT NULL) |
7147 | + * @name: name of profile (NOT NULL) | |
7148 | + * | |
7149 | + * Requires: ns lock protecting list be held | |
7150 | + * | |
7151 | + * Returns: unrefcounted profile ptr, or NULL if not found | |
7152 | + */ | |
76514441 | 7153 | +static struct aa_profile *__find_child(struct list_head *head, const char *name) |
9474138d | 7154 | +{ |
ceaf2cfb | 7155 | + return (struct aa_profile *)__policy_find(head, name); |
9474138d AM |
7156 | +} |
7157 | + | |
ceaf2cfb | 7158 | +/** |
76514441 | 7159 | + * __strn_find_child - find a profile on @head list using substring of @name |
ceaf2cfb AM |
7160 | + * @head: list to search (NOT NULL) |
7161 | + * @name: name of profile (NOT NULL) | |
7162 | + * @len: length of @name substring to match | |
7163 | + * | |
7164 | + * Requires: ns lock protecting list be held | |
7165 | + * | |
7166 | + * Returns: unrefcounted profile ptr, or NULL if not found | |
7167 | + */ | |
76514441 AM |
7168 | +static struct aa_profile *__strn_find_child(struct list_head *head, |
7169 | + const char *name, int len) | |
9474138d | 7170 | +{ |
ceaf2cfb | 7171 | + return (struct aa_profile *)__policy_strn_find(head, name, len); |
9474138d | 7172 | +} |
2380c486 | 7173 | + |
2380c486 | 7174 | +/** |
9474138d | 7175 | + * aa_find_child - find a profile by @name in @parent |
ceaf2cfb AM |
7176 | + * @parent: profile to search (NOT NULL) |
7177 | + * @name: profile name to search for (NOT NULL) | |
9474138d | 7178 | + * |
76514441 | 7179 | + * Returns: a refcounted profile or NULL if not found |
2380c486 | 7180 | + */ |
9474138d AM |
7181 | +struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) |
7182 | +{ | |
7183 | + struct aa_profile *profile; | |
2380c486 | 7184 | + |
ceaf2cfb | 7185 | + read_lock(&parent->ns->lock); |
76514441 | 7186 | + profile = aa_get_profile(__find_child(&parent->base.profiles, name)); |
ceaf2cfb | 7187 | + read_unlock(&parent->ns->lock); |
2380c486 | 7188 | + |
76514441 | 7189 | + /* refcount released by caller */ |
9474138d AM |
7190 | + return profile; |
7191 | +} | |
2380c486 | 7192 | + |
ceaf2cfb | 7193 | +/** |
76514441 | 7194 | + * __lookup_parent - lookup the parent of a profile of name @hname |
ceaf2cfb AM |
7195 | + * @ns: namespace to lookup profile in (NOT NULL) |
7196 | + * @hname: hierarchical profile name to find parent of (NOT NULL) | |
7197 | + * | |
7198 | + * Lookups up the parent of a fully qualified profile name, the profile | |
7199 | + * that matches hname does not need to exist, in general this | |
7200 | + * is used to load a new profile. | |
7201 | + * | |
7202 | + * Requires: ns->lock be held | |
7203 | + * | |
7204 | + * Returns: unrefcounted policy or NULL if not found | |
7205 | + */ | |
76514441 AM |
7206 | +static struct aa_policy *__lookup_parent(struct aa_namespace *ns, |
7207 | + const char *hname) | |
9474138d | 7208 | +{ |
ceaf2cfb | 7209 | + struct aa_policy *policy; |
9474138d AM |
7210 | + struct aa_profile *profile = NULL; |
7211 | + char *split; | |
7212 | + | |
ceaf2cfb | 7213 | + policy = &ns->base; |
9474138d | 7214 | + |
ceaf2cfb | 7215 | + for (split = strstr(hname, "//"); split;) { |
76514441 AM |
7216 | + profile = __strn_find_child(&policy->profiles, hname, |
7217 | + split - hname); | |
9474138d AM |
7218 | + if (!profile) |
7219 | + return NULL; | |
ceaf2cfb AM |
7220 | + policy = &profile->base; |
7221 | + hname = split + 2; | |
7222 | + split = strstr(hname, "//"); | |
9474138d AM |
7223 | + } |
7224 | + if (!profile) | |
7225 | + return &ns->base; | |
7226 | + return &profile->base; | |
7227 | +} | |
7228 | + | |
ceaf2cfb | 7229 | +/** |
76514441 | 7230 | + * __lookup_profile - lookup the profile matching @hname |
ceaf2cfb AM |
7231 | + * @base: base list to start looking up profile name from (NOT NULL) |
7232 | + * @hname: hierarchical profile name (NOT NULL) | |
7233 | + * | |
7234 | + * Requires: ns->lock be held | |
7235 | + * | |
7236 | + * Returns: unrefcounted profile pointer or NULL if not found | |
7237 | + * | |
7238 | + * Do a relative name lookup, recursing through profile tree. | |
7239 | + */ | |
76514441 AM |
7240 | +static struct aa_profile *__lookup_profile(struct aa_policy *base, |
7241 | + const char *hname) | |
9474138d | 7242 | +{ |
9474138d AM |
7243 | + struct aa_profile *profile = NULL; |
7244 | + char *split; | |
7245 | + | |
ceaf2cfb | 7246 | + for (split = strstr(hname, "//"); split;) { |
76514441 AM |
7247 | + profile = __strn_find_child(&base->profiles, hname, |
7248 | + split - hname); | |
9474138d AM |
7249 | + if (!profile) |
7250 | + return NULL; | |
7251 | + | |
ceaf2cfb AM |
7252 | + base = &profile->base; |
7253 | + hname = split + 2; | |
7254 | + split = strstr(hname, "//"); | |
9474138d AM |
7255 | + } |
7256 | + | |
76514441 | 7257 | + profile = __find_child(&base->profiles, hname); |
2380c486 | 7258 | + |
9474138d AM |
7259 | + return profile; |
7260 | +} | |
2380c486 | 7261 | + |
9474138d | 7262 | +/** |
76514441 AM |
7263 | + * aa_lookup_profile - find a profile by its full or partial name |
7264 | + * @ns: the namespace to start from (NOT NULL) | |
7265 | + * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) | |
ceaf2cfb AM |
7266 | + * |
7267 | + * Returns: refcounted profile or NULL if not found | |
9474138d | 7268 | + */ |
76514441 | 7269 | +struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) |
9474138d AM |
7270 | +{ |
7271 | + struct aa_profile *profile; | |
2380c486 | 7272 | + |
ceaf2cfb | 7273 | + read_lock(&ns->lock); |
76514441 | 7274 | + profile = aa_get_profile(__lookup_profile(&ns->base, hname)); |
ceaf2cfb | 7275 | + read_unlock(&ns->lock); |
76514441 AM |
7276 | + |
7277 | + /* refcount released by caller */ | |
9474138d AM |
7278 | + return profile; |
7279 | +} | |
2380c486 | 7280 | + |
ceaf2cfb AM |
7281 | +/** |
7282 | + * replacement_allowed - test to see if replacement is allowed | |
7283 | + * @profile: profile to test if it can be replaced (MAYBE NULL) | |
76514441 AM |
7284 | + * @noreplace: true if replacement shouldn't be allowed but addition is okay |
7285 | + * @info: Returns - info about why replacement failed (NOT NULL) | |
9474138d | 7286 | + * |
76514441 | 7287 | + * Returns: %0 if replacement allowed else error code |
9474138d | 7288 | + */ |
76514441 AM |
7289 | +static int replacement_allowed(struct aa_profile *profile, int noreplace, |
7290 | + const char **info) | |
9474138d | 7291 | +{ |
ceaf2cfb AM |
7292 | + if (profile) { |
7293 | + if (profile->flags & PFLAG_IMMUTABLE) { | |
76514441 AM |
7294 | + *info = "cannot replace immutible profile"; |
7295 | + return -EPERM; | |
7296 | + } else if (noreplace) { | |
7297 | + *info = "profile already exists"; | |
7298 | + return -EEXIST; | |
ceaf2cfb | 7299 | + } |
9474138d | 7300 | + } |
76514441 | 7301 | + return 0; |
ceaf2cfb | 7302 | +} |
2380c486 | 7303 | + |
ceaf2cfb | 7304 | +/** |
76514441 | 7305 | + * __add_new_profile - simple wrapper around __list_add_profile |
ceaf2cfb AM |
7306 | + * @ns: namespace that profile is being added to (NOT NULL) |
7307 | + * @policy: the policy container to add the profile to (NOT NULL) | |
7308 | + * @profile: profile to add (NOT NULL) | |
7309 | + * | |
7310 | + * add a profile to a list and do other required basic allocations | |
7311 | + */ | |
76514441 | 7312 | +static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, |
ceaf2cfb AM |
7313 | + struct aa_profile *profile) |
7314 | +{ | |
7315 | + if (policy != &ns->base) | |
76514441 | 7316 | + /* released on profile replacement or free_profile */ |
ceaf2cfb | 7317 | + profile->parent = aa_get_profile((struct aa_profile *) policy); |
76514441 AM |
7318 | + __list_add_profile(&policy->profiles, profile); |
7319 | + /* released on free_profile */ | |
ceaf2cfb AM |
7320 | + profile->sid = aa_alloc_sid(); |
7321 | + profile->ns = aa_get_namespace(ns); | |
9474138d AM |
7322 | +} |
7323 | + | |
7324 | +/** | |
76514441 AM |
7325 | + * aa_audit_policy - Do auditing of policy changes |
7326 | + * @op: policy operation being performed | |
7327 | + * @gfp: memory allocation flags | |
7328 | + * @name: name of profile being manipulated (NOT NULL) | |
7329 | + * @info: any extra information to be audited (MAYBE NULL) | |
7330 | + * @error: error code | |
7331 | + * | |
7332 | + * Returns: the error to be returned after audit is done | |
7333 | + */ | |
7334 | +static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, | |
7335 | + int error) | |
7336 | +{ | |
7337 | + struct common_audit_data sa; | |
7338 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
7339 | + sa.aad.op = op; | |
7340 | + sa.aad.name = name; | |
7341 | + sa.aad.info = info; | |
7342 | + sa.aad.error = error; | |
7343 | + | |
7344 | + return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, | |
7345 | + &sa, NULL); | |
7346 | +} | |
7347 | + | |
7348 | +/** | |
7349 | + * aa_may_manage_policy - can the current task manage policy | |
7350 | + * @op: the policy manipulation operation being done | |
7351 | + * | |
7352 | + * Returns: true if the task is allowed to manipulate policy | |
7353 | + */ | |
7354 | +bool aa_may_manage_policy(int op) | |
7355 | +{ | |
7356 | + /* check if loading policy is locked out */ | |
7357 | + if (aa_g_lock_policy) { | |
7358 | + audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); | |
7359 | + return 0; | |
7360 | + } | |
7361 | + | |
7362 | + if (!capable(CAP_MAC_ADMIN)) { | |
7363 | + audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); | |
7364 | + return 0; | |
7365 | + } | |
7366 | + | |
7367 | + return 1; | |
7368 | +} | |
7369 | + | |
7370 | +/** | |
7371 | + * aa_replace_profiles - replace profile(s) on the profile list | |
ceaf2cfb AM |
7372 | + * @udata: serialized data stream (NOT NULL) |
7373 | + * @size: size of the serialized data stream | |
76514441 | 7374 | + * @noreplace: true if only doing addition, no replacement allowed |
ceaf2cfb AM |
7375 | + * |
7376 | + * unpack and replace a profile on the profile list and uses of that profile | |
7377 | + * by any aa_task_cxt. If the profile does not exist on the profile list | |
7378 | + * it is added. | |
7379 | + * | |
7380 | + * Returns: size of data consumed else error code on failure. | |
9474138d | 7381 | + */ |
76514441 | 7382 | +ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) |
2380c486 | 7383 | +{ |
ceaf2cfb AM |
7384 | + struct aa_policy *policy; |
7385 | + struct aa_profile *old_profile = NULL, *new_profile = NULL; | |
7386 | + struct aa_profile *rename_profile = NULL; | |
76514441 AM |
7387 | + struct aa_namespace *ns = NULL; |
7388 | + const char *ns_name, *name = NULL, *info = NULL; | |
7389 | + int op = OP_PROF_REPL; | |
ceaf2cfb | 7390 | + ssize_t error; |
9474138d | 7391 | + |
ceaf2cfb | 7392 | + /* released below */ |
76514441 | 7393 | + new_profile = aa_unpack(udata, size, &ns_name); |
ceaf2cfb | 7394 | + if (IS_ERR(new_profile)) { |
76514441 AM |
7395 | + error = PTR_ERR(new_profile); |
7396 | + new_profile = NULL; | |
ceaf2cfb AM |
7397 | + goto fail; |
7398 | + } | |
7399 | + | |
7400 | + /* released below */ | |
76514441 | 7401 | + ns = aa_prepare_namespace(ns_name); |
ceaf2cfb | 7402 | + if (!ns) { |
76514441 AM |
7403 | + info = "failed to prepare namespace"; |
7404 | + error = -ENOMEM; | |
7405 | + name = ns_name; | |
ceaf2cfb AM |
7406 | + goto fail; |
7407 | + } | |
7408 | + | |
76514441 | 7409 | + name = new_profile->base.hname; |
ceaf2cfb AM |
7410 | + |
7411 | + write_lock(&ns->lock); | |
7412 | + /* no ref on policy only use inside lock */ | |
76514441 | 7413 | + policy = __lookup_parent(ns, new_profile->base.hname); |
ceaf2cfb AM |
7414 | + |
7415 | + if (!policy) { | |
76514441 AM |
7416 | + info = "parent does not exist"; |
7417 | + error = -ENOENT; | |
ceaf2cfb AM |
7418 | + goto audit; |
7419 | + } | |
7420 | + | |
76514441 | 7421 | + old_profile = __find_child(&policy->profiles, new_profile->base.name); |
ceaf2cfb AM |
7422 | + /* released below */ |
7423 | + aa_get_profile(old_profile); | |
7424 | + | |
7425 | + if (new_profile->rename) { | |
76514441 AM |
7426 | + rename_profile = __lookup_profile(&ns->base, |
7427 | + new_profile->rename); | |
ceaf2cfb AM |
7428 | + /* released below */ |
7429 | + aa_get_profile(rename_profile); | |
7430 | + | |
7431 | + if (!rename_profile) { | |
76514441 AM |
7432 | + info = "profile to rename does not exist"; |
7433 | + name = new_profile->rename; | |
7434 | + error = -ENOENT; | |
ceaf2cfb AM |
7435 | + goto audit; |
7436 | + } | |
7437 | + } | |
7438 | + | |
76514441 AM |
7439 | + error = replacement_allowed(old_profile, noreplace, &info); |
7440 | + if (error) | |
ceaf2cfb AM |
7441 | + goto audit; |
7442 | + | |
76514441 AM |
7443 | + error = replacement_allowed(rename_profile, noreplace, &info); |
7444 | + if (error) | |
ceaf2cfb AM |
7445 | + goto audit; |
7446 | + | |
7447 | +audit: | |
7448 | + if (!old_profile && !rename_profile) | |
76514441 | 7449 | + op = OP_PROF_LOAD; |
ceaf2cfb | 7450 | + |
76514441 | 7451 | + error = audit_policy(op, GFP_ATOMIC, name, info, error); |
ceaf2cfb AM |
7452 | + |
7453 | + if (!error) { | |
ceaf2cfb | 7454 | + if (rename_profile) |
76514441 AM |
7455 | + __replace_profile(rename_profile, new_profile); |
7456 | + if (old_profile) { | |
7457 | + /* when there are both rename and old profiles | |
7458 | + * inherit old profiles sid | |
7459 | + */ | |
7460 | + if (rename_profile) | |
7461 | + aa_free_sid(new_profile->sid); | |
7462 | + __replace_profile(old_profile, new_profile); | |
7463 | + } | |
ceaf2cfb AM |
7464 | + if (!(old_profile || rename_profile)) |
7465 | + __add_new_profile(ns, policy, new_profile); | |
7466 | + } | |
7467 | + write_unlock(&ns->lock); | |
7468 | + | |
7469 | +out: | |
7470 | + aa_put_namespace(ns); | |
7471 | + aa_put_profile(rename_profile); | |
7472 | + aa_put_profile(old_profile); | |
7473 | + aa_put_profile(new_profile); | |
7474 | + if (error) | |
7475 | + return error; | |
7476 | + return size; | |
7477 | + | |
7478 | +fail: | |
76514441 | 7479 | + error = audit_policy(op, GFP_KERNEL, name, info, error); |
ceaf2cfb | 7480 | + goto out; |
9474138d AM |
7481 | +} |
7482 | + | |
7483 | +/** | |
76514441 | 7484 | + * aa_remove_profiles - remove profile(s) from the system |
ceaf2cfb AM |
7485 | + * @fqname: name of the profile or namespace to remove (NOT NULL) |
7486 | + * @size: size of the name | |
9474138d | 7487 | + * |
ceaf2cfb AM |
7488 | + * Remove a profile or sub namespace from the current namespace, so that |
7489 | + * they can not be found anymore and mark them as replaced by unconfined | |
9474138d | 7490 | + * |
ceaf2cfb AM |
7491 | + * NOTE: removing confinement does not restore rlimits to preconfinemnet values |
7492 | + * | |
7493 | + * Returns: size of data consume else error code if fails | |
9474138d | 7494 | + */ |
76514441 | 7495 | +ssize_t aa_remove_profiles(char *fqname, size_t size) |
9474138d | 7496 | +{ |
ceaf2cfb AM |
7497 | + struct aa_namespace *root, *ns = NULL; |
7498 | + struct aa_profile *profile = NULL; | |
76514441 AM |
7499 | + const char *name = fqname, *info = NULL; |
7500 | + ssize_t error = 0; | |
9474138d | 7501 | + |
ceaf2cfb | 7502 | + if (*fqname == 0) { |
76514441 AM |
7503 | + info = "no profile specified"; |
7504 | + error = -ENOENT; | |
ceaf2cfb AM |
7505 | + goto fail; |
7506 | + } | |
7507 | + | |
ceaf2cfb AM |
7508 | + root = aa_current_profile()->ns; |
7509 | + | |
7510 | + if (fqname[0] == ':') { | |
7511 | + char *ns_name; | |
7512 | + name = aa_split_fqname(fqname, &ns_name); | |
76514441 | 7513 | + if (ns_name) { |
ceaf2cfb AM |
7514 | + /* released below */ |
7515 | + ns = aa_find_namespace(root, ns_name); | |
76514441 AM |
7516 | + if (!ns) { |
7517 | + info = "namespace does not exist"; | |
7518 | + error = -ENOENT; | |
7519 | + goto fail; | |
7520 | + } | |
7521 | + } | |
ceaf2cfb AM |
7522 | + } else |
7523 | + /* released below */ | |
7524 | + ns = aa_get_namespace(root); | |
7525 | + | |
ceaf2cfb AM |
7526 | + write_lock(&ns->lock); |
7527 | + if (!name) { | |
7528 | + /* remove namespace - can only happen if fqname[0] == ':' */ | |
76514441 | 7529 | + __remove_namespace(ns); |
ceaf2cfb AM |
7530 | + } else { |
7531 | + /* remove profile */ | |
76514441 | 7532 | + profile = aa_get_profile(__lookup_profile(&ns->base, name)); |
ceaf2cfb | 7533 | + if (!profile) { |
76514441 AM |
7534 | + error = -ENOENT; |
7535 | + info = "profile does not exist"; | |
ceaf2cfb AM |
7536 | + goto fail_ns_lock; |
7537 | + } | |
76514441 AM |
7538 | + name = profile->base.hname; |
7539 | + __remove_profile(profile); | |
ceaf2cfb AM |
7540 | + } |
7541 | + write_unlock(&ns->lock); | |
2380c486 | 7542 | + |
ceaf2cfb | 7543 | + /* don't fail removal if audit fails */ |
76514441 | 7544 | + (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); |
ceaf2cfb AM |
7545 | + aa_put_namespace(ns); |
7546 | + aa_put_profile(profile); | |
7547 | + return size; | |
7548 | + | |
7549 | +fail_ns_lock: | |
7550 | + write_unlock(&ns->lock); | |
7551 | + aa_put_namespace(ns); | |
7552 | + | |
7553 | +fail: | |
76514441 | 7554 | + (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); |
ceaf2cfb AM |
7555 | + return error; |
7556 | +} | |
7557 | diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c | |
7558 | new file mode 100644 | |
76514441 | 7559 | index 0000000..6b0637b |
ceaf2cfb AM |
7560 | --- /dev/null |
7561 | +++ b/security/apparmor/policy_unpack.c | |
76514441 | 7562 | @@ -0,0 +1,740 @@ |
2380c486 | 7563 | +/* |
9474138d AM |
7564 | + * AppArmor security module |
7565 | + * | |
7566 | + * This file contains AppArmor functions for unpacking policy loaded from | |
7567 | + * userspace. | |
2380c486 | 7568 | + * |
9474138d | 7569 | + * Copyright (C) 1998-2008 Novell/SUSE |
ceaf2cfb | 7570 | + * Copyright 2009-2010 Canonical Ltd. |
2380c486 | 7571 | + * |
9474138d AM |
7572 | + * This program is free software; you can redistribute it and/or |
7573 | + * modify it under the terms of the GNU General Public License as | |
7574 | + * published by the Free Software Foundation, version 2 of the | |
7575 | + * License. | |
7576 | + * | |
7577 | + * AppArmor uses a serialized binary format for loading policy. | |
76514441 AM |
7578 | + * To find policy format documentation look in Documentation/apparmor.txt |
7579 | + * All policy is validated before it is used. | |
2380c486 JR |
7580 | + */ |
7581 | + | |
7582 | +#include <asm/unaligned.h> | |
ceaf2cfb | 7583 | +#include <linux/ctype.h> |
9474138d | 7584 | +#include <linux/errno.h> |
2380c486 | 7585 | + |
9474138d AM |
7586 | +#include "include/apparmor.h" |
7587 | +#include "include/audit.h" | |
7588 | +#include "include/context.h" | |
7589 | +#include "include/match.h" | |
7590 | +#include "include/policy.h" | |
ceaf2cfb | 7591 | +#include "include/policy_unpack.h" |
9474138d AM |
7592 | +#include "include/sid.h" |
7593 | + | |
2380c486 JR |
7594 | +/* |
7595 | + * The AppArmor interface treats data as a type byte followed by the | |
7596 | + * actual data. The interface has the notion of a a named entry | |
7597 | + * which has a name (AA_NAME typecode followed by name string) followed by | |
7598 | + * the entries typecode and data. Named types allow for optional | |
7599 | + * elements and extensions to be added and tested for without breaking | |
76514441 | 7600 | + * backwards compatibility. |
2380c486 JR |
7601 | + */ |
7602 | + | |
7603 | +enum aa_code { | |
7604 | + AA_U8, | |
7605 | + AA_U16, | |
7606 | + AA_U32, | |
7607 | + AA_U64, | |
ceaf2cfb | 7608 | + AA_NAME, /* same as string except it is items name */ |
2380c486 JR |
7609 | + AA_STRING, |
7610 | + AA_BLOB, | |
7611 | + AA_STRUCT, | |
7612 | + AA_STRUCTEND, | |
7613 | + AA_LIST, | |
7614 | + AA_LISTEND, | |
7615 | + AA_ARRAY, | |
7616 | + AA_ARRAYEND, | |
7617 | +}; | |
7618 | + | |
7619 | +/* | |
7620 | + * aa_ext is the read of the buffer containing the serialized profile. The | |
7621 | + * data is copied into a kernel buffer in apparmorfs and then handed off to | |
7622 | + * the unpack routines. | |
7623 | + */ | |
7624 | +struct aa_ext { | |
7625 | + void *start; | |
7626 | + void *end; | |
ceaf2cfb | 7627 | + void *pos; /* pointer to current position in the buffer */ |
2380c486 | 7628 | + u32 version; |
9474138d AM |
7629 | +}; |
7630 | + | |
ceaf2cfb | 7631 | +/* audit callback for unpack fields */ |
76514441 | 7632 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 7633 | +{ |
76514441 AM |
7634 | + struct common_audit_data *sa = va; |
7635 | + if (sa->aad.iface.target) { | |
7636 | + struct aa_profile *name = sa->aad.iface.target; | |
ceaf2cfb | 7637 | + audit_log_format(ab, " name="); |
76514441 | 7638 | + audit_log_untrustedstring(ab, name->base.hname); |
5e665963 | 7639 | + } |
76514441 AM |
7640 | + if (sa->aad.iface.pos) |
7641 | + audit_log_format(ab, " offset=%ld", sa->aad.iface.pos); | |
9474138d AM |
7642 | +} |
7643 | + | |
ceaf2cfb | 7644 | +/** |
76514441 AM |
7645 | + * audit_iface - do audit message for policy unpacking/load/replace/remove |
7646 | + * @new: profile if it has been allocated (MAYBE NULL) | |
7647 | + * @name: name of the profile being manipulated (MAYBE NULL) | |
7648 | + * @info: any extra info about the failure (MAYBE NULL) | |
7649 | + * @e: buffer position info (NOT NULL) | |
7650 | + * @error: error code | |
ceaf2cfb AM |
7651 | + * |
7652 | + * Returns: %0 or error | |
7653 | + */ | |
76514441 AM |
7654 | +static int audit_iface(struct aa_profile *new, const char *name, |
7655 | + const char *info, struct aa_ext *e, int error) | |
9474138d | 7656 | +{ |
76514441 AM |
7657 | + struct aa_profile *profile = __aa_current_profile(); |
7658 | + struct common_audit_data sa; | |
7659 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
7660 | + sa.aad.iface.pos = e->pos - e->start; | |
7661 | + sa.aad.iface.target = new; | |
7662 | + sa.aad.name = name; | |
7663 | + sa.aad.info = info; | |
7664 | + sa.aad.error = error; | |
7665 | + | |
7666 | + return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, | |
7667 | + audit_cb); | |
9474138d AM |
7668 | +} |
7669 | + | |
ceaf2cfb | 7670 | +/* test if read will be in packed data bounds */ |
76514441 | 7671 | +static bool inbounds(struct aa_ext *e, size_t size) |
2380c486 JR |
7672 | +{ |
7673 | + return (size <= e->end - e->pos); | |
7674 | +} | |
7675 | + | |
7676 | +/** | |
7677 | + * aa_u16_chunck - test and do bounds checking for a u16 size based chunk | |
76514441 AM |
7678 | + * @e: serialized data read head (NOT NULL) |
7679 | + * @chunk: start address for chunk of data (NOT NULL) | |
2380c486 | 7680 | + * |
ceaf2cfb | 7681 | + * Returns: the size of chunk found with the read head at the end of the chunk. |
2380c486 | 7682 | + */ |
ceaf2cfb | 7683 | +static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) |
2380c486 | 7684 | +{ |
2380c486 JR |
7685 | + size_t size = 0; |
7686 | + | |
76514441 | 7687 | + if (!inbounds(e, sizeof(u16))) |
ceaf2cfb AM |
7688 | + return 0; |
7689 | + size = le16_to_cpu(get_unaligned((u16 *) e->pos)); | |
2380c486 | 7690 | + e->pos += sizeof(u16); |
76514441 | 7691 | + if (!inbounds(e, size)) |
ceaf2cfb | 7692 | + return 0; |
2380c486 JR |
7693 | + *chunk = e->pos; |
7694 | + e->pos += size; | |
7695 | + return size; | |
2380c486 JR |
7696 | +} |
7697 | + | |
ceaf2cfb AM |
7698 | +/* unpack control byte */ |
7699 | +static bool unpack_X(struct aa_ext *e, enum aa_code code) | |
2380c486 | 7700 | +{ |
76514441 | 7701 | + if (!inbounds(e, 1)) |
2380c486 JR |
7702 | + return 0; |
7703 | + if (*(u8 *) e->pos != code) | |
7704 | + return 0; | |
7705 | + e->pos++; | |
7706 | + return 1; | |
7707 | +} | |
7708 | + | |
7709 | +/** | |
ceaf2cfb AM |
7710 | + * unpack_nameX - check is the next element is of type X with a name of @name |
7711 | + * @e: serialized data extent information (NOT NULL) | |
2380c486 | 7712 | + * @code: type code |
ceaf2cfb | 7713 | + * @name: name to match to the serialized element. (MAYBE NULL) |
2380c486 JR |
7714 | + * |
7715 | + * check that the next serialized data element is of type X and has a tag | |
7716 | + * name @name. If @name is specified then there must be a matching | |
7717 | + * name element in the stream. If @name is NULL any name element will be | |
7718 | + * skipped and only the typecode will be tested. | |
76514441 AM |
7719 | + * |
7720 | + * Returns 1 on success (both type code and name tests match) and the read | |
2380c486 | 7721 | + * head is advanced past the headers |
ceaf2cfb | 7722 | + * |
76514441 | 7723 | + * Returns: 0 if either match fails, the read head does not move |
2380c486 | 7724 | + */ |
ceaf2cfb | 7725 | +static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) |
2380c486 | 7726 | +{ |
ceaf2cfb AM |
7727 | + /* |
7728 | + * May need to reset pos if name or type doesn't match | |
7729 | + */ | |
2380c486 JR |
7730 | + void *pos = e->pos; |
7731 | + /* | |
7732 | + * Check for presence of a tagname, and if present name size | |
7733 | + * AA_NAME tag value is a u16. | |
7734 | + */ | |
ceaf2cfb | 7735 | + if (unpack_X(e, AA_NAME)) { |
9474138d | 7736 | + char *tag = NULL; |
ceaf2cfb | 7737 | + size_t size = unpack_u16_chunk(e, &tag); |
2380c486 JR |
7738 | + /* if a name is specified it must match. otherwise skip tag */ |
7739 | + if (name && (!size || strcmp(name, tag))) | |
7740 | + goto fail; | |
7741 | + } else if (name) { | |
7742 | + /* if a name is specified and there is no name tag fail */ | |
7743 | + goto fail; | |
7744 | + } | |
7745 | + | |
7746 | + /* now check if type code matches */ | |
ceaf2cfb | 7747 | + if (unpack_X(e, code)) |
2380c486 JR |
7748 | + return 1; |
7749 | + | |
7750 | +fail: | |
7751 | + e->pos = pos; | |
7752 | + return 0; | |
7753 | +} | |
7754 | + | |
ceaf2cfb | 7755 | +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) |
2380c486 | 7756 | +{ |
ceaf2cfb | 7757 | + if (unpack_nameX(e, AA_U16, name)) { |
76514441 | 7758 | + if (!inbounds(e, sizeof(u16))) |
ceaf2cfb | 7759 | + return 0; |
2380c486 | 7760 | + if (data) |
ceaf2cfb | 7761 | + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); |
2380c486 JR |
7762 | + e->pos += sizeof(u16); |
7763 | + return 1; | |
7764 | + } | |
2380c486 JR |
7765 | + return 0; |
7766 | +} | |
7767 | + | |
ceaf2cfb | 7768 | +static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
2380c486 | 7769 | +{ |
ceaf2cfb | 7770 | + if (unpack_nameX(e, AA_U32, name)) { |
76514441 | 7771 | + if (!inbounds(e, sizeof(u32))) |
ceaf2cfb | 7772 | + return 0; |
2380c486 | 7773 | + if (data) |
ceaf2cfb | 7774 | + *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); |
2380c486 JR |
7775 | + e->pos += sizeof(u32); |
7776 | + return 1; | |
7777 | + } | |
2380c486 JR |
7778 | + return 0; |
7779 | +} | |
7780 | + | |
ceaf2cfb | 7781 | +static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) |
2380c486 | 7782 | +{ |
ceaf2cfb | 7783 | + if (unpack_nameX(e, AA_U64, name)) { |
76514441 | 7784 | + if (!inbounds(e, sizeof(u64))) |
ceaf2cfb | 7785 | + return 0; |
2380c486 | 7786 | + if (data) |
ceaf2cfb | 7787 | + *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); |
2380c486 JR |
7788 | + e->pos += sizeof(u64); |
7789 | + return 1; | |
7790 | + } | |
2380c486 JR |
7791 | + return 0; |
7792 | +} | |
7793 | + | |
ceaf2cfb | 7794 | +static size_t unpack_array(struct aa_ext *e, const char *name) |
2380c486 | 7795 | +{ |
ceaf2cfb | 7796 | + if (unpack_nameX(e, AA_ARRAY, name)) { |
2380c486 | 7797 | + int size; |
76514441 | 7798 | + if (!inbounds(e, sizeof(u16))) |
ceaf2cfb AM |
7799 | + return 0; |
7800 | + size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); | |
2380c486 JR |
7801 | + e->pos += sizeof(u16); |
7802 | + return size; | |
7803 | + } | |
2380c486 JR |
7804 | + return 0; |
7805 | +} | |
7806 | + | |
ceaf2cfb | 7807 | +static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) |
2380c486 | 7808 | +{ |
ceaf2cfb | 7809 | + if (unpack_nameX(e, AA_BLOB, name)) { |
2380c486 | 7810 | + u32 size; |
76514441 | 7811 | + if (!inbounds(e, sizeof(u32))) |
ceaf2cfb AM |
7812 | + return 0; |
7813 | + size = le32_to_cpu(get_unaligned((u32 *) e->pos)); | |
2380c486 | 7814 | + e->pos += sizeof(u32); |
76514441 | 7815 | + if (inbounds(e, (size_t) size)) { |
9474138d | 7816 | + *blob = e->pos; |
2380c486 JR |
7817 | + e->pos += size; |
7818 | + return size; | |
7819 | + } | |
7820 | + } | |
2380c486 JR |
7821 | + return 0; |
7822 | +} | |
7823 | + | |
ceaf2cfb | 7824 | +static int unpack_str(struct aa_ext *e, const char **string, const char *name) |
2380c486 JR |
7825 | +{ |
7826 | + char *src_str; | |
7827 | + size_t size = 0; | |
7828 | + void *pos = e->pos; | |
7829 | + *string = NULL; | |
ceaf2cfb AM |
7830 | + if (unpack_nameX(e, AA_STRING, name)) { |
7831 | + size = unpack_u16_chunk(e, &src_str); | |
7832 | + if (size) { | |
7833 | + /* strings are null terminated, length is size - 1 */ | |
7834 | + if (src_str[size - 1] != 0) | |
7835 | + goto fail; | |
7836 | + *string = src_str; | |
7837 | + } | |
2380c486 | 7838 | + } |
2380c486 JR |
7839 | + return size; |
7840 | + | |
7841 | +fail: | |
7842 | + e->pos = pos; | |
7843 | + return 0; | |
7844 | +} | |
7845 | + | |
ceaf2cfb | 7846 | +static int unpack_strdup(struct aa_ext *e, char **string, const char *name) |
9474138d | 7847 | +{ |
ceaf2cfb | 7848 | + const char *tmp; |
9474138d | 7849 | + void *pos = e->pos; |
ceaf2cfb | 7850 | + int res = unpack_str(e, &tmp, name); |
9474138d AM |
7851 | + *string = NULL; |
7852 | + | |
7853 | + if (!res) | |
ceaf2cfb | 7854 | + return 0; |
9474138d | 7855 | + |
ceaf2cfb | 7856 | + *string = kmemdup(tmp, res, GFP_KERNEL); |
9474138d AM |
7857 | + if (!*string) { |
7858 | + e->pos = pos; | |
7859 | + return 0; | |
7860 | + } | |
7861 | + | |
7862 | + return res; | |
7863 | +} | |
7864 | + | |
76514441 AM |
7865 | +/** |
7866 | + * verify_accept - verify the accept tables of a dfa | |
7867 | + * @dfa: dfa to verify accept tables of (NOT NULL) | |
7868 | + * @flags: flags governing dfa | |
7869 | + * | |
7870 | + * Returns: 1 if valid accept tables else 0 if error | |
7871 | + */ | |
ceaf2cfb AM |
7872 | +static bool verify_accept(struct aa_dfa *dfa, int flags) |
7873 | +{ | |
7874 | + int i; | |
7875 | + | |
7876 | + /* verify accept permissions */ | |
7877 | + for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | |
7878 | + int mode = ACCEPT_TABLE(dfa)[i]; | |
7879 | + | |
7880 | + if (mode & ~DFA_VALID_PERM_MASK) | |
7881 | + return 0; | |
7882 | + | |
7883 | + if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) | |
7884 | + return 0; | |
7885 | + } | |
7886 | + return 1; | |
7887 | +} | |
7888 | + | |
2380c486 | 7889 | +/** |
ceaf2cfb | 7890 | + * unpack_dfa - unpack a file rule dfa |
76514441 | 7891 | + * @e: serialized data extent information (NOT NULL) |
2380c486 | 7892 | + * |
ceaf2cfb | 7893 | + * returns dfa or ERR_PTR or NULL if no dfa |
2380c486 | 7894 | + */ |
ceaf2cfb | 7895 | +static struct aa_dfa *unpack_dfa(struct aa_ext *e) |
2380c486 JR |
7896 | +{ |
7897 | + char *blob = NULL; | |
ceaf2cfb | 7898 | + size_t size; |
2380c486 JR |
7899 | + struct aa_dfa *dfa = NULL; |
7900 | + | |
ceaf2cfb | 7901 | + size = unpack_blob(e, &blob, "aadfa"); |
2380c486 | 7902 | + if (size) { |
ceaf2cfb AM |
7903 | + /* |
7904 | + * The dfa is aligned with in the blob to 8 bytes | |
7905 | + * from the beginning of the stream. | |
7906 | + */ | |
7907 | + size_t sz = blob - (char *)e->start; | |
7908 | + size_t pad = ALIGN(sz, 8) - sz; | |
7909 | + int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | |
7910 | + TO_ACCEPT2_FLAG(YYTD_DATA32); | |
2380c486 | 7911 | + |
ceaf2cfb AM |
7912 | + |
7913 | + if (aa_g_paranoid_load) | |
7914 | + flags |= DFA_FLAG_VERIFY_STATES; | |
7915 | + | |
7916 | + dfa = aa_dfa_unpack(blob + pad, size - pad, flags); | |
76514441 AM |
7917 | + |
7918 | + if (IS_ERR(dfa)) | |
7919 | + return dfa; | |
7920 | + | |
7921 | + if (!verify_accept(dfa, flags)) | |
ceaf2cfb | 7922 | + goto fail; |
2380c486 JR |
7923 | + } |
7924 | + | |
7925 | + return dfa; | |
ceaf2cfb AM |
7926 | + |
7927 | +fail: | |
7928 | + aa_put_dfa(dfa); | |
7929 | + return ERR_PTR(-EPROTO); | |
2380c486 JR |
7930 | +} |
7931 | + | |
76514441 AM |
7932 | +/** |
7933 | + * unpack_trans_table - unpack a profile transition table | |
7934 | + * @e: serialized data extent information (NOT NULL) | |
7935 | + * @profile: profile to add the accept table to (NOT NULL) | |
7936 | + * | |
7937 | + * Returns: 1 if table succesfully unpacked | |
7938 | + */ | |
ceaf2cfb | 7939 | +static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) |
2380c486 JR |
7940 | +{ |
7941 | + void *pos = e->pos; | |
7942 | + | |
7943 | + /* exec table is optional */ | |
ceaf2cfb | 7944 | + if (unpack_nameX(e, AA_STRUCT, "xtable")) { |
2380c486 JR |
7945 | + int i, size; |
7946 | + | |
ceaf2cfb | 7947 | + size = unpack_array(e, NULL); |
2380c486 JR |
7948 | + /* currently 4 exec bits and entries 0-3 are reserved iupcx */ |
7949 | + if (size > 16 - 4) | |
7950 | + goto fail; | |
9474138d | 7951 | + profile->file.trans.table = kzalloc(sizeof(char *) * size, |
ceaf2cfb | 7952 | + GFP_KERNEL); |
9474138d | 7953 | + if (!profile->file.trans.table) |
2380c486 JR |
7954 | + goto fail; |
7955 | + | |
ceaf2cfb | 7956 | + profile->file.trans.size = size; |
2380c486 | 7957 | + for (i = 0; i < size; i++) { |
ceaf2cfb AM |
7958 | + char *str; |
7959 | + int c, j, size = unpack_strdup(e, &str, NULL); | |
76514441 AM |
7960 | + /* unpack_strdup verifies that the last character is |
7961 | + * null termination byte. | |
7962 | + */ | |
ceaf2cfb AM |
7963 | + if (!size) |
7964 | + goto fail; | |
7965 | + profile->file.trans.table[i] = str; | |
7966 | + /* verify that name doesn't start with space */ | |
7967 | + if (isspace(*str)) | |
7968 | + goto fail; | |
7969 | + | |
7970 | + /* count internal # of internal \0 */ | |
7971 | + for (c = j = 0; j < size - 2; j++) { | |
7972 | + if (!str[j]) | |
7973 | + c++; | |
7974 | + } | |
7975 | + if (*str == ':') { | |
76514441 AM |
7976 | + /* beginning with : requires an embedded \0, |
7977 | + * verify that exactly 1 internal \0 exists | |
7978 | + * trailing \0 already verified by unpack_strdup | |
7979 | + */ | |
ceaf2cfb AM |
7980 | + if (c != 1) |
7981 | + goto fail; | |
7982 | + /* first character after : must be valid */ | |
7983 | + if (!str[1]) | |
7984 | + goto fail; | |
7985 | + } else if (c) | |
7986 | + /* fail - all other cases with embedded \0 */ | |
2380c486 | 7987 | + goto fail; |
2380c486 | 7988 | + } |
ceaf2cfb | 7989 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
2380c486 | 7990 | + goto fail; |
ceaf2cfb | 7991 | + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
2380c486 JR |
7992 | + goto fail; |
7993 | + } | |
7994 | + return 1; | |
7995 | + | |
7996 | +fail: | |
ceaf2cfb | 7997 | + aa_free_domain_entries(&profile->file.trans); |
2380c486 JR |
7998 | + e->pos = pos; |
7999 | + return 0; | |
8000 | +} | |
8001 | + | |
ceaf2cfb | 8002 | +static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) |
2380c486 JR |
8003 | +{ |
8004 | + void *pos = e->pos; | |
8005 | + | |
8006 | + /* rlimits are optional */ | |
ceaf2cfb | 8007 | + if (unpack_nameX(e, AA_STRUCT, "rlimits")) { |
2380c486 JR |
8008 | + int i, size; |
8009 | + u32 tmp = 0; | |
ceaf2cfb | 8010 | + if (!unpack_u32(e, &tmp, NULL)) |
2380c486 JR |
8011 | + goto fail; |
8012 | + profile->rlimits.mask = tmp; | |
8013 | + | |
ceaf2cfb | 8014 | + size = unpack_array(e, NULL); |
2380c486 JR |
8015 | + if (size > RLIM_NLIMITS) |
8016 | + goto fail; | |
8017 | + for (i = 0; i < size; i++) { | |
8018 | + u64 tmp = 0; | |
76514441 | 8019 | + int a = aa_map_resource(i); |
ceaf2cfb | 8020 | + if (!unpack_u64(e, &tmp, NULL)) |
2380c486 | 8021 | + goto fail; |
76514441 | 8022 | + profile->rlimits.limits[a].rlim_max = tmp; |
2380c486 | 8023 | + } |
ceaf2cfb | 8024 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
2380c486 | 8025 | + goto fail; |
ceaf2cfb | 8026 | + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
2380c486 JR |
8027 | + goto fail; |
8028 | + } | |
8029 | + return 1; | |
8030 | + | |
8031 | +fail: | |
8032 | + e->pos = pos; | |
8033 | + return 0; | |
8034 | +} | |
8035 | + | |
8036 | +/** | |
ceaf2cfb | 8037 | + * unpack_profile - unpack a serialized profile |
76514441 | 8038 | + * @e: serialized data extent information (NOT NULL) |
ceaf2cfb AM |
8039 | + * |
8040 | + * NOTE: unpack profile sets audit struct if there is a failure | |
2380c486 | 8041 | + */ |
76514441 | 8042 | +static struct aa_profile *unpack_profile(struct aa_ext *e) |
2380c486 JR |
8043 | +{ |
8044 | + struct aa_profile *profile = NULL; | |
ceaf2cfb | 8045 | + const char *name = NULL; |
2380c486 JR |
8046 | + size_t size = 0; |
8047 | + int i, error = -EPROTO; | |
ceaf2cfb | 8048 | + kernel_cap_t tmpcap; |
9474138d AM |
8049 | + u32 tmp; |
8050 | + | |
8051 | + /* check that we have the right struct being passed */ | |
ceaf2cfb | 8052 | + if (!unpack_nameX(e, AA_STRUCT, "profile")) |
9474138d | 8053 | + goto fail; |
ceaf2cfb | 8054 | + if (!unpack_str(e, &name, NULL)) |
9474138d | 8055 | + goto fail; |
2380c486 | 8056 | + |
ceaf2cfb | 8057 | + profile = aa_alloc_profile(name); |
2380c486 JR |
8058 | + if (!profile) |
8059 | + return ERR_PTR(-ENOMEM); | |
8060 | + | |
ceaf2cfb AM |
8061 | + /* profile renaming is optional */ |
8062 | + (void) unpack_str(e, &profile->rename, "rename"); | |
8063 | + | |
9474138d | 8064 | + /* xmatch is optional and may be NULL */ |
ceaf2cfb | 8065 | + profile->xmatch = unpack_dfa(e); |
9474138d AM |
8066 | + if (IS_ERR(profile->xmatch)) { |
8067 | + error = PTR_ERR(profile->xmatch); | |
8068 | + profile->xmatch = NULL; | |
2380c486 | 8069 | + goto fail; |
9474138d | 8070 | + } |
ceaf2cfb AM |
8071 | + /* xmatch_len is not optional if xmatch is set */ |
8072 | + if (profile->xmatch) { | |
8073 | + if (!unpack_u32(e, &tmp, NULL)) | |
8074 | + goto fail; | |
8075 | + profile->xmatch_len = tmp; | |
8076 | + } | |
2380c486 JR |
8077 | + |
8078 | + /* per profile debug flags (complain, audit) */ | |
ceaf2cfb | 8079 | + if (!unpack_nameX(e, AA_STRUCT, "flags")) |
2380c486 | 8080 | + goto fail; |
ceaf2cfb | 8081 | + if (!unpack_u32(e, &tmp, NULL)) |
2380c486 | 8082 | + goto fail; |
9474138d AM |
8083 | + if (tmp) |
8084 | + profile->flags |= PFLAG_HAT; | |
ceaf2cfb | 8085 | + if (!unpack_u32(e, &tmp, NULL)) |
2380c486 | 8086 | + goto fail; |
9474138d AM |
8087 | + if (tmp) |
8088 | + profile->mode = APPARMOR_COMPLAIN; | |
ceaf2cfb | 8089 | + if (!unpack_u32(e, &tmp, NULL)) |
2380c486 | 8090 | + goto fail; |
9474138d AM |
8091 | + if (tmp) |
8092 | + profile->audit = AUDIT_ALL; | |
8093 | + | |
ceaf2cfb | 8094 | + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
2380c486 JR |
8095 | + goto fail; |
8096 | + | |
ceaf2cfb | 8097 | + /* path_flags is optional */ |
76514441 AM |
8098 | + if (unpack_u32(e, &profile->path_flags, "path_flags")) |
8099 | + profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED; | |
8100 | + else | |
8101 | + /* set a default value if path_flags field is not present */ | |
8102 | + profile->path_flags = PFLAG_MEDIATE_DELETED; | |
ceaf2cfb | 8103 | + |
76514441 | 8104 | + if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) |
2380c486 | 8105 | + goto fail; |
ceaf2cfb | 8106 | + if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) |
2380c486 | 8107 | + goto fail; |
ceaf2cfb | 8108 | + if (!unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL)) |
2380c486 | 8109 | + goto fail; |
ceaf2cfb | 8110 | + if (!unpack_u32(e, &tmpcap.cap[0], NULL)) |
2380c486 JR |
8111 | + goto fail; |
8112 | + | |
ceaf2cfb | 8113 | + if (unpack_nameX(e, AA_STRUCT, "caps64")) { |
2380c486 | 8114 | + /* optional upper half of 64 bit caps */ |
76514441 | 8115 | + if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) |
ceaf2cfb AM |
8116 | + goto fail; |
8117 | + if (!unpack_u32(e, &(profile->caps.audit.cap[1]), NULL)) | |
2380c486 | 8118 | + goto fail; |
ceaf2cfb | 8119 | + if (!unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL)) |
2380c486 | 8120 | + goto fail; |
ceaf2cfb | 8121 | + if (!unpack_u32(e, &(tmpcap.cap[1]), NULL)) |
2380c486 | 8122 | + goto fail; |
ceaf2cfb AM |
8123 | + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
8124 | + goto fail; | |
8125 | + } | |
8126 | + | |
8127 | + if (unpack_nameX(e, AA_STRUCT, "capsx")) { | |
8128 | + /* optional extended caps mediation mask */ | |
8129 | + if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) | |
2380c486 | 8130 | + goto fail; |
ceaf2cfb | 8131 | + if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) |
2380c486 JR |
8132 | + goto fail; |
8133 | + } | |
8134 | + | |
ceaf2cfb | 8135 | + if (!unpack_rlimits(e, profile)) |
2380c486 JR |
8136 | + goto fail; |
8137 | + | |
ceaf2cfb | 8138 | + size = unpack_array(e, "net_allowed_af"); |
2380c486 JR |
8139 | + if (size) { |
8140 | + if (size > AF_MAX) | |
8141 | + goto fail; | |
8142 | + | |
8143 | + for (i = 0; i < size; i++) { | |
76514441 | 8144 | + if (!unpack_u16(e, &profile->net.allow[i], NULL)) |
2380c486 | 8145 | + goto fail; |
ceaf2cfb | 8146 | + if (!unpack_u16(e, &profile->net.audit[i], NULL)) |
2380c486 | 8147 | + goto fail; |
ceaf2cfb | 8148 | + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) |
2380c486 JR |
8149 | + goto fail; |
8150 | + } | |
ceaf2cfb | 8151 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
2380c486 | 8152 | + goto fail; |
ceaf2cfb AM |
8153 | + /* |
8154 | + * allow unix domain and netlink sockets they are handled | |
2380c486 JR |
8155 | + * by IPC |
8156 | + */ | |
8157 | + } | |
76514441 AM |
8158 | + profile->net.allow[AF_UNIX] = 0xffff; |
8159 | + profile->net.allow[AF_NETLINK] = 0xffff; | |
2380c486 JR |
8160 | + |
8161 | + /* get file rules */ | |
ceaf2cfb | 8162 | + profile->file.dfa = unpack_dfa(e); |
9474138d AM |
8163 | + if (IS_ERR(profile->file.dfa)) { |
8164 | + error = PTR_ERR(profile->file.dfa); | |
8165 | + profile->file.dfa = NULL; | |
2380c486 JR |
8166 | + goto fail; |
8167 | + } | |
8168 | + | |
ceaf2cfb AM |
8169 | + if (!unpack_u32(e, &profile->file.start, "dfa_start")) |
8170 | + /* default start state */ | |
8171 | + profile->file.start = DFA_START; | |
8172 | + | |
8173 | + if (!unpack_trans_table(e, profile)) | |
2380c486 JR |
8174 | + goto fail; |
8175 | + | |
ceaf2cfb | 8176 | + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
2380c486 JR |
8177 | + goto fail; |
8178 | + | |
8179 | + return profile; | |
8180 | + | |
8181 | +fail: | |
76514441 AM |
8182 | + if (profile) |
8183 | + name = NULL; | |
8184 | + else if (!name) | |
8185 | + name = "unknown"; | |
8186 | + audit_iface(profile, name, "failed to unpack profile", e, error); | |
ceaf2cfb | 8187 | + aa_put_profile(profile); |
2380c486 JR |
8188 | + |
8189 | + return ERR_PTR(error); | |
8190 | +} | |
8191 | + | |
8192 | +/** | |
76514441 AM |
8193 | + * verify_head - unpack serialized stream header |
8194 | + * @e: serialized data read head (NOT NULL) | |
8195 | + * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | |
2380c486 | 8196 | + * |
ceaf2cfb | 8197 | + * Returns: error or 0 if header is good |
2380c486 | 8198 | + */ |
76514441 | 8199 | +static int verify_header(struct aa_ext *e, const char **ns) |
2380c486 | 8200 | +{ |
76514441 | 8201 | + int error = -EPROTONOSUPPORT; |
2380c486 | 8202 | + /* get the interface version */ |
ceaf2cfb | 8203 | + if (!unpack_u32(e, &e->version, "version")) { |
76514441 AM |
8204 | + audit_iface(NULL, NULL, "invalid profile format", e, error); |
8205 | + return error; | |
2380c486 JR |
8206 | + } |
8207 | + | |
8208 | + /* check that the interface version is currently supported */ | |
8209 | + if (e->version != 5) { | |
76514441 AM |
8210 | + audit_iface(NULL, NULL, "unsupported interface version", e, |
8211 | + error); | |
8212 | + return error; | |
2380c486 JR |
8213 | + } |
8214 | + | |
8215 | + /* read the namespace if present */ | |
76514441 AM |
8216 | + if (!unpack_str(e, ns, "namespace")) |
8217 | + *ns = NULL; | |
2380c486 JR |
8218 | + |
8219 | + return 0; | |
8220 | +} | |
8221 | + | |
ceaf2cfb | 8222 | +static bool verify_xindex(int xindex, int table_size) |
2380c486 | 8223 | +{ |
ceaf2cfb AM |
8224 | + int index, xtype; |
8225 | + xtype = xindex & AA_X_TYPE_MASK; | |
8226 | + index = xindex & AA_X_INDEX_MASK; | |
8227 | + if (xtype == AA_X_TABLE && index > table_size) | |
8228 | + return 0; | |
8229 | + return 1; | |
8230 | +} | |
9474138d | 8231 | + |
ceaf2cfb AM |
8232 | +/* verify dfa xindexes are in range of transition tables */ |
8233 | +static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) | |
8234 | +{ | |
8235 | + int i; | |
8236 | + for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | |
8237 | + if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) | |
8238 | + return 0; | |
8239 | + if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) | |
8240 | + return 0; | |
2380c486 | 8241 | + } |
ceaf2cfb AM |
8242 | + return 1; |
8243 | +} | |
2380c486 | 8244 | + |
76514441 AM |
8245 | +/** |
8246 | + * verify_profile - Do post unpack analysis to verify profile consistency | |
8247 | + * @profile: profile to verify (NOT NULL) | |
8248 | + * | |
8249 | + * Returns: 0 if passes verification else error | |
8250 | + */ | |
8251 | +static int verify_profile(struct aa_profile *profile) | |
ceaf2cfb AM |
8252 | +{ |
8253 | + if (aa_g_paranoid_load) { | |
8254 | + if (profile->file.dfa && | |
8255 | + !verify_dfa_xindex(profile->file.dfa, | |
8256 | + profile->file.trans.size)) { | |
76514441 AM |
8257 | + audit_iface(profile, NULL, "Invalid named transition", |
8258 | + NULL, -EPROTO); | |
ceaf2cfb AM |
8259 | + return -EPROTO; |
8260 | + } | |
2380c486 | 8261 | + } |
2380c486 | 8262 | + |
ceaf2cfb | 8263 | + return 0; |
2380c486 JR |
8264 | +} |
8265 | + | |
8266 | +/** | |
ceaf2cfb AM |
8267 | + * aa_unpack - unpack packed binary profile data loaded from user space |
8268 | + * @udata: user data copied to kmem (NOT NULL) | |
8269 | + * @size: the size of the user data | |
76514441 | 8270 | + * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) |
2380c486 | 8271 | + * |
ceaf2cfb AM |
8272 | + * Unpack user data and return refcounted allocated profile or ERR_PTR |
8273 | + * | |
8274 | + * Returns: profile else error pointer if fails to unpack | |
2380c486 | 8275 | + */ |
76514441 | 8276 | +struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) |
2380c486 | 8277 | +{ |
76514441 | 8278 | + struct aa_profile *profile = NULL; |
ceaf2cfb | 8279 | + int error; |
2380c486 JR |
8280 | + struct aa_ext e = { |
8281 | + .start = udata, | |
8282 | + .end = udata + size, | |
8283 | + .pos = udata, | |
2380c486 | 8284 | + }; |
2380c486 | 8285 | + |
76514441 | 8286 | + error = verify_header(&e, ns); |
2380c486 | 8287 | + if (error) |
ceaf2cfb | 8288 | + return ERR_PTR(error); |
2380c486 | 8289 | + |
76514441 | 8290 | + profile = unpack_profile(&e); |
ceaf2cfb | 8291 | + if (IS_ERR(profile)) |
76514441 | 8292 | + return profile; |
2380c486 | 8293 | + |
76514441 | 8294 | + error = verify_profile(profile); |
ceaf2cfb AM |
8295 | + if (error) { |
8296 | + aa_put_profile(profile); | |
8297 | + profile = ERR_PTR(error); | |
2380c486 | 8298 | + } |
9474138d | 8299 | + |
ceaf2cfb AM |
8300 | + /* return refcount */ |
8301 | + return profile; | |
2380c486 | 8302 | +} |
76514441 AM |
8303 | diff --git a/security/apparmor/policy_unpack.c.rej b/security/apparmor/policy_unpack.c.rej |
8304 | new file mode 100644 | |
8305 | index 0000000..5bddfd6 | |
8306 | --- /dev/null | |
8307 | +++ b/security/apparmor/policy_unpack.c.rej | |
8308 | @@ -0,0 +1,11 @@ | |
8309 | +diff a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c (rejected hunks) | |
8310 | +@@ -473,7 +473,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |
8311 | + { | |
8312 | + struct aa_profile *profile = NULL; | |
8313 | + const char *name = NULL, *info = NULL; | |
8314 | +- int error = -EPROTO; | |
8315 | ++ size_t size = 0; | |
8316 | ++ int i, error = -EPROTO; | |
8317 | + kernel_cap_t tmpcap; | |
8318 | + u32 tmp; | |
8319 | + u64 tmp64; | |
ceaf2cfb AM |
8320 | diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c |
8321 | new file mode 100644 | |
76514441 | 8322 | index 0000000..04a2cf8 |
ceaf2cfb AM |
8323 | --- /dev/null |
8324 | +++ b/security/apparmor/procattr.c | |
76514441 | 8325 | @@ -0,0 +1,170 @@ |
2380c486 | 8326 | +/* |
9474138d AM |
8327 | + * AppArmor security module |
8328 | + * | |
8329 | + * This file contains AppArmor /proc/<pid>/attr/ interface functions | |
2380c486 | 8330 | + * |
9474138d | 8331 | + * Copyright (C) 1998-2008 Novell/SUSE |
ceaf2cfb | 8332 | + * Copyright 2009-2010 Canonical Ltd. |
2380c486 | 8333 | + * |
9474138d AM |
8334 | + * This program is free software; you can redistribute it and/or |
8335 | + * modify it under the terms of the GNU General Public License as | |
8336 | + * published by the Free Software Foundation, version 2 of the | |
8337 | + * License. | |
2380c486 JR |
8338 | + */ |
8339 | + | |
9474138d | 8340 | +#include "include/apparmor.h" |
76514441 | 8341 | +#include "include/context.h" |
9474138d AM |
8342 | +#include "include/policy.h" |
8343 | +#include "include/domain.h" | |
2380c486 | 8344 | + |
76514441 | 8345 | + |
ceaf2cfb AM |
8346 | +/** |
8347 | + * aa_getprocattr - Return the profile information for @profile | |
8348 | + * @profile: the profile to print profile info about (NOT NULL) | |
76514441 | 8349 | + * @string: Returns - string containing the profile info (NOT NULL) |
ceaf2cfb AM |
8350 | + * |
8351 | + * Returns: length of @string on success else error on failure | |
8352 | + * | |
8353 | + * Requires: profile != NULL | |
8354 | + * | |
8355 | + * Creates a string containing the namespace_name://profile_name for | |
8356 | + * @profile. | |
8357 | + * | |
8358 | + * Returns: size of string placed in @string else error code on failure | |
8359 | + */ | |
8360 | +int aa_getprocattr(struct aa_profile *profile, char **string) | |
2380c486 JR |
8361 | +{ |
8362 | + char *str; | |
76514441 | 8363 | + int len = 0, mode_len = 0, ns_len = 0, name_len; |
ceaf2cfb | 8364 | + const char *mode_str = profile_mode_names[profile->mode]; |
76514441 | 8365 | + const char *ns_name = NULL; |
ceaf2cfb | 8366 | + struct aa_namespace *ns = profile->ns; |
76514441 | 8367 | + struct aa_namespace *current_ns = __aa_current_profile()->ns; |
ceaf2cfb AM |
8368 | + char *s; |
8369 | + | |
76514441 AM |
8370 | + if (!aa_ns_visible(current_ns, ns)) |
8371 | + return -EACCES; | |
8372 | + | |
8373 | + ns_name = aa_ns_name(current_ns, ns); | |
8374 | + ns_len = strlen(ns_name); | |
8375 | + | |
8376 | + /* if the visible ns_name is > 0 increase size for : :// seperator */ | |
8377 | + if (ns_len) | |
8378 | + ns_len += 4; | |
8379 | + | |
8380 | + /* unconfined profiles don't have a mode string appended */ | |
8381 | + if (!unconfined(profile)) | |
8382 | + mode_len = strlen(mode_str) + 3; /* + 3 for _() */ | |
8383 | + | |
ceaf2cfb | 8384 | + name_len = strlen(profile->base.hname); |
76514441 AM |
8385 | + len = mode_len + ns_len + name_len + 1; /* + 1 for \n */ |
8386 | + s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */ | |
ceaf2cfb AM |
8387 | + if (!str) |
8388 | + return -ENOMEM; | |
2380c486 | 8389 | + |
ceaf2cfb | 8390 | + if (ns_len) { |
76514441 AM |
8391 | + /* skip over prefix current_ns->base.hname and separating // */ |
8392 | + sprintf(s, ":%s://", ns_name); | |
ceaf2cfb | 8393 | + s += ns_len; |
2380c486 | 8394 | + } |
76514441 AM |
8395 | + if (unconfined(profile)) |
8396 | + /* mode string not being appended */ | |
ceaf2cfb AM |
8397 | + sprintf(s, "%s\n", profile->base.hname); |
8398 | + else | |
8399 | + sprintf(s, "%s (%s)\n", profile->base.hname, mode_str); | |
2380c486 JR |
8400 | + *string = str; |
8401 | + | |
ceaf2cfb | 8402 | + /* NOTE: len does not include \0 of string, not saved as part of file */ |
9474138d | 8403 | + return len; |
2380c486 JR |
8404 | +} |
8405 | + | |
ceaf2cfb AM |
8406 | +/** |
8407 | + * split_token_from_name - separate a string of form <token>^<name> | |
76514441 | 8408 | + * @op: operation being checked |
ceaf2cfb AM |
8409 | + * @args: string to parse (NOT NULL) |
8410 | + * @token: stores returned parsed token value (NOT NULL) | |
8411 | + * | |
8412 | + * Returns: start position of name after token else NULL on failure | |
8413 | + */ | |
76514441 | 8414 | +static char *split_token_from_name(int op, char *args, u64 * token) |
2380c486 JR |
8415 | +{ |
8416 | + char *name; | |
8417 | + | |
9474138d | 8418 | + *token = simple_strtoull(args, &name, 16); |
2380c486 | 8419 | + if ((name == args) || *name != '^') { |
76514441 | 8420 | + AA_ERROR("%s: Invalid input '%s'", op_table[op], args); |
2380c486 JR |
8421 | + return ERR_PTR(-EINVAL); |
8422 | + } | |
8423 | + | |
ceaf2cfb | 8424 | + name++; /* skip ^ */ |
2380c486 JR |
8425 | + if (!*name) |
8426 | + name = NULL; | |
8427 | + return name; | |
8428 | +} | |
8429 | + | |
ceaf2cfb AM |
8430 | +/** |
8431 | + * aa_setprocattr_chagnehat - handle procattr interface to change_hat | |
8432 | + * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) | |
8433 | + * @size: size of the args | |
8434 | + * @test: true if this is a test of change_hat permissions | |
8435 | + * | |
8436 | + * Returns: %0 or error code if change_hat fails | |
8437 | + */ | |
8438 | +int aa_setprocattr_changehat(char *args, size_t size, int test) | |
2380c486 JR |
8439 | +{ |
8440 | + char *hat; | |
9474138d | 8441 | + u64 token; |
ceaf2cfb AM |
8442 | + const char *hats[16]; /* current hard limit on # of names */ |
8443 | + int count = 0; | |
2380c486 | 8444 | + |
76514441 | 8445 | + hat = split_token_from_name(OP_CHANGE_HAT, args, &token); |
2380c486 JR |
8446 | + if (IS_ERR(hat)) |
8447 | + return PTR_ERR(hat); | |
8448 | + | |
9474138d | 8449 | + if (!hat && !token) { |
2380c486 JR |
8450 | + AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); |
8451 | + return -EINVAL; | |
8452 | + } | |
8453 | + | |
ceaf2cfb | 8454 | + if (hat) { |
76514441 AM |
8455 | + /* set up hat name vector, args guaranteed null terminated |
8456 | + * at args[size] by setprocattr. | |
8457 | + * | |
8458 | + * If there are multiple hat names in the buffer each is | |
8459 | + * separated by a \0. Ie. userspace writes them pre tokenized | |
ceaf2cfb AM |
8460 | + */ |
8461 | + char *end = args + size; | |
8462 | + for (count = 0; (hat < end) && count < 16; ++count) { | |
8463 | + char *next = hat + strlen(hat) + 1; | |
8464 | + hats[count] = hat; | |
8465 | + hat = next; | |
8466 | + } | |
8467 | + } | |
8468 | + | |
2380c486 | 8469 | + AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", |
9474138d | 8470 | + __func__, token, hat ? hat : NULL); |
2380c486 | 8471 | + |
ceaf2cfb | 8472 | + return aa_change_hat(hats, count, token, test); |
2380c486 JR |
8473 | +} |
8474 | + | |
ceaf2cfb AM |
8475 | +/** |
8476 | + * aa_setprocattr_changeprofile - handle procattr interface to changeprofile | |
8477 | + * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL) | |
8478 | + * @onexec: true if change_profile should be delayed until exec | |
8479 | + * @test: true if this is a test of change_profile permissions | |
8480 | + * | |
8481 | + * Returns: %0 or error code if change_profile fails | |
8482 | + */ | |
76514441 | 8483 | +int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) |
2380c486 | 8484 | +{ |
9474138d AM |
8485 | + char *name, *ns_name; |
8486 | + | |
ceaf2cfb | 8487 | + name = aa_split_fqname(fqname, &ns_name); |
9474138d AM |
8488 | + return aa_change_profile(ns_name, name, onexec, test); |
8489 | +} | |
2380c486 | 8490 | + |
ceaf2cfb | 8491 | +int aa_setprocattr_permipc(char *fqname) |
9474138d AM |
8492 | +{ |
8493 | + /* TODO: add ipc permission querying */ | |
8494 | + return -ENOTSUPP; | |
2380c486 | 8495 | +} |
ceaf2cfb AM |
8496 | diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c |
8497 | new file mode 100644 | |
76514441 | 8498 | index 0000000..4a368f1 |
ceaf2cfb AM |
8499 | --- /dev/null |
8500 | +++ b/security/apparmor/resource.c | |
76514441 | 8501 | @@ -0,0 +1,134 @@ |
9474138d AM |
8502 | +/* |
8503 | + * AppArmor security module | |
8504 | + * | |
8505 | + * This file contains AppArmor resource mediation and attachment | |
8506 | + * | |
8507 | + * Copyright (C) 1998-2008 Novell/SUSE | |
ceaf2cfb | 8508 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
8509 | + * |
8510 | + * This program is free software; you can redistribute it and/or | |
8511 | + * modify it under the terms of the GNU General Public License as | |
8512 | + * published by the Free Software Foundation, version 2 of the | |
8513 | + * License. | |
8514 | + */ | |
8515 | + | |
8516 | +#include <linux/audit.h> | |
8517 | + | |
8518 | +#include "include/audit.h" | |
8519 | +#include "include/resource.h" | |
8520 | +#include "include/policy.h" | |
8521 | + | |
76514441 AM |
8522 | +/* |
8523 | + * Table of rlimit names: we generate it from resource.h. | |
8524 | + */ | |
8525 | +#include "rlim_names.h" | |
2380c486 | 8526 | + |
ceaf2cfb | 8527 | +/* audit callback for resource specific fields */ |
76514441 | 8528 | +static void audit_cb(struct audit_buffer *ab, void *va) |
2380c486 | 8529 | +{ |
76514441 | 8530 | + struct common_audit_data *sa = va; |
9474138d | 8531 | + |
76514441 AM |
8532 | + audit_log_format(ab, " rlimit=%s value=%lu", |
8533 | + rlim_names[sa->aad.rlim.rlim], sa->aad.rlim.max); | |
9474138d AM |
8534 | +} |
8535 | + | |
ceaf2cfb | 8536 | +/** |
76514441 | 8537 | + * audit_resource - audit setting resource limit |
ceaf2cfb | 8538 | + * @profile: profile being enforced (NOT NULL) |
76514441 AM |
8539 | + * @resoure: rlimit being auditing |
8540 | + * @value: value being set | |
8541 | + * @error: error value | |
ceaf2cfb AM |
8542 | + * |
8543 | + * Returns: 0 or sa->error else other error code on failure | |
8544 | + */ | |
76514441 AM |
8545 | +static int audit_resource(struct aa_profile *profile, unsigned int resource, |
8546 | + unsigned long value, int error) | |
8547 | +{ | |
8548 | + struct common_audit_data sa; | |
8549 | + | |
8550 | + COMMON_AUDIT_DATA_INIT(&sa, NONE); | |
8551 | + sa.aad.op = OP_SETRLIMIT, | |
8552 | + sa.aad.rlim.rlim = resource; | |
8553 | + sa.aad.rlim.max = value; | |
8554 | + sa.aad.error = error; | |
8555 | + return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, | |
8556 | + audit_cb); | |
8557 | +} | |
8558 | + | |
8559 | +/** | |
8560 | + * aa_map_resouce - map compiled policy resource to internal # | |
8561 | + * @resource: flattened policy resource number | |
8562 | + * | |
8563 | + * Returns: resource # for the current architecture. | |
8564 | + * | |
8565 | + * rlimit resource can vary based on architecture, map the compiled policy | |
8566 | + * resource # to the internal representation for the architecture. | |
8567 | + */ | |
8568 | +int aa_map_resource(int resource) | |
9474138d | 8569 | +{ |
76514441 | 8570 | + return rlim_map[resource]; |
9474138d AM |
8571 | +} |
8572 | + | |
8573 | +/** | |
8574 | + * aa_task_setrlimit - test permission to set an rlimit | |
ceaf2cfb | 8575 | + * @profile - profile confining the task (NOT NULL) |
9474138d | 8576 | + * @resource - the resource being set |
ceaf2cfb | 8577 | + * @new_rlim - the new resource limit (NOT NULL) |
9474138d AM |
8578 | + * |
8579 | + * Control raising the processes hard limit. | |
ceaf2cfb AM |
8580 | + * |
8581 | + * Returns: 0 or error code if setting resource failed | |
9474138d AM |
8582 | + */ |
8583 | +int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | |
8584 | + struct rlimit *new_rlim) | |
8585 | +{ | |
9474138d | 8586 | + int error = 0; |
2380c486 | 8587 | + |
9474138d | 8588 | + if (profile->rlimits.mask & (1 << resource) && |
76514441 | 8589 | + new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) |
9474138d | 8590 | + |
76514441 AM |
8591 | + error = audit_resource(profile, resource, new_rlim->rlim_max, |
8592 | + -EACCES); | |
2380c486 | 8593 | + |
9474138d AM |
8594 | + return error; |
8595 | +} | |
8596 | + | |
ceaf2cfb AM |
8597 | +/** |
8598 | + * __aa_transition_rlimits - apply new profile rlimits | |
76514441 | 8599 | + * @old: old profile on task (NOT NULL) |
ceaf2cfb AM |
8600 | + * @new: new profile with rlimits to apply (NOT NULL) |
8601 | + */ | |
9474138d AM |
8602 | +void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) |
8603 | +{ | |
8604 | + unsigned int mask = 0; | |
8605 | + struct rlimit *rlim, *initrlim; | |
8606 | + int i; | |
8607 | + | |
8608 | + /* for any rlimits the profile controlled reset the soft limit | |
8609 | + * to the less of the tasks hard limit and the init tasks soft limit | |
8610 | + */ | |
76514441 | 8611 | + if (old->rlimits.mask) { |
ceaf2cfb | 8612 | + for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { |
9474138d AM |
8613 | + if (old->rlimits.mask & mask) { |
8614 | + rlim = current->signal->rlim + i; | |
8615 | + initrlim = init_task.signal->rlim + i; | |
8616 | + rlim->rlim_cur = min(rlim->rlim_max, | |
8617 | + initrlim->rlim_cur); | |
8618 | + } | |
2380c486 JR |
8619 | + } |
8620 | + } | |
8621 | + | |
9474138d | 8622 | + /* set any new hard limits as dictated by the new profile */ |
76514441 | 8623 | + if (!new->rlimits.mask) |
9474138d | 8624 | + return; |
ceaf2cfb | 8625 | + for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { |
9474138d AM |
8626 | + if (!(new->rlimits.mask & mask)) |
8627 | + continue; | |
2380c486 | 8628 | + |
9474138d AM |
8629 | + rlim = current->signal->rlim + i; |
8630 | + rlim->rlim_max = min(rlim->rlim_max, | |
8631 | + new->rlimits.limits[i].rlim_max); | |
8632 | + /* soft limit should not exceed hard limit */ | |
8633 | + rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); | |
2380c486 | 8634 | + } |
9474138d | 8635 | +} |
ceaf2cfb AM |
8636 | diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c |
8637 | new file mode 100644 | |
76514441 | 8638 | index 0000000..f0b34f7 |
ceaf2cfb AM |
8639 | --- /dev/null |
8640 | +++ b/security/apparmor/sid.c | |
8641 | @@ -0,0 +1,55 @@ | |
9474138d AM |
8642 | +/* |
8643 | + * AppArmor security module | |
8644 | + * | |
8645 | + * This file contains AppArmor security identifier (sid) manipulation fns | |
8646 | + * | |
ceaf2cfb | 8647 | + * Copyright 2009-2010 Canonical Ltd. |
9474138d AM |
8648 | + * |
8649 | + * This program is free software; you can redistribute it and/or | |
8650 | + * modify it under the terms of the GNU General Public License as | |
8651 | + * published by the Free Software Foundation, version 2 of the | |
8652 | + * License. | |
8653 | + * | |
8654 | + * | |
8655 | + * AppArmor allocates a unique sid for every profile loaded. If a profile | |
76514441 | 8656 | + * is replaced it receives the sid of the profile it is replacing. |
9474138d | 8657 | + * |
ceaf2cfb | 8658 | + * The sid value of 0 is invalid. |
9474138d AM |
8659 | + */ |
8660 | + | |
8661 | +#include <linux/spinlock.h> | |
8662 | +#include <linux/errno.h> | |
8663 | +#include <linux/err.h> | |
8664 | + | |
8665 | +#include "include/sid.h" | |
8666 | + | |
8667 | +/* global counter from which sids are allocated */ | |
76514441 | 8668 | +static u32 global_sid; |
9474138d | 8669 | +static DEFINE_SPINLOCK(sid_lock); |
2380c486 | 8670 | + |
9474138d AM |
8671 | +/* TODO FIXME: add sid to profile mapping, and sid recycling */ |
8672 | + | |
9474138d AM |
8673 | +/** |
8674 | + * aa_alloc_sid - allocate a new sid for a profile | |
9474138d | 8675 | + */ |
ceaf2cfb | 8676 | +u32 aa_alloc_sid(void) |
9474138d AM |
8677 | +{ |
8678 | + u32 sid; | |
8679 | + | |
ceaf2cfb | 8680 | + /* |
9474138d AM |
8681 | + * TODO FIXME: sid recycling - part of profile mapping table |
8682 | + */ | |
8683 | + spin_lock(&sid_lock); | |
ceaf2cfb | 8684 | + sid = (++global_sid); |
9474138d AM |
8685 | + spin_unlock(&sid_lock); |
8686 | + return sid; | |
8687 | +} | |
8688 | + | |
8689 | +/** | |
8690 | + * aa_free_sid - free a sid | |
8691 | + * @sid: sid to free | |
8692 | + */ | |
8693 | +void aa_free_sid(u32 sid) | |
8694 | +{ | |
ceaf2cfb | 8695 | + ; /* NOP ATM */ |
9474138d | 8696 | +} |
76514441 AM |
8697 | -- |
8698 | 1.7.1 | |
8699 |