Skip to content

Commit 6e1918c

Browse files
drosen-googleebiggers
authored andcommitted
fscrypt: don't allow v1 policies with casefolding
Casefolded encrypted directories will use a new dirhash method that requires a secret key. If the directory uses a v2 encryption policy, it's easy to derive this key from the master key using HKDF. However, v1 encryption policies don't provide a way to derive additional keys. Therefore, don't allow casefolding on directories that use a v1 policy. Specifically, make it so that trying to enable casefolding on a directory that has a v1 policy fails, trying to set a v1 policy on a casefolded directory fails, and trying to open a casefolded directory that has a v1 policy (if one somehow exists on-disk) fails. Signed-off-by: Daniel Rosenberg <[email protected]> [EB: improved commit message, updated fscrypt.rst, and other cleanups] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Eric Biggers <[email protected]>
1 parent 1b3b827 commit 6e1918c

File tree

5 files changed

+49
-2
lines changed

5 files changed

+49
-2
lines changed

Documentation/filesystems/fscrypt.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,9 @@ FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors:
513513
- ``EEXIST``: the file is already encrypted with an encryption policy
514514
different from the one specified
515515
- ``EINVAL``: an invalid encryption policy was specified (invalid
516-
version, mode(s), or flags; or reserved bits were set)
516+
version, mode(s), or flags; or reserved bits were set); or a v1
517+
encryption policy was specified but the directory has the casefold
518+
flag enabled (casefolding is incompatible with v1 policies).
517519
- ``ENOKEY``: a v2 encryption policy was specified, but the key with
518520
the specified ``master_key_identifier`` has not been added, nor does
519521
the process have the CAP_FOWNER capability in the initial user

fs/crypto/hooks.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,34 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
122122
}
123123
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
124124

125+
/**
126+
* fscrypt_prepare_setflags() - prepare to change flags with FS_IOC_SETFLAGS
127+
* @inode: the inode on which flags are being changed
128+
* @oldflags: the old flags
129+
* @flags: the new flags
130+
*
131+
* The caller should be holding i_rwsem for write.
132+
*
133+
* Return: 0 on success; -errno if the flags change isn't allowed or if
134+
* another error occurs.
135+
*/
136+
int fscrypt_prepare_setflags(struct inode *inode,
137+
unsigned int oldflags, unsigned int flags)
138+
{
139+
struct fscrypt_info *ci;
140+
int err;
141+
142+
if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL)) {
143+
err = fscrypt_require_key(inode);
144+
if (err)
145+
return err;
146+
ci = inode->i_crypt_info;
147+
if (ci->ci_policy.version != FSCRYPT_POLICY_V2)
148+
return -EINVAL;
149+
}
150+
return 0;
151+
}
152+
125153
int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
126154
unsigned int max_len,
127155
struct fscrypt_str *disk_link)

fs/crypto/policy.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
124124
policy->filenames_encryption_mode))
125125
return false;
126126

127+
if (IS_CASEFOLDED(inode)) {
128+
/* With v1, there's no way to derive dirhash keys. */
129+
fscrypt_warn(inode,
130+
"v1 policies can't be used on casefolded directories");
131+
return false;
132+
}
133+
127134
return true;
128135
}
129136

fs/inode.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/security.h>
1313
#include <linux/cdev.h>
1414
#include <linux/memblock.h>
15+
#include <linux/fscrypt.h>
1516
#include <linux/fsnotify.h>
1617
#include <linux/mount.h>
1718
#include <linux/posix_acl.h>
@@ -2252,7 +2253,7 @@ int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
22522253
!capable(CAP_LINUX_IMMUTABLE))
22532254
return -EPERM;
22542255

2255-
return 0;
2256+
return fscrypt_prepare_setflags(inode, oldflags, flags);
22562257
}
22572258
EXPORT_SYMBOL(vfs_ioc_setflags_prepare);
22582259

include/linux/fscrypt.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ extern int __fscrypt_prepare_rename(struct inode *old_dir,
263263
unsigned int flags);
264264
extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
265265
struct fscrypt_name *fname);
266+
extern int fscrypt_prepare_setflags(struct inode *inode,
267+
unsigned int oldflags, unsigned int flags);
266268
extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
267269
unsigned int max_len,
268270
struct fscrypt_str *disk_link);
@@ -519,6 +521,13 @@ static inline int __fscrypt_prepare_lookup(struct inode *dir,
519521
return -EOPNOTSUPP;
520522
}
521523

524+
static inline int fscrypt_prepare_setflags(struct inode *inode,
525+
unsigned int oldflags,
526+
unsigned int flags)
527+
{
528+
return 0;
529+
}
530+
522531
static inline int __fscrypt_prepare_symlink(struct inode *dir,
523532
unsigned int len,
524533
unsigned int max_len,

0 commit comments

Comments
 (0)