fscrypt: pass up error codes from ->get_context()
authorEric Biggers <ebiggers@google.com>
Mon, 5 Dec 2016 19:12:48 +0000 (11:12 -0800)
committerTheodore Ts'o <tytso@mit.edu>
Sat, 31 Dec 2016 21:26:21 +0000 (16:26 -0500)
It was possible for the ->get_context() operation to fail with a
specific error code, which was then not returned to the caller of
FS_IOC_SET_ENCRYPTION_POLICY or FS_IOC_GET_ENCRYPTION_POLICY.  Make sure
to pass through these error codes.  Also reorganize the code so that
->get_context() only needs to be called one time when setting an
encryption policy, and handle contexts of unrecognized sizes more
appropriately.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/crypto/policy.c

index 521b182dd902756d24a9cf382e70517b5668f206..4c99972899c79c5a5728cc1592ce80abcd74121c 100644 (file)
 #include <linux/mount.h>
 #include "fscrypt_private.h"
 
-static int inode_has_encryption_context(struct inode *inode)
-{
-       if (!inode->i_sb->s_cop->get_context)
-               return 0;
-       return (inode->i_sb->s_cop->get_context(inode, NULL, 0L) > 0);
-}
-
 /*
- * check whether the policy is consistent with the encryption context
- * for the inode
+ * check whether an encryption policy is consistent with an encryption context
  */
-static int is_encryption_context_consistent_with_policy(struct inode *inode,
+static bool is_encryption_context_consistent_with_policy(
+                               const struct fscrypt_context *ctx,
                                const struct fscrypt_policy *policy)
 {
-       struct fscrypt_context ctx;
-       int res;
-
-       if (!inode->i_sb->s_cop->get_context)
-               return 0;
-
-       res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
-       if (res != sizeof(ctx))
-               return 0;
-
-       return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
-                       FS_KEY_DESCRIPTOR_SIZE) == 0 &&
-                       (ctx.flags == policy->flags) &&
-                       (ctx.contents_encryption_mode ==
-                        policy->contents_encryption_mode) &&
-                       (ctx.filenames_encryption_mode ==
-                        policy->filenames_encryption_mode));
+       return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor,
+                     FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+               (ctx->flags == policy->flags) &&
+               (ctx->contents_encryption_mode ==
+                policy->contents_encryption_mode) &&
+               (ctx->filenames_encryption_mode ==
+                policy->filenames_encryption_mode);
 }
 
 static int create_encryption_context_from_policy(struct inode *inode,
@@ -90,6 +73,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
        struct fscrypt_policy policy;
        struct inode *inode = file_inode(filp);
        int ret;
+       struct fscrypt_context ctx;
 
        if (copy_from_user(&policy, arg, sizeof(policy)))
                return -EFAULT;
@@ -106,7 +90,8 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
 
        inode_lock(inode);
 
-       if (!inode_has_encryption_context(inode)) {
+       ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
+       if (ret == -ENODATA) {
                if (!S_ISDIR(inode->i_mode))
                        ret = -ENOTDIR;
                else if (!inode->i_sb->s_cop->empty_dir)
@@ -116,8 +101,13 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
                else
                        ret = create_encryption_context_from_policy(inode,
                                                                    &policy);
-       } else if (!is_encryption_context_consistent_with_policy(inode,
-                                                                &policy)) {
+       } else if (ret == sizeof(ctx) &&
+                  is_encryption_context_consistent_with_policy(&ctx,
+                                                               &policy)) {
+               /* The file already uses the same encryption policy. */
+               ret = 0;
+       } else if (ret >= 0 || ret == -ERANGE) {
+               /* The file already uses a different encryption policy. */
                ret = -EEXIST;
        }
 
@@ -140,8 +130,10 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
                return -ENODATA;
 
        res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
+       if (res < 0 && res != -ERANGE)
+               return res;
        if (res != sizeof(ctx))
-               return -ENODATA;
+               return -EINVAL;
        if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
                return -EINVAL;