Commit 100f59d9 authored by Mickaël Salaün's avatar Mickaël Salaün

LSM: Remove double path_rename hook calls for RENAME_EXCHANGE

In order to be able to identify a file exchange with renameat2(2) and
RENAME_EXCHANGE, which will be useful for Landlock [1], propagate the
rename flags to LSMs.  This may also improve performance because of the
switch from two set of LSM hook calls to only one, and because LSMs
using this hook may optimize the double check (e.g. only one lock,
reduce the number of path walks).

AppArmor, Landlock and Tomoyo are updated to leverage this change.  This
should not change the current behavior (same check order), except
(different level of) speed boosts.

[1] https://lore.kernel.org/r/20220221212522.320243-1-mic@digikod.net

Cc: James Morris <jmorris@namei.org>
Cc: Kentaro Takeda <takedakn@nttdata.co.jp>
Cc: Serge E. Hallyn <serge@hallyn.com>
Acked-by: default avatarJohn Johansen <john.johansen@canonical.com>
Acked-by: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reviewed-by: default avatarPaul Moore <paul@paul-moore.com>
Signed-off-by: default avatarMickaël Salaün <mic@digikod.net>
Link: https://lore.kernel.org/r/20220506161102.525323-7-mic@digikod.net
parent 9da82b20
...@@ -100,7 +100,7 @@ LSM_HOOK(int, 0, path_link, struct dentry *old_dentry, ...@@ -100,7 +100,7 @@ LSM_HOOK(int, 0, path_link, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry) const struct path *new_dir, struct dentry *new_dentry)
LSM_HOOK(int, 0, path_rename, const struct path *old_dir, LSM_HOOK(int, 0, path_rename, const struct path *old_dir,
struct dentry *old_dentry, const struct path *new_dir, struct dentry *old_dentry, const struct path *new_dir,
struct dentry *new_dentry) struct dentry *new_dentry, unsigned int flags)
LSM_HOOK(int, 0, path_chmod, const struct path *path, umode_t mode) LSM_HOOK(int, 0, path_chmod, const struct path *path, umode_t mode)
LSM_HOOK(int, 0, path_chown, const struct path *path, kuid_t uid, kgid_t gid) LSM_HOOK(int, 0, path_chown, const struct path *path, kuid_t uid, kgid_t gid)
LSM_HOOK(int, 0, path_chroot, const struct path *path) LSM_HOOK(int, 0, path_chroot, const struct path *path)
......
...@@ -358,6 +358,7 @@ ...@@ -358,6 +358,7 @@
* @old_dentry contains the dentry structure of the old link. * @old_dentry contains the dentry structure of the old link.
* @new_dir contains the path structure for parent of the new link. * @new_dir contains the path structure for parent of the new link.
* @new_dentry contains the dentry structure of the new link. * @new_dentry contains the dentry structure of the new link.
* @flags may contain rename options such as RENAME_EXCHANGE.
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @path_chmod: * @path_chmod:
* Check for permission to change a mode of the file @path. The new * Check for permission to change a mode of the file @path. The new
......
...@@ -354,13 +354,16 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_ ...@@ -354,13 +354,16 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
} }
static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_dentry, static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry) const struct path *new_dir, struct dentry *new_dentry,
const unsigned int flags)
{ {
struct aa_label *label; struct aa_label *label;
int error = 0; int error = 0;
if (!path_mediated_fs(old_dentry)) if (!path_mediated_fs(old_dentry))
return 0; return 0;
if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry))
return 0;
label = begin_current_label_crit_section(); label = begin_current_label_crit_section();
if (!unconfined(label)) { if (!unconfined(label)) {
...@@ -374,6 +377,23 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d ...@@ -374,6 +377,23 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
d_backing_inode(old_dentry)->i_mode d_backing_inode(old_dentry)->i_mode
}; };
if (flags & RENAME_EXCHANGE) {
struct path_cond cond_exchange = {
i_uid_into_mnt(mnt_userns, d_backing_inode(new_dentry)),
d_backing_inode(new_dentry)->i_mode
};
error = aa_path_perm(OP_RENAME_SRC, label, &new_path, 0,
MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
AA_MAY_SETATTR | AA_MAY_DELETE,
&cond_exchange);
if (!error)
error = aa_path_perm(OP_RENAME_DEST, label, &old_path,
0, MAY_WRITE | AA_MAY_SETATTR |
AA_MAY_CREATE, &cond_exchange);
}
if (!error)
error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0, error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
MAY_READ | AA_MAY_GETATTR | MAY_WRITE | MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
AA_MAY_SETATTR | AA_MAY_DELETE, AA_MAY_SETATTR | AA_MAY_DELETE,
......
...@@ -622,10 +622,12 @@ static int hook_path_link(struct dentry *const old_dentry, ...@@ -622,10 +622,12 @@ static int hook_path_link(struct dentry *const old_dentry,
static int hook_path_rename(const struct path *const old_dir, static int hook_path_rename(const struct path *const old_dir,
struct dentry *const old_dentry, struct dentry *const old_dentry,
const struct path *const new_dir, const struct path *const new_dir,
struct dentry *const new_dentry) struct dentry *const new_dentry,
const unsigned int flags)
{ {
const struct landlock_ruleset *const dom = const struct landlock_ruleset *const dom =
landlock_get_current_domain(); landlock_get_current_domain();
u32 exchange_access = 0;
if (!dom) if (!dom)
return 0; return 0;
...@@ -633,12 +635,19 @@ static int hook_path_rename(const struct path *const old_dir, ...@@ -633,12 +635,19 @@ static int hook_path_rename(const struct path *const old_dir,
if (old_dir->dentry != new_dir->dentry) if (old_dir->dentry != new_dir->dentry)
/* Gracefully forbids reparenting. */ /* Gracefully forbids reparenting. */
return -EXDEV; return -EXDEV;
if (flags & RENAME_EXCHANGE) {
if (unlikely(d_is_negative(new_dentry)))
return -ENOENT;
exchange_access =
get_mode_access(d_backing_inode(new_dentry)->i_mode);
}
if (unlikely(d_is_negative(old_dentry))) if (unlikely(d_is_negative(old_dentry)))
return -ENOENT; return -ENOENT;
/* RENAME_EXCHANGE is handled because directories are the same. */ /* RENAME_EXCHANGE is handled because directories are the same. */
return check_access_path( return check_access_path(
dom, old_dir, dom, old_dir,
maybe_remove(old_dentry) | maybe_remove(new_dentry) | maybe_remove(old_dentry) | maybe_remove(new_dentry) |
exchange_access |
get_mode_access(d_backing_inode(old_dentry)->i_mode)); get_mode_access(d_backing_inode(old_dentry)->i_mode));
} }
......
...@@ -1197,15 +1197,8 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, ...@@ -1197,15 +1197,8 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
(d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry))))) (d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry)))))
return 0; return 0;
if (flags & RENAME_EXCHANGE) {
int err = call_int_hook(path_rename, 0, new_dir, new_dentry,
old_dir, old_dentry);
if (err)
return err;
}
return call_int_hook(path_rename, 0, old_dir, old_dentry, new_dir, return call_int_hook(path_rename, 0, old_dir, old_dentry, new_dir,
new_dentry); new_dentry, flags);
} }
EXPORT_SYMBOL(security_path_rename); EXPORT_SYMBOL(security_path_rename);
......
...@@ -264,17 +264,26 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di ...@@ -264,17 +264,26 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di
* @old_dentry: Pointer to "struct dentry". * @old_dentry: Pointer to "struct dentry".
* @new_parent: Pointer to "struct path". * @new_parent: Pointer to "struct path".
* @new_dentry: Pointer to "struct dentry". * @new_dentry: Pointer to "struct dentry".
* @flags: Rename options.
* *
* Returns 0 on success, negative value otherwise. * Returns 0 on success, negative value otherwise.
*/ */
static int tomoyo_path_rename(const struct path *old_parent, static int tomoyo_path_rename(const struct path *old_parent,
struct dentry *old_dentry, struct dentry *old_dentry,
const struct path *new_parent, const struct path *new_parent,
struct dentry *new_dentry) struct dentry *new_dentry,
const unsigned int flags)
{ {
struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry }; struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry }; struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
if (flags & RENAME_EXCHANGE) {
const int err = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path2,
&path1);
if (err)
return err;
}
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2); return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment