]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ceph: implement -o test_dummy_encryption mount option
authorJeff Layton <jlayton@kernel.org>
Tue, 8 Sep 2020 13:47:40 +0000 (09:47 -0400)
committerJeff Layton <jlayton@kernel.org>
Tue, 7 Dec 2021 15:32:46 +0000 (10:32 -0500)
Signed-off-by: Jeff Layton <jlayton@kernel.org>
fs/ceph/crypto.c
fs/ceph/crypto.h
fs/ceph/inode.c
fs/ceph/super.c
fs/ceph/super.h
fs/ceph/xattr.c

index a513ff373b134bcf79cca9864705028c1ef9a1d0..017f31eacb74c9db6e581b7551b44b4e9e12da76 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/fscrypt.h>
 
 #include "super.h"
+#include "mds_client.h"
 #include "crypto.h"
 
 static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
@@ -64,9 +65,20 @@ static bool ceph_crypt_empty_dir(struct inode *inode)
        return ci->i_rsubdirs + ci->i_rfiles == 1;
 }
 
+void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
+{
+       fscrypt_free_dummy_policy(&fsc->dummy_enc_policy);
+}
+
+static const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb)
+{
+       return ceph_sb_to_client(sb)->dummy_enc_policy.policy;
+}
+
 static struct fscrypt_operations ceph_fscrypt_ops = {
        .get_context            = ceph_crypt_get_context,
        .set_context            = ceph_crypt_set_context,
+       .get_dummy_policy       = ceph_get_dummy_policy,
        .empty_dir              = ceph_crypt_empty_dir,
 };
 
@@ -74,3 +86,44 @@ void ceph_fscrypt_set_ops(struct super_block *sb)
 {
        fscrypt_set_ops(sb, &ceph_fscrypt_ops);
 }
+
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+                                struct ceph_acl_sec_ctx *as)
+{
+       int ret, ctxsize;
+       bool encrypted = false;
+       struct ceph_inode_info *ci = ceph_inode(inode);
+
+       ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
+       if (ret)
+               return ret;
+       if (!encrypted)
+               return 0;
+
+       as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL);
+       if (!as->fscrypt_auth)
+               return -ENOMEM;
+
+       ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob, inode);
+       if (ctxsize < 0)
+               return ctxsize;
+
+       as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
+       as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize);
+
+       WARN_ON_ONCE(ci->fscrypt_auth);
+       kfree(ci->fscrypt_auth);
+       ci->fscrypt_auth_len = ceph_fscrypt_auth_len(as->fscrypt_auth);
+       ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len, GFP_KERNEL);
+       if (!ci->fscrypt_auth)
+               return -ENOMEM;
+
+       inode->i_flags |= S_ENCRYPTED;
+
+       return 0;
+}
+
+void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as)
+{
+       swap(req->r_fscrypt_auth, as->fscrypt_auth);
+}
index 6dca674f79b82f7432ef28c276ccffbc3bc8ee13..cb00fe42d5b7b427cce62c984e5217c6b0491054 100644 (file)
@@ -8,6 +8,10 @@
 
 #include <linux/fscrypt.h>
 
+struct ceph_fs_client;
+struct ceph_acl_sec_ctx;
+struct ceph_mds_request;
+
 struct ceph_fscrypt_auth {
        __le32  cfa_version;
        __le32  cfa_blob_len;
@@ -25,12 +29,34 @@ static inline u32 ceph_fscrypt_auth_len(struct ceph_fscrypt_auth *fa)
 #ifdef CONFIG_FS_ENCRYPTION
 void ceph_fscrypt_set_ops(struct super_block *sb);
 
+void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
+
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+                                struct ceph_acl_sec_ctx *as);
+void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as);
+
 #else /* CONFIG_FS_ENCRYPTION */
 
 static inline void ceph_fscrypt_set_ops(struct super_block *sb)
 {
 }
 
+static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
+{
+}
+
+static inline int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+                                               struct ceph_acl_sec_ctx *as)
+{
+       if (IS_ENCRYPTED(dir))
+               return -EOPNOTSUPP;
+       return 0;
+}
+
+static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
+                                               struct ceph_acl_sec_ctx *as_ctx)
+{
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 #endif
index c85dabb6caaa06d1f7d82924891e32baf91d0202..be865a4a0e6a0f0dec06bf2b323357c5543c52ba 100644 (file)
@@ -83,12 +83,17 @@ struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
                        goto out_err;
        }
 
+       inode->i_state = 0;
+       inode->i_mode = *mode;
+
        err = ceph_security_init_secctx(dentry, *mode, as_ctx);
        if (err < 0)
                goto out_err;
 
-       inode->i_state = 0;
-       inode->i_mode = *mode;
+       err = ceph_fscrypt_prepare_context(dir, inode, as_ctx);
+       if (err)
+               goto out_err;
+
        return inode;
 out_err:
        iput(inode);
