mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-5.4.y' into android-5.4
* aosp/upstream-f2fs-stable-linux-5.4.y: fs-verity: use u64_to_user_ptr() fs-verity: use mempool for hash requests fs-verity: implement readahead of Merkle tree pages fs-verity: implement readahead for FS_IOC_ENABLE_VERITY fscrypt: improve format of no-key names ubifs: allow both hash and disk name to be provided in no-key names ubifs: don't trigger assertion on invalid no-key filename fscrypt: clarify what is meant by a per-file key fscrypt: derive dirhash key for casefolded directories fscrypt: don't allow v1 policies with casefolding fscrypt: add "fscrypt_" prefix to fname_encrypt() fscrypt: don't print name of busy file when removing key ubifs: use IS_ENCRYPTED() instead of ubifs_crypt_is_encrypted() fscrypt: document gfp_flags for bounce page allocation fscrypt: optimize fscrypt_zeroout_range() fscrypt: remove redundant bi_status check fscrypt: Allow modular crypto algorithms fscrypt: include <linux/ioctl.h> in UAPI header fscrypt: don't check for ENOKEY from fscrypt_get_encryption_info() fscrypt: remove fscrypt_is_direct_key_policy() fscrypt: move fscrypt_valid_enc_modes() to policy.c fscrypt: check for appropriate use of DIRECT_KEY flag earlier fscrypt: split up fscrypt_supported_policy() by policy version fscrypt: introduce fscrypt_needs_contents_encryption() fscrypt: move fscrypt_d_revalidate() to fname.c fscrypt: constify inode parameter to filename encryption functions fscrypt: constify struct fscrypt_hkdf parameter to fscrypt_hkdf_expand() fscrypt: verify that the crypto_skcipher has the correct ivsize fscrypt: use crypto_skcipher_driver_name() fscrypt: support passing a keyring key to FS_IOC_ADD_ENCRYPTION_KEY Conflicts: fs/crypto/Kconfig fs/crypto/bio.c fs/crypto/fname.c fs/crypto/fscrypt_private.h fs/crypto/keyring.c fs/crypto/keysetup.c fs/ubifs/dir.c include/uapi/linux/fscrypt.h Resolved the conflicts as per the corresponding android-mainline change, Ib1e6b9eda8fb5dcfc6bdc8fa89d93f72b088c5f6. Bug: 148667616 Change-Id: I5f8b846f0cd4d5403d8c61b9e12acb4581fac6f7 Signed-off-by: Eric Biggers <[email protected]>
This commit is contained in:
commit
b115f51088
33 changed files with 1185 additions and 514 deletions
|
|
@ -2,13 +2,8 @@
|
|||
config FS_ENCRYPTION
|
||||
bool "FS Encryption (Per-file encryption)"
|
||||
select CRYPTO
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_SHA512
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_HASH
|
||||
select CRYPTO_BLKCIPHER
|
||||
select KEYS
|
||||
help
|
||||
Enable encryption of files and directories. This
|
||||
|
|
@ -17,6 +12,19 @@ config FS_ENCRYPTION
|
|||
decrypted pages in the page cache. Currently Ext4,
|
||||
F2FS and UBIFS make use of this feature.
|
||||
|
||||
# Filesystems supporting encryption must select this if FS_ENCRYPTION. This
|
||||
# allows the algorithms to be built as modules when all the filesystems are.
|
||||
config FS_ENCRYPTION_ALGS
|
||||
tristate
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_SHA512
|
||||
select CRYPTO_XTS
|
||||
|
||||
config FS_ENCRYPTION_INLINE_CRYPT
|
||||
bool "Enable fscrypt to use inline crypto"
|
||||
depends on FS_ENCRYPTION && BLK_INLINE_ENCRYPTION
|
||||
|
|
|
|||
183
fs/crypto/bio.c
183
fs/crypto/bio.c
|
|
@ -41,63 +41,154 @@ void fscrypt_decrypt_bio(struct bio *bio)
|
|||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_bio);
|
||||
|
||||
int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
static int fscrypt_zeroout_range_inlinecrypt(const struct inode *inode,
|
||||
pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
const unsigned int blockbits = inode->i_blkbits;
|
||||
const unsigned int blocksize = 1 << blockbits;
|
||||
const bool inlinecrypt = fscrypt_inode_uses_inline_crypto(inode);
|
||||
struct page *ciphertext_page;
|
||||
const unsigned int blocks_per_page_bits = PAGE_SHIFT - blockbits;
|
||||
const unsigned int blocks_per_page = 1 << blocks_per_page_bits;
|
||||
unsigned int i;
|
||||
struct bio *bio;
|
||||
int ret, err = 0;
|
||||
int ret, err;
|
||||
|
||||
if (inlinecrypt) {
|
||||
ciphertext_page = ZERO_PAGE(0);
|
||||
} else {
|
||||
ciphertext_page = fscrypt_alloc_bounce_page(GFP_NOWAIT);
|
||||
if (!ciphertext_page)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
if (!inlinecrypt) {
|
||||
err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), ciphertext_page,
|
||||
blocksize, 0, GFP_NOFS);
|
||||
if (err)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
bio = bio_alloc(GFP_NOWAIT, 1);
|
||||
if (!bio) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOIO);
|
||||
/* This always succeeds since __GFP_DIRECT_RECLAIM is set. */
|
||||
bio = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
|
||||
|
||||
do {
|
||||
bio_set_dev(bio, inode->i_sb->s_bdev);
|
||||
bio->bi_iter.bi_sector = pblk << (blockbits - 9);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
ret = bio_add_page(bio, ciphertext_page, blocksize, 0);
|
||||
if (WARN_ON(ret != blocksize)) {
|
||||
/* should never happen! */
|
||||
bio_put(bio);
|
||||
err = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOFS);
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
unsigned int blocks_this_page =
|
||||
min(len, blocks_per_page);
|
||||
unsigned int bytes_this_page =
|
||||
blocks_this_page << blockbits;
|
||||
|
||||
ret = bio_add_page(bio, ZERO_PAGE(0),
|
||||
bytes_this_page, 0);
|
||||
if (WARN_ON(ret != bytes_this_page)) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
lblk += blocks_this_page;
|
||||
pblk += blocks_this_page;
|
||||
len -= blocks_this_page;
|
||||
} while (++i != BIO_MAX_PAGES && len != 0);
|
||||
|
||||
err = submit_bio_wait(bio);
|
||||
if (err == 0 && bio->bi_status)
|
||||
err = -EIO;
|
||||
bio_put(bio);
|
||||
if (err)
|
||||
goto errout;
|
||||
lblk++;
|
||||
pblk++;
|
||||
}
|
||||
goto out;
|
||||
bio_reset(bio);
|
||||
} while (len != 0);
|
||||
err = 0;
|
||||
errout:
|
||||
if (!inlinecrypt)
|
||||
fscrypt_free_bounce_page(ciphertext_page);
|
||||
out:
|
||||
bio_put(bio);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file
|
||||
* @inode: the file's inode
|
||||
* @lblk: the first file logical block to zero out
|
||||
* @pblk: the first filesystem physical block to zero out
|
||||
* @len: number of blocks to zero out
|
||||
*
|
||||
* Zero out filesystem blocks in an encrypted regular file on-disk, i.e. write
|
||||
* ciphertext blocks which decrypt to the all-zeroes block. The blocks must be
|
||||
* both logically and physically contiguous. It's also assumed that the
|
||||
* filesystem only uses a single block device, ->s_bdev.
|
||||
*
|
||||
* Note that since each block uses a different IV, this involves writing a
|
||||
* different ciphertext to each block; we can't simply reuse the same one.
|
||||
*
|
||||
* Return: 0 on success; -errno on failure.
|
||||
*/
|
||||
int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
const unsigned int blockbits = inode->i_blkbits;
|
||||
const unsigned int blocksize = 1 << blockbits;
|
||||
const unsigned int blocks_per_page_bits = PAGE_SHIFT - blockbits;
|
||||
const unsigned int blocks_per_page = 1 << blocks_per_page_bits;
|
||||
struct page *pages[16]; /* write up to 16 pages at a time */
|
||||
unsigned int nr_pages;
|
||||
unsigned int i;
|
||||
unsigned int offset;
|
||||
struct bio *bio;
|
||||
int ret, err;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (fscrypt_inode_uses_inline_crypto(inode))
|
||||
return fscrypt_zeroout_range_inlinecrypt(inode, lblk, pblk,
|
||||
len);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(pages) > BIO_MAX_PAGES);
|
||||
nr_pages = min_t(unsigned int, ARRAY_SIZE(pages),
|
||||
(len + blocks_per_page - 1) >> blocks_per_page_bits);
|
||||
|
||||
/*
|
||||
* We need at least one page for ciphertext. Allocate the first one
|
||||
* from a mempool, with __GFP_DIRECT_RECLAIM set so that it can't fail.
|
||||
*
|
||||
* Any additional page allocations are allowed to fail, as they only
|
||||
* help performance, and waiting on the mempool for them could deadlock.
|
||||
*/
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
pages[i] = fscrypt_alloc_bounce_page(i == 0 ? GFP_NOFS :
|
||||
GFP_NOWAIT | __GFP_NOWARN);
|
||||
if (!pages[i])
|
||||
break;
|
||||
}
|
||||
nr_pages = i;
|
||||
if (WARN_ON(nr_pages <= 0))
|
||||
return -EINVAL;
|
||||
|
||||
/* This always succeeds since __GFP_DIRECT_RECLAIM is set. */
|
||||
bio = bio_alloc(GFP_NOFS, nr_pages);
|
||||
|
||||
do {
|
||||
bio_set_dev(bio, inode->i_sb->s_bdev);
|
||||
bio->bi_iter.bi_sector = pblk << (blockbits - 9);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
|
||||
i = 0;
|
||||
offset = 0;
|
||||
do {
|
||||
err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), pages[i],
|
||||
blocksize, offset, GFP_NOFS);
|
||||
if (err)
|
||||
goto out;
|
||||
lblk++;
|
||||
pblk++;
|
||||
len--;
|
||||
offset += blocksize;
|
||||
if (offset == PAGE_SIZE || len == 0) {
|
||||
ret = bio_add_page(bio, pages[i++], offset, 0);
|
||||
if (WARN_ON(ret != offset)) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
offset = 0;
|
||||
}
|
||||
} while (i != nr_pages && len != 0);
|
||||
|
||||
err = submit_bio_wait(bio);
|
||||
if (err)
|
||||
goto out;
|
||||
bio_reset(bio);
|
||||
} while (len != 0);
|
||||
err = 0;
|
||||
out:
|
||||
bio_put(bio);
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
fscrypt_free_bounce_page(pages[i]);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_zeroout_range);
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/namei.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
|
|
@ -140,7 +138,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
|
|||
* multiple of the filesystem's block size.
|
||||
* @offs: Byte offset within @page of the first block to encrypt. Must be
|
||||
* a multiple of the filesystem's block size.
|
||||
* @gfp_flags: Memory allocation flags
|
||||
* @gfp_flags: Memory allocation flags. See details below.
|
||||
*
|
||||
* A new bounce page is allocated, and the specified block(s) are encrypted into
|
||||
* it. In the bounce page, the ciphertext block(s) will be located at the same
|
||||
|
|
@ -150,6 +148,11 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
|
|||
*
|
||||
* This is for use by the filesystem's ->writepages() method.
|
||||
*
|
||||
* The bounce page allocation is mempool-backed, so it will always succeed when
|
||||
* @gfp_flags includes __GFP_DIRECT_RECLAIM, e.g. when it's GFP_NOFS. However,
|
||||
* only the first page of each bio can be allocated this way. To prevent
|
||||
* deadlocks, for any additional pages a mask like GFP_NOWAIT must be used.
|
||||
*
|
||||
* Return: the new encrypted bounce page on success; an ERR_PTR() on failure
|
||||
*/
|
||||
struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
|
||||
|
|
@ -286,54 +289,6 @@ int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page,
|
|||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_block_inplace);
|
||||
|
||||
/*
|
||||
* Validate dentries in encrypted directories to make sure we aren't potentially
|
||||
* caching stale dentries after a key has been added.
|
||||
*/
|
||||
static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct dentry *dir;
|
||||
int err;
|
||||
int valid;
|
||||
|
||||
/*
|
||||
* Plaintext names are always valid, since fscrypt doesn't support
|
||||
* reverting to ciphertext names without evicting the directory's inode
|
||||
* -- which implies eviction of the dentries in the directory.
|
||||
*/
|
||||
if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Ciphertext name; valid if the directory's key is still unavailable.
|
||||
*
|
||||
* Although fscrypt forbids rename() on ciphertext names, we still must
|
||||
* use dget_parent() here rather than use ->d_parent directly. That's
|
||||
* because a corrupted fs image may contain directory hard links, which
|
||||
* the VFS handles by moving the directory's dentry tree in the dcache
|
||||
* each time ->lookup() finds the directory and it already has a dentry
|
||||
* elsewhere. Thus ->d_parent can be changing, and we must safely grab
|
||||
* a reference to some ->d_parent to prevent it from being freed.
|
||||
*/
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dget_parent(dentry);
|
||||
err = fscrypt_get_encryption_info(d_inode(dir));
|
||||
valid = !fscrypt_has_encryption_key(d_inode(dir));
|
||||
dput(dir);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
const struct dentry_operations fscrypt_d_ops = {
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
};
|
||||
|
||||
/**
|
||||
* fscrypt_initialize() - allocate major buffers for fs encryption.
|
||||
* @cop_flags: fscrypt operations flags
|
||||
|
|
|
|||
|
|
@ -11,10 +11,87 @@
|
|||
* This has not yet undergone a rigorous security audit.
|
||||
*/
|
||||
|
||||
#include <linux/namei.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/**
|
||||
* struct fscrypt_nokey_name - identifier for directory entry when key is absent
|
||||
*
|
||||
* When userspace lists an encrypted directory without access to the key, the
|
||||
* filesystem must present a unique "no-key name" for each filename that allows
|
||||
* it to find the directory entry again if requested. Naively, that would just
|
||||
* mean using the ciphertext filenames. However, since the ciphertext filenames
|
||||
* can contain illegal characters ('\0' and '/'), they must be encoded in some
|
||||
* way. We use base64. But that can cause names to exceed NAME_MAX (255
|
||||
* bytes), so we also need to use a strong hash to abbreviate long names.
|
||||
*
|
||||
* The filesystem may also need another kind of hash, the "dirhash", to quickly
|
||||
* find the directory entry. Since filesystems normally compute the dirhash
|
||||
* over the on-disk filename (i.e. the ciphertext), it's not computable from
|
||||
* no-key names that abbreviate the ciphertext using the strong hash to fit in
|
||||
* NAME_MAX. It's also not computable if it's a keyed hash taken over the
|
||||
* plaintext (but it may still be available in the on-disk directory entry);
|
||||
* casefolded directories use this type of dirhash. At least in these cases,
|
||||
* each no-key name must include the name's dirhash too.
|
||||
*
|
||||
* To meet all these requirements, we base64-encode the following
|
||||
* variable-length structure. It contains the dirhash, or 0's if the filesystem
|
||||
* didn't provide one; up to 149 bytes of the ciphertext name; and for
|
||||
* ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes.
|
||||
*
|
||||
* This ensures that each no-key name contains everything needed to find the
|
||||
* directory entry again, contains only legal characters, doesn't exceed
|
||||
* NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only
|
||||
* take the performance hit of SHA-256 on very long filenames (which are rare).
|
||||
*/
|
||||
struct fscrypt_nokey_name {
|
||||
u32 dirhash[2];
|
||||
u8 bytes[149];
|
||||
u8 sha256[SHA256_DIGEST_SIZE];
|
||||
}; /* 189 bytes => 252 bytes base64-encoded, which is <= NAME_MAX (255) */
|
||||
|
||||
/*
|
||||
* Decoded size of max-size nokey name, i.e. a name that was abbreviated using
|
||||
* the strong hash and thus includes the 'sha256' field. This isn't simply
|
||||
* sizeof(struct fscrypt_nokey_name), as the padding at the end isn't included.
|
||||
*/
|
||||
#define FSCRYPT_NOKEY_NAME_MAX offsetofend(struct fscrypt_nokey_name, sha256)
|
||||
|
||||
static struct crypto_shash *sha256_hash_tfm;
|
||||
|
||||
static int fscrypt_do_sha256(const u8 *data, unsigned int data_len, u8 *result)
|
||||
{
|
||||
struct crypto_shash *tfm = READ_ONCE(sha256_hash_tfm);
|
||||
|
||||
if (unlikely(!tfm)) {
|
||||
struct crypto_shash *prev_tfm;
|
||||
|
||||
tfm = crypto_alloc_shash("sha256", 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
fscrypt_err(NULL,
|
||||
"Error allocating SHA-256 transform: %ld",
|
||||
PTR_ERR(tfm));
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
prev_tfm = cmpxchg(&sha256_hash_tfm, NULL, tfm);
|
||||
if (prev_tfm) {
|
||||
crypto_free_shash(tfm);
|
||||
tfm = prev_tfm;
|
||||
}
|
||||
}
|
||||
{
|
||||
SHASH_DESC_ON_STACK(desc, tfm);
|
||||
|
||||
desc->tfm = tfm;
|
||||
|
||||
return crypto_shash_digest(desc, data, data_len, result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
||||
{
|
||||
if (str->len == 1 && str->name[0] == '.')
|
||||
|
|
@ -27,19 +104,19 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
|||
}
|
||||
|
||||
/**
|
||||
* fname_encrypt() - encrypt a filename
|
||||
* fscrypt_fname_encrypt() - encrypt a filename
|
||||
*
|
||||
* The output buffer must be at least as large as the input buffer.
|
||||
* Any extra space is filled with NUL padding before encryption.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fname_encrypt(struct inode *inode, const struct qstr *iname,
|
||||
u8 *out, unsigned int olen)
|
||||
int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
|
||||
u8 *out, unsigned int olen)
|
||||
{
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_CRYPTO_WAIT(wait);
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
const struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_key.tfm;
|
||||
union fscrypt_iv iv;
|
||||
struct scatterlist sg;
|
||||
|
|
@ -85,14 +162,14 @@ int fname_encrypt(struct inode *inode, const struct qstr *iname,
|
|||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static int fname_decrypt(struct inode *inode,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
static int fname_decrypt(const struct inode *inode,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_CRYPTO_WAIT(wait);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
const struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_key.tfm;
|
||||
union fscrypt_iv iv;
|
||||
int res;
|
||||
|
|
@ -206,9 +283,7 @@ int fscrypt_fname_alloc_buffer(const struct inode *inode,
|
|||
u32 max_encrypted_len,
|
||||
struct fscrypt_str *crypto_str)
|
||||
{
|
||||
const u32 max_encoded_len =
|
||||
max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
|
||||
const u32 max_encoded_len = BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX);
|
||||
u32 max_presented_len;
|
||||
|
||||
max_presented_len = max(max_encoded_len, max_encrypted_len);
|
||||
|
|
@ -241,19 +316,21 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer);
|
|||
*
|
||||
* The caller must have allocated sufficient memory for the @oname string.
|
||||
*
|
||||
* If the key is available, we'll decrypt the disk name; otherwise, we'll encode
|
||||
* it for presentation. Short names are directly base64-encoded, while long
|
||||
* names are encoded in fscrypt_digested_name format.
|
||||
* If the key is available, we'll decrypt the disk name. Otherwise, we'll
|
||||
* encode it for presentation in fscrypt_nokey_name format.
|
||||
* See struct fscrypt_nokey_name for details.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
int fscrypt_fname_disk_to_usr(const struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
struct fscrypt_digested_name digested_name;
|
||||
struct fscrypt_nokey_name nokey_name;
|
||||
u32 size; /* size of the unencoded no-key name */
|
||||
int err;
|
||||
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
|
|
@ -268,24 +345,37 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
|||
if (fscrypt_has_encryption_key(inode))
|
||||
return fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) {
|
||||
oname->len = base64_encode(iname->name, iname->len,
|
||||
oname->name);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Sanity check that struct fscrypt_nokey_name doesn't have padding
|
||||
* between fields and that its encoded size never exceeds NAME_MAX.
|
||||
*/
|
||||
BUILD_BUG_ON(offsetofend(struct fscrypt_nokey_name, dirhash) !=
|
||||
offsetof(struct fscrypt_nokey_name, bytes));
|
||||
BUILD_BUG_ON(offsetofend(struct fscrypt_nokey_name, bytes) !=
|
||||
offsetof(struct fscrypt_nokey_name, sha256));
|
||||
BUILD_BUG_ON(BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX) > NAME_MAX);
|
||||
|
||||
if (hash) {
|
||||
digested_name.hash = hash;
|
||||
digested_name.minor_hash = minor_hash;
|
||||
nokey_name.dirhash[0] = hash;
|
||||
nokey_name.dirhash[1] = minor_hash;
|
||||
} else {
|
||||
digested_name.hash = 0;
|
||||
digested_name.minor_hash = 0;
|
||||
nokey_name.dirhash[0] = 0;
|
||||
nokey_name.dirhash[1] = 0;
|
||||
}
|
||||
memcpy(digested_name.digest,
|
||||
FSCRYPT_FNAME_DIGEST(iname->name, iname->len),
|
||||
FSCRYPT_FNAME_DIGEST_SIZE);
|
||||
oname->name[0] = '_';
|
||||
oname->len = 1 + base64_encode((const u8 *)&digested_name,
|
||||
sizeof(digested_name), oname->name + 1);
|
||||
if (iname->len <= sizeof(nokey_name.bytes)) {
|
||||
memcpy(nokey_name.bytes, iname->name, iname->len);
|
||||
size = offsetof(struct fscrypt_nokey_name, bytes[iname->len]);
|
||||
} else {
|
||||
memcpy(nokey_name.bytes, iname->name, sizeof(nokey_name.bytes));
|
||||
/* Compute strong hash of remaining part of name. */
|
||||
err = fscrypt_do_sha256(&iname->name[sizeof(nokey_name.bytes)],
|
||||
iname->len - sizeof(nokey_name.bytes),
|
||||
nokey_name.sha256);
|
||||
if (err)
|
||||
return err;
|
||||
size = FSCRYPT_NOKEY_NAME_MAX;
|
||||
}
|
||||
oname->len = base64_encode((const u8 *)&nokey_name, size, oname->name);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
||||
|
|
@ -306,8 +396,7 @@ EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
|||
* get the disk_name.
|
||||
*
|
||||
* Else, for keyless @lookup operations, @iname is the presented ciphertext, so
|
||||
* we decode it to get either the ciphertext disk_name (for short names) or the
|
||||
* fscrypt_digested_name (for long names). Non-@lookup operations will be
|
||||
* we decode it to get the fscrypt_nokey_name. Non-@lookup operations will be
|
||||
* impossible in this case, so we fail them with ENOKEY.
|
||||
*
|
||||
* If successful, fscrypt_free_filename() must be called later to clean up.
|
||||
|
|
@ -317,8 +406,8 @@ EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
|||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
struct fscrypt_nokey_name *nokey_name;
|
||||
int ret;
|
||||
int digested;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
|
|
@ -342,8 +431,8 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
|||
if (!fname->crypto_buf.name)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = fname_encrypt(dir, iname, fname->crypto_buf.name,
|
||||
fname->crypto_buf.len);
|
||||
ret = fscrypt_fname_encrypt(dir, iname, fname->crypto_buf.name,
|
||||
fname->crypto_buf.len);
|
||||
if (ret)
|
||||
goto errout;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
|
|
@ -358,40 +447,31 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
|||
* We don't have the key and we are doing a lookup; decode the
|
||||
* user-supplied name
|
||||
*/
|
||||
if (iname->name[0] == '_') {
|
||||
if (iname->len !=
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)))
|
||||
return -ENOENT;
|
||||
digested = 1;
|
||||
} else {
|
||||
if (iname->len >
|
||||
BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE))
|
||||
return -ENOENT;
|
||||
digested = 0;
|
||||
}
|
||||
|
||||
fname->crypto_buf.name =
|
||||
kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE,
|
||||
sizeof(struct fscrypt_digested_name)),
|
||||
GFP_KERNEL);
|
||||
if (iname->len > BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX))
|
||||
return -ENOENT;
|
||||
|
||||
fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = base64_decode(iname->name + digested, iname->len - digested,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = base64_decode(iname->name, iname->len, fname->crypto_buf.name);
|
||||
if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) ||
|
||||
(ret > offsetof(struct fscrypt_nokey_name, sha256) &&
|
||||
ret != FSCRYPT_NOKEY_NAME_MAX)) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (digested) {
|
||||
const struct fscrypt_digested_name *n =
|
||||
(const void *)fname->crypto_buf.name;
|
||||
fname->hash = n->hash;
|
||||
fname->minor_hash = n->minor_hash;
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
|
||||
nokey_name = (void *)fname->crypto_buf.name;
|
||||
fname->hash = nokey_name->dirhash[0];
|
||||
fname->minor_hash = nokey_name->dirhash[1];
|
||||
if (ret != FSCRYPT_NOKEY_NAME_MAX) {
|
||||
/* The full ciphertext filename is available. */
|
||||
fname->disk_name.name = nokey_name->bytes;
|
||||
fname->disk_name.len =
|
||||
ret - offsetof(struct fscrypt_nokey_name, bytes);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
|
@ -400,3 +480,109 @@ errout:
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_setup_filename);
|
||||
|
||||
/**
|
||||
* fscrypt_match_name() - test whether the given name matches a directory entry
|
||||
* @fname: the name being searched for
|
||||
* @de_name: the name from the directory entry
|
||||
* @de_name_len: the length of @de_name in bytes
|
||||
*
|
||||
* Normally @fname->disk_name will be set, and in that case we simply compare
|
||||
* that to the name stored in the directory entry. The only exception is that
|
||||
* if we don't have the key for an encrypted directory and the name we're
|
||||
* looking for is very long, then we won't have the full disk_name and instead
|
||||
* we'll need to match against a fscrypt_nokey_name that includes a strong hash.
|
||||
*
|
||||
* Return: %true if the name matches, otherwise %false.
|
||||
*/
|
||||
bool fscrypt_match_name(const struct fscrypt_name *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
const struct fscrypt_nokey_name *nokey_name =
|
||||
(const void *)fname->crypto_buf.name;
|
||||
u8 sha256[SHA256_DIGEST_SIZE];
|
||||
|
||||
if (likely(fname->disk_name.name)) {
|
||||
if (de_name_len != fname->disk_name.len)
|
||||
return false;
|
||||
return !memcmp(de_name, fname->disk_name.name, de_name_len);
|
||||
}
|
||||
if (de_name_len <= sizeof(nokey_name->bytes))
|
||||
return false;
|
||||
if (memcmp(de_name, nokey_name->bytes, sizeof(nokey_name->bytes)))
|
||||
return false;
|
||||
if (fscrypt_do_sha256(&de_name[sizeof(nokey_name->bytes)],
|
||||
de_name_len - sizeof(nokey_name->bytes), sha256))
|
||||
return false;
|
||||
return !memcmp(sha256, nokey_name->sha256, sizeof(sha256));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_match_name);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_siphash() - calculate the SipHash of a filename
|
||||
* @dir: the parent directory
|
||||
* @name: the filename to calculate the SipHash of
|
||||
*
|
||||
* Given a plaintext filename @name and a directory @dir which uses SipHash as
|
||||
* its dirhash method and has had its fscrypt key set up, this function
|
||||
* calculates the SipHash of that name using the directory's secret dirhash key.
|
||||
*
|
||||
* Return: the SipHash of @name using the hash key of @dir
|
||||
*/
|
||||
u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name)
|
||||
{
|
||||
const struct fscrypt_info *ci = dir->i_crypt_info;
|
||||
|
||||
WARN_ON(!ci->ci_dirhash_key_initialized);
|
||||
|
||||
return siphash(name->name, name->len, &ci->ci_dirhash_key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_fname_siphash);
|
||||
|
||||
/*
|
||||
* Validate dentries in encrypted directories to make sure we aren't potentially
|
||||
* caching stale dentries after a key has been added.
|
||||
*/
|
||||
static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct dentry *dir;
|
||||
int err;
|
||||
int valid;
|
||||
|
||||
/*
|
||||
* Plaintext names are always valid, since fscrypt doesn't support
|
||||
* reverting to ciphertext names without evicting the directory's inode
|
||||
* -- which implies eviction of the dentries in the directory.
|
||||
*/
|
||||
if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Ciphertext name; valid if the directory's key is still unavailable.
|
||||
*
|
||||
* Although fscrypt forbids rename() on ciphertext names, we still must
|
||||
* use dget_parent() here rather than use ->d_parent directly. That's
|
||||
* because a corrupted fs image may contain directory hard links, which
|
||||
* the VFS handles by moving the directory's dentry tree in the dcache
|
||||
* each time ->lookup() finds the directory and it already has a dentry
|
||||
* elsewhere. Thus ->d_parent can be changing, and we must safely grab
|
||||
* a reference to some ->d_parent to prevent it from being freed.
|
||||
*/
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dget_parent(dentry);
|
||||
err = fscrypt_get_encryption_info(d_inode(dir));
|
||||
valid = !fscrypt_has_encryption_key(d_inode(dir));
|
||||
dput(dir);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
const struct dentry_operations fscrypt_d_ops = {
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#define _FSCRYPT_PRIVATE_H
|
||||
|
||||
#include <linux/fscrypt.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <linux/bio-crypt-ctx.h>
|
||||
|
||||
|
|
@ -138,12 +139,6 @@ fscrypt_policy_flags(const union fscrypt_policy *policy)
|
|||
BUG();
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fscrypt_is_direct_key_policy(const union fscrypt_policy *policy)
|
||||
{
|
||||
return fscrypt_policy_flags(policy) & FSCRYPT_POLICY_FLAG_DIRECT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
|
|
@ -218,6 +213,14 @@ struct fscrypt_info {
|
|||
*/
|
||||
struct fscrypt_direct_key *ci_direct_key;
|
||||
|
||||
/*
|
||||
* This inode's hash key for filenames. This is a 128-bit SipHash-2-4
|
||||
* key. This is only set for directories that use a keyed dirhash over
|
||||
* the plaintext filenames -- currently just casefolded directories.
|
||||
*/
|
||||
siphash_key_t ci_dirhash_key;
|
||||
bool ci_dirhash_key_initialized;
|
||||
|
||||
/* The encryption policy used by this inode */
|
||||
union fscrypt_policy ci_policy;
|
||||
|
||||
|
|
@ -230,24 +233,6 @@ typedef enum {
|
|||
FS_ENCRYPT,
|
||||
} fscrypt_direction_t;
|
||||
|
||||
static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
|
||||
u32 filenames_mode)
|
||||
{
|
||||
if (contents_mode == FSCRYPT_MODE_AES_128_CBC &&
|
||||
filenames_mode == FSCRYPT_MODE_AES_128_CTS)
|
||||
return true;
|
||||
|
||||
if (contents_mode == FSCRYPT_MODE_AES_256_XTS &&
|
||||
filenames_mode == FSCRYPT_MODE_AES_256_CTS)
|
||||
return true;
|
||||
|
||||
if (contents_mode == FSCRYPT_MODE_ADIANTUM &&
|
||||
filenames_mode == FSCRYPT_MODE_ADIANTUM)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* crypto.c */
|
||||
extern struct kmem_cache *fscrypt_info_cachep;
|
||||
extern int fscrypt_initialize(unsigned int cop_flags);
|
||||
|
|
@ -257,7 +242,6 @@ extern int fscrypt_crypt_block(const struct inode *inode,
|
|||
unsigned int len, unsigned int offs,
|
||||
gfp_t gfp_flags);
|
||||
extern struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags);
|
||||
extern const struct dentry_operations fscrypt_d_ops;
|
||||
|
||||
extern void __printf(3, 4) __cold
|
||||
fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...);
|
||||
|
|
@ -285,11 +269,13 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
|
|||
const struct fscrypt_info *ci);
|
||||
|
||||
/* fname.c */
|
||||
extern int fname_encrypt(struct inode *inode, const struct qstr *iname,
|
||||
u8 *out, unsigned int olen);
|
||||
extern int fscrypt_fname_encrypt(const struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
u8 *out, unsigned int olen);
|
||||
extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
|
||||
u32 orig_len, u32 max_len,
|
||||
u32 *encrypted_len_ret);
|
||||
extern const struct dentry_operations fscrypt_d_ops;
|
||||
|
||||
/* hkdf.c */
|
||||
|
||||
|
|
@ -308,11 +294,12 @@ extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
|
|||
* output doesn't reveal another.
|
||||
*/
|
||||
#define HKDF_CONTEXT_KEY_IDENTIFIER 1
|
||||
#define HKDF_CONTEXT_PER_FILE_KEY 2
|
||||
#define HKDF_CONTEXT_PER_FILE_ENC_KEY 2
|
||||
#define HKDF_CONTEXT_DIRECT_KEY 3
|
||||
#define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4
|
||||
#define HKDF_CONTEXT_DIRHASH_KEY 5
|
||||
|
||||
extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context,
|
||||
extern int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
|
||||
const u8 *info, unsigned int infolen,
|
||||
u8 *okm, unsigned int okmlen);
|
||||
|
||||
|
|
@ -568,20 +555,17 @@ struct fscrypt_mode {
|
|||
|
||||
extern struct fscrypt_mode fscrypt_modes[];
|
||||
|
||||
static inline bool
|
||||
fscrypt_mode_supports_direct_key(const struct fscrypt_mode *mode)
|
||||
{
|
||||
return mode->ivsize >= offsetofend(union fscrypt_iv, nonce);
|
||||
}
|
||||
|
||||
extern int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
|
||||
const u8 *raw_key, unsigned int raw_key_size,
|
||||
const struct fscrypt_info *ci);
|
||||
|
||||
extern void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key);
|
||||
|
||||
extern int fscrypt_set_derived_key(struct fscrypt_info *ci,
|
||||
const u8 *derived_key);
|
||||
extern int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci,
|
||||
const u8 *raw_key);
|
||||
|
||||
extern int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
|
||||
const struct fscrypt_master_key *mk);
|
||||
|
||||
/* keysetup_v1.c */
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ out:
|
|||
* adds to its application-specific info strings to guarantee that it doesn't
|
||||
* accidentally repeat an info string when using HKDF for different purposes.)
|
||||
*/
|
||||
int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context,
|
||||
int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
|
||||
const u8 *info, unsigned int infolen,
|
||||
u8 *okm, unsigned int okmlen)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
* Encryption hooks for higher-level filesystem operations.
|
||||
*/
|
||||
|
||||
#include <linux/key.h>
|
||||
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/**
|
||||
|
|
@ -122,6 +124,48 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
|
||||
|
||||
/**
|
||||
* fscrypt_prepare_setflags() - prepare to change flags with FS_IOC_SETFLAGS
|
||||
* @inode: the inode on which flags are being changed
|
||||
* @oldflags: the old flags
|
||||
* @flags: the new flags
|
||||
*
|
||||
* The caller should be holding i_rwsem for write.
|
||||
*
|
||||
* Return: 0 on success; -errno if the flags change isn't allowed or if
|
||||
* another error occurs.
|
||||
*/
|
||||
int fscrypt_prepare_setflags(struct inode *inode,
|
||||
unsigned int oldflags, unsigned int flags)
|
||||
{
|
||||
struct fscrypt_info *ci;
|
||||
struct fscrypt_master_key *mk;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* When the CASEFOLD flag is set on an encrypted directory, we must
|
||||
* derive the secret key needed for the dirhash. This is only possible
|
||||
* if the directory uses a v2 encryption policy.
|
||||
*/
|
||||
if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL)) {
|
||||
err = fscrypt_require_key(inode);
|
||||
if (err)
|
||||
return err;
|
||||
ci = inode->i_crypt_info;
|
||||
if (ci->ci_policy.version != FSCRYPT_POLICY_V2)
|
||||
return -EINVAL;
|
||||
mk = ci->ci_master_key->payload.data[0];
|
||||
down_read(&mk->mk_secret_sem);
|
||||
if (is_master_key_secret_present(&mk->mk_secret))
|
||||
err = fscrypt_derive_dirhash_key(ci, mk);
|
||||
else
|
||||
err = -ENOKEY;
|
||||
up_read(&mk->mk_secret_sem);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
|
||||
unsigned int max_len,
|
||||
struct fscrypt_str *disk_link)
|
||||
|
|
@ -188,7 +232,8 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
|
|||
ciphertext_len = disk_link->len - sizeof(*sd);
|
||||
sd->len = cpu_to_le16(ciphertext_len);
|
||||
|
||||
err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len);
|
||||
err = fscrypt_fname_encrypt(inode, &iname, sd->encrypted_path,
|
||||
ciphertext_len);
|
||||
if (err)
|
||||
goto err_free_sd;
|
||||
|
||||
|
|
|
|||
|
|
@ -465,6 +465,111 @@ out_unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
const struct fscrypt_provisioning_key_payload *payload = prep->data;
|
||||
|
||||
BUILD_BUG_ON(FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE < FSCRYPT_MAX_KEY_SIZE);
|
||||
|
||||
if (prep->datalen < sizeof(*payload) + FSCRYPT_MIN_KEY_SIZE ||
|
||||
prep->datalen > sizeof(*payload) + FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
|
||||
payload->type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER)
|
||||
return -EINVAL;
|
||||
|
||||
if (payload->__reserved)
|
||||
return -EINVAL;
|
||||
|
||||
prep->payload.data[0] = kmemdup(payload, prep->datalen, GFP_KERNEL);
|
||||
if (!prep->payload.data[0])
|
||||
return -ENOMEM;
|
||||
|
||||
prep->quotalen = prep->datalen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fscrypt_provisioning_key_free_preparse(
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
kzfree(prep->payload.data[0]);
|
||||
}
|
||||
|
||||
static void fscrypt_provisioning_key_describe(const struct key *key,
|
||||
struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, key->description);
|
||||
if (key_is_positive(key)) {
|
||||
const struct fscrypt_provisioning_key_payload *payload =
|
||||
key->payload.data[0];
|
||||
|
||||
seq_printf(m, ": %u [%u]", key->datalen, payload->type);
|
||||
}
|
||||
}
|
||||
|
||||
static void fscrypt_provisioning_key_destroy(struct key *key)
|
||||
{
|
||||
kzfree(key->payload.data[0]);
|
||||
}
|
||||
|
||||
static struct key_type key_type_fscrypt_provisioning = {
|
||||
.name = "fscrypt-provisioning",
|
||||
.preparse = fscrypt_provisioning_key_preparse,
|
||||
.free_preparse = fscrypt_provisioning_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.describe = fscrypt_provisioning_key_describe,
|
||||
.destroy = fscrypt_provisioning_key_destroy,
|
||||
};
|
||||
|
||||
/*
|
||||
* Retrieve the raw key from the Linux keyring key specified by 'key_id', and
|
||||
* store it into 'secret'.
|
||||
*
|
||||
* The key must be of type "fscrypt-provisioning" and must have the field
|
||||
* fscrypt_provisioning_key_payload::type set to 'type', indicating that it's
|
||||
* only usable with fscrypt with the particular KDF version identified by
|
||||
* 'type'. We don't use the "logon" key type because there's no way to
|
||||
* completely restrict the use of such keys; they can be used by any kernel API
|
||||
* that accepts "logon" keys and doesn't require a specific service prefix.
|
||||
*
|
||||
* The ability to specify the key via Linux keyring key is intended for cases
|
||||
* where userspace needs to re-add keys after the filesystem is unmounted and
|
||||
* re-mounted. Most users should just provide the raw key directly instead.
|
||||
*/
|
||||
static int get_keyring_key(u32 key_id, u32 type,
|
||||
struct fscrypt_master_key_secret *secret)
|
||||
{
|
||||
key_ref_t ref;
|
||||
struct key *key;
|
||||
const struct fscrypt_provisioning_key_payload *payload;
|
||||
int err;
|
||||
|
||||
ref = lookup_user_key(key_id, 0, KEY_NEED_SEARCH);
|
||||
if (IS_ERR(ref))
|
||||
return PTR_ERR(ref);
|
||||
key = key_ref_to_ptr(ref);
|
||||
|
||||
if (key->type != &key_type_fscrypt_provisioning)
|
||||
goto bad_key;
|
||||
payload = key->payload.data[0];
|
||||
|
||||
/* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */
|
||||
if (payload->type != type)
|
||||
goto bad_key;
|
||||
|
||||
secret->size = key->datalen - sizeof(*payload);
|
||||
memcpy(secret->raw, payload->raw, secret->size);
|
||||
err = 0;
|
||||
goto out_put;
|
||||
|
||||
bad_key:
|
||||
err = -EKEYREJECTED;
|
||||
out_put:
|
||||
key_ref_put(ref);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Size of software "secret" derived from hardware-wrapped key */
|
||||
#define RAW_SECRET_SIZE 32
|
||||
|
||||
|
|
@ -512,20 +617,28 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
|
|||
if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
|
||||
return -EINVAL;
|
||||
|
||||
BUILD_BUG_ON(FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE <
|
||||
FSCRYPT_MAX_KEY_SIZE);
|
||||
|
||||
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
|
||||
arg.raw_size >
|
||||
((arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ?
|
||||
FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_KEY_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&secret, 0, sizeof(secret));
|
||||
secret.size = arg.raw_size;
|
||||
err = -EFAULT;
|
||||
if (copy_from_user(secret.raw, uarg->raw, secret.size))
|
||||
goto out_wipe_secret;
|
||||
if (arg.key_id) {
|
||||
if (arg.raw_size != 0)
|
||||
return -EINVAL;
|
||||
err = get_keyring_key(arg.key_id, arg.key_spec.type, &secret);
|
||||
if (err)
|
||||
goto out_wipe_secret;
|
||||
err = -EINVAL;
|
||||
if (!(arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) &&
|
||||
secret.size > FSCRYPT_MAX_KEY_SIZE)
|
||||
goto out_wipe_secret;
|
||||
} else {
|
||||
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
|
||||
arg.raw_size >
|
||||
((arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ?
|
||||
FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_KEY_SIZE))
|
||||
return -EINVAL;
|
||||
secret.size = arg.raw_size;
|
||||
err = -EFAULT;
|
||||
if (copy_from_user(secret.raw, uarg->raw, secret.size))
|
||||
goto out_wipe_secret;
|
||||
}
|
||||
|
||||
switch (arg.key_spec.type) {
|
||||
case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR:
|
||||
|
|
@ -1019,8 +1132,14 @@ int __init fscrypt_init_keyring(void)
|
|||
if (err)
|
||||
goto err_unregister_fscrypt;
|
||||
|
||||
err = register_key_type(&key_type_fscrypt_provisioning);
|
||||
if (err)
|
||||
goto err_unregister_fscrypt_user;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_fscrypt_user:
|
||||
unregister_key_type(&key_type_fscrypt_user);
|
||||
err_unregister_fscrypt:
|
||||
unregister_key_type(&key_type_fscrypt);
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -92,8 +92,11 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
|
|||
* first time a mode is used.
|
||||
*/
|
||||
pr_info("fscrypt: %s using implementation \"%s\"\n",
|
||||
mode->friendly_name,
|
||||
crypto_skcipher_alg(tfm)->base.cra_driver_name);
|
||||
mode->friendly_name, crypto_skcipher_driver_name(tfm));
|
||||
}
|
||||
if (WARN_ON(crypto_skcipher_ivsize(tfm) != mode->ivsize)) {
|
||||
err = -EINVAL;
|
||||
goto err_free_tfm;
|
||||
}
|
||||
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
|
||||
err = crypto_skcipher_setkey(tfm, raw_key, mode->keysize);
|
||||
|
|
@ -143,18 +146,18 @@ void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key)
|
|||
fscrypt_destroy_inline_crypt_key(prep_key);
|
||||
}
|
||||
|
||||
/* Given the per-file key, set up the file's crypto transform object */
|
||||
int fscrypt_set_derived_key(struct fscrypt_info *ci, const u8 *derived_key)
|
||||
/* Given a per-file encryption key, set up the file's crypto transform object */
|
||||
int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
|
||||
{
|
||||
ci->ci_owns_key = true;
|
||||
return fscrypt_prepare_key(&ci->ci_key, derived_key,
|
||||
return fscrypt_prepare_key(&ci->ci_key, raw_key,
|
||||
ci->ci_mode->keysize, ci);
|
||||
}
|
||||
|
||||
static int setup_per_mode_key(struct fscrypt_info *ci,
|
||||
struct fscrypt_master_key *mk,
|
||||
struct fscrypt_prepared_key *keys,
|
||||
u8 hkdf_context, bool include_fs_uuid)
|
||||
static int setup_per_mode_enc_key(struct fscrypt_info *ci,
|
||||
struct fscrypt_master_key *mk,
|
||||
struct fscrypt_prepared_key *keys,
|
||||
u8 hkdf_context, bool include_fs_uuid)
|
||||
{
|
||||
static DEFINE_MUTEX(mode_key_setup_mutex);
|
||||
const struct inode *inode = ci->ci_inode;
|
||||
|
|
@ -231,10 +234,24 @@ out_unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
|
||||
const struct fscrypt_master_key *mk)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_DIRHASH_KEY,
|
||||
ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE,
|
||||
(u8 *)&ci->ci_dirhash_key,
|
||||
sizeof(ci->ci_dirhash_key));
|
||||
if (err)
|
||||
return err;
|
||||
ci->ci_dirhash_key_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
|
||||
struct fscrypt_master_key *mk)
|
||||
{
|
||||
u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
|
||||
int err;
|
||||
|
||||
if (mk->mk_secret.is_hw_wrapped &&
|
||||
|
|
@ -246,21 +263,15 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
|
|||
|
||||
if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
|
||||
/*
|
||||
* DIRECT_KEY: instead of deriving per-file keys, the per-file
|
||||
* nonce will be included in all the IVs. But unlike v1
|
||||
* policies, for v2 policies in this case we don't encrypt with
|
||||
* the master key directly but rather derive a per-mode key.
|
||||
* This ensures that the master key is consistently used only
|
||||
* for HKDF, avoiding key reuse issues.
|
||||
* DIRECT_KEY: instead of deriving per-file encryption keys, the
|
||||
* per-file nonce will be included in all the IVs. But unlike
|
||||
* v1 policies, for v2 policies in this case we don't encrypt
|
||||
* with the master key directly but rather derive a per-mode
|
||||
* encryption key. This ensures that the master key is
|
||||
* consistently used only for HKDF, avoiding key reuse issues.
|
||||
*/
|
||||
if (!fscrypt_mode_supports_direct_key(ci->ci_mode)) {
|
||||
fscrypt_warn(ci->ci_inode,
|
||||
"Direct key flag not allowed with %s",
|
||||
ci->ci_mode->friendly_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
return setup_per_mode_key(ci, mk, mk->mk_direct_keys,
|
||||
HKDF_CONTEXT_DIRECT_KEY, false);
|
||||
err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys,
|
||||
HKDF_CONTEXT_DIRECT_KEY, false);
|
||||
} else if (ci->ci_policy.v2.flags &
|
||||
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
|
||||
/*
|
||||
|
|
@ -269,21 +280,34 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
|
|||
* the IVs. This format is optimized for use with inline
|
||||
* encryption hardware compliant with the UFS or eMMC standards.
|
||||
*/
|
||||
return setup_per_mode_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
|
||||
HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
|
||||
true);
|
||||
}
|
||||
err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
|
||||
HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
|
||||
true);
|
||||
} else {
|
||||
u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
|
||||
|
||||
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
|
||||
HKDF_CONTEXT_PER_FILE_KEY,
|
||||
ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE,
|
||||
derived_key, ci->ci_mode->keysize);
|
||||
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
|
||||
HKDF_CONTEXT_PER_FILE_ENC_KEY,
|
||||
ci->ci_nonce,
|
||||
FS_KEY_DERIVATION_NONCE_SIZE,
|
||||
derived_key, ci->ci_mode->keysize);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fscrypt_set_per_file_enc_key(ci, derived_key);
|
||||
memzero_explicit(derived_key, ci->ci_mode->keysize);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fscrypt_set_derived_key(ci, derived_key);
|
||||
memzero_explicit(derived_key, ci->ci_mode->keysize);
|
||||
return err;
|
||||
/* Derive a secret dirhash key for directories that need it. */
|
||||
if (S_ISDIR(ci->ci_inode->i_mode) && IS_CASEFOLDED(ci->ci_inode)) {
|
||||
err = fscrypt_derive_dirhash_key(ci, mk);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
* This file implements compatibility functions for the original encryption
|
||||
* policy version ("v1"), including:
|
||||
*
|
||||
* - Deriving per-file keys using the AES-128-ECB based KDF
|
||||
* - Deriving per-file encryption keys using the AES-128-ECB based KDF
|
||||
* (rather than the new method of using HKDF-SHA512)
|
||||
*
|
||||
* - Retrieving fscrypt master keys from process-subscribed keyrings
|
||||
|
|
@ -252,23 +252,8 @@ err_free_dk:
|
|||
static int setup_v1_file_key_direct(struct fscrypt_info *ci,
|
||||
const u8 *raw_master_key)
|
||||
{
|
||||
const struct fscrypt_mode *mode = ci->ci_mode;
|
||||
struct fscrypt_direct_key *dk;
|
||||
|
||||
if (!fscrypt_mode_supports_direct_key(mode)) {
|
||||
fscrypt_warn(ci->ci_inode,
|
||||
"Direct key mode not allowed with %s",
|
||||
mode->friendly_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ci->ci_policy.v1.contents_encryption_mode !=
|
||||
ci->ci_policy.v1.filenames_encryption_mode) {
|
||||
fscrypt_warn(ci->ci_inode,
|
||||
"Direct key mode not allowed with different contents and filenames modes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dk = fscrypt_get_direct_key(ci, raw_master_key);
|
||||
if (IS_ERR(dk))
|
||||
return PTR_ERR(dk);
|
||||
|
|
@ -297,7 +282,7 @@ static int setup_v1_file_key_derived(struct fscrypt_info *ci,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
err = fscrypt_set_derived_key(ci, derived_key);
|
||||
err = fscrypt_set_per_file_enc_key(ci, derived_key);
|
||||
out:
|
||||
kzfree(derived_key);
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,43 @@ bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
|
|||
return !memcmp(policy1, policy2, fscrypt_policy_size(policy1));
|
||||
}
|
||||
|
||||
static bool fscrypt_valid_enc_modes(u32 contents_mode, u32 filenames_mode)
|
||||
{
|
||||
if (contents_mode == FSCRYPT_MODE_AES_256_XTS &&
|
||||
filenames_mode == FSCRYPT_MODE_AES_256_CTS)
|
||||
return true;
|
||||
|
||||
if (contents_mode == FSCRYPT_MODE_AES_128_CBC &&
|
||||
filenames_mode == FSCRYPT_MODE_AES_128_CTS)
|
||||
return true;
|
||||
|
||||
if (contents_mode == FSCRYPT_MODE_ADIANTUM &&
|
||||
filenames_mode == FSCRYPT_MODE_ADIANTUM)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool supported_direct_key_modes(const struct inode *inode,
|
||||
u32 contents_mode, u32 filenames_mode)
|
||||
{
|
||||
const struct fscrypt_mode *mode;
|
||||
|
||||
if (contents_mode != filenames_mode) {
|
||||
fscrypt_warn(inode,
|
||||
"Direct key flag not allowed with different contents and filenames modes");
|
||||
return false;
|
||||
}
|
||||
mode = &fscrypt_modes[contents_mode];
|
||||
|
||||
if (mode->ivsize < offsetofend(union fscrypt_iv, nonce)) {
|
||||
fscrypt_warn(inode, "Direct key flag not allowed with %s",
|
||||
mode->friendly_name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool supported_iv_ino_lblk_64_policy(
|
||||
const struct fscrypt_policy_v2 *policy,
|
||||
const struct inode *inode)
|
||||
|
|
@ -63,13 +100,82 @@ static bool supported_iv_ino_lblk_64_policy(
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
|
||||
const struct inode *inode)
|
||||
{
|
||||
if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode)) {
|
||||
fscrypt_warn(inode,
|
||||
"Unsupported encryption modes (contents %d, filenames %d)",
|
||||
policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK |
|
||||
FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
|
||||
fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)",
|
||||
policy->flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
|
||||
!supported_direct_key_modes(inode, policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode))
|
||||
return false;
|
||||
|
||||
if (IS_CASEFOLDED(inode)) {
|
||||
/* With v1, there's no way to derive dirhash keys. */
|
||||
fscrypt_warn(inode,
|
||||
"v1 policies can't be used on casefolded directories");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
|
||||
const struct inode *inode)
|
||||
{
|
||||
if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode)) {
|
||||
fscrypt_warn(inode,
|
||||
"Unsupported encryption modes (contents %d, filenames %d)",
|
||||
policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) {
|
||||
fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)",
|
||||
policy->flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
|
||||
!supported_direct_key_modes(inode, policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode))
|
||||
return false;
|
||||
|
||||
if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) &&
|
||||
!supported_iv_ino_lblk_64_policy(policy, inode))
|
||||
return false;
|
||||
|
||||
if (memchr_inv(policy->__reserved, 0, sizeof(policy->__reserved))) {
|
||||
fscrypt_warn(inode, "Reserved bits set in encryption policy");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscrypt_supported_policy - check whether an encryption policy is supported
|
||||
*
|
||||
* Given an encryption policy, check whether all its encryption modes and other
|
||||
* settings are supported by this kernel. (But we don't currently don't check
|
||||
* for crypto API support here, so attempting to use an algorithm not configured
|
||||
* into the crypto API will still fail later.)
|
||||
* settings are supported by this kernel on the given inode. (But we don't
|
||||
* currently don't check for crypto API support here, so attempting to use an
|
||||
* algorithm not configured into the crypto API will still fail later.)
|
||||
*
|
||||
* Return: %true if supported, else %false
|
||||
*/
|
||||
|
|
@ -77,60 +183,10 @@ bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
|
|||
const struct inode *inode)
|
||||
{
|
||||
switch (policy_u->version) {
|
||||
case FSCRYPT_POLICY_V1: {
|
||||
const struct fscrypt_policy_v1 *policy = &policy_u->v1;
|
||||
|
||||
if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode)) {
|
||||
fscrypt_warn(inode,
|
||||
"Unsupported encryption modes (contents %d, filenames %d)",
|
||||
policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK |
|
||||
FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
|
||||
fscrypt_warn(inode,
|
||||
"Unsupported encryption flags (0x%02x)",
|
||||
policy->flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case FSCRYPT_POLICY_V2: {
|
||||
const struct fscrypt_policy_v2 *policy = &policy_u->v2;
|
||||
|
||||
if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode)) {
|
||||
fscrypt_warn(inode,
|
||||
"Unsupported encryption modes (contents %d, filenames %d)",
|
||||
policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) {
|
||||
fscrypt_warn(inode,
|
||||
"Unsupported encryption flags (0x%02x)",
|
||||
policy->flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) &&
|
||||
!supported_iv_ino_lblk_64_policy(policy, inode))
|
||||
return false;
|
||||
|
||||
if (memchr_inv(policy->__reserved, 0,
|
||||
sizeof(policy->__reserved))) {
|
||||
fscrypt_warn(inode,
|
||||
"Reserved bits set in encryption policy");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case FSCRYPT_POLICY_V1:
|
||||
return fscrypt_supported_v1_policy(&policy_u->v1, inode);
|
||||
case FSCRYPT_POLICY_V2:
|
||||
return fscrypt_supported_v2_policy(&policy_u->v2, inode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue