mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
				synced 2025-11-04 08:34:47 +10:00 
			
		
		
		
	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