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]> --- security/apparmor/apparmorfs.c | 210 +++++++++++++++++++++++++++++--- security/apparmor/include/apparmorfs.h | 10 ++ security/apparmor/include/policy.h | 10 ++ security/apparmor/policy.c | 30 ++++- 4 files changed, 244 insertions(+), 16 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec..cdcdb6d 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -182,8 +182,179 @@ const struct file_operations aa_fs_seq_file_ops = { .release = single_release, }; -/** Base file system setup **/ +static int aa_fs_seq_string_show(struct seq_file *seq, void *v) +{ + char *string = seq->private; + + if (string) + seq_printf(seq, "%s", string); + + return 0; +} + +static int aa_fs_seq_string_open(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_string_show, inode->i_private); +} + +const struct file_operations aa_fs_seq_string_fops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_string_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int aa_fs_seq_mode_show(struct seq_file *seq, void *v) +{ + enum profile_mode *mode = seq->private; + + switch (*mode) { + case APPARMOR_ENFORCE: + seq_printf(seq, "enforce"); + break; + case APPARMOR_COMPLAIN: + seq_printf(seq, "complain"); + break; + case APPARMOR_KILL: + seq_printf(seq, "kill"); + break; + } + + return 0; +} + +static int aa_fs_seq_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_mode_show, inode->i_private); +} + +const struct file_operations aa_fs_seq_mode_fops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_mode_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** fns to setup dynamic per profile/namespace files **/ +void __aa_fs_profile_rmdir(struct aa_profile *profile) +{ + struct aa_profile *child; + int i; + + list_for_each_entry(child, &profile->base.profiles, base.list) + __aa_fs_profile_rmdir(child); + + for (i = ARRAY_LEN(profile->dents) - 1; i >= 0; --i) + securityfs_remove(profile->dents[i]); +} + +int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) +{ + struct aa_profile *child; + struct dentry *dir; + int error, i = 0; + + profile->dents[i++] = securityfs_create_dir(profile->sidstr, parent); + if (IS_ERR(profile->dents[i - 1])) + goto fail; + dir = profile->dents[0]; + + profile->dents[i++] = securityfs_create_dir("profiles", dir); + if (IS_ERR(profile->dents[i - 1])) + goto fail; + profile->dents[i++] = securityfs_create_file("name", S_IFREG | 0444, + dir, profile->base.name, + &aa_fs_seq_string_fops); + if (IS_ERR(profile->dents[i - 1])) + goto fail; + profile->dents[i++] = securityfs_create_file("mode", S_IFREG | 0444, + dir, &profile->mode, + &aa_fs_seq_mode_fops); + if (IS_ERR(profile->dents[i - 1])) + goto fail; + + list_for_each_entry(child, &profile->base.profiles, base.list) { + error = __aa_fs_profile_mkdir(child, profile->dents[1]); + if (error) + goto fail2; + } + + return 0; + +fail: + error = PTR_ERR(profile->dents[i - 1]); + profile->dents[i - 1] = NULL; + +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; + + 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) + __aa_fs_namespace_rmdir(sub); + + for (i = ARRAY_LEN(ns->dents) - 1; i >= 0 ; --i) + securityfs_remove(ns->dents[i]); +} + +int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent) +{ + struct aa_namespace *sub; + struct aa_profile *child; + struct dentry *dir; + int error, i = 0; + + ns->dents[i++] = securityfs_create_dir(ns->base.name, parent); + if (IS_ERR(ns->dents[i - 1])) + goto fail; + dir = ns->dents[0]; + + ns->dents[i++] = securityfs_create_dir("profiles", dir); + if (IS_ERR(ns->dents[i - 1])) + goto fail; + ns->dents[i++] = securityfs_create_dir("namespaces", dir); + if (IS_ERR(ns->dents[i - 1])) + goto fail; + + list_for_each_entry(child, &ns->base.profiles, base.list) { + error = __aa_fs_profile_mkdir(child, ns->dents[1]); + if (error) + goto fail2; + } + + list_for_each_entry(sub, &ns->sub_ns, base.list) { + error = __aa_fs_namespace_mkdir(sub, ns->dents[2]); + if (error) + goto fail2; + } + + return 0; + +fail: + error = PTR_ERR(ns->dents[i - 1]); + ns->dents[i - 1] = NULL; + +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"), @@ -207,6 +378,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { }; static struct aa_fs_entry aa_fs_entry_apparmor[] = { + AA_FS_DIR("policy", NULL), /* don't move see aacreate_aafs */ 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), @@ -214,8 +386,10 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { { } }; -static struct aa_fs_entry aa_fs_entry = - AA_FS_DIR("apparmor", aa_fs_entry_apparmor); +static struct aa_fs_entry aa_fs_entry[] = { + AA_FS_DIR("apparmor", aa_fs_entry_apparmor), + { } +}; /** * aafs_create_file - create a file entry in the apparmor securityfs @@ -240,6 +414,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) @@ -250,17 +425,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 @@ -272,6 +446,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, return 0; failed: + aafs_remove_dir(fs_dir); + return error; } @@ -296,7 +472,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 @@ -313,7 +489,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir) */ void __init aa_destroy_aafs(void) { - aafs_remove_dir(&aa_fs_entry); + aafs_remove_dir(aa_fs_entry); } /** @@ -330,13 +506,17 @@ static int __init aa_create_aafs(void) if (!apparmor_initialized) return 0; - if (aa_fs_entry.dentry) { + if (aa_fs_entry[0].dentry) { AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); return -EEXIST; } /* Populate fs tree. */ - error = aafs_create_dir(&aa_fs_entry, NULL); + error = aafs_create_dir(aa_fs_entry, NULL); + if (error) + goto error; + + error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry_apparmor[0].dentry); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 7ea4769..d55954d 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -15,6 +15,8 @@ #ifndef __AA_APPARMORFS_H #define __AA_APPARMORFS_H +#define ARRAY_LEN(ARRAY) (sizeof(ARRAY)/sizeof(*(ARRAY))) + enum aa_fs_type { AA_FS_TYPE_BOOLEAN, AA_FS_TYPE_STRING, @@ -61,4 +63,12 @@ extern const struct file_operations aa_fs_seq_file_ops; extern void __init aa_destroy_aafs(void); +struct aa_profile; +struct aa_namespace; + +void __aa_fs_profile_rmdir(struct aa_profile *profile); +int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); +void __aa_fs_namespace_rmdir(struct aa_namespace *ns); +int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent); + #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569..1cdc73b 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -105,6 +105,7 @@ struct aa_ns_acct { * @acct: accounting for the namespace * @unconfined: special unconfined profile for the namespace * @sub_ns: list of namespaces under the current namespace. + * @dents: dentries for the namespaces file entries in apparmorfs * * An aa_namespace defines the set profiles that are searched to determine * which profile to attach to a task. Profiles can not be shared between @@ -127,6 +128,8 @@ struct aa_namespace { struct aa_ns_acct acct; struct aa_profile *unconfined; struct list_head sub_ns; + + struct dentry *dents[3]; }; /* struct aa_policydb - match engine for a policy @@ -159,6 +162,9 @@ struct aa_policydb { * @caps: capabilities for the profile * @rlimits: rlimits for the profile * + * @dents: dentries for the profiles file entries in apparmorfs + * @sidstr: hex string representation of the sid + * * The AppArmor profile contains the basic confinement data. Each profile * has a name, and exists in a namespace. The @name and @exec_match are * used to determine profile attachment against unconfined tasks. All other @@ -195,8 +201,12 @@ struct aa_profile { struct aa_file_rules file; struct aa_caps caps; struct aa_rlimit rlimits; + + struct dentry *dents[4]; + char sidstr[9]; }; + extern struct aa_namespace *root_ns; extern enum profile_mode aa_g_profile_mode; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index cf5fd22..1e53048 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -293,6 +293,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix, goto fail_unconfined; ns->unconfined->sid = aa_alloc_sid(); + sprintf(ns->unconfined->sidstr, "%x", ns->unconfined->sid); ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | PFLAG_IMMUTABLE; @@ -421,6 +422,13 @@ static struct aa_namespace *aa_prepare_namespace(const char *name) list_add(&new_ns->base.list, &root->sub_ns); /* add list ref */ ns = aa_get_namespace(new_ns); + + /* Fixme: deal with failure */ + if (__aa_fs_namespace_mkdir(new_ns, + new_ns->parent->dents[1])) + AA_ERROR("Added namespace %s but failed to " + "add interface files.\n", + new_ns->base.name); } else { /* raced so free the new one */ free_namespace(new_ns); @@ -498,6 +506,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new) new->parent = aa_get_profile(old->parent); new->ns = aa_get_namespace(old->ns); new->sid = old->sid; + strcpy(new->sidstr, old->sidstr); __list_add_profile(&policy->profiles, new); /* inherit children */ list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { @@ -691,6 +700,7 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) goto fail; profile->sid = sid; + sprintf(profile->sidstr, "%x", sid); profile->mode = APPARMOR_COMPLAIN; profile->flags = PFLAG_NULL; if (hat) @@ -951,6 +961,7 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, __list_add_profile(&policy->profiles, profile); /* released on free_profile */ profile->sid = aa_alloc_sid(); + sprintf(profile->sidstr, "%x", profile->sid); profile->ns = aa_get_namespace(ns); } @@ -1086,8 +1097,10 @@ audit: error = audit_policy(op, GFP_ATOMIC, name, info, error); if (!error) { - if (rename_profile) + if (rename_profile) { __replace_profile(rename_profile, new_profile); + __aa_fs_profile_rmdir(rename_profile); + } if (old_profile) { /* when there are both rename and old profiles * inherit old profiles sid @@ -1095,9 +1108,22 @@ audit: if (rename_profile) aa_free_sid(new_profile->sid); __replace_profile(old_profile, new_profile); + __aa_fs_profile_rmdir(old_profile); } if (!(old_profile || rename_profile)) __add_new_profile(ns, policy, new_profile); + + /* FIXME: currently can fail, but old policy is replaced with + * new. We do not want to fail the replacement at this + * point. Problem can't create files & dirs that already + * exist so maybe transfer them? + */ + if (__aa_fs_profile_mkdir(new_profile, + new_profile->parent ? + new_profile->parent->dents[1] : + new_profile->ns->dents[1])) + AA_ERROR("added profile %s but failed to add " + "interface files.\n", new_profile->base.name); } write_unlock(&ns->lock); @@ -1162,6 +1188,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) /* remove namespace - can only happen if fqname[0] == ':' */ write_lock(&ns->parent->lock); __remove_namespace(ns); + __aa_fs_namespace_rmdir(ns); write_unlock(&ns->parent->lock); } else { /* remove profile */ @@ -1174,6 +1201,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) } name = profile->base.hname; __remove_profile(profile); + __aa_fs_profile_rmdir(profile); write_unlock(&ns->lock); } -- 1.7.9.5 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
