[2/8] fscrypt: Don't allow v1 policies with casefolding
diff mbox series

Message ID 20191203051049.44573-3-drosen@google.com
State New
Headers show
Series
  • Support for Casefolding and Encryption
Related show

Commit Message

Daniel Rosenberg Dec. 3, 2019, 5:10 a.m. UTC
Casefolding requires a derived key for computing the siphash.
This is available for v2 policies, but not v1, so we disallow it for v1.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
---
 fs/crypto/policy.c      | 26 +++++++++++++++++++++++---
 fs/inode.c              |  8 ++++++++
 include/linux/fscrypt.h |  7 +++++++
 3 files changed, 38 insertions(+), 3 deletions(-)

Comments

Eric Biggers Dec. 3, 2019, 11:37 p.m. UTC | #1
On Mon, Dec 02, 2019 at 09:10:43PM -0800, Daniel Rosenberg wrote:
> Casefolding requires a derived key for computing the siphash.
> This is available for v2 policies, but not v1, so we disallow it for v1.
> 
> Signed-off-by: Daniel Rosenberg <drosen@google.com>
> ---
>  fs/crypto/policy.c      | 26 +++++++++++++++++++++++---
>  fs/inode.c              |  8 ++++++++
>  include/linux/fscrypt.h |  7 +++++++
>  3 files changed, 38 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
> index 96f528071bed..94d96d3212d6 100644
> --- a/fs/crypto/policy.c
> +++ b/fs/crypto/policy.c
> @@ -67,9 +67,9 @@ static bool supported_iv_ino_lblk_64_policy(
>   * fscrypt_supported_policy - check whether an encryption policy is supported
>   *
>   * Given an encryption policy, check whether all its encryption modes and other
> - * settings are supported by this kernel.  (But we don't currently don't check
> - * for crypto API support here, so attempting to use an algorithm not configured
> - * into the crypto API will still fail later.)
> + * settings are supported by this kernel on the given inode.  (But we don't
> + * currently don't check for crypto API support here, so attempting to use an
> + * algorithm not configured into the crypto API will still fail later.)
>   *
>   * Return: %true if supported, else %false
>   */
> @@ -97,6 +97,12 @@ bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
>  			return false;
>  		}
>  
> +		if (IS_CASEFOLDED(inode)) {
> +			fscrypt_warn(inode,
> +				     "v1 policy does not support casefolded directories");
> +			return false;
> +		}
> +
>  		return true;
>  	}
>  	case FSCRYPT_POLICY_V2: {
> @@ -530,3 +536,17 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
>  	return preload ? fscrypt_get_encryption_info(child): 0;
>  }
>  EXPORT_SYMBOL(fscrypt_inherit_context);
> +
> +int fscrypt_set_casefolding_allowed(struct inode *inode)
> +{
> +	union fscrypt_policy policy;
> +	int ret = fscrypt_get_policy(inode, &policy);
> +
> +	if (ret < 0)
> +		return ret;

In fs/crypto/ we're trying to use 'err' rather than 'ret' when a variable can
only be 0 or a negative errno value.  I.e.:

        union fscrypt_policy policy;
        int err;

        err = fscrypt_get_policy(inode, &policy);
        if (err)
                return err;

> +
> +	if (policy.version == FSCRYPT_POLICY_V2)
> +		return 0;
> +	else
> +		return -EINVAL;
> +}

In kernel code normally an early return is used in cases like this.  I.e.:

	if (policy.version != FSCRYPT_POLICY_V2)
		return -EINVAL;

	return 0;

> @@ -2245,6 +2246,13 @@ int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
>  	    !capable(CAP_LINUX_IMMUTABLE))
>  		return -EPERM;
>  
> +	/*
> +	 * When a directory is encrypted, the CASEFOLD flag can only be turned
> +	 * on if the fscrypt policy supports it.
> +	 */
> +	if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL))
> +		return fscrypt_set_casefolding_allowed(inode);
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL(vfs_ioc_setflags_prepare);