@@ -101,6 +106,7 @@ void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *a
                req->r_pagelist = as_ctx->pagelist;
                as_ctx->pagelist = NULL;
        }
+       ceph_fscrypt_as_ctx_to_req(req, as_ctx);
 }
 
 /**
index 93adecd86e7a5ab76eb9e735d078663bb0957c38..bba5e76e475a7f18acc51a7f4fe6a7d63f60bd51 100644 (file)
@@ -45,6 +45,7 @@ static void ceph_put_super(struct super_block *s)
        struct ceph_fs_client *fsc = ceph_sb_to_client(s);
 
        dout("put_super\n");
+       ceph_fscrypt_free_dummy_policy(fsc);
        ceph_mdsc_close_sessions(fsc->mdsc);
 }
 
@@ -161,6 +162,7 @@ enum {
        Opt_quotadf,
        Opt_copyfrom,
        Opt_wsync,
+       Opt_test_dummy_encryption,
 };
 
 enum ceph_recover_session_mode {
@@ -188,6 +190,7 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
        fsparam_string  ("fsc",                         Opt_fscache), // fsc=...
        fsparam_flag_no ("ino32",                       Opt_ino32),
        fsparam_string  ("mds_namespace",               Opt_mds_namespace),
+       fsparam_string  ("mon_addr",                    Opt_mon_addr),
        fsparam_flag_no ("poolperm",                    Opt_poolperm),
        fsparam_flag_no ("quotadf",                     Opt_quotadf),
        fsparam_u32     ("rasize",                      Opt_rasize),
@@ -199,7 +202,8 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
        fsparam_u32     ("rsize",                       Opt_rsize),
        fsparam_string  ("snapdirname",                 Opt_snapdirname),
        fsparam_string  ("source",                      Opt_source),
-       fsparam_string  ("mon_addr",                    Opt_mon_addr),
+       fsparam_flag    ("test_dummy_encryption",       Opt_test_dummy_encryption),
+       fsparam_string  ("test_dummy_encryption",       Opt_test_dummy_encryption),
        fsparam_u32     ("wsize",                       Opt_wsize),
        fsparam_flag_no ("wsync",                       Opt_wsync),
        {}
@@ -568,6 +572,16 @@ static int ceph_parse_mount_param(struct fs_context *fc,
                else
                        fsopt->flags |= CEPH_MOUNT_OPT_ASYNC_DIROPS;
                break;
+       case Opt_test_dummy_encryption:
+#ifdef CONFIG_FS_ENCRYPTION
+               kfree(fsopt->test_dummy_encryption);
+               fsopt->test_dummy_encryption = param->string;
+               param->string = NULL;
+               fsopt->flags |= CEPH_MOUNT_OPT_TEST_DUMMY_ENC;
+#else
+               warnfc(fc, "FS encryption not supported: test_dummy_encryption mount option ignored");
+#endif
+               break;
        default:
                BUG();
        }
@@ -588,6 +602,7 @@ static void destroy_mount_options(struct ceph_mount_options *args)
        kfree(args->server_path);
        kfree(args->fscache_uniq);
        kfree(args->mon_addr);
+       kfree(args->test_dummy_encryption);
        kfree(args);
 }
 
@@ -703,6 +718,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
        if (!(fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS))
                seq_puts(m, ",wsync");
 
+       fscrypt_show_test_dummy_encryption(m, ',', root->d_sb);
+
        if (fsopt->wsize != CEPH_MAX_WRITE_SIZE)
                seq_printf(m, ",wsize=%u", fsopt->wsize);
        if (fsopt->rsize != CEPH_MAX_READ_SIZE)
@@ -1038,6 +1055,52 @@ out:
        return root;
 }
 
+#ifdef CONFIG_FS_ENCRYPTION
+static int ceph_set_test_dummy_encryption(struct super_block *sb, struct fs_context *fc,
+                                               struct ceph_mount_options *fsopt)
+{
+       /*
+        * No changing encryption context on remount. Note that
+        * fscrypt_set_test_dummy_encryption will validate the version
+        * string passed in (if any).
+        */
+       if (fsopt->flags & CEPH_MOUNT_OPT_TEST_DUMMY_ENC) {
+               struct ceph_fs_client *fsc = sb->s_fs_info;
+               int err = 0;
+
+               if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE && !fsc->dummy_enc_policy.policy) {
+                       errorfc(fc, "Can't set test_dummy_encryption on remount");
+                       return -EEXIST;
+               }
+
+               err = fscrypt_set_test_dummy_encryption(sb,
+                                                       fsc->mount_options->test_dummy_encryption,
+                                                       &fsc->dummy_enc_policy);
+               if (err) {
+                       if (err == -EEXIST)
+                               errorfc(fc, "Can't change test_dummy_encryption on remount");
+                       else if (err == -EINVAL)
+                               errorfc(fc, "Value of option \"%s\" is unrecognized",
+                                       fsc->mount_options->test_dummy_encryption);
+                       else
+                               errorfc(fc, "Error processing option \"%s\" [%d]",
+                                       fsc->mount_options->test_dummy_encryption, err);
+                       return err;
+               }
+               warnfc(fc, "test_dummy_encryption mode enabled");
+       }
+       return 0;
+}
+#else
+static inline int ceph_set_test_dummy_encryption(struct super_block *sb, struct fs_context *fc,
+                                               struct ceph_mount_options *fsopt)
+{
+       if (fsopt->flags & CEPH_MOUNT_OPT_TEST_DUMMY_ENC)
+               warnfc(fc, "test_dummy_encryption mode ignored");
+       return 0;
+}
+#endif
+
 /*
  * mount: join the ceph cluster, and open root directory.
  */
