From aebd873e8d3e34757c9295eef074d1be229f5893 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 9 Jun 2017 16:02:25 -0700 Subject: apparmor: refactor path name lookup and permission checks around labels Signed-off-by: John Johansen --- security/apparmor/file.c | 112 +++++++++++++++++++++++++++------------ security/apparmor/include/file.h | 5 +- security/apparmor/lsm.c | 13 ++--- 3 files changed, 85 insertions(+), 45 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/file.c b/security/apparmor/file.c index a40bc1e276dc..1b216f889131 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -152,6 +152,39 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, return aa_audit(type, profile, &sa, file_audit_cb); } +/** + * is_deleted - test if a file has been completely unlinked + * @dentry: dentry of file to test for deletion (NOT NULL) + * + * Returns: %1 if deleted else %0 + */ +static inline bool is_deleted(struct dentry *dentry) +{ + if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) + return 1; + return 0; +} + +static int path_name(const char *op, struct aa_label *label, + const struct path *path, int flags, char *buffer, + const char **name, struct path_cond *cond, u32 request) +{ + struct aa_profile *profile; + const char *info = NULL; + int error; + + error = aa_path_name(path, flags, buffer, name, &info, + labels_profile(label)->disconnected); + if (error) { + fn_for_each_confined(label, profile, + aa_audit_file(profile, &nullperms, op, request, *name, + NULL, NULL, cond->uid, info, error)); + return error; + } + + return 0; +} + /** * map_old_perms - map old file perms layout to the new layout * @old: permission set in old mapping @@ -249,23 +282,46 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, return state; } -/** - * is_deleted - test if a file has been completely unlinked - * @dentry: dentry of file to test for deletion (NOT NULL) - * - * Returns: %1 if deleted else %0 - */ -static inline bool is_deleted(struct dentry *dentry) +int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, + u32 request, struct path_cond *cond, int flags, + struct aa_perms *perms) { - if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) - return 1; - return 0; + int e = 0; + + if (profile_unconfined(profile)) + return 0; + aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); + if (request & ~perms->allow) + e = -EACCES; + return aa_audit_file(profile, perms, op, request, name, NULL, NULL, + cond->uid, NULL, e); +} + + +static int profile_path_perm(const char *op, struct aa_profile *profile, + const struct path *path, char *buffer, u32 request, + struct path_cond *cond, int flags, + struct aa_perms *perms) +{ + const char *name; + int error; + + if (profile_unconfined(profile)) + return 0; + + error = path_name(op, &profile->label, path, + flags | profile->path_flags, buffer, &name, cond, + request); + if (error) + return error; + return __aa_path_perm(op, profile, name, request, cond, flags, + perms); } /** * 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 @@ -273,36 +329,22 @@ static inline bool is_deleted(struct dentry *dentry) * * Returns: %0 else error if access denied or other error */ -int aa_path_perm(const char *op, struct aa_profile *profile, +int aa_path_perm(const char *op, struct aa_label *label, const struct path *path, int flags, u32 request, struct path_cond *cond) { - char *buffer = NULL; struct aa_perms perms = {}; - const char *name, *info = NULL; + struct aa_profile *profile; + char *buffer = NULL; int error; - flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); + flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR : + 0); get_buffers(buffer); - error = aa_path_name(path, flags, buffer, &name, &info, - profile->disconnected); - if (error) { - if (error == -ENOENT && is_deleted(path->dentry)) { - /* Access to open files that are deleted are - * give a pass (implicit delegation) - */ - error = 0; - info = NULL; - perms.allow = request; - } - } else { - aa_str_perms(profile->file.dfa, profile->file.start, name, cond, - &perms); - if (request & ~perms.allow) - error = -EACCES; - } - error = aa_audit_file(profile, &perms, op, request, name, NULL, NULL, - cond->uid, info, error); + error = fn_for_each_confined(label, profile, + profile_path_perm(op, profile, path, buffer, request, + cond, flags, &perms)); + put_buffers(buffer); return error; @@ -482,7 +524,7 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, /* TODO: label cross check */ if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) - error = aa_path_perm(op, labels_profile(label), &file->f_path, + error = aa_path_perm(op, label, &file->f_path, PATH_DELEGATE_DELETED, request, &cond); done: diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 7c6026460272..8daad14c47fd 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -190,7 +190,10 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, const char *name, struct path_cond *cond, struct aa_perms *perms); -int aa_path_perm(const char *op, struct aa_profile *profile, +int __aa_path_perm(const char *op, struct aa_profile *profile, + const char *name, u32 request, struct path_cond *cond, + int flags, struct aa_perms *perms); +int aa_path_perm(const char *op, struct aa_label *label, const struct path *path, int flags, u32 request, struct path_cond *cond); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 011fbb009663..d0c5721aa8b3 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -196,8 +196,7 @@ static int common_perm(const char *op, const struct path *path, u32 mask, label = __begin_current_label_crit_section(); 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); __end_current_label_crit_section(label); return error; @@ -359,15 +358,12 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d d_backing_inode(old_dentry)->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_GETATTR | MAY_WRITE | AA_MAY_SETATTR | 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_SETATTR | AA_MAY_CREATE, &cond); @@ -416,8 +412,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) struct inode *inode = file_inode(file); 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 */ fctx->allow = aa_map_file_to_perms(file); -- cgit v1.2.3