Commit 7c7fdaf6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'fscrypt-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt

Pull fscrypt updates from Eric Biggers:
 "This release there are some fixes for longstanding problems, as well
  as some cleanups:

   - Fix a race condition where a duplicate filename could be created in
     an encrypted directory if a syscall that creates a new filename
     raced with the directory's encryption key being added.

   - Allow deleting files that use an unsupported encryption policy.

   - Simplify the locking for 'struct fscrypt_master_key'.

   - Remove kernel-internal constants from the UAPI header.

  As usual, all these patches have been in linux-next with no reported
  issues, and I've tested them with xfstests"

* tag 'fscrypt-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt:
  fscrypt: allow deleting files with unsupported encryption policy
  fscrypt: unexport fscrypt_get_encryption_info()
  fscrypt: move fscrypt_require_key() to fscrypt_private.h
  fscrypt: move body of fscrypt_prepare_setattr() out-of-line
  fscrypt: introduce fscrypt_prepare_readdir()
  ext4: don't call fscrypt_get_encryption_info() from dx_show_leaf()
  ubifs: remove ubifs_dir_open()
  f2fs: remove f2fs_dir_open()
  ext4: remove ext4_dir_open()
  fscrypt: simplify master key locking
  fscrypt: remove unnecessary calls to fscrypt_require_key()
  ubifs: prevent creating duplicate encrypted filenames
  f2fs: prevent creating duplicate encrypted filenames
  ext4: prevent creating duplicate encrypted filenames
  fscrypt: add fscrypt_is_nokey_name()
  fscrypt: remove kernel-internal constants from UAPI header
parents da062855 a14d0b67
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -404,7 +404,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
		fname->disk_name.len = iname->len;
		return 0;
	}
	ret = fscrypt_get_encryption_info(dir);
	ret = fscrypt_get_encryption_info(dir, lookup);
	if (ret)
		return ret;

@@ -560,7 +560,11 @@ int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
		return -ECHILD;

	dir = dget_parent(dentry);
	err = fscrypt_get_encryption_info(d_inode(dir));
	/*
	 * Pass allow_unsupported=true, so that files with an unsupported
	 * encryption policy can be deleted.
	 */
	err = fscrypt_get_encryption_info(d_inode(dir), true);
	valid = !fscrypt_has_encryption_key(d_inode(dir));
	dput(dir);

+40 −16
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@
#define FSCRYPT_CONTEXT_V1	1
#define FSCRYPT_CONTEXT_V2	2

/* Keep this in sync with include/uapi/linux/fscrypt.h */
#define FSCRYPT_MODE_MAX	FSCRYPT_MODE_ADIANTUM

struct fscrypt_context_v1 {
	u8 version; /* FSCRYPT_CONTEXT_V1 */
	u8 contents_encryption_mode;
@@ -436,16 +439,9 @@ struct fscrypt_master_key {
	 * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or
	 * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again.
	 *
	 * Locking: protected by key->sem (outer) and mk_secret_sem (inner).
	 * The reason for two locks is that key->sem also protects modifying
	 * mk_users, which ranks it above the semaphore for the keyring key
	 * type, which is in turn above page faults (via keyring_read).  But
	 * sometimes filesystems call fscrypt_get_encryption_info() from within
	 * a transaction, which ranks it below page faults.  So we need a
	 * separate lock which protects mk_secret but not also mk_users.
	 * Locking: protected by this master key's key->sem.
	 */
	struct fscrypt_master_key_secret	mk_secret;
	struct rw_semaphore			mk_secret_sem;

	/*
	 * For v1 policy keys: an arbitrary key descriptor which was assigned by
@@ -464,8 +460,8 @@ struct fscrypt_master_key {
	 *
	 * This is NULL for v1 policy keys; those can only be added by root.
	 *
	 * Locking: in addition to this keyrings own semaphore, this is
	 * protected by the master key's key->sem, so we can do atomic
	 * Locking: in addition to this keyring's own semaphore, this is
	 * protected by this master key's key->sem, so we can do atomic
	 * search+insert.  It can also be searched without taking any locks, but
	 * in that case the returned key may have already been removed.
	 */
@@ -491,9 +487,9 @@ struct fscrypt_master_key {
	 * Per-mode encryption keys for the various types of encryption policies
	 * that use them.  Allocated and derived on-demand.
	 */
	struct fscrypt_prepared_key mk_direct_keys[__FSCRYPT_MODE_MAX + 1];
	struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1];
	struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1];
	struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1];
	struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1];
	struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1];

	/* Hash key for inode numbers.  Initialized only when needed. */
	siphash_key_t		mk_ino_hash_key;
