[apparmor] [PATCH 27/36] apparmor: treat each task as if the label can have mutiple entries
John Johansen
john.johansen at canonical.com
Wed May 1 21:31:12 UTC 2013
next baby step to labels. Update most code to walk labels as if there
is multiple entries in a label, even though atm there can only be
one.
This does not update the domain transitions, exec, change_hat, change_profile
(separate patch).
Also it bails on first error, where for learning purposes it might be
desireable to check permission, and log against all profiles before failing.
Signed-off-by: John Johansen <john.johansen at canonical.com>
---
security/apparmor/file.c | 167 ++++++++++++++++++++++++---------------
security/apparmor/include/file.h | 7 +-
security/apparmor/ipc.c | 22 ++++--
security/apparmor/lsm.c | 48 +++++++----
security/apparmor/resource.c | 62 +++++++++------
5 files changed, 193 insertions(+), 113 deletions(-)
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index cd21ec5..15847fb 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -267,7 +267,7 @@ static inline bool is_deleted(struct dentry *dentry)
/**
* aa_path_perm - do permissions check & audit for @path
* @op: operation being checked
- * @profile: profile being enforced (NOT NULL)
+ * @label: profile being enforced (NOT NULL)
* @path: path to check permissions of (NOT NULL)
* @flags: any additional path flags beyond what the profile specifies
* @request: requested permissions
@@ -275,15 +275,17 @@ static inline bool is_deleted(struct dentry *dentry)
*
* Returns: %0 else error if access denied or other error
*/
-int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
+int aa_path_perm(int op, struct aa_label *label, struct path *path,
int flags, u32 request, struct path_cond *cond)
{
char *buffer = NULL;
struct file_perms perms = {};
const char *name, *info = NULL;
- int error;
+ struct aa_profile *profile;
+ int i, error;
- flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
+ /* TODO: fix path lookup flags */
+ flags |= labels_profile(label)->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
error = aa_path_name(path, flags, &buffer, &name, &info);
if (error) {
if (error == -ENOENT && is_deleted(path->dentry)) {
@@ -294,14 +296,29 @@ int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
info = NULL;
perms.allow = request;
}
- } else {
+
+ label_for_each_confined(i, label, profile) {
+ error = aa_audit_file(profile, &perms, GFP_KERNEL,
+ op, request, name, NULL,
+ cond->uid, info, error);
+ if (error)
+ break;
+ }
+ goto out;
+ }
+
+ label_for_each_confined(i, label, profile) {
aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
&perms);
if (request & ~perms.allow)
error = -EACCES;
+ error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
+ name, NULL, cond->uid, info, error);
+ if (error)
+ break;
}
- error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name,
- NULL, cond->uid, info, error);
+
+out:
kfree(buffer);
return error;
@@ -329,7 +346,7 @@ static inline bool xindex_is_subset(u32 link, u32 target)
/**
* aa_path_link - Handle hard link permission check
- * @profile: the profile being enforced (NOT NULL)
+ * @label: the label being enforced (NOT NULL)
* @old_dentry: the target dentry (NOT NULL)
* @new_dir: directory the new link will be created in (NOT NULL)
* @new_dentry: the link being created (NOT NULL)
@@ -345,7 +362,7 @@ static inline bool xindex_is_subset(u32 link, u32 target)
*
* Returns: %0 if allowed else error
*/
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry)
{
struct path link = { new_dir->mnt, new_dentry };
@@ -359,93 +376,115 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
struct file_perms lperms, perms;
u32 request = AA_MAY_LINK;
unsigned int state;
- int error;
+ struct aa_profile *profile;
+ int i, error;
lperms = nullperms;
+ /* TODO: fix path lookup flags, auditing of failed path for profile */
+ profile = labels_profile(label);
/* buffer freed below, lname is pointer in buffer */
- error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
+ error = aa_path_name(&link, labels_profile(label)->path_flags, &buffer, &lname,
&info);
if (error)
- goto audit;
+ goto err;
/* buffer2 freed below, tname is pointer in buffer2 */
- error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
+ error = aa_path_name(&target, labels_profile(label)->path_flags, &buffer2, &tname,
&info);
if (error)
- goto audit;
+ goto err;
- error = -EACCES;
- /* aa_str_perms - handles the case of the dfa being NULL */
- state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
- &cond, &lperms);
- if (!(lperms.allow & AA_MAY_LINK))
- goto audit;
+ label_for_each_confined(i, label, profile) {
+ error = -EACCES;
+ /* aa_str_perms - handles the case of the dfa being NULL */
+ state = aa_str_perms(profile->file.dfa, profile->file.start,
+ lname, &cond, &lperms);
- /* test to see if target can be paired with link */
- state = aa_dfa_null_transition(profile->file.dfa, state);
- aa_str_perms(profile->file.dfa, state, tname, &cond, &perms);
+ if (!(lperms.allow & AA_MAY_LINK))
+ goto audit;
- /* force audit/quiet masks for link are stored in the second entry
- * in the link pair.
- */
- lperms.audit = perms.audit;
- lperms.quiet = perms.quiet;
- lperms.kill = perms.kill;
+ /* test to see if target can be paired with link */
+ state = aa_dfa_null_transition(profile->file.dfa, state);
+ aa_str_perms(profile->file.dfa, state, tname, &cond, &perms);
- if (!(perms.allow & AA_MAY_LINK)) {
- info = "target restricted";
- goto audit;
- }
+ /* force audit/quiet masks for link are stored in the second
+ * entry in the link pair.
+ */
+ lperms.audit = perms.audit;
+ lperms.quiet = perms.quiet;
+ lperms.kill = perms.kill;
- /* done if link subset test is not required */
- if (!(perms.allow & AA_LINK_SUBSET))
- goto done_tests;
+ if (!(perms.allow & AA_MAY_LINK)) {
+ info = "target restricted";
+ goto audit;
+ }
- /* Do link perm subset test requiring allowed permission on link are a
- * subset of the allowed permissions on target.
- */
- aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond,
- &perms);
-
- /* AA_MAY_LINK is not considered in the subset test */
- request = lperms.allow & ~AA_MAY_LINK;
- lperms.allow &= perms.allow | AA_MAY_LINK;
-
- request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow);
- if (request & ~lperms.allow) {
- goto audit;
- } else if ((lperms.allow & MAY_EXEC) &&
- !xindex_is_subset(lperms.xindex, perms.xindex)) {
- lperms.allow &= ~MAY_EXEC;
- request |= MAY_EXEC;
- info = "link not subset of target";
- goto audit;
- }
+ /* done if link subset test is not required */
+ if (!(perms.allow & AA_LINK_SUBSET))
+ goto done_tests;
+
+ /* Do link perm subset test requiring allowed permission on
+ * link are a subset of the allowed permissions on target.
+ */
+ aa_str_perms(profile->file.dfa, profile->file.start, tname,
+ &cond, &perms);
+
+ /* AA_MAY_LINK is not considered in the subset test */
+ request = lperms.allow & ~AA_MAY_LINK;
+ lperms.allow &= perms.allow | AA_MAY_LINK;
+
+ request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow);
+ if (request & ~lperms.allow) {
+ goto audit;
+ } else if ((lperms.allow & MAY_EXEC) &&
+ !xindex_is_subset(lperms.xindex, perms.xindex)) {
+ lperms.allow &= ~MAY_EXEC;
+ request |= MAY_EXEC;
+ info = "link not subset of target";
+ goto audit;
+ }
-done_tests:
- error = 0;
+ done_tests:
+ error = 0;
-audit:
- error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request,
- lname, tname, cond.uid, info, error);
+ audit:
+ error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK,
+ request, lname, tname, cond.uid, info,
+ error);
+ if (error)
+ break;
+ }
+
+out:
kfree(buffer);
kfree(buffer2);
return error;
+
+err:
+ label_for_each_confined(i, label, profile) {
+ error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK,
+ request, lname, tname, cond.uid, info,
+ error);
+ if (error)
+ break;
+ }
+
+ goto out;
}
/**
* aa_file_perm - do permission revalidation check & audit for @file
* @op: operation being checked
- * @profile: profile being enforced (NOT NULL)
+ * @label: profile being enforced (NOT NULL)
* @file: file to revalidate access permissions on (NOT NULL)
* @request: requested permissions
*
* Returns: %0 if access allowed else error
*/
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(int op, struct aa_label *label, struct file *file,
u32 request)
{
struct path_cond cond = {
@@ -453,6 +492,6 @@ int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
.mode = file->f_path.dentry->d_inode->i_mode
};
- return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED,
+ return aa_path_perm(op, label, &file->f_path, PATH_DELEGATE_DELETED,
request, &cond);
}
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 2c922b8..0b0c1ab 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -17,6 +17,7 @@
#include "domain.h"
#include "match.h"
+#include "label.h"
struct aa_profile;
struct path;
@@ -171,13 +172,13 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond,
struct file_perms *perms);
-int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
+int aa_path_perm(int op, struct aa_label *label, struct path *path,
int flags, u32 request, struct path_cond *cond);
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry);
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(int op, struct aa_label *label, struct file *file,
u32 request);
static inline void aa_free_file_rules(struct aa_file_rules *rules)
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index 364a76b..a780917 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -64,15 +64,25 @@ static int aa_audit_ptrace(struct aa_profile *profile,
int aa_may_ptrace(struct task_struct *tracer_task, struct aa_label *tracer,
struct aa_label *tracee, unsigned int mode)
{
- /* TODO: currently only based on capability, not extended ptrace
- * rules,
- * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH
- */
+ struct aa_profile *profile;
+ int i, error = 0;
if (unconfined(tracer) || tracer == tracee)
return 0;
- /* log this capability request */
- return aa_capable(tracer_task, labels_profile(tracer), CAP_SYS_PTRACE, 1);
+
+ label_for_each_confined(i, tracer, profile) {
+ /* TODO: currently only based on capability, not extended ptrace
+ * rules,
+ * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH
+ */
+
+ /* log this capability request */
+ error = aa_capable(tracer_task, profile, CAP_SYS_PTRACE, 1);
+ if (error)
+ break;
+ }
+
+ return error;
}
/**
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index c4eb445..eb7bf2a 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -127,9 +127,17 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
*inheritable = cred->cap_inheritable;
*permitted = cred->cap_permitted;
- if (!unconfined(label) && !COMPLAIN_MODE(labels_profile(label))) {
- *effective = cap_intersect(*effective, labels_profile(label)->caps.allow);
- *permitted = cap_intersect(*permitted, labels_profile(label)->caps.allow);
+ if (!unconfined(label)) {
+ struct aa_profile *profile;
+ int i;
+ label_for_each_confined(i, label, profile) {
+ if (COMPLAIN_MODE(profile))
+ continue;
+ *effective = cap_intersect(*effective,
+ profile->caps.allow);
+ *permitted = cap_intersect(*permitted,
+ profile->caps.allow);
+ }
}
rcu_read_unlock();
@@ -139,14 +147,23 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
+ struct aa_profile *profile;
struct aa_label *label;
/* cap_capable returns 0 on success, else -EPERM */
- int error = cap_capable(cred, ns, cap, audit);
- if (!error) {
- label = aa_cred_label(cred);
- if (!unconfined(label))
- error = aa_capable(current, labels_profile(label), cap, audit);
+ int i, error = cap_capable(cred, ns, cap, audit);
+ if (error)
+ return error;
+
+ label = aa_cred_label(cred);
+ if (unconfined(label))
+ return 0;
+
+ label_for_each_confined(i, label, profile) {
+ error = aa_capable(current, profile, cap, audit);
+ if (error)
+ break;
}
+
return error;
}
@@ -167,7 +184,7 @@ static int common_perm(int op, struct path *path, u32 mask,
label = __aa_current_label();
if (!unconfined(label))
- error = aa_path_perm(op, labels_profile(label), path, 0, mask, cond);
+ error = aa_path_perm(op, label, path, 0, mask, cond);
return error;
}
@@ -310,7 +327,7 @@ static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir,
label = aa_current_label();
if (!unconfined(label))
- error = aa_path_link(labels_profile(label), old_dentry, new_dir, new_dentry);
+ error = aa_path_link(label, old_dentry, new_dir, new_dentry);
return error;
}
@@ -331,12 +348,12 @@ static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
old_dentry->d_inode->i_mode
};
- error = aa_path_perm(OP_RENAME_SRC, labels_profile(label), &old_path, 0,
+ error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
MAY_READ | AA_MAY_META_READ | MAY_WRITE |
AA_MAY_META_WRITE | AA_MAY_DELETE,
&cond);
if (!error)
- error = aa_path_perm(OP_RENAME_DEST, labels_profile(label), &new_path,
+ error = aa_path_perm(OP_RENAME_DEST, label, &new_path,
0, MAY_WRITE | AA_MAY_META_WRITE |
AA_MAY_CREATE, &cond);
@@ -349,7 +366,8 @@ static int apparmor_path_chmod(struct path *path, umode_t mode)
if (!mediated_filesystem(path->dentry->d_inode))
return 0;
- return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD);
+ return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry,
+ AA_MAY_CHMOD);
}
static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
@@ -397,7 +415,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
struct inode *inode = file->f_path.dentry->d_inode;
struct path_cond cond = { inode->i_uid, inode->i_mode };
- error = aa_path_perm(OP_OPEN, labels_profile(label), &file->f_path, 0,
+ error = aa_path_perm(OP_OPEN, label, &file->f_path, 0,
aa_map_file_to_perms(file), &cond);
/* todo cache full allowed permissions set and state */
fcxt->allow = aa_map_file_to_perms(file);
@@ -446,7 +464,7 @@ static int common_file_perm(int op, struct file *file, u32 mask)
*/
if (!unconfined(label) && !unconfined(flabel) &&
((flabel != label) || (mask & ~fcxt->allow)))
- error = aa_file_perm(op, labels_profile(label), file, mask);
+ error = aa_file_perm(op, label, file, mask);
return error;
}
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c
index e0a1d39..b9489c5 100644
--- a/security/apparmor/resource.c
+++ b/security/apparmor/resource.c
@@ -91,8 +91,9 @@ int aa_map_resource(int resource)
int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
unsigned int resource, struct rlimit *new_rlim)
{
+ struct aa_profile *profile;
struct aa_label *task_label;
- int error = 0;
+ int i, error = 0;
rcu_read_lock();
task_label = aa_get_label(aa_cred_label(__task_cred(task)));
@@ -103,14 +104,20 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
* that the task is setting the resource of a task confined with
* the same profile.
*/
- if (label != task_label ||
- (labels_profile(label)->rlimits.mask & (1 << resource) &&
- new_rlim->rlim_max > labels_profile(label)->rlimits.limits[resource].rlim_max))
- error = -EACCES;
+ label_for_each_confined(i, label, profile) {
+ if (label != task_label ||
+ (profile->rlimits.mask & (1 << resource) &&
+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
+ error = -EACCES;
+ error = audit_resource(labels_profile(label), resource,
+ new_rlim->rlim_max, error);
+ if (error)
+ break;
+ }
aa_put_label(task_label);
- return audit_resource(labels_profile(label), resource, new_rlim->rlim_max, error);
+ return error;
}
/**
@@ -128,31 +135,36 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
old = labels_profile(old_l);
new = labels_profile(new_l);
- /* for any rlimits the profile controlled reset the soft limit
- * to the less of the tasks hard limit and the init tasks soft limit
+ /* for any rlimits the profile controlled, reset the soft limit
+ * to the lesser of the tasks hard limit and the init tasks soft limit
*/
- if (old->rlimits.mask) {
- for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
- if (old->rlimits.mask & mask) {
- rlim = current->signal->rlim + i;
- initrlim = init_task.signal->rlim + i;
- rlim->rlim_cur = min(rlim->rlim_max,
- initrlim->rlim_cur);
+ label_for_each_confined(i, old_l, old) {
+ if (old->rlimits.mask) {
+ for (i = 0, mask = 1; i < RLIM_NLIMITS; i++,
+ mask <<= 1) {
+ if (old->rlimits.mask & mask) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim + i;
+ rlim->rlim_cur = min(rlim->rlim_max,
+ initrlim->rlim_cur);
+ }
}
}
}
/* set any new hard limits as dictated by the new profile */
- if (!new->rlimits.mask)
- return;
- for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
- if (!(new->rlimits.mask & mask))
+ label_for_each_confined(i, new_l, new) {
+ if (!new->rlimits.mask)
continue;
-
- rlim = current->signal->rlim + i;
- rlim->rlim_max = min(rlim->rlim_max,
- new->rlimits.limits[i].rlim_max);
- /* soft limit should not exceed hard limit */
- rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
+ for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
+ if (!(new->rlimits.mask & mask))
+ continue;
+
+ rlim = current->signal->rlim + i;
+ rlim->rlim_max = min(rlim->rlim_max,
+ new->rlimits.limits[i].rlim_max);
+ /* soft limit should not exceed hard limit */
+ rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
+ }
}
}
--
1.8.1.2
More information about the AppArmor
mailing list