[v1] LSM: Enable multiple calls to security_add_hooks() for the same LSM
diff mbox series

Message ID 20170429190257.27137-1-mic@digikod.net
State New, archived
Headers show
Series
  • [v1] LSM: Enable multiple calls to security_add_hooks() for the same LSM
Related show

Commit Message

Mickaël Salaün April 29, 2017, 7:02 p.m. UTC
Check if the registering LSM already registered hooks just before. This
enable to split hook declarations into multiple files without
registering multiple time the same LSM name, starting from commit
d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Casey Schaufler <casey@schaufler-ca.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Link: https://lkml.kernel.org/r/ccad825b-7a58-e499-e51b-bd7c98581afe@schaufler-ca.com
---
 security/security.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

Comments

Casey Schaufler April 29, 2017, 8 p.m. UTC | #1
On 4/29/2017 12:02 PM, Mickaël Salaün wrote:
> Check if the registering LSM already registered hooks just before. This
> enable to split hook declarations into multiple files without
> registering multiple time the same LSM name, starting from commit
> d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").

What's special about the previous registration? Keep it
simple and check it the name is already anywhere on the
list and only add it if it's not already there. I don't
see advantage to:

	% cat /sys/kernel/security/lsm
	capability,yama,spiffy,selinux,spiffy

over
	% cat /sys/kernel/security/lsm
	capability,yama,spiffy,selinux

