Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 677375ce authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents dd727dad 6f9d696f
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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
+70 −20
Original line number Diff line number Diff line
@@ -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)))
	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;
+0 −13
Original line number Diff line number Diff line
@@ -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.
+0 −3
Original line number Diff line number Diff line
@@ -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) ||
+69 −29
Original line number Diff line number Diff line
@@ -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)
		return 0;

	return (memcmp(parent_ci->ci_master_key,
			child_ci->ci_master_key,
	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));
			(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;

	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;
Loading