ext4 crypto: enforce context consistency
authorTheodore Ts'o <tytso@mit.edu>
Sun, 12 Apr 2015 04:55:08 +0000 (00:55 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 12 Apr 2015 04:55:08 +0000 (00:55 -0400)
Enforce the following inheritance policy:

1) An unencrypted directory may contain encrypted or unencrypted files
or directories.

2) All files or directories in a directory must be protected using the
same key as their containing directory.

As a result, assuming the following setup:

mke2fs -t ext4 -Fq -O encrypt /dev/vdc
mount -t ext4 /dev/vdc /vdc
mkdir /vdc/a /vdc/b /vdc/c
echo foo | e4crypt add_key /vdc/a
echo bar | e4crypt add_key /vdc/b
for i in a b c ; do cp /etc/motd /vdc/$i/motd-$i ; done

Then we will see the following results:

cd /vdc
mv a b # will fail; /vdc/a and /vdc/b have different keys
mv b/motd-b a # will fail, see above
ln a/motd-a b # will fail, see above
mv c a      # will fail; all inodes in an encrypted directory
       # must be encrypted
ln c/motd-c b # will fail, see above
mv a/motd-a c # will succeed
mv c/motd-a a # will succeed

Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/namei.c

index 0dbd2d2..acd7919 100644 (file)
@@ -1416,6 +1416,18 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
                                         ino);
                        return ERR_PTR(-EIO);
                }
+               if (!IS_ERR(inode) && ext4_encrypted_inode(dir) &&
+                   (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+                    S_ISLNK(inode->i_mode)) &&
+                   !ext4_is_child_context_consistent_with_parent(dir,
+                                                                 inode)) {
+                       iput(inode);
+                       ext4_warning(inode->i_sb,
+                                    "Inconsistent encryption contexts: %lu/%lu\n",
+                                    (unsigned long) dir->i_ino,
+                                    (unsigned long) inode->i_ino);
+                       return ERR_PTR(-EPERM);
+               }
        }
        return d_splice_alias(inode, dentry);
 }
@@ -2944,7 +2956,9 @@ static int ext4_link(struct dentry *old_dentry,
 
        if (inode->i_nlink >= EXT4_LINK_MAX)
                return -EMLINK;
-
+       if (ext4_encrypted_inode(dir) &&
+           !ext4_is_child_context_consistent_with_parent(dir, inode))
+               return -EPERM;
        dquot_initialize(dir);
 
 retry:
@@ -3245,6 +3259,14 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino)
                goto end_rename;
 
+       if ((old.dir != new.dir) &&
+           ext4_encrypted_inode(new.dir) &&
+           !ext4_is_child_context_consistent_with_parent(new.dir,
+                                                         old.inode)) {
+               retval = -EPERM;
+               goto end_rename;
+       }
+
        new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
                                 &new.de, &new.inlined);
        if (IS_ERR(new.bh)) {