@@ -507,9 +503,9 @@ is_master_key_secret_present(const struct fscrypt_master_key_secret *secret)
	/*
	 * The READ_ONCE() is only necessary for fscrypt_drop_inode() and
	 * fscrypt_key_describe().  These run in atomic context, so they can't
	 * take ->mk_secret_sem and thus 'secret' can change concurrently which
	 * would be a data race.  But they only need to know whether the secret
	 * *was* present at the time of check, so READ_ONCE() suffices.
	 * take the key semaphore and thus 'secret' can change concurrently
	 * which would be a data race.  But they only need to know whether the
	 * secret *was* present at the time of check, so READ_ONCE() suffices.
	 */
	return READ_ONCE(secret->size) != 0;
}
@@ -575,6 +571,34 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
void fscrypt_hash_inode_number(struct fscrypt_info *ci,
			       const struct fscrypt_master_key *mk);

int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported);

/**
 * fscrypt_require_key() - require an inode's encryption key
 * @inode: the inode we need the key for
 *
 * If the inode is encrypted, set up its encryption key if not already done.
 * Then require that the key be present and return -ENOKEY otherwise.
 *
 * No locks are needed, and the key will live as long as the struct inode --- so
 * it won't go away from under you.
 *
 * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code
 * if a problem occurred while setting up the encryption key.
 */
static inline int fscrypt_require_key(struct inode *inode)
{
	if (IS_ENCRYPTED(inode)) {
		int err = fscrypt_get_encryption_info(inode, false);

		if (err)
			return err;
		if (!fscrypt_has_encryption_key(inode))
			return -ENOKEY;
	}
	return 0;
}

/* keysetup_v1.c */

void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
+31 −24
Original line number Diff line number Diff line
@@ -54,15 +54,12 @@ EXPORT_SYMBOL_GPL(fscrypt_file_open);
int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
			   struct dentry *dentry)
{
	int err;

	err = fscrypt_require_key(dir);
	if (err)
		return err;

	/* ... in case we looked up no-key name before key was added */
	if (dentry->d_flags & DCACHE_NOKEY_NAME)
	if (fscrypt_is_nokey_name(dentry))
		return -ENOKEY;
	/*
	 * We don't need to separately check that the directory inode's key is
	 * available, as it's implied by the dentry not being a no-key name.
	 */

