Skip to content

Commit

Permalink
apparmor: add interface files for profiles and namespaces
Browse files Browse the repository at this point in the history
Add basic interface files to access namespace and profile information.
The interface files are created when a profile is loaded and removed
when the profile or namespace is removed.

Signed-off-by: John Johansen <[email protected]>
  • Loading branch information
John Johansen committed Aug 14, 2013
1 parent 0381650 commit 0d259f0
Show file tree
Hide file tree
Showing 7 changed files with 436 additions and 29 deletions.
322 changes: 312 additions & 10 deletions security/apparmor/apparmorfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* License.
*/

#include <linux/ctype.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
Expand All @@ -27,6 +28,45 @@
#include "include/policy.h"
#include "include/resource.h"

/**
* aa_mangle_name - mangle a profile name to std profile layout form
* @name: profile name to mangle (NOT NULL)
* @target: buffer to store mangled name, same length as @name (MAYBE NULL)
*
* Returns: length of mangled name
*/
static int mangle_name(char *name, char *target)
{
char *t = target;

while (*name == '/' || *name == '.')
name++;

if (target) {
for (; *name; name++) {
if (*name == '/')
*(t)++ = '.';
else if (isspace(*name))
*(t)++ = '_';
else if (isalnum(*name) || strchr("._-", *name))
*(t)++ = *name;
}

*t = 0;
} else {
int len = 0;
for (; *name; name++) {
if (isalnum(*name) || isspace(*name) ||
strchr("/._-", *name))
len++;
}

return len;
}

return t - target;
}

/**
* aa_simple_write_to_buffer - common routine for getting policy from user
* @op: operation doing the user buffer copy
Expand Down Expand Up @@ -182,8 +222,263 @@ const struct file_operations aa_fs_seq_file_ops = {
.release = single_release,
};

/** Base file system setup **/
static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
int (*show)(struct seq_file *, void *))
{
struct aa_replacedby *r = aa_get_replacedby(inode->i_private);
int error = single_open(file, show, r);

if (error) {
file->private_data = NULL;
aa_put_replacedby(r);
}

return error;
}

static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq)
aa_put_replacedby(seq->private);
return single_release(inode, file);
}

static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
{
struct aa_replacedby *r = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
seq_printf(seq, "%s\n", profile->base.name);
aa_put_profile(profile);

return 0;
}

static int aa_fs_seq_profname_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show);
}

static const struct file_operations aa_fs_profname_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_profname_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};

static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
{
struct aa_replacedby *r = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile);
seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
aa_put_profile(profile);

return 0;
}

static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show);
}

static const struct file_operations aa_fs_profmode_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_profmode_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};

/** fns to setup dynamic per profile/namespace files **/
void __aa_fs_profile_rmdir(struct aa_profile *profile)
{
struct aa_profile *child;
int i;

if (!profile)
return;

list_for_each_entry(child, &profile->base.profiles, base.list)
__aa_fs_profile_rmdir(child);

for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
struct aa_replacedby *r;
if (!profile->dents[i])
continue;

r = profile->dents[i]->d_inode->i_private;
securityfs_remove(profile->dents[i]);
aa_put_replacedby(r);
profile->dents[i] = NULL;
}
}

void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new)
{
int i;

for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
new->dents[i] = old->dents[i];
old->dents[i] = NULL;
}
}

static struct dentry *create_profile_file(struct dentry *dir, const char *name,
struct aa_profile *profile,
const struct file_operations *fops)
{
struct aa_replacedby *r = aa_get_replacedby(profile->replacedby);
struct dentry *dent;

dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops);
if (IS_ERR(dent))
aa_put_replacedby(r);

return dent;
}

/* requires lock be held */
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
{
struct aa_profile *child;
struct dentry *dent = NULL, *dir;
int error;

if (!parent) {
struct aa_profile *p;
p = aa_deref_parent(profile);
dent = prof_dir(p);
/* adding to parent that previously didn't have children */
dent = securityfs_create_dir("profiles", dent);
if (IS_ERR(dent))
goto fail;
prof_child_dir(p) = parent = dent;
}

if (!profile->dirname) {
int len, id_len;
len = mangle_name(profile->base.name, NULL);
id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);

profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
if (!profile->dirname)
goto fail;

mangle_name(profile->base.name, profile->dirname);
sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
}

dent = securityfs_create_dir(profile->dirname, parent);
if (IS_ERR(dent))
goto fail;
prof_dir(profile) = dir = dent;

dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_NAME] = dent;

dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_MODE] = dent;

list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
if (error)
goto fail2;
}

return 0;

fail:
error = PTR_ERR(dent);

fail2:
__aa_fs_profile_rmdir(profile);

return error;
}

void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
{
struct aa_namespace *sub;
struct aa_profile *child;
int i;

if (!ns)
return;

list_for_each_entry(child, &ns->base.profiles, base.list)
__aa_fs_profile_rmdir(child);

list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
__aa_fs_namespace_rmdir(sub);
mutex_unlock(&sub->lock);
}

for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
securityfs_remove(ns->dents[i]);
ns->dents[i] = NULL;
}
}

int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
const char *name)
{
struct aa_namespace *sub;
struct aa_profile *child;
struct dentry *dent, *dir;
int error;

if (!name)
name = ns->base.name;

dent = securityfs_create_dir(name, parent);
if (IS_ERR(dent))
goto fail;
ns_dir(ns) = dir = dent;

dent = securityfs_create_dir("profiles", dir);
if (IS_ERR(dent))
goto fail;
ns_subprofs_dir(ns) = dent;

dent = securityfs_create_dir("namespaces", dir);
if (IS_ERR(dent))
goto fail;
ns_subns_dir(ns) = dent;

list_for_each_entry(child, &ns->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
if (error)
goto fail2;
}

list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock);
error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL);
mutex_unlock(&sub->lock);
if (error)
goto fail2;
}

return 0;

fail:
error = PTR_ERR(dent);

fail2:
__aa_fs_namespace_rmdir(ns);

return error;
}


/** 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 " \
"link lock"),
Expand Down Expand Up @@ -246,6 +541,7 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
return error;
}

static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
/**
* aafs_create_dir - recursively create a directory entry in the securityfs
* @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
Expand All @@ -256,17 +552,16 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
struct dentry *parent)
{
int error;
struct aa_fs_entry *fs_file;
struct dentry *dir;
int error;

fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent);
if (IS_ERR(fs_dir->dentry)) {
error = PTR_ERR(fs_dir->dentry);
fs_dir->dentry = NULL;
goto failed;
}
dir = securityfs_create_dir(fs_dir->name, parent);
if (IS_ERR(dir))
return PTR_ERR(dir);
fs_dir->dentry = dir;

for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
if (fs_file->v_type == AA_FS_TYPE_DIR)
error = aafs_create_dir(fs_file, fs_dir->dentry);
else
Expand All @@ -278,6 +573,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
return 0;

failed:
aafs_remove_dir(fs_dir);

return error;
}

Expand All @@ -302,7 +599,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
{
struct aa_fs_entry *fs_file;

for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
if (fs_file->v_type == AA_FS_TYPE_DIR)
aafs_remove_dir(fs_file);
else
Expand Down Expand Up @@ -346,6 +643,11 @@ static int __init aa_create_aafs(void)
if (error)
goto error;

error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry,
"policy");
if (error)
goto error;

/* TODO: add support for apparmorfs_null and apparmorfs_mnt */

/* Report that AppArmor fs is enabled */
Expand Down
Loading

0 comments on commit 0d259f0

Please sign in to comment.