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 <[email protected]> --- 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 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
