diff options
author | John Johansen <john.johansen@canonical.com> | 2013-07-11 00:18:43 -0400 |
---|---|---|
committer | John Johansen <john.johansen@canonical.com> | 2013-08-14 14:42:07 -0400 |
commit | 29b3822f1e132aa0f115f69730d6e4182df153d4 (patch) | |
tree | 343a9a8509d7515c17703da63e20f61721921bdb /security/apparmor | |
parent | 556d0be74b19cb6288e5eb2f3216eac247d87968 (diff) |
apparmor: add the profile introspection file to interface
Add the dynamic namespace relative profiles file to the interace, to allow
introspection of loaded profiles and their modes.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees@ubuntu.com>
Diffstat (limited to 'security/apparmor')
-rw-r--r-- | security/apparmor/apparmorfs.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d6329aa7aa98..7a26608a5666 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
21 | #include <linux/namei.h> | 21 | #include <linux/namei.h> |
22 | #include <linux/capability.h> | 22 | #include <linux/capability.h> |
23 | #include <linux/rcupdate.h> | ||
23 | 24 | ||
24 | #include "include/apparmor.h" | 25 | #include "include/apparmor.h" |
25 | #include "include/apparmorfs.h" | 26 | #include "include/apparmorfs.h" |
@@ -512,6 +513,240 @@ fail2: | |||
512 | } | 513 | } |
513 | 514 | ||
514 | 515 | ||
516 | #define list_entry_next(pos, member) \ | ||
517 | list_entry(pos->member.next, typeof(*pos), member) | ||
518 | #define list_entry_is_head(pos, head, member) (&pos->member == (head)) | ||
519 | |||
520 | /** | ||
521 | * __next_namespace - find the next namespace to list | ||
522 | * @root: root namespace to stop search at (NOT NULL) | ||
523 | * @ns: current ns position (NOT NULL) | ||
524 | * | ||
525 | * Find the next namespace from @ns under @root and handle all locking needed | ||
526 | * while switching current namespace. | ||
527 | * | ||
528 | * Returns: next namespace or NULL if at last namespace under @root | ||
529 | * Requires: ns->parent->lock to be held | ||
530 | * NOTE: will not unlock root->lock | ||
531 | */ | ||
532 | static struct aa_namespace *__next_namespace(struct aa_namespace *root, | ||
533 | struct aa_namespace *ns) | ||
534 | { | ||
535 | struct aa_namespace *parent, *next; | ||
536 | |||
537 | /* is next namespace a child */ | ||
538 | if (!list_empty(&ns->sub_ns)) { | ||
539 | next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); | ||
540 | mutex_lock(&next->lock); | ||
541 | return next; | ||
542 | } | ||
543 | |||
544 | /* check if the next ns is a sibling, parent, gp, .. */ | ||
545 | parent = ns->parent; | ||
546 | while (parent) { | ||
547 | mutex_unlock(&ns->lock); | ||
548 | next = list_entry_next(ns, base.list); | ||
549 | if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { | ||
550 | mutex_lock(&next->lock); | ||
551 | return next; | ||
552 | } | ||
553 | if (parent == root) | ||
554 | return NULL; | ||
555 | ns = parent; | ||
556 | parent = parent->parent; | ||
557 | } | ||
558 | |||
559 | return NULL; | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * __first_profile - find the first profile in a namespace | ||
564 | * @root: namespace that is root of profiles being displayed (NOT NULL) | ||
565 | * @ns: namespace to start in (NOT NULL) | ||
566 | * | ||
567 | * Returns: unrefcounted profile or NULL if no profile | ||
568 | * Requires: profile->ns.lock to be held | ||
569 | */ | ||
570 | static struct aa_profile *__first_profile(struct aa_namespace *root, | ||
571 | struct aa_namespace *ns) | ||
572 | { | ||
573 | for (; ns; ns = __next_namespace(root, ns)) { | ||
574 | if (!list_empty(&ns->base.profiles)) | ||
575 | return list_first_entry(&ns->base.profiles, | ||
576 | struct aa_profile, base.list); | ||
577 | } | ||
578 | return NULL; | ||
579 | } | ||
580 | |||
581 | /** | ||
582 | * __next_profile - step to the next profile in a profile tree | ||
583 | * @profile: current profile in tree (NOT NULL) | ||
584 | * | ||
585 | * Perform a depth first traversal on the profile tree in a namespace | ||
586 | * | ||
587 | * Returns: next profile or NULL if done | ||
588 | * Requires: profile->ns.lock to be held | ||
589 | */ | ||
590 | static struct aa_profile *__next_profile(struct aa_profile *p) | ||
591 | { | ||
592 | struct aa_profile *parent; | ||
593 | struct aa_namespace *ns = p->ns; | ||
594 | |||
595 | /* is next profile a child */ | ||
596 | if (!list_empty(&p->base.profiles)) | ||
597 | return list_first_entry(&p->base.profiles, typeof(*p), | ||
598 | base.list); | ||
599 | |||
600 | /* is next profile a sibling, parent sibling, gp, sibling, .. */ | ||
601 | parent = rcu_dereference_protected(p->parent, | ||
602 | mutex_is_locked(&p->ns->lock)); | ||
603 | while (parent) { | ||
604 | p = list_entry_next(p, base.list); | ||
605 | if (!list_entry_is_head(p, &parent->base.profiles, base.list)) | ||
606 | return p; | ||
607 | p = parent; | ||
608 | parent = rcu_dereference_protected(parent->parent, | ||
609 | mutex_is_locked(&parent->ns->lock)); | ||
610 | } | ||
611 | |||
612 | /* is next another profile in the namespace */ | ||
613 | p = list_entry_next(p, base.list); | ||
614 | if (!list_entry_is_head(p, &ns->base.profiles, base.list)) | ||
615 | return p; | ||
616 | |||
617 | return NULL; | ||
618 | } | ||
619 | |||
620 | /** | ||
621 | * next_profile - step to the next profile in where ever it may be | ||
622 | * @root: root namespace (NOT NULL) | ||
623 | * @profile: current profile (NOT NULL) | ||
624 | * | ||
625 | * Returns: next profile or NULL if there isn't one | ||
626 | */ | ||
627 | static struct aa_profile *next_profile(struct aa_namespace *root, | ||
628 | struct aa_profile *profile) | ||
629 | { | ||
630 | struct aa_profile *next = __next_profile(profile); | ||
631 | if (next) | ||
632 | return next; | ||
633 | |||
634 | /* finished all profiles in namespace move to next namespace */ | ||
635 | return __first_profile(root, __next_namespace(root, profile->ns)); | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * p_start - start a depth first traversal of profile tree | ||
640 | * @f: seq_file to fill | ||
641 | * @pos: current position | ||
642 | * | ||
643 | * Returns: first profile under current namespace or NULL if none found | ||
644 | * | ||
645 | * acquires first ns->lock | ||
646 | */ | ||
647 | static void *p_start(struct seq_file *f, loff_t *pos) | ||
648 | { | ||
649 | struct aa_profile *profile = NULL; | ||
650 | struct aa_namespace *root = aa_current_profile()->ns; | ||
651 | loff_t l = *pos; | ||
652 | f->private = aa_get_namespace(root); | ||
653 | |||
654 | |||
655 | /* find the first profile */ | ||
656 | mutex_lock(&root->lock); | ||
657 | profile = __first_profile(root, root); | ||
658 | |||
659 | /* skip to position */ | ||
660 | for (; profile && l > 0; l--) | ||
661 | profile = next_profile(root, profile); | ||
662 | |||
663 | return profile; | ||
664 | } | ||
665 | |||
666 | /** | ||
667 | * p_next - read the next profile entry | ||
668 | * @f: seq_file to fill | ||
669 | * @p: profile previously returned | ||
670 | * @pos: current position | ||
671 | * | ||
672 | * Returns: next profile after @p or NULL if none | ||
673 | * | ||
674 | * may acquire/release locks in namespace tree as necessary | ||
675 | */ | ||
676 | static void *p_next(struct seq_file *f, void *p, loff_t *pos) | ||
677 | { | ||
678 | struct aa_profile *profile = p; | ||
679 | struct aa_namespace *ns = f->private; | ||
680 | (*pos)++; | ||
681 | |||
682 | return next_profile(ns, profile); | ||
683 | } | ||
684 | |||
685 | /** | ||
686 | * p_stop - stop depth first traversal | ||
687 | * @f: seq_file we are filling | ||
688 | * @p: the last profile writen | ||
689 | * | ||
690 | * Release all locking done by p_start/p_next on namespace tree | ||
691 | */ | ||
692 | static void p_stop(struct seq_file *f, void *p) | ||
693 | { | ||
694 | struct aa_profile *profile = p; | ||
695 | struct aa_namespace *root = f->private, *ns; | ||
696 | |||
697 | if (profile) { | ||
698 | for (ns = profile->ns; ns && ns != root; ns = ns->parent) | ||
699 | mutex_unlock(&ns->lock); | ||
700 | } | ||
701 | mutex_unlock(&root->lock); | ||
702 | aa_put_namespace(root); | ||
703 | } | ||
704 | |||
705 | /** | ||
706 | * seq_show_profile - show a profile entry | ||
707 | * @f: seq_file to file | ||
708 | * @p: current position (profile) (NOT NULL) | ||
709 | * | ||
710 | * Returns: error on failure | ||
711 | */ | ||
712 | static int seq_show_profile(struct seq_file *f, void *p) | ||
713 | { | ||
714 | struct aa_profile *profile = (struct aa_profile *)p; | ||
715 | struct aa_namespace *root = f->private; | ||
716 | |||
717 | if (profile->ns != root) | ||
718 | seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); | ||
719 | seq_printf(f, "%s (%s)\n", profile->base.hname, | ||
720 | aa_profile_mode_names[profile->mode]); | ||
721 | |||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static const struct seq_operations aa_fs_profiles_op = { | ||
726 | .start = p_start, | ||
727 | .next = p_next, | ||
728 | .stop = p_stop, | ||
729 | .show = seq_show_profile, | ||
730 | }; | ||
731 | |||
732 | static int profiles_open(struct inode *inode, struct file *file) | ||
733 | { | ||
734 | return seq_open(file, &aa_fs_profiles_op); | ||
735 | } | ||
736 | |||
737 | static int profiles_release(struct inode *inode, struct file *file) | ||
738 | { | ||
739 | return seq_release(inode, file); | ||
740 | } | ||
741 | |||
742 | static const struct file_operations aa_fs_profiles_fops = { | ||
743 | .open = profiles_open, | ||
744 | .read = seq_read, | ||
745 | .llseek = seq_lseek, | ||
746 | .release = profiles_release, | ||
747 | }; | ||
748 | |||
749 | |||
515 | /** Base file system setup **/ | 750 | /** Base file system setup **/ |
516 | static struct aa_fs_entry aa_fs_entry_file[] = { | 751 | static struct aa_fs_entry aa_fs_entry_file[] = { |
517 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ | 752 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ |
@@ -545,6 +780,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { | |||
545 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), | 780 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), |
546 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), | 781 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), |
547 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), | 782 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), |
783 | AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), | ||
548 | AA_FS_DIR("features", aa_fs_entry_features), | 784 | AA_FS_DIR("features", aa_fs_entry_features), |
549 | { } | 785 | { } |
550 | }; | 786 | }; |