From patchwork Tue Jun 26 08:43:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yan, Zheng" X-Patchwork-Id: 10488295 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 259FF602B3 for ; Tue, 26 Jun 2018 08:43:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 11A43283CF for ; Tue, 26 Jun 2018 08:43:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0621D2876C; Tue, 26 Jun 2018 08:43:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 32537287CC for ; Tue, 26 Jun 2018 08:43:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932353AbeFZInY (ORCPT ); Tue, 26 Jun 2018 04:43:24 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:48258 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932301AbeFZInV (ORCPT ); Tue, 26 Jun 2018 04:43:21 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 70BC9406E81E; Tue, 26 Jun 2018 08:43:20 +0000 (UTC) Received: from ovpn-12-60.pek2.redhat.com (ovpn-12-60.pek2.redhat.com [10.72.12.60]) by smtp.corp.redhat.com (Postfix) with ESMTP id A8E0E111AF0B; Tue, 26 Jun 2018 08:43:17 +0000 (UTC) From: "Yan, Zheng" To: linux-security-module@vger.kernel.org Cc: jmorris@namei.org, paul@paul-moore.com, ceph-devel@vger.kernel.org, "Yan, Zheng" Subject: [PATCH 3/3] ceph: xattr security label support Date: Tue, 26 Jun 2018 16:43:06 +0800 Message-Id: <20180626084306.27511-3-zyan@redhat.com> In-Reply-To: <20180626084306.27511-1-zyan@redhat.com> References: <20180626084306.27511-1-zyan@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Tue, 26 Jun 2018 08:43:20 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Tue, 26 Jun 2018 08:43:20 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'zyan@redhat.com' RCPT:'' Sender: ceph-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: ceph-devel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When creating new file/directory, uses dentry_init_security() to prepare security context for the new inode, then sends openc/mkdir request to MDS, together with security xattr "security." Signed-off-by: "Yan, Zheng" --- fs/ceph/Kconfig | 5 ++ fs/ceph/caps.c | 1 + fs/ceph/dir.c | 12 +++++ fs/ceph/file.c | 3 ++ fs/ceph/inode.c | 1 + fs/ceph/super.h | 19 +++++++ fs/ceph/xattr.c | 140 ++++++++++++++++++++++++++++++++++++++++++------ 7 files changed, 164 insertions(+), 17 deletions(-) diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig index 52095f473464..e1a4100c99eb 100644 --- a/fs/ceph/Kconfig +++ b/fs/ceph/Kconfig @@ -35,3 +35,8 @@ config CEPH_FS_POSIX_ACL groups beyond the owner/group/world scheme. If you don't know what Access Control Lists are, say N + +config CEPH_FS_SECURITY_LABEL + bool + depends on CEPH_FS && SECURITY + default y diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 990258cbd836..ec49a3858288 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -3144,6 +3144,7 @@ static void handle_cap_grant(struct inode *inode, ci->i_xattrs.blob = ceph_buffer_get(xattr_buf); ci->i_xattrs.version = version; ceph_forget_all_cached_acls(inode); + ceph_security_invalidate_secctx(inode); } } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index f451ad5a37ab..18ece4be4493 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -833,6 +833,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry, err = ceph_pre_init_acls(dir, &mode, &as_ctx); if (err < 0) return err; + err = ceph_security_init_secctx(dentry, mode, &as_ctx); + if (err < 0) + goto out; dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n", dir, dentry, mode, rdev); @@ -878,6 +881,7 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; + struct ceph_acl_sec_ctx as_ctx = {}; int err; if (ceph_snap(dir) != CEPH_NOSNAP) @@ -886,6 +890,10 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, if (ceph_quota_is_max_files_exceeded(dir)) return -EDQUOT; + err = ceph_security_init_secctx(dentry, S_IFLNK | S_IRWXUGO, &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)) { @@ -911,6 +919,7 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, out: if (err) d_drop(dentry); + ceph_release_acl_sec_ctx(&as_ctx); return err; } @@ -945,6 +954,9 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) err = ceph_pre_init_acls(dir, &mode, &as_ctx); if (err < 0) goto out; + err = ceph_security_init_secctx(dentry, mode, &as_ctx); + if (err < 0) + goto out; req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); if (IS_ERR(req)) { diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 701506ec5768..0e835ca720cb 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -453,6 +453,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, err = ceph_pre_init_acls(dir, &mode, &as_ctx); if (err < 0) return err; + err = ceph_security_init_secctx(dentry, mode, &as_ctx); + if (err < 0) + goto out_ctx; } /* do the open */ diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 1fe3a02336b0..db0079fd5c06 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -896,6 +896,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page, iinfo->xattr_data, iinfo->xattr_len); ci->i_xattrs.version = le64_to_cpu(info->xattr_version); ceph_forget_all_cached_acls(inode); + ceph_security_invalidate_secctx(inode); xattr_blob = NULL; } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 83561421afda..60151b8cd5c7 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -910,6 +910,10 @@ struct ceph_acl_sec_ctx { #ifdef CONFIG_CEPH_FS_POSIX_ACL void *default_acl; void *acl; +#endif +#ifdef CONFIG_CEPH_FS_SECURITY_LABEL + void *sec_ctx; + u32 sec_ctxlen; #endif struct ceph_pagelist *pagelist; }; @@ -928,6 +932,21 @@ static inline bool ceph_security_xattr_wanted(struct inode *in) } #endif +#ifdef CONFIG_CEPH_FS_SECURITY_LABEL +extern int ceph_security_init_secctx(struct dentry *dentry, umode_t mode, + struct ceph_acl_sec_ctx *ctx); +extern void ceph_security_invalidate_secctx(struct inode *inode); +#else +static inline int ceph_security_init_secctx(struct dentry *dentry, umode_t mode, + struct ceph_acl_sec_ctx *ctx) +{ + return 0; +} +static inline void ceph_security_invalidate_secctx(struct inode *inode) +{ +} +#endif + void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx); /* acl.c */ diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index ef0e968d56a1..3e87208cbde8 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -17,26 +18,9 @@ static int __remove_xattr(struct ceph_inode_info *ci, struct ceph_inode_xattr *xattr); -static const struct xattr_handler ceph_other_xattr_handler; - -/* - * List of handlers for synthetic system.* attributes. Other - * attributes are handled directly. - */ -const struct xattr_handler *ceph_xattr_handlers[] = { -#ifdef CONFIG_CEPH_FS_POSIX_ACL - &posix_acl_access_xattr_handler, - &posix_acl_default_xattr_handler, -#endif - &ceph_other_xattr_handler, - NULL, -}; - static bool ceph_is_valid_xattr(const char *name) { return !strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN) || - !strncmp(name, XATTR_SECURITY_PREFIX, - XATTR_SECURITY_PREFIX_LEN) || !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) || !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); } @@ -1189,6 +1173,109 @@ bool ceph_security_xattr_deadlock(struct inode *in) spin_unlock(&ci->i_ceph_lock); return ret; } + +#ifdef CONFIG_CEPH_FS_SECURITY_LABEL +int ceph_security_init_secctx(struct dentry *dentry, umode_t mode, + struct ceph_acl_sec_ctx *as_ctx) +{ + struct ceph_pagelist *pagelist = as_ctx->pagelist; + const char *label; + size_t label_len; + int err; + + err = security_dentry_init_security(dentry, mode, &dentry->d_name, + &label, &as_ctx->sec_ctx, + &as_ctx->sec_ctxlen); + if (err < 0) { + err = 0; /* do nothing */ + goto out; + } + + err = -ENOMEM; + if (!pagelist) { + pagelist = kmalloc(sizeof(struct ceph_pagelist), GFP_KERNEL); + if (!pagelist) + goto out; + ceph_pagelist_init(pagelist); + + err = ceph_pagelist_reserve(pagelist, PAGE_SIZE); + if (err) + goto out; + ceph_pagelist_encode_32(pagelist, 1); + } + + label_len = strlen(label); + err = ceph_pagelist_reserve(pagelist, XATTR_SECURITY_PREFIX_LEN + + label_len + as_ctx->sec_ctxlen + 8); + if (err) + goto out; + + if (as_ctx->pagelist) { + /* update count of KV pairs */ + 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); + } + } else { + as_ctx->pagelist = pagelist; + } + + ceph_pagelist_encode_32(pagelist, + XATTR_SECURITY_PREFIX_LEN + label_len); + ceph_pagelist_append(pagelist, XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN); + ceph_pagelist_append(pagelist, label, label_len); + + ceph_pagelist_encode_32(pagelist, as_ctx->sec_ctxlen); + ceph_pagelist_append(pagelist, as_ctx->sec_ctx, as_ctx->sec_ctxlen); + + err = 0; +out: + if (pagelist && !as_ctx->pagelist) + ceph_pagelist_release(pagelist); + return err; +} + +void ceph_security_invalidate_secctx(struct inode *inode) +{ + security_inode_invalidate_secctx(inode); +} + +static int ceph_xattr_set_security_label(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *key, const void *buf, + size_t buflen, int flags) +{ + if (security_ismaclabel(key)) { + const char *name = xattr_full_name(handler, key); + return __ceph_setxattr(inode, name, buf, buflen, flags); + } + return -EOPNOTSUPP; +} + +static int ceph_xattr_get_security_label(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *key, void *buf, size_t buflen) +{ + if (security_ismaclabel(key)) { + const char *name = xattr_full_name(handler, key); + return __ceph_getxattr(inode, name, buf, buflen); + } + return -EOPNOTSUPP; +} + +static const struct xattr_handler ceph_security_label_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .get = ceph_xattr_get_security_label, + .set = ceph_xattr_set_security_label, +}; +#endif #endif void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx) @@ -1196,7 +1283,26 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx) #ifdef CONFIG_CEPH_FS_POSIX_ACL posix_acl_release(as_ctx->acl); posix_acl_release(as_ctx->default_acl); +#endif +#ifdef CONFIG_CEPH_FS_SECURITY_LABEL + security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen); #endif if (as_ctx->pagelist) ceph_pagelist_release(as_ctx->pagelist); } + +/* + * List of handlers for synthetic system.* attributes. Other + * attributes are handled directly. + */ +const struct xattr_handler *ceph_xattr_handlers[] = { +#ifdef CONFIG_CEPH_FS_POSIX_ACL + &posix_acl_access_xattr_handler, + &posix_acl_default_xattr_handler, +#endif +#ifdef CONFIG_CEPH_FS_SECURITY_LABEL + &ceph_security_label_handler, +#endif + &ceph_other_xattr_handler, + NULL, +};