]>
Commit | Line | Data |
---|---|---|
6a930a95 BS |
1 | From: John Johansen <jjohansen@suse.de> |
2 | Subject: AppArmor: all the rest | |
3 | ||
4 | All the things that didn't nicely fit in a category on their own: kbuild | |
5 | code, declararions and inline functions, /sys/kernel/security/apparmor | |
6 | filesystem for controlling apparmor from user space, profile list | |
7 | functions, locking documentation, /proc/$pid/task/$tid/attr/current | |
8 | access. | |
9 | ||
10 | Signed-off-by: John Johansen <jjohansen@suse.de> | |
11 | Signed-off-by: Andreas Gruenbacher <agruen@suse.de> | |
12 | ||
13 | --- | |
14 | security/apparmor/Kconfig | 42 ++++ | |
15 | security/apparmor/Makefile | 13 + | |
16 | security/apparmor/apparmor.h | 371 +++++++++++++++++++++++++++++++++++++++++ | |
17 | security/apparmor/apparmorfs.c | 281 +++++++++++++++++++++++++++++++ | |
18 | security/apparmor/inline.h | 250 +++++++++++++++++++++++++++ | |
19 | security/apparmor/list.c | 174 +++++++++++++++++++ | |
20 | security/apparmor/locking.txt | 68 +++++++ | |
21 | security/apparmor/procattr.c | 195 +++++++++++++++++++++ | |
22 | 8 files changed, 1394 insertions(+) | |
23 | ||
24 | --- /dev/null | |
25 | +++ b/security/apparmor/Kconfig | |
26 | @@ -0,0 +1,42 @@ | |
27 | +config SECURITY_APPARMOR | |
28 | + bool "AppArmor support" | |
29 | + depends on SECURITY | |
30 | + select AUDIT | |
31 | + help | |
32 | + This enables the AppArmor security module. | |
33 | + Required userspace tools (if they are not included in your | |
34 | + distribution) and further information may be found at | |
35 | + <http://forge.novell.com/modules/xfmod/project/?apparmor> | |
36 | + | |
37 | + If you are unsure how to answer this question, answer N. | |
38 | + | |
39 | +config SECURITY_APPARMOR_BOOTPARAM_VALUE | |
40 | + int "AppArmor boot parameter default value" | |
41 | + depends on SECURITY_APPARMOR | |
42 | + range 0 1 | |
43 | + default 1 | |
44 | + help | |
45 | + This option sets the default value for the kernel parameter | |
46 | + 'apparmor', which allows AppArmor to be enabled or disabled | |
47 | + at boot. If this option is set to 0 (zero), the AppArmor | |
48 | + kernel parameter will default to 0, disabling AppArmor at | |
49 | + bootup. If this option is set to 1 (one), the AppArmor | |
50 | + kernel parameter will default to 1, enabling AppArmor at | |
51 | + bootup. | |
52 | + | |
53 | + If you are unsure how to answer this question, answer 1. | |
54 | + | |
55 | +config SECURITY_APPARMOR_DISABLE | |
56 | + bool "AppArmor runtime disable" | |
57 | + depends on SECURITY_APPARMOR | |
58 | + default n | |
59 | + help | |
60 | + This option enables writing to a apparmorfs node 'disable', which | |
61 | + allows AppArmor to be disabled at runtime prior to the policy load. | |
62 | + AppArmor will then remain disabled until the next boot. | |
63 | + This option is similar to the apparmor.enabled=0 boot parameter, | |
64 | + but is to support runtime disabling of AppArmor, e.g. from | |
65 | + /sbin/init, for portability across platforms where boot | |
66 | + parameters are difficult to employ. | |
67 | + | |
68 | + If you are unsure how to answer this question, answer N. | |
69 | --- /dev/null | |
70 | +++ b/security/apparmor/Makefile | |
71 | @@ -0,0 +1,13 @@ | |
72 | +# Makefile for AppArmor Linux Security Module | |
73 | +# | |
74 | +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |
75 | + | |
76 | +apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \ | |
77 | + module_interface.o match.o | |
78 | + | |
79 | +quiet_cmd_make-caps = GEN $@ | |
80 | +cmd_make-caps = 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 > $@ | |
81 | + | |
82 | +$(obj)/main.o : $(obj)/capability_names.h | |
83 | +$(obj)/capability_names.h : $(srctree)/include/linux/capability.h | |
84 | + $(call cmd,make-caps) | |
85 | --- /dev/null | |
86 | +++ b/security/apparmor/apparmor.h | |
87 | @@ -0,0 +1,371 @@ | |
88 | +/* | |
89 | + * Copyright (C) 1998-2007 Novell/SUSE | |
90 | + * | |
91 | + * This program is free software; you can redistribute it and/or | |
92 | + * modify it under the terms of the GNU General Public License as | |
93 | + * published by the Free Software Foundation, version 2 of the | |
94 | + * License. | |
95 | + * | |
96 | + * AppArmor internal prototypes | |
97 | + */ | |
98 | + | |
99 | +#ifndef __APPARMOR_H | |
100 | +#define __APPARMOR_H | |
101 | + | |
102 | +#include <linux/sched.h> | |
103 | +#include <linux/fs.h> | |
104 | +#include <linux/binfmts.h> | |
105 | +#include <linux/rcupdate.h> | |
106 | + | |
107 | +/* | |
108 | + * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags | |
109 | + * for profile permissions | |
110 | + */ | |
111 | +#define AA_MAY_LINK 0x0010 | |
112 | +#define AA_MAY_LOCK 0x0020 | |
113 | +#define AA_EXEC_MMAP 0x0040 | |
114 | +#define AA_MAY_MOUNT 0x0080 /* no direct audit mapping */ | |
115 | +#define AA_EXEC_UNSAFE 0x0100 | |
116 | +#define AA_EXEC_INHERIT 0x0200 | |
117 | +#define AA_EXEC_MOD_0 0x0400 | |
118 | +#define AA_EXEC_MOD_1 0x0800 | |
119 | +#define AA_EXEC_MOD_2 0x1000 | |
120 | +#define AA_EXEC_MOD_3 0x2000 | |
121 | + | |
122 | +#define AA_BASE_PERMS (MAY_READ | MAY_WRITE | MAY_EXEC | \ | |
123 | + MAY_APPEND | AA_MAY_LINK | \ | |
124 | + AA_MAY_LOCK | AA_EXEC_MMAP | \ | |
125 | + AA_MAY_MOUNT | AA_EXEC_UNSAFE | \ | |
126 | + AA_EXEC_INHERIT | AA_EXEC_MOD_0 | \ | |
127 | + AA_EXEC_MOD_1 | AA_EXEC_MOD_2 | \ | |
128 | + AA_EXEC_MOD_3) | |
129 | + | |
130 | +#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \ | |
131 | + AA_EXEC_MOD_2 | AA_EXEC_MOD_3) | |
132 | + | |
133 | +#define AA_EXEC_TYPE (AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \ | |
134 | + AA_EXEC_MODIFIERS) | |
135 | + | |
136 | +#define AA_EXEC_UNCONFINED AA_EXEC_MOD_0 | |
137 | +#define AA_EXEC_PROFILE AA_EXEC_MOD_1 | |
138 | +#define AA_EXEC_CHILD (AA_EXEC_MOD_0 | AA_EXEC_MOD_1) | |
139 | +/* remaining exec modes are index into profile name table */ | |
140 | +#define AA_EXEC_INDEX(mode) ((mode & AA_EXEC_MODIFIERS) >> 10) | |
141 | + | |
142 | +#define AA_USER_SHIFT 0 | |
143 | +#define AA_OTHER_SHIFT 14 | |
144 | + | |
145 | +#define AA_USER_PERMS (AA_BASE_PERMS << AA_USER_SHIFT) | |
146 | +#define AA_OTHER_PERMS (AA_BASE_PERMS << AA_OTHER_SHIFT) | |
147 | + | |
148 | +#define AA_FILE_PERMS (AA_USER_PERMS | AA_OTHER_PERMS) | |
149 | + | |
150 | +#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \ | |
151 | + (AA_MAY_LINK << AA_OTHER_SHIFT)) | |
152 | + | |
153 | +#define AA_USER_EXEC (MAY_EXEC << AA_USER_SHIFT) | |
154 | +#define AA_OTHER_EXEC (MAY_EXEC << AA_OTHER_SHIFT) | |
155 | + | |
156 | +#define AA_USER_EXEC_TYPE (AA_EXEC_TYPE << AA_USER_SHIFT) | |
157 | +#define AA_OTHER_EXEC_TYPE (AA_EXEC_TYPE << AA_OTHER_SHIFT) | |
158 | + | |
159 | +#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC) | |
160 | + | |
161 | +#define ALL_AA_EXEC_UNSAFE ((AA_EXEC_UNSAFE << AA_USER_SHIFT) | \ | |
162 | + (AA_EXEC_UNSAFE << AA_OTHER_SHIFT)) | |
163 | + | |
164 | +#define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE) | |
165 | + | |
166 | +/* overloaded permissions for link pairs */ | |
167 | +#define AA_LINK_SUBSET_TEST 0x0020 | |
168 | + | |
169 | +#define AA_USER_PTRACE 0x10000000 | |
170 | +#define AA_OTHER_PTRACE 0x20000000 | |
171 | +#define AA_PTRACE_PERMS (AA_USER_PTRACE | AA_OTHER_PTRACE) | |
172 | + | |
173 | +/* shared permissions that are not duplicated in user::other */ | |
174 | +#define AA_CHANGE_HAT 0x40000000 | |
175 | +#define AA_CHANGE_PROFILE 0x80000000 | |
176 | + | |
177 | +#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE) | |
178 | + | |
179 | +#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_PTRACE_PERMS | \ | |
180 | + AA_SHARED_PERMS) | |
181 | + | |
182 | +/* audit bits for the second accept field */ | |
183 | +#define AUDIT_FILE_MASK 0x1fc07f | |
184 | +#define AUDIT_QUIET_MASK(mask) ((mask >> 7) & AUDIT_FILE_MASK) | |
185 | +#define AA_VALID_PERM2_MASK 0x0fffffff | |
186 | + | |
187 | +#define AA_SECURE_EXEC_NEEDED 1 | |
188 | + | |
189 | +/* Control parameters (0 or 1), settable thru module/boot flags or | |
190 | + * via /sys/kernel/security/apparmor/control */ | |
191 | +extern int apparmor_complain; | |
192 | +extern int apparmor_debug; | |
193 | +extern int apparmor_audit; | |
194 | +extern int apparmor_logsyscall; | |
195 | +extern unsigned int apparmor_path_max; | |
196 | + | |
197 | +#define PROFILE_COMPLAIN(_profile) \ | |
198 | + (apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain)) | |
199 | + | |
200 | +#define APPARMOR_COMPLAIN(_cxt) \ | |
201 | + (apparmor_complain == 1 || \ | |
202 | + ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.complain)) | |
203 | + | |
204 | +#define PROFILE_AUDIT(_profile) \ | |
205 | + (apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit)) | |
206 | + | |
207 | +#define APPARMOR_AUDIT(_cxt) \ | |
208 | + (apparmor_audit == 1 || \ | |
209 | + ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.audit)) | |
210 | + | |
211 | +#define PROFILE_IS_HAT(_profile) \ | |
212 | + ((_profile) && (_profile)->flags.hat) | |
213 | + | |
214 | +/* | |
215 | + * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | |
216 | + * which is not related to profile accesses. | |
217 | + */ | |
218 | + | |
219 | +#define AA_DEBUG(fmt, args...) \ | |
220 | + do { \ | |
221 | + if (apparmor_debug) \ | |
222 | + printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ | |
223 | + } while (0) | |
224 | + | |
225 | +#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args) | |
226 | + | |
227 | +struct aa_profile; | |
228 | + | |
229 | +/* struct aa_namespace - namespace for a set of profiles | |
230 | + * @name: the name of the namespace | |
231 | + * @list: list the namespace is on | |
232 | + * @profiles: list of profile in the namespace | |
233 | + * @profile_count: the number of profiles in the namespace | |
234 | + * @null_complain_profile: special profile used for learning in this namespace | |
235 | + * @count: reference count on the namespace | |
236 | + * @lock: lock for adding/removing profile to the namespace | |
237 | + */ | |
238 | +struct aa_namespace { | |
239 | + char *name; | |
240 | + struct list_head list; | |
241 | + struct list_head profiles; | |
242 | + int profile_count; | |
243 | + struct aa_profile *null_complain_profile; | |
244 | + | |
245 | + struct kref count; | |
246 | + rwlock_t lock; | |
247 | +}; | |
248 | + | |
249 | +/* struct aa_profile - basic confinement data | |
250 | + * @name: the profiles name | |
251 | + * @list: list this profile is on | |
252 | + * @ns: namespace the profile is in | |
253 | + * @file_rules: dfa containing the profiles file rules | |
254 | + * @flags: flags controlling profile behavior | |
255 | + * @isstale: flag indicating if profile is stale | |
256 | + * @set_caps: capabilities that are being set | |
257 | + * @capabilities: capabilities mask | |
258 | + * @audit_caps: caps that are to be audited | |
259 | + * @quiet_caps: caps that should not be audited | |
260 | + * @capabilities: capabilities granted by the process | |
261 | + * @count: reference count of the profile | |
262 | + * @task_contexts: list of tasks confined by profile | |
263 | + * @lock: lock for the task_contexts list | |
264 | + * @network_families: basic network permissions | |
265 | + * @audit_network: which network permissions to force audit | |
266 | + * @quiet_network: which network permissions to quiet rejects | |
267 | + * | |
268 | + * The AppArmor profile contains the basic confinement data. Each profile | |
269 | + * has a name, and all nonstale profile are in a profile namespace. | |
270 | + * | |
271 | + * The task_contexts list and the isstale flag are protected by the | |
272 | + * profile lock. | |
273 | + * | |
274 | + * If a task context is moved between two profiles, we first need to grab | |
275 | + * both profile locks. lock_both_profiles() does that in a deadlock-safe | |
276 | + * way. | |
277 | + */ | |
278 | +struct aa_profile { | |
279 | + char *name; | |
280 | + struct list_head list; | |
281 | + struct aa_namespace *ns; | |
282 | + | |
283 | + int exec_table_size; | |
284 | + char **exec_table; | |
285 | + struct aa_dfa *file_rules; | |
286 | + struct { | |
287 | + int hat; | |
288 | + int complain; | |
289 | + int audit; | |
290 | + } flags; | |
291 | + int isstale; | |
292 | + | |
293 | + kernel_cap_t set_caps; | |
294 | + kernel_cap_t capabilities; | |
295 | + kernel_cap_t audit_caps; | |
296 | + kernel_cap_t quiet_caps; | |
297 | + | |
298 | + struct kref count; | |
299 | + struct list_head task_contexts; | |
300 | + spinlock_t lock; | |
301 | + unsigned long int_flags; | |
302 | +}; | |
303 | + | |
304 | +extern struct list_head profile_ns_list; | |
305 | +extern rwlock_t profile_ns_list_lock; | |
306 | +extern struct mutex aa_interface_lock; | |
307 | + | |
308 | +/** | |
309 | + * struct aa_task_context - primary label for confined tasks | |
310 | + * @profile: the current profile | |
311 | + * @previous_profile: profile the task may return to | |
312 | + * @cookie: magic value the task must know for returning to @previous_profile | |
313 | + * @list: list this aa_task_context is on | |
314 | + * @task: task that the aa_task_context confines | |
315 | + * @rcu: rcu head used when freeing the aa_task_context | |
316 | + * @caps_logged: caps that have previously generated log entries | |
317 | + * | |
318 | + * Contains the task's current profile (which could change due to | |
319 | + * change_hat). Plus the hat_magic needed during change_hat. | |
320 | + */ | |
321 | +struct aa_task_context { | |
322 | + struct aa_profile *profile; | |
323 | + struct aa_profile *previous_profile; | |
324 | + u64 cookie; | |
325 | + struct list_head list; | |
326 | + struct task_struct *task; | |
327 | + struct rcu_head rcu; | |
328 | + kernel_cap_t caps_logged; | |
329 | +}; | |
330 | + | |
331 | +extern struct aa_namespace *default_namespace; | |
332 | + | |
333 | +/* aa_audit - AppArmor auditing structure | |
334 | + * Structure is populated by access control code and passed to aa_audit which | |
335 | + * provides for a single point of logging. | |
336 | + */ | |
337 | + | |
338 | +struct aa_audit { | |
339 | + const char *operation; | |
340 | + gfp_t gfp_mask; | |
341 | + const char *info; | |
342 | + const char *name; | |
343 | + const char *name2; | |
344 | + const char *name3; | |
345 | + int request_mask, denied_mask, audit_mask; | |
346 | + struct iattr *iattr; | |
347 | + pid_t task, parent; | |
348 | + int error_code; | |
349 | +}; | |
350 | + | |
351 | +/* Flags for the permission check functions */ | |
352 | +#define AA_CHECK_FD 1 /* coming from a file descriptor */ | |
353 | +#define AA_CHECK_DIR 2 /* file type is directory */ | |
354 | + | |
355 | +/* lock subtypes so lockdep does not raise false dependencies */ | |
356 | +enum aa_lock_class { | |
357 | + aa_lock_normal, | |
358 | + aa_lock_nested, | |
359 | + aa_lock_task_release | |
360 | +}; | |
361 | + | |
362 | +/* main.c */ | |
363 | +extern int alloc_default_namespace(void); | |
364 | +extern void free_default_namespace(void); | |
365 | +extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa, | |
366 | + int type); | |
367 | +void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa); | |
368 | +void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa); | |
369 | +int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa); | |
370 | +extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp, | |
371 | + const char *); | |
372 | +extern int aa_audit(struct aa_profile *profile, struct aa_audit *); | |
373 | + | |
374 | +extern int aa_attr(struct aa_profile *profile, struct dentry *dentry, | |
375 | + struct vfsmount *mnt, struct iattr *iattr); | |
376 | +extern int aa_perm_xattr(struct aa_profile *profile, const char *operation, | |
377 | + struct dentry *dentry, struct vfsmount *mnt, | |
378 | + int mask, int check); | |
379 | +extern int aa_capability(struct aa_task_context *cxt, int cap); | |
380 | +extern int aa_perm(struct aa_profile *profile, const char *operation, | |
381 | + struct dentry *dentry, struct vfsmount *mnt, int mask, | |
382 | + int check); | |
383 | +extern int aa_perm_dir(struct aa_profile *profile, const char *operation, | |
384 | + struct dentry *dentry, struct vfsmount *mnt, | |
385 | + int mask); | |
386 | +extern int aa_perm_path(struct aa_profile *, const char *operation, | |
387 | + const char *name, int mask, uid_t uid); | |
388 | +extern int aa_link(struct aa_profile *profile, | |
389 | + struct dentry *link, struct vfsmount *link_mnt, | |
390 | + struct dentry *target, struct vfsmount *target_mnt); | |
391 | +extern int aa_clone(struct task_struct *task); | |
392 | +extern int aa_register(struct linux_binprm *bprm); | |
393 | +extern void aa_release(struct task_struct *task); | |
394 | +extern int aa_change_hat(const char *id, u64 hat_magic); | |
395 | +extern int aa_change_profile(const char *ns_name, const char *name); | |
396 | +extern struct aa_profile *__aa_replace_profile(struct task_struct *task, | |
397 | + struct aa_profile *profile); | |
398 | +extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task, | |
399 | + struct aa_profile *profile); | |
400 | +extern void unlock_task_and_profiles(struct task_struct *task, | |
401 | + struct aa_task_context *cxt, | |
402 | + struct aa_profile *profile); | |
403 | +extern void aa_change_task_context(struct task_struct *task, | |
404 | + struct aa_task_context *new_cxt, | |
405 | + struct aa_profile *profile, u64 cookie, | |
406 | + struct aa_profile *previous_profile); | |
407 | +extern int aa_may_ptrace(struct aa_task_context *cxt, | |
408 | + struct aa_profile *tracee); | |
409 | + | |
410 | +/* lsm.c */ | |
411 | +extern int apparmor_initialized; | |
412 | +extern void info_message(const char *str); | |
413 | +extern void apparmor_disable(void); | |
414 | + | |
415 | +/* list.c */ | |
416 | +extern struct aa_namespace *__aa_find_namespace(const char *name, | |
417 | + struct list_head *list); | |
418 | +extern struct aa_profile *__aa_find_profile(const char *name, | |
419 | + struct list_head *list); | |
420 | +extern void aa_profile_ns_list_release(void); | |
421 | + | |
422 | +/* module_interface.c */ | |
423 | +extern ssize_t aa_add_profile(void *, size_t); | |
424 | +extern ssize_t aa_replace_profile(void *, size_t); | |
425 | +extern ssize_t aa_remove_profile(char *, size_t); | |
426 | +extern struct aa_namespace *alloc_aa_namespace(char *name); | |
427 | +extern void free_aa_namespace(struct aa_namespace *ns); | |
428 | +extern void free_aa_namespace_kref(struct kref *kref); | |
429 | +extern struct aa_profile *alloc_aa_profile(void); | |
430 | +extern void free_aa_profile(struct aa_profile *profile); | |
431 | +extern void free_aa_profile_kref(struct kref *kref); | |
432 | +extern void aa_unconfine_tasks(struct aa_profile *profile); | |
433 | + | |
434 | +/* procattr.c */ | |
435 | +extern int aa_getprocattr(struct aa_profile *profile, char **string, | |
436 | + unsigned *len); | |
437 | +extern int aa_setprocattr_changehat(char *args); | |
438 | +extern int aa_setprocattr_changeprofile(char *args); | |
439 | +extern int aa_setprocattr_setprofile(struct task_struct *task, char *args); | |
440 | + | |
441 | +/* apparmorfs.c */ | |
442 | +extern int create_apparmorfs(void); | |
443 | +extern void destroy_apparmorfs(void); | |
444 | + | |
445 | +/* match.c */ | |
446 | +extern struct aa_dfa *aa_match_alloc(void); | |
447 | +extern void aa_match_free(struct aa_dfa *dfa); | |
448 | +extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size); | |
449 | +extern int verify_dfa(struct aa_dfa *dfa); | |
450 | +extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *); | |
451 | +extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start, | |
452 | + const char *str); | |
453 | +extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start, | |
454 | + const char *str, unsigned int *final); | |
455 | +extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | |
456 | + unsigned int start); | |
457 | + | |
458 | +#endif /* __APPARMOR_H */ | |
459 | --- /dev/null | |
460 | +++ b/security/apparmor/apparmorfs.c | |
461 | @@ -0,0 +1,281 @@ | |
462 | +/* | |
463 | + * Copyright (C) 1998-2007 Novell/SUSE | |
464 | + * | |
465 | + * This program is free software; you can redistribute it and/or | |
466 | + * modify it under the terms of the GNU General Public License as | |
467 | + * published by the Free Software Foundation, version 2 of the | |
468 | + * License. | |
469 | + * | |
470 | + * AppArmor filesystem (part of securityfs) | |
471 | + */ | |
472 | + | |
473 | +#include <linux/security.h> | |
474 | +#include <linux/vmalloc.h> | |
475 | +#include <linux/module.h> | |
476 | +#include <linux/seq_file.h> | |
477 | +#include <asm/uaccess.h> | |
478 | +#include <linux/namei.h> | |
479 | + | |
480 | +#include "apparmor.h" | |
481 | +#include "inline.h" | |
482 | + | |
483 | +static char *aa_simple_write_to_buffer(const char __user *userbuf, | |
484 | + size_t alloc_size, size_t copy_size, | |
485 | + loff_t *pos, const char *operation) | |
486 | +{ | |
487 | + struct aa_profile *profile; | |
488 | + char *data; | |
489 | + | |
490 | + if (*pos != 0) { | |
491 | + /* only writes from pos 0, that is complete writes */ | |
492 | + data = ERR_PTR(-ESPIPE); | |
493 | + goto out; | |
494 | + } | |
495 | + | |
496 | + /* | |
497 | + * Don't allow confined processes to load/replace/remove profiles. | |
498 | + * No sane person would add rules allowing this to a profile | |
499 | + * but we enforce the restriction anyways. | |
500 | + */ | |
501 | + profile = aa_get_profile(current); | |
502 | + if (profile) { | |
503 | + struct aa_audit sa; | |
504 | + memset(&sa, 0, sizeof(sa)); | |
505 | + sa.operation = operation; | |
506 | + sa.gfp_mask = GFP_KERNEL; | |
507 | + sa.error_code = -EACCES; | |
508 | + data = ERR_PTR(aa_audit_reject(profile, &sa)); | |
509 | + aa_put_profile(profile); | |
510 | + goto out; | |
511 | + } | |
512 | + | |
513 | + data = vmalloc(alloc_size); | |
514 | + if (data == NULL) { | |
515 | + data = ERR_PTR(-ENOMEM); | |
516 | + goto out; | |
517 | + } | |
518 | + | |
519 | + if (copy_from_user(data, userbuf, copy_size)) { | |
520 | + vfree(data); | |
521 | + data = ERR_PTR(-EFAULT); | |
522 | + goto out; | |
523 | + } | |
524 | + | |
525 | +out: | |
526 | + return data; | |
527 | +} | |
528 | + | |
529 | +/* apparmor/profiles */ | |
530 | +extern struct seq_operations apparmorfs_profiles_op; | |
531 | + | |
532 | +static int aa_profiles_open(struct inode *inode, struct file *file) | |
533 | +{ | |
534 | + return seq_open(file, &apparmorfs_profiles_op); | |
535 | +} | |
536 | + | |
537 | + | |
538 | +static int aa_profiles_release(struct inode *inode, struct file *file) | |
539 | +{ | |
540 | + return seq_release(inode, file); | |
541 | +} | |
542 | + | |
543 | +static struct file_operations apparmorfs_profiles_fops = { | |
544 | + .open = aa_profiles_open, | |
545 | + .read = seq_read, | |
546 | + .llseek = seq_lseek, | |
547 | + .release = aa_profiles_release, | |
548 | +}; | |
549 | + | |
550 | +/* apparmor/matching */ | |
551 | +static ssize_t aa_matching_read(struct file *file, char __user *buf, | |
552 | + size_t size, loff_t *ppos) | |
553 | +{ | |
554 | + const char *matching = "pattern=aadfa audit perms=rwxamlk/ user::other"; | |
555 | + | |
556 | + return simple_read_from_buffer(buf, size, ppos, matching, | |
557 | + strlen(matching)); | |
558 | +} | |
559 | + | |
560 | +static struct file_operations apparmorfs_matching_fops = { | |
561 | + .read = aa_matching_read, | |
562 | +}; | |
563 | + | |
564 | +/* apparmor/features */ | |
565 | +static ssize_t aa_features_read(struct file *file, char __user *buf, | |
566 | + size_t size, loff_t *ppos) | |
567 | +{ | |
568 | + const char *features = "file=3.0 capability=2.0 network=1.0 " | |
569 | + "change_hat=1.5 change_profile=1.0 " | |
570 | + "aanamespaces=1.0"; | |
571 | + | |
572 | + return simple_read_from_buffer(buf, size, ppos, features, | |
573 | + strlen(features)); | |
574 | +} | |
575 | + | |
576 | +static struct file_operations apparmorfs_features_fops = { | |
577 | + .read = aa_features_read, | |
578 | +}; | |
579 | + | |
580 | +/* apparmor/.load */ | |
581 | +static ssize_t aa_profile_load(struct file *f, const char __user *buf, | |
582 | + size_t size, loff_t *pos) | |
583 | +{ | |
584 | + char *data; | |
585 | + ssize_t error; | |
586 | + | |
587 | + data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load"); | |
588 | + | |
589 | + error = PTR_ERR(data); | |
590 | + if (!IS_ERR(data)) { | |
591 | + error = aa_add_profile(data, size); | |
592 | + vfree(data); | |
593 | + } | |
594 | + | |
595 | + return error; | |
596 | +} | |
597 | + | |
598 | + | |
599 | +static struct file_operations apparmorfs_profile_load = { | |
600 | + .write = aa_profile_load | |
601 | +}; | |
602 | + | |
603 | +/* apparmor/.replace */ | |
604 | +static ssize_t aa_profile_replace(struct file *f, const char __user *buf, | |
605 | + size_t size, loff_t *pos) | |
606 | +{ | |
607 | + char *data; | |
608 | + ssize_t error; | |
609 | + | |
610 | + data = aa_simple_write_to_buffer(buf, size, size, pos, | |
611 | + "profile_replace"); | |
612 | + | |
613 | + error = PTR_ERR(data); | |
614 | + if (!IS_ERR(data)) { | |
615 | + error = aa_replace_profile(data, size); | |
616 | + vfree(data); | |
617 | + } | |
618 | + | |
619 | + return error; | |
620 | +} | |
621 | + | |
622 | + | |
623 | +static struct file_operations apparmorfs_profile_replace = { | |
624 | + .write = aa_profile_replace | |
625 | +}; | |
626 | + | |
627 | +/* apparmor/.remove */ | |
628 | +static ssize_t aa_profile_remove(struct file *f, const char __user *buf, | |
629 | + size_t size, loff_t *pos) | |
630 | +{ | |
631 | + char *data; | |
632 | + ssize_t error; | |
633 | + | |
634 | + /* | |
635 | + * aa_remove_profile needs a null terminated string so 1 extra | |
636 | + * byte is allocated and the copied data is null terminated. | |
637 | + */ | |
638 | + data = aa_simple_write_to_buffer(buf, size + 1, size, pos, | |
639 | + "profile_remove"); | |
640 | + | |
641 | + error = PTR_ERR(data); | |
642 | + if (!IS_ERR(data)) { | |
643 | + data[size] = 0; | |
644 | + error = aa_remove_profile(data, size); | |
645 | + vfree(data); | |
646 | + } | |
647 | + | |
648 | + return error; | |
649 | +} | |
650 | + | |
651 | +static struct file_operations apparmorfs_profile_remove = { | |
652 | + .write = aa_profile_remove | |
653 | +}; | |
654 | + | |
655 | +static struct dentry *apparmor_dentry; | |
656 | + | |
657 | +static void aafs_remove(const char *name) | |
658 | +{ | |
659 | + struct dentry *dentry; | |
660 | + | |
661 | + dentry = lookup_one_len(name, apparmor_dentry, strlen(name)); | |
662 | + if (!IS_ERR(dentry)) { | |
663 | + securityfs_remove(dentry); | |
664 | + dput(dentry); | |
665 | + } | |
666 | +} | |
667 | + | |
668 | +static int aafs_create(const char *name, int mask, struct file_operations *fops) | |
669 | +{ | |
670 | + struct dentry *dentry; | |
671 | + | |
672 | + dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry, | |
673 | + NULL, fops); | |
674 | + | |
675 | + return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; | |
676 | +} | |
677 | + | |
678 | +void destroy_apparmorfs(void) | |
679 | +{ | |
680 | + if (apparmor_dentry) { | |
681 | + aafs_remove(".remove"); | |
682 | + aafs_remove(".replace"); | |
683 | + aafs_remove(".load"); | |
684 | + aafs_remove("matching"); | |
685 | + aafs_remove("features"); | |
686 | + aafs_remove("profiles"); | |
687 | + securityfs_remove(apparmor_dentry); | |
688 | + apparmor_dentry = NULL; | |
689 | + } | |
690 | +} | |
691 | + | |
692 | +int create_apparmorfs(void) | |
693 | +{ | |
694 | + int error; | |
695 | + | |
696 | + if (!apparmor_initialized) | |
697 | + return 0; | |
698 | + | |
699 | + if (apparmor_dentry) { | |
700 | + AA_ERROR("%s: AppArmor securityfs already exists\n", | |
701 | + __FUNCTION__); | |
702 | + return -EEXIST; | |
703 | + } | |
704 | + | |
705 | + apparmor_dentry = securityfs_create_dir("apparmor", NULL); | |
706 | + if (IS_ERR(apparmor_dentry)) { | |
707 | + error = PTR_ERR(apparmor_dentry); | |
708 | + apparmor_dentry = NULL; | |
709 | + goto error; | |
710 | + } | |
711 | + error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops); | |
712 | + if (error) | |
713 | + goto error; | |
714 | + error = aafs_create("matching", 0444, &apparmorfs_matching_fops); | |
715 | + if (error) | |
716 | + goto error; | |
717 | + error = aafs_create("features", 0444, &apparmorfs_features_fops); | |
718 | + if (error) | |
719 | + goto error; | |
720 | + error = aafs_create(".load", 0640, &apparmorfs_profile_load); | |
721 | + if (error) | |
722 | + goto error; | |
723 | + error = aafs_create(".replace", 0640, &apparmorfs_profile_replace); | |
724 | + if (error) | |
725 | + goto error; | |
726 | + error = aafs_create(".remove", 0640, &apparmorfs_profile_remove); | |
727 | + if (error) | |
728 | + goto error; | |
729 | + | |
730 | + /* Report that AppArmor fs is enabled */ | |
731 | + info_message("AppArmor Filesystem Enabled"); | |
732 | + return 0; | |
733 | + | |
734 | +error: | |
735 | + destroy_apparmorfs(); | |
736 | + AA_ERROR("Error creating AppArmor securityfs\n"); | |
737 | + apparmor_disable(); | |
738 | + return error; | |
739 | +} | |
740 | + | |
741 | +fs_initcall(create_apparmorfs); | |
742 | + | |
743 | --- /dev/null | |
744 | +++ b/security/apparmor/inline.h | |
745 | @@ -0,0 +1,250 @@ | |
746 | +/* | |
747 | + * Copyright (C) 1998-2007 Novell/SUSE | |
748 | + * | |
749 | + * This program is free software; you can redistribute it and/or | |
750 | + * modify it under the terms of the GNU General Public License as | |
751 | + * published by the Free Software Foundation, version 2 of the | |
752 | + * License. | |
753 | + */ | |
754 | + | |
755 | +#ifndef __INLINE_H | |
756 | +#define __INLINE_H | |
757 | + | |
758 | +#include <linux/sched.h> | |
759 | + | |
760 | +#include "match.h" | |
761 | + | |
762 | +static inline int mediated_filesystem(struct inode *inode) | |
763 | +{ | |
764 | + return !(inode->i_sb->s_flags & MS_NOUSER); | |
765 | +} | |
766 | + | |
767 | +static inline struct aa_task_context *aa_task_context(struct task_struct *task) | |
768 | +{ | |
769 | + return (struct aa_task_context *) rcu_dereference(task->security); | |
770 | +} | |
771 | + | |
772 | +static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | |
773 | +{ | |
774 | + if (ns) | |
775 | + kref_get(&(ns->count)); | |
776 | + | |
777 | + return ns; | |
778 | +} | |
779 | + | |
780 | +static inline void aa_put_namespace(struct aa_namespace *ns) | |
781 | +{ | |
782 | + if (ns) | |
783 | + kref_put(&ns->count, free_aa_namespace_kref); | |
784 | +} | |
785 | + | |
786 | + | |
787 | +static inline struct aa_namespace *aa_find_namespace(const char *name) | |
788 | +{ | |
789 | + struct aa_namespace *ns = NULL; | |
790 | + | |
791 | + read_lock(&profile_ns_list_lock); | |
792 | + ns = aa_get_namespace(__aa_find_namespace(name, &profile_ns_list)); | |
793 | + read_unlock(&profile_ns_list_lock); | |
794 | + | |
795 | + return ns; | |
796 | +} | |
797 | + | |
798 | +/** | |
799 | + * aa_dup_profile - increment refcount on profile @p | |
800 | + * @p: profile | |
801 | + */ | |
802 | +static inline struct aa_profile *aa_dup_profile(struct aa_profile *p) | |
803 | +{ | |
804 | + if (p) | |
805 | + kref_get(&(p->count)); | |
806 | + | |
807 | + return p; | |
808 | +} | |
809 | + | |
810 | +/** | |
811 | + * aa_put_profile - decrement refcount on profile @p | |
812 | + * @p: profile | |
813 | + */ | |
814 | +static inline void aa_put_profile(struct aa_profile *p) | |
815 | +{ | |
816 | + if (p) | |
817 | + kref_put(&p->count, free_aa_profile_kref); | |
818 | +} | |
819 | + | |
820 | +static inline struct aa_profile *aa_get_profile(struct task_struct *task) | |
821 | +{ | |
822 | + struct aa_task_context *cxt; | |
823 | + struct aa_profile *profile = NULL; | |
824 | + | |
825 | + rcu_read_lock(); | |
826 | + cxt = aa_task_context(task); | |
827 | + if (cxt) { | |
828 | + profile = cxt->profile; | |
829 | + aa_dup_profile(profile); | |
830 | + } | |
831 | + rcu_read_unlock(); | |
832 | + | |
833 | + return profile; | |
834 | +} | |
835 | + | |
836 | +static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns, | |
837 | + const char *name) | |
838 | +{ | |
839 | + struct aa_profile *profile = NULL; | |
840 | + | |
841 | + read_lock(&ns->lock); | |
842 | + profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles)); | |
843 | + read_unlock(&ns->lock); | |
844 | + | |
845 | + return profile; | |
846 | +} | |
847 | + | |
848 | +static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags) | |
849 | +{ | |
850 | + struct aa_task_context *cxt; | |
851 | + | |
852 | + cxt = kzalloc(sizeof(*cxt), flags); | |
853 | + if (cxt) { | |
854 | + INIT_LIST_HEAD(&cxt->list); | |
855 | + INIT_RCU_HEAD(&cxt->rcu); | |
856 | + } | |
857 | + | |
858 | + return cxt; | |
859 | +} | |
860 | + | |
861 | +static inline void aa_free_task_context(struct aa_task_context *cxt) | |
862 | +{ | |
863 | + if (cxt) { | |
864 | + aa_put_profile(cxt->profile); | |
865 | + aa_put_profile(cxt->previous_profile); | |
866 | + kfree(cxt); | |
867 | + } | |
868 | +} | |
869 | + | |
870 | +/** | |
871 | + * lock_profile - lock a profile | |
872 | + * @profile: the profile to lock | |
873 | + * | |
874 | + * While the profile is locked, local interrupts are disabled. This also | |
875 | + * gives us RCU reader safety. | |
876 | + */ | |
877 | +static inline void lock_profile_nested(struct aa_profile *profile, | |
878 | + enum aa_lock_class lock_class) | |
879 | +{ | |
880 | + /* | |
881 | + * Lock the profile. | |
882 | + * | |
883 | + * Need to disable interrupts here because this lock is used in | |
884 | + * the task_free_security hook, which may run in RCU context. | |
885 | + */ | |
886 | + if (profile) | |
887 | + spin_lock_irqsave_nested(&profile->lock, profile->int_flags, | |
888 | + lock_class); | |
889 | +} | |
890 | + | |
891 | +static inline void lock_profile(struct aa_profile *profile) | |
892 | +{ | |
893 | + lock_profile_nested(profile, aa_lock_normal); | |
894 | +} | |
895 | + | |
896 | +/** | |
897 | + * unlock_profile - unlock a profile | |
898 | + * @profile: the profile to unlock | |
899 | + */ | |
900 | +static inline void unlock_profile(struct aa_profile *profile) | |
901 | +{ | |
902 | + /* Unlock the profile. */ | |
903 | + if (profile) | |
904 | + spin_unlock_irqrestore(&profile->lock, profile->int_flags); | |
905 | +} | |
906 | + | |
907 | +/** | |
908 | + * lock_both_profiles - lock two profiles in a deadlock-free way | |
909 | + * @profile1: profile to lock (may be NULL) | |
910 | + * @profile2: profile to lock (may be NULL) | |
911 | + * | |
912 | + * The order in which profiles are passed into lock_both_profiles() / | |
913 | + * unlock_both_profiles() does not matter. | |
914 | + * While the profile is locked, local interrupts are disabled. This also | |
915 | + * gives us RCU reader safety. | |
916 | + */ | |
917 | +static inline void lock_both_profiles(struct aa_profile *profile1, | |
918 | + struct aa_profile *profile2) | |
919 | +{ | |
920 | + /* | |
921 | + * Lock the two profiles. | |
922 | + * | |
923 | + * We need to disable interrupts because the profile locks are | |
924 | + * used in the task_free_security hook, which may run in RCU | |
925 | + * context. | |
926 | + * | |
927 | + * Do not nest spin_lock_irqsave()/spin_unlock_irqresore(): | |
928 | + * interrupts only need to be turned off once. | |
929 | + */ | |
930 | + if (!profile1 || profile1 == profile2) { | |
931 | + if (profile2) | |
932 | + spin_lock_irqsave_nested(&profile2->lock, | |
933 | + profile2->int_flags, | |
934 | + aa_lock_normal); | |
935 | + } else if (profile1 > profile2) { | |
936 | + /* profile1 cannot be NULL here. */ | |
937 | + spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags, | |
938 | + aa_lock_normal); | |
939 | + if (profile2) | |
940 | + spin_lock_nested(&profile2->lock, aa_lock_nested); | |
941 | + | |
942 | + } else { | |
943 | + /* profile2 cannot be NULL here. */ | |
944 | + spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags, | |
945 | + aa_lock_normal); | |
946 | + spin_lock_nested(&profile1->lock, aa_lock_nested); | |
947 | + } | |
948 | +} | |
949 | + | |
950 | +/** | |
951 | + * unlock_both_profiles - unlock two profiles in a deadlock-free way | |
952 | + * @profile1: profile to unlock (may be NULL) | |
953 | + * @profile2: profile to unlock (may be NULL) | |
954 | + * | |
955 | + * The order in which profiles are passed into lock_both_profiles() / | |
956 | + * unlock_both_profiles() does not matter. | |
957 | + * While the profile is locked, local interrupts are disabled. This also | |
958 | + * gives us RCU reader safety. | |
959 | + */ | |
960 | +static inline void unlock_both_profiles(struct aa_profile *profile1, | |
961 | + struct aa_profile *profile2) | |
962 | +{ | |
963 | + /* Unlock the two profiles. */ | |
964 | + if (!profile1 || profile1 == profile2) { | |
965 | + if (profile2) | |
966 | + spin_unlock_irqrestore(&profile2->lock, | |
967 | + profile2->int_flags); | |
968 | + } else if (profile1 > profile2) { | |
969 | + /* profile1 cannot be NULL here. */ | |
970 | + if (profile2) | |
971 | + spin_unlock(&profile2->lock); | |
972 | + spin_unlock_irqrestore(&profile1->lock, profile1->int_flags); | |
973 | + } else { | |
974 | + /* profile2 cannot be NULL here. */ | |
975 | + spin_unlock(&profile1->lock); | |
976 | + spin_unlock_irqrestore(&profile2->lock, profile2->int_flags); | |
977 | + } | |
978 | +} | |
979 | + | |
980 | +static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname, | |
981 | + int *audit_mask) | |
982 | +{ | |
983 | + if (dfa) | |
984 | + return aa_dfa_match(dfa, pathname, audit_mask); | |
985 | + if (audit_mask) | |
986 | + *audit_mask = 0; | |
987 | + return 0; | |
988 | +} | |
989 | + | |
990 | +static inline int dfa_audit_mask(struct aa_dfa *dfa, unsigned int state) | |
991 | +{ | |
992 | + return ACCEPT_TABLE2(dfa)[state]; | |
993 | +} | |
994 | + | |
995 | +#endif /* __INLINE_H__ */ | |
996 | --- /dev/null | |
997 | +++ b/security/apparmor/list.c | |
998 | @@ -0,0 +1,174 @@ | |
999 | +/* | |
1000 | + * Copyright (C) 1998-2007 Novell/SUSE | |
1001 | + * | |
1002 | + * This program is free software; you can redistribute it and/or | |
1003 | + * modify it under the terms of the GNU General Public License as | |
1004 | + * published by the Free Software Foundation, version 2 of the | |
1005 | + * License. | |
1006 | + * | |
1007 | + * AppArmor Profile List Management | |
1008 | + */ | |
1009 | + | |
1010 | +#include <linux/seq_file.h> | |
1011 | +#include "apparmor.h" | |
1012 | +#include "inline.h" | |
1013 | + | |
1014 | +/* list of profile namespaces and lock */ | |
1015 | +LIST_HEAD(profile_ns_list); | |
1016 | +rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED; | |
1017 | + | |
1018 | +/** | |
1019 | + * __aa_find_namespace - look up a profile namespace on the namespace list | |
1020 | + * @name: name of namespace to find | |
1021 | + * @head: list to search | |
1022 | + * | |
1023 | + * Returns a pointer to the namespace on the list, or NULL if no namespace | |
1024 | + * called @name exists. The caller must hold the profile_ns_list_lock. | |
1025 | + */ | |
1026 | +struct aa_namespace *__aa_find_namespace(const char *name, | |
1027 | + struct list_head *head) | |
1028 | +{ | |
1029 | + struct aa_namespace *ns; | |
1030 | + | |
1031 | + list_for_each_entry(ns, head, list) { | |
1032 | + if (!strcmp(ns->name, name)) | |
1033 | + return ns; | |
1034 | + } | |
1035 | + | |
1036 | + return NULL; | |
1037 | +} | |
1038 | + | |
1039 | +/** | |
1040 | + * __aa_find_profile - look up a profile on the profile list | |
1041 | + * @name: name of profile to find | |
1042 | + * @head: list to search | |
1043 | + * | |
1044 | + * Returns a pointer to the profile on the list, or NULL if no profile | |
1045 | + * called @name exists. The caller must hold the profile_list_lock. | |
1046 | + */ | |
1047 | +struct aa_profile *__aa_find_profile(const char *name, struct list_head *head) | |
1048 | +{ | |
1049 | + struct aa_profile *profile; | |
1050 | + | |
1051 | + list_for_each_entry(profile, head, list) { | |
1052 | + if (!strcmp(profile->name, name)) | |
1053 | + return profile; | |
1054 | + } | |
1055 | + | |
1056 | + return NULL; | |
1057 | +} | |
1058 | + | |
1059 | +static void aa_profile_list_release(struct list_head *head) | |
1060 | +{ | |
1061 | + struct aa_profile *profile, *tmp; | |
1062 | + list_for_each_entry_safe(profile, tmp, head, list) { | |
1063 | + /* Remove the profile from each task context it is on. */ | |
1064 | + lock_profile(profile); | |
1065 | + profile->isstale = 1; | |
1066 | + aa_unconfine_tasks(profile); | |
1067 | + list_del_init(&profile->list); | |
1068 | + unlock_profile(profile); | |
1069 | + aa_put_profile(profile); | |
1070 | + } | |
1071 | +} | |
1072 | + | |
1073 | +/** | |
1074 | + * aa_profilelist_release - Remove all profiles from profile_list | |
1075 | + */ | |
1076 | +void aa_profile_ns_list_release(void) | |
1077 | +{ | |
1078 | + struct aa_namespace *ns, *tmp; | |
1079 | + | |
1080 | + /* Remove and release all the profiles on namespace profile lists. */ | |
1081 | + write_lock(&profile_ns_list_lock); | |
1082 | + list_for_each_entry_safe(ns, tmp, &profile_ns_list, list) { | |
1083 | + write_lock(&ns->lock); | |
1084 | + aa_profile_list_release(&ns->profiles); | |
1085 | + list_del_init(&ns->list); | |
1086 | + write_unlock(&ns->lock); | |
1087 | + aa_put_namespace(ns); | |
1088 | + } | |
1089 | + write_unlock(&profile_ns_list_lock); | |
1090 | +} | |
1091 | + | |
1092 | + | |
1093 | +static struct aa_profile *next_profile(struct aa_profile *profile) | |
1094 | +{ | |
1095 | + struct aa_profile *next = profile; | |
1096 | + struct aa_namespace *ns; | |
1097 | + | |
1098 | + list_for_each_entry_continue(next, &profile->ns->profiles, list) | |
1099 | + return next; | |
1100 | + | |
1101 | + ns = profile->ns; | |
1102 | + read_unlock(&ns->lock); | |
1103 | + list_for_each_entry_continue(ns, &profile_ns_list, list) { | |
1104 | + read_lock(&ns->lock); | |
1105 | + list_for_each_entry(profile, &ns->profiles, list) | |
1106 | + return profile; | |
1107 | + read_unlock(&ns->lock); | |
1108 | + } | |
1109 | + return NULL; | |
1110 | +} | |
1111 | + | |
1112 | +static void *p_start(struct seq_file *f, loff_t *pos) | |
1113 | +{ | |
1114 | + struct aa_namespace *ns; | |
1115 | + loff_t l = *pos; | |
1116 | + | |
1117 | + read_lock(&profile_ns_list_lock); | |
1118 | + if (!list_empty(&profile_ns_list)) { | |
1119 | + struct aa_profile *profile = NULL; | |
1120 | + ns = list_first_entry(&profile_ns_list, typeof(*ns), list); | |
1121 | + read_lock(&ns->lock); | |
1122 | + if (!list_empty(&ns->profiles)) | |
1123 | + profile = list_first_entry(&ns->profiles, | |
1124 | + typeof(*profile), list); | |
1125 | + else | |
1126 | + read_unlock(&ns->lock); | |
1127 | + for ( ; profile && l > 0; l--) | |
1128 | + profile = next_profile(profile); | |
1129 | + return profile; | |
1130 | + } | |
1131 | + return NULL; | |
1132 | +} | |
1133 | + | |
1134 | +static void *p_next(struct seq_file *f, void *p, loff_t *pos) | |
1135 | +{ | |
1136 | + struct aa_profile *profile = (struct aa_profile *) p; | |
1137 | + | |
1138 | + (*pos)++; | |
1139 | + profile = next_profile(profile); | |
1140 | + | |
1141 | + return profile; | |
1142 | +} | |
1143 | + | |
1144 | +static void p_stop(struct seq_file *f, void *p) | |
1145 | +{ | |
1146 | + struct aa_profile *profile = (struct aa_profile *) p; | |
1147 | + | |
1148 | + if (profile) | |
1149 | + read_unlock(&profile->ns->lock); | |
1150 | + read_unlock(&profile_ns_list_lock); | |
1151 | +} | |
1152 | + | |
1153 | +static int seq_show_profile(struct seq_file *f, void *p) | |
1154 | +{ | |
1155 | + struct aa_profile *profile = (struct aa_profile *)p; | |
1156 | + | |
1157 | + if (profile->ns == default_namespace) | |
1158 | + seq_printf(f, "%s (%s)\n", profile->name, | |
1159 | + PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); | |
1160 | + else | |
1161 | + seq_printf(f, ":%s:%s (%s)\n", profile->ns->name, profile->name, | |
1162 | + PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); | |
1163 | + return 0; | |
1164 | +} | |
1165 | + | |
1166 | +/* Used in apparmorfs.c */ | |
1167 | +struct seq_operations apparmorfs_profiles_op = { | |
1168 | + .start = p_start, | |
1169 | + .next = p_next, | |
1170 | + .stop = p_stop, | |
1171 | + .show = seq_show_profile, | |
1172 | +}; | |
1173 | --- /dev/null | |
1174 | +++ b/security/apparmor/locking.txt | |
1175 | @@ -0,0 +1,68 @@ | |
1176 | +Locking in AppArmor | |
1177 | +=================== | |
1178 | + | |
1179 | +Lock hierarchy: | |
1180 | + | |
1181 | + aa_interface_lock | |
1182 | + profile_list_lock | |
1183 | + aa_profile->lock | |
1184 | + task_lock() | |
1185 | + | |
1186 | + | |
1187 | +Which lock protects what? | |
1188 | + | |
1189 | + /-----------------------+-------------------------------\ | |
1190 | + | Variable | Lock | | |
1191 | + >-----------------------+-------------------------------< | |
1192 | + | profile_list | profile_list_lock | | |
1193 | + +-----------------------+-------------------------------+ | |
1194 | + | aa_profile | (reference count) | | |
1195 | + +-----------------------+-------------------------------+ | |
1196 | + | aa_profile-> | aa_profile->lock | | |
1197 | + | isstale, | | | |
1198 | + | task_contexts | | | |
1199 | + +-----------------------+-------------------------------+ | |
1200 | + | task_struct->security | read: RCU | | |
1201 | + | | write: task_lock() | | |
1202 | + +-----------------------+-------------------------------+ | |
1203 | + | aa_profile->sub | handle on the profile (list | | |
1204 | + | | is never modified) | | |
1205 | + \-----------------------+-------------------------------/ | |
1206 | + | |
1207 | +(Obviously, the list_heads embedded in data structures are always | |
1208 | +protected with the lock that also protects the list.) | |
1209 | + | |
1210 | +When moving a task context from one profile to another, we grab both | |
1211 | +profile locks with lock_both_profiles(). This ensures that both locks | |
1212 | +are always taken in the same order, and so we won't deadlock. | |
1213 | + | |
1214 | +Since task_struct->security is RCU protected the aa_task_struct it | |
1215 | +references is only guarenteed to exist for the rcu cycle. Where | |
1216 | +aa_task_context->profile is needed in blocking operations the | |
1217 | +profile's reference count is incremented and the profile reference | |
1218 | +is used. | |
1219 | + | |
1220 | +Profiles on profile_list are never stale: when a profile becomes stale, | |
1221 | +it is removed from profile_list at the same time (under profile_list_lock | |
1222 | +and aa_profile->lock). | |
1223 | + | |
1224 | +The aa_interface_lock is taken whenever user-space modifies the profile | |
1225 | +list, and can sleep. This ensures that profile loading/replacement/removal | |
1226 | +won't race with itself. We release the profile_list_lock as soon as | |
1227 | +possible to avoid stalling exec during profile loading/replacement/removal. | |
1228 | + | |
1229 | +AppArmor uses lock subtyping to avoid false positives from lockdep. The | |
1230 | +profile lock is often taken nested, but it is guaranteed to be in a lock | |
1231 | +safe order and not the same lock when done, so it is safe. | |
1232 | + | |
1233 | +A third lock type (aa_lock_task_release) is given to the profile lock | |
1234 | +when it is taken in soft irq context during task release (aa_release). | |
1235 | +This is to avoid a false positive between the task lock and the profile | |
1236 | +lock. In task context the profile lock wraps the task lock with irqs | |
1237 | +off, but the kernel takes the task lock with irqs enabled. This won't | |
1238 | +result in a deadlock because for a deadlock to occur the kernel must | |
1239 | +take dead task A's lock (irqs on), the rcu callback hook freeing | |
1240 | +dead task A must be run and AppArmor must be changing the profile on | |
1241 | +dead task A. The kernel should not be taking a dead task's task_lock | |
1242 | +at the same time the task is being freed by task rcu cleanup other wise | |
1243 | +the task would not be out of its quiescent period. | |
1244 | --- /dev/null | |
1245 | +++ b/security/apparmor/procattr.c | |
1246 | @@ -0,0 +1,195 @@ | |
1247 | +/* | |
1248 | + * Copyright (C) 1998-2007 Novell/SUSE | |
1249 | + * | |
1250 | + * This program is free software; you can redistribute it and/or | |
1251 | + * modify it under the terms of the GNU General Public License as | |
1252 | + * published by the Free Software Foundation, version 2 of the | |
1253 | + * License. | |
1254 | + * | |
1255 | + * AppArmor /proc/pid/attr handling | |
1256 | + */ | |
1257 | + | |
1258 | +#include "apparmor.h" | |
1259 | +#include "inline.h" | |
1260 | + | |
1261 | +int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len) | |
1262 | +{ | |
1263 | + char *str; | |
1264 | + | |
1265 | + if (profile) { | |
1266 | + const char *mode_str = PROFILE_COMPLAIN(profile) ? | |
1267 | + " (complain)" : " (enforce)"; | |
1268 | + int mode_len, name_len, ns_len = 0; | |
1269 | + | |
1270 | + mode_len = strlen(mode_str); | |
1271 | + name_len = strlen(profile->name); | |
1272 | + if (profile->ns != default_namespace) | |
1273 | + ns_len = strlen(profile->ns->name) + 2; | |
1274 | + *len = mode_len + ns_len + name_len + 1; | |
1275 | + str = kmalloc(*len, GFP_ATOMIC); | |
1276 | + if (!str) | |
1277 | + return -ENOMEM; | |
1278 | + | |
1279 | + if (ns_len) { | |
1280 | + *str++ = ':'; | |
1281 | + memcpy(str, profile->ns->name, ns_len - 2); | |
1282 | + str += ns_len - 2; | |
1283 | + *str++ = ':'; | |
1284 | + } | |
1285 | + memcpy(str, profile->name, name_len); | |
1286 | + str += name_len; | |
1287 | + memcpy(str, mode_str, mode_len); | |
1288 | + str += mode_len; | |
1289 | + *str++ = '\n'; | |
1290 | + str -= *len; | |
1291 | + } else { | |
1292 | + const char *unconfined_str = "unconfined\n"; | |
1293 | + | |
1294 | + *len = strlen(unconfined_str); | |
1295 | + str = kmalloc(*len, GFP_ATOMIC); | |
1296 | + if (!str) | |
1297 | + return -ENOMEM; | |
1298 | + | |
1299 | + memcpy(str, unconfined_str, *len); | |
1300 | + } | |
1301 | + *string = str; | |
1302 | + | |
1303 | + return 0; | |
1304 | +} | |
1305 | + | |
1306 | +static char *split_token_from_name(const char *op, char *args, u64 *cookie) | |
1307 | +{ | |
1308 | + char *name; | |
1309 | + | |
1310 | + *cookie = simple_strtoull(args, &name, 16); | |
1311 | + if ((name == args) || *name != '^') { | |
1312 | + AA_ERROR("%s: Invalid input '%s'", op, args); | |
1313 | + return ERR_PTR(-EINVAL); | |
1314 | + } | |
1315 | + | |
1316 | + name++; /* skip ^ */ | |
1317 | + if (!*name) | |
1318 | + name = NULL; | |
1319 | + return name; | |
1320 | +} | |
1321 | + | |
1322 | +int aa_setprocattr_changehat(char *args) | |
1323 | +{ | |
1324 | + char *hat; | |
1325 | + u64 cookie; | |
1326 | + | |
1327 | + hat = split_token_from_name("change_hat", args, &cookie); | |
1328 | + if (IS_ERR(hat)) | |
1329 | + return PTR_ERR(hat); | |
1330 | + | |
1331 | + if (!hat && !cookie) { | |
1332 | + AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); | |
1333 | + return -EINVAL; | |
1334 | + } | |
1335 | + | |
1336 | + AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", | |
1337 | + __FUNCTION__, cookie, hat ? hat : NULL); | |
1338 | + | |
1339 | + return aa_change_hat(hat, cookie); | |
1340 | +} | |
1341 | + | |
1342 | +int aa_setprocattr_changeprofile(char *args) | |
1343 | +{ | |
1344 | + char *name = args, *ns_name = NULL; | |
1345 | + | |
1346 | + if (name[0] == ':') { | |
1347 | + char *split = strchr(&name[1], ':'); | |
1348 | + if (split) { | |
1349 | + *split = 0; | |
1350 | + ns_name = &name[1]; | |
1351 | + name = split + 1; | |
1352 | + } | |
1353 | + } | |
1354 | + | |
1355 | + return aa_change_profile(ns_name, name); | |
1356 | +} | |
1357 | + | |
1358 | +int aa_setprocattr_setprofile(struct task_struct *task, char *args) | |
1359 | +{ | |
1360 | + struct aa_profile *old_profile, *new_profile; | |
1361 | + struct aa_namespace *ns; | |
1362 | + struct aa_audit sa; | |
1363 | + char *name, *ns_name = NULL; | |
1364 | + | |
1365 | + memset(&sa, 0, sizeof(sa)); | |
1366 | + sa.operation = "profile_set"; | |
1367 | + sa.gfp_mask = GFP_KERNEL; | |
1368 | + sa.task = task->pid; | |
1369 | + | |
1370 | + AA_DEBUG("%s: current %d\n", | |
1371 | + __FUNCTION__, current->pid); | |
1372 | + | |
1373 | + name = args; | |
1374 | + if (args[0] != '/') { | |
1375 | + char *split = strchr(args, ':'); | |
1376 | + if (split) { | |
1377 | + *split = 0; | |
1378 | + ns_name = args; | |
1379 | + name = split + 1; | |
1380 | + } | |
1381 | + } | |
1382 | + if (ns_name) | |
1383 | + ns = aa_find_namespace(ns_name); | |
1384 | + else | |
1385 | + ns = aa_get_namespace(default_namespace); | |
1386 | + if (!ns) { | |
1387 | + sa.name = ns_name; | |
1388 | + sa.info = "unknown namespace"; | |
1389 | + aa_audit_reject(NULL, &sa); | |
1390 | + aa_put_namespace(ns); | |
1391 | + return -EINVAL; | |
1392 | + } | |
1393 | + | |
1394 | +repeat: | |
1395 | + if (strcmp(name, "unconfined") == 0) | |
1396 | + new_profile = NULL; | |
1397 | + else { | |
1398 | + new_profile = aa_find_profile(ns, name); | |
1399 | + if (!new_profile) { | |
1400 | + sa.name = ns_name; | |
1401 | + sa.name2 = name; | |
1402 | + sa.info = "unknown profile"; | |
1403 | + aa_audit_reject(NULL, &sa); | |
1404 | + aa_put_namespace(ns); | |
1405 | + return -EINVAL; | |
1406 | + } | |
1407 | + } | |
1408 | + | |
1409 | + old_profile = __aa_replace_profile(task, new_profile); | |
1410 | + if (IS_ERR(old_profile)) { | |
1411 | + int error; | |
1412 | + | |
1413 | + aa_put_profile(new_profile); | |
1414 | + error = PTR_ERR(old_profile); | |
1415 | + if (error == -ESTALE) | |
1416 | + goto repeat; | |
1417 | + aa_put_namespace(ns); | |
1418 | + return error; | |
1419 | + } | |
1420 | + | |
1421 | + if (new_profile) { | |
1422 | + sa.name = ns_name; | |
1423 | + sa.name2 = name; | |
1424 | + sa.name3 = old_profile ? old_profile->name : | |
1425 | + "unconfined"; | |
1426 | + aa_audit_status(NULL, &sa); | |
1427 | + } else { | |
1428 | + if (old_profile) { | |
1429 | + sa.name = "unconfined"; | |
1430 | + sa.name2 = old_profile->name; | |
1431 | + aa_audit_status(NULL, &sa); | |
1432 | + } else { | |
1433 | + sa.info = "task is unconfined"; | |
1434 | + aa_audit_status(NULL, &sa); | |
1435 | + } | |
1436 | + } | |
1437 | + aa_put_namespace(ns); | |
1438 | + aa_put_profile(old_profile); | |
1439 | + aa_put_profile(new_profile); | |
1440 | + return 0; | |
1441 | +} |