@@ -1066,6 +1129,10 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
                                goto out;
                }
 
+               err = ceph_set_test_dummy_encryption(fsc->sb, fc, fsc->mount_options);
+               if (err)
+                       goto out;
+
                dout("mount opening path '%s'\n", path);
 
                ceph_fs_debugfs_init(fsc);
@@ -1274,9 +1341,15 @@ static void ceph_free_fc(struct fs_context *fc)
 
 static int ceph_reconfigure_fc(struct fs_context *fc)
 {
+       int err;
        struct ceph_parse_opts_ctx *pctx = fc->fs_private;
        struct ceph_mount_options *fsopt = pctx->opts;
-       struct ceph_fs_client *fsc = ceph_sb_to_client(fc->root->d_sb);
+       struct super_block *sb = fc->root->d_sb;
+       struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
+
+       err = ceph_set_test_dummy_encryption(sb, fc, fsopt);
+       if (err)
+               return err;
 
        if (fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)
                ceph_set_mount_opt(fsc, ASYNC_DIROPS);
@@ -1290,7 +1363,7 @@ static int ceph_reconfigure_fc(struct fs_context *fc)
                pr_notice("ceph: monitor addresses recorded, but not used for reconnection");
        }
 
-       sync_filesystem(fc->root->d_sb);
+       sync_filesystem(sb);
        return 0;
 }
 
index 256d87409f925e9a5ce593733dad5d988f643ea2..f641a1a771e29822cf8b69bc12f112a89de6360d 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/posix_acl.h>
 #include <linux/refcount.h>
 #include <linux/security.h>
+#include <linux/fscrypt.h>
 
 #include <linux/ceph/libceph.h>
 
@@ -25,6 +26,8 @@
 #include <linux/fscache.h>
 #endif
 
+#include "crypto.h"
+
 /* f_type in struct statfs */
 #define CEPH_SUPER_MAGIC 0x00c36400
 
@@ -46,6 +49,7 @@
 #define CEPH_MOUNT_OPT_NOQUOTADF       (1<<13) /* no root dir quota in statfs */
 #define CEPH_MOUNT_OPT_NOCOPYFROM      (1<<14) /* don't use RADOS 'copy-from' op */
 #define CEPH_MOUNT_OPT_ASYNC_DIROPS    (1<<15) /* allow async directory ops */
+#define CEPH_MOUNT_OPT_TEST_DUMMY_ENC  (1<<16) /* enable dummy encryption (for testing) */
 
 #define CEPH_MOUNT_OPT_DEFAULT                 \
        (CEPH_MOUNT_OPT_DCACHE |                \
@@ -102,6 +106,7 @@ struct ceph_mount_options {
        char *server_path;    /* default NULL (means "/") */
        char *fscache_uniq;   /* default NULL */
        char *mon_addr;
+       char *test_dummy_encryption;    /* default NULL */
 };
 
 struct ceph_fs_client {
@@ -141,9 +146,11 @@ struct ceph_fs_client {
 #ifdef CONFIG_CEPH_FSCACHE
        struct fscache_cookie *fscache;
 #endif
+#ifdef CONFIG_FS_ENCRYPTION
+       struct fscrypt_dummy_policy dummy_enc_policy;
+#endif
 };
 
-
 /*
  * File i/o capability.  This tracks shared state with the metadata
  * server that allows us to cache or writeback attributes or to read
@@ -1083,6 +1090,9 @@ struct ceph_acl_sec_ctx {
 #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
        void *sec_ctx;
        u32 sec_ctxlen;
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+       struct ceph_fscrypt_auth *fscrypt_auth;
 #endif
        struct ceph_pagelist *pagelist;
 };
index fcf7dfdecf96603fe56085460dc68a6b7d34b6fd..5e3522457deb54bbb3d9b1846f52c7f57bb901fc 100644 (file)
@@ -1380,6 +1380,9 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
 #endif
 #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
        security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen);
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+       kfree(as_ctx->fscrypt_auth);
 #endif
        if (as_ctx->pagelist)
                ceph_pagelist_release(as_ctx->pagelist);