>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Casey Schaufler <casey@schaufler-ca.com>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> Link: https://lkml.kernel.org/r/ccad825b-7a58-e499-e51b-bd7c98581afe@schaufler-ca.com
> ---
>  security/security.c | 30 ++++++++++++++++++++++++++++++
>  1 file changed, 30 insertions(+)
>
> diff --git a/security/security.c b/security/security.c
> index 549bddcc2116..6be65050b268 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -25,6 +25,7 @@
>  #include <linux/mount.h>
>  #include <linux/personality.h>
>  #include <linux/backing-dev.h>
> +#include <linux/string.h>
>  #include <net/flow.h>
>  
>  #define MAX_LSM_EVM_XATTR	2
> @@ -86,6 +87,32 @@ static int __init choose_lsm(char *str)
>  }
>  __setup("security=", choose_lsm);
>  
> +static bool match_last_lsm(const char *list, const char *last)
> +{
> +	size_t list_len, last_len, i;
> +
> +	if (!list || !last)
> +		return false;
> +	list_len = strlen(list);
> +	last_len = strlen(last);
> +	if (!last_len || !list_len)
> +		return false;
> +	if (last_len > list_len)
> +		return false;
> +
> +	for (i = 0; i < last_len; i++) {
> +		if (list[list_len - 1 - i] != last[last_len - 1 - i])
> +			return false;
> +	}
> +	/* Check if last_len == list_len */
> +	if (i == list_len)
> +		return true;
> +	/* Check if it is a full name */
> +	if (list[list_len - 1 - i] == ',')
> +		return true;
> +	return false;
> +}
> +
>  static int lsm_append(char *new, char **result)
>  {
>  	char *cp;
> @@ -93,6 +120,9 @@ static int lsm_append(char *new, char **result)
>  	if (*result == NULL) {
>  		*result = kstrdup(new, GFP_KERNEL);
>  	} else {
> +		/* Check if it is the last registered name */
> +		if (match_last_lsm(*result, new))
> +			return 0;
>  		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
>  		if (cp == NULL)
>  			return -ENOMEM;
Tetsuo Handa April 30, 2017, 2:11 a.m. UTC | #2
Casey Schaufler wrote:
> On 4/29/2017 12:02 PM, Mickael Salaun wrote:
> > Check if the registering LSM already registered hooks just before. This
> > enable to split hook declarations into multiple files without
> > registering multiple time the same LSM name, starting from commit
> > d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").
> 
> What's special about the previous registration? Keep it
> simple and check it the name is already anywhere on the
> list and only add it if it's not already there. I don't
> see advantage to:
> 
> 	% cat /sys/kernel/security/lsm
> 	capability,yama,spiffy,selinux,spiffy
> 
> over
> 	% cat /sys/kernel/security/lsm
> 	capability,yama,spiffy,selinux
> 

-	if (lsm_append(lsm, &lsm_names) < 0)
+	if (lsm && lsm_append(lsm, &lsm_names) < 0)

in security_add_hooks()?
Mickaël Salaün April 30, 2017, 9:35 a.m. UTC | #3
On 30/04/2017 04:11, Tetsuo Handa wrote:
> Casey Schaufler wrote:
>> On 4/29/2017 12:02 PM, Mickael Salaun wrote:
>>> Check if the registering LSM already registered hooks just before. This
>>> enable to split hook declarations into multiple files without
>>> registering multiple time the same LSM name, starting from commit
>>> d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").
>>
>> What's special about the previous registration? Keep it
>> simple and check it the name is already anywhere on the
>> list and only add it if it's not already there. I don't
>> see advantage to:
>>
>> 	% cat /sys/kernel/security/lsm
>> 	capability,yama,spiffy,selinux,spiffy
>>
>> over
>> 	% cat /sys/kernel/security/lsm
>> 	capability,yama,spiffy,selinux
>>

That was my first though, but then I realized that I don't see any use
case where an LSM would register hooks interleaved with other LSM. I
find the current approach simpler because we only search from the end of
the string and we do not handle special cases (e.g. matching only a
sub-name). Moreover, this approach respects the semantic describe in
Documentation/security/LSM.txt: "The list reflects the order in which
checks are made".

> 
> -	if (lsm_append(lsm, &lsm_names) < 0)
> +	if (lsm && lsm_append(lsm, &lsm_names) < 0)
> 
> in security_add_hooks()?
> 

That was considered
[https://lkml.kernel.org/r/CAGXu5jJCvJ6-uZ=Kfhh3xD7UvaY+G99e9NXFMzvi=9OQzA6Ecg@mail.gmail.com]
but Kees and Casey seem to prefer the current approach.
James Morris April 30, 2017, 11:28 p.m. UTC | #4
On Sat, 29 Apr 2017, Mickaël Salaün wrote:

> Check if the registering LSM already registered hooks just before. This
> enable to split hook declarations into multiple files without
> registering multiple time the same LSM name, starting from commit
> d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").

Please include a detailed rationale for these patches.  The above tells us 
very little about why they are needed.
Mickaël Salaün May 8, 2017, 7:24 p.m. UTC | #5
On 01/05/2017 01:28, James Morris wrote:
> On Sat, 29 Apr 2017, Mickaël Salaün wrote:
> 
>> Check if the registering LSM already registered hooks just before. This
>> enable to split hook declarations into multiple files without
>> registering multiple time the same LSM name, starting from commit
>> d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").
> 
> Please include a detailed rationale for these patches.  The above tells us 
> very little about why they are needed.

Right, what do you think about that?

The commit d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm") extend
security_add_hooks() with a new parameter to register the LSM name,
which may be useful to make the list of currently loaded LSM available
to userspace. However, there is no clean way for an LSM no split its
hook declarations into multiple files, which may reduce the mess with
all the included files (needed for LSM hook argument types) and make the
source code easier to review and maintain.

This change allows an LSM to register multiple times its hook while
keeping a consistent list of LSM names as described in
Documentation/security/LSM.txt . The list reflects the order in which
checks are made. This patch only check for the last registered LSM,
which should be the only case. If an LSM register multiple times its
hooks, interleaved with other LSM registrations, which should not
happen, its name will still appear in the same order that the hooks are
called, hence multiple times.


Casey, Tetsuo, are you OK with this approach or do you want me to handle
the case with interleaved hook registration, i.e. no duplicate name nor
following the current Documentation/security/LSM.txt?
What about the API with the NULL name (which is much simpler)?

Regards,
 Mickaël
Casey Schaufler May 8, 2017, 8:07 p.m. UTC | #6
On 5/8/2017 12:24 PM, Mickaël Salaün wrote:
> On 01/05/2017 01:28, James Morris wrote:
>> On Sat, 29 Apr 2017, Mickaël Salaün wrote:
>>
>>> Check if the registering LSM already registered hooks just before. This
>>> enable to split hook declarations into multiple files without
>>> registering multiple time the same LSM name, starting from commit
>>> d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").
>> Please include a detailed rationale for these patches.  The above tells us 
>> very little about why they are needed.
> Right, what do you think about that?
>
> The commit d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm") extend
> security_add_hooks() with a new parameter to register the LSM name,
> which may be useful to make the list of currently loaded LSM available
> to userspace. However, there is no clean way for an LSM no split its
> hook declarations into multiple files, which may reduce the mess with
> all the included files (needed for LSM hook argument types) and make the
> source code easier to review and maintain.
>
> This change allows an LSM to register multiple times its hook while
> keeping a consistent list of LSM names as described in
> Documentation/security/LSM.txt . The list reflects the order in which
> checks are made. This patch only check for the last registered LSM,
> which should be the only case. If an LSM register multiple times its
> hooks, interleaved with other LSM registrations, which should not
> happen, its name will still appear in the same order that the hooks are
> called, hence multiple times.
>
>
> Casey, Tetsuo, are you OK with this approach or do you want me to handle
> the case with interleaved hook registration, i.e. no duplicate name nor
> following the current Documentation/security/LSM.txt?
> What about the API with the NULL name (which is much simpler)?

Initially I thought that the module name should
never appear more than once, however I could see
a module that would bracket another or another set,
so

	capability,spiffy,selinux

might be different behaviorally than

	capability,spiffy,selinux,spiffy

and userspace might care. I still don't see any value
in

	capability,selinux,spiffy,spiffy

Passing a NULL name could lead to ambiguity if more
than one module did that, so I can't say I approve.


>
> Regards,
>  Mickaël
>
Mickaël Salaün May 8, 2017, 8:12 p.m. UTC | #7
On 08/05/2017 22:07, Casey Schaufler wrote:
> On 5/8/2017 12:24 PM, Mickaël Salaün wrote:
>> On 01/05/2017 01:28, James Morris wrote:
>>> On Sat, 29 Apr 2017, Mickaël Salaün wrote:
>>>
>>>> Check if the registering LSM already registered hooks just before. This
>>>> enable to split hook declarations into multiple files without
>>>> registering multiple time the same LSM name, starting from commit
>>>> d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm").
>>> Please include a detailed rationale for these patches.  The above tells us 
>>> very little about why they are needed.
>> Right, what do you think about that?
>>
>> The commit d69dece5f5b6 ("LSM: Add /sys/kernel/security/lsm") extend
>> security_add_hooks() with a new parameter to register the LSM name,
>> which may be useful to make the list of currently loaded LSM available
>> to userspace. However, there is no clean way for an LSM no split its
>> hook declarations into multiple files, which may reduce the mess with
>> all the included files (needed for LSM hook argument types) and make the
>> source code easier to review and maintain.
>>
>> This change allows an LSM to register multiple times its hook while
>> keeping a consistent list of LSM names as described in
>> Documentation/security/LSM.txt . The list reflects the order in which
>> checks are made. This patch only check for the last registered LSM,
>> which should be the only case. If an LSM register multiple times its
>> hooks, interleaved with other LSM registrations, which should not
>> happen, its name will still appear in the same order that the hooks are
>> called, hence multiple times.
>>
>>
>> Casey, Tetsuo, are you OK with this approach or do you want me to handle
>> the case with interleaved hook registration, i.e. no duplicate name nor
>> following the current Documentation/security/LSM.txt?
>> What about the API with the NULL name (which is much simpler)?
> 
> Initially I thought that the module name should
> never appear more than once, however I could see
> a module that would bracket another or another set,
> so
> 
> 	capability,spiffy,selinux
> 
> might be different behaviorally than
> 
> 	capability,spiffy,selinux,spiffy
> 
> and userspace might care. I still don't see any value
> in
> 
> 	capability,selinux,spiffy,spiffy
> 
> Passing a NULL name could lead to ambiguity if more
> than one module did that, so I can't say I approve.

OK, so this patch seems good.

Patch
diff mbox series

diff --git a/security/security.c b/security/security.c
index 549bddcc2116..6be65050b268 100644
--- a/security/security.c
+++ b/security/security.c
@@ -25,6 +25,7 @@ 
 #include <linux/mount.h>
 #include <linux/personality.h>
 #include <linux/backing-dev.h>
+#include <linux/string.h>
 #include <net/flow.h>
 
 #define MAX_LSM_EVM_XATTR	2
@@ -86,6 +87,32 @@  static int __init choose_lsm(char *str)
 }
 __setup("security=", choose_lsm);
 
+static bool match_last_lsm(const char *list, const char *last)
+{
+	size_t list_len, last_len, i;
+
+	if (!list || !last)
+		return false;
+	list_len = strlen(list);
+	last_len = strlen(last);
+	if (!last_len || !list_len)
+		return false;
+	if (last_len > list_len)
+		return false;
+
+	for (i = 0; i < last_len; i++) {
+		if (list[list_len - 1 - i] != last[last_len - 1 - i])
+			return false;
+	}
+	/* Check if last_len == list_len */
+	if (i == list_len)
+		return true;
+	/* Check if it is a full name */
+	if (list[list_len - 1 - i] == ',')
+		return true;
+	return false;
+}
+
 static int lsm_append(char *new, char **result)
 {
 	char *cp;
@@ -93,6 +120,9 @@  static int lsm_append(char *new, char **result)
 	if (*result == NULL) {
 		*result = kstrdup(new, GFP_KERNEL);
 	} else {
+		/* Check if it is the last registered name */
+		if (match_last_lsm(*result, new))
+			return 0;
 		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
 		if (cp == NULL)
 			return -ENOMEM;