]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 May 2017 10:23:23 +0000 (12:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 May 2017 10:23:23 +0000 (12:23 +0200)
added patches:
f2fs-check-entire-encrypted-bigname-when-finding-a-dentry.patch
fscrypt-avoid-collisions-when-presenting-long-encrypted-filenames.patch

queue-4.9/f2fs-check-entire-encrypted-bigname-when-finding-a-dentry.patch [new file with mode: 0644]
queue-4.9/fscrypt-avoid-collisions-when-presenting-long-encrypted-filenames.patch [new file with mode: 0644]
queue-4.9/series

diff --git a/queue-4.9/f2fs-check-entire-encrypted-bigname-when-finding-a-dentry.patch b/queue-4.9/f2fs-check-entire-encrypted-bigname-when-finding-a-dentry.patch
new file mode 100644 (file)
index 0000000..b779819
--- /dev/null
@@ -0,0 +1,159 @@
+From 6332cd32c8290a80e929fc044dc5bdba77396e33 Mon Sep 17 00:00:00 2001
+From: Jaegeuk Kim <jaegeuk@kernel.org>
+Date: Mon, 24 Apr 2017 10:00:08 -0700
+Subject: f2fs: check entire encrypted bigname when finding a dentry
+
+From: Jaegeuk Kim <jaegeuk@kernel.org>
+
+commit 6332cd32c8290a80e929fc044dc5bdba77396e33 upstream.
+
+If user has no key under an encrypted dir, fscrypt gives digested dentries.
+Previously, when looking up a dentry, f2fs only checks its hash value with
+first 4 bytes of the digested dentry, which didn't handle hash collisions fully.
+This patch enhances to check entire dentry bytes likewise ext4.
+
+Eric reported how to reproduce this issue by:
+
+ # seq -f "edir/abcdefghijklmnopqrstuvwxyz012345%.0f" 100000 | xargs touch
+ # find edir -type f | xargs stat -c %i | sort | uniq | wc -l
+100000
+ # sync
+ # echo 3 > /proc/sys/vm/drop_caches
+ # keyctl new_session
+ # find edir -type f | xargs stat -c %i | sort | uniq | wc -l
+99999
+
+Cc: <stable@vger.kernel.org>
+Reported-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+(fixed f2fs_dentry_hash() to work even when the hash is 0)
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/f2fs/dir.c    |   37 +++++++++++++++++++++----------------
+ fs/f2fs/f2fs.h   |    3 ++-
+ fs/f2fs/hash.c   |    7 ++++++-
+ fs/f2fs/inline.c |    4 ++--
+ 4 files changed, 31 insertions(+), 20 deletions(-)
+
+--- a/fs/f2fs/dir.c
++++ b/fs/f2fs/dir.c
+@@ -130,19 +130,29 @@ struct f2fs_dir_entry *find_target_dentr
+                       continue;
+               }
+-              /* encrypted case */
++              if (de->hash_code != namehash)
++                      goto not_match;
++
+               de_name.name = d->filename[bit_pos];
+               de_name.len = le16_to_cpu(de->name_len);
+-              /* show encrypted name */
+-              if (fname->hash) {
+-                      if (de->hash_code == fname->hash)
+-                              goto found;
+-              } else if (de_name.len == name->len &&
+-                      de->hash_code == namehash &&
+-                      !memcmp(de_name.name, name->name, name->len))
++#ifdef CONFIG_F2FS_FS_ENCRYPTION
++              if (unlikely(!name->name)) {
++                      if (fname->usr_fname->name[0] == '_') {
++                              if (de_name.len >= 16 &&
++                                      !memcmp(de_name.name + de_name.len - 16,
++                                              fname->crypto_buf.name + 8, 16))
++                                      goto found;
++                              goto not_match;
++                      }
++                      name->name = fname->crypto_buf.name;
++                      name->len = fname->crypto_buf.len;
++              }
++#endif
++              if (de_name.len == name->len &&
++                              !memcmp(de_name.name, name->name, name->len))
+                       goto found;
+-
++not_match:
+               if (max_slots && max_len > *max_slots)
+                       *max_slots = max_len;
+               max_len = 0;
+@@ -170,12 +180,7 @@ static struct f2fs_dir_entry *find_in_le
+       struct f2fs_dir_entry *de = NULL;
+       bool room = false;
+       int max_slots;
+-      f2fs_hash_t namehash;
+-
+-      if(fname->hash)
+-              namehash = cpu_to_le32(fname->hash);
+-      else
+-              namehash = f2fs_dentry_hash(&name);
++      f2fs_hash_t namehash = f2fs_dentry_hash(&name, fname);
+       nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
+       nblock = bucket_blocks(level);
+@@ -539,7 +544,7 @@ int f2fs_add_regular_entry(struct inode
+       level = 0;
+       slots = GET_DENTRY_SLOTS(new_name->len);
+-      dentry_hash = f2fs_dentry_hash(new_name);
++      dentry_hash = f2fs_dentry_hash(new_name, NULL);
+       current_depth = F2FS_I(dir)->i_current_depth;
+       if (F2FS_I(dir)->chash == dentry_hash) {
+--- a/fs/f2fs/f2fs.h
++++ b/fs/f2fs/f2fs.h
+@@ -2016,7 +2016,8 @@ int sanity_check_ckpt(struct f2fs_sb_inf
+ /*
+  * hash.c
+  */
+-f2fs_hash_t f2fs_dentry_hash(const struct qstr *);
++f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
++                              struct fscrypt_name *fname);
+ /*
+  * node.c
+--- a/fs/f2fs/hash.c
++++ b/fs/f2fs/hash.c
+@@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned c
+               *buf++ = pad;
+ }
+-f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
++f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
++                              struct fscrypt_name *fname)
+ {
+       __u32 hash;
+       f2fs_hash_t f2fs_hash;
+@@ -79,6 +80,10 @@ f2fs_hash_t f2fs_dentry_hash(const struc
+       const unsigned char *name = name_info->name;
+       size_t len = name_info->len;
++      /* encrypted bigname case */
++      if (fname && !fname->disk_name.name)
++              return cpu_to_le32(fname->hash);
++
+       if (is_dot_dotdot(name_info))
+               return 0;
+--- a/fs/f2fs/inline.c
++++ b/fs/f2fs/inline.c
+@@ -294,7 +294,7 @@ struct f2fs_dir_entry *find_in_inline_di
+               return NULL;
+       }
+-      namehash = f2fs_dentry_hash(&name);
++      namehash = f2fs_dentry_hash(&name, fname);
+       inline_dentry = inline_data_addr(ipage);
+@@ -531,7 +531,7 @@ int f2fs_add_inline_entry(struct inode *
+       f2fs_wait_on_page_writeback(ipage, NODE, true);
+-      name_hash = f2fs_dentry_hash(new_name);
++      name_hash = f2fs_dentry_hash(new_name, NULL);
+       make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
+       f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos);
diff --git a/queue-4.9/fscrypt-avoid-collisions-when-presenting-long-encrypted-filenames.patch b/queue-4.9/fscrypt-avoid-collisions-when-presenting-long-encrypted-filenames.patch
new file mode 100644 (file)
index 0000000..a0d036d
--- /dev/null
@@ -0,0 +1,111 @@
+From 6b06cdee81d68a8a829ad8e8d0f31d6836744af9 Mon Sep 17 00:00:00 2001
+From: Eric Biggers <ebiggers@google.com>
+Date: Mon, 24 Apr 2017 10:00:09 -0700
+Subject: fscrypt: avoid collisions when presenting long encrypted filenames
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit 6b06cdee81d68a8a829ad8e8d0f31d6836744af9 upstream.
+
+When accessing an encrypted directory without the key, userspace must
+operate on filenames derived from the ciphertext names, which contain
+arbitrary bytes.  Since we must support filenames as long as NAME_MAX,
+we can't always just base64-encode the ciphertext, since that may make
+it too long.  Currently, this is solved by presenting long names in an
+abbreviated form containing any needed filesystem-specific hashes (e.g.
+to identify a directory block), then the last 16 bytes of ciphertext.
+This needs to be sufficient to identify the actual name on lookup.
+
+However, there is a bug.  It seems to have been assumed that due to the
+use of a CBC (ciphertext block chaining)-based encryption mode, the last
+16 bytes (i.e. the AES block size) of ciphertext would depend on the
+full plaintext, preventing collisions.  However, we actually use CBC
+with ciphertext stealing (CTS), which handles the last two blocks
+specially, causing them to appear "flipped".  Thus, it's actually the
+second-to-last block which depends on the full plaintext.
+
+This caused long filenames that differ only near the end of their
+plaintexts to, when observed without the key, point to the wrong inode
+and be undeletable.  For example, with ext4:
+
+    # echo pass | e4crypt add_key -p 16 edir/
+    # seq -f "edir/abcdefghijklmnopqrstuvwxyz012345%.0f" 100000 | xargs touch
+    # find edir/ -type f | xargs stat -c %i | sort | uniq | wc -l
+    100000
+    # sync
+    # echo 3 > /proc/sys/vm/drop_caches
+    # keyctl new_session
+    # find edir/ -type f | xargs stat -c %i | sort | uniq | wc -l
+    2004
+    # rm -rf edir/
+    rm: cannot remove 'edir/_A7nNFi3rhkEQlJ6P,hdzluhODKOeWx5V': Structure needs cleaning
+    ...
+
+To fix this, when presenting long encrypted filenames, encode the
+second-to-last block of ciphertext rather than the last 16 bytes.
+
+Although it would be nice to solve this without depending on a specific
+encryption mode, that would mean doing a cryptographic hash like SHA-256
+which would be much less efficient.  This way is sufficient for now, and
+it's still compatible with encryption modes like HEH which are strong
+pseudorandom permutations.  Also, changing the presented names is still
+allowed at any time because they are only provided to allow applications
+to do things like delete encrypted directories.  They're not designed to
+be used to persistently identify files --- which would be hard to do
+anyway, given that they're encrypted after all.
+
+For ease of backports, this patch only makes the minimal fix to both
+ext4 and f2fs.  It leaves ubifs as-is, since ubifs doesn't compare the
+ciphertext block yet.  Follow-on patches will clean things up properly
+and make the filesystems use a shared helper function.
+
+Fixes: 5de0b4d0cd15 ("ext4 crypto: simplify and speed up filename encryption")
+Reported-by: Gwendal Grignou <gwendal@chromium.org>
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/crypto/fname.c |    2 +-
+ fs/ext4/namei.c   |    4 ++--
+ fs/f2fs/dir.c     |    4 ++--
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/fs/crypto/fname.c
++++ b/fs/crypto/fname.c
+@@ -300,7 +300,7 @@ int fscrypt_fname_disk_to_usr(struct ino
+       } else {
+               memset(buf, 0, 8);
+       }
+-      memcpy(buf + 8, iname->name + iname->len - 16, 16);
++      memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
+       oname->name[0] = '_';
+       oname->len = 1 + digest_encode(buf, 24, oname->name + 1);
+       return 0;
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -1255,9 +1255,9 @@ static inline int ext4_match(struct ext4
+       if (unlikely(!name)) {
+               if (fname->usr_fname->name[0] == '_') {
+                       int ret;
+-                      if (de->name_len < 16)
++                      if (de->name_len <= 32)
+                               return 0;
+-                      ret = memcmp(de->name + de->name_len - 16,
++                      ret = memcmp(de->name + ((de->name_len - 17) & ~15),
+                                    fname->crypto_buf.name + 8, 16);
+                       return (ret == 0) ? 1 : 0;
+               }
+--- a/fs/f2fs/dir.c
++++ b/fs/f2fs/dir.c
+@@ -139,8 +139,8 @@ struct f2fs_dir_entry *find_target_dentr
+ #ifdef CONFIG_F2FS_FS_ENCRYPTION
+               if (unlikely(!name->name)) {
+                       if (fname->usr_fname->name[0] == '_') {
+-                              if (de_name.len >= 16 &&
+-                                      !memcmp(de_name.name + de_name.len - 16,
++                              if (de_name.len > 32 &&
++                                      !memcmp(de_name.name + ((de_name.len - 17) & ~15),
+                                               fname->crypto_buf.name + 8, 16))
+                                       goto found;
+                               goto not_match;
index 484fd114e49ef5325ab1f38257da5118e0809b4b..bdf4bdcf412904468ef094717159a5a004a3b117 100644 (file)
@@ -72,3 +72,5 @@ pid_ns-fix-race-between-setns-ed-fork-and-zap_pid_ns_processes.patch
 usb-serial-ftdi_sio-fix-setting-latency-for-unprivileged-users.patch
 usb-serial-ftdi_sio-add-olimex-arm-usb-tiny-h-pids.patch
 usb-chaoskey-fix-alea-quirk-on-big-endian-hosts.patch
+f2fs-check-entire-encrypted-bigname-when-finding-a-dentry.patch
+fscrypt-avoid-collisions-when-presenting-long-encrypted-filenames.patch