Commit 94af0470 authored by Jeff Layton's avatar Jeff Layton Committed by Ilya Dryomov
Browse files

ceph: add some fscrypt guardrails



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: default avatarJeff Layton <jlayton@kernel.org>
Reviewed-by: default avatarXiubo Li <xiubli@redhat.com>
Reviewed-and-tested-by: default avatarLuís Henriques <lhenriques@suse.de>
Reviewed-by: default avatarMilind Changire <mchangir@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent 79f2f6ad
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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;
+9 −0
Original line number Diff line number Diff line
@@ -1148,6 +1148,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
	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 %llx.%llx old_dentry %p:'%pd' dentry %p:'%pd'\n",
	     dir, ceph_vinop(dir), old_dentry, old_dentry, dentry, dentry);
	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS);
@@ -1395,6 +1399,11 @@ static int ceph_rename(struct mnt_idmap *idmap, struct inode *old_dir,
	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);
+20 −1
Original line number Diff line number Diff line
@@ -366,8 +366,13 @@ int ceph_open(struct inode *inode, struct file *file)

	/* 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);
@@ -879,6 +884,13 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
		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);
@@ -2222,6 +2234,9 @@ static long ceph_fallocate(struct file *file, int mode,
	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;
@@ -2543,6 +2558,10 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off,
		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 */

+18 −8
Original line number Diff line number Diff line
@@ -972,13 +972,6 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
	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
@@ -1004,6 +997,15 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
		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));
@@ -2495,6 +2497,10 @@ int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
	if (ceph_inode_is_shutdown(inode))
		return -ESTALE;

	err = fscrypt_prepare_setattr(dentry, attr);
	if (err)
		return err;

	err = setattr_prepare(&nop_mnt_idmap, dentry, attr);
	if (err != 0)
		return err;
@@ -2778,8 +2784,12 @@ int ceph_getattr(struct mnt_idmap *idmap, const struct path *path,
			stat->nlink = 1 + 1 + ci->i_subdirs;
	}

	stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC;
	stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
	if (IS_ENCRYPTED(inode))
		stat->attributes |= STATX_ATTR_ENCRYPTED;
	stat->attributes_mask |= (STATX_ATTR_CHANGE_MONOTONIC |
				  STATX_ATTR_ENCRYPTED);

	stat->result_mask = request_mask & valid_mask;
	return err;
}
+4 −0
Original line number Diff line number Diff line
@@ -294,6 +294,10 @@ static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
	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;