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 | |
| 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>
| -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; | ||
