diff mbox series

[4/5] LSM: Define SELinux function to measure security state

Message ID 20200613024130.3356-5-nramas@linux.microsoft.com (mailing list archive)
State New, archived
Headers show
Series LSM: Measure security module state | expand

Commit Message

Lakshmi Ramasubramanian June 13, 2020, 2:41 a.m. UTC
SELinux needs to implement the interface function, security_state(), for
the LSM to gather SELinux data for measuring. Define the security_state()
function in SELinux.

The security modules should be able to notify the LSM when there is
a change in the module's data. Define a function namely 
security_state_change() in the LSM that the security modules
can call to provide the updated data for measurement.

Call security_state_change() function from SELinux to report data
when SELinux's state is updated.

Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
---
 include/linux/lsm_hooks.h           |  3 ++
 security/security.c                 |  5 ++++
 security/selinux/hooks.c            | 43 +++++++++++++++++++++++++++++
 security/selinux/include/security.h |  2 ++
 security/selinux/selinuxfs.c        |  1 +
 5 files changed, 54 insertions(+)

Comments

Stephen Smalley June 15, 2020, 11:57 a.m. UTC | #1
On Fri, Jun 12, 2020 at 10:42 PM Lakshmi Ramasubramanian
<nramas@linux.microsoft.com> wrote:
>
> SELinux needs to implement the interface function, security_state(), for
> the LSM to gather SELinux data for measuring. Define the security_state()
> function in SELinux.
>
> The security modules should be able to notify the LSM when there is
> a change in the module's data. Define a function namely
> security_state_change() in the LSM that the security modules
> can call to provide the updated data for measurement.
>
> Call security_state_change() function from SELinux to report data
> when SELinux's state is updated.
>
> Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
> ---
> diff --git a/security/security.c b/security/security.c
> index a6e2d1cd95af..e7175db5a093 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -238,6 +238,11 @@ static void __init initialize_lsm(struct lsm_info *lsm)
>         }
>  }
>
> +void security_state_change(char *lsm_name, void *state, int state_len)
> +{
> +       ima_lsm_state(lsm_name, state, state_len);
> +}
> +

What's the benefit of this trivial function instead of just calling
ima_lsm_state() directly?

> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 7e954b555be6..bbc908a1fcd1 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -7225,6 +7225,47 @@ static __init int selinux_init(void)
>         return 0;
>  }
>
> +static int selinux_security_state(char **lsm_name, void **state,
> +                                 int *state_len)
> +{
> +       int rc = 0;
> +       char *new_state;
> +       static char *security_state_string = "enabled=%d;enforcing=%d";
> +
> +       *lsm_name = kstrdup("selinux", GFP_KERNEL);
> +       if (!*lsm_name)
> +               return -ENOMEM;
> +
> +       new_state = kzalloc(strlen(security_state_string) + 1, GFP_KERNEL);
> +       if (!new_state) {
> +               kfree(*lsm_name);
> +               *lsm_name = NULL;
> +               rc = -ENOMEM;
> +               goto out;
> +       }
> +
> +       *state_len = sprintf(new_state, security_state_string,
> +                            !selinux_disabled(&selinux_state),
> +                            enforcing_enabled(&selinux_state));

I think I mentioned this on a previous version of these patches, but I
would recommend including more than just the enabled and enforcing
states in your measurement.  Other low-hanging fruit would be the
other selinux_state booleans (checkreqprot, initialized,
policycap[0..__POLICYDB_CAPABILITY_MAX]).  Going a bit further one
could take a hash of the loaded policy by using security_read_policy()
and then computing a hash using whatever hash ima prefers over the
returned data,len pair.  You likely also need to think about how to
allow future extensibility of the state in a backward-compatible
manner, so that future additions do not immediately break systems
relying on older measurements.
Stephen Smalley June 15, 2020, 12:15 p.m. UTC | #2
On Mon, Jun 15, 2020 at 7:57 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
> I think I mentioned this on a previous version of these patches, but I
> would recommend including more than just the enabled and enforcing
> states in your measurement.  Other low-hanging fruit would be the
> other selinux_state booleans (checkreqprot, initialized,
> policycap[0..__POLICYDB_CAPABILITY_MAX]).  Going a bit further one
> could take a hash of the loaded policy by using security_read_policy()

On second thought, you probably a variant of security_read_policy()
since it would be a kernel-internal allocation and thus shouldn't use
vmalloc_user().

> and then computing a hash using whatever hash ima prefers over the
> returned data,len pair.  You likely also need to think about how to
> allow future extensibility of the state in a backward-compatible
> manner, so that future additions do not immediately break systems
> relying on older measurements.
Lakshmi Ramasubramanian June 15, 2020, 4:45 p.m. UTC | #3
On 6/15/20 4:57 AM, Stephen Smalley wrote:

Hi Stephen,

Thanks for reviewing the patches.

>> +void security_state_change(char *lsm_name, void *state, int state_len)
>> +{
>> +       ima_lsm_state(lsm_name, state, state_len);
>> +}
>> +
> 
> What's the benefit of this trivial function instead of just calling
> ima_lsm_state() directly?

One of the feedback Casey Schaufler had given earlier was that calling 
an IMA function directly from SELinux (or, any of the Security Modules) 
would be a layering violation.

LSM framework (security/security.c) already calls IMA functions now (for 
example, ima_bprm_check() is called from security_bprm_check()). I 
followed the same pattern for measuring LSM data as well.

Please let me know if I misunderstood Casey's comment.

>> +static int selinux_security_state(char **lsm_name, void **state,
>> +                                 int *state_len)
>> +{
>> +       int rc = 0;
>> +       char *new_state;
>> +       static char *security_state_string = "enabled=%d;enforcing=%d";
>> +
>> +       *lsm_name = kstrdup("selinux", GFP_KERNEL);
>> +       if (!*lsm_name)
>> +               return -ENOMEM;
>> +
>> +       new_state = kzalloc(strlen(security_state_string) + 1, GFP_KERNEL);
>> +       if (!new_state) {
>> +               kfree(*lsm_name);
>> +               *lsm_name = NULL;
>> +               rc = -ENOMEM;
>> +               goto out;
>> +       }
>> +
>> +       *state_len = sprintf(new_state, security_state_string,
>> +                            !selinux_disabled(&selinux_state),
>> +                            enforcing_enabled(&selinux_state));
> 
> I think I mentioned this on a previous version of these patches, but I
> would recommend including more than just the enabled and enforcing
> states in your measurement.  Other low-hanging fruit would be the
> other selinux_state booleans (checkreqprot, initialized,
> policycap[0..__POLICYDB_CAPABILITY_MAX]).  Going a bit further one
> could take a hash of the loaded policy by using security_read_policy()
> and then computing a hash using whatever hash ima prefers over the
> returned data,len pair.  You likely also need to think about how to
> allow future extensibility of the state in a backward-compatible
> manner, so that future additions do not immediately break systems
> relying on older measurements.
> 

Sure - I will address this one in the next update.

thanks,
  -lakshmi
Casey Schaufler June 15, 2020, 5:33 p.m. UTC | #4
On 6/15/2020 9:45 AM, Lakshmi Ramasubramanian wrote:
> On 6/15/20 4:57 AM, Stephen Smalley wrote:
>
> Hi Stephen,
>
> Thanks for reviewing the patches.
>
>>> +void security_state_change(char *lsm_name, void *state, int state_len)
>>> +{
>>> +       ima_lsm_state(lsm_name, state, state_len);
>>> +}
>>> +
>>
>> What's the benefit of this trivial function instead of just calling
>> ima_lsm_state() directly?
>
> One of the feedback Casey Schaufler had given earlier was that calling an IMA function directly from SELinux (or, any of the Security Modules) would be a layering violation.

Hiding the ima_lsm_state() call doesn't address the layering.
The point is that SELinux code being called from IMA (or the
other way around) breaks the subsystem isolation. Unfortunately,
it isn't obvious to me how you would go about what you're doing
without integrating the subsystems.

>
> LSM framework (security/security.c) already calls IMA functions now (for example, ima_bprm_check() is called from security_bprm_check()). I followed the same pattern for measuring LSM data as well.
>
> Please let me know if I misunderstood Casey's comment.
>
Mimi Zohar June 15, 2020, 5:44 p.m. UTC | #5
(Cc'ing John)

On Mon, 2020-06-15 at 10:33 -0700, Casey Schaufler wrote:
> On 6/15/2020 9:45 AM, Lakshmi Ramasubramanian wrote:
> > On 6/15/20 4:57 AM, Stephen Smalley wrote:
> >
> > Hi Stephen,
> >
> > Thanks for reviewing the patches.
> >
> >>> +void security_state_change(char *lsm_name, void *state, int state_len)
> >>> +{
> >>> +       ima_lsm_state(lsm_name, state, state_len);
> >>> +}
> >>> +
> >>
> >> What's the benefit of this trivial function instead of just calling
> >> ima_lsm_state() directly?
> >
> > One of the feedback Casey Schaufler had given earlier was that calling an IMA function directly from SELinux (or, any of the Security Modules) would be a layering violation.
> 
> Hiding the ima_lsm_state() call doesn't address the layering.
> The point is that SELinux code being called from IMA (or the
> other way around) breaks the subsystem isolation. Unfortunately,
> it isn't obvious to me how you would go about what you're doing
> without integrating the subsystems.

Casey, I'm not sure why you think there is a layering issue here.
 There were multiple iterations of IMA before it was upstreamed.  One
iteration had separate integrity hooks(LIM).  Only when the IMA calls
and the security hooks are co-located, are they combined, as requested
by Linus.

There was some AppArmour discussion about calling IMA directly, but I
haven't heard about it in a while or seen the patch.

Mimi
Stephen Smalley June 15, 2020, 8:31 p.m. UTC | #6
On Mon, Jun 15, 2020 at 12:45 PM Lakshmi Ramasubramanian
<nramas@linux.microsoft.com> wrote:
>
> On 6/15/20 4:57 AM, Stephen Smalley wrote:
> > I think I mentioned this on a previous version of these patches, but I
> > would recommend including more than just the enabled and enforcing
> > states in your measurement.  Other low-hanging fruit would be the
> > other selinux_state booleans (checkreqprot, initialized,
> > policycap[0..__POLICYDB_CAPABILITY_MAX]).  Going a bit further one
> > could take a hash of the loaded policy by using security_read_policy()
> > and then computing a hash using whatever hash ima prefers over the
> > returned data,len pair.  You likely also need to think about how to
> > allow future extensibility of the state in a backward-compatible
> > manner, so that future additions do not immediately break systems
> > relying on older measurements.
> >
>
> Sure - I will address this one in the next update.

Please add selinux list to the cc for future versions too.
Casey Schaufler June 15, 2020, 11:18 p.m. UTC | #7
On 6/15/2020 10:44 AM, Mimi Zohar wrote:
> (Cc'ing John)
>
> On Mon, 2020-06-15 at 10:33 -0700, Casey Schaufler wrote:
>> On 6/15/2020 9:45 AM, Lakshmi Ramasubramanian wrote:
>>> On 6/15/20 4:57 AM, Stephen Smalley wrote:
>>>
>>> Hi Stephen,
>>>
>>> Thanks for reviewing the patches.
>>>
>>>>> +void security_state_change(char *lsm_name, void *state, int state_len)
>>>>> +{
>>>>> +       ima_lsm_state(lsm_name, state, state_len);
>>>>> +}
>>>>> +
>>>> What's the benefit of this trivial function instead of just calling
>>>> ima_lsm_state() directly?
>>> One of the feedback Casey Schaufler had given earlier was that calling an IMA function directly from SELinux (or, any of the Security Modules) would be a layering violation.
>> Hiding the ima_lsm_state() call doesn't address the layering.
>> The point is that SELinux code being called from IMA (or the
>> other way around) breaks the subsystem isolation. Unfortunately,
>> it isn't obvious to me how you would go about what you're doing
>> without integrating the subsystems.
> Casey, I'm not sure why you think there is a layering issue here.

I don't think there is, after further review. If the IMA code called
selinux_dosomething() directly I'd be very concerned, but calling
security_dosomething() which then calls selinux_dosomething() is fine.
If YAMA called security_dosomething() I'd be very concerned, but that's
not what's happening here.

>  There were multiple iterations of IMA before it was upstreamed.  One
> iteration had separate integrity hooks(LIM).  Only when the IMA calls
> and the security hooks are co-located, are they combined, as requested
> by Linus.
>
> There was some AppArmour discussion about calling IMA directly, but I
> haven't heard about it in a while or seen the patch.
>
> Mimi
Mimi Zohar June 16, 2020, 12:44 a.m. UTC | #8
On Mon, 2020-06-15 at 16:18 -0700, Casey Schaufler wrote:
> On 6/15/2020 10:44 AM, Mimi Zohar wrote:
> > (Cc'ing John)
> >
> > On Mon, 2020-06-15 at 10:33 -0700, Casey Schaufler wrote:
> >> On 6/15/2020 9:45 AM, Lakshmi Ramasubramanian wrote:
> >>> On 6/15/20 4:57 AM, Stephen Smalley wrote:
> >>>
> >>> Hi Stephen,
> >>>
> >>> Thanks for reviewing the patches.
> >>>
> >>>>> +void security_state_change(char *lsm_name, void *state, int state_len)
> >>>>> +{
> >>>>> +       ima_lsm_state(lsm_name, state, state_len);
> >>>>> +}
> >>>>> +
> >>>> What's the benefit of this trivial function instead of just calling
> >>>> ima_lsm_state() directly?
> >>> One of the feedback Casey Schaufler had given earlier was that calling an IMA function directly from SELinux (or, any of the Security Modules) would be a layering violation.
> >> Hiding the ima_lsm_state() call doesn't address the layering.
> >> The point is that SELinux code being called from IMA (or the
> >> other way around) breaks the subsystem isolation. Unfortunately,
> >> it isn't obvious to me how you would go about what you're doing
> >> without integrating the subsystems.
> > Casey, I'm not sure why you think there is a layering issue here.
> 
> I don't think there is, after further review. If the IMA code called
> selinux_dosomething() directly I'd be very concerned, but calling
> security_dosomething() which then calls selinux_dosomething() is fine.
> If YAMA called security_dosomething() I'd be very concerned, but that's
> not what's happening here.

As long as the call to IMA is not an LSM hook, there shouldn't be a
problem with an LSM calling IMA directly.  A perfect example is
measuring, appraising and/or auditing LSM policies.

Mimi

> 
> >  There were multiple iterations of IMA before it was upstreamed.  One
> > iteration had separate integrity hooks(LIM).  Only when the IMA calls
> > and the security hooks are co-located, are they combined, as requested
> > by Linus.
> >
> > There was some AppArmour discussion about calling IMA directly, but I
> > haven't heard about it in a while or seen the patch.
John Johansen June 16, 2020, 8:38 a.m. UTC | #9
On 6/15/20 10:44 AM, Mimi Zohar wrote:
> (Cc'ing John)
> 
> On Mon, 2020-06-15 at 10:33 -0700, Casey Schaufler wrote:
>> On 6/15/2020 9:45 AM, Lakshmi Ramasubramanian wrote:
>>> On 6/15/20 4:57 AM, Stephen Smalley wrote:
>>>
>>> Hi Stephen,
>>>
>>> Thanks for reviewing the patches.
>>>
>>>>> +void security_state_change(char *lsm_name, void *state, int state_len)
>>>>> +{
>>>>> +       ima_lsm_state(lsm_name, state, state_len);
>>>>> +}
>>>>> +
>>>>
>>>> What's the benefit of this trivial function instead of just calling
>>>> ima_lsm_state() directly?
>>>
>>> One of the feedback Casey Schaufler had given earlier was that calling an IMA function directly from SELinux (or, any of the Security Modules) would be a layering violation.
>>
>> Hiding the ima_lsm_state() call doesn't address the layering.
>> The point is that SELinux code being called from IMA (or the
>> other way around) breaks the subsystem isolation. Unfortunately,
>> it isn't obvious to me how you would go about what you're doing
>> without integrating the subsystems.
> 
> Casey, I'm not sure why you think there is a layering issue here.
>  There were multiple iterations of IMA before it was upstreamed.  One
> iteration had separate integrity hooks(LIM).  Only when the IMA calls
> and the security hooks are co-located, are they combined, as requested
> by Linus.
> 
I don't see the layering violation here either, Casey has already
responded and I don't have anything to add

> There was some AppArmour discussion about calling IMA directly, but I
> haven't heard about it in a while or seen the patch.
> 
its lower priority than other work. I think calling IMA directly has use
cases but must be done very carefully, and well reviewed. I have would
have more concerns with IMA calling directly into the various LSMs.
diff mbox series

Patch

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index da248c3fd4ac..a63de046139e 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1572,6 +1572,9 @@  struct lsm_info {
 			      int *state_len); /*Optional */
 };
 
+/* Called by LSMs to notify security state change */
+extern void security_state_change(char *lsm_name, void *state, int state_len);
+
 extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
 extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
 
diff --git a/security/security.c b/security/security.c
index a6e2d1cd95af..e7175db5a093 100644
--- a/security/security.c
+++ b/security/security.c
@@ -238,6 +238,11 @@  static void __init initialize_lsm(struct lsm_info *lsm)
 	}
 }
 
+void security_state_change(char *lsm_name, void *state, int state_len)
+{
+	ima_lsm_state(lsm_name, state, state_len);
+}
+
 static int measure_security_state(struct lsm_info *lsm)
 {
 	char *lsm_name = NULL;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 7e954b555be6..bbc908a1fcd1 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7225,6 +7225,47 @@  static __init int selinux_init(void)
 	return 0;
 }
 
+static int selinux_security_state(char **lsm_name, void **state,
+				  int *state_len)
+{
+	int rc = 0;
+	char *new_state;
+	static char *security_state_string = "enabled=%d;enforcing=%d";
+
+	*lsm_name = kstrdup("selinux", GFP_KERNEL);
+	if (!*lsm_name)
+		return -ENOMEM;
+
+	new_state = kzalloc(strlen(security_state_string) + 1, GFP_KERNEL);
+	if (!new_state) {
+		kfree(*lsm_name);
+		*lsm_name = NULL;
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	*state_len = sprintf(new_state, security_state_string,
+			     !selinux_disabled(&selinux_state),
+			     enforcing_enabled(&selinux_state));
+	*state = new_state;
+
+out:
+	return rc;
+}
+
+void notify_security_state_change(void)
+{
+	char *lsm_name = NULL;
+	void *state = NULL;
+	int state_len = 0;
+
+	if (!selinux_security_state(&lsm_name, &state, &state_len)) {
+		security_state_change(lsm_name, state, state_len);
+		kfree(state);
+		kfree(lsm_name);
+	}
+}
+
 static void delayed_superblock_init(struct super_block *sb, void *unused)
 {
 	selinux_set_mnt_opts(sb, NULL, 0, NULL);
@@ -7247,6 +7288,7 @@  DEFINE_LSM(selinux) = {
 	.enabled = &selinux_enabled_boot,
 	.blobs = &selinux_blob_sizes,
 	.init = selinux_init,
+	.security_state = selinux_security_state,
 };
 
 #if defined(CONFIG_NETFILTER)
@@ -7357,6 +7399,7 @@  int selinux_disable(struct selinux_state *state)
 	}
 
 	selinux_mark_disabled(state);
+	notify_security_state_change();
 
 	pr_info("SELinux:  Disabled at runtime.\n");
 
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index b0e02cfe3ce1..83c6ada45c7c 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -232,6 +232,8 @@  size_t security_policydb_len(struct selinux_state *state);
 int security_policycap_supported(struct selinux_state *state,
 				 unsigned int req_cap);
 
+void notify_security_state_change(void);
+
 #define SEL_VEC_MAX 32
 struct av_decision {
 	u32 allowed;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 4781314c2510..8a5ba32a7775 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -173,6 +173,7 @@  static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
 			from_kuid(&init_user_ns, audit_get_loginuid(current)),
 			audit_get_sessionid(current));
 		enforcing_set(state, new_value);
+		notify_security_state_change();
 		if (new_value)
 			avc_ss_reset(state->avc, 0);
 		selnl_notify_setenforce(new_value);