ubifs: Add skeleton for fscrypto
authorRichard Weinberger <richard@nod.at>
Thu, 20 Oct 2016 14:47:56 +0000 (16:47 +0200)
committerRichard Weinberger <richard@nod.at>
Mon, 12 Dec 2016 22:07:38 +0000 (23:07 +0100)
This is the first building block to provide file level
encryption on UBIFS.

Signed-off-by: Richard Weinberger <richard@nod.at>
fs/ubifs/Kconfig
fs/ubifs/Makefile
fs/ubifs/crypto.c [new file with mode: 0644]
fs/ubifs/dir.c
fs/ubifs/ioctl.c
fs/ubifs/super.c
fs/ubifs/ubifs-media.h
fs/ubifs/ubifs.h
fs/ubifs/xattr.c

index 7ff7712f284e030baf4e90a268c0577429497574..0a908ae7af1382d46efa5c8073300db4680ce7e2 100644 (file)
@@ -50,3 +50,14 @@ config UBIFS_ATIME_SUPPORT
          strictatime is the "heavy", relatime is "lighter", etc.
 
          If unsure, say 'N'
+
+config UBIFS_FS_ENCRYPTION
+       bool "UBIFS Encryption"
+       depends on UBIFS_FS
+       select FS_ENCRYPTION
+       default n
+       help
+         Enable encryption of UBIFS files and directories. This
+         feature is similar to ecryptfs, but it is more memory
+         efficient since it avoids caching the encrypted and
+         decrypted pages in the page cache.
index c54a24360f853139435a8741253baa268a88291a..6f3251c2bf088e7bd205662277e1a49b8471751b 100644 (file)
@@ -5,3 +5,4 @@ ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o
 ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
 ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o xattr.o debug.o
 ubifs-y += misc.o
+ubifs-$(CONFIG_UBIFS_FS_ENCRYPTION) += crypto.o
diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c
new file mode 100644 (file)
index 0000000..12a0072
--- /dev/null
@@ -0,0 +1,46 @@
+#include "ubifs.h"
+
+static int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+       return ubifs_xattr_get(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
+                              ctx, len);
+}
+
+static int ubifs_crypt_set_context(struct inode *inode, const void *ctx,
+                                  size_t len, void *fs_data)
+{
+       return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
+                              ctx, len, 0);
+}
+
+static bool ubifs_crypt_empty_dir(struct inode *inode)
+{
+       return ubifs_check_dir_empty(inode) == 0;
+}
+
+static unsigned int ubifs_crypt_max_namelen(struct inode *inode)
+{
+       if (S_ISLNK(inode->i_mode))
+               return UBIFS_MAX_INO_DATA;
+       else
+               return UBIFS_MAX_NLEN;
+}
+
+static int ubifs_key_prefix(struct inode *inode, u8 **key)
+{
+       static char prefix[] = "ubifs:";
+
+       *key = prefix;
+
+       return sizeof(prefix) - 1;
+}
+
+struct fscrypt_operations ubifs_crypt_operations = {
+       .flags                  = FS_CFLG_INPLACE_ENCRYPTION,
+       .get_context            = ubifs_crypt_get_context,
+       .set_context            = ubifs_crypt_set_context,
+       .is_encrypted           = ubifs_crypt_is_encrypted,
+       .empty_dir              = ubifs_crypt_empty_dir,
+       .max_namelen            = ubifs_crypt_max_namelen,
+       .key_prefix             = ubifs_key_prefix,
+};
index 14a226d47f4c4383e9e6940ea30458a447e9241f..2315cb864c39f24860485c19d832024f2977672b 100644 (file)
@@ -85,11 +85,26 @@ static int inherit_flags(const struct inode *dir, umode_t mode)
  * initializes it. Returns new inode in case of success and an error code in
  * case of failure.
  */
-struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
                              umode_t mode)
 {
+       int err;
        struct inode *inode;
        struct ubifs_inode *ui;
+       bool encrypted = false;
+
+       if (ubifs_crypt_is_encrypted(dir)) {
+               err = fscrypt_get_encryption_info(dir);
+               if (err) {
+                       ubifs_err(c, "fscrypt_get_encryption_info failed: %i", err);
+                       return ERR_PTR(err);
+               }
+
+               if (!fscrypt_has_encryption_key(dir))
+                       return ERR_PTR(-EPERM);
+
+               encrypted = true;
+       }
 
        inode = new_inode(c->vfs_sb);
        ui = ubifs_inode(inode);
@@ -165,6 +180,17 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
         */
        ui->creat_sqnum = ++c->max_sqnum;
        spin_unlock(&c->cnt_lock);
+
+       if (encrypted) {
+               err = fscrypt_inherit_context(dir, inode, &encrypted, true);
+               if (err) {
+                       ubifs_err(c, "fscrypt_inherit_context failed: %i", err);
+                       make_bad_inode(inode);
+                       iput(inode);
+                       return ERR_PTR(err);
+               }
+       }
+
        return inode;
 }
 
index 3c7b29de0ca7353c039bdbb56be5c3e2d6b136f9..6bb5b35050de5f2912328d1f1e5c561818d1e93e 100644 (file)
@@ -181,6 +181,41 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                mnt_drop_write_file(file);
                return err;
        }
+       case FS_IOC_SET_ENCRYPTION_POLICY: {
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+               struct fscrypt_policy policy;
+
+               if (copy_from_user(&policy,
+                                  (struct fscrypt_policy __user *)arg,
+                                  sizeof(policy)))
+                       return -EFAULT;
+
+               err = fscrypt_process_policy(file, &policy);
+
+               return err;
+#else
+               return -EOPNOTSUPP;
+#endif
+       }
+       case FS_IOC_GET_ENCRYPTION_POLICY: {
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+               struct fscrypt_policy policy;
+
+               if (!ubifs_crypt_is_encrypted(inode))
+                       return -ENOENT;
+
+               err = fscrypt_get_policy(inode, &policy);
+               if (err)
+                       return err;
+
+               if (copy_to_user((void __user *)arg, &policy, sizeof(policy)))
+                       return -EFAULT;
+
+               return 0;
+#else
+               return -EOPNOTSUPP;
+#endif
+       }
 
        default:
                return -ENOTTY;
index 4ec051089186ea2aef6bb4f96e22fefa24a10777..e85d5a47aeac4fedf5c75e71f60818c5d21f8e36 100644 (file)
@@ -380,6 +380,9 @@ out:
        }
 done:
        clear_inode(inode);
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+       fscrypt_put_encryption_info(inode, NULL);
+#endif
 }
 
 static void ubifs_dirty_inode(struct inode *inode, int flags)
@@ -1995,6 +1998,12 @@ static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
        return c;
 }
 
+#ifndef CONFIG_UBIFS_FS_ENCRYPTION
+struct fscrypt_operations ubifs_crypt_operations = {
+       .is_encrypted           = ubifs_crypt_is_encrypted,
+};
+#endif
+
 static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct ubifs_info *c = sb->s_fs_info;
@@ -2041,6 +2050,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
                sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
        sb->s_op = &ubifs_super_operations;
        sb->s_xattr = ubifs_xattr_handlers;
+       sb->s_cop = &ubifs_crypt_operations;
 
        mutex_lock(&c->umount_mutex);
        err = mount_ubifs(c);
index d47e9569b3de058550822bd4e9adfc274fb0955b..aa302b11aec86bfd2d1bbd8d73b66f60dedb8b88 100644 (file)
@@ -316,6 +316,7 @@ enum {
  * UBIFS_APPEND_FL: writes to the inode may only append data
  * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous
  * UBIFS_XATTR_FL: this inode is the inode for an extended attribute value
+ * UBIFS_CRYPT_FL: use encryption for this inode
  *
  * Note, these are on-flash flags which correspond to ioctl flags
  * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not
@@ -328,6 +329,7 @@ enum {
        UBIFS_APPEND_FL    = 0x08,
        UBIFS_DIRSYNC_FL   = 0x10,
        UBIFS_XATTR_FL     = 0x20,
+       UBIFS_CRYPT_FL     = 0x40,
 };
 
 /* Inode flag bits used by UBIFS */
index 0f8a3ec6a7fe7677404056d56e186add94ed1d55..27a85015e3ff1fe46911d6cd1f589fc065f9d016 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/backing-dev.h>
 #include <linux/security.h>
 #include <linux/xattr.h>
+#include <linux/fscrypto.h>
 #include "ubifs-media.h"
 
 /* Version of this UBIFS implementation */
@@ -1724,7 +1725,7 @@ int ubifs_update_time(struct inode *inode, struct timespec *time, int flags);
 #endif
 
 /* dir.c */
-struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
                              umode_t mode);
 int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat);
