diff mbox series

[v38,39/39] LSM: Create lsm_module_list system call

Message ID 20220927203155.15060-1-casey@schaufler-ca.com (mailing list archive)
State Handled Elsewhere
Delegated to: Paul Moore
Headers show
Series [v38,01/39] LSM: Identify modules by more than name | expand

Commit Message

Casey Schaufler Sept. 27, 2022, 8:31 p.m. UTC
Create a system call to report the list of Linux Security Modules
that are active on the system. The list is provided as an array
of LSM ID numbers.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 arch/x86/entry/syscalls/syscall_64.tbl |  1 +
 include/linux/syscalls.h               |  1 +
 include/uapi/asm-generic/unistd.h      |  5 ++-
 kernel/sys_ni.c                        |  1 +
 security/lsm_syscalls.c                | 50 ++++++++++++++++++++++++++
 5 files changed, 57 insertions(+), 1 deletion(-)

Comments

kernel test robot Sept. 28, 2022, 2:27 p.m. UTC | #1
Hi Casey,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.0-rc7]
[cannot apply to pcmoore-audit/next pcmoore-selinux/next zohar-integrity/next-integrity next-20220927]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Casey-Schaufler/LSM-Identify-modules-by-more-than-name/20220928-045406
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 46452d3786a82bd732ba73fb308ae5cbe4e1e591
config: s390-defconfig
compiler: s390-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/6f957bc7939d85848cbe2a2a1c1007e344629ae0
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Casey-Schaufler/LSM-Identify-modules-by-more-than-name/20220928-045406
        git checkout 6f957bc7939d85848cbe2a2a1c1007e344629ae0
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   security/lsm_syscalls.c:51: warning: expecting prototype for lsm_self_attr(). Prototype was for sys_lsm_self_attr() instead
>> security/lsm_syscalls.c:175: warning: expecting prototype for lsm_module_list(). Prototype was for sys_lsm_module_list() instead


