Skip to content

Commit

Permalink
LSM: lsm_get_self_attr syscall for LSM self attributes
Browse files Browse the repository at this point in the history
Create a system call lsm_get_self_attr() to provide the security
module maintained attributes of the current process. Historically
these attributes have been exposed to user space via entries in
procfs under /proc/self/attr.

Attributes are provided as a collection of lsm_ctx structures
which are placed into a user supplied buffer. Each structure
identifys the size of the attribute, and the attribute value.
The format of the attribute value is defined by the security
module, but will always be \0 terminated. The ctx_len value
will always be strlen(ctx)+1.

        ---------------------------
        | __u32 id                |
        ---------------------------
        | __u64 flags             |
        ---------------------------
        | __kernel_size_t ctx_len |
        ---------------------------
        | __u8 ctx[ctx_len]       |
        ---------------------------
        | __u32 id                |
        ---------------------------
        | __u64 flags             |
        ---------------------------
        | __kernel_size_t ctx_len |
        ---------------------------
        | __u8 ctx[ctx_len]       |
        ---------------------------

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
  • Loading branch information
cschaufler authored and intel-lab-lkp committed Nov 23, 2022
1 parent ef6b0a7 commit 3ac4c5a
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Documentation/userspace-api/lsm.rst
Expand Up @@ -48,6 +48,15 @@ creating socket objects.
The proc filesystem provides this value in ``/proc/self/attr/sockcreate``.
This is supported by the SELinux security module.

Kernel interface
================

Get the security attributes of the current process
--------------------------------------------------

.. kernel-doc:: security/lsm_syscalls.c
:identifiers: sys_lsm_get_self_attr

Additional documentation
========================

Expand Down
3 changes: 3 additions & 0 deletions include/linux/syscalls.h
Expand Up @@ -71,6 +71,7 @@ struct clone_args;
struct open_how;
struct mount_attr;
struct landlock_ruleset_attr;
struct lsm_cxt;
enum landlock_rule_type;

#include <linux/types.h>
Expand Down Expand Up @@ -1056,6 +1057,8 @@ asmlinkage long sys_memfd_secret(unsigned int flags);
asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long len,
unsigned long home_node,
unsigned long flags);
asmlinkage long sys_lsm_get_self_attr(struct lsm_ctx *ctx, size_t *size,
int flags);

/*
* Architecture-specific system calls
Expand Down
21 changes: 21 additions & 0 deletions include/uapi/linux/lsm.h
Expand Up @@ -9,6 +9,27 @@
#ifndef _UAPI_LINUX_LSM_H
#define _UAPI_LINUX_LSM_H

#include <linux/types.h>
#include <linux/unistd.h>

/**
* struct lsm_ctx - LSM context
* @id: the LSM id number, see LSM_ID_XXX
* @flags: context specifier and LSM specific flags
* @ctx_len: the size of @ctx
* @ctx: the LSM context, a nul terminated string
*
* @ctx in a nul terminated string.
* (strlen(@ctx) < @ctx_len) is always true.
* (strlen(@ctx) == @ctx_len + 1) is not guaranteed.
*/
struct lsm_ctx {
__u32 id;
__u64 flags;
__kernel_size_t ctx_len;
__u8 ctx[];
};

/*
* ID values to identify security modules.
* A system may use more than one security module.
Expand Down
3 changes: 3 additions & 0 deletions kernel/sys_ni.c
Expand Up @@ -262,6 +262,9 @@ COND_SYSCALL_COMPAT(recvmsg);
/* mm/nommu.c, also with MMU */
COND_SYSCALL(mremap);

/* security/lsm_syscalls.c */
COND_SYSCALL(lsm_get_self_attr);

/* security/keys/keyctl.c */
COND_SYSCALL(add_key);
COND_SYSCALL(request_key);
Expand Down
1 change: 1 addition & 0 deletions security/Makefile
Expand Up @@ -7,6 +7,7 @@ obj-$(CONFIG_KEYS) += keys/

# always enable default capabilities
obj-y += commoncap.o
obj-$(CONFIG_SECURITY) += lsm_syscalls.o
obj-$(CONFIG_MMU) += min_addr.o

# Object file lists
Expand Down
182 changes: 182 additions & 0 deletions security/lsm_syscalls.c
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* System calls implementing the Linux Security Module API.
*
* Copyright (C) 2022 Casey Schaufler <casey@schaufler-ca.com>
* Copyright (C) 2022 Intel Corporation
*/

#include <asm/current.h>
#include <linux/compiler_types.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/security.h>
#include <linux/stddef.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/lsm_hooks.h>
#include <uapi/linux/lsm.h>

struct attrs_used_map {
char *name;
int attrs_used;
};