This needs to only return early on error.  Otherwise when people add checks for
more flags later, those checks will not be executed when the CASEFOLD flag is
enabled on an encrypted directory.

I.e.:

        if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL)) {
                err = fscrypt_set_casefolding_allowed(inode);
                if (err)
                        return err;
        }

I'm also wondering if this is the right level of abstraction.
Maybe the API should be:

	err = fscrypt_ioc_setflags_prepare(inode, oldflags, flags);
	if (err)
		return err;

Then the VFS code will be "obvious", and the comment:

        /*
         * When a directory is encrypted, the CASEFOLD flag can only be turned
         * on if the fscrypt policy supports it.
         */

can be moved to the definition of fscrypt_ioc_setflags_prepare() in fs/crypto/.

- Eric

Patch
diff mbox series

diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 96f528071bed..94d96d3212d6 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -67,9 +67,9 @@  static bool supported_iv_ino_lblk_64_policy(
  * fscrypt_supported_policy - check whether an encryption policy is supported
  *
  * Given an encryption policy, check whether all its encryption modes and other
- * settings are supported by this kernel.  (But we don't currently don't check
- * for crypto API support here, so attempting to use an algorithm not configured
- * into the crypto API will still fail later.)
+ * settings are supported by this kernel on the given inode.  (But we don't
+ * currently don't check for crypto API support here, so attempting to use an
+ * algorithm not configured into the crypto API will still fail later.)
  *
  * Return: %true if supported, else %false
  */
@@ -97,6 +97,12 @@  bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
 			return false;
 		}
 
+		if (IS_CASEFOLDED(inode)) {
+			fscrypt_warn(inode,
+				     "v1 policy does not support casefolded directories");
+			return false;
+		}
+
 		return true;
 	}
 	case FSCRYPT_POLICY_V2: {
@@ -530,3 +536,17 @@  int fscrypt_inherit_context(struct inode *parent, struct inode *child,
 	return preload ? fscrypt_get_encryption_info(child): 0;
 }
 EXPORT_SYMBOL(fscrypt_inherit_context);
+
+int fscrypt_set_casefolding_allowed(struct inode *inode)
+{
+	union fscrypt_policy policy;
+	int ret = fscrypt_get_policy(inode, &policy);
+
+	if (ret < 0)
+		return ret;
+
+	if (policy.version == FSCRYPT_POLICY_V2)
+		return 0;
+	else
+		return -EINVAL;
+}
diff --git a/fs/inode.c b/fs/inode.c
index fef457a42882..b615ec272a1e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -20,6 +20,7 @@ 
 #include <linux/ratelimit.h>
 #include <linux/list_lru.h>
 #include <linux/iversion.h>
+#include <linux/fscrypt.h>
 #include <trace/events/writeback.h>
 #include "internal.h"
 
@@ -2245,6 +2246,13 @@  int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
 	    !capable(CAP_LINUX_IMMUTABLE))
 		return -EPERM;
 
+	/*
+	 * When a directory is encrypted, the CASEFOLD flag can only be turned
+	 * on if the fscrypt policy supports it.
+	 */
+	if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL))
+		return fscrypt_set_casefolding_allowed(inode);
+
 	return 0;
 }
 EXPORT_SYMBOL(vfs_ioc_setflags_prepare);
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index e13ff68a99f0..028aed925e51 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -127,6 +127,8 @@  extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *);
 extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
 extern int fscrypt_inherit_context(struct inode *, struct inode *,
 					void *, bool);
+extern int fscrypt_set_casefolding_allowed(struct inode *inode);
+
 /* keyring.c */
 extern void fscrypt_sb_free(struct super_block *sb);
 extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
@@ -361,6 +363,11 @@  static inline int fscrypt_inherit_context(struct inode *parent,
 	return -EOPNOTSUPP;
 }
 
+static inline int fscrypt_set_casefolding_allowed(struct inode *inode)
+{
+	return 0;
+}
+
 /* keyring.c */
 static inline void fscrypt_sb_free(struct super_block *sb)
 {