Skip to content

Commit

Permalink
ceph: add selinux support
Browse files Browse the repository at this point in the history
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" <[email protected]>
Reviewed-by: Jeff Layton <[email protected]>
Signed-off-by: Ilya Dryomov <[email protected]>
  • Loading branch information
ukernel authored and idryomov committed Jul 8, 2019
1 parent 5c31e92 commit ac6713c
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 17 deletions.
12 changes: 12 additions & 0 deletions fs/ceph/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,15 @@ 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 "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.
1 change: 1 addition & 0 deletions fs/ceph/caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -3156,6 +3156,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);
}
}

Expand Down
12 changes: 12 additions & 0 deletions fs/ceph/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry,
}

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;

Expand Down Expand Up @@ -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_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)
Expand All @@ -894,6 +898,10 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
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);
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
if (IS_ERR(req)) {
Expand All @@ -919,6 +927,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;
}

Expand Down Expand Up @@ -951,6 +960,9 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)

mode |= S_IFDIR;
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;

Expand Down
3 changes: 3 additions & 0 deletions fs/ceph/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,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 */
Expand Down
1 change: 1 addition & 0 deletions fs/ceph/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,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;
}

Expand Down
19 changes: 19 additions & 0 deletions fs/ceph/super.h
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,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;
};
Expand All @@ -951,6 +955,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 */
Expand Down
142 changes: 125 additions & 17 deletions fs/ceph/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <linux/ceph/decode.h>

#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/posix_acl_xattr.h>
#include <linux/slab.h>

Expand All @@ -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);
}
Expand Down Expand Up @@ -1196,14 +1180,138 @@ 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 *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

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,
};

0 comments on commit ac6713c

Please sign in to comment.