Re: [PATCH v3 2/3] ext4 crypto: add ioctls to allow backup of encryption metadata

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Dec 10, 2015, at 8:04 AM, Theodore Ts'o <tytso@xxxxxxx> wrote:
> 
> Add new ioctls which allow for the metadata of encrypted files (the
> filename and the crypto policy) to be backed up.  We can restore the
> crypto policy, but for now we can't restore the encrypted filename
> because messing with encrypted directories directly while bypassing
> the VFS would get fairly tricky/nasty.

Instead of exposing these via ioctls, why not use virtual xattrs
(e.g. "trusted.crypt_meta" or similar) so that this can be backed
up and restored using a normal backup tool instead of basically
nothing that exists today.

For reading the ciphertext, do existing tools such as tar and rsync
allow reading with O_DIRECT with CIPHERTEXT_ACCESS?  If not, one might
consider to allow normal reads w/o the file key to return the
ciphertext so that "tar --xattr" by some process with CAP_SYS_ADMIN
but out any keys could be used to backup and restore the ciphertext
in a sensible manner.  That is no more risk than the same process
reading the blocks from the disk device directly, but would be a lot
more useful (i.e. it is practical to use normal backup tools for
encrypted files).

Cheers, Andreas

> Signed-off-by: Theodore Ts'o <tytso@xxxxxxx>
> ---
> fs/ext4/crypto_key.c  | 57 ++++++++++++++++++++++++++++++++++++++++++++-
> fs/ext4/ext4.h        |  9 ++++++++
> fs/ext4/ext4_crypto.h |  8 +++++++
> fs/ext4/ioctl.c       | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/namei.c       | 24 +++++++++++++++++++
> 5 files changed, 161 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
> index c5882b3..039b22d 100644
> --- a/fs/ext4/crypto_key.c
> +++ b/fs/ext4/crypto_key.c
> @@ -14,7 +14,7 @@
> #include <linux/scatterlist.h>
> #include <uapi/linux/keyctl.h>
> 
> -#include "ext4.h"
> +#include "ext4_jbd2.h"
> #include "xattr.h"
> 
> static void derive_crypt_complete(struct crypto_async_request *req, int rc)
> @@ -270,3 +270,58 @@ int ext4_has_encryption_key(struct inode *inode)
> 
> 	return (ei->i_crypt_info != NULL);
> }
> +
> +int ext4_get_encryption_metadata(struct inode *inode,
> +				 struct ext4_encrypted_metadata *mdata)
> +{
> +	int res;
> +
> +	if (mdata->len < sizeof(struct ext4_encryption_context))
> +		return -EINVAL;
> +
> +	res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +			     &mdata->metadata, mdata->len);
> +	if (res < 0)
> +		return res;
> +	mdata->len = res;
> +	return 0;
> +}
> +
> +int ext4_set_encryption_metadata(struct inode *inode,
> +				 struct ext4_encrypted_metadata *mdata)
> +{
> +	struct ext4_encryption_context *ctx;
> +	handle_t *handle;
> +	int res;
> +
> +	if (mdata->len != sizeof(struct ext4_encryption_context))
> +		return -EINVAL;
> +	ctx = (struct ext4_encryption_context *) &mdata->metadata;
> +	if (ctx->format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1)
> +		return -EINVAL;
> +
> +	res = ext4_convert_inline_data(inode);
> +	if (res)
> +		return res;
> +
> +	handle = ext4_journal_start(inode, EXT4_HT_MISC,
> +				    ext4_jbd2_credits_xattr(inode));
> +	if (IS_ERR(handle))
> +		return PTR_ERR(handle);
> +	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx,
> +			     sizeof(struct ext4_encryption_context), 0);
> +	if (res < 0)
> +		goto errout;
> +	ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
> +	ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
> +	res = ext4_mark_inode_dirty(handle, inode);
> +	if (res)
> +		EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
> +	else
> +		res = ext4_get_encryption_info(inode);
> +errout:
> +	ext4_journal_stop(handle);
> +	return res;
> +}
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index cf7a885..c569430 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -617,6 +617,9 @@ enum {
> #define EXT4_IOC_SET_ENCRYPTION_POLICY	_IOR('f', 19, struct ext4_encryption_policy)
> #define EXT4_IOC_GET_ENCRYPTION_PWSALT	_IOW('f', 20, __u8[16])
> #define EXT4_IOC_GET_ENCRYPTION_POLICY	_IOW('f', 21, struct ext4_encryption_policy)
> +#define EXT4_IOC_GET_ENCRYPTION_METADATA _IOWR('f', 22, struct ext4_encrypted_metadata)
> +#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOR('f', 23, struct ext4_encrypted_metadata)
> +#define EXT4_IOC_GET_ENCRYPTED_FILENAME	_IOWR('f', 24, struct ext4_encrypted_metadata)
> 
> #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
> /*
> @@ -2311,6 +2314,10 @@ static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
> void ext4_free_crypt_info(struct ext4_crypt_info *ci);
> void ext4_free_encryption_info(struct inode *inode, struct ext4_crypt_info *ci);
> int _ext4_get_encryption_info(struct inode *inode);
> +int ext4_set_encryption_metadata(struct inode *inode,
> +				 struct ext4_encrypted_metadata *mdata);
> +int ext4_get_encryption_metadata(struct inode *inode,
> +				 struct ext4_encrypted_metadata *mdata);
> 
> #ifdef CONFIG_EXT4_FS_ENCRYPTION
> int ext4_has_encryption_key(struct inode *inode);
> @@ -2546,6 +2553,8 @@ extern int ext4_generic_delete_entry(handle_t *handle,
> 				     int buf_size,
> 				     int csum_size);
> extern int ext4_empty_dir(struct inode *inode);
> +extern int ext4_get_encrypted_filename(struct file *filp,
> +				       struct ext4_encrypted_metadata *mdata);
> 
> /* resize.c */
> extern int ext4_group_add(struct super_block *sb,
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> index ac7d4e8..f267cd3 100644
> --- a/fs/ext4/ext4_crypto.h
> +++ b/fs/ext4/ext4_crypto.h
> @@ -156,4 +156,12 @@ static inline u32 encrypted_symlink_data_len(u32 l)
> 	return (l + sizeof(struct ext4_encrypted_symlink_data) - 1);
> }
> 
> +/**
> + * Structure used for communicating encrypted metadata with userspace
> + */
> +struct ext4_encrypted_metadata {
> +	u32 len;
> +	char metadata[288];
> +};
> +
> #endif	/* _EXT4_CRYPTO_H */
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index 5e872fd..afb51f5 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -689,6 +689,67 @@ encryption_policy_out:
> 		return -EOPNOTSUPP;
> #endif
> 	}
> +	case EXT4_IOC_GET_ENCRYPTION_METADATA: {
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		struct ext4_encrypted_metadata mdata;
> +		int err = 0;
> +
> +		if (get_user(mdata.len, (u32 __user *) arg))
> +			return -EFAULT;
> +		if (mdata.len > sizeof(mdata.metadata))
> +			return -EINVAL;
> +
> +		if (!ext4_encrypted_inode(inode))
> +			return -ENOENT;
> +		err = ext4_get_encryption_metadata(inode, &mdata);
> +		if (err)
> +			return err;
> +		if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata)))
> +			return -EFAULT;
> +		return 0;
> +#else
> +		return -EOPNOTSUPP;
> +#endif
> +	}
> +	case EXT4_IOC_SET_ENCRYPTION_METADATA: {
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		struct ext4_encrypted_metadata mdata;
> +		int err = 0;
> +
> +		if (ext4_encrypted_inode(inode))
> +			return -EINVAL;
> +		if (copy_from_user(&mdata,
> +				   (struct ext4_encrypted_metadata __user *)arg,
> +				   sizeof(mdata)))
> +			return -EFAULT;
> +		err = ext4_set_encryption_metadata(inode, &mdata);
> +		return err;
> +#else
> +		return -EOPNOTSUPP;
> +#endif
> +	}
> +	case EXT4_IOC_GET_ENCRYPTED_FILENAME: {
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		struct ext4_encrypted_metadata mdata;
> +		int err = 0;
> +
> +		if (get_user(mdata.len, (u32 __user *) arg))
> +			return -EFAULT;
> +		if (mdata.len > sizeof(mdata.metadata))
> +			return -EINVAL;
> +
> +		if (!ext4_encrypted_inode(inode))
> +			return -ENOENT;
> +		err = ext4_get_encrypted_filename(filp, &mdata);
> +		if (err)
> +			return err;
> +		if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata)))
> +			return -EFAULT;
> +		return 0;
> +#else
> +		return -EOPNOTSUPP;
> +#endif
> +	}
> 	default:
> 		return -ENOTTY;
> 	}
> @@ -755,6 +816,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> 	case EXT4_IOC_SET_ENCRYPTION_POLICY:
> 	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
> 	case EXT4_IOC_GET_ENCRYPTION_POLICY:
> +	case EXT4_IOC_GET_ENCRYPTION_METADATA:
> +	case EXT4_IOC_SET_ENCRYPTION_METADATA:
> +	case EXT4_IOC_GET_ENCRYPTED_FILENAME:
> 		break;
> 	default:
> 		return -ENOIOCTLCMD;
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index a969ab3..1cc4eef 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -3861,3 +3861,27 @@ const struct inode_operations ext4_special_inode_operations = {
> 	.get_acl	= ext4_get_acl,
> 	.set_acl	= ext4_set_acl,
> };
> +
> +int ext4_get_encrypted_filename(struct file *filp,
> +				struct ext4_encrypted_metadata *mdata)
> +{
> +	struct dentry *dentry = filp->f_path.dentry;
> +	struct inode *dir = dentry->d_parent->d_inode;
> +	struct buffer_head *bh;
> +	struct ext4_dir_entry_2 *de;
> +
> +	if (!dir || !ext4_encrypted_inode(dir))
> +		return -EINVAL;
> +
> +	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
> +	if (IS_ERR(bh))
> +		return PTR_ERR(bh);
> +	if (de == NULL)
> +		return -ENOENT;
> +
> +	if (mdata->len < de->name_len)
> +		return -ENOSPC;
> +	mdata->len = de->name_len;
> +	memcpy(mdata->metadata, de->name, de->name_len);
> +	return 0;
> +}
> --
> 2.5.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas





Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail


[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux