Merge tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 8 May 2017 18:40:34 +0000 (11:40 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 8 May 2017 18:40:34 +0000 (11:40 -0700)
Pull fscrypt updates from Ted Ts'o:
 "Only bug fixes and cleanups for this merge window"

* tag 'fscrypt_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt:
  fscrypt: correct collision claim for digested names
  MAINTAINERS: fscrypt: update mailing list, patchwork, and git
  ext4: clean up ext4_match() and callers
  f2fs: switch to using fscrypt_match_name()
  ext4: switch to using fscrypt_match_name()
  fscrypt: introduce helper function for filename matching
  fscrypt: avoid collisions when presenting long encrypted filenames
  f2fs: check entire encrypted bigname when finding a dentry
  ubifs: check for consistent encryption contexts in ubifs_lookup()
  f2fs: sync f2fs_lookup() with ext4_lookup()
  ext4: remove "nokey" check from ext4_lookup()
  fscrypt: fix context consistency check when key(s) unavailable
  fscrypt: Remove __packed from fscrypt_policy
  fscrypt: Move key structure and constants to uapi
  fscrypt: remove fscrypt_symlink_data_len()
  fscrypt: remove unnecessary checks for NULL operations

16 files changed:
MAINTAINERS
fs/crypto/fname.c
fs/crypto/fscrypt_private.h
fs/crypto/keyinfo.c
fs/crypto/policy.c
fs/ext4/namei.c
fs/f2fs/dir.c
fs/f2fs/f2fs.h
fs/f2fs/hash.c
fs/f2fs/inline.c
fs/f2fs/namei.c
fs/ubifs/dir.c
include/linux/fscrypt_common.h
include/linux/fscrypt_notsupp.h
include/linux/fscrypt_supp.h
include/uapi/linux/fs.h

index b948dfaaacd965599083d0db81d28b725d76e6e2..08360bb0468bed0f0f5764ae8ded3c5dfde46c7e 100644 (file)
@@ -5417,10 +5417,12 @@ F:      Documentation/filesystems/caching/
 F:     fs/fscache/
 F:     include/linux/fscache*.h
 
-FS-CRYPTO: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
+FSCRYPT: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
 M:     Theodore Y. Ts'o <tytso@mit.edu>
 M:     Jaegeuk Kim <jaegeuk@kernel.org>
-L:     linux-fsdevel@vger.kernel.org
+L:     linux-fscrypt@vger.kernel.org
+Q:     https://patchwork.kernel.org/project/linux-fscrypt/list/
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt.git
 S:     Supported
 F:     fs/crypto/
 F:     include/linux/fscrypt*.h
index 37b49894c762344841117b2a0042c5e9ec8b7140..d1bb02b1ee589ecd68876ff240bf57e756a70466 100644 (file)
@@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode,
 static const char *lookup_table =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
 
+#define BASE64_CHARS(nbytes)   DIV_ROUND_UP((nbytes) * 4, 3)
+
 /**
  * digest_encode() -
  *
@@ -230,11 +232,14 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
 int fscrypt_fname_alloc_buffer(const struct inode *inode,
                                u32 ilen, struct fscrypt_str *crypto_str)
 {
-       unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
+       u32 olen = fscrypt_fname_encrypted_size(inode, ilen);
+       const u32 max_encoded_len =
+               max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
+                     1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
 
        crypto_str->len = olen;
-       if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
-               olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
+       olen = max(olen, max_encoded_len);
+
        /*
         * Allocated buffer can hold one more character to null-terminate the
         * string
@@ -266,6 +271,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer);
  *
  * The caller must have allocated sufficient memory for the @oname string.
  *
+ * If the key is available, we'll decrypt the disk name; otherwise, we'll encode
+ * it for presentation.  Short names are directly base64-encoded, while long
+ * names are encoded in fscrypt_digested_name format.
+ *
  * Return: 0 on success, -errno on failure
  */
 int fscrypt_fname_disk_to_usr(struct inode *inode,
@@ -274,7 +283,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
                        struct fscrypt_str *oname)
 {
        const struct qstr qname = FSTR_TO_QSTR(iname);
-       char buf[24];
+       struct fscrypt_digested_name digested_name;
 
        if (fscrypt_is_dot_dotdot(&qname)) {
                oname->name[0] = '.';
@@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
        if (inode->i_crypt_info)
                return fname_decrypt(inode, iname, oname);
 
-       if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) {
+       if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) {
                oname->len = digest_encode(iname->name, iname->len,
                                           oname->name);
                return 0;
        }
        if (hash) {
-               memcpy(buf, &hash, 4);
-               memcpy(buf + 4, &minor_hash, 4);
+               digested_name.hash = hash;
+               digested_name.minor_hash = minor_hash;
        } else {
-               memset(buf, 0, 8);
+               digested_name.hash = 0;
+               digested_name.minor_hash = 0;
        }
-       memcpy(buf + 8, iname->name + iname->len - 16, 16);
+       memcpy(digested_name.digest,
+              FSCRYPT_FNAME_DIGEST(iname->name, iname->len),
+              FSCRYPT_FNAME_DIGEST_SIZE);
        oname->name[0] = '_';
-       oname->len = 1 + digest_encode(buf, 24, oname->name + 1);
+       oname->len = 1 + digest_encode((const char *)&digested_name,
+                                      sizeof(digested_name), oname->name + 1);
        return 0;
 }
 EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
@@ -336,10 +349,35 @@ int fscrypt_fname_usr_to_disk(struct inode *inode,
 }
 EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
 
+/**
+ * fscrypt_setup_filename() - prepare to search a possibly encrypted directory
+ * @dir: the directory that will be searched
+ * @iname: the user-provided filename being searched for
+ * @lookup: 1 if we're allowed to proceed without the key because it's
+ *     ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot
+ *     proceed without the key because we're going to create the dir_entry.
+ * @fname: the filename information to be filled in
+ *
+ * Given a user-provided filename @iname, this function sets @fname->disk_name
+ * to the name that would be stored in the on-disk directory entry, if possible.
+ * If the directory is unencrypted this is simply @iname.  Else, if we have the
+ * directory's encryption key, then @iname is the plaintext, so we encrypt it to
+ * get the disk_name.
+ *
+ * Else, for keyless @lookup operations, @iname is the presented ciphertext, so
+ * we decode it to get either the ciphertext disk_name (for short names) or the
+ * fscrypt_digested_name (for long names).  Non-@lookup operations will be
+ * impossible in this case, so we fail them with ENOKEY.
+ *
+ * If successful, fscrypt_free_filename() must be called later to clean up.
+ *
+ * Return: 0 on success, -errno on failure
+ */
 int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
                              int lookup, struct fscrypt_name *fname)
 {
-       int ret = 0, bigname = 0;
+       int ret;
+       int digested;
 
        memset(fname, 0, sizeof(struct fscrypt_name));
        fname->usr_fname = iname;
@@ -373,25 +411,37 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
         * We don't have the key and we are doing a lookup; decode the
         * user-supplied name
         */
-       if (iname->name[0] == '_')
-               bigname = 1;
-       if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
-               return -ENOENT;
+       if (iname->name[0] == '_') {
+               if (iname->len !=
+                   1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)))
+                       return -ENOENT;
+               digested = 1;
+       } else {
+               if (iname->len >
+                   BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE))
+                       return -ENOENT;
+               digested = 0;
+       }
 
-       fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
+       fname->crypto_buf.name =
+               kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE,
+                             sizeof(struct fscrypt_digested_name)),
+                       GFP_KERNEL);
        if (fname->crypto_buf.name == NULL)
                return -ENOMEM;
 
-       ret = digest_decode(iname->name + bigname, iname->len - bigname,
+       ret = digest_decode(iname->name + digested, iname->len - digested,
                                fname->crypto_buf.name);
        if (ret < 0) {
                ret = -ENOENT;
                goto errout;
        }
        fname->crypto_buf.len = ret;
-       if (bigname) {
-               memcpy(&fname->hash, fname->crypto_buf.name, 4);
-               memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
+       if (digested) {
+               const struct fscrypt_digested_name *n =
+                       (const void *)fname->crypto_buf.name;
+               fname->hash = n->hash;
+               fname->minor_hash = n->minor_hash;
        } else {
                fname->disk_name.name = fname->crypto_buf.name;
                fname->disk_name.len = fname->crypto_buf.len;
index e39696e644942a80d110035e2d49570840823af7..1e1f8a361b75479f3051cb924207f633cfbe8487 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <linux/fscrypt_supp.h>
 
-#define FS_FNAME_CRYPTO_DIGEST_SIZE    32
-
 /* Encryption parameters */
 #define FS_XTS_TWEAK_SIZE              16
 #define FS_AES_128_ECB_KEY_SIZE                16
 #define FS_AES_256_CBC_KEY_SIZE                32
 #define FS_AES_256_CTS_KEY_SIZE                32
 #define FS_AES_256_XTS_KEY_SIZE                64
-#define FS_MAX_KEY_SIZE                        64
-
-#define FS_KEY_DESC_PREFIX             "fscrypt:"
-#define FS_KEY_DESC_PREFIX_SIZE                8
 
 #define FS_KEY_DERIVATION_NONCE_SIZE           16
 
@@ -51,13 +45,6 @@ struct fscrypt_context {
 
 #define FS_ENCRYPTION_CONTEXT_FORMAT_V1                1
 
-/* This is passed in from userspace into the kernel keyring */
-struct fscrypt_key {
-       u32 mode;
-       u8 raw[FS_MAX_KEY_SIZE];
-       u32 size;
-} __packed;
-
 /*
  * A pointer to this structure is stored in the file system's in-core
  * representation of an inode.
index 8cdfddce2b34868f0cfe3f71da55d64187172a38..179e578b875b1abbc58fbb2d3fd634b3608243ad 100644 (file)
@@ -183,9 +183,6 @@ int fscrypt_get_encryption_info(struct inode *inode)
        if (res)
                return res;
 
-       if (!inode->i_sb->s_cop->get_context)
-               return -EOPNOTSUPP;
-
        res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
        if (res < 0) {
                if (!fscrypt_dummy_context_enabled(inode) ||
index 4908906d54d562263093cd5245fcb14e36d18b8e..210976e7a269ff0760cf81f6b1975632f9876862 100644 (file)
@@ -34,9 +34,6 @@ static int create_encryption_context_from_policy(struct inode *inode,
 {
        struct fscrypt_context ctx;
 
-       if (!inode->i_sb->s_cop->set_context)
-               return -EOPNOTSUPP;
-
        ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
        memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
                                        FS_KEY_DESCRIPTOR_SIZE);
@@ -87,8 +84,6 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
        if (ret == -ENODATA) {
                if (!S_ISDIR(inode->i_mode))
                        ret = -ENOTDIR;
-               else if (!inode->i_sb->s_cop->empty_dir)
-                       ret = -EOPNOTSUPP;
                else if (!inode->i_sb->s_cop->empty_dir(inode))
                        ret = -ENOTEMPTY;
                else
@@ -118,8 +113,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
        struct fscrypt_policy policy;
        int res;
 
-       if (!inode->i_sb->s_cop->get_context ||
-                       !inode->i_sb->s_cop->is_encrypted(inode))
+       if (!inode->i_sb->s_cop->is_encrypted(inode))
                return -ENODATA;
 
        res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
@@ -143,27 +137,61 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
 }
 EXPORT_SYMBOL(fscrypt_ioctl_get_policy);
 
+/**
+ * fscrypt_has_permitted_context() - is a file's encryption policy permitted
+ *                                  within its directory?
+ *
+ * @parent: inode for parent directory
+ * @child: inode for file being looked up, opened, or linked into @parent
+ *
+ * Filesystems must call this before permitting access to an inode in a
+ * situation where the parent directory is encrypted (either before allowing
+ * ->lookup() to succeed, or for a regular file before allowing it to be opened)
+ * and before any operation that involves linking an inode into an encrypted
+ * directory, including link, rename, and cross rename.  It enforces the
+ * constraint that within a given encrypted directory tree, all files use the
+ * same encryption policy.  The pre-access check is needed to detect potentially
+ * malicious offline violations of this constraint, while the link and rename
+ * checks are needed to prevent online violations of this constraint.
+ *
+ * Return: 1 if permitted, 0 if forbidden.  If forbidden, the caller must fail
+ * the filesystem operation with EPERM.
+ */
 int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
 {
-       struct fscrypt_info *parent_ci, *child_ci;
+       const struct fscrypt_operations *cops = parent->i_sb->s_cop;
+       const struct fscrypt_info *parent_ci, *child_ci;
+       struct fscrypt_context parent_ctx, child_ctx;
        int res;
 
-       if ((parent == NULL) || (child == NULL)) {
-               printk(KERN_ERR "parent %p child %p\n", parent, child);
-               BUG_ON(1);
-       }
-
        /* No restrictions on file types which are never encrypted */
        if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
            !S_ISLNK(child->i_mode))
                return 1;
 
-       /* no restrictions if the parent directory is not encrypted */
-       if (!parent->i_sb->s_cop->is_encrypted(parent))
+       /* No restrictions if the parent directory is unencrypted */
+       if (!cops->is_encrypted(parent))
                return 1;
-       /* if the child directory is not encrypted, this is always a problem */
-       if (!parent->i_sb->s_cop->is_encrypted(child))
+
+       /* Encrypted directories must not contain unencrypted files */
+       if (!cops->is_encrypted(child))
                return 0;
+
+       /*
+        * Both parent and child are encrypted, so verify they use the same
+        * encryption policy.  Compare the fscrypt_info structs if the keys are
+        * available, otherwise retrieve and compare the fscrypt_contexts.
+        *
+        * Note that the fscrypt_context retrieval will be required frequently
+        * when accessing an encrypted directory tree without the key.
+        * Performance-wise this is not a big deal because we already don't
+        * really optimize for file access without the key (to the extent that
+        * such access is even possible), given that any attempted access
+        * already causes a fscrypt_context retrieval and keyring search.
+        *
+        * In any case, if an unexpected error occurs, fall back to "forbidden".
+        */
+
        res = fscrypt_get_encryption_info(parent);
        if (res)
                return 0;
@@ -172,17 +200,32 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
                return 0;
        parent_ci = parent->i_crypt_info;
        child_ci = child->i_crypt_info;
-       if (!parent_ci && !child_ci)
-               return 1;
-       if (!parent_ci || !child_ci)
+
+       if (parent_ci && child_ci) {
+               return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
+                             FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+                       (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+                       (parent_ci->ci_filename_mode ==
+                        child_ci->ci_filename_mode) &&
+                       (parent_ci->ci_flags == child_ci->ci_flags);
+       }
+
+       res = cops->get_context(parent, &parent_ctx, sizeof(parent_ctx));
+       if (res != sizeof(parent_ctx))
                return 0;
 
-       return (memcmp(parent_ci->ci_master_key,
-                       child_ci->ci_master_key,
-                       FS_KEY_DESCRIPTOR_SIZE) == 0 &&
-               (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
-               (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
-               (parent_ci->ci_flags == child_ci->ci_flags));
+       res = cops->get_context(child, &child_ctx, sizeof(child_ctx));
+       if (res != sizeof(child_ctx))
+               return 0;
+
+       return memcmp(parent_ctx.master_key_descriptor,
+                     child_ctx.master_key_descriptor,
+                     FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+               (parent_ctx.contents_encryption_mode ==
+                child_ctx.contents_encryption_mode) &&
+               (parent_ctx.filenames_encryption_mode ==
+                child_ctx.filenames_encryption_mode) &&
+               (parent_ctx.flags == child_ctx.flags);
 }
 EXPORT_SYMBOL(fscrypt_has_permitted_context);
 
@@ -202,9 +245,6 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
        struct fscrypt_info *ci;
        int res;
 
-       if (!parent->i_sb->s_cop->set_context)
-               return -EOPNOTSUPP;
-
        res = fscrypt_get_encryption_info(parent);
        if (res < 0)
                return res;
index 6577a3c458151342771842357552a225bc9e0d16..b81f7d46f344d482d4a7ab15a5cc1ad3654b3259 100644 (file)
@@ -1237,37 +1237,24 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
 }
 
 /*
- * NOTE! unlike strncmp, ext4_match returns 1 for success, 0 for failure.
+ * Test whether a directory entry matches the filename being searched for.
  *
- * `len <= EXT4_NAME_LEN' is guaranteed by caller.
- * `de != NULL' is guaranteed by caller.
+ * Return: %true if the directory entry matches, otherwise %false.
  */
-static inline int ext4_match(struct ext4_filename *fname,
-                            struct ext4_dir_entry_2 *de)
+static inline bool ext4_match(const struct ext4_filename *fname,
+                             const struct ext4_dir_entry_2 *de)
 {
-       const void *name = fname_name(fname);
-       u32 len = fname_len(fname);
+       struct fscrypt_name f;
 
        if (!de->inode)
-               return 0;
+               return false;
 
+       f.usr_fname = fname->usr_fname;
+       f.disk_name = fname->disk_name;
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       if (unlikely(!name)) {
-               if (fname->usr_fname->name[0] == '_') {
-                       int ret;
-                       if (de->name_len < 16)
-                               return 0;
-                       ret = memcmp(de->name + de->name_len - 16,
-                                    fname->crypto_buf.name + 8, 16);
-                       return (ret == 0) ? 1 : 0;
-               }
-               name = fname->crypto_buf.name;
-               len = fname->crypto_buf.len;
-       }
+       f.crypto_buf = fname->crypto_buf;
 #endif
-       if (de->name_len != len)
-               return 0;
-       return (memcmp(de->name, name, len) == 0) ? 1 : 0;
+       return fscrypt_match_name(&f, de->name, de->name_len);
 }
 
 /*
@@ -1281,48 +1268,31 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
        struct ext4_dir_entry_2 * de;
        char * dlimit;
        int de_len;
-       int res;
 
        de = (struct ext4_dir_entry_2 *)search_buf;
        dlimit = search_buf + buf_size;
        while ((char *) de < dlimit) {
                /* this code is executed quadratically often */
                /* do minimal checking `by hand' */
-               if ((char *) de + de->name_len <= dlimit) {
-                       res = ext4_match(fname, de);
-                       if (res < 0) {
-                               res = -1;
-                               goto return_result;
-                       }
-                       if (res > 0) {
-                               /* found a match - just to be sure, do
-                                * a full check */
-                               if (ext4_check_dir_entry(dir, NULL, de, bh,
-                                               bh->b_data,
-                                                bh->b_size, offset)) {
-                                       res = -1;
-                                       goto return_result;
-                               }
-                               *res_dir = de;
-                               res = 1;
-                               goto return_result;
-                       }
-
+               if ((char *) de + de->name_len <= dlimit &&
+                   ext4_match(fname, de)) {
+                       /* found a match - just to be sure, do
+                        * a full check */
+                       if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+                                                bh->b_size, offset))
+                               return -1;
+                       *res_dir = de;
+                       return 1;
                }
                /* prevent looping on a bad block */
                de_len = ext4_rec_len_from_disk(de->rec_len,
                                                dir->i_sb->s_blocksize);
-               if (de_len <= 0) {
-                       res = -1;
-                       goto return_result;
-               }
+               if (de_len <= 0)
+                       return -1;
                offset += de_len;
                de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
        }
-
-       res = 0;
-return_result:
-       return res;
+       return 0;
 }
 
 static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
@@ -1616,16 +1586,9 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
                if (!IS_ERR(inode) && ext4_encrypted_inode(dir) &&
                    (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
                    !fscrypt_has_permitted_context(dir, inode)) {
-                       int nokey = ext4_encrypted_inode(inode) &&
-                               !fscrypt_has_encryption_key(inode);
-                       if (nokey) {
-                               iput(inode);
-                               return ERR_PTR(-ENOKEY);
-                       }
                        ext4_warning(inode->i_sb,
                                     "Inconsistent encryption contexts: %lu/%lu",
-                                    (unsigned long) dir->i_ino,
-                                    (unsigned long) inode->i_ino);
+                                    dir->i_ino, inode->i_ino);
                        iput(inode);
                        return ERR_PTR(-EPERM);
                }
@@ -1833,24 +1796,15 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
        int nlen, rlen;
        unsigned int offset = 0;
        char *top;
-       int res;
 
        de = (struct ext4_dir_entry_2 *)buf;
        top = buf + buf_size - reclen;
        while ((char *) de <= top) {
                if (ext4_check_dir_entry(dir, NULL, de, bh,
-                                        buf, buf_size, offset)) {
-                       res = -EFSCORRUPTED;
-                       goto return_result;
-               }
-               /* Provide crypto context and crypto buffer to ext4 match */
-               res = ext4_match(fname, de);
-               if (res < 0)
-                       goto return_result;
-               if (res > 0) {
-                       res = -EEXIST;
-                       goto return_result;
-               }
+                                        buf, buf_size, offset))
+                       return -EFSCORRUPTED;
+               if (ext4_match(fname, de))
+                       return -EEXIST;
                nlen = EXT4_DIR_REC_LEN(de->name_len);
                rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
                if ((de->inode ? rlen - nlen : rlen) >= reclen)
@@ -1858,15 +1812,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
                de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
                offset += rlen;
        }
-
        if ((char *) de > top)
-               res = -ENOSPC;
-       else {
-               *dest_de = de;
-               res = 0;
-       }
-return_result:
-       return res;
+               return -ENOSPC;
+
+       *dest_de = de;
+       return 0;
 }
 
 void ext4_insert_dentry(struct inode *inode,
index 8d5c62b07b283f53e90ded2366c8bb9375409fa2..e640870528349ecd67dae9bd4688bedc4fa84333 100644 (file)
@@ -111,8 +111,6 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
        struct f2fs_dir_entry *de;
        unsigned long bit_pos = 0;
        int max_len = 0;
-       struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
-       struct fscrypt_str *name = &fname->disk_name;
 
        if (max_slots)
                *max_slots = 0;
@@ -130,17 +128,9 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
                        continue;
                }
 
