Allow the client to create a new encryption context prior to creating a new inode, and ensure that we set it in the ceph_acl_sec_ctx (alongside acls, selinux contexts, etc.). Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/ceph/crypto.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/ceph/crypto.h | 8 +++++++ fs/ceph/dir.c | 15 +++++++++++++ fs/ceph/file.c | 4 ++++ fs/ceph/super.h | 4 ++++ 5 files changed, 86 insertions(+) diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 22a09d422b72..0e57497c2e55 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -67,3 +67,58 @@ int ceph_fscrypt_set_ops(struct super_block *sb) } return 0; } + +int ceph_fscrypt_new_context(struct inode *parent, struct ceph_acl_sec_ctx *as) +{ + int ret, ctxsize; + size_t name_len; + char *name; + struct ceph_pagelist *pagelist = as->pagelist; + + /* Do nothing if subtree isn't encrypted */ + if (!IS_ENCRYPTED(parent)) + return 0; + + ctxsize = fscrypt_new_context_from_parent(parent, as->fscrypt); + if (ctxsize <= 0) + return ctxsize; + + /* marshal it in page array */ + if (!pagelist) { + pagelist = ceph_pagelist_alloc(GFP_KERNEL); + if (!pagelist) + return -ENOMEM; + ret = ceph_pagelist_reserve(pagelist, PAGE_SIZE); + if (ret) + goto out; + ceph_pagelist_encode_32(pagelist, 1); + } + + name = CEPH_XATTR_NAME_ENCRYPTION_CONTEXT; + name_len = strlen(name); + ret = ceph_pagelist_reserve(pagelist, 4 * 2 + name_len + ctxsize); + if (ret) + goto out; + + if (as->pagelist) { + BUG_ON(pagelist->length <= sizeof(__le32)); + if (list_is_singular(&pagelist->head)) { + le32_add_cpu((__le32*)pagelist->mapped_tail, 1); + } else { + struct page *page = list_first_entry(&pagelist->head, + struct page, lru); + void *addr = kmap_atomic(page); + le32_add_cpu((__le32*)addr, 1); + kunmap_atomic(addr); + } + } + + ceph_pagelist_encode_32(pagelist, name_len); + ceph_pagelist_append(pagelist, name, name_len); + ceph_pagelist_encode_32(pagelist, ctxsize); + ceph_pagelist_append(pagelist, as->fscrypt, ctxsize); +out: + if (pagelist && !as->pagelist) + ceph_pagelist_release(pagelist); + return ret; +} diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h index 309925b345fc..e7d00831ef36 100644 --- a/fs/ceph/crypto.h +++ b/fs/ceph/crypto.h @@ -8,9 +8,12 @@ #ifdef CONFIG_FS_ENCRYPTION +#define CEPH_XATTR_NAME_ENCRYPTION_CONTEXT "encryption.ctx" + #define DUMMY_ENCRYPTION_ENABLED(fsc) ((fsc)->dummy_enc_ctx.ctx != NULL) int ceph_fscrypt_set_ops(struct super_block *sb); +int ceph_fscrypt_new_context(struct inode *parent, struct ceph_acl_sec_ctx *as); #else /* CONFIG_FS_ENCRYPTION */ @@ -21,6 +24,11 @@ static inline int ceph_fscrypt_set_ops(struct super_block *sb) return 0; } +static int ceph_fscrypt_new_context(struct inode *parent, struct ceph_acl_sec_ctx *as) +{ + return 0; +} + #endif /* CONFIG_FS_ENCRYPTION */ #endif diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 34f669220a8b..2e7f2bfa2c12 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -9,6 +9,7 @@ #include "super.h" #include "mds_client.h" +#include "crypto.h" /* * Directory operations: readdir, lookup, create, link, unlink, @@ -848,6 +849,12 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry, if (err < 0) goto out; + if (S_ISREG(mode)) { + err = ceph_fscrypt_new_context(dir, &as_ctx); + if (err < 0) + goto out; + } + dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n", dir, dentry, mode, rdev); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS); @@ -907,6 +914,10 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, if (err < 0) goto out; + err = ceph_fscrypt_new_context(dir, &as_ctx); + if (err < 0) + goto out; + dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); if (IS_ERR(req)) { @@ -975,6 +986,10 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) if (err < 0) goto out; + err = ceph_fscrypt_new_context(dir, &as_ctx); + if (err < 0) + goto out; + req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); if (IS_ERR(req)) { err = PTR_ERR(req); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index cf3f66d75162..94558e04f34a 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -19,6 +19,7 @@ #include "cache.h" #include "io.h" #include "metric.h" +#include "crypto.h" static __le32 ceph_flags_sys2wire(u32 flags) { @@ -686,6 +687,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, err = ceph_security_init_secctx(dentry, mode, &as_ctx); if (err < 0) goto out_ctx; + err = ceph_fscrypt_new_context(dir, &as_ctx); + if (err < 0) + goto out_ctx; } else if (!d_in_lookup(dentry)) { /* If it's not being looked up, it's negative */ return -ENOENT; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 6327b5739286..a481cacf775a 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -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> @@ -991,6 +992,9 @@ struct ceph_acl_sec_ctx { #ifdef CONFIG_CEPH_FS_SECURITY_LABEL void *sec_ctx; u32 sec_ctxlen; +#endif +#ifdef CONFIG_FS_ENCRYPTION + u8 fscrypt[FSCRYPT_SET_CONTEXT_MAX_SIZE]; #endif struct ceph_pagelist *pagelist; }; -- 2.26.2