static const struct attrs_used_map lsm_attr_names[] = {
{ .name = "current", .attrs_used = LSM_ATTR_CURRENT, },
{ .name = "exec", .attrs_used = LSM_ATTR_EXEC, },
{ .name = "fscreate", .attrs_used = LSM_ATTR_FSCREATE, },
{ .name = "keycreate", .attrs_used = LSM_ATTR_KEYCREATE, },
{ .name = "prev", .attrs_used = LSM_ATTR_PREV, },
{ .name = "sockcreate", .attrs_used = LSM_ATTR_SOCKCREATE, },
};

static int attr_used_index(u32 flags)
{
int i;

if (flags == 0)
return -EINVAL;

for (i = 0; i < ARRAY_SIZE(lsm_attr_names); i++)
if ((lsm_attr_names[i].attrs_used & flags) == flags)
return i;

return -EINVAL;
}

/**
* sys_lsm_get_self_attr - Return current task's security module attributes
* @ctx: the LSM contexts
* @size: size of @ctx, updated on return
* @flags: which attribute to return
*
* Returns the calling task's LSM contexts. On success this
* function returns the number of @ctx array elements. This value
* may be zero if there are no LSM contexts assigned. If @size is
* insufficient to contain the return data -E2BIG is returned and
* @size is set to the minimum required size. In all other cases
* a negative value indicating the error is returned.
*/
SYSCALL_DEFINE3(lsm_get_self_attr,
struct lsm_ctx __user *, ctx,
__kernel_size_t __user *, size,
__u32, flags)
{
int i;
int rc = 0;
int len;
int attr;
int count = 0;
void *curr;
char *cp;
char *np;
char **interum_ctx;
size_t total_size = 0;
struct lsm_ctx *ip;
struct lsm_ctx *interum;
struct lsm_ctx *final = NULL;

attr = attr_used_index(flags);
if (attr < 0)
return attr;

interum = kzalloc(ARRAY_SIZE(lsm_attr_names) * lsm_active_cnt *
sizeof(*interum), GFP_KERNEL);
if (interum == NULL)
return -ENOMEM;
ip = interum;

interum_ctx = kzalloc(ARRAY_SIZE(lsm_attr_names) * lsm_active_cnt *
sizeof(*interum_ctx), GFP_KERNEL);
if (interum_ctx == NULL) {
kfree(interum);
return -ENOMEM;
}

for (i = 0; i < lsm_active_cnt; i++) {
if ((lsm_idlist[i]->attrs_used &
lsm_attr_names[attr].attrs_used) == 0)
continue;

len = security_getprocattr(current, lsm_idlist[i]->id,
lsm_attr_names[attr].name,
&cp);
if (len <= 0)
continue;

ip->id = lsm_idlist[i]->id;
ip->flags = lsm_attr_names[attr].attrs_used;
interum_ctx[count] = cp;

/*
* A security module that returns a binary attribute
* will need to identify itself to prevent string
* processing.
*
* At least one security module adds a \n at the
* end of a context to make it look nicer. Change
* that to a \0 so that user space doesn't have to
* work around it.
*
* Security modules have been inconsistent about
* including the \0 terminator in the size. If it's
* not there make space for it.
*
* The length returned will reflect the length of
* the string provided by the security module, which
* may not match what getprocattr returned.
*/
np = strnchr(cp, len, '\n');
if (np != NULL)
*np = '\0';
ip->ctx_len = strnlen(cp, len) + 1;
total_size += sizeof(*interum) + ip->ctx_len;
ip++;
count++;
}

if (count == 0)
goto free_out;

final = kzalloc(total_size, GFP_KERNEL);
if (final == NULL) {
rc = -ENOMEM;
goto free_out;
}

curr = final;
ip = interum;
for (i = 0; i < count; i++) {
memcpy(curr, ip, sizeof(*interum));
curr += sizeof(*interum);
if (ip->ctx_len > 1)
memcpy(curr, interum_ctx[i], ip->ctx_len - 1);
curr += ip->ctx_len;
ip++;
}

if (get_user(len, size)) {
rc = -EFAULT;
goto free_out;
}
if (total_size > len) {
rc = -ERANGE;
if (put_user(total_size, size) != 0)
rc = -EFAULT;
goto free_out;
}
if (copy_to_user(ctx, final, total_size) != 0 ||
put_user(total_size, size) != 0)
rc = -EFAULT;
else
rc = count;

free_out:
for (i = 0; i < count; i++)
kfree(interum_ctx[i]);
kfree(interum_ctx);
kfree(interum);
kfree(final);
return rc;
}

0 comments on commit 3ac4c5a

Please sign in to comment.