fscrypt: new helper function - fscrypt_prepare_rename()
authorEric Biggers <ebiggers@google.com>
Mon, 9 Oct 2017 19:15:42 +0000 (12:15 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 5 Jan 2018 00:17:33 +0000 (16:17 -0800)
Introduce a helper function which prepares to rename a file into a
possibly encrypted directory.  It handles loading the encryption keys
for the source and target directories if needed, and it handles
enforcing that if the target directory (and the source directory for a
cross-rename) is encrypted, then the file being moved into the directory
has the same encryption policy as its containing directory.

Acked-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/crypto/hooks.c
include/linux/fscrypt.h
include/linux/fscrypt_notsupp.h
include/linux/fscrypt_supp.h

index 8b90217320dd121edaf7feb6042d013538062778..822cb78f9b45eb4bc0bfe1d5d42f84ffd9316f69 100644 (file)
@@ -62,3 +62,33 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir)
        return 0;
 }
 EXPORT_SYMBOL_GPL(__fscrypt_prepare_link);
+
+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;
+
+       if (old_dir != new_dir) {
+               if (IS_ENCRYPTED(new_dir) &&
+                   !fscrypt_has_permitted_context(new_dir,
+                                                  d_inode(old_dentry)))
+                       return -EPERM;
+
+               if ((flags & RENAME_EXCHANGE) &&
+                   IS_ENCRYPTED(old_dir) &&
+                   !fscrypt_has_permitted_context(old_dir,
+                                                  d_inode(new_dentry)))
+                       return -EPERM;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename);
index 6ceb76abd8428c599471e78d4ce6ff36d6d1f6b8..f3c7684d3fdf4812e2b692a04083e3804b4add4c 100644 (file)
@@ -205,4 +205,37 @@ static inline int fscrypt_prepare_link(struct dentry *old_dentry,
        return 0;
 }
 
+/**
+ * fscrypt_prepare_rename - prepare for a rename between possibly-encrypted directories
+ * @old_dir: source directory
+ * @old_dentry: dentry for source file
+ * @new_dir: target directory
+ * @new_dentry: dentry for target location (may be negative unless exchanging)
+ * @flags: rename flags (we care at least about %RENAME_EXCHANGE)
+ *
+ * Prepare for ->rename() where the source and/or target directories may be
+ * encrypted.  A new link can only be added to an encrypted directory if the
+ * directory's encryption key is available --- since otherwise we'd have no way
+ * to encrypt the filename.  A rename to an existing name, on the other hand,
+ * *is* cryptographically possible without the key.  However, we take the more
+ * conservative approach and just forbid all no-key renames.
+ *
+ * We also verify that the rename will not violate the constraint that all files
+ * in an encrypted directory tree use the same encryption policy.
+ *
+ * Return: 0 on success, -ENOKEY if an encryption key is missing, -EPERM if the
+ * rename would cause inconsistent encryption policies, or another -errno code.
+ */
+static inline int fscrypt_prepare_rename(struct inode *old_dir,
+                                        struct dentry *old_dentry,
+                                        struct inode *new_dir,
+                                        struct dentry *new_dentry,
+                                        unsigned int flags)
+{
+       if (IS_ENCRYPTED(old_dir) || IS_ENCRYPTED(new_dir))
+               return __fscrypt_prepare_rename(old_dir, old_dentry,
+                                               new_dir, new_dentry, flags);
+       return 0;
+}
+
 #endif /* _LINUX_FSCRYPT_H */
index a80917bc21026e3db091c63c5a28cb783e9f608b..480640710493e3e0eb6bc082f6f2b8a4095e6d6b 100644 (file)
@@ -193,4 +193,13 @@ static inline int __fscrypt_prepare_link(struct inode *inode,
        return -EOPNOTSUPP;
 }
 
+static inline int __fscrypt_prepare_rename(struct inode *old_dir,
+                                          struct dentry *old_dentry,
+                                          struct inode *new_dir,
+                                          struct dentry *new_dentry,
+                                          unsigned int flags)
+{
+       return -EOPNOTSUPP;
+}
+
 #endif /* _LINUX_FSCRYPT_NOTSUPP_H */
index 547cdbd04f17090429ae4c3103debc6c602d2824..0254575542c5ab32d6c1540f20582596e5de3ac5 100644 (file)
@@ -147,5 +147,10 @@ extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
 /* hooks.c */
 extern int fscrypt_file_open(struct inode *inode, struct file *filp);
 extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir);
+extern int __fscrypt_prepare_rename(struct inode *old_dir,
+                                   struct dentry *old_dentry,
+                                   struct inode *new_dir,
+                                   struct dentry *new_dentry,
+                                   unsigned int flags);
 
 #endif /* _LINUX_FSCRYPT_SUPP_H */