	if (!fscrypt_has_permitted_context(dir, inode))
		return -EXDEV;
@@ -75,19 +72,13 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
			     struct inode *new_dir, struct dentry *new_dentry,
			     unsigned int flags)
{
	int err;

	err = fscrypt_require_key(old_dir);
	if (err)
		return err;

	err = fscrypt_require_key(new_dir);
	if (err)
		return err;

	/* ... in case we looked up no-key name(s) before key was added */
	if ((old_dentry->d_flags | new_dentry->d_flags) & DCACHE_NOKEY_NAME)
	if (fscrypt_is_nokey_name(old_dentry) ||
	    fscrypt_is_nokey_name(new_dentry))
		return -ENOKEY;
	/*
	 * We don't need to separately check that the directory inodes' keys are
	 * available, as it's implied by the dentries not being no-key names.
	 */

	if (old_dir != new_dir) {
		if (IS_ENCRYPTED(new_dir) &&
@@ -123,6 +114,20 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);

int __fscrypt_prepare_readdir(struct inode *dir)
{
	return fscrypt_get_encryption_info(dir, true);
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_readdir);

int __fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr)
{
	if (attr->ia_valid & ATTR_SIZE)
		return fscrypt_require_key(d_inode(dentry));
	return 0;
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_setattr);

/**
 * fscrypt_prepare_setflags() - prepare to change flags with FS_IOC_SETFLAGS
 * @inode: the inode on which flags are being changed
@@ -138,6 +143,7 @@ int fscrypt_prepare_setflags(struct inode *inode,
			     unsigned int oldflags, unsigned int flags)
{
	struct fscrypt_info *ci;
	struct key *key;
	struct fscrypt_master_key *mk;
	int err;

@@ -153,13 +159,14 @@ int fscrypt_prepare_setflags(struct inode *inode,
		ci = inode->i_crypt_info;
		if (ci->ci_policy.version != FSCRYPT_POLICY_V2)
			return -EINVAL;
		mk = ci->ci_master_key->payload.data[0];
		down_read(&mk->mk_secret_sem);
		key = ci->ci_master_key;
		mk = key->payload.data[0];
		down_read(&key->sem);
		if (is_master_key_secret_present(&mk->mk_secret))
			err = fscrypt_derive_dirhash_key(ci, mk);
		else
			err = -ENOKEY;
		up_read(&mk->mk_secret_sem);
		up_read(&key->sem);
		return err;
	}
	return 0;
@@ -325,7 +332,7 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
	 * Try to set up the symlink's encryption key, but we can continue
	 * regardless of whether the key is available or not.
	 */
	err = fscrypt_get_encryption_info(inode);
	err = fscrypt_get_encryption_info(inode, false);
	if (err)
		return ERR_PTR(err);
	has_key = fscrypt_has_encryption_key(inode);
+2 −8
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ static void free_master_key(struct fscrypt_master_key *mk)

	wipe_master_key_secret(&mk->mk_secret);

	for (i = 0; i <= __FSCRYPT_MODE_MAX; i++) {
	for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
		fscrypt_destroy_prepared_key(&mk->mk_direct_keys[i]);
		fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_64_keys[i]);
		fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_32_keys[i]);
@@ -347,7 +347,6 @@ static int add_new_master_key(struct fscrypt_master_key_secret *secret,
	mk->mk_spec = *mk_spec;

	move_master_key_secret(&mk->mk_secret, secret);
	init_rwsem(&mk->mk_secret_sem);

	refcount_set(&mk->mk_refcount, 1); /* secret is present */
	INIT_LIST_HEAD(&mk->mk_decrypted_inodes);
@@ -427,11 +426,8 @@ static int add_existing_master_key(struct fscrypt_master_key *mk,
	}

	/* Re-add the secret if needed. */
	if (rekey) {
		down_write(&mk->mk_secret_sem);
	if (rekey)
		move_master_key_secret(&mk->mk_secret, secret);
		up_write(&mk->mk_secret_sem);
	}
	return 0;
}