vim +175 security/lsm_syscalls.c

    33	
    34	/**
    35	 * lsm_self_attr - Return current task's security module attributes
    36	 * @ctx: the LSM contexts
    37	 * @size: size of @ctx, updated on return
    38	 * @flags: reserved for future use, must be zero
    39	 *
    40	 * Returns the calling task's LSM contexts. On success this
    41	 * function returns the number of @ctx array elements. This value
    42	 * may be zero if there are no LSM contexts assigned. If @size is
    43	 * insufficient to contain the return data -E2BIG is returned and
    44	 * @size is set to the minimum required size. In all other cases
    45	 * a negative value indicating the error is returned.
    46	 */
    47	SYSCALL_DEFINE3(lsm_self_attr,
    48		       struct lsm_ctx __user *, ctx,
    49		       size_t __user *, size,
    50		       int, flags)
  > 51	{
    52		struct lsm_ctx *final = NULL;
    53		struct lsm_ctx *interum;
    54		struct lsm_ctx *ip;
    55		void *curr;
    56		char **interum_ctx;
    57		char *cp;
    58		size_t total_size = 0;
    59		int count = 0;
    60		int attr;
    61		int len;
    62		int rc = 0;
    63		int i;
    64	
    65		interum = kzalloc(ARRAY_SIZE(lsm_attr_names) * lsm_id *
    66				  sizeof(*interum), GFP_KERNEL);
    67		if (interum == NULL)
    68			return -ENOMEM;
    69		ip = interum;
    70	
    71		interum_ctx = kzalloc(ARRAY_SIZE(lsm_attr_names) * lsm_id *
    72				      sizeof(*interum_ctx), GFP_KERNEL);
    73		if (interum_ctx == NULL) {
    74			kfree(interum);
    75			return -ENOMEM;
    76		}
    77	
    78		for (attr = 0; attr < ARRAY_SIZE(lsm_attr_names); attr++) {
    79			for (i = 0; i < lsm_id; i++) {
    80				if ((lsm_idlist[i]->features &
    81				     lsm_attr_names[attr].feature) == 0)
    82					continue;
    83	
    84				len = security_getprocattr(current, lsm_idlist[i]->id,
    85							   lsm_attr_names[attr].name,
    86							   &cp);
    87				if (len <= 0)
    88					continue;
    89	
    90				ip->id = lsm_idlist[i]->id;
    91				ip->flags = lsm_attr_names[attr].feature;
    92				/* space for terminating \0 is allocated below */
    93				ip->ctx_len = len + 1;
    94				interum_ctx[count] = cp;
    95				/*
    96				 * Security modules have been inconsistent about
    97				 * including the \0 terminator in the size. The
    98				 * context len has been adjusted to ensure there
    99				 * is one.
   100				 * At least one security module adds a \n at the
   101				 * end of a context to make it look nicer. Change
   102				 * that to a \0 so that user space doesn't have to
   103				 * work around it. Because of this meddling it is
   104				 * safe to assume that lsm_ctx.name is terminated
   105				 * and that strlen(lsm_ctx.name) < lsm.ctx_len.
   106				 */
   107				total_size += sizeof(*interum) + ip->ctx_len;
   108				cp = strnchr(cp, len, '\n');
   109				if (cp != NULL)
   110					*cp = '\0';
   111				ip++;
   112				count++;
   113			}
   114		}
   115	
   116		if (count == 0)
   117			goto free_out;
   118	
   119		final = kzalloc(total_size, GFP_KERNEL);
   120		if (final == NULL) {
   121			rc = -ENOMEM;
   122			goto free_out;
   123		}
   124	
   125		curr = final;
   126		ip = interum;
   127		for (i = 0; i < count; i++) {
   128			memcpy(curr, ip, sizeof(*interum));
   129			curr += sizeof(*interum);
   130			memcpy(curr, interum_ctx[i], ip->ctx_len);
   131			curr += ip->ctx_len;
   132			ip++;
   133		}
   134	
   135		if (get_user(len, size)) {
   136			rc = -EFAULT;
   137			goto free_out;
   138		}
   139		if (total_size > len) {
   140			rc = -ERANGE;
   141			goto free_out;
   142		}
   143		if (copy_to_user(ctx, final, total_size) != 0 ||
   144		    put_user(total_size, size) != 0)
   145			rc = -EFAULT;
   146		else
   147			rc = count;
   148	
   149	free_out:
   150		for (i = 0; i < count; i++)
   151			kfree(interum_ctx[i]);
   152		kfree(interum_ctx);
   153		kfree(interum);
   154		kfree(final);
   155		return rc;
   156	}
   157	
   158	/**
   159	 * lsm_module_list - Return a list of the active security modules
   160	 * @ids: the LSM module ids
   161	 * @size: size of @ids, updated on return
   162	 * @flags: reserved for future use, must be zero
   163	 *
   164	 * Returns a list of the active LSM ids. On success this function
   165	 * returns the number of @ids array elements. This value may be zero
   166	 * if there are no LSMs active. If @size is insufficient to contain
   167	 * the return data -E2BIG is returned and @size is set to the minimum
   168	 * required size. In all other cases a negative value indicating the
   169	 * error is returned.
   170	 */
   171	SYSCALL_DEFINE3(lsm_module_list,
   172		       unsigned int __user *, ids,
   173		       size_t __user *, size,
   174		       int, flags)
 > 175	{
Mickaël Salaün Oct. 12, 2022, 9:19 p.m. UTC | #2
On 27/09/2022 22:31, Casey Schaufler wrote:
> Create a system call to report the list of Linux Security Modules
> that are active on the system. The list is provided as an array
> of LSM ID numbers.

With lsm_self_attr(), this would look like a dir/file structure.

Would it be useful for user space to list all the currently used LSMs 
instead of only retrieving information about a known (list of) LSM?
What is the use case for this syscall?


> 
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
>   arch/x86/entry/syscalls/syscall_64.tbl |  1 +
>   include/linux/syscalls.h               |  1 +
>   include/uapi/asm-generic/unistd.h      |  5 ++-
>   kernel/sys_ni.c                        |  1 +
>   security/lsm_syscalls.c                | 50 ++++++++++++++++++++++++++
>   5 files changed, 57 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index 56d5c5202fd0..40b35e7069a7 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -373,6 +373,7 @@
>   449	common	futex_waitv		sys_futex_waitv
>   450	common	set_mempolicy_home_node	sys_set_mempolicy_home_node
>   451	common	lsm_self_attr		sys_lsm_self_attr
> +452	common	lsm_module_list		sys_lsm_module_list

As for the other syscall, this should also be in the same dedicated 
"wire syscalls" patch.


>   
>   #
>   # Due to a historical design error, certain syscalls are numbered differently
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 7f87ef8be546..e2e2a9e93e8c 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -1057,6 +1057,7 @@ asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long l
>   					    unsigned long home_node,
>   					    unsigned long flags);
>   asmlinkage long sys_lsm_self_attr(struct lsm_ctx *ctx, size_t *size, int flags);
> +asmlinkage long sys_lsm_module_list(unsigned int *ids, size_t *size, int flags);
>   
>   /*
>    * Architecture-specific system calls
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
> index aa66718e1b48..090617a9a53a 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -889,8 +889,11 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node)
>   #define __NR_lsm_self_attr 451
>   __SYSCALL(__NR_lsm_self_attr, sys_lsm_self_attr)
>   
> +#define __NR_lsm_module_list 452
> +__SYSCALL(__NR_lsm_module_list, sys_lsm_module_list)
> +
>   #undef __NR_syscalls
> -#define __NR_syscalls 452
> +#define __NR_syscalls 453

Same here.


>   
>   /*
>    * 32 bit systems traditionally used different
> diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
> index 0fdb0341251d..bde9e74a3473 100644
> --- a/kernel/sys_ni.c
> +++ b/kernel/sys_ni.c
> @@ -264,6 +264,7 @@ COND_SYSCALL(mremap);
>   
>   /* security/lsm_syscalls.c */
>   COND_SYSCALL(lsm_self_attr);
> +COND_SYSCALL(lsm_module_list);
>   
>   /* security/keys/keyctl.c */
>   COND_SYSCALL(add_key);
> diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
> index da0fab7065e2..41d9ef945ede 100644
> --- a/security/lsm_syscalls.c
> +++ b/security/lsm_syscalls.c
> @@ -154,3 +154,53 @@ SYSCALL_DEFINE3(lsm_self_attr,
>   	kfree(final);
>   	return rc;
>   }
> +
> +/**
> + * lsm_module_list - Return a list of the active security modules
> + * @ids: the LSM module ids
> + * @size: size of @ids, updated on return
> + * @flags: reserved for future use, must be zero
> + *
> + * Returns a list of the active LSM ids. On success this function
> + * returns the number of @ids array elements. This value may be zero
> + * if there are no LSMs active. 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_module_list,
> +	       unsigned int __user *, ids,
> +	       size_t __user *, size,
> +	       int, flags)
> +{
> +	unsigned int *interum;
> +	size_t total_size = lsm_id * sizeof(*interum);
> +	size_t usize;
> +	int rc;
> +	int i;
> +
> +	if (get_user(usize, size))
> +		return -EFAULT;
> +
> +	if (usize < total_size) {
> +		if (put_user(total_size, size) != 0)
> +			return -EFAULT;
> +		return -E2BIG;
> +	}
> +
> +	interum = kzalloc(total_size, GFP_KERNEL);
> +	if (interum == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < lsm_id; i++)
> +		interum[i] = lsm_idlist[i]->id;
> +
> +	if (copy_to_user(ids, interum, total_size) != 0 ||
> +	    put_user(total_size, size) != 0)
> +		rc = -EFAULT;
> +	else
> +		rc = lsm_id;
> +
> +	kfree(interum);
> +	return rc;
> +}
Kees Cook Oct. 12, 2022, 10:04 p.m. UTC | #3
On Tue, Sep 27, 2022 at 01:31:55PM -0700, Casey Schaufler wrote:
> +SYSCALL_DEFINE3(lsm_module_list,
> +	       unsigned int __user *, ids,
> +	       size_t __user *, size,
> +	       int, flags)

Please make this unsigned int.

> +{
> +	unsigned int *interum;
> +	size_t total_size = lsm_id * sizeof(*interum);
> +	size_t usize;
> +	int rc;
> +	int i;

Please test that flags == 0 so it can be used in the future:

	if (flags)
		return -EINVAL;

> +
> +	if (get_user(usize, size))
> +		return -EFAULT;
> +
> +	if (usize < total_size) {
> +		if (put_user(total_size, size) != 0)
> +			return -EFAULT;
> +		return -E2BIG;
> +	}
> +
> +	interum = kzalloc(total_size, GFP_KERNEL);
> +	if (interum == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < lsm_id; i++)
> +		interum[i] = lsm_idlist[i]->id;
> +
> +	if (copy_to_user(ids, interum, total_size) != 0 ||
> +	    put_user(total_size, size) != 0)
> +		rc = -EFAULT;

No need to repeat this, if it is written first.

> +	else
> +		rc = lsm_id;
> +
> +	kfree(interum);
> +	return rc;

No need for the alloc/free. Here's what I would imagine for the whole
thing:

	if (flags)
		return -EINVAL;

	if (get_user(usize, size))
		return -EFAULT;

	if (put_user(total_size, size) != 0)
		return -EFAULT;

	if (usize < total_size)
		return -E2BIG;

	for (i = 0; i < lsm_id; i++)
		if (put_user(lsm_idlist[i]->id, id++))
			return -EFAULT;

	return lsm_id;
Casey Schaufler Oct. 20, 2022, 8:28 p.m. UTC | #4
On 10/12/2022 2:19 PM, Mickaël Salaün wrote:
>
> On 27/09/2022 22:31, Casey Schaufler wrote:
>> Create a system call to report the list of Linux Security Modules
>> that are active on the system. The list is provided as an array
>> of LSM ID numbers.
>
> With lsm_self_attr(), this would look like a dir/file structure.

I'm not sure I understand what you mean by this. I think you're suggesting
that lsm_module_list() shows the list of possibilities and lsm_self_attr()
shows the data. That's roughly correct. Note that many security modules
will never provide any data in lsm_self_attr(), and that others will
only provide it when it has been explicitly set. 

>
> Would it be useful for user space to list all the currently used LSMs
> instead of only retrieving information about a known (list of) LSM?

I believe so. User space tends to lag behind kernel features. ps(1) can report
the "current" value for any LSM without knowing which module supplied the value
today by using the /proc/self/attr/current interface. id(1) could do the same
were it not unnecessarily coded to be SELinux specific. lsm_module_list(2), like
the existing /sys/kernel/security/lsm interface, allows an application to know
if it should address the modules it knows about. It also provides the LSM order,
which could be significant to systemd, dbus, auditd or other sophisticated system
services.


> What is the use case for this syscall?

1. Identify if a specific LSM is in the list.
	Used by ps(1) to format the -Z output correctly
	Used by systemd(1) to determine which service start options to support
	Used by dbus to identify what policy to enforce at runtime
2. Identify the order of LSMs in the list.
	Will AppArmor data show up in /proc/self/attr/current, or will Smack data?

>
>
>>
>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>> ---
>>   arch/x86/entry/syscalls/syscall_64.tbl |  1 +
>>   include/linux/syscalls.h               |  1 +
>>   include/uapi/asm-generic/unistd.h      |  5 ++-
>>   kernel/sys_ni.c                        |  1 +
>>   security/lsm_syscalls.c                | 50 ++++++++++++++++++++++++++
>>   5 files changed, 57 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl
>> b/arch/x86/entry/syscalls/syscall_64.tbl
>> index 56d5c5202fd0..40b35e7069a7 100644
>> --- a/arch/x86/entry/syscalls/syscall_64.tbl
>> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
>> @@ -373,6 +373,7 @@
>>   449    common    futex_waitv        sys_futex_waitv
>>   450    common    set_mempolicy_home_node   
>> sys_set_mempolicy_home_node
>>   451    common    lsm_self_attr        sys_lsm_self_attr
>> +452    common    lsm_module_list        sys_lsm_module_list
>
> As for the other syscall, this should also be in the same dedicated
> "wire syscalls" patch.
>
>
>>     #
>>   # Due to a historical design error, certain syscalls are numbered
>> differently
>> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
>> index 7f87ef8be546..e2e2a9e93e8c 100644
>> --- a/include/linux/syscalls.h
>> +++ b/include/linux/syscalls.h
>> @@ -1057,6 +1057,7 @@ asmlinkage long
>> sys_set_mempolicy_home_node(unsigned long start, unsigned long l
>>                           unsigned long home_node,
>>                           unsigned long flags);
>>   asmlinkage long sys_lsm_self_attr(struct lsm_ctx *ctx, size_t
>> *size, int flags);
>> +asmlinkage long sys_lsm_module_list(unsigned int *ids, size_t *size,
>> int flags);
>>     /*
>>    * Architecture-specific system calls
>> diff --git a/include/uapi/asm-generic/unistd.h
>> b/include/uapi/asm-generic/unistd.h
>> index aa66718e1b48..090617a9a53a 100644
>> --- a/include/uapi/asm-generic/unistd.h
>> +++ b/include/uapi/asm-generic/unistd.h
>> @@ -889,8 +889,11 @@ __SYSCALL(__NR_set_mempolicy_home_node,
>> sys_set_mempolicy_home_node)
>>   #define __NR_lsm_self_attr 451
>>   __SYSCALL(__NR_lsm_self_attr, sys_lsm_self_attr)
>>   +#define __NR_lsm_module_list 452
>> +__SYSCALL(__NR_lsm_module_list, sys_lsm_module_list)
>> +
>>   #undef __NR_syscalls
>> -#define __NR_syscalls 452
>> +#define __NR_syscalls 453
>
> Same here.
>
>
>>     /*
>>    * 32 bit systems traditionally used different
>> diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
>> index 0fdb0341251d..bde9e74a3473 100644
>> --- a/kernel/sys_ni.c
>> +++ b/kernel/sys_ni.c
>> @@ -264,6 +264,7 @@ COND_SYSCALL(mremap);
>>     /* security/lsm_syscalls.c */
>>   COND_SYSCALL(lsm_self_attr);
>> +COND_SYSCALL(lsm_module_list);
>>     /* security/keys/keyctl.c */
>>   COND_SYSCALL(add_key);
>> diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
>> index da0fab7065e2..41d9ef945ede 100644
>> --- a/security/lsm_syscalls.c
>> +++ b/security/lsm_syscalls.c
>> @@ -154,3 +154,53 @@ SYSCALL_DEFINE3(lsm_self_attr,
>>       kfree(final);
>>       return rc;
>>   }
>> +
>> +/**
>> + * lsm_module_list - Return a list of the active security modules
>> + * @ids: the LSM module ids
>> + * @size: size of @ids, updated on return
>> + * @flags: reserved for future use, must be zero
>> + *
>> + * Returns a list of the active LSM ids. On success this function
>> + * returns the number of @ids array elements. This value may be zero
>> + * if there are no LSMs active. 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_module_list,
>> +           unsigned int __user *, ids,
>> +           size_t __user *, size,
>> +           int, flags)
>> +{
>> +    unsigned int *interum;
>> +    size_t total_size = lsm_id * sizeof(*interum);
>> +    size_t usize;
>> +    int rc;
>> +    int i;
>> +
>> +    if (get_user(usize, size))
>> +        return -EFAULT;
>> +
>> +    if (usize < total_size) {
>> +        if (put_user(total_size, size) != 0)
>> +            return -EFAULT;
>> +        return -E2BIG;
>> +    }
>> +
>> +    interum = kzalloc(total_size, GFP_KERNEL);
>> +    if (interum == NULL)
>> +        return -ENOMEM;
>> +
>> +    for (i = 0; i < lsm_id; i++)
>> +        interum[i] = lsm_idlist[i]->id;
>> +
>> +    if (copy_to_user(ids, interum, total_size) != 0 ||
>> +        put_user(total_size, size) != 0)
>> +        rc = -EFAULT;
>> +    else
>> +        rc = lsm_id;
>> +
>> +    kfree(interum);
>> +    return rc;
>> +}
Casey Schaufler Oct. 25, 2022, 12:39 a.m. UTC | #5
On 10/12/2022 3:04 PM, Kees Cook wrote:
> On Tue, Sep 27, 2022 at 01:31:55PM -0700, Casey Schaufler wrote:
>> +SYSCALL_DEFINE3(lsm_module_list,
>> +	       unsigned int __user *, ids,
>> +	       size_t __user *, size,
>> +	       int, flags)
> Please make this unsigned int.

Sure.


>> +{
>> +	unsigned int *interum;
>> +	size_t total_size = lsm_id * sizeof(*interum);
>> +	size_t usize;
>> +	int rc;
>> +	int i;
> Please test that flags == 0 so it can be used in the future:
>
> 	if (flags)
> 		return -EINVAL;

Yup.

>> +
>> +	if (get_user(usize, size))
>> +		return -EFAULT;
>> +
>> +	if (usize < total_size) {
>> +		if (put_user(total_size, size) != 0)
>> +			return -EFAULT;
>> +		return -E2BIG;
>> +	}
>> +
>> +	interum = kzalloc(total_size, GFP_KERNEL);
>> +	if (interum == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < lsm_id; i++)
>> +		interum[i] = lsm_idlist[i]->id;
>> +
>> +	if (copy_to_user(ids, interum, total_size) != 0 ||
>> +	    put_user(total_size, size) != 0)
>> +		rc = -EFAULT;
> No need to repeat this, if it is written first.
>
>> +	else
>> +		rc = lsm_id;
>> +
>> +	kfree(interum);
>> +	return rc;
> No need for the alloc/free. Here's what I would imagine for the whole
> thing:

A better approach. Thank you.

>
> 	if (flags)
> 		return -EINVAL;
>
> 	if (get_user(usize, size))
> 		return -EFAULT;
>
> 	if (put_user(total_size, size) != 0)
> 		return -EFAULT;
>
> 	if (usize < total_size)
> 		return -E2BIG;
>
> 	for (i = 0; i < lsm_id; i++)
> 		if (put_user(lsm_idlist[i]->id, id++))
> 			return -EFAULT;
>
> 	return lsm_id;
>
diff mbox series

Patch

diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 56d5c5202fd0..40b35e7069a7 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -373,6 +373,7 @@ 
 449	common	futex_waitv		sys_futex_waitv
 450	common	set_mempolicy_home_node	sys_set_mempolicy_home_node
 451	common	lsm_self_attr		sys_lsm_self_attr
+452	common	lsm_module_list		sys_lsm_module_list
 
 #
 # Due to a historical design error, certain syscalls are numbered differently
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 7f87ef8be546..e2e2a9e93e8c 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -1057,6 +1057,7 @@  asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long l
 					    unsigned long home_node,
 					    unsigned long flags);
 asmlinkage long sys_lsm_self_attr(struct lsm_ctx *ctx, size_t *size, int flags);
+asmlinkage long sys_lsm_module_list(unsigned int *ids, size_t *size, int flags);
 
 /*
  * Architecture-specific system calls
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index aa66718e1b48..090617a9a53a 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -889,8 +889,11 @@  __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node)
 #define __NR_lsm_self_attr 451
 __SYSCALL(__NR_lsm_self_attr, sys_lsm_self_attr)
 
+#define __NR_lsm_module_list 452
+__SYSCALL(__NR_lsm_module_list, sys_lsm_module_list)
+
 #undef __NR_syscalls
-#define __NR_syscalls 452
+#define __NR_syscalls 453
 
 /*
  * 32 bit systems traditionally used different
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 0fdb0341251d..bde9e74a3473 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -264,6 +264,7 @@  COND_SYSCALL(mremap);
 
 /* security/lsm_syscalls.c */
 COND_SYSCALL(lsm_self_attr);