@@ -1773,10 +1774,44 @@ void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
 int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
                     void *out, int *out_len, int compr_type);
 
+extern struct fscrypt_operations ubifs_crypt_operations;
+
 #include "debug.h"
 #include "misc.h"
 #include "key.h"
 
+#ifndef CONFIG_UBIFS_FS_ENCRYPTION
+#define fscrypt_set_d_op(i)
+#define fscrypt_get_ctx                 fscrypt_notsupp_get_ctx
+#define fscrypt_release_ctx             fscrypt_notsupp_release_ctx
+#define fscrypt_encrypt_page            fscrypt_notsupp_encrypt_page
+#define fscrypt_decrypt_page            fscrypt_notsupp_decrypt_page
+#define fscrypt_decrypt_bio_pages       fscrypt_notsupp_decrypt_bio_pages
+#define fscrypt_pullback_bio_page       fscrypt_notsupp_pullback_bio_page
+#define fscrypt_restore_control_page    fscrypt_notsupp_restore_control_page
+#define fscrypt_zeroout_range           fscrypt_notsupp_zeroout_range
+#define fscrypt_process_policy          fscrypt_notsupp_process_policy
+#define fscrypt_get_policy              fscrypt_notsupp_get_policy
+#define fscrypt_has_permitted_context   fscrypt_notsupp_has_permitted_context
+#define fscrypt_inherit_context         fscrypt_notsupp_inherit_context
+#define fscrypt_get_encryption_info     fscrypt_notsupp_get_encryption_info
+#define fscrypt_put_encryption_info     fscrypt_notsupp_put_encryption_info
+#define fscrypt_setup_filename          fscrypt_notsupp_setup_filename
+#define fscrypt_free_filename           fscrypt_notsupp_free_filename
+#define fscrypt_fname_encrypted_size    fscrypt_notsupp_fname_encrypted_size
+#define fscrypt_fname_alloc_buffer      fscrypt_notsupp_fname_alloc_buffer
+#define fscrypt_fname_free_buffer       fscrypt_notsupp_fname_free_buffer
+#define fscrypt_fname_disk_to_usr       fscrypt_notsupp_fname_disk_to_usr
+#define fscrypt_fname_usr_to_disk       fscrypt_notsupp_fname_usr_to_disk
+#endif
+
+static inline bool ubifs_crypt_is_encrypted(struct inode *inode)
+{
+       struct ubifs_inode *ui = ubifs_inode(inode);
+
+       return ui->flags & UBIFS_CRYPT_FL;
+}
+
 /* Normal UBIFS messages */
 __printf(2, 3)
 void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...);
index 2d09dbeecd58851475d9de7ba4e5667f80ce534c..95a16028bbdb8ec324466b5c6eb0e9954127ef93 100644 (file)
@@ -158,6 +158,15 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
        host_ui->xattr_size += CALC_XATTR_BYTES(size);
        host_ui->xattr_names += nm->len;
 
+       /*
+        * We handle UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT here because we
+        * have to set the UBIFS_CRYPT_FL flag on the host inode.
+        * To avoid multiple updates of the same inode in the same operation,
+        * let's do it here.
+        */
+       if (strcmp(nm->name, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
+               host_ui->flags |= UBIFS_CRYPT_FL;
+
        err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
        if (err)
                goto out_cancel;
@@ -173,6 +182,7 @@ out_cancel:
        host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
        host_ui->xattr_size -= CALC_XATTR_BYTES(size);
        host_ui->xattr_names -= nm->len;
+       host_ui->flags &= ~UBIFS_CRYPT_FL;
        mutex_unlock(&host_ui->ui_mutex);
 out_free:
        make_bad_inode(inode);