@@ -975,10 +971,8 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users)
	/* No user claims remaining.  Go ahead and wipe the secret. */
	dead = false;
	if (is_master_key_secret_present(&mk->mk_secret)) {
		down_write(&mk->mk_secret_sem);
		wipe_master_key_secret(&mk->mk_secret);
		dead = refcount_dec_and_test(&mk->mk_refcount);
		up_write(&mk->mk_secret_sem);
	}
	up_write(&key->sem);
	if (dead) {
+29 −15
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ static struct fscrypt_mode *
select_encryption_mode(const union fscrypt_policy *policy,
		       const struct inode *inode)
{
	BUILD_BUG_ON(ARRAY_SIZE(fscrypt_modes) != FSCRYPT_MODE_MAX + 1);

	if (S_ISREG(inode->i_mode))
		return &fscrypt_modes[fscrypt_policy_contents_mode(policy)];

@@ -168,7 +170,7 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
	unsigned int hkdf_infolen = 0;
	int err;

	if (WARN_ON(mode_num > __FSCRYPT_MODE_MAX))
	if (WARN_ON(mode_num > FSCRYPT_MODE_MAX))
		return -EINVAL;

	prep_key = &keys[mode_num];
@@ -335,11 +337,11 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
 * Find the master key, then set up the inode's actual encryption key.
 *
 * If the master key is found in the filesystem-level keyring, then the
 * corresponding 'struct key' is returned in *master_key_ret with
 * ->mk_secret_sem read-locked.  This is needed to ensure that only one task
 * links the fscrypt_info into ->mk_decrypted_inodes (as multiple tasks may race
 * to create an fscrypt_info for the same inode), and to synchronize the master
 * key being removed with a new inode starting to use it.
 * corresponding 'struct key' is returned in *master_key_ret with its semaphore
 * read-locked.  This is needed to ensure that only one task links the
 * fscrypt_info into ->mk_decrypted_inodes (as multiple tasks may race to create
 * an fscrypt_info for the same inode), and to synchronize the master key being
 * removed with a new inode starting to use it.
 */
static int setup_file_encryption_key(struct fscrypt_info *ci,
				     bool need_dirhash_key,
@@ -388,7 +390,7 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
	}

	mk = key->payload.data[0];
	down_read(&mk->mk_secret_sem);
	down_read(&key->sem);

	/* Has the secret been removed (via FS_IOC_REMOVE_ENCRYPTION_KEY)? */
	if (!is_master_key_secret_present(&mk->mk_secret)) {
@@ -431,7 +433,7 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
	return 0;

out_release_key:
	up_read(&mk->mk_secret_sem);
	up_read(&key->sem);
	key_put(key);
	return err;
}
@@ -534,9 +536,7 @@ fscrypt_setup_encryption_info(struct inode *inode,
	res = 0;
out:
	if (master_key) {
		struct fscrypt_master_key *mk = master_key->payload.data[0];

		up_read(&mk->mk_secret_sem);
		up_read(&master_key->sem);
		key_put(master_key);
	}
	put_crypt_info(crypt_info);
@@ -546,6 +546,11 @@ fscrypt_setup_encryption_info(struct inode *inode,
/**
 * fscrypt_get_encryption_info() - set up an inode's encryption key
 * @inode: the inode to set up the key for.  Must be encrypted.
 * @allow_unsupported: if %true, treat an unsupported encryption policy (or
 *		       unrecognized encryption context) the same way as the key
 *		       being unavailable, instead of returning an error.  Use
 *		       %false unless the operation being performed is needed in
 *		       order for files (or directories) to be deleted.
 *
 * Set up ->i_crypt_info, if it hasn't already been done.
 *
@@ -556,7 +561,7 @@ fscrypt_setup_encryption_info(struct inode *inode,
 *	   encryption key is unavailable.  (Use fscrypt_has_encryption_key() to
 *	   distinguish these cases.)  Also can return another -errno code.
 */
int fscrypt_get_encryption_info(struct inode *inode)
int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
{
	int res;
	union fscrypt_context ctx;
@@ -567,29 +572,38 @@ int fscrypt_get_encryption_info(struct inode *inode)

	res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
	if (res < 0) {
		if (res == -ERANGE && allow_unsupported)
			return 0;
		fscrypt_warn(inode, "Error %d getting encryption context", res);
		return res;
	}

	res = fscrypt_policy_from_context(&policy, &ctx, res);
	if (res) {
		if (allow_unsupported)
			return 0;
		fscrypt_warn(inode,
			     "Unrecognized or corrupt encryption context");
		return res;
	}

	if (!fscrypt_supported_policy(&policy, inode))
	if (!fscrypt_supported_policy(&policy, inode)) {
		if (allow_unsupported)
			return 0;
		return -EINVAL;
	}

	res = fscrypt_setup_encryption_info(inode, &policy,
					    fscrypt_context_nonce(&ctx),
					    IS_CASEFOLDED(inode) &&
					    S_ISDIR(inode->i_mode));

	if (res == -ENOPKG && allow_unsupported) /* Algorithm unavailable? */
		res = 0;
	if (res == -ENOKEY)
		res = 0;
	return res;
}
EXPORT_SYMBOL(fscrypt_get_encryption_info);

/**
 * fscrypt_prepare_new_inode() - prepare to create a new inode in a directory
@@ -710,7 +724,7 @@ int fscrypt_drop_inode(struct inode *inode)
		return 0;

	/*
	 * Note: since we aren't holding ->mk_secret_sem, the result here can
	 * Note: since we aren't holding the key semaphore, the result here can
	 * immediately become outdated.  But there's no correctness problem with
	 * unnecessarily evicting.  Nor is there a correctness problem with not
	 * evicting while iput() is racing with the key being removed, since
Loading