apparmor: add custom apparmorfs that will be used by policy namespace files

AppArmor policy needs to be able to be resolved based on the policy
namespace a task is confined by. Add a base apparmorfs filesystem that
(like nsfs) will exist as a kern mount and be accessed via jump_link
through a securityfs file.

Setup the base apparmorfs fns and data, but don't use it yet.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Reviewed-by: Seth Arnold <seth.arnold@canonical.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
John Johansen 2017-05-25 05:52:56 -07:00
parent 64c8697045
commit a481f4d917
2 changed files with 338 additions and 17 deletions

View file

@ -80,6 +80,8 @@
#define BTRFS_TEST_MAGIC 0x73727279 #define BTRFS_TEST_MAGIC 0x73727279
#define NSFS_MAGIC 0x6e736673 #define NSFS_MAGIC 0x6e736673
#define BPF_FS_MAGIC 0xcafe4a11 #define BPF_FS_MAGIC 0xcafe4a11
#define AAFS_MAGIC 0x5a3c69f0
/* Since UDF 2.01 is ISO 13346 based... */ /* Since UDF 2.01 is ISO 13346 based... */
#define UDF_SUPER_MAGIC 0x15013346 #define UDF_SUPER_MAGIC 0x15013346
#define BALLOON_KVM_MAGIC 0x13661366 #define BALLOON_KVM_MAGIC 0x13661366

View file