-               /* encrypted case */
-               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 == cpu_to_le32(fname->hash))
-                               goto found;
-               } else if (de_name.len == name->len &&
-                       de->hash_code == namehash &&
-                       !memcmp(de_name.name, name->name, name->len))
+               if (de->hash_code == namehash &&
+                   fscrypt_match_name(fname, d->filename[bit_pos],
+                                      le16_to_cpu(de->name_len)))
                        goto found;
 
                if (max_slots && max_len > *max_slots)
@@ -170,12 +160,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
        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);
@@ -542,7 +527,7 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
 
        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) {
index 0a6e115562f62edca5b60ee4c833e889a904c202..05d7e2cefc566d73115de05a108c21dcc2df0756 100644 (file)
@@ -2133,7 +2133,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi);
 /*
  * hash.c
  */
-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);
 
 /*
  * node.c
index 71b7206c431ea00f937e5b1e59b5f90999091d2a..eb2e031ea887bed43229f63e9923986bc14f3955 100644 (file)
@@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len,
                *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 struct qstr *name_info)
        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;
 
index e32a9e5279686035829dba5d45396e22cf10438f..fa729ff6b2f924dd34630fd40a4c7618220bba63 100644 (file)
@@ -296,7 +296,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
                return NULL;
        }
 
-       namehash = f2fs_dentry_hash(&name);
+       namehash = f2fs_dentry_hash(&name, fname);
 
        inline_dentry = inline_data_addr(ipage);
 
@@ -533,7 +533,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
 
        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);
 
index 98f00a3a7f5016a42741169736f1182efe83a3b0..9a5b9fa553181959174dc18f99efc764b558ade4 100644 (file)
@@ -324,9 +324,10 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
        if (f2fs_encrypted_inode(dir) &&
            (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
            !fscrypt_has_permitted_context(dir, inode)) {
-               bool nokey = f2fs_encrypted_inode(inode) &&
-                       !fscrypt_has_encryption_key(inode);
-               err = nokey ? -ENOKEY : -EPERM;
+               f2fs_msg(inode->i_sb, KERN_WARNING,
+                        "Inconsistent encryption contexts: %lu/%lu",
+                        dir->i_ino, inode->i_ino);
+               err = -EPERM;
                goto err_out;
        }
        return d_splice_alias(inode, dentry);
index b777bddaa1dda9f2768952dc562e06df6b068d32..8049851cac42ff50e3f1280e62b1845ab7a7720a 100644 (file)
@@ -285,6 +285,15 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
                goto out_dent;
        }
 
+       if (ubifs_crypt_is_encrypted(dir) &&
+           (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
+           !fscrypt_has_permitted_context(dir, inode)) {
+               ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu",
+                          dir->i_ino, inode->i_ino);
+               err = -EPERM;
+               goto out_inode;
+       }
+
 done:
        kfree(dent);
        fscrypt_free_filename(&nm);
@@ -295,6 +304,8 @@ done:
        d_add(dentry, inode);
        return NULL;
 
+out_inode:
+       iput(inode);
 out_dent:
        kfree(dent);
 out_fname:
index 10c1abfbac6c45d1049fdf9f1b1f133371521461..0a30c106c1e535188637d7947a74f77c933968a1 100644 (file)
@@ -46,17 +46,6 @@ struct fscrypt_symlink_data {
        char encrypted_path[1];
 } __packed;
 
-/**
- * This function is used to calculate the disk space required to
- * store a filename of length l in encrypted symlink format.
- */
-static inline u32 fscrypt_symlink_data_len(u32 l)
-{
-       if (l < FS_CRYPTO_BLOCK_SIZE)
-               l = FS_CRYPTO_BLOCK_SIZE;
-       return (l + sizeof(struct fscrypt_symlink_data) - 1);
-}
-
 struct fscrypt_str {
        unsigned char *name;
        u32 len;
index 3511ca7988043e75440886f0530abae2dde1f1d8..ec406aed2f2f8c3331d0002fba8a83036e75c08b 100644 (file)
@@ -147,6 +147,15 @@ static inline int fscrypt_fname_usr_to_disk(struct inode *inode,
        return -EOPNOTSUPP;
 }
 
+static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
+                                     const u8 *de_name, u32 de_name_len)
+{
+       /* Encryption support disabled; use standard comparison */
+       if (de_name_len != fname->disk_name.len)
+               return false;
+       return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
+}
+
 /* bio.c */
 static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx,
                                             struct bio *bio)
