From: Jeff Layton <jlayton@kernel.org>
Date: Thu, 1 Jul 2021 14:40:51 -0400
Subject: ceph: add some fscrypt guardrails
Mime-version: 1.0
Content-type: text/plain; charset=UTF-8
Content-transfer-encoding: 8bit
Git-commit: 94af0470924c6368b07f9125fde29d6698ed1558
Patch-mainline: v6.6-rc1
References: jsc#SES-1880
Add the appropriate calls into fscrypt for various actions, including
link, rename, setattr, and the open codepaths.
Disable fallocate for encrypted inodes -- hopefully, just for now.
If we have an encrypted inode, then the client will need to re-encrypt
the contents of the new object. Disable copy offload to or from
encrypted inodes.
Set i_blkbits to crypto block size for encrypted inodes -- some of the
underlying infrastructure for fscrypt relies on i_blkbits being aligned
to crypto blocksize.
Report STATX_ATTR_ENCRYPTED on encrypted inodes.
[ lhenriques: forbid encryption with striped layouts ]
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Xiubo Li <xiubli@redhat.com>
Reviewed-and-tested-by: Luís Henriques <lhenriques@suse.de>
Reviewed-by: Milind Changire <mchangir@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
[adjusted context as we don't have commit f6102994338c ("ceph: report the inode
version in getattr if requested")]
Acked-by: Luis Henriques <lhenriques@suse.de>
---
fs/ceph/crypto.h | 4 ++++
fs/ceph/dir.c | 9 +++++++++
fs/ceph/file.c | 21 ++++++++++++++++++++-
fs/ceph/inode.c | 24 +++++++++++++++++-------
fs/ceph/ioctl.c | 4 ++++
5 files changed, 54 insertions(+), 8 deletions(-)
--- a/fs/ceph/crypto.h
+++ b/fs/ceph/crypto.h
@@ -9,6 +9,10 @@
#include <crypto/sha2.h>
#include <linux/fscrypt.h>
+#define CEPH_FSCRYPT_BLOCK_SHIFT 12
+#define CEPH_FSCRYPT_BLOCK_SIZE (_AC(1, UL) << CEPH_FSCRYPT_BLOCK_SHIFT)
+#define CEPH_FSCRYPT_BLOCK_MASK (~(CEPH_FSCRYPT_BLOCK_SIZE-1))
+
struct ceph_fs_client;
struct ceph_acl_sec_ctx;
struct ceph_mds_request;
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1101,6 +1101,10 @@ static int ceph_link(struct dentry *old_
if (ceph_snap(dir) != CEPH_NOSNAP)
return -EROFS;
+ err = fscrypt_prepare_link(old_dentry, dir, dentry);
+ if (err)
+ return err;
+
dout("link in dir %p old_dentry %p dentry %p\n", dir,
old_dentry, dentry);
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS);
@@ -1342,6 +1346,11 @@ static int ceph_rename(struct user_names
if (err)
return err;
+ err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
+ flags);
+ if (err)
+ return err;
+
dout("rename dir %p dentry %p to dir %p dentry %p\n",
old_dir, old_dentry, new_dir, new_dentry);
req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -371,8 +371,13 @@ int ceph_open(struct inode *inode, struc
/* filter out O_CREAT|O_EXCL; vfs did that already. yuck. */
flags = file->f_flags & ~(O_CREAT|O_EXCL);
- if (S_ISDIR(inode->i_mode))
+ if (S_ISDIR(inode->i_mode)) {
flags = O_DIRECTORY; /* mds likes to know */
+ } else if (S_ISREG(inode->i_mode)) {
+ err = fscrypt_file_open(inode, file);
+ if (err)
+ return err;
+ }
dout("open inode %p ino %llx.%llx file %p flags %d (%d)\n", inode,
ceph_vinop(inode), file, flags, file->f_flags);
@@ -889,6 +894,13 @@ retry:
dout("atomic_open finish_no_open on dn %p\n", dn);
err = finish_no_open(file, dn);
} else {
+ if (IS_ENCRYPTED(dir) &&
+ !fscrypt_has_permitted_context(dir, d_inode(dentry))) {
+ pr_warn("Inconsistent encryption context (parent %llx:%llx child %llx:%llx)\n",
+ ceph_vinop(dir), ceph_vinop(d_inode(dentry)));
+ goto out_req;
+ }
+
dout("atomic_open finish_open on dn %p\n", dn);
if (req->r_op == CEPH_MDS_OP_CREATE && req->r_reply_info.has_create_ino) {
struct inode *newino = d_inode(dentry);
@@ -2175,6 +2187,9 @@ static long ceph_fallocate(struct file *
if (!S_ISREG(inode->i_mode))
return -EOPNOTSUPP;
+ if (IS_ENCRYPTED(inode))
+ return -EOPNOTSUPP;
+
prealloc_cf = ceph_alloc_cap_flush();
if (!prealloc_cf)
return -ENOMEM;
@@ -2496,6 +2511,10 @@ static ssize_t __ceph_copy_file_range(st
return -EOPNOTSUPP;
}
+ /* Every encrypted inode gets its own key, so we can't offload them */
+ if (IS_ENCRYPTED(src_inode) || IS_ENCRYPTED(dst_inode))
+ return -EOPNOTSUPP;
+
if (len < src_ci->i_layout.object_size)
return -EOPNOTSUPP; /* no remote copy will be done */
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -942,13 +942,6 @@ int ceph_fill_inode(struct inode *inode,
issued |= __ceph_caps_dirty(ci);
new_issued = ~issued & info_caps;
- /* directories have fl_stripe_unit set to zero */
- if (le32_to_cpu(info->layout.fl_stripe_unit))
- inode->i_blkbits =
- fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
- else
- inode->i_blkbits = CEPH_BLOCK_SHIFT;
-
__ceph_update_quota(ci, iinfo->max_bytes, iinfo->max_files);
#ifdef CONFIG_FS_ENCRYPTION
@@ -974,6 +967,15 @@ int ceph_fill_inode(struct inode *inode,
ceph_decode_timespec64(&ci->i_snap_btime, &iinfo->snap_btime);
}
+ /* directories have fl_stripe_unit set to zero */
+ if (IS_ENCRYPTED(inode))
+ inode->i_blkbits = CEPH_FSCRYPT_BLOCK_SHIFT;
+ else if (le32_to_cpu(info->layout.fl_stripe_unit))
+ inode->i_blkbits =
+ fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
+ else
+ inode->i_blkbits = CEPH_BLOCK_SHIFT;
+
if ((new_version || (new_issued & CEPH_CAP_LINK_SHARED)) &&
(issued & CEPH_CAP_LINK_EXCL) == 0)
set_nlink(inode, le32_to_cpu(info->nlink));
@@ -2406,6 +2408,10 @@ int ceph_setattr(struct user_namespace *
if (ceph_inode_is_shutdown(inode))
return -ESTALE;
+ err = fscrypt_prepare_setattr(dentry, attr);
+ if (err)
+ return err;
+
err = setattr_prepare(&init_user_ns, dentry, attr);
if (err != 0)
return err;
@@ -2685,6 +2691,10 @@ int ceph_getattr(struct user_namespace *
stat->nlink = 1 + 1 + ci->i_subdirs;
}
+ if (IS_ENCRYPTED(inode))
+ stat->attributes |= STATX_ATTR_ENCRYPTED;
+ stat->attributes_mask |= STATX_ATTR_ENCRYPTED;
+
stat->result_mask = request_mask & valid_mask;
return err;
}
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -294,6 +294,10 @@ static long ceph_set_encryption_policy(s
struct inode *inode = file_inode(file);
struct ceph_inode_info *ci = ceph_inode(inode);
+ /* encrypted directories can't have striped layout */
+ if (ci->i_layout.stripe_count > 1)
+ return -EINVAL;
+
ret = vet_mds_for_fscrypt(file);
if (ret)
return ret;