]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
fscrypt: new helper function - fscrypt_prepare_rename()
authorEric Biggers <ebiggers@google.com>
Mon, 9 Oct 2017 19:15:42 +0000 (12:15 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 18 Oct 2017 23:52:38 +0000 (19:52 -0400)
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 9c9a53f99327f2c9387942d56defb38625bb2b78..c422367baed925e9b706bba99f9d69abe5d909b1 100644 (file)
@@ -204,4 +204,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 d7d1039eb6b5499212e4b7310f0e67b81050aa37..6af378d8126ec8c2c37728d39740567a74d522a8 100644 (file)
@@ -192,4 +192,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 80706283da757f9976d52cbea9ec26fe6a201646..40f35073145f8559be64f26084f16a80e6c4fe0d 100644 (file)
@@ -146,5 +146,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 */