diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-07 17:34:07 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-07 17:34:07 -0400 |
commit | 11c7b03d42a847db90862d0f9d8be6ce9b2f0553 (patch) | |
tree | 9c1d9e95c8ae09b4ee44821a9e570247d5a85a72 /security | |
parent | 6be48f2940af9ea8d93c23a0dd8e322672c92efd (diff) | |
parent | 73203361468894c3c017bfbdd9ddcbb468039604 (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Nothing major for this kernel, just maintenance updates"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (21 commits)
apparmor: add the ability to report a sha1 hash of loaded policy
apparmor: export set of capabilities supported by the apparmor module
apparmor: add the profile introspection file to interface
apparmor: add an optional profile attachment string for profiles
apparmor: add interface files for profiles and namespaces
apparmor: allow setting any profile into the unconfined state
apparmor: make free_profile available outside of policy.c
apparmor: rework namespace free path
apparmor: update how unconfined is handled
apparmor: change how profile replacement update is done
apparmor: convert profile lists to RCU based locking
apparmor: provide base for multiple profiles to be replaced at once
apparmor: add a features/policy dir to interface
apparmor: enable users to query whether apparmor is enabled
apparmor: remove minimum size check for vmalloc()
Smack: parse multiple rules per write to load2, up to PAGE_SIZE-1 bytes
Smack: network label match fix
security: smack: add a hash table to quicken smk_find_entry()
security: smack: fix memleak in smk_write_rules_list()
xattr: Constify ->name member of "struct xattr".
...
Diffstat (limited to 'security')
28 files changed, 1666 insertions, 547 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b2e321..d49c53960b60 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig | |||
@@ -29,3 +29,15 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE | |||
29 | boot. | 29 | boot. |
30 | 30 | ||
31 | If you are unsure how to answer this question, answer 1. | 31 | If you are unsure how to answer this question, answer 1. |
32 | |||
33 | config SECURITY_APPARMOR_HASH | ||
34 | bool "SHA1 hash of loaded profiles" | ||
35 | depends on SECURITY_APPARMOR | ||
36 | depends on CRYPTO | ||
37 | select CRYPTO_SHA1 | ||
38 | default y | ||
39 | |||
40 | help | ||
41 | This option selects whether sha1 hashing is done against loaded | ||
42 | profiles and exported for inspection to user space via the apparmor | ||
43 | filesystem. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74c857f..d693df874818 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |||
5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
7 | resource.o sid.o file.o | 7 | resource.o sid.o file.o |
8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o | ||
8 | 9 | ||
9 | clean-files := capability_names.h rlim_names.h | 10 | clean-files := capability_names.h rlim_names.h |
10 | 11 | ||
@@ -18,7 +19,11 @@ quiet_cmd_make-caps = GEN $@ | |||
18 | cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ | 19 | cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ |
19 | sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ | 20 | sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ |
20 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ | 21 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ |
21 | echo "};" >> $@ | 22 | echo "};" >> $@ ;\ |
23 | echo -n '\#define AA_FS_CAPS_MASK "' >> $@ ;\ | ||
24 | sed $< -r -n -e '/CAP_FS_MASK/d' \ | ||
25 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ | ||
26 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | ||
22 | 27 | ||
23 | 28 | ||
24 | # Build a lower case string table of rlimit names. | 29 | # Build a lower case string table of rlimit names. |
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec6f670..95c2b2689a03 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * License. | 12 | * License. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/ctype.h> | ||
15 | #include <linux/security.h> | 16 | #include <linux/security.h> |
16 | #include <linux/vmalloc.h> | 17 | #include <linux/vmalloc.h> |
17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
@@ -19,15 +20,56 @@ | |||
19 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
20 | #include <linux/namei.h> | 21 | #include <linux/namei.h> |
21 | #include <linux/capability.h> | 22 | #include <linux/capability.h> |
23 | #include <linux/rcupdate.h> | ||
22 | 24 | ||
23 | #include "include/apparmor.h" | 25 | #include "include/apparmor.h" |
24 | #include "include/apparmorfs.h" | 26 | #include "include/apparmorfs.h" |
25 | #include "include/audit.h" | 27 | #include "include/audit.h" |
26 | #include "include/context.h" | 28 | #include "include/context.h" |
29 | #include "include/crypto.h" | ||
27 | #include "include/policy.h" | 30 | #include "include/policy.h" |
28 | #include "include/resource.h" | 31 | #include "include/resource.h" |
29 | 32 | ||
30 | /** | 33 | /** |
34 | * aa_mangle_name - mangle a profile name to std profile layout form | ||
35 | * @name: profile name to mangle (NOT NULL) | ||
36 | * @target: buffer to store mangled name, same length as @name (MAYBE NULL) | ||
37 | * | ||
38 | * Returns: length of mangled name | ||
39 | */ | ||
40 | static int mangle_name(char *name, char *target) | ||
41 | { | ||
42 | char *t = target; | ||
43 | |||
44 | while (*name == '/' || *name == '.') | ||
45 | name++; | ||
46 | |||
47 | if (target) { | ||
48 | for (; *name; name++) { | ||
49 | if (*name == '/') | ||
50 | *(t)++ = '.'; | ||
51 | else if (isspace(*name)) | ||
52 | *(t)++ = '_'; | ||
53 | else if (isalnum(*name) || strchr("._-", *name)) | ||
54 | *(t)++ = *name; | ||
55 | } | ||
56 | |||
57 | *t = 0; | ||
58 | } else { | ||
59 | int len = 0; | ||
60 | for (; *name; name++) { | ||
61 | if (isalnum(*name) || isspace(*name) || | ||
62 | strchr("/._-", *name)) | ||
63 | len++; | ||
64 | } | ||
65 | |||
66 | return len; | ||
67 | } | ||
68 | |||
69 | return t - target; | ||
70 | } | ||
71 | |||
72 | /** | ||
31 | * aa_simple_write_to_buffer - common routine for getting policy from user | 73 | * aa_simple_write_to_buffer - common routine for getting policy from user |
32 | * @op: operation doing the user buffer copy | 74 | * @op: operation doing the user buffer copy |
33 | * @userbuf: user buffer to copy data from (NOT NULL) | 75 | * @userbuf: user buffer to copy data from (NOT NULL) |
@@ -182,8 +224,567 @@ const struct file_operations aa_fs_seq_file_ops = { | |||
182 | .release = single_release, | 224 | .release = single_release, |
183 | }; | 225 | }; |
184 | 226 | ||
185 | /** Base file system setup **/ | 227 | static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, |
228 | int (*show)(struct seq_file *, void *)) | ||
229 | { | ||
230 | struct aa_replacedby *r = aa_get_replacedby(inode->i_private); | ||
231 | int error = single_open(file, show, r); | ||
232 | |||
233 | if (error) { | ||
234 | file->private_data = NULL; | ||
235 | aa_put_replacedby(r); | ||
236 | } | ||
237 | |||
238 | return error; | ||
239 | } | ||
240 | |||
241 | static int aa_fs_seq_profile_release(struct inode *inode, struct file *file) | ||
242 | { | ||
243 | struct seq_file *seq = (struct seq_file *) file->private_data; | ||
244 | if (seq) | ||
245 | aa_put_replacedby(seq->private); | ||
246 | return single_release(inode, file); | ||
247 | } | ||
248 | |||
249 | static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) | ||
250 | { | ||
251 | struct aa_replacedby *r = seq->private; | ||
252 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
253 | seq_printf(seq, "%s\n", profile->base.name); | ||
254 | aa_put_profile(profile); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int aa_fs_seq_profname_open(struct inode *inode, struct file *file) | ||
260 | { | ||
261 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show); | ||
262 | } | ||
263 | |||
264 | static const struct file_operations aa_fs_profname_fops = { | ||
265 | .owner = THIS_MODULE, | ||
266 | .open = aa_fs_seq_profname_open, | ||
267 | .read = seq_read, | ||
268 | .llseek = seq_lseek, | ||
269 | .release = aa_fs_seq_profile_release, | ||
270 | }; | ||
271 | |||
272 | static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) | ||
273 | { | ||
274 | struct aa_replacedby *r = seq->private; | ||
275 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
276 | seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); | ||
277 | aa_put_profile(profile); | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file) | ||
283 | { | ||
284 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show); | ||
285 | } | ||
286 | |||
287 | static const struct file_operations aa_fs_profmode_fops = { | ||
288 | .owner = THIS_MODULE, | ||
289 | .open = aa_fs_seq_profmode_open, | ||
290 | .read = seq_read, | ||
291 | .llseek = seq_lseek, | ||
292 | .release = aa_fs_seq_profile_release, | ||
293 | }; | ||
294 | |||
295 | static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) | ||
296 | { | ||
297 | struct aa_replacedby *r = seq->private; | ||
298 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
299 | if (profile->attach) | ||
300 | seq_printf(seq, "%s\n", profile->attach); | ||
301 | else if (profile->xmatch) | ||
302 | seq_puts(seq, "<unknown>\n"); | ||
303 | else | ||
304 | seq_printf(seq, "%s\n", profile->base.name); | ||
305 | aa_put_profile(profile); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int aa_fs_seq_profattach_open(struct inode *inode, struct file *file) | ||
311 | { | ||
312 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profattach_show); | ||
313 | } | ||
314 | |||
315 | static const struct file_operations aa_fs_profattach_fops = { | ||
316 | .owner = THIS_MODULE, | ||
317 | .open = aa_fs_seq_profattach_open, | ||
318 | .read = seq_read, | ||
319 | .llseek = seq_lseek, | ||
320 | .release = aa_fs_seq_profile_release, | ||
321 | }; | ||
322 | |||
323 | static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) | ||
324 | { | ||
325 | struct aa_replacedby *r = seq->private; | ||
326 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
327 | unsigned int i, size = aa_hash_size(); | ||
328 | |||
329 | if (profile->hash) { | ||
330 | for (i = 0; i < size; i++) | ||
331 | seq_printf(seq, "%.2x", profile->hash[i]); | ||
332 | seq_puts(seq, "\n"); | ||
333 | } | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int aa_fs_seq_hash_open(struct inode *inode, struct file *file) | ||
339 | { | ||
340 | return single_open(file, aa_fs_seq_hash_show, inode->i_private); | ||
341 | } | ||
342 | |||
343 | static const struct file_operations aa_fs_seq_hash_fops = { | ||
344 | .owner = THIS_MODULE, | ||
345 | .open = aa_fs_seq_hash_open, | ||
346 | .read = seq_read, | ||
347 | .llseek = seq_lseek, | ||
348 | .release = single_release, | ||
349 | }; | ||
350 | |||
351 | /** fns to setup dynamic per profile/namespace files **/ | ||
352 | void __aa_fs_profile_rmdir(struct aa_profile *profile) | ||
353 | { | ||
354 | struct aa_profile *child; | ||
355 | int i; | ||
356 | |||
357 | if (!profile) | ||
358 | return; | ||
359 | |||
360 | list_for_each_entry(child, &profile->base.profiles, base.list) | ||
361 | __aa_fs_profile_rmdir(child); | ||
362 | |||
363 | for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { | ||
364 | struct aa_replacedby *r; | ||
365 | if (!profile->dents[i]) | ||
366 | continue; | ||
367 | |||
368 | r = profile->dents[i]->d_inode->i_private; | ||
369 | securityfs_remove(profile->dents[i]); | ||
370 | aa_put_replacedby(r); | ||
371 | profile->dents[i] = NULL; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | ||
376 | struct aa_profile *new) | ||
377 | { | ||
378 | int i; | ||
379 | |||
380 | for (i = 0; i < AAFS_PROF_SIZEOF; i++) { | ||
381 | new->dents[i] = old->dents[i]; | ||
382 | old->dents[i] = NULL; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | static struct dentry *create_profile_file(struct dentry *dir, const char *name, | ||
387 | struct aa_profile *profile, | ||
388 | const struct file_operations *fops) | ||
389 | { | ||
390 | struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); | ||
391 | struct dentry *dent; | ||
392 | |||
393 | dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); | ||
394 | if (IS_ERR(dent)) | ||
395 | aa_put_replacedby(r); | ||
396 | |||
397 | return dent; | ||
398 | } | ||
399 | |||
400 | /* requires lock be held */ | ||
401 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) | ||
402 | { | ||
403 | struct aa_profile *child; | ||
404 | struct dentry *dent = NULL, *dir; | ||
405 | int error; | ||
406 | |||
407 | if (!parent) { | ||
408 | struct aa_profile *p; | ||
409 | p = aa_deref_parent(profile); | ||
410 | dent = prof_dir(p); | ||
411 | /* adding to parent that previously didn't have children */ | ||
412 | dent = securityfs_create_dir("profiles", dent); | ||
413 | if (IS_ERR(dent)) | ||
414 | goto fail; | ||
415 | prof_child_dir(p) = parent = dent; | ||
416 | } | ||
417 | |||
418 | if (!profile->dirname) { | ||
419 | int len, id_len; | ||
420 | len = mangle_name(profile->base.name, NULL); | ||
421 | id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id); | ||
422 | |||
423 | profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL); | ||
424 | if (!profile->dirname) | ||
425 | goto fail; | ||
426 | |||
427 | mangle_name(profile->base.name, profile->dirname); | ||
428 | sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++); | ||
429 | } | ||
430 | |||
431 | dent = securityfs_create_dir(profile->dirname, parent); | ||
432 | if (IS_ERR(dent)) | ||
433 | goto fail; | ||
434 | prof_dir(profile) = dir = dent; | ||
435 | |||
436 | dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops); | ||
437 | if (IS_ERR(dent)) | ||
438 | goto fail; | ||
439 | profile->dents[AAFS_PROF_NAME] = dent; | ||
440 | |||
441 | dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops); | ||
442 | if (IS_ERR(dent)) | ||
443 | goto fail; | ||
444 | profile->dents[AAFS_PROF_MODE] = dent; | ||
445 | |||
446 | dent = create_profile_file(dir, "attach", profile, | ||
447 | &aa_fs_profattach_fops); | ||
448 | if (IS_ERR(dent)) | ||
449 | goto fail; | ||
450 | profile->dents[AAFS_PROF_ATTACH] = dent; | ||
451 | |||
452 | if (profile->hash) { | ||
453 | dent = create_profile_file(dir, "sha1", profile, | ||
454 | &aa_fs_seq_hash_fops); | ||
455 | if (IS_ERR(dent)) | ||
456 | goto fail; | ||
457 | profile->dents[AAFS_PROF_HASH] = dent; | ||
458 | } | ||
459 | |||
460 | list_for_each_entry(child, &profile->base.profiles, base.list) { | ||
461 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); | ||
462 | if (error) | ||
463 | goto fail2; | ||
464 | } | ||
465 | |||
466 | return 0; | ||
467 | |||
468 | fail: | ||
469 | error = PTR_ERR(dent); | ||
470 | |||
471 | fail2: | ||
472 | __aa_fs_profile_rmdir(profile); | ||
473 | |||
474 | return error; | ||
475 | } | ||
476 | |||
477 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns) | ||
478 | { | ||
479 | struct aa_namespace *sub; | ||
480 | struct aa_profile *child; | ||
481 | int i; | ||
482 | |||
483 | if (!ns) | ||
484 | return; | ||
485 | |||
486 | list_for_each_entry(child, &ns->base.profiles, base.list) | ||
487 | __aa_fs_profile_rmdir(child); | ||
488 | |||
489 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | ||
490 | mutex_lock(&sub->lock); | ||
491 | __aa_fs_namespace_rmdir(sub); | ||
492 | mutex_unlock(&sub->lock); | ||
493 | } | ||
186 | 494 | ||
495 | for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { | ||
496 | securityfs_remove(ns->dents[i]); | ||
497 | ns->dents[i] = NULL; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | ||
502 | const char *name) | ||
503 | { | ||
504 | struct aa_namespace *sub; | ||
505 | struct aa_profile *child; | ||
506 | struct dentry *dent, *dir; | ||
507 | int error; | ||
508 | |||
509 | if (!name) | ||
510 | name = ns->base.name; | ||
511 | |||
512 | dent = securityfs_create_dir(name, parent); | ||
513 | if (IS_ERR(dent)) | ||
514 | goto fail; | ||
515 | ns_dir(ns) = dir = dent; | ||
516 | |||
517 | dent = securityfs_create_dir("profiles", dir); | ||
518 | if (IS_ERR(dent)) | ||
519 | goto fail; | ||
520 | ns_subprofs_dir(ns) = dent; | ||
521 | |||
522 | dent = securityfs_create_dir("namespaces", dir); | ||
523 | if (IS_ERR(dent)) | ||
524 | goto fail; | ||
525 | ns_subns_dir(ns) = dent; | ||
526 | |||
527 | list_for_each_entry(child, &ns->base.profiles, base.list) { | ||
528 | error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); | ||
529 | if (error) | ||
530 | goto fail2; | ||
531 | } | ||
532 | |||
533 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | ||
534 | mutex_lock(&sub->lock); | ||
535 | error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); | ||
536 | mutex_unlock(&sub->lock); | ||
537 | if (error) | ||
538 | goto fail2; | ||
539 | } | ||
540 | |||
541 | return 0; | ||
542 | |||
543 | fail: | ||
544 | error = PTR_ERR(dent); | ||
545 | |||
546 | fail2: | ||
547 | __aa_fs_namespace_rmdir(ns); | ||
548 | |||
549 | return error; | ||
550 | } | ||
551 | |||
552 | |||
553 | #define list_entry_next(pos, member) \ | ||
554 | list_entry(pos->member.next, typeof(*pos), member) | ||
555 | #define list_entry_is_head(pos, head, member) (&pos->member == (head)) | ||
556 | |||
557 | /** | ||
558 | * __next_namespace - find the next namespace to list | ||
559 | * @root: root namespace to stop search at (NOT NULL) | ||
560 | * @ns: current ns position (NOT NULL) | ||
561 | * | ||
562 | * Find the next namespace from @ns under @root and handle all locking needed | ||
563 | * while switching current namespace. | ||
564 | * | ||
565 | * Returns: next namespace or NULL if at last namespace under @root | ||
566 | * Requires: ns->parent->lock to be held | ||
567 | * NOTE: will not unlock root->lock | ||
568 | */ | ||
569 | static struct aa_namespace *__next_namespace(struct aa_namespace *root, | ||
570 | struct aa_namespace *ns) | ||
571 | { | ||
572 | struct aa_namespace *parent, *next; | ||
573 | |||
574 | /* is next namespace a child */ | ||
575 | if (!list_empty(&ns->sub_ns)) { | ||
576 | next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); | ||
577 | mutex_lock(&next->lock); | ||
578 | return next; | ||
579 | } | ||
580 | |||
581 | /* check if the next ns is a sibling, parent, gp, .. */ | ||
582 | parent = ns->parent; | ||
583 | while (parent) { | ||
584 | mutex_unlock(&ns->lock); | ||
585 | next = list_entry_next(ns, base.list); | ||
586 | if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { | ||
587 | mutex_lock(&next->lock); | ||
588 | return next; | ||
589 | } | ||
590 | if (parent == root) | ||
591 | return NULL; | ||
592 | ns = parent; | ||
593 | parent = parent->parent; | ||
594 | } | ||
595 | |||
596 | return NULL; | ||
597 | } | ||
598 | |||
599 | /** | ||
600 | * __first_profile - find the first profile in a namespace | ||
601 | * @root: namespace that is root of profiles being displayed (NOT NULL) | ||
602 | * @ns: namespace to start in (NOT NULL) | ||
603 | * | ||
604 | * Returns: unrefcounted profile or NULL if no profile | ||
605 | * Requires: profile->ns.lock to be held | ||
606 | */ | ||
607 | static struct aa_profile *__first_profile(struct aa_namespace *root, | ||
608 | struct aa_namespace *ns) | ||
609 | { | ||
610 | for (; ns; ns = __next_namespace(root, ns)) { | ||
611 | if (!list_empty(&ns->base.profiles)) | ||
612 | return list_first_entry(&ns->base.profiles, | ||
613 | struct aa_profile, base.list); | ||
614 | } | ||
615 | return NULL; | ||
616 | } | ||
617 | |||
618 | /** | ||
619 | * __next_profile - step to the next profile in a profile tree | ||
620 | * @profile: current profile in tree (NOT NULL) | ||
621 | * | ||
622 | * Perform a depth first traversal on the profile tree in a namespace | ||
623 | * | ||
624 | * Returns: next profile or NULL if done | ||
625 | * Requires: profile->ns.lock to be held | ||
626 | */ | ||
627 | static struct aa_profile *__next_profile(struct aa_profile *p) | ||
628 | { | ||
629 | struct aa_profile *parent; | ||
630 | struct aa_namespace *ns = p->ns; | ||
631 | |||
632 | /* is next profile a child */ | ||
633 | if (!list_empty(&p->base.profiles)) | ||
634 | return list_first_entry(&p->base.profiles, typeof(*p), | ||
635 | base.list); | ||
636 | |||
637 | /* is next profile a sibling, parent sibling, gp, sibling, .. */ | ||
638 | parent = rcu_dereference_protected(p->parent, | ||
639 | mutex_is_locked(&p->ns->lock)); | ||
640 | while (parent) { | ||
641 | p = list_entry_next(p, base.list); | ||
642 | if (!list_entry_is_head(p, &parent->base.profiles, base.list)) | ||
643 | return p; | ||
644 | p = parent; | ||
645 | parent = rcu_dereference_protected(parent->parent, | ||
646 | mutex_is_locked(&parent->ns->lock)); | ||
647 | } | ||
648 | |||
649 | /* is next another profile in the namespace */ | ||
650 | p = list_entry_next(p, base.list); | ||
651 | if (!list_entry_is_head(p, &ns->base.profiles, base.list)) | ||
652 | return p; | ||
653 | |||
654 | return NULL; | ||
655 | } | ||
656 | |||
657 | /** | ||
658 | * next_profile - step to the next profile in where ever it may be | ||
659 | * @root: root namespace (NOT NULL) | ||
660 | * @profile: current profile (NOT NULL) | ||
661 | * | ||
662 | * Returns: next profile or NULL if there isn't one | ||
663 | */ | ||
664 | static struct aa_profile *next_profile(struct aa_namespace *root, | ||
665 | struct aa_profile *profile) | ||
666 | { | ||
667 | struct aa_profile *next = __next_profile(profile); | ||
668 | if (next) | ||
669 | return next; | ||
670 | |||
671 | /* finished all profiles in namespace move to next namespace */ | ||
672 | return __first_profile(root, __next_namespace(root, profile->ns)); | ||
673 | } | ||
674 | |||
675 | /** | ||
676 | * p_start - start a depth first traversal of profile tree | ||
677 | * @f: seq_file to fill | ||
678 | * @pos: current position | ||
679 | * | ||
680 | * Returns: first profile under current namespace or NULL if none found | ||
681 | * | ||
682 | * acquires first ns->lock | ||
683 | */ | ||
684 | static void *p_start(struct seq_file *f, loff_t *pos) | ||
685 | { | ||
686 | struct aa_profile *profile = NULL; | ||
687 | struct aa_namespace *root = aa_current_profile()->ns; | ||
688 | loff_t l = *pos; | ||
689 | f->private = aa_get_namespace(root); | ||
690 | |||
691 | |||
692 | /* find the first profile */ | ||
693 | mutex_lock(&root->lock); | ||
694 | profile = __first_profile(root, root); | ||
695 | |||
696 | /* skip to position */ | ||
697 | for (; profile && l > 0; l--) | ||
698 | profile = next_profile(root, profile); | ||
699 | |||
700 | return profile; | ||
701 | } | ||
702 | |||
703 | /** | ||
704 | * p_next - read the next profile entry | ||
705 | * @f: seq_file to fill | ||
706 | * @p: profile previously returned | ||
707 | * @pos: current position | ||
708 | * | ||
709 | * Returns: next profile after @p or NULL if none | ||
710 | * | ||
711 | * may acquire/release locks in namespace tree as necessary | ||
712 | */ | ||
713 | static void *p_next(struct seq_file *f, void *p, loff_t *pos) | ||
714 | { | ||
715 | struct aa_profile *profile = p; | ||
716 | struct aa_namespace *ns = f->private; | ||
717 | (*pos)++; | ||
718 | |||
719 | return next_profile(ns, profile); | ||
720 | } | ||
721 | |||
722 | /** | ||
723 | * p_stop - stop depth first traversal | ||
724 | * @f: seq_file we are filling | ||
725 | * @p: the last profile writen | ||
726 | * | ||
727 | * Release all locking done by p_start/p_next on namespace tree | ||
728 | */ | ||
729 | static void p_stop(struct seq_file *f, void *p) | ||
730 | { | ||
731 | struct aa_profile *profile = p; | ||
732 | struct aa_namespace *root = f->private, *ns; | ||
733 | |||
734 | if (profile) { | ||
735 | for (ns = profile->ns; ns && ns != root; ns = ns->parent) | ||
736 | mutex_unlock(&ns->lock); | ||
737 | } | ||
738 | mutex_unlock(&root->lock); | ||
739 | aa_put_namespace(root); | ||
740 | } | ||
741 | |||
742 | /** | ||
743 | * seq_show_profile - show a profile entry | ||
744 | * @f: seq_file to file | ||
745 | * @p: current position (profile) (NOT NULL) | ||
746 | * | ||
747 | * Returns: error on failure | ||
748 | */ | ||
749 | static int seq_show_profile(struct seq_file *f, void *p) | ||
750 | { | ||
751 | struct aa_profile *profile = (struct aa_profile *)p; | ||
752 | struct aa_namespace *root = f->private; | ||
753 | |||
754 | if (profile->ns != root) | ||
755 | seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); | ||
756 | seq_printf(f, "%s (%s)\n", profile->base.hname, | ||
757 | aa_profile_mode_names[profile->mode]); | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static const struct seq_operations aa_fs_profiles_op = { | ||
763 | .start = p_start, | ||
764 | .next = p_next, | ||
765 | .stop = p_stop, | ||
766 | .show = seq_show_profile, | ||
767 | }; | ||
768 | |||
769 | static int profiles_open(struct inode *inode, struct file *file) | ||
770 | { | ||
771 | return seq_open(file, &aa_fs_profiles_op); | ||
772 | } | ||
773 | |||
774 | static int profiles_release(struct inode *inode, struct file *file) | ||
775 | { | ||
776 | return seq_release(inode, file); | ||
777 | } | ||
778 | |||
779 | static const struct file_operations aa_fs_profiles_fops = { | ||
780 | .open = profiles_open, | ||
781 | .read = seq_read, | ||
782 | .llseek = seq_lseek, | ||
783 | .release = profiles_release, | ||
784 | }; | ||
785 | |||
786 | |||
787 | /** Base file system setup **/ | ||
187 | static struct aa_fs_entry aa_fs_entry_file[] = { | 788 | static struct aa_fs_entry aa_fs_entry_file[] = { |
188 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ | 789 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ |
189 | "link lock"), | 790 | "link lock"), |
@@ -198,11 +799,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { | |||
198 | { } | 799 | { } |
199 | }; | 800 | }; |
200 | 801 | ||
802 | static struct aa_fs_entry aa_fs_entry_policy[] = { | ||
803 | AA_FS_FILE_BOOLEAN("set_load", 1), | ||
804 | {} | ||
805 | }; | ||
806 | |||
201 | static struct aa_fs_entry aa_fs_entry_features[] = { | 807 | static struct aa_fs_entry aa_fs_entry_features[] = { |
808 | AA_FS_DIR("policy", aa_fs_entry_policy), | ||
202 | AA_FS_DIR("domain", aa_fs_entry_domain), | 809 | AA_FS_DIR("domain", aa_fs_entry_domain), |
203 | AA_FS_DIR("file", aa_fs_entry_file), | 810 | AA_FS_DIR("file", aa_fs_entry_file), |
204 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | 811 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), |
205 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | 812 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), |
813 | AA_FS_DIR("caps", aa_fs_entry_caps), | ||
206 | { } | 814 | { } |
207 | }; | 815 | }; |
208 | 816 | ||
@@ -210,6 +818,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { | |||
210 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), | 818 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), |
211 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), | 819 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), |
212 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), | 820 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), |
821 | AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), | ||
213 | AA_FS_DIR("features", aa_fs_entry_features), | 822 | AA_FS_DIR("features", aa_fs_entry_features), |
214 | { } | 823 | { } |
215 | }; | 824 | }; |
@@ -240,6 +849,7 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file, | |||
240 | return error; | 849 | return error; |
241 | } | 850 | } |
242 | 851 | ||
852 | static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir); | ||
243 | /** | 853 | /** |
244 | * aafs_create_dir - recursively create a directory entry in the securityfs | 854 | * aafs_create_dir - recursively create a directory entry in the securityfs |
245 | * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) | 855 | * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) |
@@ -250,17 +860,16 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file, | |||
250 | static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, | 860 | static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, |
251 | struct dentry *parent) | 861 | struct dentry *parent) |
252 | { | 862 | { |
253 | int error; | ||
254 | struct aa_fs_entry *fs_file; | 863 | struct aa_fs_entry *fs_file; |
864 | struct dentry *dir; | ||
865 | int error; | ||
255 | 866 | ||
256 | fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent); | 867 | dir = securityfs_create_dir(fs_dir->name, parent); |
257 | if (IS_ERR(fs_dir->dentry)) { | 868 | if (IS_ERR(dir)) |
258 | error = PTR_ERR(fs_dir->dentry); | 869 | return PTR_ERR(dir); |
259 | fs_dir->dentry = NULL; | 870 | fs_dir->dentry = dir; |
260 | goto failed; | ||
261 | } | ||
262 | 871 | ||
263 | for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { | 872 | for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) { |
264 | if (fs_file->v_type == AA_FS_TYPE_DIR) | 873 | if (fs_file->v_type == AA_FS_TYPE_DIR) |
265 | error = aafs_create_dir(fs_file, fs_dir->dentry); | 874 | error = aafs_create_dir(fs_file, fs_dir->dentry); |
266 | else | 875 | else |
@@ -272,6 +881,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, | |||
272 | return 0; | 881 | return 0; |
273 | 882 | ||
274 | failed: | 883 | failed: |
884 | aafs_remove_dir(fs_dir); | ||
885 | |||
275 | return error; | 886 | return error; |
276 | } | 887 | } |
277 | 888 | ||
@@ -296,7 +907,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir) | |||
296 | { | 907 | { |
297 | struct aa_fs_entry *fs_file; | 908 | struct aa_fs_entry *fs_file; |
298 | 909 | ||
299 | for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { | 910 | for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) { |
300 | if (fs_file->v_type == AA_FS_TYPE_DIR) | 911 | if (fs_file->v_type == AA_FS_TYPE_DIR) |
301 | aafs_remove_dir(fs_file); | 912 | aafs_remove_dir(fs_file); |
302 | else | 913 | else |
@@ -340,6 +951,11 @@ static int __init aa_create_aafs(void) | |||
340 | if (error) | 951 | if (error) |
341 | goto error; | 952 | goto error; |
342 | 953 | ||
954 | error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, | ||
955 | "policy"); | ||
956 | if (error) | ||
957 | goto error; | ||
958 | |||
343 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | 959 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ |
344 | 960 | ||
345 | /* Report that AppArmor fs is enabled */ | 961 | /* Report that AppArmor fs is enabled */ |
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 887a5e948945..84d1f5f53877 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c | |||
@@ -27,6 +27,11 @@ | |||
27 | */ | 27 | */ |
28 | #include "capability_names.h" | 28 | #include "capability_names.h" |
29 | 29 | ||
30 | struct aa_fs_entry aa_fs_entry_caps[] = { | ||
31 | AA_FS_FILE_STRING("mask", AA_FS_CAPS_MASK), | ||
32 | { } | ||
33 | }; | ||
34 | |||
30 | struct audit_cache { | 35 | struct audit_cache { |
31 | struct aa_profile *profile; | 36 | struct aa_profile *profile; |
32 | kernel_cap_t caps; | 37 | kernel_cap_t caps; |
diff --git a/security/apparmor/context.c b/security/apparmor/context.c index d5af1d15f26d..3064c6ced87c 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c | |||
@@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile) | |||
112 | aa_clear_task_cxt_trans(cxt); | 112 | aa_clear_task_cxt_trans(cxt); |
113 | 113 | ||
114 | /* be careful switching cxt->profile, when racing replacement it | 114 | /* be careful switching cxt->profile, when racing replacement it |
115 | * is possible that cxt->profile->replacedby is the reference keeping | 115 | * is possible that cxt->profile->replacedby->profile is the reference |
116 | * @profile valid, so make sure to get its reference before dropping | 116 | * keeping @profile valid, so make sure to get its reference before |
117 | * the reference on cxt->profile */ | 117 | * dropping the reference on cxt->profile */ |
118 | aa_get_profile(profile); | 118 | aa_get_profile(profile); |
119 | aa_put_profile(cxt->profile); | 119 | aa_put_profile(cxt->profile); |
120 | cxt->profile = profile; | 120 | cxt->profile = profile; |
@@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) | |||
175 | abort_creds(new); | 175 | abort_creds(new); |
176 | return -EACCES; | 176 | return -EACCES; |
177 | } | 177 | } |
178 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | 178 | cxt->profile = aa_get_newest_profile(profile); |
179 | /* clear exec on switching context */ | 179 | /* clear exec on switching context */ |
180 | aa_put_profile(cxt->onexec); | 180 | aa_put_profile(cxt->onexec); |
181 | cxt->onexec = NULL; | 181 | cxt->onexec = NULL; |
@@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token) | |||
212 | } | 212 | } |
213 | 213 | ||
214 | aa_put_profile(cxt->profile); | 214 | aa_put_profile(cxt->profile); |
215 | cxt->profile = aa_newest_version(cxt->previous); | 215 | cxt->profile = aa_get_newest_profile(cxt->previous); |
216 | BUG_ON(!cxt->profile); | 216 | BUG_ON(!cxt->profile); |
217 | if (unlikely(cxt->profile != cxt->previous)) { | ||
218 | aa_get_profile(cxt->profile); | ||
219 | aa_put_profile(cxt->previous); | ||
220 | } | ||
221 | /* ref has been transfered so avoid putting ref in clear_task_cxt */ | ||
222 | cxt->previous = NULL; | ||
223 | /* clear exec && prev information when restoring to previous context */ | 217 | /* clear exec && prev information when restoring to previous context */ |
224 | aa_clear_task_cxt_trans(cxt); | 218 | aa_clear_task_cxt_trans(cxt); |
225 | 219 | ||
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c new file mode 100644 index 000000000000..d6222ba4e919 --- /dev/null +++ b/security/apparmor/crypto.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy loading interface function definitions. | ||
5 | * | ||
6 | * Copyright 2013 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | * | ||
13 | * Fns to provide a checksum of policy that has been loaded this can be | ||
14 | * compared to userspace policy compiles to check loaded policy is what | ||
15 | * it should be. | ||
16 | */ | ||
17 | |||
18 | #include <linux/crypto.h> | ||
19 | |||
20 | #include "include/apparmor.h" | ||
21 | #include "include/crypto.h" | ||
22 | |||
23 | static unsigned int apparmor_hash_size; | ||
24 | |||
25 | static struct crypto_hash *apparmor_tfm; | ||
26 | |||
27 | unsigned int aa_hash_size(void) | ||
28 | { | ||
29 | return apparmor_hash_size; | ||
30 | } | ||
31 | |||
32 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | ||
33 | size_t len) | ||
34 | { | ||
35 | struct scatterlist sg[2]; | ||
36 | struct hash_desc desc = { | ||
37 | .tfm = apparmor_tfm, | ||
38 | .flags = 0 | ||
39 | }; | ||
40 | int error = -ENOMEM; | ||
41 | u32 le32_version = cpu_to_le32(version); | ||
42 | |||
43 | if (!apparmor_tfm) | ||
44 | return 0; | ||
45 | |||
46 | sg_init_table(sg, 2); | ||
47 | sg_set_buf(&sg[0], &le32_version, 4); | ||
48 | sg_set_buf(&sg[1], (u8 *) start, len); | ||
49 | |||
50 | profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL); | ||
51 | if (!profile->hash) | ||
52 | goto fail; | ||
53 | |||
54 | error = crypto_hash_init(&desc); | ||
55 | if (error) | ||
56 | goto fail; | ||
57 | error = crypto_hash_update(&desc, &sg[0], 4); | ||
58 | if (error) | ||
59 | goto fail; | ||
60 | error = crypto_hash_update(&desc, &sg[1], len); | ||
61 | if (error) | ||
62 | goto fail; | ||
63 | error = crypto_hash_final(&desc, profile->hash); | ||
64 | if (error) | ||
65 | goto fail; | ||
66 | |||
67 | return 0; | ||
68 | |||
69 | fail: | ||
70 | kfree(profile->hash); | ||
71 | profile->hash = NULL; | ||
72 | |||
73 | return error; | ||
74 | } | ||
75 | |||
76 | static int __init init_profile_hash(void) | ||
77 | { | ||
78 | struct crypto_hash *tfm; | ||
79 | |||
80 | if (!apparmor_initialized) | ||
81 | return 0; | ||
82 | |||
83 | tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC); | ||
84 | if (IS_ERR(tfm)) { | ||
85 | int error = PTR_ERR(tfm); | ||
86 | AA_ERROR("failed to setup profile sha1 hashing: %d\n", error); | ||
87 | return error; | ||
88 | } | ||
89 | apparmor_tfm = tfm; | ||
90 | apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm); | ||
91 | |||
92 | aa_info_message("AppArmor sha1 policy hashing enabled"); | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | late_initcall(init_profile_hash); | ||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 01b7bd669a88..26c607c971f5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
@@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name, | |||
144 | int len = 0; | 144 | int len = 0; |
145 | struct aa_profile *profile, *candidate = NULL; | 145 | struct aa_profile *profile, *candidate = NULL; |
146 | 146 | ||
147 | list_for_each_entry(profile, head, base.list) { | 147 | list_for_each_entry_rcu(profile, head, base.list) { |
148 | if (profile->flags & PFLAG_NULL) | 148 | if (profile->flags & PFLAG_NULL) |
149 | continue; | 149 | continue; |
150 | if (profile->xmatch && profile->xmatch_len > len) { | 150 | if (profile->xmatch && profile->xmatch_len > len) { |
@@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns, | |||
177 | { | 177 | { |
178 | struct aa_profile *profile; | 178 | struct aa_profile *profile; |
179 | 179 | ||
180 | read_lock(&ns->lock); | 180 | rcu_read_lock(); |
181 | profile = aa_get_profile(__attach_match(name, list)); | 181 | profile = aa_get_profile(__attach_match(name, list)); |
182 | read_unlock(&ns->lock); | 182 | rcu_read_unlock(); |
183 | 183 | ||
184 | return profile; | 184 | return profile; |
185 | } | 185 | } |
@@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
359 | cxt = cred_cxt(bprm->cred); | 359 | cxt = cred_cxt(bprm->cred); |
360 | BUG_ON(!cxt); | 360 | BUG_ON(!cxt); |
361 | 361 | ||
362 | profile = aa_get_profile(aa_newest_version(cxt->profile)); | 362 | profile = aa_get_newest_profile(cxt->profile); |
363 | /* | 363 | /* |
364 | * get the namespace from the replacement profile as replacement | 364 | * get the namespace from the replacement profile as replacement |
365 | * can change the namespace | 365 | * can change the namespace |
@@ -371,8 +371,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
371 | error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, | 371 | error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, |
372 | &name, &info); | 372 | &name, &info); |
373 | if (error) { | 373 | if (error) { |
374 | if (profile->flags & | 374 | if (unconfined(profile) || |
375 | (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) | 375 | (profile->flags & PFLAG_IX_ON_NAME_ERROR)) |
376 | error = 0; | 376 | error = 0; |
377 | name = bprm->filename; | 377 | name = bprm->filename; |
378 | goto audit; | 378 | goto audit; |
@@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
417 | 417 | ||
418 | if (!(cp.allow & AA_MAY_ONEXEC)) | 418 | if (!(cp.allow & AA_MAY_ONEXEC)) |
419 | goto audit; | 419 | goto audit; |
420 | new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | 420 | new_profile = aa_get_newest_profile(cxt->onexec); |
421 | goto apply; | 421 | goto apply; |
422 | } | 422 | } |
423 | 423 | ||
@@ -434,7 +434,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
434 | new_profile = aa_get_profile(profile); | 434 | new_profile = aa_get_profile(profile); |
435 | goto x_clear; | 435 | goto x_clear; |
436 | } else if (perms.xindex & AA_X_UNCONFINED) { | 436 | } else if (perms.xindex & AA_X_UNCONFINED) { |
437 | new_profile = aa_get_profile(ns->unconfined); | 437 | new_profile = aa_get_newest_profile(ns->unconfined); |
438 | info = "ux fallback"; | 438 | info = "ux fallback"; |
439 | } else { | 439 | } else { |
440 | error = -ENOENT; | 440 | error = -ENOENT; |
@@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
641 | if (count) { | 641 | if (count) { |
642 | /* attempting to change into a new hat or switch to a sibling */ | 642 | /* attempting to change into a new hat or switch to a sibling */ |
643 | struct aa_profile *root; | 643 | struct aa_profile *root; |
644 | root = PROFILE_IS_HAT(profile) ? profile->parent : profile; | 644 | if (PROFILE_IS_HAT(profile)) |
645 | root = aa_get_profile_rcu(&profile->parent); | ||
646 | else | ||
647 | root = aa_get_profile(profile); | ||
645 | 648 | ||
646 | /* find first matching hat */ | 649 | /* find first matching hat */ |
647 | for (i = 0; i < count && !hat; i++) | 650 | for (i = 0; i < count && !hat; i++) |
@@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
653 | error = -ECHILD; | 656 | error = -ECHILD; |
654 | else | 657 | else |
655 | error = -ENOENT; | 658 | error = -ENOENT; |
659 | aa_put_profile(root); | ||
656 | goto out; | 660 | goto out; |
657 | } | 661 | } |
658 | 662 | ||
@@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
667 | 671 | ||
668 | /* freed below */ | 672 | /* freed below */ |
669 | name = new_compound_name(root->base.hname, hats[0]); | 673 | name = new_compound_name(root->base.hname, hats[0]); |
674 | aa_put_profile(root); | ||
670 | target = name; | 675 | target = name; |
671 | /* released below */ | 676 | /* released below */ |
672 | hat = aa_new_null_profile(profile, 1); | 677 | hat = aa_new_null_profile(profile, 1); |
@@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
676 | goto audit; | 681 | goto audit; |
677 | } | 682 | } |
678 | } else { | 683 | } else { |
684 | aa_put_profile(root); | ||
679 | target = hat->base.hname; | 685 | target = hat->base.hname; |
680 | if (!PROFILE_IS_HAT(hat)) { | 686 | if (!PROFILE_IS_HAT(hat)) { |
681 | info = "target not hat"; | 687 | info = "target not hat"; |
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1ba2ca56a6ef..8fb1488a3cd4 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h | |||
@@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size) | |||
78 | return __aa_kvmalloc(size, __GFP_ZERO); | 78 | return __aa_kvmalloc(size, __GFP_ZERO); |
79 | } | 79 | } |
80 | 80 | ||
81 | /* returns 0 if kref not incremented */ | ||
82 | static inline int kref_get_not0(struct kref *kref) | ||
83 | { | ||
84 | return atomic_inc_not_zero(&kref->refcount); | ||
85 | } | ||
86 | |||
81 | /** | 87 | /** |
82 | * aa_strneq - compare null terminated @str to a non null terminated substring | 88 | * aa_strneq - compare null terminated @str to a non null terminated substring |
83 | * @str: a null terminated string | 89 | * @str: a null terminated string |
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 7ea4769fab3f..414e56878dd0 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h | |||
@@ -61,4 +61,44 @@ extern const struct file_operations aa_fs_seq_file_ops; | |||
61 | 61 | ||
62 | extern void __init aa_destroy_aafs(void); | 62 | extern void __init aa_destroy_aafs(void); |
63 | 63 | ||
64 | struct aa_profile; | ||
65 | struct aa_namespace; | ||
66 | |||
67 | enum aafs_ns_type { | ||
68 | AAFS_NS_DIR, | ||
69 | AAFS_NS_PROFS, | ||
70 | AAFS_NS_NS, | ||
71 | AAFS_NS_COUNT, | ||
72 | AAFS_NS_MAX_COUNT, | ||
73 | AAFS_NS_SIZE, | ||
74 | AAFS_NS_MAX_SIZE, | ||
75 | AAFS_NS_OWNER, | ||
76 | AAFS_NS_SIZEOF, | ||
77 | }; | ||
78 | |||
79 | enum aafs_prof_type { | ||
80 | AAFS_PROF_DIR, | ||
81 | AAFS_PROF_PROFS, | ||
82 | AAFS_PROF_NAME, | ||
83 | AAFS_PROF_MODE, | ||
84 | AAFS_PROF_ATTACH, | ||
85 | AAFS_PROF_HASH, | ||
86 | AAFS_PROF_SIZEOF, | ||
87 | }; | ||
88 | |||
89 | #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) | ||
90 | #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) | ||
91 | #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) | ||
92 | |||
93 | #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) | ||
94 | #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) | ||
95 | |||
96 | void __aa_fs_profile_rmdir(struct aa_profile *profile); | ||
97 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | ||
98 | struct aa_profile *new); | ||
99 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); | ||
100 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns); | ||
101 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | ||
102 | const char *name); | ||
103 | |||
64 | #endif /* __AA_APPARMORFS_H */ | 104 | #endif /* __AA_APPARMORFS_H */ |
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae634e7..30e8d7687259 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h | |||
@@ -27,7 +27,6 @@ struct aa_profile; | |||
27 | 27 | ||
28 | extern const char *const audit_mode_names[]; | 28 | extern const char *const audit_mode_names[]; |
29 | #define AUDIT_MAX_INDEX 5 | 29 | #define AUDIT_MAX_INDEX 5 |
30 | |||
31 | enum audit_mode { | 30 | enum audit_mode { |
32 | AUDIT_NORMAL, /* follow normal auditing of accesses */ | 31 | AUDIT_NORMAL, /* follow normal auditing of accesses */ |
33 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | 32 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ |
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index c24d2959ea02..2e7c9d6a2f3b 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h | |||
@@ -17,6 +17,8 @@ | |||
17 | 17 | ||
18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
19 | 19 | ||
20 | #include "apparmorfs.h" | ||
21 | |||
20 | struct aa_profile; | 22 | struct aa_profile; |
21 | 23 | ||
22 | /* aa_caps - confinement data for capabilities | 24 | /* aa_caps - confinement data for capabilities |
@@ -34,6 +36,8 @@ struct aa_caps { | |||
34 | kernel_cap_t extended; | 36 | kernel_cap_t extended; |
35 | }; | 37 | }; |
36 | 38 | ||
39 | extern struct aa_fs_entry aa_fs_entry_caps[]; | ||
40 | |||
37 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | 41 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, |
38 | int audit); | 42 | int audit); |
39 | 43 | ||
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index d44ba5802e3d..6bf65798e5d1 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h | |||
@@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | |||
98 | { | 98 | { |
99 | struct aa_task_cxt *cxt = cred_cxt(cred); | 99 | struct aa_task_cxt *cxt = cred_cxt(cred); |
100 | BUG_ON(!cxt || !cxt->profile); | 100 | BUG_ON(!cxt || !cxt->profile); |
101 | return aa_newest_version(cxt->profile); | 101 | return cxt->profile; |
102 | } | 102 | } |
103 | 103 | ||
104 | /** | 104 | /** |
@@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void) | |||
152 | struct aa_profile *profile; | 152 | struct aa_profile *profile; |
153 | BUG_ON(!cxt || !cxt->profile); | 153 | BUG_ON(!cxt || !cxt->profile); |
154 | 154 | ||
155 | profile = aa_newest_version(cxt->profile); | 155 | if (PROFILE_INVALID(cxt->profile)) { |
156 | /* | 156 | profile = aa_get_newest_profile(cxt->profile); |
157 | * Whether or not replacement succeeds, use newest profile so | ||
158 | * there is no need to update it after replacement. | ||
159 | */ | ||
160 | if (unlikely((cxt->profile != profile))) | ||
161 | aa_replace_current_profile(profile); | 157 | aa_replace_current_profile(profile); |
158 | aa_put_profile(profile); | ||
159 | cxt = current_cxt(); | ||
160 | } | ||
162 | 161 | ||
163 | return profile; | 162 | return cxt->profile; |
164 | } | 163 | } |
165 | 164 | ||
166 | /** | 165 | /** |
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h new file mode 100644 index 000000000000..dc418e5024d9 --- /dev/null +++ b/security/apparmor/include/crypto.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy loading interface function definitions. | ||
5 | * | ||
6 | * Copyright 2013 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | */ | ||
13 | |||
14 | #ifndef __APPARMOR_CRYPTO_H | ||
15 | #define __APPARMOR_CRYPTO_H | ||
16 | |||
17 | #include "policy.h" | ||
18 | |||
19 | #ifdef CONFIG_SECURITY_APPARMOR_HASH | ||
20 | unsigned int aa_hash_size(void); | ||
21 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | ||
22 | size_t len); | ||
23 | #else | ||
24 | static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, | ||
25 | void *start, size_t len) | ||
26 | { | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static inline unsigned int aa_hash_size(void) | ||
31 | { | ||
32 | return 0; | ||
33 | } | ||
34 | #endif | ||
35 | |||
36 | #endif /* __APPARMOR_CRYPTO_H */ | ||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b25491a3046a..f2d4b6348cbc 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
@@ -29,8 +29,8 @@ | |||
29 | #include "file.h" | 29 | #include "file.h" |
30 | #include "resource.h" | 30 | #include "resource.h" |
31 | 31 | ||
32 | extern const char *const profile_mode_names[]; | 32 | extern const char *const aa_profile_mode_names[]; |
33 | #define APPARMOR_NAMES_MAX_INDEX 3 | 33 | #define APPARMOR_MODE_NAMES_MAX_INDEX 4 |
34 | 34 | ||
35 | #define PROFILE_MODE(_profile, _mode) \ | 35 | #define PROFILE_MODE(_profile, _mode) \ |
36 | ((aa_g_profile_mode == (_mode)) || \ | 36 | ((aa_g_profile_mode == (_mode)) || \ |
@@ -42,6 +42,10 @@ extern const char *const profile_mode_names[]; | |||
42 | 42 | ||
43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) | 43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) |
44 | 44 | ||
45 | #define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) | ||
46 | |||
47 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) | ||
48 | |||
45 | /* | 49 | /* |
46 | * FIXME: currently need a clean way to replace and remove profiles as a | 50 | * FIXME: currently need a clean way to replace and remove profiles as a |
47 | * set. It should be done at the namespace level. | 51 | * set. It should be done at the namespace level. |
@@ -52,17 +56,19 @@ enum profile_mode { | |||
52 | APPARMOR_ENFORCE, /* enforce access rules */ | 56 | APPARMOR_ENFORCE, /* enforce access rules */ |
53 | APPARMOR_COMPLAIN, /* allow and log access violations */ | 57 | APPARMOR_COMPLAIN, /* allow and log access violations */ |
54 | APPARMOR_KILL, /* kill task on access violation */ | 58 | APPARMOR_KILL, /* kill task on access violation */ |
59 | APPARMOR_UNCONFINED, /* profile set to unconfined */ | ||
55 | }; | 60 | }; |
56 | 61 | ||
57 | enum profile_flags { | 62 | enum profile_flags { |
58 | PFLAG_HAT = 1, /* profile is a hat */ | 63 | PFLAG_HAT = 1, /* profile is a hat */ |
59 | PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ | ||
60 | PFLAG_NULL = 4, /* profile is null learning profile */ | 64 | PFLAG_NULL = 4, /* profile is null learning profile */ |
61 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ | 65 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ |
62 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ | 66 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ |
63 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | 67 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ |
64 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | 68 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ |
65 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | 69 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ |
70 | PFLAG_INVALID = 0x200, /* profile replaced/removed */ | ||
71 | PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ | ||
66 | 72 | ||
67 | /* These flags must correspond with PATH_flags */ | 73 | /* These flags must correspond with PATH_flags */ |
68 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | 74 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ |
@@ -73,14 +79,12 @@ struct aa_profile; | |||
73 | /* struct aa_policy - common part of both namespaces and profiles | 79 | /* struct aa_policy - common part of both namespaces and profiles |
74 | * @name: name of the object | 80 | * @name: name of the object |
75 | * @hname - The hierarchical name | 81 | * @hname - The hierarchical name |
76 | * @count: reference count of the obj | ||
77 | * @list: list policy object is on | 82 | * @list: list policy object is on |
78 | * @profiles: head of the profiles list contained in the object | 83 | * @profiles: head of the profiles list contained in the object |
79 | */ | 84 | */ |
80 | struct aa_policy { | 85 | struct aa_policy { |
81 | char *name; | 86 | char *name; |
82 | char *hname; | 87 | char *hname; |
83 | struct kref count; | ||
84 | struct list_head list; | 88 | struct list_head list; |
85 | struct list_head profiles; | 89 | struct list_head profiles; |
86 | }; | 90 | }; |
@@ -106,6 +110,8 @@ struct aa_ns_acct { | |||
106 | * @unconfined: special unconfined profile for the namespace | 110 | * @unconfined: special unconfined profile for the namespace |
107 | * @sub_ns: list of namespaces under the current namespace. | 111 | * @sub_ns: list of namespaces under the current namespace. |
108 | * @uniq_null: uniq value used for null learning profiles | 112 | * @uniq_null: uniq value used for null learning profiles |
113 | * @uniq_id: a unique id count for the profiles in the namespace | ||
114 | * @dents: dentries for the namespaces file entries in apparmorfs | ||
109 | * | 115 | * |
110 | * An aa_namespace defines the set profiles that are searched to determine | 116 | * An aa_namespace defines the set profiles that are searched to determine |
111 | * which profile to attach to a task. Profiles can not be shared between | 117 | * which profile to attach to a task. Profiles can not be shared between |
@@ -124,11 +130,14 @@ struct aa_ns_acct { | |||
124 | struct aa_namespace { | 130 | struct aa_namespace { |
125 | struct aa_policy base; | 131 | struct aa_policy base; |
126 | struct aa_namespace *parent; | 132 | struct aa_namespace *parent; |
127 | rwlock_t lock; | 133 | struct mutex lock; |
128 | struct aa_ns_acct acct; | 134 | struct aa_ns_acct acct; |
129 | struct aa_profile *unconfined; | 135 | struct aa_profile *unconfined; |
130 | struct list_head sub_ns; | 136 | struct list_head sub_ns; |
131 | atomic_t uniq_null; | 137 | atomic_t uniq_null; |
138 | long uniq_id; | ||
139 | |||
140 | struct dentry *dents[AAFS_NS_SIZEOF]; | ||
132 | }; | 141 | }; |
133 | 142 | ||
134 | /* struct aa_policydb - match engine for a policy | 143 | /* struct aa_policydb - match engine for a policy |
@@ -142,12 +151,21 @@ struct aa_policydb { | |||
142 | 151 | ||
143 | }; | 152 | }; |
144 | 153 | ||
154 | struct aa_replacedby { | ||
155 | struct kref count; | ||
156 | struct aa_profile __rcu *profile; | ||
157 | }; | ||
158 | |||
159 | |||
145 | /* struct aa_profile - basic confinement data | 160 | /* struct aa_profile - basic confinement data |
146 | * @base - base components of the profile (name, refcount, lists, lock ...) | 161 | * @base - base components of the profile (name, refcount, lists, lock ...) |
162 | * @count: reference count of the obj | ||
163 | * @rcu: rcu head used when removing from @list | ||
147 | * @parent: parent of profile | 164 | * @parent: parent of profile |
148 | * @ns: namespace the profile is in | 165 | * @ns: namespace the profile is in |
149 | * @replacedby: is set to the profile that replaced this profile | 166 | * @replacedby: is set to the profile that replaced this profile |
150 | * @rename: optional profile name that this profile renamed | 167 | * @rename: optional profile name that this profile renamed |
168 | * @attach: human readable attachment string | ||
151 | * @xmatch: optional extended matching for unconfined executables names | 169 | * @xmatch: optional extended matching for unconfined executables names |
152 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority | 170 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority |
153 | * @audit: the auditing mode of the profile | 171 | * @audit: the auditing mode of the profile |
@@ -160,13 +178,15 @@ struct aa_policydb { | |||
160 | * @caps: capabilities for the profile | 178 | * @caps: capabilities for the profile |
161 | * @rlimits: rlimits for the profile | 179 | * @rlimits: rlimits for the profile |
162 | * | 180 | * |
181 | * @dents: dentries for the profiles file entries in apparmorfs | ||
182 | * @dirname: name of the profile dir in apparmorfs | ||
183 | * | ||
163 | * The AppArmor profile contains the basic confinement data. Each profile | 184 | * The AppArmor profile contains the basic confinement data. Each profile |
164 | * has a name, and exists in a namespace. The @name and @exec_match are | 185 | * has a name, and exists in a namespace. The @name and @exec_match are |
165 | * used to determine profile attachment against unconfined tasks. All other | 186 | * used to determine profile attachment against unconfined tasks. All other |
166 | * attachments are determined by profile X transition rules. | 187 | * attachments are determined by profile X transition rules. |
167 | * | 188 | * |
168 | * The @replacedby field is write protected by the profile lock. Reads | 189 | * The @replacedby struct is write protected by the profile lock. |
169 | * are assumed to be atomic, and are done without locking. | ||
170 | * | 190 | * |
171 | * Profiles have a hierarchy where hats and children profiles keep | 191 | * Profiles have a hierarchy where hats and children profiles keep |
172 | * a reference to their parent. | 192 | * a reference to their parent. |
@@ -177,17 +197,20 @@ struct aa_policydb { | |||
177 | */ | 197 | */ |
178 | struct aa_profile { | 198 | struct aa_profile { |
179 | struct aa_policy base; | 199 | struct aa_policy base; |
180 | struct aa_profile *parent; | 200 | struct kref count; |
201 | struct rcu_head rcu; | ||
202 | struct aa_profile __rcu *parent; | ||
181 | 203 | ||
182 | struct aa_namespace *ns; | 204 | struct aa_namespace *ns; |
183 | struct aa_profile *replacedby; | 205 | struct aa_replacedby *replacedby; |
184 | const char *rename; | 206 | const char *rename; |
185 | 207 | ||
208 | const char *attach; | ||
186 | struct aa_dfa *xmatch; | 209 | struct aa_dfa *xmatch; |
187 | int xmatch_len; | 210 | int xmatch_len; |
188 | enum audit_mode audit; | 211 | enum audit_mode audit; |
189 | enum profile_mode mode; | 212 | long mode; |
190 | u32 flags; | 213 | long flags; |
191 | u32 path_flags; | 214 | u32 path_flags; |
192 | int size; | 215 | int size; |
193 | 216 | ||
@@ -195,6 +218,10 @@ struct aa_profile { | |||
195 | struct aa_file_rules file; | 218 | struct aa_file_rules file; |
196 | struct aa_caps caps; | 219 | struct aa_caps caps; |
197 | struct aa_rlimit rlimits; | 220 | struct aa_rlimit rlimits; |
221 | |||
222 | unsigned char *hash; | ||
223 | char *dirname; | ||
224 | struct dentry *dents[AAFS_PROF_SIZEOF]; | ||
198 | }; | 225 | }; |
199 | 226 | ||
200 | extern struct aa_namespace *root_ns; | 227 | extern struct aa_namespace *root_ns; |
@@ -211,43 +238,11 @@ void aa_free_namespace_kref(struct kref *kref); | |||
211 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | 238 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, |
212 | const char *name); | 239 | const char *name); |
213 | 240 | ||
214 | static inline struct aa_policy *aa_get_common(struct aa_policy *c) | ||
215 | { | ||
216 | if (c) | ||
217 | kref_get(&c->count); | ||
218 | |||
219 | return c; | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * aa_get_namespace - increment references count on @ns | ||
224 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
225 | * | ||
226 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
227 | * Requires: @ns must be held with valid refcount when called | ||
228 | */ | ||
229 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
230 | { | ||
231 | if (ns) | ||
232 | kref_get(&(ns->base.count)); | ||
233 | |||
234 | return ns; | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * aa_put_namespace - decrement refcount on @ns | ||
239 | * @ns: namespace to put reference of | ||
240 | * | ||
241 | * Decrement reference count of @ns and if no longer in use free it | ||
242 | */ | ||
243 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
244 | { | ||
245 | if (ns) | ||
246 | kref_put(&ns->base.count, aa_free_namespace_kref); | ||
247 | } | ||
248 | 241 | ||
242 | void aa_free_replacedby_kref(struct kref *kref); | ||
249 | struct aa_profile *aa_alloc_profile(const char *name); | 243 | struct aa_profile *aa_alloc_profile(const char *name); |
250 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | 244 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); |
245 | void aa_free_profile(struct aa_profile *profile); | ||
251 | void aa_free_profile_kref(struct kref *kref); | 246 | void aa_free_profile_kref(struct kref *kref); |
252 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); | 247 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); |
253 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); | 248 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); |
@@ -259,25 +254,13 @@ ssize_t aa_remove_profiles(char *name, size_t size); | |||
259 | #define PROF_ADD 1 | 254 | #define PROF_ADD 1 |
260 | #define PROF_REPLACE 0 | 255 | #define PROF_REPLACE 0 |
261 | 256 | ||
262 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | 257 | #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) |
263 | 258 | ||
264 | /** | ||
265 | * aa_newest_version - find the newest version of @profile | ||
266 | * @profile: the profile to check for newer versions of (NOT NULL) | ||
267 | * | ||
268 | * Returns: newest version of @profile, if @profile is the newest version | ||
269 | * return @profile. | ||
270 | * | ||
271 | * NOTE: the profile returned is not refcounted, The refcount on @profile | ||
272 | * must be held until the caller decides what to do with the returned newest | ||
273 | * version. | ||
274 | */ | ||
275 | static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | ||
276 | { | ||
277 | while (profile->replacedby) | ||
278 | profile = profile->replacedby; | ||
279 | 259 | ||
280 | return profile; | 260 | static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) |
261 | { | ||
262 | return rcu_dereference_protected(p->parent, | ||
263 | mutex_is_locked(&p->ns->lock)); | ||
281 | } | 264 | } |
282 | 265 | ||
283 | /** | 266 | /** |
@@ -290,19 +273,124 @@ static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | |||
290 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | 273 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) |
291 | { | 274 | { |
292 | if (p) | 275 | if (p) |
293 | kref_get(&(p->base.count)); | 276 | kref_get(&(p->count)); |
294 | 277 | ||
295 | return p; | 278 | return p; |
296 | } | 279 | } |
297 | 280 | ||
298 | /** | 281 | /** |
282 | * aa_get_profile_not0 - increment refcount on profile @p found via lookup | ||
283 | * @p: profile (MAYBE NULL) | ||
284 | * | ||
285 | * Returns: pointer to @p if @p is NULL will return NULL | ||
286 | * Requires: @p must be held with valid refcount when called | ||
287 | */ | ||
288 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) | ||
289 | { | ||
290 | if (p && kref_get_not0(&p->count)) | ||
291 | return p; | ||
292 | |||
293 | return NULL; | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * aa_get_profile_rcu - increment a refcount profile that can be replaced | ||
298 | * @p: pointer to profile that can be replaced (NOT NULL) | ||
299 | * | ||
300 | * Returns: pointer to a refcounted profile. | ||
301 | * else NULL if no profile | ||
302 | */ | ||
303 | static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) | ||
304 | { | ||
305 | struct aa_profile *c; | ||
306 | |||
307 | rcu_read_lock(); | ||
308 | do { | ||
309 | c = rcu_dereference(*p); | ||
310 | } while (c && !kref_get_not0(&c->count)); | ||
311 | rcu_read_unlock(); | ||
312 | |||
313 | return c; | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * aa_get_newest_profile - find the newest version of @profile | ||
318 | * @profile: the profile to check for newer versions of | ||
319 | * | ||
320 | * Returns: refcounted newest version of @profile taking into account | ||
321 | * replacement, renames and removals | ||
322 | * return @profile. | ||
323 | */ | ||
324 | static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) | ||
325 | { | ||
326 | if (!p) | ||
327 | return NULL; | ||
328 | |||
329 | if (PROFILE_INVALID(p)) | ||
330 | return aa_get_profile_rcu(&p->replacedby->profile); | ||
331 | |||
332 | return aa_get_profile(p); | ||
333 | } | ||
334 | |||
335 | /** | ||
299 | * aa_put_profile - decrement refcount on profile @p | 336 | * aa_put_profile - decrement refcount on profile @p |
300 | * @p: profile (MAYBE NULL) | 337 | * @p: profile (MAYBE NULL) |
301 | */ | 338 | */ |
302 | static inline void aa_put_profile(struct aa_profile *p) | 339 | static inline void aa_put_profile(struct aa_profile *p) |
303 | { | 340 | { |
304 | if (p) | 341 | if (p) |
305 | kref_put(&p->base.count, aa_free_profile_kref); | 342 | kref_put(&p->count, aa_free_profile_kref); |
343 | } | ||
344 | |||
345 | static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) | ||
346 | { | ||
347 | if (p) | ||
348 | kref_get(&(p->count)); | ||
349 | |||
350 | return p; | ||
351 | } | ||
352 | |||
353 | static inline void aa_put_replacedby(struct aa_replacedby *p) | ||
354 | { | ||
355 | if (p) | ||
356 | kref_put(&p->count, aa_free_replacedby_kref); | ||
357 | } | ||
358 | |||
359 | /* requires profile list write lock held */ | ||
360 | static inline void __aa_update_replacedby(struct aa_profile *orig, | ||
361 | struct aa_profile *new) | ||
362 | { | ||
363 | struct aa_profile *tmp = rcu_dereference(orig->replacedby->profile); | ||
364 | rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); | ||
365 | orig->flags |= PFLAG_INVALID; | ||
366 | aa_put_profile(tmp); | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * aa_get_namespace - increment references count on @ns | ||
371 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
372 | * | ||
373 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
374 | * Requires: @ns must be held with valid refcount when called | ||
375 | */ | ||
376 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
377 | { | ||
378 | if (ns) | ||
379 | aa_get_profile(ns->unconfined); | ||
380 | |||
381 | return ns; | ||
382 | } | ||
383 | |||
384 | /** | ||
385 | * aa_put_namespace - decrement refcount on @ns | ||
386 | * @ns: namespace to put reference of | ||
387 | * | ||
388 | * Decrement reference count of @ns and if no longer in use free it | ||
389 | */ | ||
390 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
391 | { | ||
392 | if (ns) | ||
393 | aa_put_profile(ns->unconfined); | ||
306 | } | 394 | } |
307 | 395 | ||
308 | static inline int AUDIT_MODE(struct aa_profile *profile) | 396 | static inline int AUDIT_MODE(struct aa_profile *profile) |
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index a2dcccac45aa..c214fb88b1bc 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h | |||
@@ -15,6 +15,25 @@ | |||
15 | #ifndef __POLICY_INTERFACE_H | 15 | #ifndef __POLICY_INTERFACE_H |
16 | #define __POLICY_INTERFACE_H | 16 | #define __POLICY_INTERFACE_H |
17 | 17 | ||
18 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); | 18 | #include <linux/list.h> |
19 | |||
20 | struct aa_load_ent { | ||
21 | struct list_head list; | ||
22 | struct aa_profile *new; | ||
23 | struct aa_profile *old; | ||
24 | struct aa_profile *rename; | ||
25 | }; | ||
26 | |||
27 | void aa_load_ent_free(struct aa_load_ent *ent); | ||
28 | struct aa_load_ent *aa_load_ent_alloc(void); | ||
29 | |||
30 | #define PACKED_FLAG_HAT 1 | ||
31 | |||
32 | #define PACKED_MODE_ENFORCE 0 | ||
33 | #define PACKED_MODE_COMPLAIN 1 | ||
34 | #define PACKED_MODE_KILL 2 | ||
35 | #define PACKED_MODE_UNCONFINED 3 | ||
36 | |||
37 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); | ||
19 | 38 | ||
20 | #endif /* __POLICY_INTERFACE_H */ | 39 | #endif /* __POLICY_INTERFACE_H */ |
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index fcfe0233574c..69689922c491 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c | |||
@@ -97,11 +97,6 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) | |||
97 | if (size <= (16*PAGE_SIZE)) | 97 | if (size <= (16*PAGE_SIZE)) |
98 | buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); | 98 | buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); |
99 | if (!buffer) { | 99 | if (!buffer) { |
100 | /* see kvfree for why size must be at least work_struct size | ||
101 | * when allocated via vmalloc | ||
102 | */ | ||
103 | if (size < sizeof(struct work_struct)) | ||
104 | size = sizeof(struct work_struct); | ||
105 | if (flags & __GFP_ZERO) | 100 | if (flags & __GFP_ZERO) |
106 | buffer = vzalloc(size); | 101 | buffer = vzalloc(size); |
107 | else | 102 | else |
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e3a704c75ef6..fb99e18123b4 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
@@ -508,19 +508,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, | |||
508 | /* released below */ | 508 | /* released below */ |
509 | const struct cred *cred = get_task_cred(task); | 509 | const struct cred *cred = get_task_cred(task); |
510 | struct aa_task_cxt *cxt = cred_cxt(cred); | 510 | struct aa_task_cxt *cxt = cred_cxt(cred); |
511 | struct aa_profile *profile = NULL; | ||
511 | 512 | ||
512 | if (strcmp(name, "current") == 0) | 513 | if (strcmp(name, "current") == 0) |
513 | error = aa_getprocattr(aa_newest_version(cxt->profile), | 514 | profile = aa_get_newest_profile(cxt->profile); |
514 | value); | ||
515 | else if (strcmp(name, "prev") == 0 && cxt->previous) | 515 | else if (strcmp(name, "prev") == 0 && cxt->previous) |
516 | error = aa_getprocattr(aa_newest_version(cxt->previous), | 516 | profile = aa_get_newest_profile(cxt->previous); |
517 | value); | ||
518 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | 517 | else if (strcmp(name, "exec") == 0 && cxt->onexec) |
519 | error = aa_getprocattr(aa_newest_version(cxt->onexec), | 518 | profile = aa_get_newest_profile(cxt->onexec); |
520 | value); | ||
521 | else | 519 | else |
522 | error = -EINVAL; | 520 | error = -EINVAL; |
523 | 521 | ||
522 | if (profile) | ||
523 | error = aa_getprocattr(profile, value); | ||
524 | |||
525 | aa_put_profile(profile); | ||
524 | put_cred(cred); | 526 | put_cred(cred); |
525 | 527 | ||
526 | return error; | 528 | return error; |
@@ -744,7 +746,7 @@ module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | |||
744 | 746 | ||
745 | /* Boot time disable flag */ | 747 | /* Boot time disable flag */ |
746 | static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; | 748 | static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; |
747 | module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); | 749 | module_param_named(enabled, apparmor_enabled, bool, S_IRUGO); |
748 | 750 | ||
749 | static int __init apparmor_enabled_setup(char *str) | 751 | static int __init apparmor_enabled_setup(char *str) |
750 | { | 752 | { |
@@ -843,7 +845,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) | |||
843 | if (!apparmor_enabled) | 845 | if (!apparmor_enabled) |
844 | return -EINVAL; | 846 | return -EINVAL; |
845 | 847 | ||
846 | return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); | 848 | return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); |
847 | } | 849 | } |
848 | 850 | ||
849 | static int param_set_mode(const char *val, struct kernel_param *kp) | 851 | static int param_set_mode(const char *val, struct kernel_param *kp) |
@@ -858,8 +860,8 @@ static int param_set_mode(const char *val, struct kernel_param *kp) | |||
858 | if (!val) | 860 | if (!val) |
859 | return -EINVAL; | 861 | return -EINVAL; |
860 | 862 | ||
861 | for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { | 863 | for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) { |
862 | if (strcmp(val, profile_mode_names[i]) == 0) { | 864 | if (strcmp(val, aa_profile_mode_names[i]) == 0) { |
863 | aa_g_profile_mode = i; | 865 | aa_g_profile_mode = i; |
864 | return 0; | 866 | return 0; |
865 | } | 867 | } |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0f345c4dee5f..6172509fa2b7 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -92,10 +92,11 @@ | |||
92 | /* root profile namespace */ | 92 | /* root profile namespace */ |
93 | struct aa_namespace *root_ns; | 93 | struct aa_namespace *root_ns; |
94 | 94 | ||
95 | const char *const profile_mode_names[] = { | 95 | const char *const aa_profile_mode_names[] = { |
96 | "enforce", | 96 | "enforce", |
97 | "complain", | 97 | "complain", |
98 | "kill", | 98 | "kill", |
99 | "unconfined", | ||
99 | }; | 100 | }; |
100 | 101 | ||
101 | /** | 102 | /** |
@@ -141,7 +142,6 @@ static bool policy_init(struct aa_policy *policy, const char *prefix, | |||
141 | policy->name = (char *)hname_tail(policy->hname); | 142 | policy->name = (char *)hname_tail(policy->hname); |
142 | INIT_LIST_HEAD(&policy->list); | 143 | INIT_LIST_HEAD(&policy->list); |
143 | INIT_LIST_HEAD(&policy->profiles); | 144 | INIT_LIST_HEAD(&policy->profiles); |
144 | kref_init(&policy->count); | ||
145 | 145 | ||
146 | return 1; | 146 | return 1; |
147 | } | 147 | } |
@@ -153,13 +153,13 @@ static bool policy_init(struct aa_policy *policy, const char *prefix, | |||
153 | static void policy_destroy(struct aa_policy *policy) | 153 | static void policy_destroy(struct aa_policy *policy) |
154 | { | 154 | { |
155 | /* still contains profiles -- invalid */ | 155 | /* still contains profiles -- invalid */ |
156 | if (!list_empty(&policy->profiles)) { | 156 | if (on_list_rcu(&policy->profiles)) { |
157 | AA_ERROR("%s: internal error, " | 157 | AA_ERROR("%s: internal error, " |
158 | "policy '%s' still contains profiles\n", | 158 | "policy '%s' still contains profiles\n", |
159 | __func__, policy->name); | 159 | __func__, policy->name); |
160 | BUG(); | 160 | BUG(); |
161 | } | 161 | } |
162 | if (!list_empty(&policy->list)) { | 162 | if (on_list_rcu(&policy->list)) { |
163 | AA_ERROR("%s: internal error, policy '%s' still on list\n", | 163 | AA_ERROR("%s: internal error, policy '%s' still on list\n", |
164 | __func__, policy->name); | 164 | __func__, policy->name); |
165 | BUG(); | 165 | BUG(); |
@@ -174,7 +174,7 @@ static void policy_destroy(struct aa_policy *policy) | |||
174 | * @head: list to search (NOT NULL) | 174 | * @head: list to search (NOT NULL) |
175 | * @name: name to search for (NOT NULL) | 175 | * @name: name to search for (NOT NULL) |
176 | * | 176 | * |
177 | * Requires: correct locks for the @head list be held | 177 | * Requires: rcu_read_lock be held |
178 | * | 178 | * |
179 | * Returns: unrefcounted policy that match @name or NULL if not found | 179 | * Returns: unrefcounted policy that match @name or NULL if not found |
180 | */ | 180 | */ |
@@ -182,7 +182,7 @@ static struct aa_policy *__policy_find(struct list_head *head, const char *name) | |||
182 | { | 182 | { |
183 | struct aa_policy *policy; | 183 | struct aa_policy *policy; |
184 | 184 | ||
185 | list_for_each_entry(policy, head, list) { | 185 | list_for_each_entry_rcu(policy, head, list) { |
186 | if (!strcmp(policy->name, name)) | 186 | if (!strcmp(policy->name, name)) |
187 | return policy; | 187 | return policy; |
188 | } | 188 | } |
@@ -195,7 +195,7 @@ static struct aa_policy *__policy_find(struct list_head *head, const char *name) | |||
195 | * @str: string to search for (NOT NULL) | 195 | * @str: string to search for (NOT NULL) |
196 | * @len: length of match required | 196 | * @len: length of match required |
197 | * | 197 | * |
198 | * Requires: correct locks for the @head list be held | 198 | * Requires: rcu_read_lock be held |
199 | * | 199 | * |
200 | * Returns: unrefcounted policy that match @str or NULL if not found | 200 | * Returns: unrefcounted policy that match @str or NULL if not found |
201 | * | 201 | * |
@@ -207,7 +207,7 @@ static struct aa_policy *__policy_strn_find(struct list_head *head, | |||
207 | { | 207 | { |
208 | struct aa_policy *policy; | 208 | struct aa_policy *policy; |
209 | 209 | ||
210 | list_for_each_entry(policy, head, list) { | 210 | list_for_each_entry_rcu(policy, head, list) { |
211 | if (aa_strneq(policy->name, str, len)) | 211 | if (aa_strneq(policy->name, str, len)) |
212 | return policy; | 212 | return policy; |
213 | } | 213 | } |
@@ -284,22 +284,19 @@ static struct aa_namespace *alloc_namespace(const char *prefix, | |||
284 | goto fail_ns; | 284 | goto fail_ns; |
285 | 285 | ||
286 | INIT_LIST_HEAD(&ns->sub_ns); | 286 | INIT_LIST_HEAD(&ns->sub_ns); |
287 | rwlock_init(&ns->lock); | 287 | mutex_init(&ns->lock); |
288 | 288 | ||
289 | /* released by free_namespace */ | 289 | /* released by free_namespace */ |
290 | ns->unconfined = aa_alloc_profile("unconfined"); | 290 | ns->unconfined = aa_alloc_profile("unconfined"); |
291 | if (!ns->unconfined) | 291 | if (!ns->unconfined) |
292 | goto fail_unconfined; | 292 | goto fail_unconfined; |
293 | 293 | ||
294 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | | 294 | ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | |
295 | PFLAG_IMMUTABLE; | 295 | PFLAG_IMMUTABLE | PFLAG_NS_COUNT; |
296 | ns->unconfined->mode = APPARMOR_UNCONFINED; | ||
296 | 297 | ||
297 | /* | 298 | /* ns and ns->unconfined share ns->unconfined refcount */ |
298 | * released by free_namespace, however __remove_namespace breaks | 299 | ns->unconfined->ns = ns; |
299 | * the cyclic references (ns->unconfined, and unconfined->ns) and | ||
300 | * replaces with refs to parent namespace unconfined | ||
301 | */ | ||
302 | ns->unconfined->ns = aa_get_namespace(ns); | ||
303 | 300 | ||
304 | atomic_set(&ns->uniq_null, 0); | 301 | atomic_set(&ns->uniq_null, 0); |
305 | 302 | ||
@@ -327,30 +324,19 @@ static void free_namespace(struct aa_namespace *ns) | |||
327 | policy_destroy(&ns->base); | 324 | policy_destroy(&ns->base); |
328 | aa_put_namespace(ns->parent); | 325 | aa_put_namespace(ns->parent); |
329 | 326 | ||
330 | if (ns->unconfined && ns->unconfined->ns == ns) | 327 | ns->unconfined->ns = NULL; |
331 | ns->unconfined->ns = NULL; | 328 | aa_free_profile(ns->unconfined); |
332 | |||
333 | aa_put_profile(ns->unconfined); | ||
334 | kzfree(ns); | 329 | kzfree(ns); |
335 | } | 330 | } |
336 | 331 | ||
337 | /** | 332 | /** |
338 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | ||
339 | * @kr: kref callback for freeing of a namespace (NOT NULL) | ||
340 | */ | ||
341 | void aa_free_namespace_kref(struct kref *kref) | ||
342 | { | ||
343 | free_namespace(container_of(kref, struct aa_namespace, base.count)); | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * __aa_find_namespace - find a namespace on a list by @name | 333 | * __aa_find_namespace - find a namespace on a list by @name |
348 | * @head: list to search for namespace on (NOT NULL) | 334 | * @head: list to search for namespace on (NOT NULL) |
349 | * @name: name of namespace to look for (NOT NULL) | 335 | * @name: name of namespace to look for (NOT NULL) |
350 | * | 336 | * |
351 | * Returns: unrefcounted namespace | 337 | * Returns: unrefcounted namespace |
352 | * | 338 | * |
353 | * Requires: ns lock be held | 339 | * Requires: rcu_read_lock be held |
354 | */ | 340 | */ |
355 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, | 341 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, |
356 | const char *name) | 342 | const char *name) |
@@ -373,9 +359,9 @@ struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | |||
373 | { | 359 | { |
374 | struct aa_namespace *ns = NULL; | 360 | struct aa_namespace *ns = NULL; |
375 | 361 | ||
376 | read_lock(&root->lock); | 362 | rcu_read_lock(); |
377 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | 363 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); |
378 | read_unlock(&root->lock); | 364 | rcu_read_unlock(); |
379 | 365 | ||
380 | return ns; | 366 | return ns; |
381 | } | 367 | } |
@@ -392,7 +378,7 @@ static struct aa_namespace *aa_prepare_namespace(const char *name) | |||
392 | 378 | ||
393 | root = aa_current_profile()->ns; | 379 | root = aa_current_profile()->ns; |
394 | 380 | ||
395 | write_lock(&root->lock); | 381 | mutex_lock(&root->lock); |
396 | 382 | ||
397 | /* if name isn't specified the profile is loaded to the current ns */ | 383 | /* if name isn't specified the profile is loaded to the current ns */ |
398 | if (!name) { | 384 | if (!name) { |
@@ -405,31 +391,23 @@ static struct aa_namespace *aa_prepare_namespace(const char *name) | |||
405 | /* released by caller */ | 391 | /* released by caller */ |
406 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | 392 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); |
407 | if (!ns) { | 393 | if (!ns) { |
408 | /* namespace not found */ | 394 | ns = alloc_namespace(root->base.hname, name); |
409 | struct aa_namespace *new_ns; | 395 | if (!ns) |
410 | write_unlock(&root->lock); | 396 | goto out; |
411 | new_ns = alloc_namespace(root->base.hname, name); | 397 | if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { |
412 | if (!new_ns) | 398 | AA_ERROR("Failed to create interface for ns %s\n", |
413 | return NULL; | 399 | ns->base.name); |
414 | write_lock(&root->lock); | 400 | free_namespace(ns); |
415 | /* test for race when new_ns was allocated */ | 401 | ns = NULL; |
416 | ns = __aa_find_namespace(&root->sub_ns, name); | 402 | goto out; |
417 | if (!ns) { | ||
418 | /* add parent ref */ | ||
419 | new_ns->parent = aa_get_namespace(root); | ||
420 | |||
421 | list_add(&new_ns->base.list, &root->sub_ns); | ||
422 | /* add list ref */ | ||
423 | ns = aa_get_namespace(new_ns); | ||
424 | } else { | ||
425 | /* raced so free the new one */ | ||
426 | free_namespace(new_ns); | ||
427 | /* get reference on namespace */ | ||
428 | aa_get_namespace(ns); | ||
429 | } | 403 | } |
404 | ns->parent = aa_get_namespace(root); | ||
405 | list_add_rcu(&ns->base.list, &root->sub_ns); | ||
406 | /* add list ref */ | ||
407 | aa_get_namespace(ns); | ||
430 | } | 408 | } |
431 | out: | 409 | out: |
432 | write_unlock(&root->lock); | 410 | mutex_unlock(&root->lock); |
433 | 411 | ||
434 | /* return ref */ | 412 | /* return ref */ |
435 | return ns; | 413 | return ns; |
@@ -447,7 +425,7 @@ out: | |||
447 | static void __list_add_profile(struct list_head *list, | 425 | static void __list_add_profile(struct list_head *list, |
448 | struct aa_profile *profile) | 426 | struct aa_profile *profile) |
449 | { | 427 | { |
450 | list_add(&profile->base.list, list); | 428 | list_add_rcu(&profile->base.list, list); |
451 | /* get list reference */ | 429 | /* get list reference */ |
452 | aa_get_profile(profile); | 430 | aa_get_profile(profile); |
453 | } | 431 | } |
@@ -466,49 +444,8 @@ static void __list_add_profile(struct list_head *list, | |||
466 | */ | 444 | */ |
467 | static void __list_remove_profile(struct aa_profile *profile) | 445 | static void __list_remove_profile(struct aa_profile *profile) |
468 | { | 446 | { |
469 | list_del_init(&profile->base.list); | 447 | list_del_rcu(&profile->base.list); |
470 | if (!(profile->flags & PFLAG_NO_LIST_REF)) | 448 | aa_put_profile(profile); |
471 | /* release list reference */ | ||
472 | aa_put_profile(profile); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * __replace_profile - replace @old with @new on a list | ||
477 | * @old: profile to be replaced (NOT NULL) | ||
478 | * @new: profile to replace @old with (NOT NULL) | ||
479 | * | ||
480 | * Will duplicate and refcount elements that @new inherits from @old | ||
481 | * and will inherit @old children. | ||
482 | * | ||
483 | * refcount @new for list, put @old list refcount | ||
484 | * | ||
485 | * Requires: namespace list lock be held, or list not be shared | ||
486 | */ | ||
487 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
488 | { | ||
489 | struct aa_policy *policy; | ||
490 | struct aa_profile *child, *tmp; | ||
491 | |||
492 | if (old->parent) | ||
493 | policy = &old->parent->base; | ||
494 | else | ||
495 | policy = &old->ns->base; | ||
496 | |||
497 | /* released when @new is freed */ | ||
498 | new->parent = aa_get_profile(old->parent); | ||
499 | new->ns = aa_get_namespace(old->ns); | ||
500 | __list_add_profile(&policy->profiles, new); | ||
501 | /* inherit children */ | ||
502 | list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | ||
503 | aa_put_profile(child->parent); | ||
504 | child->parent = aa_get_profile(new); | ||
505 | /* list refcount transferred to @new*/ | ||
506 | list_move(&child->base.list, &new->base.profiles); | ||
507 | } | ||
508 | |||
509 | /* released by free_profile */ | ||
510 | old->replacedby = aa_get_profile(new); | ||
511 | __list_remove_profile(old); | ||
512 | } | 449 | } |
513 | 450 | ||
514 | static void __profile_list_release(struct list_head *head); | 451 | static void __profile_list_release(struct list_head *head); |
@@ -524,7 +461,8 @@ static void __remove_profile(struct aa_profile *profile) | |||
524 | /* release any children lists first */ | 461 | /* release any children lists first */ |
525 | __profile_list_release(&profile->base.profiles); | 462 | __profile_list_release(&profile->base.profiles); |
526 | /* released by free_profile */ | 463 | /* released by free_profile */ |
527 | profile->replacedby = aa_get_profile(profile->ns->unconfined); | 464 | __aa_update_replacedby(profile, profile->ns->unconfined); |
465 | __aa_fs_profile_rmdir(profile); | ||
528 | __list_remove_profile(profile); | 466 | __list_remove_profile(profile); |
529 | } | 467 | } |
530 | 468 | ||
@@ -552,14 +490,17 @@ static void destroy_namespace(struct aa_namespace *ns) | |||
552 | if (!ns) | 490 | if (!ns) |
553 | return; | 491 | return; |
554 | 492 | ||
555 | write_lock(&ns->lock); | 493 | mutex_lock(&ns->lock); |
556 | /* release all profiles in this namespace */ | 494 | /* release all profiles in this namespace */ |
557 | __profile_list_release(&ns->base.profiles); | 495 | __profile_list_release(&ns->base.profiles); |
558 | 496 | ||
559 | /* release all sub namespaces */ | 497 | /* release all sub namespaces */ |
560 | __ns_list_release(&ns->sub_ns); | 498 | __ns_list_release(&ns->sub_ns); |
561 | 499 | ||
562 | write_unlock(&ns->lock); | 500 | if (ns->parent) |
501 | __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); | ||
502 | __aa_fs_namespace_rmdir(ns); | ||
503 | mutex_unlock(&ns->lock); | ||
563 | } | 504 | } |
564 | 505 | ||
565 | /** | 506 | /** |
@@ -570,25 +511,9 @@ static void destroy_namespace(struct aa_namespace *ns) | |||
570 | */ | 511 | */ |
571 | static void __remove_namespace(struct aa_namespace *ns) | 512 | static void __remove_namespace(struct aa_namespace *ns) |
572 | { | 513 | { |
573 | struct aa_profile *unconfined = ns->unconfined; | ||
574 | |||
575 | /* remove ns from namespace list */ | 514 | /* remove ns from namespace list */ |
576 | list_del_init(&ns->base.list); | 515 | list_del_rcu(&ns->base.list); |
577 | |||
578 | /* | ||
579 | * break the ns, unconfined profile cyclic reference and forward | ||
580 | * all new unconfined profiles requests to the parent namespace | ||
581 | * This will result in all confined tasks that have a profile | ||
582 | * being removed, inheriting the parent->unconfined profile. | ||
583 | */ | ||
584 | if (ns->parent) | ||
585 | ns->unconfined = aa_get_profile(ns->parent->unconfined); | ||
586 | |||
587 | destroy_namespace(ns); | 516 | destroy_namespace(ns); |
588 | |||
589 | /* release original ns->unconfined ref */ | ||
590 | aa_put_profile(unconfined); | ||
591 | /* release ns->base.list ref, from removal above */ | ||
592 | aa_put_namespace(ns); | 517 | aa_put_namespace(ns); |
593 | } | 518 | } |
594 | 519 | ||
@@ -634,8 +559,25 @@ void __init aa_free_root_ns(void) | |||
634 | aa_put_namespace(ns); | 559 | aa_put_namespace(ns); |
635 | } | 560 | } |
636 | 561 | ||
562 | |||
563 | static void free_replacedby(struct aa_replacedby *r) | ||
564 | { | ||
565 | if (r) { | ||
566 | aa_put_profile(rcu_dereference(r->profile)); | ||
567 | kzfree(r); | ||
568 | } | ||
569 | } | ||
570 | |||
571 | |||
572 | void aa_free_replacedby_kref(struct kref *kref) | ||
573 | { | ||
574 | struct aa_replacedby *r = container_of(kref, struct aa_replacedby, | ||
575 | count); | ||
576 | free_replacedby(r); | ||
577 | } | ||
578 | |||
637 | /** | 579 | /** |
638 | * free_profile - free a profile | 580 | * aa_free_profile - free a profile |
639 | * @profile: the profile to free (MAYBE NULL) | 581 | * @profile: the profile to free (MAYBE NULL) |
640 | * | 582 | * |
641 | * Free a profile, its hats and null_profile. All references to the profile, | 583 | * Free a profile, its hats and null_profile. All references to the profile, |
@@ -644,25 +586,16 @@ void __init aa_free_root_ns(void) | |||
644 | * If the profile was referenced from a task context, free_profile() will | 586 | * If the profile was referenced from a task context, free_profile() will |
645 | * be called from an rcu callback routine, so we must not sleep here. | 587 | * be called from an rcu callback routine, so we must not sleep here. |
646 | */ | 588 | */ |
647 | static void free_profile(struct aa_profile *profile) | 589 | void aa_free_profile(struct aa_profile *profile) |
648 | { | 590 | { |
649 | struct aa_profile *p; | ||
650 | |||
651 | AA_DEBUG("%s(%p)\n", __func__, profile); | 591 | AA_DEBUG("%s(%p)\n", __func__, profile); |
652 | 592 | ||
653 | if (!profile) | 593 | if (!profile) |
654 | return; | 594 | return; |
655 | 595 | ||
656 | if (!list_empty(&profile->base.list)) { | ||
657 | AA_ERROR("%s: internal error, " | ||
658 | "profile '%s' still on ns list\n", | ||
659 | __func__, profile->base.name); | ||
660 | BUG(); | ||
661 | } | ||
662 | |||
663 | /* free children profiles */ | 596 | /* free children profiles */ |
664 | policy_destroy(&profile->base); | 597 | policy_destroy(&profile->base); |
665 | aa_put_profile(profile->parent); | 598 | aa_put_profile(rcu_access_pointer(profile->parent)); |
666 | 599 | ||
667 | aa_put_namespace(profile->ns); | 600 | aa_put_namespace(profile->ns); |
668 | kzfree(profile->rename); | 601 | kzfree(profile->rename); |
@@ -671,44 +604,35 @@ static void free_profile(struct aa_profile *profile) | |||
671 | aa_free_cap_rules(&profile->caps); | 604 | aa_free_cap_rules(&profile->caps); |
672 | aa_free_rlimit_rules(&profile->rlimits); | 605 | aa_free_rlimit_rules(&profile->rlimits); |
673 | 606 | ||
607 | kzfree(profile->dirname); | ||
674 | aa_put_dfa(profile->xmatch); | 608 | aa_put_dfa(profile->xmatch); |
675 | aa_put_dfa(profile->policy.dfa); | 609 | aa_put_dfa(profile->policy.dfa); |
676 | 610 | aa_put_replacedby(profile->replacedby); | |
677 | /* put the profile reference for replacedby, but not via | ||
678 | * put_profile(kref_put). | ||
679 | * replacedby can form a long chain that can result in cascading | ||
680 | * frees that blows the stack because kref_put makes a nested fn | ||
681 | * call (it looks like recursion, with free_profile calling | ||
682 | * free_profile) for each profile in the chain lp#1056078. | ||
683 | */ | ||
684 | for (p = profile->replacedby; p; ) { | ||
685 | if (atomic_dec_and_test(&p->base.count.refcount)) { | ||
686 | /* no more refs on p, grab its replacedby */ | ||
687 | struct aa_profile *next = p->replacedby; | ||
688 | /* break the chain */ | ||
689 | p->replacedby = NULL; | ||
690 | /* now free p, chain is broken */ | ||
691 | free_profile(p); | ||
692 | |||
693 | /* follow up with next profile in the chain */ | ||
694 | p = next; | ||
695 | } else | ||
696 | break; | ||
697 | } | ||
698 | 611 | ||
699 | kzfree(profile); | 612 | kzfree(profile); |
700 | } | 613 | } |
701 | 614 | ||
702 | /** | 615 | /** |
616 | * aa_free_profile_rcu - free aa_profile by rcu (called by aa_free_profile_kref) | ||
617 | * @head: rcu_head callback for freeing of a profile (NOT NULL) | ||
618 | */ | ||
619 | static void aa_free_profile_rcu(struct rcu_head *head) | ||
620 | { | ||
621 | struct aa_profile *p = container_of(head, struct aa_profile, rcu); | ||
622 | if (p->flags & PFLAG_NS_COUNT) | ||
623 | free_namespace(p->ns); | ||
624 | else | ||
625 | aa_free_profile(p); | ||
626 | } | ||
627 | |||
628 | /** | ||
703 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) | 629 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) |
704 | * @kr: kref callback for freeing of a profile (NOT NULL) | 630 | * @kr: kref callback for freeing of a profile (NOT NULL) |
705 | */ | 631 | */ |
706 | void aa_free_profile_kref(struct kref *kref) | 632 | void aa_free_profile_kref(struct kref *kref) |
707 | { | 633 | { |
708 | struct aa_profile *p = container_of(kref, struct aa_profile, | 634 | struct aa_profile *p = container_of(kref, struct aa_profile, count); |
709 | base.count); | 635 | call_rcu(&p->rcu, aa_free_profile_rcu); |
710 | |||
711 | free_profile(p); | ||
712 | } | 636 | } |
713 | 637 | ||
714 | /** | 638 | /** |
@@ -726,13 +650,23 @@ struct aa_profile *aa_alloc_profile(const char *hname) | |||
726 | if (!profile) | 650 | if (!profile) |
727 | return NULL; | 651 | return NULL; |
728 | 652 | ||
729 | if (!policy_init(&profile->base, NULL, hname)) { | 653 | profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL); |
730 | kzfree(profile); | 654 | if (!profile->replacedby) |
731 | return NULL; | 655 | goto fail; |
732 | } | 656 | kref_init(&profile->replacedby->count); |
657 | |||
658 | if (!policy_init(&profile->base, NULL, hname)) | ||
659 | goto fail; | ||
660 | kref_init(&profile->count); | ||
733 | 661 | ||
734 | /* refcount released by caller */ | 662 | /* refcount released by caller */ |
735 | return profile; | 663 | return profile; |
664 | |||
665 | fail: | ||
666 | kzfree(profile->replacedby); | ||
667 | kzfree(profile); | ||
668 | |||
669 | return NULL; | ||
736 | } | 670 | } |
737 | 671 | ||
738 | /** | 672 | /** |
@@ -772,12 +706,12 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) | |||
772 | profile->flags |= PFLAG_HAT; | 706 | profile->flags |= PFLAG_HAT; |
773 | 707 | ||
774 | /* released on free_profile */ | 708 | /* released on free_profile */ |
775 | profile->parent = aa_get_profile(parent); | 709 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); |
776 | profile->ns = aa_get_namespace(parent->ns); | 710 | profile->ns = aa_get_namespace(parent->ns); |
777 | 711 | ||
778 | write_lock(&profile->ns->lock); | 712 | mutex_lock(&profile->ns->lock); |
779 | __list_add_profile(&parent->base.profiles, profile); | 713 | __list_add_profile(&parent->base.profiles, profile); |
780 | write_unlock(&profile->ns->lock); | 714 | mutex_unlock(&profile->ns->lock); |
781 | 715 | ||
782 | /* refcount released by caller */ | 716 | /* refcount released by caller */ |
783 | return profile; | 717 | return profile; |
@@ -793,7 +727,7 @@ fail: | |||
793 | * @head: list to search (NOT NULL) | 727 | * @head: list to search (NOT NULL) |
794 | * @name: name of profile (NOT NULL) | 728 | * @name: name of profile (NOT NULL) |
795 | * | 729 | * |
796 | * Requires: ns lock protecting list be held | 730 | * Requires: rcu_read_lock be held |
797 | * | 731 | * |
798 | * Returns: unrefcounted profile ptr, or NULL if not found | 732 | * Returns: unrefcounted profile ptr, or NULL if not found |
799 | */ | 733 | */ |
@@ -808,7 +742,7 @@ static struct aa_profile *__find_child(struct list_head *head, const char *name) | |||
808 | * @name: name of profile (NOT NULL) | 742 | * @name: name of profile (NOT NULL) |
809 | * @len: length of @name substring to match | 743 | * @len: length of @name substring to match |
810 | * | 744 | * |
811 | * Requires: ns lock protecting list be held | 745 | * Requires: rcu_read_lock be held |
812 | * | 746 | * |
813 | * Returns: unrefcounted profile ptr, or NULL if not found | 747 | * Returns: unrefcounted profile ptr, or NULL if not found |
814 | */ | 748 | */ |
@@ -829,9 +763,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | |||
829 | { | 763 | { |
830 | struct aa_profile *profile; | 764 | struct aa_profile *profile; |
831 | 765 | ||
832 | read_lock(&parent->ns->lock); | 766 | rcu_read_lock(); |
833 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); | 767 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); |
834 | read_unlock(&parent->ns->lock); | 768 | rcu_read_unlock(); |
835 | 769 | ||
836 | /* refcount released by caller */ | 770 | /* refcount released by caller */ |
837 | return profile; | 771 | return profile; |
@@ -846,7 +780,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | |||
846 | * that matches hname does not need to exist, in general this | 780 | * that matches hname does not need to exist, in general this |
847 | * is used to load a new profile. | 781 | * is used to load a new profile. |
848 | * | 782 | * |
849 | * Requires: ns->lock be held | 783 | * Requires: rcu_read_lock be held |
850 | * | 784 | * |
851 | * Returns: unrefcounted policy or NULL if not found | 785 | * Returns: unrefcounted policy or NULL if not found |
852 | */ | 786 | */ |
@@ -878,7 +812,7 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | |||
878 | * @base: base list to start looking up profile name from (NOT NULL) | 812 | * @base: base list to start looking up profile name from (NOT NULL) |
879 | * @hname: hierarchical profile name (NOT NULL) | 813 | * @hname: hierarchical profile name (NOT NULL) |
880 | * | 814 | * |
881 | * Requires: ns->lock be held | 815 | * Requires: rcu_read_lock be held |
882 | * | 816 | * |
883 | * Returns: unrefcounted profile pointer or NULL if not found | 817 | * Returns: unrefcounted profile pointer or NULL if not found |
884 | * | 818 | * |
@@ -917,13 +851,15 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | |||
917 | { | 851 | { |
918 | struct aa_profile *profile; | 852 | struct aa_profile *profile; |
919 | 853 | ||
920 | read_lock(&ns->lock); | 854 | rcu_read_lock(); |
921 | profile = aa_get_profile(__lookup_profile(&ns->base, hname)); | 855 | do { |
922 | read_unlock(&ns->lock); | 856 | profile = __lookup_profile(&ns->base, hname); |
857 | } while (profile && !aa_get_profile_not0(profile)); | ||
858 | rcu_read_unlock(); | ||
923 | 859 | ||
924 | /* the unconfined profile is not in the regular profile list */ | 860 | /* the unconfined profile is not in the regular profile list */ |
925 | if (!profile && strcmp(hname, "unconfined") == 0) | 861 | if (!profile && strcmp(hname, "unconfined") == 0) |
926 | profile = aa_get_profile(ns->unconfined); | 862 | profile = aa_get_newest_profile(ns->unconfined); |
927 | 863 | ||
928 | /* refcount released by caller */ | 864 | /* refcount released by caller */ |
929 | return profile; | 865 | return profile; |
@@ -953,25 +889,6 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, | |||
953 | } | 889 | } |
954 | 890 | ||
955 | /** | 891 | /** |
956 | * __add_new_profile - simple wrapper around __list_add_profile | ||
957 | * @ns: namespace that profile is being added to (NOT NULL) | ||
958 | * @policy: the policy container to add the profile to (NOT NULL) | ||
959 | * @profile: profile to add (NOT NULL) | ||
960 | * | ||
961 | * add a profile to a list and do other required basic allocations | ||
962 | */ | ||
963 | static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, | ||
964 | struct aa_profile *profile) | ||
965 | { | ||
966 | if (policy != &ns->base) | ||
967 | /* released on profile replacement or free_profile */ | ||
968 | profile->parent = aa_get_profile((struct aa_profile *) policy); | ||
969 | __list_add_profile(&policy->profiles, profile); | ||
970 | /* released on free_profile */ | ||
971 | profile->ns = aa_get_namespace(ns); | ||
972 | } | ||
973 | |||
974 | /** | ||
975 | * aa_audit_policy - Do auditing of policy changes | 892 | * aa_audit_policy - Do auditing of policy changes |
976 | * @op: policy operation being performed | 893 | * @op: policy operation being performed |
977 | * @gfp: memory allocation flags | 894 | * @gfp: memory allocation flags |
@@ -1019,6 +936,121 @@ bool aa_may_manage_policy(int op) | |||
1019 | return 1; | 936 | return 1; |
1020 | } | 937 | } |
1021 | 938 | ||
939 | static struct aa_profile *__list_lookup_parent(struct list_head *lh, | ||
940 | struct aa_profile *profile) | ||
941 | { | ||
942 | const char *base = hname_tail(profile->base.hname); | ||
943 | long len = base - profile->base.hname; | ||
944 | struct aa_load_ent *ent; | ||
945 | |||
946 | /* parent won't have trailing // so remove from len */ | ||
947 | if (len <= 2) | ||
948 | return NULL; | ||
949 | len -= 2; | ||
950 | |||
951 | list_for_each_entry(ent, lh, list) { | ||
952 | if (ent->new == profile) | ||
953 | continue; | ||
954 | if (strncmp(ent->new->base.hname, profile->base.hname, len) == | ||
955 | 0 && ent->new->base.hname[len] == 0) | ||
956 | return ent->new; | ||
957 | } | ||
958 | |||
959 | return NULL; | ||
960 | } | ||
961 | |||
962 | /** | ||
963 | * __replace_profile - replace @old with @new on a list | ||
964 | * @old: profile to be replaced (NOT NULL) | ||
965 | * @new: profile to replace @old with (NOT NULL) | ||
966 | * @share_replacedby: transfer @old->replacedby to @new | ||
967 | * | ||
968 | * Will duplicate and refcount elements that @new inherits from @old | ||
969 | * and will inherit @old children. | ||
970 | * | ||
971 | * refcount @new for list, put @old list refcount | ||
972 | * | ||
973 | * Requires: namespace list lock be held, or list not be shared | ||
974 | */ | ||
975 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | ||
976 | bool share_replacedby) | ||
977 | { | ||
978 | struct aa_profile *child, *tmp; | ||
979 | |||
980 | if (!list_empty(&old->base.profiles)) { | ||
981 | LIST_HEAD(lh); | ||
982 | list_splice_init_rcu(&old->base.profiles, &lh, synchronize_rcu); | ||
983 | |||
984 | list_for_each_entry_safe(child, tmp, &lh, base.list) { | ||
985 | struct aa_profile *p; | ||
986 | |||
987 | list_del_init(&child->base.list); | ||
988 | p = __find_child(&new->base.profiles, child->base.name); | ||
989 | if (p) { | ||
990 | /* @p replaces @child */ | ||
991 | __replace_profile(child, p, share_replacedby); | ||
992 | continue; | ||
993 | } | ||
994 | |||
995 | /* inherit @child and its children */ | ||
996 | /* TODO: update hname of inherited children */ | ||
997 | /* list refcount transferred to @new */ | ||
998 | p = aa_deref_parent(child); | ||
999 | rcu_assign_pointer(child->parent, aa_get_profile(new)); | ||
1000 | list_add_rcu(&child->base.list, &new->base.profiles); | ||
1001 | aa_put_profile(p); | ||
1002 | } | ||
1003 | } | ||
1004 | |||
1005 | if (!rcu_access_pointer(new->parent)) { | ||
1006 | struct aa_profile *parent = aa_deref_parent(old); | ||
1007 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); | ||
1008 | } | ||
1009 | __aa_update_replacedby(old, new); | ||
1010 | if (share_replacedby) { | ||
1011 | aa_put_replacedby(new->replacedby); | ||
1012 | new->replacedby = aa_get_replacedby(old->replacedby); | ||
1013 | } else if (!rcu_access_pointer(new->replacedby->profile)) | ||
1014 | /* aafs interface uses replacedby */ | ||
1015 | rcu_assign_pointer(new->replacedby->profile, | ||
1016 | aa_get_profile(new)); | ||
1017 | __aa_fs_profile_migrate_dents(old, new); | ||
1018 | |||
1019 | if (list_empty(&new->base.list)) { | ||
1020 | /* new is not on a list already */ | ||
1021 | list_replace_rcu(&old->base.list, &new->base.list); | ||
1022 | aa_get_profile(new); | ||
1023 | aa_put_profile(old); | ||
1024 | } else | ||
1025 | __list_remove_profile(old); | ||
1026 | } | ||
1027 | |||
1028 | /** | ||
1029 | * __lookup_replace - lookup replacement information for a profile | ||
1030 | * @ns - namespace the lookup occurs in | ||
1031 | * @hname - name of profile to lookup | ||
1032 | * @noreplace - true if not replacing an existing profile | ||
1033 | * @p - Returns: profile to be replaced | ||
1034 | * @info - Returns: info string on why lookup failed | ||
1035 | * | ||
1036 | * Returns: profile to replace (no ref) on success else ptr error | ||
1037 | */ | ||
1038 | static int __lookup_replace(struct aa_namespace *ns, const char *hname, | ||
1039 | bool noreplace, struct aa_profile **p, | ||
1040 | const char **info) | ||
1041 | { | ||
1042 | *p = aa_get_profile(__lookup_profile(&ns->base, hname)); | ||
1043 | if (*p) { | ||
1044 | int error = replacement_allowed(*p, noreplace, info); | ||
1045 | if (error) { | ||
1046 | *info = "profile can not be replaced"; | ||
1047 | return error; | ||
1048 | } | ||
1049 | } | ||
1050 | |||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1022 | /** | 1054 | /** |
1023 | * aa_replace_profiles - replace profile(s) on the profile list | 1055 | * aa_replace_profiles - replace profile(s) on the profile list |
1024 | * @udata: serialized data stream (NOT NULL) | 1056 | * @udata: serialized data stream (NOT NULL) |
@@ -1033,21 +1065,17 @@ bool aa_may_manage_policy(int op) | |||
1033 | */ | 1065 | */ |
1034 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | 1066 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) |
1035 | { | 1067 | { |
1036 | struct aa_policy *policy; | ||
1037 | struct aa_profile *old_profile = NULL, *new_profile = NULL; | ||
1038 | struct aa_profile *rename_profile = NULL; | ||
1039 | struct aa_namespace *ns = NULL; | ||
1040 | const char *ns_name, *name = NULL, *info = NULL; | 1068 | const char *ns_name, *name = NULL, *info = NULL; |
1069 | struct aa_namespace *ns = NULL; | ||
1070 | struct aa_load_ent *ent, *tmp; | ||
1041 | int op = OP_PROF_REPL; | 1071 | int op = OP_PROF_REPL; |
1042 | ssize_t error; | 1072 | ssize_t error; |
1073 | LIST_HEAD(lh); | ||
1043 | 1074 | ||
1044 | /* released below */ | 1075 | /* released below */ |
1045 | new_profile = aa_unpack(udata, size, &ns_name); | 1076 | error = aa_unpack(udata, size, &lh, &ns_name); |
1046 | if (IS_ERR(new_profile)) { | 1077 | if (error) |
1047 | error = PTR_ERR(new_profile); | 1078 | goto out; |
1048 | new_profile = NULL; | ||
1049 | goto fail; | ||
1050 | } | ||
1051 | 1079 | ||
1052 | /* released below */ | 1080 | /* released below */ |
1053 | ns = aa_prepare_namespace(ns_name); | 1081 | ns = aa_prepare_namespace(ns_name); |
@@ -1058,71 +1086,140 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
1058 | goto fail; | 1086 | goto fail; |
1059 | } | 1087 | } |
1060 | 1088 | ||
1061 | name = new_profile->base.hname; | 1089 | mutex_lock(&ns->lock); |
1062 | 1090 | /* setup parent and ns info */ | |
1063 | write_lock(&ns->lock); | 1091 | list_for_each_entry(ent, &lh, list) { |
1064 | /* no ref on policy only use inside lock */ | 1092 | struct aa_policy *policy; |
1065 | policy = __lookup_parent(ns, new_profile->base.hname); | 1093 | |
1094 | name = ent->new->base.hname; | ||
1095 | error = __lookup_replace(ns, ent->new->base.hname, noreplace, | ||
1096 | &ent->old, &info); | ||
1097 | if (error) | ||
1098 | goto fail_lock; | ||
1099 | |||
1100 | if (ent->new->rename) { | ||
1101 | error = __lookup_replace(ns, ent->new->rename, | ||
1102 | noreplace, &ent->rename, | ||
1103 | &info); | ||
1104 | if (error) | ||
1105 | goto fail_lock; | ||
1106 | } | ||
1066 | 1107 | ||
1067 | if (!policy) { | 1108 | /* released when @new is freed */ |
1068 | info = "parent does not exist"; | 1109 | ent->new->ns = aa_get_namespace(ns); |
1069 | error = -ENOENT; | 1110 | |
1070 | goto audit; | 1111 | if (ent->old || ent->rename) |
1112 | continue; | ||
1113 | |||
1114 | /* no ref on policy only use inside lock */ | ||
1115 | policy = __lookup_parent(ns, ent->new->base.hname); | ||
1116 | if (!policy) { | ||
1117 | struct aa_profile *p; | ||
1118 | p = __list_lookup_parent(&lh, ent->new); | ||
1119 | if (!p) { | ||
1120 | error = -ENOENT; | ||
1121 | info = "parent does not exist"; | ||
1122 | name = ent->new->base.hname; | ||
1123 | goto fail_lock; | ||
1124 | } | ||
1125 | rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); | ||
1126 | } else if (policy != &ns->base) { | ||
1127 | /* released on profile replacement or free_profile */ | ||
1128 | struct aa_profile *p = (struct aa_profile *) policy; | ||
1129 | rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); | ||
1130 | } | ||
1071 | } | 1131 | } |
1072 | 1132 | ||
1073 | old_profile = __find_child(&policy->profiles, new_profile->base.name); | 1133 | /* create new fs entries for introspection if needed */ |
1074 | /* released below */ | 1134 | list_for_each_entry(ent, &lh, list) { |
1075 | aa_get_profile(old_profile); | 1135 | if (ent->old) { |
1136 | /* inherit old interface files */ | ||
1076 | 1137 | ||
1077 | if (new_profile->rename) { | 1138 | /* if (ent->rename) |
1078 | rename_profile = __lookup_profile(&ns->base, | 1139 | TODO: support rename */ |
1079 | new_profile->rename); | 1140 | /* } else if (ent->rename) { |
1080 | /* released below */ | 1141 | TODO: support rename */ |
1081 | aa_get_profile(rename_profile); | 1142 | } else { |
1143 | struct dentry *parent; | ||
1144 | if (rcu_access_pointer(ent->new->parent)) { | ||
1145 | struct aa_profile *p; | ||
1146 | p = aa_deref_parent(ent->new); | ||
1147 | parent = prof_child_dir(p); | ||
1148 | } else | ||
1149 | parent = ns_subprofs_dir(ent->new->ns); | ||
1150 | error = __aa_fs_profile_mkdir(ent->new, parent); | ||
1151 | } | ||
1082 | 1152 | ||
1083 | if (!rename_profile) { | 1153 | if (error) { |
1084 | info = "profile to rename does not exist"; | 1154 | info = "failed to create "; |
1085 | name = new_profile->rename; | 1155 | goto fail_lock; |
1086 | error = -ENOENT; | ||
1087 | goto audit; | ||
1088 | } | 1156 | } |
1089 | } | 1157 | } |
1090 | 1158 | ||
1091 | error = replacement_allowed(old_profile, noreplace, &info); | 1159 | /* Done with checks that may fail - do actual replacement */ |
1092 | if (error) | 1160 | list_for_each_entry_safe(ent, tmp, &lh, list) { |
1093 | goto audit; | 1161 | list_del_init(&ent->list); |
1094 | 1162 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; | |
1095 | error = replacement_allowed(rename_profile, noreplace, &info); | 1163 | |
1096 | if (error) | 1164 | audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); |
1097 | goto audit; | 1165 | |
1098 | 1166 | if (ent->old) { | |
1099 | audit: | 1167 | __replace_profile(ent->old, ent->new, 1); |
1100 | if (!old_profile && !rename_profile) | 1168 | if (ent->rename) { |
1101 | op = OP_PROF_LOAD; | 1169 | /* aafs interface uses replacedby */ |
1102 | 1170 | struct aa_replacedby *r = ent->new->replacedby; | |
1103 | error = audit_policy(op, GFP_ATOMIC, name, info, error); | 1171 | rcu_assign_pointer(r->profile, |
1104 | 1172 | aa_get_profile(ent->new)); | |
1105 | if (!error) { | 1173 | __replace_profile(ent->rename, ent->new, 0); |
1106 | if (rename_profile) | 1174 | } |
1107 | __replace_profile(rename_profile, new_profile); | 1175 | } else if (ent->rename) { |
1108 | if (old_profile) | 1176 | /* aafs interface uses replacedby */ |
1109 | __replace_profile(old_profile, new_profile); | 1177 | rcu_assign_pointer(ent->new->replacedby->profile, |
1110 | if (!(old_profile || rename_profile)) | 1178 | aa_get_profile(ent->new)); |
1111 | __add_new_profile(ns, policy, new_profile); | 1179 | __replace_profile(ent->rename, ent->new, 0); |
1180 | } else if (ent->new->parent) { | ||
1181 | struct aa_profile *parent, *newest; | ||
1182 | parent = aa_deref_parent(ent->new); | ||
1183 | newest = aa_get_newest_profile(parent); | ||
1184 | |||
1185 | /* parent replaced in this atomic set? */ | ||
1186 | if (newest != parent) { | ||
1187 | aa_get_profile(newest); | ||
1188 | aa_put_profile(parent); | ||
1189 | rcu_assign_pointer(ent->new->parent, newest); | ||
1190 | } else | ||
1191 | aa_put_profile(newest); | ||
1192 | /* aafs interface uses replacedby */ | ||
1193 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
1194 | aa_get_profile(ent->new)); | ||
1195 | __list_add_profile(&parent->base.profiles, ent->new); | ||
1196 | } else { | ||
1197 | /* aafs interface uses replacedby */ | ||
1198 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
1199 | aa_get_profile(ent->new)); | ||
1200 | __list_add_profile(&ns->base.profiles, ent->new); | ||
1201 | } | ||
1202 | aa_load_ent_free(ent); | ||
1112 | } | 1203 | } |
1113 | write_unlock(&ns->lock); | 1204 | mutex_unlock(&ns->lock); |
1114 | 1205 | ||
1115 | out: | 1206 | out: |
1116 | aa_put_namespace(ns); | 1207 | aa_put_namespace(ns); |
1117 | aa_put_profile(rename_profile); | 1208 | |
1118 | aa_put_profile(old_profile); | ||
1119 | aa_put_profile(new_profile); | ||
1120 | if (error) | 1209 | if (error) |
1121 | return error; | 1210 | return error; |
1122 | return size; | 1211 | return size; |
1123 | 1212 | ||
1213 | fail_lock: | ||
1214 | mutex_unlock(&ns->lock); | ||
1124 | fail: | 1215 | fail: |
1125 | error = audit_policy(op, GFP_KERNEL, name, info, error); | 1216 | error = audit_policy(op, GFP_KERNEL, name, info, error); |
1217 | |||
1218 | list_for_each_entry_safe(ent, tmp, &lh, list) { | ||
1219 | list_del_init(&ent->list); | ||
1220 | aa_load_ent_free(ent); | ||
1221 | } | ||
1222 | |||
1126 | goto out; | 1223 | goto out; |
1127 | } | 1224 | } |
1128 | 1225 | ||
@@ -1169,12 +1266,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
1169 | 1266 | ||
1170 | if (!name) { | 1267 | if (!name) { |
1171 | /* remove namespace - can only happen if fqname[0] == ':' */ | 1268 | /* remove namespace - can only happen if fqname[0] == ':' */ |
1172 | write_lock(&ns->parent->lock); | 1269 | mutex_lock(&ns->parent->lock); |
1173 | __remove_namespace(ns); | 1270 | __remove_namespace(ns); |
1174 | write_unlock(&ns->parent->lock); | 1271 | mutex_unlock(&ns->parent->lock); |
1175 | } else { | 1272 | } else { |
1176 | /* remove profile */ | 1273 | /* remove profile */ |
1177 | write_lock(&ns->lock); | 1274 | mutex_lock(&ns->lock); |
1178 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); | 1275 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); |
1179 | if (!profile) { | 1276 | if (!profile) { |
1180 | error = -ENOENT; | 1277 | error = -ENOENT; |
@@ -1183,7 +1280,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
1183 | } | 1280 | } |
1184 | name = profile->base.hname; | 1281 | name = profile->base.hname; |
1185 | __remove_profile(profile); | 1282 | __remove_profile(profile); |
1186 | write_unlock(&ns->lock); | 1283 | mutex_unlock(&ns->lock); |
1187 | } | 1284 | } |
1188 | 1285 | ||
1189 | /* don't fail removal if audit fails */ | 1286 | /* don't fail removal if audit fails */ |
@@ -1193,7 +1290,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
1193 | return size; | 1290 | return size; |
1194 | 1291 | ||
1195 | fail_ns_lock: | 1292 | fail_ns_lock: |
1196 | write_unlock(&ns->lock); | 1293 | mutex_unlock(&ns->lock); |
1197 | aa_put_namespace(ns); | 1294 | aa_put_namespace(ns); |
1198 | 1295 | ||
1199 | fail: | 1296 | fail: |
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6dac7d77cb4d..a689f10930b5 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "include/apparmor.h" | 24 | #include "include/apparmor.h" |
25 | #include "include/audit.h" | 25 | #include "include/audit.h" |
26 | #include "include/context.h" | 26 | #include "include/context.h" |
27 | #include "include/crypto.h" | ||
27 | #include "include/match.h" | 28 | #include "include/match.h" |
28 | #include "include/policy.h" | 29 | #include "include/policy.h" |
29 | #include "include/policy_unpack.h" | 30 | #include "include/policy_unpack.h" |
@@ -333,8 +334,10 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) | |||
333 | /* | 334 | /* |
334 | * The dfa is aligned with in the blob to 8 bytes | 335 | * The dfa is aligned with in the blob to 8 bytes |
335 | * from the beginning of the stream. | 336 | * from the beginning of the stream. |
337 | * alignment adjust needed by dfa unpack | ||
336 | */ | 338 | */ |
337 | size_t sz = blob - (char *)e->start; | 339 | size_t sz = blob - (char *) e->start - |
340 | ((e->pos - e->start) & 7); | ||
338 | size_t pad = ALIGN(sz, 8) - sz; | 341 | size_t pad = ALIGN(sz, 8) - sz; |
339 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | 342 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | |
340 | TO_ACCEPT2_FLAG(YYTD_DATA32); | 343 | TO_ACCEPT2_FLAG(YYTD_DATA32); |
@@ -490,6 +493,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
490 | /* profile renaming is optional */ | 493 | /* profile renaming is optional */ |
491 | (void) unpack_str(e, &profile->rename, "rename"); | 494 | (void) unpack_str(e, &profile->rename, "rename"); |
492 | 495 | ||
496 | /* attachment string is optional */ | ||
497 | (void) unpack_str(e, &profile->attach, "attach"); | ||
498 | |||
493 | /* xmatch is optional and may be NULL */ | 499 | /* xmatch is optional and may be NULL */ |
494 | profile->xmatch = unpack_dfa(e); | 500 | profile->xmatch = unpack_dfa(e); |
495 | if (IS_ERR(profile->xmatch)) { | 501 | if (IS_ERR(profile->xmatch)) { |
@@ -509,12 +515,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
509 | goto fail; | 515 | goto fail; |
510 | if (!unpack_u32(e, &tmp, NULL)) | 516 | if (!unpack_u32(e, &tmp, NULL)) |
511 | goto fail; | 517 | goto fail; |
512 | if (tmp) | 518 | if (tmp & PACKED_FLAG_HAT) |
513 | profile->flags |= PFLAG_HAT; | 519 | profile->flags |= PFLAG_HAT; |
514 | if (!unpack_u32(e, &tmp, NULL)) | 520 | if (!unpack_u32(e, &tmp, NULL)) |
515 | goto fail; | 521 | goto fail; |
516 | if (tmp) | 522 | if (tmp == PACKED_MODE_COMPLAIN) |
517 | profile->mode = APPARMOR_COMPLAIN; | 523 | profile->mode = APPARMOR_COMPLAIN; |
524 | else if (tmp == PACKED_MODE_KILL) | ||
525 | profile->mode = APPARMOR_KILL; | ||
526 | else if (tmp == PACKED_MODE_UNCONFINED) | ||
527 | profile->mode = APPARMOR_UNCONFINED; | ||
518 | if (!unpack_u32(e, &tmp, NULL)) | 528 | if (!unpack_u32(e, &tmp, NULL)) |
519 | goto fail; | 529 | goto fail; |
520 | if (tmp) | 530 | if (tmp) |
@@ -614,7 +624,7 @@ fail: | |||
614 | else if (!name) | 624 | else if (!name) |
615 | name = "unknown"; | 625 | name = "unknown"; |
616 | audit_iface(profile, name, "failed to unpack profile", e, error); | 626 | audit_iface(profile, name, "failed to unpack profile", e, error); |
617 | aa_put_profile(profile); | 627 | aa_free_profile(profile); |
618 | 628 | ||
619 | return ERR_PTR(error); | 629 | return ERR_PTR(error); |
620 | } | 630 | } |
@@ -622,29 +632,41 @@ fail: | |||
622 | /** | 632 | /** |
623 | * verify_head - unpack serialized stream header | 633 | * verify_head - unpack serialized stream header |
624 | * @e: serialized data read head (NOT NULL) | 634 | * @e: serialized data read head (NOT NULL) |
635 | * @required: whether the header is required or optional | ||
625 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | 636 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) |
626 | * | 637 | * |
627 | * Returns: error or 0 if header is good | 638 | * Returns: error or 0 if header is good |
628 | */ | 639 | */ |
629 | static int verify_header(struct aa_ext *e, const char **ns) | 640 | static int verify_header(struct aa_ext *e, int required, const char **ns) |
630 | { | 641 | { |
631 | int error = -EPROTONOSUPPORT; | 642 | int error = -EPROTONOSUPPORT; |
643 | const char *name = NULL; | ||
644 | *ns = NULL; | ||
645 | |||
632 | /* get the interface version */ | 646 | /* get the interface version */ |
633 | if (!unpack_u32(e, &e->version, "version")) { | 647 | if (!unpack_u32(e, &e->version, "version")) { |
634 | audit_iface(NULL, NULL, "invalid profile format", e, error); | 648 | if (required) { |
635 | return error; | 649 | audit_iface(NULL, NULL, "invalid profile format", e, |
636 | } | 650 | error); |
651 | return error; | ||
652 | } | ||
637 | 653 | ||
638 | /* check that the interface version is currently supported */ | 654 | /* check that the interface version is currently supported */ |
639 | if (e->version != 5) { | 655 | if (e->version != 5) { |
640 | audit_iface(NULL, NULL, "unsupported interface version", e, | 656 | audit_iface(NULL, NULL, "unsupported interface version", |
641 | error); | 657 | e, error); |
642 | return error; | 658 | return error; |
659 | } | ||
643 | } | 660 | } |
644 | 661 | ||
662 | |||
645 | /* read the namespace if present */ | 663 | /* read the namespace if present */ |
646 | if (!unpack_str(e, ns, "namespace")) | 664 | if (unpack_str(e, &name, "namespace")) { |
647 | *ns = NULL; | 665 | if (*ns && strcmp(*ns, name)) |
666 | audit_iface(NULL, NULL, "invalid ns change", e, error); | ||
667 | else if (!*ns) | ||
668 | *ns = name; | ||
669 | } | ||
648 | 670 | ||
649 | return 0; | 671 | return 0; |
650 | } | 672 | } |
@@ -693,18 +715,40 @@ static int verify_profile(struct aa_profile *profile) | |||
693 | return 0; | 715 | return 0; |
694 | } | 716 | } |
695 | 717 | ||
718 | void aa_load_ent_free(struct aa_load_ent *ent) | ||
719 | { | ||
720 | if (ent) { | ||
721 | aa_put_profile(ent->rename); | ||
722 | aa_put_profile(ent->old); | ||
723 | aa_put_profile(ent->new); | ||
724 | kzfree(ent); | ||
725 | } | ||
726 | } | ||
727 | |||
728 | struct aa_load_ent *aa_load_ent_alloc(void) | ||
729 | { | ||
730 | struct aa_load_ent *ent = kzalloc(sizeof(*ent), GFP_KERNEL); | ||
731 | if (ent) | ||
732 | INIT_LIST_HEAD(&ent->list); | ||
733 | return ent; | ||
734 | } | ||
735 | |||
696 | /** | 736 | /** |
697 | * aa_unpack - unpack packed binary profile data loaded from user space | 737 | * aa_unpack - unpack packed binary profile(s) data loaded from user space |
698 | * @udata: user data copied to kmem (NOT NULL) | 738 | * @udata: user data copied to kmem (NOT NULL) |
699 | * @size: the size of the user data | 739 | * @size: the size of the user data |
740 | * @lh: list to place unpacked profiles in a aa_repl_ws | ||
700 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | 741 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) |
701 | * | 742 | * |
702 | * Unpack user data and return refcounted allocated profile or ERR_PTR | 743 | * Unpack user data and return refcounted allocated profile(s) stored in |
744 | * @lh in order of discovery, with the list chain stored in base.list | ||
745 | * or error | ||
703 | * | 746 | * |
704 | * Returns: profile else error pointer if fails to unpack | 747 | * Returns: profile(s) on @lh else error pointer if fails to unpack |
705 | */ | 748 | */ |
706 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | 749 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) |
707 | { | 750 | { |
751 | struct aa_load_ent *tmp, *ent; | ||
708 | struct aa_profile *profile = NULL; | 752 | struct aa_profile *profile = NULL; |
709 | int error; | 753 | int error; |
710 | struct aa_ext e = { | 754 | struct aa_ext e = { |
@@ -713,20 +757,49 @@ struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | |||
713 | .pos = udata, | 757 | .pos = udata, |
714 | }; | 758 | }; |
715 | 759 | ||
716 | error = verify_header(&e, ns); | 760 | *ns = NULL; |
717 | if (error) | 761 | while (e.pos < e.end) { |
718 | return ERR_PTR(error); | 762 | void *start; |
763 | error = verify_header(&e, e.pos == e.start, ns); | ||
764 | if (error) | ||
765 | goto fail; | ||
766 | |||
767 | start = e.pos; | ||
768 | profile = unpack_profile(&e); | ||
769 | if (IS_ERR(profile)) { | ||
770 | error = PTR_ERR(profile); | ||
771 | goto fail; | ||
772 | } | ||
773 | |||
774 | error = verify_profile(profile); | ||
775 | if (error) | ||
776 | goto fail_profile; | ||
777 | |||
778 | error = aa_calc_profile_hash(profile, e.version, start, | ||
779 | e.pos - start); | ||
780 | if (error) | ||
781 | goto fail_profile; | ||
782 | |||
783 | ent = aa_load_ent_alloc(); | ||
784 | if (!ent) { | ||
785 | error = -ENOMEM; | ||
786 | goto fail_profile; | ||
787 | } | ||
788 | |||
789 | ent->new = profile; | ||
790 | list_add_tail(&ent->list, lh); | ||
791 | } | ||
792 | |||
793 | return 0; | ||
719 | 794 | ||
720 | profile = unpack_profile(&e); | 795 | fail_profile: |
721 | if (IS_ERR(profile)) | 796 | aa_put_profile(profile); |
722 | return profile; | ||
723 | 797 | ||
724 | error = verify_profile(profile); | 798 | fail: |
725 | if (error) { | 799 | list_for_each_entry_safe(ent, tmp, lh, list) { |
726 | aa_put_profile(profile); | 800 | list_del_init(&ent->list); |
727 | profile = ERR_PTR(error); | 801 | aa_load_ent_free(ent); |
728 | } | 802 | } |
729 | 803 | ||
730 | /* return refcount */ | 804 | return error; |
731 | return profile; | ||
732 | } | 805 | } |
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 6c9390179b89..b125acc9aa26 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c | |||
@@ -37,7 +37,7 @@ int aa_getprocattr(struct aa_profile *profile, char **string) | |||
37 | { | 37 | { |
38 | char *str; | 38 | char *str; |
39 | int len = 0, mode_len = 0, ns_len = 0, name_len; | 39 | int len = 0, mode_len = 0, ns_len = 0, name_len; |
40 | const char *mode_str = profile_mode_names[profile->mode]; | 40 | const char *mode_str = aa_profile_mode_names[profile->mode]; |
41 | const char *ns_name = NULL; | 41 | const char *ns_name = NULL; |
42 | struct aa_namespace *ns = profile->ns; | 42 | struct aa_namespace *ns = profile->ns; |
43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | 43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; |
diff --git a/security/capability.c b/security/capability.c index 32b515766df1..dbeb9bc27b24 100644 --- a/security/capability.c +++ b/security/capability.c | |||
@@ -129,7 +129,7 @@ static void cap_inode_free_security(struct inode *inode) | |||
129 | } | 129 | } |
130 | 130 | ||
131 | static int cap_inode_init_security(struct inode *inode, struct inode *dir, | 131 | static int cap_inode_init_security(struct inode *inode, struct inode *dir, |
132 | const struct qstr *qstr, char **name, | 132 | const struct qstr *qstr, const char **name, |
133 | void **value, size_t *len) | 133 | void **value, size_t *len) |
134 | { | 134 | { |
135 | return -EOPNOTSUPP; | 135 | return -EOPNOTSUPP; |
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index df0fa451a871..af9b6852f4e1 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c | |||
@@ -418,7 +418,7 @@ int evm_inode_init_security(struct inode *inode, | |||
418 | 418 | ||
419 | evm_xattr->value = xattr_data; | 419 | evm_xattr->value = xattr_data; |
420 | evm_xattr->value_len = sizeof(*xattr_data); | 420 | evm_xattr->value_len = sizeof(*xattr_data); |
421 | evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS); | 421 | evm_xattr->name = XATTR_EVM_SUFFIX; |
422 | return 0; | 422 | return 0; |
423 | out: | 423 | out: |
424 | kfree(xattr_data); | 424 | kfree(xattr_data); |
diff --git a/security/security.c b/security/security.c index 94b35aef6871..4dc31f4f2700 100644 --- a/security/security.c +++ b/security/security.c | |||
@@ -348,10 +348,10 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, | |||
348 | if (unlikely(IS_PRIVATE(inode))) | 348 | if (unlikely(IS_PRIVATE(inode))) |
349 | return 0; | 349 | return 0; |
350 | 350 | ||
351 | memset(new_xattrs, 0, sizeof new_xattrs); | ||
352 | if (!initxattrs) | 351 | if (!initxattrs) |
353 | return security_ops->inode_init_security(inode, dir, qstr, | 352 | return security_ops->inode_init_security(inode, dir, qstr, |
354 | NULL, NULL, NULL); | 353 | NULL, NULL, NULL); |
354 | memset(new_xattrs, 0, sizeof(new_xattrs)); | ||
355 | lsm_xattr = new_xattrs; | 355 | lsm_xattr = new_xattrs; |
356 | ret = security_ops->inode_init_security(inode, dir, qstr, | 356 | ret = security_ops->inode_init_security(inode, dir, qstr, |
357 | &lsm_xattr->name, | 357 | &lsm_xattr->name, |
@@ -366,16 +366,14 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, | |||
366 | goto out; | 366 | goto out; |
367 | ret = initxattrs(inode, new_xattrs, fs_data); | 367 | ret = initxattrs(inode, new_xattrs, fs_data); |
368 | out: | 368 | out: |
369 | for (xattr = new_xattrs; xattr->name != NULL; xattr++) { | 369 | for (xattr = new_xattrs; xattr->value != NULL; xattr++) |
370 | kfree(xattr->name); | ||
371 | kfree(xattr->value); | 370 | kfree(xattr->value); |
372 | } | ||
373 | return (ret == -EOPNOTSUPP) ? 0 : ret; | 371 | return (ret == -EOPNOTSUPP) ? 0 : ret; |
374 | } | 372 | } |
375 | EXPORT_SYMBOL(security_inode_init_security); | 373 | EXPORT_SYMBOL(security_inode_init_security); |
376 | 374 | ||
377 | int security_old_inode_init_security(struct inode *inode, struct inode *dir, | 375 | int security_old_inode_init_security(struct inode *inode, struct inode *dir, |
378 | const struct qstr *qstr, char **name, | 376 | const struct qstr *qstr, const char **name, |
379 | void **value, size_t *len) | 377 | void **value, size_t *len) |
380 | { | 378 | { |
381 | if (unlikely(IS_PRIVATE(inode))) | 379 | if (unlikely(IS_PRIVATE(inode))) |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c956390a9136..a5091ec06aa6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -2587,7 +2587,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, | |||
2587 | } | 2587 | } |
2588 | 2588 | ||
2589 | static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | 2589 | static int selinux_inode_init_security(struct inode *inode, struct inode *dir, |
2590 | const struct qstr *qstr, char **name, | 2590 | const struct qstr *qstr, |
2591 | const char **name, | ||
2591 | void **value, size_t *len) | 2592 | void **value, size_t *len) |
2592 | { | 2593 | { |
2593 | const struct task_security_struct *tsec = current_security(); | 2594 | const struct task_security_struct *tsec = current_security(); |
@@ -2595,7 +2596,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
2595 | struct superblock_security_struct *sbsec; | 2596 | struct superblock_security_struct *sbsec; |
2596 | u32 sid, newsid, clen; | 2597 | u32 sid, newsid, clen; |
2597 | int rc; | 2598 | int rc; |
2598 | char *namep = NULL, *context; | 2599 | char *context; |
2599 | 2600 | ||
2600 | dsec = dir->i_security; | 2601 | dsec = dir->i_security; |
2601 | sbsec = dir->i_sb->s_security; | 2602 | sbsec = dir->i_sb->s_security; |
@@ -2631,19 +2632,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
2631 | if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) | 2632 | if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) |
2632 | return -EOPNOTSUPP; | 2633 | return -EOPNOTSUPP; |
2633 | 2634 | ||
2634 | if (name) { | 2635 | if (name) |
2635 | namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_NOFS); | 2636 | *name = XATTR_SELINUX_SUFFIX; |
2636 | if (!namep) | ||
2637 | return -ENOMEM; | ||
2638 | *name = namep; | ||
2639 | } | ||
2640 | 2637 | ||
2641 | if (value && len) { | 2638 | if (value && len) { |
2642 | rc = security_sid_to_context_force(newsid, &context, &clen); | 2639 | rc = security_sid_to_context_force(newsid, &context, &clen); |
2643 | if (rc) { | 2640 | if (rc) |
2644 | kfree(namep); | ||
2645 | return rc; | 2641 | return rc; |
2646 | } | ||
2647 | *value = context; | 2642 | *value = context; |
2648 | *len = clen; | 2643 | *len = clen; |
2649 | } | 2644 | } |
diff --git a/security/smack/smack.h b/security/smack/smack.h index 339614c76e63..076b8e8a51ab 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
@@ -53,6 +53,7 @@ | |||
53 | */ | 53 | */ |
54 | struct smack_known { | 54 | struct smack_known { |
55 | struct list_head list; | 55 | struct list_head list; |
56 | struct hlist_node smk_hashed; | ||
56 | char *smk_known; | 57 | char *smk_known; |
57 | u32 smk_secid; | 58 | u32 smk_secid; |
58 | struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ | 59 | struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ |
@@ -167,9 +168,13 @@ struct smk_port_label { | |||
167 | #define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */ | 168 | #define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */ |
168 | #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ | 169 | #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ |
169 | #define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */ | 170 | #define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */ |
170 | #define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */ | ||
171 | #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ | 171 | #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ |
172 | #define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */ | 172 | /* |
173 | * CIPSO 2.2 standard is 239, but Smack wants to use the | ||
174 | * categories in a structured way that limits the value to | ||
175 | * the bits in 23 bytes, hence the unusual number. | ||
176 | */ | ||
177 | #define SMACK_CIPSO_MAXCATNUM 184 /* 23 * 8 */ | ||
173 | 178 | ||
174 | /* | 179 | /* |
175 | * Flag for transmute access | 180 | * Flag for transmute access |
@@ -222,6 +227,7 @@ char *smk_parse_smack(const char *string, int len); | |||
222 | int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); | 227 | int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); |
223 | char *smk_import(const char *, int); | 228 | char *smk_import(const char *, int); |
224 | struct smack_known *smk_import_entry(const char *, int); | 229 | struct smack_known *smk_import_entry(const char *, int); |
230 | void smk_insert_entry(struct smack_known *skp); | ||
225 | struct smack_known *smk_find_entry(const char *); | 231 | struct smack_known *smk_find_entry(const char *); |
226 | u32 smack_to_secid(const char *); | 232 | u32 smack_to_secid(const char *); |
227 | 233 | ||
@@ -247,6 +253,9 @@ extern struct list_head smk_netlbladdr_list; | |||
247 | 253 | ||
248 | extern struct security_operations smack_ops; | 254 | extern struct security_operations smack_ops; |
249 | 255 | ||
256 | #define SMACK_HASH_SLOTS 16 | ||
257 | extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; | ||
258 | |||
250 | /* | 259 | /* |
251 | * Is the directory transmuting? | 260 | * Is the directory transmuting? |
252 | */ | 261 | */ |
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 6a0377f38620..b3b59b1e93d6 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c | |||
@@ -325,6 +325,25 @@ void smack_log(char *subject_label, char *object_label, int request, | |||
325 | 325 | ||
326 | DEFINE_MUTEX(smack_known_lock); | 326 | DEFINE_MUTEX(smack_known_lock); |
327 | 327 | ||
328 | struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; | ||
329 | |||
330 | /** | ||
331 | * smk_insert_entry - insert a smack label into a hash map, | ||
332 | * | ||
333 | * this function must be called under smack_known_lock | ||
334 | */ | ||
335 | void smk_insert_entry(struct smack_known *skp) | ||
336 | { | ||
337 | unsigned int hash; | ||
338 | struct hlist_head *head; | ||
339 | |||
340 | hash = full_name_hash(skp->smk_known, strlen(skp->smk_known)); | ||
341 | head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)]; | ||
342 | |||
343 | hlist_add_head_rcu(&skp->smk_hashed, head); | ||
344 | list_add_rcu(&skp->list, &smack_known_list); | ||
345 | } | ||
346 | |||
328 | /** | 347 | /** |
329 | * smk_find_entry - find a label on the list, return the list entry | 348 | * smk_find_entry - find a label on the list, return the list entry |
330 | * @string: a text string that might be a Smack label | 349 | * @string: a text string that might be a Smack label |
@@ -334,12 +353,16 @@ DEFINE_MUTEX(smack_known_lock); | |||
334 | */ | 353 | */ |
335 | struct smack_known *smk_find_entry(const char *string) | 354 | struct smack_known *smk_find_entry(const char *string) |
336 | { | 355 | { |
356 | unsigned int hash; | ||
357 | struct hlist_head *head; | ||
337 | struct smack_known *skp; | 358 | struct smack_known *skp; |
338 | 359 | ||
339 | list_for_each_entry_rcu(skp, &smack_known_list, list) { | 360 | hash = full_name_hash(string, strlen(string)); |
361 | head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)]; | ||
362 | |||
363 | hlist_for_each_entry_rcu(skp, head, smk_hashed) | ||
340 | if (strcmp(skp->smk_known, string) == 0) | 364 | if (strcmp(skp->smk_known, string) == 0) |
341 | return skp; | 365 | return skp; |
342 | } | ||
343 | 366 | ||
344 | return NULL; | 367 | return NULL; |
345 | } | 368 | } |
@@ -475,7 +498,7 @@ struct smack_known *smk_import_entry(const char *string, int len) | |||
475 | * Make sure that the entry is actually | 498 | * Make sure that the entry is actually |
476 | * filled before putting it on the list. | 499 | * filled before putting it on the list. |
477 | */ | 500 | */ |
478 | list_add_rcu(&skp->list, &smack_known_list); | 501 | smk_insert_entry(skp); |
479 | goto unlockout; | 502 | goto unlockout; |
480 | } | 503 | } |
481 | /* | 504 | /* |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index eefbd10e408f..8825375cc031 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
@@ -582,7 +582,7 @@ static void smack_inode_free_security(struct inode *inode) | |||
582 | * Returns 0 if it all works out, -ENOMEM if there's no memory | 582 | * Returns 0 if it all works out, -ENOMEM if there's no memory |
583 | */ | 583 | */ |
584 | static int smack_inode_init_security(struct inode *inode, struct inode *dir, | 584 | static int smack_inode_init_security(struct inode *inode, struct inode *dir, |
585 | const struct qstr *qstr, char **name, | 585 | const struct qstr *qstr, const char **name, |
586 | void **value, size_t *len) | 586 | void **value, size_t *len) |
587 | { | 587 | { |
588 | struct inode_smack *issp = inode->i_security; | 588 | struct inode_smack *issp = inode->i_security; |
@@ -591,11 +591,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, | |||
591 | char *dsp = smk_of_inode(dir); | 591 | char *dsp = smk_of_inode(dir); |
592 | int may; | 592 | int may; |
593 | 593 | ||
594 | if (name) { | 594 | if (name) |
595 | *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS); | 595 | *name = XATTR_SMACK_SUFFIX; |
596 | if (*name == NULL) | ||
597 | return -ENOMEM; | ||
598 | } | ||
599 | 596 | ||
600 | if (value) { | 597 | if (value) { |
601 | rcu_read_lock(); | 598 | rcu_read_lock(); |
@@ -3065,6 +3062,8 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, | |||
3065 | { | 3062 | { |
3066 | struct smack_known *skp; | 3063 | struct smack_known *skp; |
3067 | int found = 0; | 3064 | int found = 0; |
3065 | int acat; | ||
3066 | int kcat; | ||
3068 | 3067 | ||
3069 | if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { | 3068 | if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { |
3070 | /* | 3069 | /* |
@@ -3081,12 +3080,28 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, | |||
3081 | list_for_each_entry(skp, &smack_known_list, list) { | 3080 | list_for_each_entry(skp, &smack_known_list, list) { |
3082 | if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) | 3081 | if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) |
3083 | continue; | 3082 | continue; |
3084 | if (memcmp(sap->attr.mls.cat, | 3083 | /* |
3085 | skp->smk_netlabel.attr.mls.cat, | 3084 | * Compare the catsets. Use the netlbl APIs. |
3086 | SMK_CIPSOLEN) != 0) | 3085 | */ |
3087 | continue; | 3086 | if ((sap->flags & NETLBL_SECATTR_MLS_CAT) == 0) { |
3088 | found = 1; | 3087 | if ((skp->smk_netlabel.flags & |
3089 | break; | 3088 | NETLBL_SECATTR_MLS_CAT) == 0) |
3089 | found = 1; | ||
3090 | break; | ||
3091 | } | ||
3092 | for (acat = -1, kcat = -1; acat == kcat; ) { | ||
3093 | acat = netlbl_secattr_catmap_walk( | ||
3094 | sap->attr.mls.cat, acat + 1); | ||
3095 | kcat = netlbl_secattr_catmap_walk( | ||
3096 | skp->smk_netlabel.attr.mls.cat, | ||
3097 | kcat + 1); | ||
3098 | if (acat < 0 || kcat < 0) | ||
3099 | break; | ||
3100 | } | ||
3101 | if (acat == kcat) { | ||
3102 | found = 1; | ||
3103 | break; | ||
3104 | } | ||
3090 | } | 3105 | } |
3091 | rcu_read_unlock(); | 3106 | rcu_read_unlock(); |
3092 | 3107 | ||
@@ -3877,12 +3892,12 @@ static __init void init_smack_known_list(void) | |||
3877 | /* | 3892 | /* |
3878 | * Create the known labels list | 3893 | * Create the known labels list |
3879 | */ | 3894 | */ |
3880 | list_add(&smack_known_huh.list, &smack_known_list); | 3895 | smk_insert_entry(&smack_known_huh); |
3881 | list_add(&smack_known_hat.list, &smack_known_list); | 3896 | smk_insert_entry(&smack_known_hat); |
3882 | list_add(&smack_known_star.list, &smack_known_list); | 3897 | smk_insert_entry(&smack_known_star); |
3883 | list_add(&smack_known_floor.list, &smack_known_list); | 3898 | smk_insert_entry(&smack_known_floor); |
3884 | list_add(&smack_known_invalid.list, &smack_known_list); | 3899 | smk_insert_entry(&smack_known_invalid); |
3885 | list_add(&smack_known_web.list, &smack_known_list); | 3900 | smk_insert_entry(&smack_known_web); |
3886 | } | 3901 | } |
3887 | 3902 | ||
3888 | /** | 3903 | /** |
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index ab167037b2dd..80f4b4a45725 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
@@ -368,56 +368,43 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule, | |||
368 | * @data: string to be parsed, null terminated | 368 | * @data: string to be parsed, null terminated |
369 | * @rule: Will be filled with Smack parsed rule | 369 | * @rule: Will be filled with Smack parsed rule |
370 | * @import: if non-zero, import labels | 370 | * @import: if non-zero, import labels |
371 | * @change: if non-zero, data is from /smack/change-rule | 371 | * @tokens: numer of substrings expected in data |
372 | * | 372 | * |
373 | * Returns 0 on success, -1 on failure | 373 | * Returns number of processed bytes on success, -1 on failure. |
374 | */ | 374 | */ |
375 | static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, | 375 | static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, |
376 | int import, int change) | 376 | int import, int tokens) |
377 | { | 377 | { |
378 | char *subject; | 378 | ssize_t cnt = 0; |
379 | char *object; | 379 | char *tok[4]; |
380 | char *access1; | 380 | int i; |
381 | char *access2; | ||
382 | int datalen; | ||
383 | int rc = -1; | ||
384 | 381 | ||
385 | /* This is inefficient */ | 382 | /* |
386 | datalen = strlen(data); | 383 | * Parsing the rule in-place, filling all white-spaces with '\0' |
384 | */ | ||
385 | for (i = 0; i < tokens; ++i) { | ||
386 | while (isspace(data[cnt])) | ||
387 | data[cnt++] = '\0'; | ||
387 | 388 | ||
388 | /* Our first element can be 64 + \0 with no spaces */ | 389 | if (data[cnt] == '\0') |
389 | subject = kzalloc(datalen + 1, GFP_KERNEL); | 390 | /* Unexpected end of data */ |
390 | if (subject == NULL) | 391 | return -1; |
391 | return -1; | 392 | |
392 | object = kzalloc(datalen, GFP_KERNEL); | 393 | tok[i] = data + cnt; |
393 | if (object == NULL) | 394 | |
394 | goto free_out_s; | 395 | while (data[cnt] && !isspace(data[cnt])) |
395 | access1 = kzalloc(datalen, GFP_KERNEL); | 396 | ++cnt; |
396 | if (access1 == NULL) | ||
397 | goto free_out_o; | ||
398 | access2 = kzalloc(datalen, GFP_KERNEL); | ||
399 | if (access2 == NULL) | ||
400 | goto free_out_a; | ||
401 | |||
402 | if (change) { | ||
403 | if (sscanf(data, "%s %s %s %s", | ||
404 | subject, object, access1, access2) == 4) | ||
405 | rc = smk_fill_rule(subject, object, access1, access2, | ||
406 | rule, import, 0); | ||
407 | } else { | ||
408 | if (sscanf(data, "%s %s %s", subject, object, access1) == 3) | ||
409 | rc = smk_fill_rule(subject, object, access1, NULL, | ||
410 | rule, import, 0); | ||
411 | } | 397 | } |
398 | while (isspace(data[cnt])) | ||
399 | data[cnt++] = '\0'; | ||
412 | 400 | ||
413 | kfree(access2); | 401 | while (i < 4) |
414 | free_out_a: | 402 | tok[i++] = NULL; |
415 | kfree(access1); | 403 | |
416 | free_out_o: | 404 | if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0)) |
417 | kfree(object); | 405 | return -1; |
418 | free_out_s: | 406 | |
419 | kfree(subject); | 407 | return cnt; |
420 | return rc; | ||
421 | } | 408 | } |
422 | 409 | ||
423 | #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ | 410 | #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ |
@@ -447,11 +434,12 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
447 | struct list_head *rule_list, | 434 | struct list_head *rule_list, |
448 | struct mutex *rule_lock, int format) | 435 | struct mutex *rule_lock, int format) |
449 | { | 436 | { |
450 | struct smack_parsed_rule *rule; | 437 | struct smack_parsed_rule rule; |
451 | char *data; | 438 | char *data; |
452 | int datalen; | 439 | int rc; |
453 | int rc = -EINVAL; | 440 | int trunc = 0; |
454 | int load = 0; | 441 | int tokens; |
442 | ssize_t cnt = 0; | ||
455 | 443 | ||
456 | /* | 444 | /* |
457 | * No partial writes. | 445 | * No partial writes. |
@@ -466,11 +454,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
466 | */ | 454 | */ |
467 | if (count != SMK_OLOADLEN && count != SMK_LOADLEN) | 455 | if (count != SMK_OLOADLEN && count != SMK_LOADLEN) |
468 | return -EINVAL; | 456 | return -EINVAL; |
469 | datalen = SMK_LOADLEN; | 457 | } else { |
470 | } else | 458 | if (count >= PAGE_SIZE) { |
471 | datalen = count + 1; | 459 | count = PAGE_SIZE - 1; |
460 | trunc = 1; | ||
461 | } | ||
462 | } | ||
472 | 463 | ||
473 | data = kzalloc(datalen, GFP_KERNEL); | 464 | data = kmalloc(count + 1, GFP_KERNEL); |
474 | if (data == NULL) | 465 | if (data == NULL) |
475 | return -ENOMEM; | 466 | return -ENOMEM; |
476 | 467 | ||
@@ -479,47 +470,49 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
479 | goto out; | 470 | goto out; |
480 | } | 471 | } |
481 | 472 | ||
482 | rule = kzalloc(sizeof(*rule), GFP_KERNEL); | 473 | /* |
483 | if (rule == NULL) { | 474 | * In case of parsing only part of user buf, |
484 | rc = -ENOMEM; | 475 | * avoid having partial rule at the data buffer |
485 | goto out; | 476 | */ |
477 | if (trunc) { | ||
478 | while (count > 0 && (data[count - 1] != '\n')) | ||
479 | --count; | ||
480 | if (count == 0) { | ||
481 | rc = -EINVAL; | ||
482 | goto out; | ||
483 | } | ||
486 | } | 484 | } |
487 | 485 | ||
488 | if (format == SMK_LONG_FMT) { | 486 | data[count] = '\0'; |
489 | /* | 487 | tokens = (format == SMK_CHANGE_FMT ? 4 : 3); |
490 | * Be sure the data string is terminated. | 488 | while (cnt < count) { |
491 | */ | 489 | if (format == SMK_FIXED24_FMT) { |
492 | data[count] = '\0'; | 490 | rc = smk_parse_rule(data, &rule, 1); |
493 | if (smk_parse_long_rule(data, rule, 1, 0)) | 491 | if (rc != 0) { |
494 | goto out_free_rule; | 492 | rc = -EINVAL; |
495 | } else if (format == SMK_CHANGE_FMT) { | 493 | goto out; |
496 | data[count] = '\0'; | 494 | } |
497 | if (smk_parse_long_rule(data, rule, 1, 1)) | 495 | cnt = count; |
498 | goto out_free_rule; | 496 | } else { |
499 | } else { | 497 | rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens); |
500 | /* | 498 | if (rc <= 0) { |
501 | * More on the minor hack for backward compatibility | 499 | rc = -EINVAL; |
502 | */ | 500 | goto out; |
503 | if (count == (SMK_OLOADLEN)) | 501 | } |
504 | data[SMK_OLOADLEN] = '-'; | 502 | cnt += rc; |
505 | if (smk_parse_rule(data, rule, 1)) | 503 | } |
506 | goto out_free_rule; | ||
507 | } | ||
508 | 504 | ||
509 | if (rule_list == NULL) { | 505 | if (rule_list == NULL) |
510 | load = 1; | 506 | rc = smk_set_access(&rule, &rule.smk_subject->smk_rules, |
511 | rule_list = &rule->smk_subject->smk_rules; | 507 | &rule.smk_subject->smk_rules_lock, 1); |
512 | rule_lock = &rule->smk_subject->smk_rules_lock; | 508 | else |
513 | } | 509 | rc = smk_set_access(&rule, rule_list, rule_lock, 0); |
514 | 510 | ||
515 | rc = smk_set_access(rule, rule_list, rule_lock, load); | 511 | if (rc) |
516 | if (rc == 0) { | 512 | goto out; |
517 | rc = count; | ||
518 | goto out; | ||
519 | } | 513 | } |
520 | 514 | ||
521 | out_free_rule: | 515 | rc = cnt; |
522 | kfree(rule); | ||
523 | out: | 516 | out: |
524 | kfree(data); | 517 | kfree(data); |
525 | return rc; | 518 | return rc; |
@@ -901,7 +894,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, | |||
901 | for (i = 0; i < catlen; i++) { | 894 | for (i = 0; i < catlen; i++) { |
902 | rule += SMK_DIGITLEN; | 895 | rule += SMK_DIGITLEN; |
903 | ret = sscanf(rule, "%u", &cat); | 896 | ret = sscanf(rule, "%u", &cat); |
904 | if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL) | 897 | if (ret != 1 || cat > SMACK_CIPSO_MAXCATNUM) |
905 | goto out; | 898 | goto out; |
906 | 899 | ||
907 | smack_catset_bit(cat, mapcatset); | 900 | smack_catset_bit(cat, mapcatset); |
@@ -1840,7 +1833,6 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, | |||
1840 | { | 1833 | { |
1841 | struct smack_parsed_rule rule; | 1834 | struct smack_parsed_rule rule; |
1842 | char *data; | 1835 | char *data; |
1843 | char *cod; | ||
1844 | int res; | 1836 | int res; |
1845 | 1837 | ||
1846 | data = simple_transaction_get(file, buf, count); | 1838 | data = simple_transaction_get(file, buf, count); |
@@ -1853,18 +1845,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, | |||
1853 | res = smk_parse_rule(data, &rule, 0); | 1845 | res = smk_parse_rule(data, &rule, 0); |
1854 | } else { | 1846 | } else { |
1855 | /* | 1847 | /* |
1856 | * Copy the data to make sure the string is terminated. | 1848 | * simple_transaction_get() returns null-terminated data |
1857 | */ | 1849 | */ |
1858 | cod = kzalloc(count + 1, GFP_KERNEL); | 1850 | res = smk_parse_long_rule(data, &rule, 0, 3); |
1859 | if (cod == NULL) | ||
1860 | return -ENOMEM; | ||
1861 | memcpy(cod, data, count); | ||
1862 | cod[count] = '\0'; | ||
1863 | res = smk_parse_long_rule(cod, &rule, 0, 0); | ||
1864 | kfree(cod); | ||
1865 | } | 1851 | } |
1866 | 1852 | ||
1867 | if (res) | 1853 | if (res < 0) |
1868 | return -EINVAL; | 1854 | return -EINVAL; |
1869 | 1855 | ||
1870 | res = smk_access(rule.smk_subject, rule.smk_object, | 1856 | res = smk_access(rule.smk_subject, rule.smk_object, |