index a140f47e9b271212ebc0a6ecc6a1db0527e37afe..cd4e82c17304ffbe97868b4aec146ca623ec2d78 100644 (file)
@@ -57,6 +57,80 @@ extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32,
 extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
                        struct fscrypt_str *);
 
+#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE      32
+
+/* Extracts the second-to-last ciphertext block; see explanation below */
+#define FSCRYPT_FNAME_DIGEST(name, len)        \
+       ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \
+                            FS_CRYPTO_BLOCK_SIZE))
+
+#define FSCRYPT_FNAME_DIGEST_SIZE      FS_CRYPTO_BLOCK_SIZE
+
+/**
+ * fscrypt_digested_name - alternate identifier for an on-disk filename
+ *
+ * When userspace lists an encrypted directory without access to the key,
+ * filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE
+ * bytes are shown in this abbreviated form (base64-encoded) rather than as the
+ * full ciphertext (base64-encoded).  This is necessary to allow supporting
+ * filenames up to NAME_MAX bytes, since base64 encoding expands the length.
+ *
+ * To make it possible for filesystems to still find the correct directory entry
+ * despite not knowing the full on-disk name, we encode any filesystem-specific
+ * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups,
+ * followed by the second-to-last ciphertext block of the filename.  Due to the
+ * use of the CBC-CTS encryption mode, the second-to-last ciphertext block
+ * depends on the full plaintext.  (Note that ciphertext stealing causes the
+ * last two blocks to appear "flipped".)  This makes accidental collisions very
+ * unlikely: just a 1 in 2^128 chance for two filenames to collide even if they
+ * share the same filesystem-specific hashes.
+ *
+ * However, this scheme isn't immune to intentional collisions, which can be
+ * created by anyone able to create arbitrary plaintext filenames and view them
+ * without the key.  Making the "digest" be a real cryptographic hash like
+ * SHA-256 over the full ciphertext would prevent this, although it would be
+ * less efficient and harder to implement, especially since the filesystem would
+ * need to calculate it for each directory entry examined during a search.
+ */
+struct fscrypt_digested_name {
+       u32 hash;
+       u32 minor_hash;
+       u8 digest[FSCRYPT_FNAME_DIGEST_SIZE];
+};
+
+/**
+ * fscrypt_match_name() - test whether the given name matches a directory entry
+ * @fname: the name being searched for
+ * @de_name: the name from the directory entry
+ * @de_name_len: the length of @de_name in bytes
+ *
+ * Normally @fname->disk_name will be set, and in that case we simply compare
+ * that to the name stored in the directory entry.  The only exception is that
+ * if we don't have the key for an encrypted directory and a filename in it is
+ * very long, then we won't have the full disk_name and we'll instead need to
+ * match against the fscrypt_digested_name.
+ *
+ * Return: %true if the name matches, otherwise %false.
+ */
+static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
+                                     const u8 *de_name, u32 de_name_len)
+{
+       if (unlikely(!fname->disk_name.name)) {
+               const struct fscrypt_digested_name *n =
+                       (const void *)fname->crypto_buf.name;
+               if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_'))
+                       return false;
+               if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)
+                       return false;
+               return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len),
+                              n->digest, FSCRYPT_FNAME_DIGEST_SIZE);
+       }
+
+       if (de_name_len != fname->disk_name.len)
+               return false;
+       return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
+}
+
 /* bio.c */
 extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
 extern void fscrypt_pullback_bio_page(struct page **, bool);
index 048a85e9f0174170bc9cf861a388d08e19d1f36c..24e61a54feaaab505b2d2e74e5462a8638d0c897 100644 (file)
@@ -279,12 +279,25 @@ struct fscrypt_policy {
        __u8 filenames_encryption_mode;
        __u8 flags;
        __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
-} __packed;
+};
 
 #define FS_IOC_SET_ENCRYPTION_POLICY   _IOR('f', 19, struct fscrypt_policy)
 #define FS_IOC_GET_ENCRYPTION_PWSALT   _IOW('f', 20, __u8[16])
 #define FS_IOC_GET_ENCRYPTION_POLICY   _IOW('f', 21, struct fscrypt_policy)
 
+/* Parameters for passing an encryption key into the kernel keyring */
+#define FS_KEY_DESC_PREFIX             "fscrypt:"
+#define FS_KEY_DESC_PREFIX_SIZE                8
+
+/* Structure that userspace passes to the kernel keyring */
+#define FS_MAX_KEY_SIZE                        64
+
+struct fscrypt_key {
+       __u32 mode;
+       __u8 raw[FS_MAX_KEY_SIZE];
+       __u32 size;
+};
+
 /*
  * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
  *