Only bug fixes and cleanups for this merge window.
-----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAlkPYHkACgkQ8vlZVpUN gaM97ggAlOm8n/tlbcdonX/+HHjlnqcy5uYD7A9AH/JordpRzy4eqcMbxMG39p1R DBtjo9Y0i3iFEGajRc0h7KXDLeTBUQ/JZpR8H60MFfAQHnTowuI91eb3/6QeZiHh CN/2KKzpYitPIEUfEHnVeYKOfvrzR7je5hrEiAwEkPeKv7XyrNVM0LHQ/jKpbQwg ntIzHvxjQyo8plx/m5S4Yew7tqjYpNiq4plmyk/Vxtw2FmB/FC76UxYeadoB3EI5 etw+bCORB0tFZO27o56kXywg+mDcp7HEtVvq9LG28oEuBDAVKNoeKEvV7SiOBlZp +HnqIz5Hx1UTxOlTAc10IjvEhriEuw== =qCDl -----END PGP SIGNATURE----- Merge tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt Pull fscrypt updates from Ted Ts'o: "Only bug fixes and cleanups for this merge window" * tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt: fscrypt: correct collision claim for digested names MAINTAINERS: fscrypt: update mailing list, patchwork, and git ext4: clean up ext4_match() and callers f2fs: switch to using fscrypt_match_name() ext4: switch to using fscrypt_match_name() fscrypt: introduce helper function for filename matching fscrypt: avoid collisions when presenting long encrypted filenames f2fs: check entire encrypted bigname when finding a dentry ubifs: check for consistent encryption contexts in ubifs_lookup() f2fs: sync f2fs_lookup() with ext4_lookup() ext4: remove "nokey" check from ext4_lookup() fscrypt: fix context consistency check when key(s) unavailable fscrypt: Remove __packed from fscrypt_policy fscrypt: Move key structure and constants to uapi fscrypt: remove fscrypt_symlink_data_len() fscrypt: remove unnecessary checks for NULL operations
This commit is contained in:
commit
677375cef8
@ -5417,10 +5417,12 @@ F: Documentation/filesystems/caching/
|
||||
F: fs/fscache/
|
||||
F: include/linux/fscache*.h
|
||||
|
||||
FS-CRYPTO: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
|
||||
FSCRYPT: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
|
||||
M: Theodore Y. Ts'o <tytso@mit.edu>
|
||||
M: Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
L: linux-fscrypt@vger.kernel.org
|
||||
Q: https://patchwork.kernel.org/project/linux-fscrypt/list/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt.git
|
||||
S: Supported
|
||||
F: fs/crypto/
|
||||
F: include/linux/fscrypt*.h
|
||||
|
@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode,
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
|
||||
|
||||
/**
|
||||
* digest_encode() -
|
||||
*
|
||||
@ -230,11 +232,14 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
|
||||
int fscrypt_fname_alloc_buffer(const struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
u32 olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
const u32 max_encoded_len =
|
||||
max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
|
||||
|
||||
crypto_str->len = olen;
|
||||
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
olen = max(olen, max_encoded_len);
|
||||
|
||||
/*
|
||||
* Allocated buffer can hold one more character to null-terminate the
|
||||
* string
|
||||
@ -266,6 +271,10 @@ 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.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
@ -274,7 +283,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
char buf[24];
|
||||
struct fscrypt_digested_name digested_name;
|
||||
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
if (inode->i_crypt_info)
|
||||
return fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) {
|
||||
oname->len = digest_encode(iname->name, iname->len,
|
||||
oname->name);
|
||||
return 0;
|
||||
}
|
||||
if (hash) {
|
||||
memcpy(buf, &hash, 4);
|
||||
memcpy(buf + 4, &minor_hash, 4);
|
||||
digested_name.hash = hash;
|
||||
digested_name.minor_hash = minor_hash;
|
||||
} else {
|
||||
memset(buf, 0, 8);
|
||||
digested_name.hash = 0;
|
||||
digested_name.minor_hash = 0;
|
||||
}
|
||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||
memcpy(digested_name.digest,
|
||||
FSCRYPT_FNAME_DIGEST(iname->name, iname->len),
|
||||
FSCRYPT_FNAME_DIGEST_SIZE);
|
||||
oname->name[0] = '_';
|
||||
oname->len = 1 + digest_encode(buf, 24, oname->name + 1);
|
||||
oname->len = 1 + digest_encode((const char *)&digested_name,
|
||||
sizeof(digested_name), oname->name + 1);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
||||
@ -336,10 +349,35 @@ int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||
|
||||
/**
|
||||
* fscrypt_setup_filename() - prepare to search a possibly encrypted directory
|
||||
* @dir: the directory that will be searched
|
||||
* @iname: the user-provided filename being searched for
|
||||
* @lookup: 1 if we're allowed to proceed without the key because it's
|
||||
* ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot
|
||||
* proceed without the key because we're going to create the dir_entry.
|
||||
* @fname: the filename information to be filled in
|
||||
*
|
||||
* Given a user-provided filename @iname, this function sets @fname->disk_name
|
||||
* to the name that would be stored in the on-disk directory entry, if possible.
|
||||
* If the directory is unencrypted this is simply @iname. Else, if we have the
|
||||
* directory's encryption key, then @iname is the plaintext, so we encrypt it to
|
||||
* 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
|
||||
* impossible in this case, so we fail them with ENOKEY.
|
||||
*
|
||||
* If successful, fscrypt_free_filename() must be called later to clean up.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
int ret = 0, bigname = 0;
|
||||
int ret;
|
||||
int digested;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
@ -373,25 +411,37 @@ 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] == '_')
|
||||
bigname = 1;
|
||||
if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
|
||||
return -ENOENT;
|
||||
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(32, GFP_KERNEL);
|
||||
fname->crypto_buf.name =
|
||||
kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE,
|
||||
sizeof(struct fscrypt_digested_name)),
|
||||
GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
ret = digest_decode(iname->name + digested, iname->len - digested,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (bigname) {
|
||||
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
||||
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
|
||||
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;
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
#include <linux/fscrypt_supp.h>
|
||||
|
||||
#define FS_FNAME_CRYPTO_DIGEST_SIZE 32
|
||||
|
||||
/* Encryption parameters */
|
||||
#define FS_XTS_TWEAK_SIZE 16
|
||||
#define FS_AES_128_ECB_KEY_SIZE 16
|
||||
@ -22,10 +20,6 @@
|
||||
#define FS_AES_256_CBC_KEY_SIZE 32
|
||||
#define FS_AES_256_CTS_KEY_SIZE 32
|
||||
#define FS_AES_256_XTS_KEY_SIZE 64
|
||||
#define FS_MAX_KEY_SIZE 64
|
||||
|
||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||
|
||||
#define FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
@ -51,13 +45,6 @@ struct fscrypt_context {
|
||||
|
||||
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
|
||||
/* This is passed in from userspace into the kernel keyring */
|
||||
struct fscrypt_key {
|
||||
u32 mode;
|
||||
u8 raw[FS_MAX_KEY_SIZE];
|
||||
u32 size;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* A pointer to this structure is stored in the file system's in-core
|
||||
* representation of an inode.
|
||||
|
@ -183,9 +183,6 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res < 0) {
|
||||
if (!fscrypt_dummy_context_enabled(inode) ||
|
||||
|
@ -34,9 +34,6 @@ static int create_encryption_context_from_policy(struct inode *inode,
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
|
||||
if (!inode->i_sb->s_cop->set_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
@ -87,8 +84,6 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
|
||||
if (ret == -ENODATA) {
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
ret = -ENOTDIR;
|
||||
else if (!inode->i_sb->s_cop->empty_dir)
|
||||
ret = -EOPNOTSUPP;
|
||||
else if (!inode->i_sb->s_cop->empty_dir(inode))
|
||||
ret = -ENOTEMPTY;
|
||||
else
|
||||
@ -118,8 +113,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
|
||||
struct fscrypt_policy policy;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context ||
|
||||
!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
if (!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
return -ENODATA;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
@ -143,27 +137,61 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_ioctl_get_policy);
|
||||
|
||||
/**
|
||||
* fscrypt_has_permitted_context() - is a file's encryption policy permitted
|
||||
* within its directory?
|
||||
*
|
||||
* @parent: inode for parent directory
|
||||
* @child: inode for file being looked up, opened, or linked into @parent
|
||||
*
|
||||
* Filesystems must call this before permitting access to an inode in a
|
||||
* situation where the parent directory is encrypted (either before allowing
|
||||
* ->lookup() to succeed, or for a regular file before allowing it to be opened)
|
||||
* and before any operation that involves linking an inode into an encrypted
|
||||
* directory, including link, rename, and cross rename. It enforces the
|
||||
* constraint that within a given encrypted directory tree, all files use the
|
||||
* same encryption policy. The pre-access check is needed to detect potentially
|
||||
* malicious offline violations of this constraint, while the link and rename
|
||||
* checks are needed to prevent online violations of this constraint.
|
||||
*
|
||||
* Return: 1 if permitted, 0 if forbidden. If forbidden, the caller must fail
|
||||
* the filesystem operation with EPERM.
|
||||
*/
|
||||
int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
|
||||
{
|
||||
struct fscrypt_info *parent_ci, *child_ci;
|
||||
const struct fscrypt_operations *cops = parent->i_sb->s_cop;
|
||||
const struct fscrypt_info *parent_ci, *child_ci;
|
||||
struct fscrypt_context parent_ctx, child_ctx;
|
||||
int res;
|
||||
|
||||
if ((parent == NULL) || (child == NULL)) {
|
||||
printk(KERN_ERR "parent %p child %p\n", parent, child);
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
/* No restrictions on file types which are never encrypted */
|
||||
if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
|
||||
!S_ISLNK(child->i_mode))
|
||||
return 1;
|
||||
|
||||
/* no restrictions if the parent directory is not encrypted */
|
||||
if (!parent->i_sb->s_cop->is_encrypted(parent))
|
||||
/* No restrictions if the parent directory is unencrypted */
|
||||
if (!cops->is_encrypted(parent))
|
||||
return 1;
|
||||
/* if the child directory is not encrypted, this is always a problem */
|
||||
if (!parent->i_sb->s_cop->is_encrypted(child))
|
||||
|
||||
/* Encrypted directories must not contain unencrypted files */
|
||||
if (!cops->is_encrypted(child))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Both parent and child are encrypted, so verify they use the same
|
||||
* encryption policy. Compare the fscrypt_info structs if the keys are
|
||||
* available, otherwise retrieve and compare the fscrypt_contexts.
|
||||
*
|
||||
* Note that the fscrypt_context retrieval will be required frequently
|
||||
* when accessing an encrypted directory tree without the key.
|
||||
* Performance-wise this is not a big deal because we already don't
|
||||
* really optimize for file access without the key (to the extent that
|
||||
* such access is even possible), given that any attempted access
|
||||
* already causes a fscrypt_context retrieval and keyring search.
|
||||
*
|
||||
* In any case, if an unexpected error occurs, fall back to "forbidden".
|
||||
*/
|
||||
|
||||
res = fscrypt_get_encryption_info(parent);
|
||||
if (res)
|
||||
return 0;
|
||||
@ -172,17 +200,32 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
|
||||
return 0;
|
||||
parent_ci = parent->i_crypt_info;
|
||||
child_ci = child->i_crypt_info;
|
||||
if (!parent_ci && !child_ci)
|
||||
return 1;
|
||||
if (!parent_ci || !child_ci)
|
||||
|
||||
if (parent_ci && child_ci) {
|
||||
return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
|
||||
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(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);
|
||||
}
|
||||
|
||||
res = cops->get_context(parent, &parent_ctx, sizeof(parent_ctx));
|
||||
if (res != sizeof(parent_ctx))
|
||||
return 0;
|
||||
|
||||
return (memcmp(parent_ci->ci_master_key,
|
||||
child_ci->ci_master_key,
|
||||
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(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));
|
||||
res = cops->get_context(child, &child_ctx, sizeof(child_ctx));
|
||||
if (res != sizeof(child_ctx))
|
||||
return 0;
|
||||
|
||||
return memcmp(parent_ctx.master_key_descriptor,
|
||||
child_ctx.master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(parent_ctx.contents_encryption_mode ==
|
||||
child_ctx.contents_encryption_mode) &&
|
||||
(parent_ctx.filenames_encryption_mode ==
|
||||
child_ctx.filenames_encryption_mode) &&
|
||||
(parent_ctx.flags == child_ctx.flags);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_has_permitted_context);
|
||||
|
||||
@ -202,9 +245,6 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||
struct fscrypt_info *ci;
|
||||
int res;
|
||||
|
||||
if (!parent->i_sb->s_cop->set_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = fscrypt_get_encryption_info(parent);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
112
fs/ext4/namei.c
112
fs/ext4/namei.c
@ -1237,37 +1237,24 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE! unlike strncmp, ext4_match returns 1 for success, 0 for failure.
|
||||
* Test whether a directory entry matches the filename being searched for.
|
||||
*
|
||||
* `len <= EXT4_NAME_LEN' is guaranteed by caller.
|
||||
* `de != NULL' is guaranteed by caller.
|
||||
* Return: %true if the directory entry matches, otherwise %false.
|
||||
*/
|
||||
static inline int ext4_match(struct ext4_filename *fname,
|
||||
struct ext4_dir_entry_2 *de)
|
||||
static inline bool ext4_match(const struct ext4_filename *fname,
|
||||
const struct ext4_dir_entry_2 *de)
|
||||
{
|
||||
const void *name = fname_name(fname);
|
||||
u32 len = fname_len(fname);
|
||||
struct fscrypt_name f;
|
||||
|
||||
if (!de->inode)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
f.usr_fname = fname->usr_fname;
|
||||
f.disk_name = fname->disk_name;
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
if (unlikely(!name)) {
|
||||
if (fname->usr_fname->name[0] == '_') {
|
||||
int ret;
|
||||
if (de->name_len < 16)
|
||||
return 0;
|
||||
ret = memcmp(de->name + de->name_len - 16,
|
||||
fname->crypto_buf.name + 8, 16);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
}
|
||||
name = fname->crypto_buf.name;
|
||||
len = fname->crypto_buf.len;
|
||||
}
|
||||
f.crypto_buf = fname->crypto_buf;
|
||||
#endif
|
||||
if (de->name_len != len)
|
||||
return 0;
|
||||
return (memcmp(de->name, name, len) == 0) ? 1 : 0;
|
||||
return fscrypt_match_name(&f, de->name, de->name_len);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1281,48 +1268,31 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
|
||||
struct ext4_dir_entry_2 * de;
|
||||
char * dlimit;
|
||||
int de_len;
|
||||
int res;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)search_buf;
|
||||
dlimit = search_buf + buf_size;
|
||||
while ((char *) de < dlimit) {
|
||||
/* this code is executed quadratically often */
|
||||
/* do minimal checking `by hand' */
|
||||
if ((char *) de + de->name_len <= dlimit) {
|
||||
res = ext4_match(fname, de);
|
||||
if (res < 0) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
if (res > 0) {
|
||||
/* found a match - just to be sure, do
|
||||
* a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
bh->b_data,
|
||||
bh->b_size, offset)) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
*res_dir = de;
|
||||
res = 1;
|
||||
goto return_result;
|
||||
}
|
||||
|
||||
if ((char *) de + de->name_len <= dlimit &&
|
||||
ext4_match(fname, de)) {
|
||||
/* found a match - just to be sure, do
|
||||
* a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
|
||||
bh->b_size, offset))
|
||||
return -1;
|
||||
*res_dir = de;
|
||||
return 1;
|
||||
}
|
||||
/* prevent looping on a bad block */
|
||||
de_len = ext4_rec_len_from_disk(de->rec_len,
|
||||
dir->i_sb->s_blocksize);
|
||||
if (de_len <= 0) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
if (de_len <= 0)
|
||||
return -1;
|
||||
offset += de_len;
|
||||
de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
|
||||
}
|
||||
|
||||
res = 0;
|
||||
return_result:
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
|
||||
@ -1616,16 +1586,9 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
||||
if (!IS_ERR(inode) && ext4_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
int nokey = ext4_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
if (nokey) {
|
||||
iput(inode);
|
||||
return ERR_PTR(-ENOKEY);
|
||||
}
|
||||
ext4_warning(inode->i_sb,
|
||||
"Inconsistent encryption contexts: %lu/%lu",
|
||||
(unsigned long) dir->i_ino,
|
||||
(unsigned long) inode->i_ino);
|
||||
dir->i_ino, inode->i_ino);
|
||||
iput(inode);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
@ -1833,24 +1796,15 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
int nlen, rlen;
|
||||
unsigned int offset = 0;
|
||||
char *top;
|
||||
int res;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)buf;
|
||||
top = buf + buf_size - reclen;
|
||||
while ((char *) de <= top) {
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
buf, buf_size, offset)) {
|
||||
res = -EFSCORRUPTED;
|
||||
goto return_result;
|
||||
}
|
||||
/* Provide crypto context and crypto buffer to ext4 match */
|
||||
res = ext4_match(fname, de);
|
||||
if (res < 0)
|
||||
goto return_result;
|
||||
if (res > 0) {
|
||||
res = -EEXIST;
|
||||
goto return_result;
|
||||
}
|
||||
buf, buf_size, offset))
|
||||
return -EFSCORRUPTED;
|
||||
if (ext4_match(fname, de))
|
||||
return -EEXIST;
|
||||
nlen = EXT4_DIR_REC_LEN(de->name_len);
|
||||
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
|
||||
if ((de->inode ? rlen - nlen : rlen) >= reclen)
|
||||
@ -1858,15 +1812,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
|
||||
offset += rlen;
|
||||
}
|
||||
|
||||
if ((char *) de > top)
|
||||
res = -ENOSPC;
|
||||
else {
|
||||
*dest_de = de;
|
||||
res = 0;
|
||||
}
|
||||
return_result:
|
||||
return res;
|
||||
return -ENOSPC;
|
||||
|
||||
*dest_de = de;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
|
@ -111,8 +111,6 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
|
||||
struct f2fs_dir_entry *de;
|
||||
unsigned long bit_pos = 0;
|
||||
int max_len = 0;
|
||||
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str *name = &fname->disk_name;
|
||||
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
@ -130,17 +128,9 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* encrypted case */
|
||||
de_name.name = d->filename[bit_pos];
|
||||
de_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
/* show encrypted name */
|
||||
if (fname->hash) {
|
||||
if (de->hash_code == cpu_to_le32(fname->hash))
|
||||
goto found;
|
||||
} else if (de_name.len == name->len &&
|
||||
de->hash_code == namehash &&
|
||||
!memcmp(de_name.name, name->name, name->len))
|
||||
if (de->hash_code == namehash &&
|
||||
fscrypt_match_name(fname, d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len)))
|
||||
goto found;
|
||||
|
||||
if (max_slots && max_len > *max_slots)
|
||||
@ -170,12 +160,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
bool room = false;
|
||||
int max_slots;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
if(fname->hash)
|
||||
namehash = cpu_to_le32(fname->hash);
|
||||
else
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
f2fs_hash_t namehash = f2fs_dentry_hash(&name, fname);
|
||||
|
||||
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
|
||||
nblock = bucket_blocks(level);
|
||||
@ -542,7 +527,7 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
|
||||
|
||||
level = 0;
|
||||
slots = GET_DENTRY_SLOTS(new_name->len);
|
||||
dentry_hash = f2fs_dentry_hash(new_name);
|
||||
dentry_hash = f2fs_dentry_hash(new_name, NULL);
|
||||
|
||||
current_depth = F2FS_I(dir)->i_current_depth;
|
||||
if (F2FS_I(dir)->chash == dentry_hash) {
|
||||
|
@ -2133,7 +2133,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi);
|
||||
/*
|
||||
* hash.c
|
||||
*/
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info);
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
|
||||
struct fscrypt_name *fname);
|
||||
|
||||
/*
|
||||
* node.c
|
||||
|
@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len,
|
||||
*buf++ = pad;
|
||||
}
|
||||
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
|
||||
struct fscrypt_name *fname)
|
||||
{
|
||||
__u32 hash;
|
||||
f2fs_hash_t f2fs_hash;
|
||||
@ -79,6 +80,10 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
|
||||
const unsigned char *name = name_info->name;
|
||||
size_t len = name_info->len;
|
||||
|
||||
/* encrypted bigname case */
|
||||
if (fname && !fname->disk_name.name)
|
||||
return cpu_to_le32(fname->hash);
|
||||
|
||||
if (is_dot_dotdot(name_info))
|
||||
return 0;
|
||||
|
||||
|
@ -296,7 +296,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
namehash = f2fs_dentry_hash(&name, fname);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
|
||||
@ -533,7 +533,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
name_hash = f2fs_dentry_hash(new_name);
|
||||
name_hash = f2fs_dentry_hash(new_name, NULL);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos);
|
||||
|
||||
|
@ -324,9 +324,10 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
if (f2fs_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
bool nokey = f2fs_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
err = nokey ? -ENOKEY : -EPERM;
|
||||
f2fs_msg(inode->i_sb, KERN_WARNING,
|
||||
"Inconsistent encryption contexts: %lu/%lu",
|
||||
dir->i_ino, inode->i_ino);
|
||||
err = -EPERM;
|
||||
goto err_out;
|
||||
}
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
@ -285,6 +285,15 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
goto out_dent;
|
||||
}
|
||||
|
||||
if (ubifs_crypt_is_encrypted(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu",
|
||||
dir->i_ino, inode->i_ino);
|
||||
err = -EPERM;
|
||||
goto out_inode;
|
||||
}
|
||||
|
||||
done:
|
||||
kfree(dent);
|
||||
fscrypt_free_filename(&nm);
|
||||
@ -295,6 +304,8 @@ done:
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
|
||||
out_inode:
|
||||
iput(inode);
|
||||
out_dent:
|
||||
kfree(dent);
|
||||
out_fname:
|
||||
|
@ -46,17 +46,6 @@ struct fscrypt_symlink_data {
|
||||
char encrypted_path[1];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* This function is used to calculate the disk space required to
|
||||
* store a filename of length l in encrypted symlink format.
|
||||
*/
|
||||
static inline u32 fscrypt_symlink_data_len(u32 l)
|
||||
{
|
||||
if (l < FS_CRYPTO_BLOCK_SIZE)
|
||||
l = FS_CRYPTO_BLOCK_SIZE;
|
||||
return (l + sizeof(struct fscrypt_symlink_data) - 1);
|
||||
}
|
||||
|
||||
struct fscrypt_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
|
@ -147,6 +147,15 @@ static inline int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
/* Encryption support disabled; use standard comparison */
|
||||
if (de_name_len != fname->disk_name.len)
|
||||
return false;
|
||||
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
|
||||
}
|
||||
|
||||
/* bio.c */
|
||||
static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx,
|
||||
struct bio *bio)
|
||||
|
@ -57,6 +57,80 @@ extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32,
|
||||
extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
|
||||
struct fscrypt_str *);
|
||||
|
||||
#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32
|
||||
|
||||
/* Extracts the second-to-last ciphertext block; see explanation below */
|
||||
#define FSCRYPT_FNAME_DIGEST(name, len) \
|
||||
((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \
|
||||
FS_CRYPTO_BLOCK_SIZE))
|
||||
|
||||
#define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE
|
||||
|
||||
/**
|
||||
* fscrypt_digested_name - alternate identifier for an on-disk filename
|
||||
*
|
||||
* When userspace lists an encrypted directory without access to the key,
|
||||
* filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE
|
||||
* bytes are shown in this abbreviated form (base64-encoded) rather than as the
|
||||
* full ciphertext (base64-encoded). This is necessary to allow supporting
|
||||
* filenames up to NAME_MAX bytes, since base64 encoding expands the length.
|
||||
*
|
||||
* To make it possible for filesystems to still find the correct directory entry
|
||||
* despite not knowing the full on-disk name, we encode any filesystem-specific
|
||||
* 'hash' and/or 'minor_hash' which the filesystem may need for its lookups,
|
||||
* followed by the second-to-last ciphertext block of the filename. Due to the
|
||||
* use of the CBC-CTS encryption mode, the second-to-last ciphertext block
|
||||
* depends on the full plaintext. (Note that ciphertext stealing causes the
|
||||
* last two blocks to appear "flipped".) This makes accidental collisions very
|
||||
* unlikely: just a 1 in 2^128 chance for two filenames to collide even if they
|
||||
* share the same filesystem-specific hashes.
|
||||
*
|
||||
* However, this scheme isn't immune to intentional collisions, which can be
|
||||
* created by anyone able to create arbitrary plaintext filenames and view them
|
||||
* without the key. Making the "digest" be a real cryptographic hash like
|
||||
* SHA-256 over the full ciphertext would prevent this, although it would be
|
||||
* less efficient and harder to implement, especially since the filesystem would
|
||||
* need to calculate it for each directory entry examined during a search.
|
||||
*/
|
||||
struct fscrypt_digested_name {
|
||||
u32 hash;
|
||||
u32 minor_hash;
|
||||
u8 digest[FSCRYPT_FNAME_DIGEST_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 a filename in it is
|
||||
* very long, then we won't have the full disk_name and we'll instead need to
|
||||
* match against the fscrypt_digested_name.
|
||||
*
|
||||
* Return: %true if the name matches, otherwise %false.
|
||||
*/
|
||||
static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
if (unlikely(!fname->disk_name.name)) {
|
||||
const struct fscrypt_digested_name *n =
|
||||
(const void *)fname->crypto_buf.name;
|
||||
if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_'))
|
||||
return false;
|
||||
if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)
|
||||
return false;
|
||||
return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len),
|
||||
n->digest, FSCRYPT_FNAME_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
if (de_name_len != fname->disk_name.len)
|
||||
return false;
|
||||
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
|
||||
}
|
||||
|
||||
/* bio.c */
|
||||
extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
|
||||
extern void fscrypt_pullback_bio_page(struct page **, bool);
|
||||
|
@ -279,12 +279,25 @@ struct fscrypt_policy {
|
||||
__u8 filenames_encryption_mode;
|
||||
__u8 flags;
|
||||
__u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
} __packed;
|
||||
};
|
||||
|
||||
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
|
||||
#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
|
||||
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
|
||||
|
||||
/* Parameters for passing an encryption key into the kernel keyring */
|
||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||
|
||||
/* Structure that userspace passes to the kernel keyring */
|
||||
#define FS_MAX_KEY_SIZE 64
|
||||
|
||||
struct fscrypt_key {
|
||||
__u32 mode;
|
||||
__u8 raw[FS_MAX_KEY_SIZE];
|
||||
__u32 size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user