fscrypt: add FS_IOC_GET_ENCRYPTION_NONCE ioctl

Add an ioctl FS_IOC_GET_ENCRYPTION_NONCE which retrieves the nonce from
an encrypted file or directory.  The nonce is the 16-byte random value
stored in the inode's encryption xattr.  It is normally used together
with the master key to derive the inode's actual encryption key.

The nonces are needed by automated tests that verify the correctness of
the ciphertext on-disk.  Except for the IV_INO_LBLK_64 case, there's no
way to replicate a file's ciphertext without knowing that file's nonce.

The nonces aren't secret, and the existing ciphertext verification tests
in xfstests retrieve them from disk using debugfs or dump.f2fs.  But in
environments that lack these debugging tools, getting the nonces by
manually parsing the filesystem structure would be very hard.

To make this important type of testing much easier, let's just add an
ioctl that retrieves the nonce.

Link: https://lore.kernel.org/r/20200314205052.93294-2-ebiggers@kernel.org
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Eric Biggers 2020-03-14 13:50:49 -07:00
parent 98d54f81e3
commit e98ad46475
6 changed files with 60 additions and 15 deletions

View File

@ -633,6 +633,17 @@ from a passphrase or other low-entropy user credential.
FS_IOC_GET_ENCRYPTION_PWSALT is deprecated. Instead, prefer to FS_IOC_GET_ENCRYPTION_PWSALT is deprecated. Instead, prefer to
generate and manage any needed salt(s) in userspace. generate and manage any needed salt(s) in userspace.
Getting a file's encryption nonce
---------------------------------
Since Linux v5.7, the ioctl FS_IOC_GET_ENCRYPTION_NONCE is supported.
On encrypted files and directories it gets the inode's 16-byte nonce.
On unencrypted files and directories, it fails with ENODATA.
This ioctl can be useful for automated tests which verify that the
encryption is being done correctly. It is not needed for normal use
of fscrypt.
Adding keys Adding keys
----------- -----------

View File

@ -76,6 +76,26 @@ static inline int fscrypt_context_size(const union fscrypt_context *ctx)
return 0; return 0;
} }
/* Check whether an fscrypt_context has a recognized version number and size */
static inline bool fscrypt_context_is_valid(const union fscrypt_context *ctx,
int ctx_size)
{
return ctx_size >= 1 && ctx_size == fscrypt_context_size(ctx);
}
/* Retrieve the context's nonce, assuming the context was already validated */
static inline const u8 *fscrypt_context_nonce(const union fscrypt_context *ctx)
{
switch (ctx->version) {
case FSCRYPT_CONTEXT_V1:
return ctx->v1.nonce;
case FSCRYPT_CONTEXT_V2:
return ctx->v2.nonce;
}
WARN_ON(1);
return NULL;
}
#undef fscrypt_policy #undef fscrypt_policy
union fscrypt_policy { union fscrypt_policy {
u8 version; u8 version;

View File

@ -425,20 +425,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
goto out; goto out;
} }
switch (ctx.version) { memcpy(crypt_info->ci_nonce, fscrypt_context_nonce(&ctx),
case FSCRYPT_CONTEXT_V1:
memcpy(crypt_info->ci_nonce, ctx.v1.nonce,
FS_KEY_DERIVATION_NONCE_SIZE); FS_KEY_DERIVATION_NONCE_SIZE);
break;
case FSCRYPT_CONTEXT_V2:
memcpy(crypt_info->ci_nonce, ctx.v2.nonce,
FS_KEY_DERIVATION_NONCE_SIZE);
break;
default:
WARN_ON(1);
res = -EINVAL;
goto out;
}
if (!fscrypt_supported_policy(&crypt_info->ci_policy, inode)) { if (!fscrypt_supported_policy(&crypt_info->ci_policy, inode)) {
res = -EINVAL; res = -EINVAL;

View File

@ -258,7 +258,7 @@ int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
{ {
memset(policy_u, 0, sizeof(*policy_u)); memset(policy_u, 0, sizeof(*policy_u));
if (ctx_size <= 0 || ctx_size != fscrypt_context_size(ctx_u)) if (!fscrypt_context_is_valid(ctx_u, ctx_size))
return -EINVAL; return -EINVAL;
switch (ctx_u->version) { switch (ctx_u->version) {
@ -481,6 +481,25 @@ int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *uarg)
} }
EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_policy_ex); EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_policy_ex);
/* FS_IOC_GET_ENCRYPTION_NONCE: retrieve file's encryption nonce for testing */
int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg)
{
struct inode *inode = file_inode(filp);
union fscrypt_context ctx;
int ret;
ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
if (ret < 0)
return ret;
if (!fscrypt_context_is_valid(&ctx, ret))
return -EINVAL;
if (copy_to_user(arg, fscrypt_context_nonce(&ctx),
FS_KEY_DERIVATION_NONCE_SIZE))
return -EFAULT;
return 0;
}
EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_nonce);
/** /**
* fscrypt_has_permitted_context() - is a file's encryption policy permitted * fscrypt_has_permitted_context() - is a file's encryption policy permitted
* within its directory? * within its directory?

View File

@ -139,6 +139,7 @@ extern void fscrypt_free_bounce_page(struct page *bounce_page);
extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); extern int fscrypt_ioctl_set_policy(struct file *, const void __user *);
extern int fscrypt_ioctl_get_policy(struct file *, void __user *); extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *); extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *);
extern int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
extern int fscrypt_has_permitted_context(struct inode *, struct inode *); extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
extern int fscrypt_inherit_context(struct inode *, struct inode *, extern int fscrypt_inherit_context(struct inode *, struct inode *,
void *, bool); void *, bool);
@ -300,6 +301,11 @@ static inline int fscrypt_ioctl_get_policy_ex(struct file *filp,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg)
{
return -EOPNOTSUPP;
}
static inline int fscrypt_has_permitted_context(struct inode *parent, static inline int fscrypt_has_permitted_context(struct inode *parent,
struct inode *child) struct inode *child)
{ {

View File

@ -163,6 +163,7 @@ struct fscrypt_get_key_status_arg {
#define FS_IOC_REMOVE_ENCRYPTION_KEY _IOWR('f', 24, struct fscrypt_remove_key_arg) #define FS_IOC_REMOVE_ENCRYPTION_KEY _IOWR('f', 24, struct fscrypt_remove_key_arg)
#define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct fscrypt_remove_key_arg) #define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct fscrypt_remove_key_arg)
#define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 26, struct fscrypt_get_key_status_arg) #define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 26, struct fscrypt_get_key_status_arg)
#define FS_IOC_GET_ENCRYPTION_NONCE _IOR('f', 27, __u8[16])
/**********************************************************************/ /**********************************************************************/