Skip to content

Commit

Permalink
apparmor: add the profile introspection file to interface
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
John Johansen committed Aug 14, 2013
1 parent 556d0be commit 29b3822
Showing 1 changed file with 236 additions and 0 deletions.
236 changes: 236 additions & 0 deletions security/apparmor/apparmorfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/uaccess.h>
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>

#include "include/apparmor.h"
#include "include/apparmorfs.h"
Expand Down Expand Up @@ -512,6 +513,240 @@ int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
}


#define list_entry_next(pos, member) \
list_entry(pos->member.next, typeof(*pos), member)
#define list_entry_is_head(pos, head, member) (&pos->member == (head))

/**
* __next_namespace - find the next namespace to list
* @root: root namespace to stop search at (NOT NULL)
* @ns: current ns position (NOT NULL)
*
* Find the next namespace from @ns under @root and handle all locking needed
* while switching current namespace.
*
* Returns: next namespace or NULL if at last namespace under @root
* Requires: ns->parent->lock to be held
* NOTE: will not unlock root->lock
*/
static struct aa_namespace *__next_namespace(struct aa_namespace *root,
struct aa_namespace *ns)
{
struct aa_namespace *parent, *next;

/* is next namespace a child */
if (!list_empty(&ns->sub_ns)) {
next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
mutex_lock(&next->lock);
return next;
}

/* check if the next ns is a sibling, parent, gp, .. */
parent = ns->parent;
while (parent) {
mutex_unlock(&ns->lock);
next = list_entry_next(ns, base.list);
if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
mutex_lock(&next->lock);
return next;
}
if (parent == root)
return NULL;
ns = parent;
parent = parent->parent;
}

return NULL;
}

/**
* __first_profile - find the first profile in a namespace
* @root: namespace that is root of profiles being displayed (NOT NULL)
* @ns: namespace to start in (NOT NULL)
*
* Returns: unrefcounted profile or NULL if no profile
* Requires: profile->ns.lock to be held
*/
static struct aa_profile *__first_profile(struct aa_namespace *root,
struct aa_namespace *ns)
{
for (; ns; ns = __next_namespace(root, ns)) {
if (!list_empty(&ns->base.profiles))
return list_first_entry(&ns->base.profiles,
struct aa_profile, base.list);
}
return NULL;
}

/**
* __next_profile - step to the next profile in a profile tree
* @profile: current profile in tree (NOT NULL)
*
* Perform a depth first traversal on the profile tree in a namespace
*
* Returns: next profile or NULL if done
* Requires: profile->ns.lock to be held
*/
static struct aa_profile *__next_profile(struct aa_profile *p)
{
struct aa_profile *parent;
struct aa_namespace *ns = p->ns;

/* is next profile a child */
if (!list_empty(&p->base.profiles))
return list_first_entry(&p->base.profiles, typeof(*p),
base.list);

/* is next profile a sibling, parent sibling, gp, sibling, .. */
parent = rcu_dereference_protected(p->parent,
mutex_is_locked(&p->ns->lock));
while (parent) {
p = list_entry_next(p, base.list);
if (!list_entry_is_head(p, &parent->base.profiles, base.list))
return p;
p = parent;
parent = rcu_dereference_protected(parent->parent,
mutex_is_locked(&parent->ns->lock));
}

/* is next another profile in the namespace */
p = list_entry_next(p, base.list);
if (!list_entry_is_head(p, &ns->base.profiles, base.list))
return p;

return NULL;
}

/**
* next_profile - step to the next profile in where ever it may be
* @root: root namespace (NOT NULL)
* @profile: current profile (NOT NULL)
*
* Returns: next profile or NULL if there isn't one
*/
static struct aa_profile *next_profile(struct aa_namespace *root,
struct aa_profile *profile)
{
struct aa_profile *next = __next_profile(profile);
if (next)
return next;

/* finished all profiles in namespace move to next namespace */
return __first_profile(root, __next_namespace(root, profile->ns));
}

/**
* p_start - start a depth first traversal of profile tree
* @f: seq_file to fill
* @pos: current position
*
* Returns: first profile under current namespace or NULL if none found
*
* acquires first ns->lock
*/
static void *p_start(struct seq_file *f, loff_t *pos)
{
struct aa_profile *profile = NULL;
struct aa_namespace *root = aa_current_profile()->ns;
loff_t l = *pos;
f->private = aa_get_namespace(root);


/* find the first profile */
mutex_lock(&root->lock);
profile = __first_profile(root, root);

/* skip to position */
for (; profile && l > 0; l--)
profile = next_profile(root, profile);

return profile;
}

/**
* p_next - read the next profile entry
* @f: seq_file to fill
* @p: profile previously returned
* @pos: current position
*
* Returns: next profile after @p or NULL if none
*
* may acquire/release locks in namespace tree as necessary
*/
static void *p_next(struct seq_file *f, void *p, loff_t *pos)
{
struct aa_profile *profile = p;
struct aa_namespace *ns = f->private;
(*pos)++;

return next_profile(ns, profile);
}

/**
* p_stop - stop depth first traversal
* @f: seq_file we are filling
* @p: the last profile writen
*
* Release all locking done by p_start/p_next on namespace tree
*/
static void p_stop(struct seq_file *f, void *p)
{
struct aa_profile *profile = p;
struct aa_namespace *root = f->private, *ns;

if (profile) {
for (ns = profile->ns; ns && ns != root; ns = ns->parent)
mutex_unlock(&ns->lock);
}
mutex_unlock(&root->lock);
aa_put_namespace(root);
}

/**
* seq_show_profile - show a profile entry
* @f: seq_file to file
* @p: current position (profile) (NOT NULL)
*
* Returns: error on failure
*/
static int seq_show_profile(struct seq_file *f, void *p)
{
struct aa_profile *profile = (struct aa_profile *)p;
struct aa_namespace *root = f->private;

if (profile->ns != root)
seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
seq_printf(f, "%s (%s)\n", profile->base.hname,
aa_profile_mode_names[profile->mode]);

return 0;
}

static const struct seq_operations aa_fs_profiles_op = {
.start = p_start,
.next = p_next,
.stop = p_stop,
.show = seq_show_profile,
};

static int profiles_open(struct inode *inode, struct file *file)
{
return seq_open(file, &aa_fs_profiles_op);
}

static int profiles_release(struct inode *inode, struct file *file)
{
return seq_release(inode, file);
}

static const struct file_operations aa_fs_profiles_fops = {
.open = profiles_open,
.read = seq_read,
.llseek = seq_lseek,
.release = profiles_release,
};


/** Base file system setup **/
static struct aa_fs_entry aa_fs_entry_file[] = {
AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
Expand Down Expand Up @@ -545,6 +780,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = {
AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load),
AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace),
AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove),
AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops),
AA_FS_DIR("features", aa_fs_entry_features),
{ }
};
Expand Down

0 comments on commit 29b3822

Please sign in to comment.