AppArmor: implement static feature reporting interface This adds the ability to query the internal versions of the various policy features of AppArmor.
Signed-off-by: Kees Cook <[email protected]> --- v2: - start using enum/union for display values. v1: - initial patch. --- diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0848292..9f54625 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -18,6 +18,7 @@ #include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/namei.h> +#include <linux/capability.h> #include "include/apparmor.h" #include "include/apparmorfs.h" @@ -146,11 +147,104 @@ static const struct file_operations aa_fs_profile_remove = { static struct dentry *aa_fs_dentry __initdata; -static void __init aafs_remove(const char *name) +enum aa_fs_value { + AA_FS_STRING, + AA_FS_BOOLEAN, + AA_FS_U64, +}; + +struct aa_fs_file { + char *name; + enum aa_fs_value v_type; + union { + char *string; + unsigned long u64; + bool boolean; + } v; + const struct file_operations *file_ops; +}; + +struct aa_fs_dir { + const char *name; + struct dentry *dentry; + struct aa_fs_file *files; +}; + +static int aa_fs_seq_show(struct seq_file *seq, void *v) +{ + struct aa_fs_file *fs_file = seq->private; + + if (!fs_file) + return 0; + + switch (fs_file->v_type) { + case AA_FS_STRING: + seq_printf(seq, "%s\n", fs_file->v.string); + break; + case AA_FS_U64: + seq_printf(seq, "%#08lx\n", fs_file->v.u64); + break; + case AA_FS_BOOLEAN: + seq_printf(seq, "%s\n", fs_file->v.boolean ? + "yes" : "no"); + break; + } + + return 0; +} + +static int aa_fs_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_show, inode->i_private); +} + +static const struct file_operations aa_fs_seq_file_ops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define AA_FS_STRING_FILE(name, value) \ + { name, \ + .v_type = AA_FS_STRING, .v.string = value, \ + &aa_fs_seq_file_ops } +#define AA_FS_BOOLEAN_FILE(name, value) \ + { name, \ + .v_type = AA_FS_BOOLEAN, .v.boolean = value, \ + &aa_fs_seq_file_ops } +#define AA_FS_U64_FILE(name, value) \ + { name, \ + .v_type = AA_FS_U64, .v.u64 = value, \ + &aa_fs_seq_file_ops } +static struct aa_fs_file aa_fs_features_files[] = { + AA_FS_BOOLEAN_FILE("change_hat", 1), + AA_FS_BOOLEAN_FILE("change_hatv", 1), + AA_FS_BOOLEAN_FILE("change_onexec", 1), + AA_FS_BOOLEAN_FILE("change_profile", 1), + AA_FS_BOOLEAN_FILE("namespaces", 1), + AA_FS_BOOLEAN_FILE("network", 0), + AA_FS_STRING_FILE("file", "3.1"), + AA_FS_STRING_FILE("rlimit", "1.1"), + AA_FS_U64_FILE("capability", VFS_CAP_FLAGS_MASK), + { } +}; +#undef AA_FS_STRING_FILE + +static struct aa_fs_dir aa_fs_dirs[] = { + { "features", NULL, aa_fs_features_files }, + { } +}; + +static void __init aafs_remove(const char *name, struct dentry *dir_dentry) { struct dentry *dentry; - dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); + if (!dir_dentry) + return; + + dentry = lookup_one_len(name, dir_dentry, strlen(name)); if (!IS_ERR(dentry)) { securityfs_remove(dentry); dput(dentry); @@ -166,12 +260,14 @@ static void __init aafs_remove(const char *name) * Used aafs_remove to remove entries created with this fn. */ static int __init aafs_create(const char *name, int mask, + struct dentry *dir_dentry, + void *data, const struct file_operations *fops) { struct dentry *dentry; - dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, - NULL, fops); + dentry = securityfs_create_file(name, S_IFREG | mask, dir_dentry, + data, fops); return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; } @@ -184,9 +280,21 @@ static int __init aafs_create(const char *name, int mask, void __init aa_destroy_aafs(void) { if (aa_fs_dentry) { - aafs_remove(".remove"); - aafs_remove(".replace"); - aafs_remove(".load"); + struct aa_fs_dir *fs_dir; + + aafs_remove(".remove", aa_fs_dentry); + aafs_remove(".replace", aa_fs_dentry); + aafs_remove(".load", aa_fs_dentry); + + for (fs_dir = aa_fs_dirs; fs_dir->name; ++fs_dir) { + struct aa_fs_file *fs_file; + + for (fs_file = fs_dir->files; fs_file->name; ++fs_file) + aafs_remove(fs_file->name, fs_dir->dentry); + + aafs_remove(fs_dir->name, aa_fs_dentry); + fs_dir->dentry = NULL; + } securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; @@ -203,6 +311,7 @@ void __init aa_destroy_aafs(void) int __init aa_create_aafs(void) { int error; + struct aa_fs_dir *fs_dir; if (!apparmor_initialized) return 0; @@ -219,16 +328,39 @@ int __init aa_create_aafs(void) goto error; } - error = aafs_create(".load", 0640, &aa_fs_profile_load); + error = aafs_create(".load", 0640, aa_fs_dentry, NULL, + &aa_fs_profile_load); if (error) goto error; - error = aafs_create(".replace", 0640, &aa_fs_profile_replace); + error = aafs_create(".replace", 0640, aa_fs_dentry, NULL, + &aa_fs_profile_replace); if (error) goto error; - error = aafs_create(".remove", 0640, &aa_fs_profile_remove); + error = aafs_create(".remove", 0640, aa_fs_dentry, NULL, + &aa_fs_profile_remove); if (error) goto error; + for (fs_dir = aa_fs_dirs; fs_dir->name; ++fs_dir) { + struct aa_fs_file *fs_file; + + fs_dir->dentry = securityfs_create_dir(fs_dir->name, + aa_fs_dentry); + if (IS_ERR(fs_dir->dentry)) { + error = PTR_ERR(fs_dir->dentry); + fs_dir->dentry = NULL; + goto error; + } + + for (fs_file = fs_dir->files; fs_file->name; ++fs_file) { + error = aafs_create(fs_file->name, 0444, + fs_dir->dentry, fs_file, + fs_file->file_ops); + if (error) + goto error; + } + } + /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ /* Report that AppArmor fs is enabled */ -- Kees Cook -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
