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:
Linus Torvalds 2017-05-08 11:40:34 -07:00
commit 677375cef8
16 changed files with 301 additions and 187 deletions

View File

@ -5417,10 +5417,12 @@ F: Documentation/filesystems/caching/
F: fs/fscache/ F: fs/fscache/
F: include/linux/fscache*.h 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: Theodore Y. Ts'o <tytso@mit.edu>
M: Jaegeuk Kim <jaegeuk@kernel.org> 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 S: Supported
F: fs/crypto/ F: fs/crypto/
F: include/linux/fscrypt*.h F: include/linux/fscrypt*.h

View File

@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode,
static const char *lookup_table = static const char *lookup_table =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
/** /**
* digest_encode() - * digest_encode() -
* *
@ -230,11 +232,14 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
int fscrypt_fname_alloc_buffer(const struct inode *inode, int fscrypt_fname_alloc_buffer(const struct inode *inode,
u32 ilen, struct fscrypt_str *crypto_str) 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; crypto_str->len = olen;
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2) olen = max(olen, max_encoded_len);
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
/* /*
* Allocated buffer can hold one more character to null-terminate the * Allocated buffer can hold one more character to null-terminate the
* string * string
@ -266,6 +271,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer);
* *
* The caller must have allocated sufficient memory for the @oname string. * 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 * Return: 0 on success, -errno on failure
*/ */
int fscrypt_fname_disk_to_usr(struct inode *inode, 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) struct fscrypt_str *oname)
{ {
const struct qstr qname = FSTR_TO_QSTR(iname); const struct qstr qname = FSTR_TO_QSTR(iname);
char buf[24]; struct fscrypt_digested_name digested_name;
if (fscrypt_is_dot_dotdot(&qname)) { if (fscrypt_is_dot_dotdot(&qname)) {
oname->name[0] = '.'; oname->name[0] = '.';
@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
if (inode->i_crypt_info) if (inode->i_crypt_info)
return fname_decrypt(inode, iname, oname); 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->len = digest_encode(iname->name, iname->len,
oname->name); oname->name);
return 0; return 0;
} }
if (hash) { if (hash) {
memcpy(buf, &hash, 4); digested_name.hash = hash;
memcpy(buf + 4, &minor_hash, 4); digested_name.minor_hash = minor_hash;
} else { } 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->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; return 0;
} }
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); 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); 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 fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
int lookup, struct fscrypt_name *fname) int lookup, struct fscrypt_name *fname)
{ {
int ret = 0, bigname = 0; int ret;
int digested;
memset(fname, 0, sizeof(struct fscrypt_name)); memset(fname, 0, sizeof(struct fscrypt_name));
fname->usr_fname = iname; 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 * We don't have the key and we are doing a lookup; decode the
* user-supplied name * user-supplied name
*/ */
if (iname->name[0] == '_') if (iname->name[0] == '_') {
bigname = 1; if (iname->len !=
if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43))) 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)))
return -ENOENT; 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) if (fname->crypto_buf.name == NULL)
return -ENOMEM; return -ENOMEM;
ret = digest_decode(iname->name + bigname, iname->len - bigname, ret = digest_decode(iname->name + digested, iname->len - digested,
fname->crypto_buf.name); fname->crypto_buf.name);
if (ret < 0) { if (ret < 0) {
ret = -ENOENT; ret = -ENOENT;
goto errout; goto errout;
} }
fname->crypto_buf.len = ret; fname->crypto_buf.len = ret;
if (bigname) { if (digested) {
memcpy(&fname->hash, fname->crypto_buf.name, 4); const struct fscrypt_digested_name *n =
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4); (const void *)fname->crypto_buf.name;
fname->hash = n->hash;
fname->minor_hash = n->minor_hash;
} else { } else {
fname->disk_name.name = fname->crypto_buf.name; fname->disk_name.name = fname->crypto_buf.name;
fname->disk_name.len = fname->crypto_buf.len; fname->disk_name.len = fname->crypto_buf.len;

View File

@ -13,8 +13,6 @@
#include <linux/fscrypt_supp.h> #include <linux/fscrypt_supp.h>
#define FS_FNAME_CRYPTO_DIGEST_SIZE 32
/* Encryption parameters */ /* Encryption parameters */
#define FS_XTS_TWEAK_SIZE 16 #define FS_XTS_TWEAK_SIZE 16
#define FS_AES_128_ECB_KEY_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_CBC_KEY_SIZE 32
#define FS_AES_256_CTS_KEY_SIZE 32 #define FS_AES_256_CTS_KEY_SIZE 32
#define FS_AES_256_XTS_KEY_SIZE 64 #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 #define FS_KEY_DERIVATION_NONCE_SIZE 16
@ -51,13 +45,6 @@ struct fscrypt_context {
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 #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 * A pointer to this structure is stored in the file system's in-core
* representation of an inode. * representation of an inode.

View File

@ -183,9 +183,6 @@ int fscrypt_get_encryption_info(struct inode *inode)
if (res) if (res)
return 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)); res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
if (res < 0) { if (res < 0) {
if (!fscrypt_dummy_context_enabled(inode) || if (!fscrypt_dummy_context_enabled(inode) ||

View File

@ -34,9 +34,6 @@ static int create_encryption_context_from_policy(struct inode *inode,
{ {
struct fscrypt_context ctx; struct fscrypt_context ctx;
if (!inode->i_sb->s_cop->set_context)
return -EOPNOTSUPP;
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
FS_KEY_DESCRIPTOR_SIZE); FS_KEY_DESCRIPTOR_SIZE);
@ -87,8 +84,6 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
if (ret == -ENODATA) { if (ret == -ENODATA) {
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
ret = -ENOTDIR; ret = -ENOTDIR;
else if (!inode->i_sb->s_cop->empty_dir)
ret = -EOPNOTSUPP;
else if (!inode->i_sb->s_cop->empty_dir(inode)) else if (!inode->i_sb->s_cop->empty_dir(inode))
ret = -ENOTEMPTY; ret = -ENOTEMPTY;
else else
@ -118,8 +113,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
struct fscrypt_policy policy; struct fscrypt_policy policy;
int res; int res;
if (!inode->i_sb->s_cop->get_context || if (!inode->i_sb->s_cop->is_encrypted(inode))
!inode->i_sb->s_cop->is_encrypted(inode))
return -ENODATA; return -ENODATA;
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); 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); 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) 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; 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 */ /* No restrictions on file types which are never encrypted */
if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
!S_ISLNK(child->i_mode)) !S_ISLNK(child->i_mode))
return 1; return 1;
/* no restrictions if the parent directory is not encrypted */ /* No restrictions if the parent directory is unencrypted */
if (!parent->i_sb->s_cop->is_encrypted(parent)) if (!cops->is_encrypted(parent))
return 1; 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; 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); res = fscrypt_get_encryption_info(parent);
if (res) if (res)
return 0; return 0;
@ -172,17 +200,32 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
return 0; return 0;
parent_ci = parent->i_crypt_info; parent_ci = parent->i_crypt_info;
child_ci = child->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 0;
return (memcmp(parent_ci->ci_master_key, res = cops->get_context(child, &child_ctx, sizeof(child_ctx));
child_ci->ci_master_key, if (res != sizeof(child_ctx))
FS_KEY_DESCRIPTOR_SIZE) == 0 && return 0;
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
(parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && return memcmp(parent_ctx.master_key_descriptor,
(parent_ci->ci_flags == child_ci->ci_flags)); 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); EXPORT_SYMBOL(fscrypt_has_permitted_context);
@ -202,9 +245,6 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
struct fscrypt_info *ci; struct fscrypt_info *ci;
int res; int res;
if (!parent->i_sb->s_cop->set_context)
return -EOPNOTSUPP;
res = fscrypt_get_encryption_info(parent); res = fscrypt_get_encryption_info(parent);
if (res < 0) if (res < 0)
return res; return res;

View File

@ -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. * Return: %true if the directory entry matches, otherwise %false.
* `de != NULL' is guaranteed by caller.
*/ */
static inline int ext4_match(struct ext4_filename *fname, static inline bool ext4_match(const struct ext4_filename *fname,
struct ext4_dir_entry_2 *de) const struct ext4_dir_entry_2 *de)
{ {
const void *name = fname_name(fname); struct fscrypt_name f;
u32 len = fname_len(fname);
if (!de->inode) if (!de->inode)
return 0; return false;
f.usr_fname = fname->usr_fname;
f.disk_name = fname->disk_name;
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
if (unlikely(!name)) { f.crypto_buf = fname->crypto_buf;
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;
}
#endif #endif
if (de->name_len != len) return fscrypt_match_name(&f, de->name, de->name_len);
return 0;
return (memcmp(de->name, name, len) == 0) ? 1 : 0;
} }
/* /*
@ -1281,48 +1268,31 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
struct ext4_dir_entry_2 * de; struct ext4_dir_entry_2 * de;
char * dlimit; char * dlimit;
int de_len; int de_len;
int res;
de = (struct ext4_dir_entry_2 *)search_buf; de = (struct ext4_dir_entry_2 *)search_buf;
dlimit = search_buf + buf_size; dlimit = search_buf + buf_size;
while ((char *) de < dlimit) { while ((char *) de < dlimit) {
/* this code is executed quadratically often */ /* this code is executed quadratically often */
/* do minimal checking `by hand' */ /* do minimal checking `by hand' */
if ((char *) de + de->name_len <= dlimit) { if ((char *) de + de->name_len <= dlimit &&
res = ext4_match(fname, de); ext4_match(fname, de)) {
if (res < 0) { /* found a match - just to be sure, do
res = -1; * a full check */
goto return_result; if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
} bh->b_size, offset))
if (res > 0) { return -1;
/* found a match - just to be sure, do *res_dir = de;
* a full check */ return 1;
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;
}
} }
/* prevent looping on a bad block */ /* prevent looping on a bad block */
de_len = ext4_rec_len_from_disk(de->rec_len, de_len = ext4_rec_len_from_disk(de->rec_len,
dir->i_sb->s_blocksize); dir->i_sb->s_blocksize);
if (de_len <= 0) { if (de_len <= 0)
res = -1; return -1;
goto return_result;
}
offset += de_len; offset += de_len;
de = (struct ext4_dir_entry_2 *) ((char *) de + de_len); de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
} }
return 0;
res = 0;
return_result:
return res;
} }
static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block, 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) && if (!IS_ERR(inode) && ext4_encrypted_inode(dir) &&
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
!fscrypt_has_permitted_context(dir, inode)) { !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, ext4_warning(inode->i_sb,
"Inconsistent encryption contexts: %lu/%lu", "Inconsistent encryption contexts: %lu/%lu",
(unsigned long) dir->i_ino, dir->i_ino, inode->i_ino);
(unsigned long) inode->i_ino);
iput(inode); iput(inode);
return ERR_PTR(-EPERM); return ERR_PTR(-EPERM);
} }
@ -1833,24 +1796,15 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
int nlen, rlen; int nlen, rlen;
unsigned int offset = 0; unsigned int offset = 0;
char *top; char *top;
int res;
de = (struct ext4_dir_entry_2 *)buf; de = (struct ext4_dir_entry_2 *)buf;
top = buf + buf_size - reclen; top = buf + buf_size - reclen;
while ((char *) de <= top) { while ((char *) de <= top) {
if (ext4_check_dir_entry(dir, NULL, de, bh, if (ext4_check_dir_entry(dir, NULL, de, bh,
buf, buf_size, offset)) { buf, buf_size, offset))
res = -EFSCORRUPTED; return -EFSCORRUPTED;
goto return_result; if (ext4_match(fname, de))
} return -EEXIST;
/* 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;
}
nlen = EXT4_DIR_REC_LEN(de->name_len); nlen = EXT4_DIR_REC_LEN(de->name_len);
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
if ((de->inode ? rlen - nlen : rlen) >= reclen) 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); de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
offset += rlen; offset += rlen;
} }
if ((char *) de > top) if ((char *) de > top)
res = -ENOSPC; return -ENOSPC;
else {
*dest_de = de; *dest_de = de;
res = 0; return 0;
}
return_result:
return res;
} }
void ext4_insert_dentry(struct inode *inode, void ext4_insert_dentry(struct inode *inode,

View File

@ -111,8 +111,6 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
unsigned long bit_pos = 0; unsigned long bit_pos = 0;
int max_len = 0; int max_len = 0;
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
struct fscrypt_str *name = &fname->disk_name;
if (max_slots) if (max_slots)
*max_slots = 0; *max_slots = 0;
@ -130,17 +128,9 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
continue; continue;
} }
/* encrypted case */ if (de->hash_code == namehash &&
de_name.name = d->filename[bit_pos]; fscrypt_match_name(fname, d->filename[bit_pos],
de_name.len = le16_to_cpu(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))
goto found; goto found;
if (max_slots && max_len > *max_slots) 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; struct f2fs_dir_entry *de = NULL;
bool room = false; bool room = false;
int max_slots; int max_slots;
f2fs_hash_t namehash; f2fs_hash_t namehash = f2fs_dentry_hash(&name, fname);
if(fname->hash)
namehash = cpu_to_le32(fname->hash);
else
namehash = f2fs_dentry_hash(&name);
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
nblock = bucket_blocks(level); nblock = bucket_blocks(level);
@ -542,7 +527,7 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
level = 0; level = 0;
slots = GET_DENTRY_SLOTS(new_name->len); 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; current_depth = F2FS_I(dir)->i_current_depth;
if (F2FS_I(dir)->chash == dentry_hash) { if (F2FS_I(dir)->chash == dentry_hash) {

View File

@ -2133,7 +2133,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi);
/* /*
* hash.c * 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 * node.c

View File

@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len,
*buf++ = pad; *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; __u32 hash;
f2fs_hash_t f2fs_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; const unsigned char *name = name_info->name;
size_t len = name_info->len; 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)) if (is_dot_dotdot(name_info))
return 0; return 0;

View File

@ -296,7 +296,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
return NULL; return NULL;
} }
namehash = f2fs_dentry_hash(&name); namehash = f2fs_dentry_hash(&name, fname);
inline_dentry = inline_data_addr(ipage); 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); 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); make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos);

View File

@ -324,9 +324,10 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
if (f2fs_encrypted_inode(dir) && if (f2fs_encrypted_inode(dir) &&
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
!fscrypt_has_permitted_context(dir, inode)) { !fscrypt_has_permitted_context(dir, inode)) {
bool nokey = f2fs_encrypted_inode(inode) && f2fs_msg(inode->i_sb, KERN_WARNING,
!fscrypt_has_encryption_key(inode); "Inconsistent encryption contexts: %lu/%lu",
err = nokey ? -ENOKEY : -EPERM; dir->i_ino, inode->i_ino);
err = -EPERM;
goto err_out; goto err_out;
} }
return d_splice_alias(inode, dentry); return d_splice_alias(inode, dentry);

View File

@ -285,6 +285,15 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
goto out_dent; 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: done:
kfree(dent); kfree(dent);
fscrypt_free_filename(&nm); fscrypt_free_filename(&nm);
@ -295,6 +304,8 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
d_add(dentry, inode); d_add(dentry, inode);
return NULL; return NULL;
out_inode:
iput(inode);
out_dent: out_dent:
kfree(dent); kfree(dent);
out_fname: out_fname:

View File

@ -46,17 +46,6 @@ struct fscrypt_symlink_data {
char encrypted_path[1]; char encrypted_path[1];
} __packed; } __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 { struct fscrypt_str {
unsigned char *name; unsigned char *name;
u32 len; u32 len;

View File

@ -147,6 +147,15 @@ static inline int fscrypt_fname_usr_to_disk(struct inode *inode,
return -EOPNOTSUPP; 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 */ /* bio.c */
static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx,
struct bio *bio) struct bio *bio)

View File

@ -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 *, extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
struct fscrypt_str *); 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 */ /* bio.c */
extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
extern void fscrypt_pullback_bio_page(struct page **, bool); extern void fscrypt_pullback_bio_page(struct page **, bool);

View File

@ -279,12 +279,25 @@ struct fscrypt_policy {
__u8 filenames_encryption_mode; __u8 filenames_encryption_mode;
__u8 flags; __u8 flags;
__u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
} __packed; };
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) #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_PWSALT _IOW('f', 20, __u8[16])
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) #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) * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
* *