@ -22,8 +22,9 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <uapi/linux/major.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/apparmorfs.h" #include "include/apparmorfs.h"
@ -74,6 +75,265 @@ static int mangle_name(const char *name, char *target)
return t - target; return t - target;
} }
/*
* aafs - core fns and data for the policy tree
*/
#define AAFS_NAME "apparmorfs"
static struct vfsmount *aafs_mnt;
static int aafs_count;
static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
seq_printf(seq, "%s:[%lu]", AAFS_NAME, inode->i_ino);
return 0;
}
static void aafs_evict_inode(struct inode *inode)
{
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
}
static const struct super_operations aafs_super_ops = {
.statfs = simple_statfs,
.evict_inode = aafs_evict_inode,
.show_path = aafs_show_path,
};
static int fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr files[] = { {""} };
int error;
error = simple_fill_super(sb, AAFS_MAGIC, files);
if (error)
return error;
sb->s_op = &aafs_super_ops;
return 0;
}
static struct dentry *aafs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_single(fs_type, flags, data, fill_super);
}
static struct file_system_type aafs_ops = {
.owner = THIS_MODULE,
.name = AAFS_NAME,
.mount = aafs_mount,
.kill_sb = kill_anon_super,
};
/**
* __aafs_setup_d_inode - basic inode setup for apparmorfs
* @dir: parent directory for the dentry
* @dentry: dentry we are seting the inode up for
* @mode: permissions the file should have
* @data: data to store on inode.i_private, available in open()
* @link: if symlink, symlink target string
* @fops: struct file_operations that should be used
* @iops: struct of inode_operations that should be used
*/
static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry,
umode_t mode, void *data, char *link,
const struct file_operations *fops,
const struct inode_operations *iops)
{
struct inode *inode = new_inode(dir->i_sb);
AA_BUG(!dir);
AA_BUG(!dentry);
if (!inode)
return -ENOMEM;
inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_private = data;
if (S_ISDIR(mode)) {
inode->i_op = iops ? iops : &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inc_nlink(inode);
inc_nlink(dir);
} else if (S_ISLNK(mode)) {
inode->i_op = iops ? iops : &simple_symlink_inode_operations;
inode->i_link = link;
} else {
inode->i_fop = fops;
}
d_instantiate(dentry, inode);
dget(dentry);
return 0;
}
/**
* aafs_create - create a dentry in the apparmorfs filesystem
*
* @name: name of dentry to create
* @mode: permissions the file should have
* @parent: parent directory for this dentry
* @data: data to store on inode.i_private, available in open()
* @link: if symlink, symlink target string
* @fops: struct file_operations that should be used for
* @iops: struct of inode_operations that should be used
*
* This is the basic "create a xxx" function for apparmorfs.
*
* Returns a pointer to a dentry if it succeeds, that must be free with
* aafs_remove(). Will return ERR_PTR on failure.
*/
static struct dentry *aafs_create(const char *name, umode_t mode,
struct dentry *parent, void *data, void *link,
const struct file_operations *fops,
const struct inode_operations *iops)
{
struct dentry *dentry;
struct inode *dir;
int error;
AA_BUG(!name);
AA_BUG(!parent);
if (!(mode & S_IFMT))
mode = (mode & S_IALLUGO) | S_IFREG;
error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count);
if (error)
return ERR_PTR(error);
dir = d_inode(parent);
inode_lock(dir);
dentry = lookup_one_len(name, parent, strlen(name));
if (IS_ERR(dentry))
goto fail_lock;
if (d_really_is_positive(dentry)) {
error = -EEXIST;
goto fail_dentry;
}
error = __aafs_setup_d_inode(dir, dentry, mode, data, link, fops, iops);
if (error)
goto fail_dentry;
inode_unlock(dir);
return dentry;
fail_dentry:
dput(dentry);
fail_lock:
inode_unlock(dir);
simple_release_fs(&aafs_mnt, &aafs_count);
return ERR_PTR(error);
}
/**
* aafs_create_file - create a file in the apparmorfs filesystem
*
* @name: name of dentry to create
* @mode: permissions the file should have
* @parent: parent directory for this dentry
* @data: data to store on inode.i_private, available in open()
* @fops: struct file_operations that should be used for
*
* see aafs_create
*/
static struct dentry *aafs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops)
{
return aafs_create(name, mode, parent, data, NULL, fops, NULL);
}
/**
* aafs_create_dir - create a directory in the apparmorfs filesystem
*
* @name: name of dentry to create
* @parent: parent directory for this dentry
*
* see aafs_create
*/
static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
{
return aafs_create(name, S_IFDIR | 0755, parent, NULL, NULL, NULL,
NULL);
}
/**
* aafs_create_symlink - create a symlink in the apparmorfs filesystem
* @name: name of dentry to create
* @parent: parent directory for this dentry
* @target: if symlink, symlink target string
* @iops: struct of inode_operations that should be used
*
* If @target parameter is %NULL, then the @iops parameter needs to be
* setup to handle .readlink and .get_link inode_operations.
*/
static struct dentry *aafs_create_symlink(const char *name,
struct dentry *parent,
const char *target,
const struct inode_operations *iops)
{
struct dentry *dent;
char *link = NULL;
if (target) {
link = kstrdup(target, GFP_KERNEL);
if (!link)
return ERR_PTR(-ENOMEM);
}
dent = aafs_create(name, S_IFLNK | 0444, parent, NULL, link, NULL,
iops);
if (IS_ERR(dent))
kfree(link);
return dent;
}
/**
* aafs_remove - removes a file or directory from the apparmorfs filesystem
*
* @dentry: dentry of the file/directory/symlink to removed.
*/
static void aafs_remove(struct dentry *dentry)
{
struct inode *dir;
if (!dentry || IS_ERR(dentry))
return;
dir = d_inode(dentry->d_parent);
inode_lock(dir);
if (simple_positive(dentry)) {
if (d_is_dir(dentry))
simple_rmdir(dir, dentry);
else
simple_unlink(dir, dentry);
dput(dentry);
}
inode_unlock(dir);
simple_release_fs(&aafs_mnt, &aafs_count);
}
/*
* aa_fs - policy load/replace/remove
*/
/** /**
* aa_simple_write_to_buffer - common routine for getting policy from user * aa_simple_write_to_buffer - common routine for getting policy from user
* @userbuf: user buffer to copy data from (NOT NULL) * @userbuf: user buffer to copy data from (NOT NULL)
@ -1369,14 +1629,14 @@ static struct aa_fs_entry aa_fs_entry =
AA_FS_DIR("apparmor", aa_fs_entry_apparmor); AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
/** /**
* aafs_create_file - create a file entry in the apparmor securityfs * entry_create_file - create a file entry in the apparmor securityfs
* @fs_file: aa_fs_entry to build an entry for (NOT NULL) * @fs_file: aa_fs_entry to build an entry for (NOT NULL)
* @parent: the parent dentry in the securityfs * @parent: the parent dentry in the securityfs
* *
* Use aafs_remove_file to remove entries created with this fn. * Use entry_remove_file to remove entries created with this fn.
*/ */
static int __init aafs_create_file(struct aa_fs_entry *fs_file, static int __init entry_create_file(struct aa_fs_entry *fs_file,
struct dentry *parent) struct dentry *parent)
{ {
int error = 0; int error = 0;
@ -1391,15 +1651,15 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
return error; return error;
} }
static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir); static void __init entry_remove_dir(struct aa_fs_entry *fs_dir);
/** /**
* aafs_create_dir - recursively create a directory entry in the securityfs * entry_create_dir - recursively create a directory entry in the securityfs
* @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
* @parent: the parent dentry in the securityfs * @parent: the parent dentry in the securityfs
* *
* Use aafs_remove_dir to remove entries created with this fn. * Use entry_remove_dir to remove entries created with this fn.
*/ */
static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, static int __init entry_create_dir(struct aa_fs_entry *fs_dir,
struct dentry *parent) struct dentry *parent)
{ {
struct aa_fs_entry *fs_file; struct aa_fs_entry *fs_file;
@ -1413,9 +1673,9 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
for (fs_file = fs_dir->v.files; fs_file && 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) if (fs_file->v_type == AA_FS_TYPE_DIR)
error = aafs_create_dir(fs_file, fs_dir->dentry); error = entry_create_dir(fs_file, fs_dir->dentry);
else else
error = aafs_create_file(fs_file, fs_dir->dentry); error = entry_create_file(fs_file, fs_dir->dentry);
if (error) if (error)
goto failed; goto failed;
} }
@ -1423,7 +1683,7 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
return 0; return 0;
failed: failed:
aafs_remove_dir(fs_dir); entry_remove_dir(fs_dir);
return error; return error;
} }
@ -1442,16 +1702,16 @@ static void __init aafs_remove_file(struct aa_fs_entry *fs_file)
} }
/** /**
* aafs_remove_dir - recursively drop a directory entry from the securityfs * entry_remove_dir - recursively drop a directory entry from the securityfs
* @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL) * @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL)
*/ */
static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir) static void __init entry_remove_dir(struct aa_fs_entry *fs_dir)
{ {
struct aa_fs_entry *fs_file; struct aa_fs_entry *fs_file;
for (fs_file = fs_dir->v.files; fs_file && 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) if (fs_file->v_type == AA_FS_TYPE_DIR)
aafs_remove_dir(fs_file); entry_remove_dir(fs_file);
else else
aafs_remove_file(fs_file); aafs_remove_file(fs_file);
} }
@ -1466,7 +1726,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
*/ */
void __init aa_destroy_aafs(void) void __init aa_destroy_aafs(void)
{ {
aafs_remove_dir(&aa_fs_entry); entry_remove_dir(&aa_fs_entry);
} }
@ -1515,6 +1775,59 @@ static int aa_mk_null_file(struct dentry *parent)
return error; return error;
} }
static const char *policy_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
{
struct aa_ns *ns;
struct path path;
if (!dentry)
return ERR_PTR(-ECHILD);
ns = aa_get_current_ns();
path.mnt = mntget(aafs_mnt);
path.dentry = dget(ns_dir(ns));
nd_jump_link(&path);
aa_put_ns(ns);
return NULL;
}
static int ns_get_name(char *buf, size_t size, struct aa_ns *ns,
struct inode *inode)
{
int res = snprintf(buf, size, "%s:[%lu]", AAFS_NAME, inode->i_ino);
if (res < 0 || res >= size)
res = -ENOENT;
return res;
}
static int policy_readlink(struct dentry *dentry, char __user *buffer,
int buflen)
{
struct aa_ns *ns;
char name[32];
int res;
ns = aa_get_current_ns();
res = ns_get_name(name, sizeof(name), ns, d_inode(dentry));
if (res >= 0)
res = readlink_copy(buffer, buflen, name);
aa_put_ns(ns);
return res;
}
static const struct inode_operations policy_link_iops = {
.readlink = policy_readlink,
.get_link = policy_get_link,
};
/** /**
* aa_create_aafs - create the apparmor security filesystem * aa_create_aafs - create the apparmor security filesystem
* *
@ -1535,8 +1848,14 @@ static int __init aa_create_aafs(void)
return -EEXIST; return -EEXIST;
} }
/* setup apparmorfs used to virtualize policy/ */
aafs_mnt = kern_mount(&aafs_ops);
if (IS_ERR(aafs_mnt))
panic("can't set apparmorfs up\n");
aafs_mnt->mnt_sb->s_flags &= ~MS_NOUSER;
/* Populate fs tree. */ /* Populate fs tree. */
error = aafs_create_dir(&aa_fs_entry, NULL); error = entry_create_dir(&aa_fs_entry, NULL);
if (error) if (error)
goto error; goto error;