@@ -94,6 +94,7 @@ fscrypt_valid_context_format(const struct fscrypt_context *ctx, int size)
*/
struct fscrypt_master_key {
struct crypto_shash *mk_hmac;
+ u8 mk_hash[FSCRYPT_KEY_HASH_SIZE];
};
/*
@@ -157,6 +158,9 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
gfp_t gfp_flags);
/* keyinfo.c */
+extern int fscrypt_compute_key_hash(const struct inode *inode,
+ const struct fscrypt_policy *policy,
+ u8 hash[FSCRYPT_KEY_HASH_SIZE]);
extern void __exit fscrypt_essiv_cleanup(void);
#endif /* _FSCRYPT_PRIVATE_H */
@@ -39,8 +39,11 @@ static struct crypto_shash *essiv_hash_tfm;
*
* Keys derived with different info strings are cryptographically isolated from
* each other --- knowledge of one derived key doesn't reveal any others.
+ * (This property is particularly important for the derived key used as the
+ * "key hash", as that is stored in the clear.)
*/
#define HKDF_CONTEXT_PER_FILE_KEY 1
+#define HKDF_CONTEXT_KEY_HASH 2
/*
* HKDF consists of two steps:
@@ -211,6 +214,12 @@ alloc_master_key(const struct fscrypt_key *payload)
err = crypto_shash_setkey(k->mk_hmac, prk, sizeof(prk));
if (err)
goto fail;
+
+ /* Calculate the "key hash" */
+ err = hkdf_expand(k->mk_hmac, HKDF_CONTEXT_KEY_HASH, NULL, 0,
+ k->mk_hash, FSCRYPT_KEY_HASH_SIZE);
+ if (err)
+ goto fail;
out:
memzero_explicit(prk, sizeof(prk));
return k;
@@ -536,6 +545,31 @@ void __exit fscrypt_essiv_cleanup(void)
crypto_free_shash(essiv_hash_tfm);
}
+int fscrypt_compute_key_hash(const struct inode *inode,
+ const struct fscrypt_policy *policy,
+ u8 hash[FSCRYPT_KEY_HASH_SIZE])
+{
+ struct fscrypt_master_key *k;
+ unsigned int min_keysize;
+
+ /*
+ * Require that the master key be long enough for both the
+ * contents and filenames encryption modes.
+ */
+ min_keysize =
+ max(available_modes[policy->contents_encryption_mode].keysize,
+ available_modes[policy->filenames_encryption_mode].keysize);
+
+ k = load_master_key_from_keyring(inode, policy->master_key_descriptor,
+ min_keysize);
+ if (IS_ERR(k))
+ return PTR_ERR(k);
+
+ memcpy(hash, k->mk_hash, FSCRYPT_KEY_HASH_SIZE);
+ put_master_key(k);
+ return 0;
+}
+
int fscrypt_get_encryption_info(struct inode *inode)
{
struct fscrypt_info *crypt_info;
@@ -612,6 +646,18 @@ int fscrypt_get_encryption_info(struct inode *inode)
goto out;
}
+ /*
+ * Make sure the master key we found has the correct hash.
+ * Buggy or malicious userspace may provide the wrong key.
+ */
+ if (memcmp(crypt_info->ci_master_key->mk_hash, ctx.key_hash,
+ FSCRYPT_KEY_HASH_SIZE)) {
+ pr_warn_ratelimited("fscrypt: wrong encryption key supplied for inode %lu\n",
+ inode->i_ino);
+ res = -ENOKEY;
+ goto out;
+ }
+
res = derive_key_hkdf(crypt_info->ci_master_key, &ctx,
derived_key, derived_keysize);
}
@@ -42,7 +42,8 @@ static u8 context_version_for_policy(const struct fscrypt_policy *policy)
*/
static bool is_encryption_context_consistent_with_policy(
const struct fscrypt_context *ctx,
- const struct fscrypt_policy *policy)
+ const struct fscrypt_policy *policy,
+ const u8 key_hash[FSCRYPT_KEY_HASH_SIZE])
{
return (ctx->version == context_version_for_policy(policy)) &&
(memcmp(ctx->master_key_descriptor,
@@ -52,11 +53,14 @@ static bool is_encryption_context_consistent_with_policy(
(ctx->contents_encryption_mode ==
policy->contents_encryption_mode) &&
(ctx->filenames_encryption_mode ==
- policy->filenames_encryption_mode);
+ policy->filenames_encryption_mode) &&
+ (ctx->version == FSCRYPT_CONTEXT_V1 ||
+ (memcmp(ctx->key_hash, key_hash, FSCRYPT_KEY_HASH_SIZE) == 0));
}
static int create_encryption_context_from_policy(struct inode *inode,
- const struct fscrypt_policy *policy)
+ const struct fscrypt_policy *policy,
+ const u8 key_hash[FSCRYPT_KEY_HASH_SIZE])
{
struct fscrypt_context ctx;
@@ -69,7 +73,7 @@ static int create_encryption_context_from_policy(struct inode *inode,
BUILD_BUG_ON(sizeof(ctx.nonce) != FS_KEY_DERIVATION_NONCE_SIZE);
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
if (ctx.version != FSCRYPT_CONTEXT_V1)
- memset(ctx.key_hash, 0, FSCRYPT_KEY_HASH_SIZE);
+ memcpy(ctx.key_hash, key_hash, FSCRYPT_KEY_HASH_SIZE);
return inode->i_sb->s_cop->set_context(inode, &ctx,
fscrypt_context_size(&ctx),
@@ -82,6 +86,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
struct inode *inode = file_inode(filp);
int ret;
struct fscrypt_context ctx;
+ u8 key_hash[FSCRYPT_KEY_HASH_SIZE];
if (copy_from_user(&policy, arg, sizeof(policy)))
return -EFAULT;
@@ -100,6 +105,25 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
if (policy.flags & ~FS_POLICY_FLAGS_VALID)
return -EINVAL;
+ if (policy.version == FS_POLICY_VERSION_ORIGINAL) {
+ /*
+ * Originally no key verification was implemented, which was
+ * insufficient for scenarios where multiple users share
+ * encrypted files. The new encryption policy version fixes
+ * this and also implements an improved key derivation function.
+ * So as long as the key can be in the keyring at the time the
+ * policy is set and compatibility with old kernels isn't
+ * required, it's recommended to use the new policy version
+ * (fscrypt_policy.version = 2).
+ */
+ pr_warn_once("%s (pid %d) is setting less secure v0 encryption policy; recommend upgrading to v2.\n",
+ current->comm, current->pid);
+ } else {
+ ret = fscrypt_compute_key_hash(inode, &policy, key_hash);
+ if (ret)
+ return ret;
+ }
+
ret = mnt_want_write_file(filp);
if (ret)
return ret;
@@ -114,10 +138,12 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
ret = -ENOTEMPTY;
else
ret = create_encryption_context_from_policy(inode,
- &policy);
+ &policy,
+ key_hash);
} else if (ret >= 0 && fscrypt_valid_context_format(&ctx, ret) &&
is_encryption_context_consistent_with_policy(&ctx,
- &policy)) {
+ &policy,
+ key_hash)) {
/* The file already uses the same encryption policy. */
ret = 0;
} else if (ret >= 0 || ret == -ERANGE) {
@@ -234,7 +260,11 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
(parent_ci->ci_filename_mode ==
child_ci->ci_filename_mode) &&
- (parent_ci->ci_flags == child_ci->ci_flags);
+ (parent_ci->ci_flags == child_ci->ci_flags) &&
+ (parent_ci->ci_context_version == FSCRYPT_CONTEXT_V1 ||
+ (memcmp(parent_ci->ci_master_key->mk_hash,
+ child_ci->ci_master_key->mk_hash,
+ FSCRYPT_KEY_HASH_SIZE) == 0));
}
res = cops->get_context(parent, &parent_ctx, sizeof(parent_ctx));
@@ -253,7 +283,10 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
child_ctx.contents_encryption_mode) &&
(parent_ctx.filenames_encryption_mode ==
child_ctx.filenames_encryption_mode) &&
- (parent_ctx.flags == child_ctx.flags);
+ (parent_ctx.flags == child_ctx.flags) &&
+ (parent_ctx.version == FSCRYPT_CONTEXT_V1 ||
+ (memcmp(parent_ctx.key_hash, child_ctx.key_hash,
+ FSCRYPT_KEY_HASH_SIZE) == 0));
}
EXPORT_SYMBOL(fscrypt_has_permitted_context);
@@ -288,8 +321,10 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
memcpy(ctx.master_key_descriptor, ci->ci_master_key_descriptor,
FS_KEY_DESCRIPTOR_SIZE);
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
- if (ctx.version != FSCRYPT_CONTEXT_V1)
- memset(ctx.key_hash, 0, FSCRYPT_KEY_HASH_SIZE);
+ if (ctx.version != FSCRYPT_CONTEXT_V1) {
+ memcpy(ctx.key_hash, ci->ci_master_key->mk_hash,
+ FSCRYPT_KEY_HASH_SIZE);
+ }
BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
res = parent->i_sb->s_cop->set_context(child, &ctx,