+COND_SYSCALL(lsm_module_list);
 
 /* security/keys/keyctl.c */
 COND_SYSCALL(add_key);
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index da0fab7065e2..41d9ef945ede 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -154,3 +154,53 @@  SYSCALL_DEFINE3(lsm_self_attr,
 	kfree(final);
 	return rc;
 }
+
+/**
+ * lsm_module_list - Return a list of the active security modules
+ * @ids: the LSM module ids
+ * @size: size of @ids, updated on return
+ * @flags: reserved for future use, must be zero
+ *
+ * Returns a list of the active LSM ids. On success this function
+ * returns the number of @ids array elements. This value may be zero
+ * if there are no LSMs active. 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_module_list,
+	       unsigned int __user *, ids,
+	       size_t __user *, size,
+	       int, flags)
+{
+	unsigned int *interum;
+	size_t total_size = lsm_id * sizeof(*interum);
+	size_t usize;
+	int rc;
+	int i;
+
+	if (get_user(usize, size))
+		return -EFAULT;
+
+	if (usize < total_size) {
+		if (put_user(total_size, size) != 0)
+			return -EFAULT;
+		return -E2BIG;
+	}
+
+	interum = kzalloc(total_size, GFP_KERNEL);
+	if (interum == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < lsm_id; i++)
+		interum[i] = lsm_idlist[i]->id;
+
+	if (copy_to_user(ids, interum, total_size) != 0 ||
+	    put_user(total_size, size) != 0)
+		rc = -EFAULT;
+	else
+		rc = lsm_id;
+
+	kfree(interum);
+	return rc;
+}