Add a mount option which allows root to be able to access the
ciphertext of a file by reading it using O_DIRECT.
Signed-off-by: Theodore Ts'o <[email protected]>
---
fs/ext4/ext4.h | 1 +
fs/ext4/file.c | 5 ++++-
fs/ext4/inode.c | 15 ++++++++-------
fs/ext4/super.c | 5 +++++
4 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1e20fa9..44278aa7 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1052,6 +1052,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000 /* Enable support for dio read nolocking */
#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */
#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */
+#define EXT4_MOUNT_CIPHERTEXT_ACCESS 0x2000000 /* Direct I/O to ciphertext */
#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 749b222..60683ab 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -388,7 +388,10 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
ret = ext4_get_encryption_info(inode);
if (ret)
return -EACCES;
- if (ext4_encryption_info(inode) == NULL)
+ if ((ext4_encryption_info(inode) == NULL) &&
+ !(test_opt(inode->i_sb, CIPHERTEXT_ACCESS) &&
+ ((filp->f_flags & O_ACCMODE) == O_RDONLY) &&
+ capable(CAP_SYS_ADMIN)))
return -ENOKEY;
}
/*
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ff2f3cd..4b21bff 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3279,9 +3279,6 @@ static ssize_t ext4_ext_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
get_block_func = ext4_get_block_write;
dio_flags = DIO_LOCKING;
}
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
- BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode));
-#endif
if (IS_DAX(inode))
ret = dax_do_io(iocb, inode, iter, offset, get_block_func,
ext4_end_io_dio, dio_flags);
@@ -3344,10 +3341,14 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
size_t count = iov_iter_count(iter);
ssize_t ret;
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
- if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
- return 0;
-#endif
+ if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
+ if (test_opt(inode->i_sb, CIPHERTEXT_ACCESS) &&
+ capable(CAP_SYS_ADMIN)) {
+ if (iov_iter_rw(iter) == WRITE)
+ return -EPERM;
+ } else
+ return 0;
+ }
/*
* If we are doing data journalling we don't support O_DIRECT
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 486e869..de875b4 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1182,6 +1182,7 @@ enum {
Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
+ Opt_ciphertext_access, Opt_nociphertext_access,
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
@@ -1273,6 +1274,8 @@ static const match_table_t tokens = {
{Opt_noinit_itable, "noinit_itable"},
{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
{Opt_test_dummy_encryption, "test_dummy_encryption"},
+ {Opt_ciphertext_access, "ciphertext_access"},
+ {Opt_nociphertext_access, "nociphertext_access"},
{Opt_removed, "check=none"}, /* mount option from ext2/3 */
{Opt_removed, "nocheck"}, /* mount option from ext2/3 */
{Opt_removed, "reservation"}, /* mount option from ext2/3 */
@@ -1475,6 +1478,8 @@ static const struct mount_opts {
{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
{Opt_max_dir_size_kb, 0, MOPT_GTE0},
{Opt_test_dummy_encryption, 0, MOPT_GTE0},
+ {Opt_ciphertext_access, EXT4_MOUNT_CIPHERTEXT_ACCESS, MOPT_SET},
+ {Opt_nociphertext_access, EXT4_MOUNT_CIPHERTEXT_ACCESS, MOPT_CLEAR},
{Opt_err, 0, 0}
};
--
2.5.0
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.
Signed-off-by: Theodore Ts'o <[email protected]>
---
fs/ext4/crypto_key.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-
fs/ext4/ext4.h | 9 ++++++++
fs/ext4/ext4_crypto.h | 8 +++++++
fs/ext4/ioctl.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/namei.c | 22 ++++++++++++++++++
5 files changed, 161 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
index c5882b3..54d4ecc 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,60 @@ 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;
+
+ mdata->len, sizeof(struct ext4_encryption_context),
+ mdata->len < sizeof(struct ext4_encryption_context));
+ 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 44278aa7..d8b2fb5 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 _IOW('f', 22, struct ext4_encrypted_metadata)
+#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOW('f', 23, struct ext4_encrypted_metadata)
+#define EXT4_IOC_GET_ENCRYPTED_FILENAME _IOW('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..81b43ca 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3861,3 +3861,25 @@ 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 (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
On Tue, Dec 08, 2015 at 03:51:05PM -0500, Theodore Ts'o 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.
>
> Signed-off-by: Theodore Ts'o <[email protected]>
> ---
> fs/ext4/crypto_key.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-
> fs/ext4/ext4.h | 9 ++++++++
> fs/ext4/ext4_crypto.h | 8 +++++++
> fs/ext4/ioctl.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/namei.c | 22 ++++++++++++++++++
> 5 files changed, 161 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
> index c5882b3..54d4ecc 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,60 @@ 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;
> +
> + mdata->len, sizeof(struct ext4_encryption_context),
> + mdata->len < sizeof(struct ext4_encryption_context));
Looks like something went wonky here.
> + 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 44278aa7..d8b2fb5 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 _IOW('f', 22, struct ext4_encrypted_metadata)
> +#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOW('f', 23, struct ext4_encrypted_metadata)
> +#define EXT4_IOC_GET_ENCRYPTED_FILENAME _IOW('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];
Did you choose 288 because of the max encrypted file name length?
While struct ext4_encryption_context is currently 28 bytes, it could
conceivably get much larger with more advanced key management
features. E.g., supporting multiple users' keys per file.
Do you think it's worth anticipating this with a version number? Or
plan on something like adding another IOCTL if we end up needing more
space?
> +};
> +
> #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..81b43ca 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -3861,3 +3861,25 @@ 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 (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
>
On Tue, Dec 08, 2015 at 01:32:15PM -0800, Michael Halcrow wrote:
>
> Looks like something went wonky here.
Oops, fixed.
> > +/**
> > + * Structure used for communicating encrypted metadata with userspace
> > + */
> > +struct ext4_encrypted_metadata {
> > + u32 len;
> > + char metadata[288];
>
> Did you choose 288 because of the max encrypted file name length?
>
> While struct ext4_encryption_context is currently 28 bytes, it could
> conceivably get much larger with more advanced key management
> features. E.g., supporting multiple users' keys per file.
>
> Do you think it's worth anticipating this with a version number? Or
> plan on something like adding another IOCTL if we end up needing more
> space?
For the ioctl's that do a GET, the caller is required to initialize
mdata.len to the size of the metadata array, and then the kernel
returns the actual size in mdata.len. (I suppose that means I should
have used _IORW for the ioctl encoding for
EXT4_IOC_GET_ENCRYPTION_METADATA and EXT4_IOC_SET_ENCRYPTION_METADATA.
I'll fix that in the next spin of the patches.)
At the moment we just blindly copy the entire ext4_encrypted_metadata
structure in and out of userspace, mainly because it's much easier,
and because today there is no way for the ioctl's to return more than
256 bytes (I added 32 bytes worth of padding just in case some day we
want to support a crypto system that requires an explicit IV for
filename encryption, although at the moment we don't have a way to
encode this in the on-disk directory entry.)
But the way I constructed the interface, it *is* possible to support
larger metadata sizes for things like public keys, etc., should we
ever decide to go down that path.
Cheers,
- Ted