ceph: add selinux support

When creating new file/directory, use security_dentry_init_security() to
prepare selinux context for the new inode, then send openc/mkdir request
to MDS, together with selinux xattr.

security_dentry_init_security() only supports single security module and
only selinux has dentry_init_security hook. So only selinux is supported
for now. We can add support for other security modules once kernel has a
generic version of dentry_init_security()

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
Yan, Zheng 2019-05-26 16:27:56 +08:00 committed by Ilya Dryomov
parent 5c31e92dff
commit ac6713ccb5
7 changed files with 173 additions and 17 deletions

View File

@ -36,3 +36,15 @@ config CEPH_FS_POSIX_ACL
groups beyond the owner/group/world scheme. groups beyond the owner/group/world scheme.
If you don't know what Access Control Lists are, say N If you don't know what Access Control Lists are, say N
config CEPH_FS_SECURITY_LABEL
bool "CephFS Security Labels"
depends on CEPH_FS && SECURITY
help
Security labels support alternative access control models
implemented by security modules like SELinux. This option
enables an extended attribute handler for file security
labels in the Ceph filesystem.
If you are not using a security module that requires using
extended attributes for file security labels, say N.

View File

@ -3156,6 +3156,7 @@ static void handle_cap_grant(struct inode *inode,
ci->i_xattrs.blob = ceph_buffer_get(xattr_buf); ci->i_xattrs.blob = ceph_buffer_get(xattr_buf);
ci->i_xattrs.version = version; ci->i_xattrs.version = version;
ceph_forget_all_cached_acls(inode); ceph_forget_all_cached_acls(inode);
ceph_security_invalidate_secctx(inode);
} }
} }

View File

@ -837,6 +837,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry,
} }
err = ceph_pre_init_acls(dir, &mode, &as_ctx); 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) if (err < 0)
goto out; goto out;
@ -884,6 +887,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_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_client *mdsc = fsc->mdsc;
struct ceph_mds_request *req; struct ceph_mds_request *req;
struct ceph_acl_sec_ctx as_ctx = {};
int err; int err;
if (ceph_snap(dir) != CEPH_NOSNAP) if (ceph_snap(dir) != CEPH_NOSNAP)
@ -894,6 +898,10 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
goto out; goto out;
} }
err = ceph_security_init_secctx(dentry, S_IFLNK | 0777, &as_ctx);
if (err < 0)
goto out;
dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); 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); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
if (IS_ERR(req)) { if (IS_ERR(req)) {
@ -919,6 +927,7 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
out: out:
if (err) if (err)
d_drop(dentry); d_drop(dentry);
ceph_release_acl_sec_ctx(&as_ctx);
return err; return err;
} }
@ -951,6 +960,9 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
mode |= S_IFDIR; mode |= S_IFDIR;
err = ceph_pre_init_acls(dir, &mode, &as_ctx); 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) if (err < 0)
goto out; goto out;

View File

@ -454,6 +454,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
err = ceph_pre_init_acls(dir, &mode, &as_ctx); err = ceph_pre_init_acls(dir, &mode, &as_ctx);
if (err < 0) if (err < 0)
return err; return err;
err = ceph_security_init_secctx(dentry, mode, &as_ctx);
if (err < 0)
goto out_ctx;
} }
/* do the open */ /* do the open */

View File

@ -888,6 +888,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
iinfo->xattr_data, iinfo->xattr_len); iinfo->xattr_data, iinfo->xattr_len);
ci->i_xattrs.version = le64_to_cpu(info->xattr_version); ci->i_xattrs.version = le64_to_cpu(info->xattr_version);
ceph_forget_all_cached_acls(inode); ceph_forget_all_cached_acls(inode);
ceph_security_invalidate_secctx(inode);
xattr_blob = NULL; xattr_blob = NULL;
} }

View File

@ -933,6 +933,10 @@ struct ceph_acl_sec_ctx {
#ifdef CONFIG_CEPH_FS_POSIX_ACL #ifdef CONFIG_CEPH_FS_POSIX_ACL
void *default_acl; void *default_acl;
void *acl; void *acl;
#endif
#ifdef CONFIG_CEPH_FS_SECURITY_LABEL
void *sec_ctx;
u32 sec_ctxlen;
#endif #endif
struct ceph_pagelist *pagelist; struct ceph_pagelist *pagelist;
}; };
@ -951,6 +955,21 @@ static inline bool ceph_security_xattr_wanted(struct inode *in)
} }
#endif #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); void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx);
/* acl.c */ /* acl.c */

View File

@ -8,6 +8,7 @@
#include <linux/ceph/decode.h> #include <linux/ceph/decode.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/security.h>
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -17,26 +18,9 @@
static int __remove_xattr(struct ceph_inode_info *ci, static int __remove_xattr(struct ceph_inode_info *ci,
struct ceph_inode_xattr *xattr); 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) static bool ceph_is_valid_xattr(const char *name)
{ {
return !strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN) || 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_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
} }
@ -1196,6 +1180,111 @@ bool ceph_security_xattr_deadlock(struct inode *in)
spin_unlock(&ci->i_ceph_lock); spin_unlock(&ci->i_ceph_lock);
return ret; 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 *name;
size_t name_len;
int err;
err = security_dentry_init_security(dentry, mode, &dentry->d_name,
&as_ctx->sec_ctx,
&as_ctx->sec_ctxlen);
if (err < 0) {
WARN_ON_ONCE(err != -EOPNOTSUPP);
err = 0; /* do nothing */
goto out;
}
err = -ENOMEM;
if (!pagelist) {
pagelist = ceph_pagelist_alloc(GFP_KERNEL);
if (!pagelist)
goto out;
err = ceph_pagelist_reserve(pagelist, PAGE_SIZE);
if (err)
goto out;
ceph_pagelist_encode_32(pagelist, 1);
}
/*
* FIXME: Make security_dentry_init_security() generic. Currently
* It only supports single security module and only selinux has
* dentry_init_security hook.
*/
name = XATTR_NAME_SELINUX;
name_len = strlen(name);
err = ceph_pagelist_reserve(pagelist,
4 * 2 + name_len + as_ctx->sec_ctxlen);
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, name_len);
ceph_pagelist_append(pagelist, name, name_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 #endif
void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx) void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
@ -1203,7 +1292,26 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
#ifdef CONFIG_CEPH_FS_POSIX_ACL #ifdef CONFIG_CEPH_FS_POSIX_ACL
posix_acl_release(as_ctx->acl); posix_acl_release(as_ctx->acl);
posix_acl_release(as_ctx->default_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 #endif
if (as_ctx->pagelist) if (as_ctx->pagelist)
ceph_pagelist_release(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,
};