* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Thomas Gleixner @ 2019-08-16 9:59 UTC (permalink / raw)
To: Jordan Glover
Cc: Alexei Starovoitov, Andy Lutomirski, Daniel Colascione, Song Liu,
Kees Cook, Networking, bpf, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Lorenz Bauer, Jann Horn, Greg KH, Linux API,
LSM List
In-Reply-To: <fkD3fs46a1YnR4lh0tEG-g3tDnDcyZuzji7bAUR9wujPLLl75ZhI8Yk-H1jZpSugO7qChVeCwxAMmxLdeoF2QFS3ZzuYlh7zmeZOmhDJxww=@protonmail.ch>
On Fri, 16 Aug 2019, Jordan Glover wrote:
> "systemd --user" service? Trying to do so will fail with:
> "Failed to apply ambient capabilities (before UID change): Operation not permitted"
>
> I think it's crucial to clear that point to avoid confusion in this discussion
> where people are talking about different things.
>
> On the other hand running "systemd --system" service with:
>
> User=nobody
> AmbientCapabilities=CAP_NET_ADMIN
>
> is perfectly legit and clears some security concerns as only privileged user
> can start such service.
While we are at it, can we please stop looking at this from a systemd only
perspective. There is a world outside of systemd.
Thanks,
tglx
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Jordan Glover @ 2019-08-16 11:33 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Alexei Starovoitov, Andy Lutomirski, Daniel Colascione, Song Liu,
Kees Cook, Networking, bpf, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Lorenz Bauer, Jann Horn, Greg KH, Linux API,
LSM List
In-Reply-To: <alpine.DEB.2.21.1908161158490.1873@nanos.tec.linutronix.de>
On Friday, August 16, 2019 9:59 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> On Fri, 16 Aug 2019, Jordan Glover wrote:
>
> > "systemd --user" service? Trying to do so will fail with:
> > "Failed to apply ambient capabilities (before UID change): Operation not permitted"
> > I think it's crucial to clear that point to avoid confusion in this discussion
> > where people are talking about different things.
> > On the other hand running "systemd --system" service with:
> > User=nobody
> > AmbientCapabilities=CAP_NET_ADMIN
> > is perfectly legit and clears some security concerns as only privileged user
> > can start such service.
>
> While we are at it, can we please stop looking at this from a systemd only
> perspective. There is a world outside of systemd.
>
> Thanks,
>
> tglx
If you define:
"systemd --user" == unprivileged process started by unprivileged user
"systemd --system" == process started by privileged user but run as another
user which keeps some of parent user privileges and drops others
you can get rid of "systemd" from the equation.
"systemd --user" was the example provided by Alexei when asked about the usecase
but his description didn't match what it does so it's not obvious what the real
usecase is. I'm sure there can be many more examples and systemd isn't important
here in particular beside to understand this specific example.
Jordan
^ permalink raw reply
* Re: [GIT PULL] Keys: Set 4 - Key ACLs for 5.3
From: David Howells @ 2019-08-16 13:36 UTC (permalink / raw)
To: Mimi Zohar
Cc: dhowells, Linus Torvalds, James Morris, keyrings, Netdev,
linux-nfs, CIFS, linux-afs, linux-fsdevel, linux-integrity,
LSM List, Linux List Kernel Mailing
In-Reply-To: <1562814435.4014.11.camel@linux.ibm.com>
Mimi Zohar <zohar@linux.ibm.com> wrote:
> Sorry for the delay. An exception is needed for loading builtin keys
> "KEY_ALLOC_BUILT_IN" onto a keyring that is not writable by userspace.
> The following works, but probably is not how David would handle the
> exception.
I think the attached is the right way to fix it.
load_system_certificate_list(), for example, when it creates keys does this:
key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1),
marking the keyring as "possessed" in make_key_ref(). This allows the
possessor permits to be used - and that's the *only* way to use them for
internal keyrings like this because you can't link to them and you can't join
them.
David
---
diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 57be78b5fdfc..1f8f26f7bb05 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -99,7 +99,7 @@ static __init int system_trusted_keyring_init(void)
builtin_trusted_keys =
keyring_alloc(".builtin_trusted_keys",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
- &internal_key_acl, KEY_ALLOC_NOT_IN_QUOTA,
+ &internal_keyring_acl, KEY_ALLOC_NOT_IN_QUOTA,
NULL, NULL);
if (IS_ERR(builtin_trusted_keys))
panic("Can't allocate builtin trusted keyring\n");
diff --git a/security/keys/permission.c b/security/keys/permission.c
index fc84d9ef6239..86efd3eaf083 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -47,7 +47,7 @@ struct key_acl internal_keyring_acl = {
.usage = REFCOUNT_INIT(1),
.nr_ace = 2,
.aces = {
- KEY_POSSESSOR_ACE(KEY_ACE_SEARCH),
+ KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_SEARCH),
}
};
^ permalink raw reply related
* Re: [RFC PATCH v3] security, capability: pass object information to security_capable
From: Stephen Smalley @ 2019-08-16 14:57 UTC (permalink / raw)
To: James Morris, Aaron Goidel
Cc: paul, rgb, mortonm, john.johansen, selinux, luto,
linux-security-module, linux-audit, nhfran2, serge
In-Reply-To: <alpine.LRH.2.21.1908160817300.22623@namei.org>
On 8/15/19 6:32 PM, James Morris wrote:
> On Thu, 15 Aug 2019, Aaron Goidel wrote:
>
>> In SELinux this new information is leveraged here to perform an
>> additional inode based check for capabilities relevant to inodes. Since
>> the inode provided to capable_wrt_inode_uidgid() is a const argument,
>> this also required propagating const down to dump_common_audit_data() and
>> dropping the use of d_find_alias() to find an alias for the inode. This
>> was sketchy to begin with and should be obsoleted by a separate change
>> that will allow LSMs to trigger audit collection for all file-related
>> information.
>
> Will the audit logs look the same once the 2nd patch is applied? We need
> to be careful about breaking existing userland.
It was already the case that the name= field in the AVC audit record was
not guaranteed to be emitted in this case, since d_find_alias could
return NULL. And it was only a hint, since that name might have nothing
to do with the name used to look up the inode in the first place. So I
don't believe userland could have ever relied upon it being present
here. Removing it also fixes a problem with AVC audit generation under
RCU walk; we should be able to drop the code that skips audit generation
in that case with this d_find_alias call gone IIUC.
With the ability for an LSM to enable collection and generation of
AUDIT_PATH and other AUDIT_* records (which is made possible via the
other patch), we will get more complete and relevant information in the
audit log. It won't look exactly the same (there will be separate AVC,
PATH, ... records that can be correlated based on timestamp/serial and
ausearch does this automatically for you).
^ permalink raw reply
* Re: [Non-DoD Source] Re: [RFC PATCH v2] security, capability: pass object information to security_capable
From: Paul Moore @ 2019-08-16 16:29 UTC (permalink / raw)
To: Aaron Goidel
Cc: Stephen Smalley, Richard Guy Briggs, mortonm, john.johansen,
selinux, James Morris, luto, linux-security-module, linux-audit,
Nicholas Franck, Serge Hallyn
In-Reply-To: <cb2833ee-3a12-9c7d-6c5b-c7944e74b3e9@tycho.nsa.gov>
On Thu, Aug 15, 2019 at 9:11 AM Aaron Goidel <acgoide@tycho.nsa.gov> wrote:
> I'm looking at how to enable LSMs to selectively turn on audit
> collection. So there seems to be two key points: audit_alloc() and
> __audit_syscall_entry(). Would it suffice to define a single boolean
> hook that takes the task and call it from both functions, to decide
> whether to override an AUDIT_DISABLED state in audit_alloc() and to
> override a 0 audit_n_rules in __audit_syscall_entry(). In audit_alloc()
> if audit_filter_task() returned AUDIT_DISABLED and the hook returned
> true, we would change the state to AUDIT_BUILD_CONTEXT. In
> __audit_syscall_entry(), if the hook returned true, we would set dummy
> to 0. Obviously, we could have a more general hook which lets us return
> arbitrary audit states, but, it isn't clear how we would reconcile
> conflicting results from audit_filter_task() and the hook for any
> situation other than AUDIT_DISABLED. We could also potentially use a
> different hook in __audit_syscall_entry(), though I don't think that we
> want the LSMs trying to interpret the syscall number or arguments.
>
> Do you think that is sufficiently general or would you suggest something
> different?
FWIW, I think treating the per-task audit switch as a boolean is fine;
I don't think we want other in-kernel callers to have to worry about
the different audit states. From their perspective it is either "on"
or "off".
However, I think there are two parts of the greater LSM-enables-audit
discussion, and we're only discussing the first part: collection. The
second part is the actual audit record generation, and I think this
part is going to be less clear. While the changes to audit_alloc(),
etc. are necessary to be able to do any meaningful audit later on, I'm
thinking introducing some granularity and LSM control to what gets
generated in audit_log_exit() might be very welcome both from a
performance and log cleanliness perspective.
Some random thoughts on this (some may be way off, but I want to start
with some expectations):
* The LSM should never be able to block collection/generation of audit
records, just enable additional records.
* The LSM controls should only affect what we call the "syscall
auditing" bits, e.g. the stuff in auditsc.c. Audit records that
happen outside of this should be untouched, the AVC records are an
example of a record that exists independent of syscall auditing.
* We should be able to have the LSM set a per-syscall audit enable
flag which would be checked in audit_log_exit() (or
audit_free()/__audit_syscall_exit()).
* It's not clear to me if we want to provide some granularity to the
LSM regarding what records get generated in audit_log_exit(), for
example do we allow the LSM to request just PATH records? I'm
guessing we wouldn't want to specify record types directly, but
perhaps record "classes", e.g. "file".
I'm not sure if any of this is going to be a good idea, but I think we
need to discuss it a bit before we start duplicating things in
lsm_audit.c.
--
paul moore
www.paul-moore.com
^ permalink raw reply
* Re: [RFC PATCH v3] security, capability: pass object information to security_capable
From: Paul Moore @ 2019-08-16 16:36 UTC (permalink / raw)
To: Stephen Smalley
Cc: James Morris, Aaron Goidel, rgb, mortonm, john.johansen, selinux,
luto, linux-security-module, linux-audit, nhfran2, Serge Hallyn
In-Reply-To: <cebacde0-5c53-c414-8f27-8d81ed928dfd@tycho.nsa.gov>
On Fri, Aug 16, 2019 at 10:57 AM Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On 8/15/19 6:32 PM, James Morris wrote:
> > On Thu, 15 Aug 2019, Aaron Goidel wrote:
> >
> >> In SELinux this new information is leveraged here to perform an
> >> additional inode based check for capabilities relevant to inodes. Since
> >> the inode provided to capable_wrt_inode_uidgid() is a const argument,
> >> this also required propagating const down to dump_common_audit_data() and
> >> dropping the use of d_find_alias() to find an alias for the inode. This
> >> was sketchy to begin with and should be obsoleted by a separate change
> >> that will allow LSMs to trigger audit collection for all file-related
> >> information.
> >
> > Will the audit logs look the same once the 2nd patch is applied? We need
> > to be careful about breaking existing userland.
>
> It was already the case that the name= field in the AVC audit record was
> not guaranteed to be emitted in this case, since d_find_alias could
> return NULL. And it was only a hint, since that name might have nothing
> to do with the name used to look up the inode in the first place. So I
> don't believe userland could have ever relied upon it being present
> here. Removing it also fixes a problem with AVC audit generation under
> RCU walk; we should be able to drop the code that skips audit generation
> in that case with this d_find_alias call gone IIUC.
>
> With the ability for an LSM to enable collection and generation of
> AUDIT_PATH and other AUDIT_* records (which is made possible via the
> other patch), we will get more complete and relevant information in the
> audit log. It won't look exactly the same (there will be separate AVC,
> PATH, ... records that can be correlated based on timestamp/serial and
> ausearch does this automatically for you).
Regardless of if it is The Right Thing, changes like this should
probably be put into a separate, unrelated patch.
I think there are a few things in dump_common_audit_data() that should
have been done differently, but unfortunately the audit records (and
IMHO the many stupid design decisions that went into them) are
effectively part of the kernel API and need to be treated with care.
--
paul moore
www.paul-moore.com
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Alexei Starovoitov @ 2019-08-16 19:52 UTC (permalink / raw)
To: Jordan Glover
Cc: Thomas Gleixner, Andy Lutomirski, Daniel Colascione, Song Liu,
Kees Cook, Networking, bpf, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Lorenz Bauer, Jann Horn, Greg KH, Linux API,
LSM List
In-Reply-To: <lGGTLXBsX3V6p1Z4TkdzAjxbNywaPS2HwX5WLleAkmXNcnKjTPpWnP6DnceSsy8NKt5NBRBbuoAb0woKTcDhJXVoFb7Ygk3Skfj8j6rVfMQ=@protonmail.ch>
On Fri, Aug 16, 2019 at 11:33:57AM +0000, Jordan Glover wrote:
> On Friday, August 16, 2019 9:59 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
>
> > On Fri, 16 Aug 2019, Jordan Glover wrote:
> >
> > > "systemd --user" service? Trying to do so will fail with:
> > > "Failed to apply ambient capabilities (before UID change): Operation not permitted"
> > > I think it's crucial to clear that point to avoid confusion in this discussion
> > > where people are talking about different things.
> > > On the other hand running "systemd --system" service with:
> > > User=nobody
> > > AmbientCapabilities=CAP_NET_ADMIN
> > > is perfectly legit and clears some security concerns as only privileged user
> > > can start such service.
> >
> > While we are at it, can we please stop looking at this from a systemd only
> > perspective. There is a world outside of systemd.
> >
> > Thanks,
> >
> > tglx
>
> If you define:
>
> "systemd --user" == unprivileged process started by unprivileged user
> "systemd --system" == process started by privileged user but run as another
> user which keeps some of parent user privileges and drops others
>
> you can get rid of "systemd" from the equation.
>
> "systemd --user" was the example provided by Alexei when asked about the usecase
> but his description didn't match what it does so it's not obvious what the real
> usecase is. I'm sure there can be many more examples and systemd isn't important
> here in particular beside to understand this specific example.
It's both of the above when 'systemd' is not taken literally.
To earlier Thomas's point: the use case is not only about systemd.
There are other containers management systems.
I've used 'systemd-like' terminology as an attempt to explain that such
daemons are trusted signed binaries that can be run as pid=1.
Sometimes it's the later:
"process started by privileged user but run as another user which keeps
some of parent user privileges and drops others".
Sometimes capability delegation to another container management daemon
is too cumbersome, so it's easier to use suid bit on that other daemon.
So it will become like the former:
"sort-of unprivileged process started by unprivileged user."
where daemon has suid and drops most of the capabilities as it starts.
Let's not focus on the model being good or bad security wise.
The point that those are the use cases that folks are thinking about.
That secondary daemon can be full root just fine.
All outer and inner daemons can be root.
These daemons need to drop privileges to make the system safer ==
less prone to corruption due to bugs in themselves. Not necessary security bugs.
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Thomas Gleixner @ 2019-08-16 20:28 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Jordan Glover, Andy Lutomirski, Daniel Colascione, Song Liu,
Kees Cook, Networking, bpf, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Lorenz Bauer, Jann Horn, Greg KH, Linux API,
LSM List
In-Reply-To: <20190816195233.vzqqbqrivnooohq6@ast-mbp.dhcp.thefacebook.com>
Alexei,
On Fri, 16 Aug 2019, Alexei Starovoitov wrote:
> It's both of the above when 'systemd' is not taken literally.
> To earlier Thomas's point: the use case is not only about systemd.
> There are other containers management systems.
<SNIP>
> These daemons need to drop privileges to make the system safer == less
> prone to corruption due to bugs in themselves. Not necessary security
> bugs.
Let's take a step back.
While real usecases are helpful to understand a design decision, the design
needs to be usecase independent.
The kernel provides mechanisms, not policies. My impression of this whole
discussion is that it is policy driven. That's the wrong approach.
So let's look at the mechanisms which we have at hand:
1) Capabilities
2) SUID and dropping priviledges
3) Seccomp and LSM
Now the real interesting questions are:
A) What kind of restrictions does BPF allow? Is it a binary on/off or is
there a more finegrained control of BPF functionality?
TBH, I can't tell.
B) Depending on the answer to #A what is the control possibility for
#1/#2/#3 ?
Answering those questions gives us a real scope of what can be achieved
independent of use cases and wishful thought out policies.
Thanks,
tglx
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Alexei Starovoitov @ 2019-08-16 21:45 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Kees Cook, Andy Lutomirski, Song Liu, Networking, bpf,
Alexei Starovoitov, Daniel Borkmann, Kernel Team, Lorenz Bauer,
Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <B0364660-AD6A-4E5C-B04F-3B6DA78B4BBE@amacapital.net>
On Thu, Aug 15, 2019 at 05:54:59PM -0700, Andy Lutomirski wrote:
>
>
> > On Aug 15, 2019, at 4:46 PM, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
>
>
> >>
> >> I'm not sure why you draw the line for VMs -- they're just as buggy
> >> as anything else. Regardless, I reject this line of thinking: yes,
> >> all software is buggy, but that isn't a reason to give up.
> >
> > hmm. are you saying you want kernel community to work towards
> > making containers (namespaces) being able to run arbitrary code
> > downloaded from the internet?
>
> Yes.
>
> As an example, Sandstorm uses a combination of namespaces (user, network, mount, ipc) and a moderately permissive seccomp policy to run arbitrary code. Not just little snippets, either — node.js, Mongo, MySQL, Meteor, and other fairly heavyweight stacks can all run under Sandstorm, with the whole stack (database engine binaries, etc) supplied by entirely untrusted customers. During the time Sandstorm was under active development, I can recall *one* bug that would have allowed a sandbox escape. That’s a pretty good track record. (Also, Meltdown and Spectre, sigh.)
exactly: "meltdown", "spectre", "sigh".
Side channels effectively stalled the work on secure containers.
And killed projects like sandstorm.
Why work on improving kaslr when there are several ways to
get kernel addresses through hw bugs? Patch mouse holes when roof is leaking ?
In case of unprivileged bpf I'm confident that all known holes are patched.
Until disclosures stop happening with the frequency they do now the time
of bpf developers is better spent on something other than unprivileged bpf.
> I’m suggesting that you engage the security community ...
> .. so that normal users can use bpf filtering
yes, but not soon. unfortunately.
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Christian Brauner @ 2019-08-16 22:22 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Andy Lutomirski, Kees Cook, Andy Lutomirski, Song Liu, Networking,
bpf, Alexei Starovoitov, Daniel Borkmann, Kernel Team,
Lorenz Bauer, Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <20190816214542.inpt6p655whc2ejw@ast-mbp.dhcp.thefacebook.com>
On Fri, Aug 16, 2019 at 02:45:44PM -0700, Alexei Starovoitov wrote:
> On Thu, Aug 15, 2019 at 05:54:59PM -0700, Andy Lutomirski wrote:
> >
> >
> > > On Aug 15, 2019, at 4:46 PM, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
> >
> >
> > >>
> > >> I'm not sure why you draw the line for VMs -- they're just as buggy
> > >> as anything else. Regardless, I reject this line of thinking: yes,
> > >> all software is buggy, but that isn't a reason to give up.
> > >
> > > hmm. are you saying you want kernel community to work towards
> > > making containers (namespaces) being able to run arbitrary code
> > > downloaded from the internet?
> >
> > Yes.
If I may weigh in here too: Yes. In fact, we already do that large
scale!
> >
> > As an example, Sandstorm uses a combination of namespaces (user, network, mount, ipc) and a moderately permissive seccomp policy to run arbitrary code. Not just little snippets, either — node.js, Mongo, MySQL, Meteor, and other fairly heavyweight stacks can all run under Sandstorm, with the whole stack (database engine binaries, etc) supplied by entirely untrusted customers. During the time Sandstorm was under active development, I can recall *one* bug that would have allowed a sandbox escape. That’s a pretty good track record. (Also, Meltdown and Spectre, sigh.)
>
> exactly: "meltdown", "spectre", "sigh".
> Side channels effectively stalled the work on secure containers.
> And killed projects like sandstorm.
If I may, Sandstorm's death has very likely nothing to do with
Meltdown/Spectre etc. since that should've also killed Qemu, Crosvm and
all the others in one fell swoop. It's also not a very good example (no
offense, Andy :)) and probably a bit dated.
We have LXD which is a full-fledged container manager that runs
*unprivileged system* containers on a large scale and is very much
alive. That is it runs systemd, openrc, what have you, i.e. simply
unmodifed distro images at this point.
It's used to run Linux workloads on all Chromebooks and in a bunch of
other places. Since its inception we did not have a single
*unprivileged* container to init userns/host breakout.
At this point in time the really bad CVEs are almost exclusively against
*privileged* containers (see this year's leading nomination for
container CVE grand mal of the year: CVE-2019-5736) which were never and
will never be considered root safe despite everyone pretending
otherwise.
> Why work on improving kaslr when there are several ways to
> get kernel addresses through hw bugs? Patch mouse holes when roof is leaking ?
> In case of unprivileged bpf I'm confident that all known holes are patched.
> Until disclosures stop happening with the frequency they do now the time
> of bpf developers is better spent on something other than unprivileged bpf.
>
> > I’m suggesting that you engage the security community ...
> > .. so that normal users can use bpf filtering
>
> yes, but not soon. unfortunately.
Tbh, I do not have a strong opinion on unprivileged bpf at this moment.
If the community thinks that the bits and pieces are not in place or the
timing is not right that's fine with me.
What we should make sure though is that we don't end up with something
halfbaked. And this /dev/bpf thing very much feels like a hack. If
unprivileged bpf is not a thing why bother with /dev/bpf or CAP_BPF at
all.
(The one usecase I'd care about is to extend seccomp to do pointer-based
syscall filtering. Whether or not that'd require (unprivileged) ebpf is
up for discussion at KSummit.)
Christian
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Alexei Starovoitov @ 2019-08-17 15:02 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Jordan Glover, Andy Lutomirski, Daniel Colascione, Song Liu,
Kees Cook, Networking, bpf, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Lorenz Bauer, Jann Horn, Greg KH, Linux API,
LSM List
In-Reply-To: <alpine.DEB.2.21.1908162211270.1923@nanos.tec.linutronix.de>
On Fri, Aug 16, 2019 at 10:28:29PM +0200, Thomas Gleixner wrote:
> Alexei,
>
> On Fri, 16 Aug 2019, Alexei Starovoitov wrote:
> > It's both of the above when 'systemd' is not taken literally.
> > To earlier Thomas's point: the use case is not only about systemd.
> > There are other containers management systems.
>
> <SNIP>
>
> > These daemons need to drop privileges to make the system safer == less
> > prone to corruption due to bugs in themselves. Not necessary security
> > bugs.
>
> Let's take a step back.
>
> While real usecases are helpful to understand a design decision, the design
> needs to be usecase independent.
>
> The kernel provides mechanisms, not policies. My impression of this whole
> discussion is that it is policy driven. That's the wrong approach.
not sure what you mean by 'policy driven'.
Proposed CAP_BPF is a policy?
My desire to do kernel.unprivileged_bpf_disabled=1 is driven by
text in Documentation/x86/mds.rst which says:
"There is one exception, which is untrusted BPF. The functionality of
untrusted BPF is limited, but it needs to be thoroughly investigated
whether it can be used to create such a construct."
commit 6a9e52927251 ("x86/speculation/mds: Add mds_clear_cpu_buffers()")
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Reviewed-by: Jon Masters <jcm@redhat.com>
Tested-by: Jon Masters <jcm@redhat.com>
The way I read this text:
- there is a concern that mds is exploitable via bpf
- there is a desire to investigate to address this concern
I'm committed to help with the investigation.
In the mean time I propose a path to do
kernel.unprivileged_bpf_disabled=1 which is CAP_BPF.
Can kernel.unprivileged_bpf_disabled=1 be used now?
Yes, but it will weaken overall system security because things that
use unpriv to load bpf and CAP_NET_ADMIN to attach bpf would need
to move to stronger CAP_SYS_ADMIN.
With CAP_BPF both load and attach would happen under CAP_BPF
instead of CAP_SYS_ADMIN.
> So let's look at the mechanisms which we have at hand:
>
> 1) Capabilities
>
> 2) SUID and dropping priviledges
>
> 3) Seccomp and LSM
>
> Now the real interesting questions are:
>
> A) What kind of restrictions does BPF allow? Is it a binary on/off or is
> there a more finegrained control of BPF functionality?
>
> TBH, I can't tell.
>
> B) Depending on the answer to #A what is the control possibility for
> #1/#2/#3 ?
Can any of the mechanisms 1/2/3 address the concern in mds.rst?
I believe Andy wants to expand the attack surface when
kernel.unprivileged_bpf_disabled=0
Before that happens I'd like the community to work on addressing the text above.
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Alexei Starovoitov @ 2019-08-17 15:08 UTC (permalink / raw)
To: Christian Brauner
Cc: Andy Lutomirski, Kees Cook, Andy Lutomirski, Song Liu, Networking,
bpf, Alexei Starovoitov, Daniel Borkmann, Kernel Team,
Lorenz Bauer, Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <20190816222252.a7zizw7azkxnv3ot@wittgenstein>
On Sat, Aug 17, 2019 at 12:22:53AM +0200, Christian Brauner wrote:
>
> (The one usecase I'd care about is to extend seccomp to do pointer-based
> syscall filtering. Whether or not that'd require (unprivileged) ebpf is
> up for discussion at KSummit.)
Kees have been always against using ebpf in seccomp. I believe he still
holds this opinion. Until he changes his mind let's stop bringing seccomp
as a use case for unpriv bpf.
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Christian Brauner @ 2019-08-17 15:16 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Andy Lutomirski, Kees Cook, Andy Lutomirski, Song Liu, Networking,
bpf, Alexei Starovoitov, Daniel Borkmann, Kernel Team,
Lorenz Bauer, Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <20190817150843.4vsmzpwpcvzndjld@ast-mbp>
On August 17, 2019 5:08:45 PM GMT+02:00, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
>On Sat, Aug 17, 2019 at 12:22:53AM +0200, Christian Brauner wrote:
>>
>> (The one usecase I'd care about is to extend seccomp to do
>pointer-based
>> syscall filtering. Whether or not that'd require (unprivileged) ebpf
>is
>> up for discussion at KSummit.)
>
>Kees have been always against using ebpf in seccomp. I believe he still
>holds this opinion. Until he changes his mind let's stop bringing
>seccomp
>as a use case for unpriv bpf.
That's why I said "whether or not".
For the record, I do prefer a non-unpriv-ebpf way.
It's still something that will most surely come up in the discussion though.
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Alexei Starovoitov @ 2019-08-17 15:36 UTC (permalink / raw)
To: Christian Brauner
Cc: Andy Lutomirski, Kees Cook, Andy Lutomirski, Song Liu, Networking,
bpf, Alexei Starovoitov, Daniel Borkmann, Kernel Team,
Lorenz Bauer, Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <61B88085-9FBB-41E6-9783-324E445E428D@ubuntu.com>
On Sat, Aug 17, 2019 at 05:16:53PM +0200, Christian Brauner wrote:
> On August 17, 2019 5:08:45 PM GMT+02:00, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
> >On Sat, Aug 17, 2019 at 12:22:53AM +0200, Christian Brauner wrote:
> >>
> >> (The one usecase I'd care about is to extend seccomp to do
> >pointer-based
> >> syscall filtering. Whether or not that'd require (unprivileged) ebpf
> >is
> >> up for discussion at KSummit.)
> >
> >Kees have been always against using ebpf in seccomp. I believe he still
> >holds this opinion. Until he changes his mind let's stop bringing
> >seccomp
> >as a use case for unpriv bpf.
>
> That's why I said "whether or not".
> For the record, I do prefer a non-unpriv-ebpf way.
> It's still something that will most surely come up in the discussion though.
It's very un-kernely way to defer to in-person meetings.
If there is anything to discuss please discuss it on the public mailing list.
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Christian Brauner @ 2019-08-17 15:42 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Andy Lutomirski, Kees Cook, Andy Lutomirski, Song Liu, Networking,
bpf, Alexei Starovoitov, Daniel Borkmann, Kernel Team,
Lorenz Bauer, Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <20190817153652.zfcsklt474j72dzm@ast-mbp.dhcp.thefacebook.com>
On August 17, 2019 5:36:54 PM GMT+02:00, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
>On Sat, Aug 17, 2019 at 05:16:53PM +0200, Christian Brauner wrote:
>> On August 17, 2019 5:08:45 PM GMT+02:00, Alexei Starovoitov
><alexei.starovoitov@gmail.com> wrote:
>> >On Sat, Aug 17, 2019 at 12:22:53AM +0200, Christian Brauner wrote:
>> >>
>> >> (The one usecase I'd care about is to extend seccomp to do
>> >pointer-based
>> >> syscall filtering. Whether or not that'd require (unprivileged)
>ebpf
>> >is
>> >> up for discussion at KSummit.)
>> >
>> >Kees have been always against using ebpf in seccomp. I believe he
>still
>> >holds this opinion. Until he changes his mind let's stop bringing
>> >seccomp
>> >as a use case for unpriv bpf.
>>
>> That's why I said "whether or not".
>> For the record, I do prefer a non-unpriv-ebpf way.
>> It's still something that will most surely come up in the discussion
>though.
>
>It's very un-kernely way to defer to in-person meetings.
>If there is anything to discuss please discuss it on the public mailing
>list.
https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2019-July/006699.html
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Andy Lutomirski @ 2019-08-17 15:44 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Thomas Gleixner, Jordan Glover, Andy Lutomirski,
Daniel Colascione, Song Liu, Kees Cook, Networking, bpf,
Alexei Starovoitov, Daniel Borkmann, Kernel Team, Lorenz Bauer,
Jann Horn, Greg KH, Linux API, LSM List
In-Reply-To: <20190817150245.xxzxqjpvgqsxmloe@ast-mbp>
> On Aug 17, 2019, at 8:02 AM, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
>
> Can any of the mechanisms 1/2/3 address the concern in mds.rst?
>
seccomp() can. It’s straightforward to use seccomp to disable bpf() outright for a process tree. In this regard, bpf() isn’t particularly unique — it’s a system call that exposes some attack surface and that isn’t required by most programs for basic functionality.
At LPC this year, there will be a discussion about seccomp improvements that will, among other things, offer fiber-grained control. It’s quite likely, for example, that seccomp will soon be able to enable and disable specific map types or attach types. The exact mechanism isn’t decided yet, but I think everyone expects that this is mostly a design problem, not an implementation problem.
This is off topic for the current thread, but it could be useful to allow bpf programs to be loaded from files directly (i.e. pass an fd to a file into bpf() to load the program), which would enable LSMs to check that the file is appropriately labeled. This would dramatically raise the bar for exploitation of verifier bugs or speculation attacks, since anyone trying to exploit it would need to get the bpf payload through LSM policy first.
> I believe Andy wants to expand the attack surface when
> kernel.unprivileged_bpf_disabled=0
> Before that happens I'd like the community to work on addressing the text above.
>
Not by much. BPF maps are already largely exposed to unprivileged code (when unprivileged_bpf_disabled=0). The attack surface is there, and they’re arguably even more exposed than they should be. My patch 1 earlier was about locking these interfaces down.
Similarly, my suggestions about reworking cgroup attach and program load don’t actually allow fully unprivileged users to run arbitrary bpf() programs [0] — under my proposal, to attach a bpf cgroup program, you need a delegated cgroup. The mechanism could be extended by a requirement that a privileged cgroup manager explicitly enable certain attach types for a delegated subtree.
A cgroup knob to turn unprivileged bpf on and off for tasks in the cgroup might actually be quite useful.
[0] on some thought, the test run mechanism should probably remain root-only.
^ permalink raw reply
* [WIP][RFC][PATCH 0/3] Introduce Infoflow LSM
From: Roberto Sassu @ 2019-08-18 23:57 UTC (permalink / raw)
To: linux-integrity
Cc: linux-security-module, zohar, dmitry.kasatkin, silviu.vlasceanu,
Roberto Sassu
This patch set introduces a new security module called Infoflow LSM. Its
main purpose is to enforce the Clark-Wilson integrity policy, in order
to protect mutable files against modifications from processes outside the
Trusted Computing Base (TCB). With this protection, mutable files inside
the TCB can be safely excluded from measurement by Integrity Measurement
Architecture (IMA), and their unknown digest won't cause a failure during
the remote attestation process.
Infoflow LSM takes advantage of the LSM stacking capability and enforces
security decisions on top of other label-based LSMs such as SELinux and
SMACK. The main benefit of this design choice is that it is not necessary
to modify the policy of the existing LSMs.
Infoflow LSM has three main modes of operation:
- discover: discovers process operations based on which the information
flow analysis can be performed and the TCB can be determined;
- enforce: enforce the Clark-Wilson policy, depending on the TCB previously
determined
- permissive: allow operations that would be denied, but show them in a
file in securityfs
Discovered operations can be obtained from
/sys/kernel/security/infoflow/rules and are in the format:
allow subj obj:class { permissions };
The TCB can be determined from discovered operations with the RA Verifier
tool available at:
https://github.com/euleros/ra-verifier
The tool takes as input the application that must be protected or the
initial TCB. It will then tells which subjects violate the Clark-Wilson
policy. Possible resolution strategies are to add a subject to the TCB or
to add a filtering interface to the TCB subject that reads a low integrity
object.
RA Verifier produces a policy for Infoflow LSM, with the list of TCB
subjects, objects and filtering interfaces.
This patch set can be retrieved at:
https://github.com/euleros/linux
Roberto
Roberto Sassu (3):
security: introduce call_int_hook_and() macro
lsm notifier: distinguish between state change and policy change
security: add infoflow LSM
.../admin-guide/kernel-parameters.txt | 23 +
drivers/infiniband/core/device.c | 2 +-
include/linux/lsm_audit.h | 3 +
include/linux/security.h | 1 +
include/uapi/linux/xattr.h | 2 +
security/Kconfig | 1 +
security/Makefile | 2 +
security/infoflow/Kconfig | 6 +
security/infoflow/Makefile | 7 +
security/infoflow/infoflow.h | 173 ++++
security/infoflow/infoflow_access.c | 182 ++++
security/infoflow/infoflow_ctx.c | 342 ++++++++
security/infoflow/infoflow_fs.c | 479 +++++++++++
security/infoflow/infoflow_lsm.c | 778 ++++++++++++++++++
security/integrity/evm/evm_main.c | 1 +
security/security.c | 19 +-
security/selinux/avc.c | 2 +-
security/selinux/selinuxfs.c | 2 +-
18 files changed, 2020 insertions(+), 5 deletions(-)
create mode 100644 security/infoflow/Kconfig
create mode 100644 security/infoflow/Makefile
create mode 100644 security/infoflow/infoflow.h
create mode 100644 security/infoflow/infoflow_access.c
create mode 100644 security/infoflow/infoflow_ctx.c
create mode 100644 security/infoflow/infoflow_fs.c
create mode 100644 security/infoflow/infoflow_lsm.c
--
2.17.1
^ permalink raw reply
* [WIP][RFC][PATCH 1/3] security: introduce call_int_hook_and() macro
From: Roberto Sassu @ 2019-08-18 23:57 UTC (permalink / raw)
To: linux-integrity
Cc: linux-security-module, zohar, dmitry.kasatkin, silviu.vlasceanu,
Roberto Sassu
In-Reply-To: <20190818235745.1417-1-roberto.sassu@huawei.com>
The LSM hooks audit_rule_known() and audit_rule_match() define 1 as
result for successful operation. However, the security_ functions use
call_int_hook() which stops iterating over LSMs if the result is not
zero.
Introduce call_int_hook_and(), so that the final result returned by the
security_ functions is 1 if all LSMs return 1.
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
security/security.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/security/security.c b/security/security.c
index cbee0b7915d5..ff1736ee3410 100644
--- a/security/security.c
+++ b/security/security.c
@@ -634,6 +634,20 @@ static void __init lsm_early_task(struct task_struct *task)
RC; \
})
+#define call_int_hook_and(FUNC, IRC, ...) ({ \
+ int RC = IRC; \
+ do { \
+ struct security_hook_list *P; \
+ \
+ hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
+ RC = P->hook.FUNC(__VA_ARGS__); \
+ if (!RC) \
+ break; \
+ } \
+ } while (0); \
+ RC; \
+})
+
/* Security operations */
int security_binder_set_context_mgr(struct task_struct *mgr)
@@ -2339,7 +2353,7 @@ int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
int security_audit_rule_known(struct audit_krule *krule)
{
- return call_int_hook(audit_rule_known, 0, krule);
+ return call_int_hook_and(audit_rule_known, 0, krule);
}
void security_audit_rule_free(void *lsmrule)
@@ -2349,7 +2363,8 @@ void security_audit_rule_free(void *lsmrule)
int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
{
- return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule);
+ return call_int_hook_and(audit_rule_match, 0, secid, field, op,
+ lsmrule);
}
#endif /* CONFIG_AUDIT */
--
2.17.1
^ permalink raw reply related
* [WIP][RFC][PATCH 2/3] lsm notifier: distinguish between state change and policy change
From: Roberto Sassu @ 2019-08-18 23:57 UTC (permalink / raw)
To: linux-integrity
Cc: linux-security-module, zohar, dmitry.kasatkin, silviu.vlasceanu,
Roberto Sassu
In-Reply-To: <20190818235745.1417-1-roberto.sassu@huawei.com>
This patch introduces a new event type called LSM_STATE_CHANGE to
distinguish between state change and policy change.
The purpose of this patch is to let upper LSMs know when they can get the
label assigned by the lower LSMs (e.g. SELinux) with
security_secid_to_secctx().
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
drivers/infiniband/core/device.c | 2 +-
include/linux/security.h | 1 +
security/selinux/avc.c | 2 +-
security/selinux/selinuxfs.c | 2 +-
4 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 187d7820cfaf..743a51fd775a 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -824,7 +824,7 @@ static void ib_policy_change_task(struct work_struct *work)
static int ib_security_change(struct notifier_block *nb, unsigned long event,
void *lsm_data)
{
- if (event != LSM_POLICY_CHANGE)
+ if (event != LSM_POLICY_CHANGE && event != LSM_STATE_CHANGE)
return NOTIFY_DONE;
schedule_work(&ib_policy_change_work);
diff --git a/include/linux/security.h b/include/linux/security.h
index 5f7441abbf42..f868193e0115 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -75,6 +75,7 @@ struct timezone;
enum lsm_event {
LSM_POLICY_CHANGE,
+ LSM_STATE_CHANGE,
};
/* These functions are in security/commoncap.c */
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 8346a4f7c5d7..3af9c6ebe580 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -982,7 +982,7 @@ int avc_ss_reset(struct selinux_avc *avc, u32 seqno)
avc_flush(avc);
for (c = avc_callbacks; c; c = c->next) {
- if (c->events & AVC_CALLBACK_RESET) {
+ if (c->events & AVC_CALLBACK_RESET && seqno) {
tmprc = c->callback(AVC_CALLBACK_RESET);
/* save the first error encountered for the return
value and continue processing the callbacks */
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 6f195c7915de..76c34261e740 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -178,7 +178,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
selnl_notify_setenforce(new_value);
selinux_status_update_setenforce(state, new_value);
if (!new_value)
- call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
+ call_blocking_lsm_notifier(LSM_STATE_CHANGE, NULL);
}
length = count;
out:
--
2.17.1
^ permalink raw reply related
* [WIP][RFC][PATCH 3/3] security: add infoflow LSM
From: Roberto Sassu @ 2019-08-18 23:57 UTC (permalink / raw)
To: linux-integrity
Cc: linux-security-module, zohar, dmitry.kasatkin, silviu.vlasceanu,
Roberto Sassu
In-Reply-To: <20190818235745.1417-1-roberto.sassu@huawei.com>
Add a new security module called Infoflow LSM to enforce the Clark-Wilson
integrity policy on top of existing label-based LSMs, such as SELinux and
SMACK.
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
.../admin-guide/kernel-parameters.txt | 23 +
include/linux/lsm_audit.h | 3 +
include/uapi/linux/xattr.h | 2 +
security/Kconfig | 1 +
security/Makefile | 2 +
security/infoflow/Kconfig | 6 +
security/infoflow/Makefile | 7 +
security/infoflow/infoflow.h | 173 ++++
security/infoflow/infoflow_access.c | 182 ++++
security/infoflow/infoflow_ctx.c | 342 ++++++++
security/infoflow/infoflow_fs.c | 479 +++++++++++
security/infoflow/infoflow_lsm.c | 778 ++++++++++++++++++
security/integrity/evm/evm_main.c | 1 +
13 files changed, 1999 insertions(+)
create mode 100644 security/infoflow/Kconfig
create mode 100644 security/infoflow/Makefile
create mode 100644 security/infoflow/infoflow.h
create mode 100644 security/infoflow/infoflow_access.c
create mode 100644 security/infoflow/infoflow_ctx.c
create mode 100644 security/infoflow/infoflow_fs.c
create mode 100644 security/infoflow/infoflow_lsm.c
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 47311cdf63d9..011b092f667a 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1673,6 +1673,29 @@
different crypto accelerators. This option can be used
to achieve best performance for particular HW.
+ infoflow= [INFOFLOW] Disable or enable Infoflow LSM at boot time.
+ Format: { "0" | "1" }
+ 0 -- disable.
+ 1 -- enable.
+
+ infoflow_mode= [INFOFLOW] Set Infoflow LSM mode.
+ Format: { "disabled" | "discover" | "enforce" |
+ "enforce-audit" };
+ disabled -- LSM disabled.
+ discover -- record operations and labels set by
+ a parent LSM (e.g. SELinux, SMACK).
+ enforce -- enforce Clark-Wilson integrity policy.
+ enforce-audit -- enforce Clark-Wilson integrity policy
+ and record denied operations.
+ permissive -- check Clark-Wilson integrity policy
+ but don't enforce decision.
+ permissive-audit -- check Clark-Wilson integrity policy
+ and record decision.
+
+ infoflow_promote [INFOFLOW] Promote low integrity objects that would
+ cause a read-down violation of the
+ Clark-Wilson integrity policy.
+
init= [KNL]
Format: <full_path>
Run specified binary instead of /sbin/init as init
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 915330abf6e5..a955087c2283 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -104,6 +104,9 @@ struct common_audit_data {
#endif
#ifdef CONFIG_SECURITY_APPARMOR
struct apparmor_audit_data *apparmor_audit_data;
+#endif
+#ifdef CONFIG_SECURITY_INFOFLOW
+ struct infoflow_audit_data *infoflow_audit_data;
#endif
}; /* per LSM data pointer union */
};
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index c1395b5bd432..ccdacb5f6e2c 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -77,5 +77,7 @@
#define XATTR_POSIX_ACL_DEFAULT "posix_acl_default"
#define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
+#define XATTR_INFOFLOW_SUFFIX "infoflow"
+#define XATTR_NAME_INFOFLOW XATTR_SECURITY_PREFIX XATTR_INFOFLOW_SUFFIX
#endif /* _UAPI_LINUX_XATTR_H */
diff --git a/security/Kconfig b/security/Kconfig
index 466cc1f8ffed..7c5f0dc80b7b 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -237,6 +237,7 @@ source "security/apparmor/Kconfig"
source "security/loadpin/Kconfig"
source "security/yama/Kconfig"
source "security/safesetid/Kconfig"
+source "security/infoflow/Kconfig"
source "security/integrity/Kconfig"
diff --git a/security/Makefile b/security/Makefile
index c598b904938f..1f33df7966dd 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -11,6 +11,7 @@ subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
+subdir-$(CONFIG_SECURITY_INFOFLOW) += infoflow
# always enable default capabilities
obj-y += commoncap.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
+obj-$(CONFIG_SECURITY_INFOFLOW) += infoflow/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
diff --git a/security/infoflow/Kconfig b/security/infoflow/Kconfig
new file mode 100644
index 000000000000..744a28436e34
--- /dev/null
+++ b/security/infoflow/Kconfig
@@ -0,0 +1,6 @@
+config SECURITY_INFOFLOW
+ bool "Infoflow LSM"
+ select SECURITYFS
+ help
+ Infoflow LSM enforces the Clark-Wilson policy on top of existing LSMs,
+ such as SELinux and SMACK.
diff --git a/security/infoflow/Makefile b/security/infoflow/Makefile
new file mode 100644
index 000000000000..521a3c0a79bb
--- /dev/null
+++ b/security/infoflow/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Infoflow LSM
+#
+
+obj-$(CONFIG_SECURITY_INFOFLOW) := infoflow.o
+
+infoflow-y := infoflow_lsm.o infoflow_ctx.o infoflow_access.o infoflow_fs.o
diff --git a/security/infoflow/infoflow.h b/security/infoflow/infoflow.h
new file mode 100644
index 000000000000..26d35718e777
--- /dev/null
+++ b/security/infoflow/infoflow.h
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018,2019 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: infoflow.h
+ * Header file.
+ */
+
+#ifndef __INFOFLOW_H
+#define __INFOFLOW_H
+
+#include <linux/lsm_hooks.h>
+#include <linux/lsm_audit.h>
+#include <linux/msg.h>
+#include <net/flow.h>
+#include <net/sock.h>
+
+enum infoflow_class {CLASS_LNK_FILE, CLASS_REG_FILE, CLASS_DIR,
+ CLASS_CHR_FILE, CLASS_BLK_FILE, CLASS_FIFO_FILE,
+ CLASS_SOCK_FILE, CLASS_IPC, CLASS_MSGQ, CLASS_SHM,
+ CLASS_SEM, CLASS_SOCKET, CLASS_KEY, CLASS_PROCESS,
+ CLASS_KERNEL, CLASS_MODULE, CLASS_UNDEFINED, CLASS__LAST};
+
+struct infoflow_class_desc {
+ const char *name;
+ u8 excluded;
+};
+
+extern struct infoflow_class_desc infoflow_class_array[CLASS__LAST];
+
+extern struct list_head contexts;
+
+#define INFOFLOW_PARENT_LSM_INIT 0x1
+#define INFOFLOW_POLICY_INIT 0x2
+#define INFOFLOW_PROMOTE 0x4
+extern int infoflow_init_flags;
+
+enum infoflow_modes { INFOFLOW_DISABLED, INFOFLOW_DISCOVER, INFOFLOW_ENFORCE,
+ INFOFLOW_ENFORCE_AUDIT, INFOFLOW_PERMISSIVE,
+ INFOFLOW_PERMISSIVE_AUDIT, INFOFLOW_MODE__LAST };
+
+extern const char *infoflow_modes_str[INFOFLOW_MODE__LAST];
+extern int infoflow_mode;
+
+#define CTX_FLAG_TCB 0x01
+#define CTX_FLAG_FILTER 0x02
+#define CTX_FLAG_INITIALIZED 0x04
+#define CTX_FLAG_CANNOT_PROMOTE 0x08
+struct infoflow_ctx {
+ struct rb_node rb_node;
+ struct list_head access_subjs;
+ struct list_head filter_subjs;
+ struct list_head context_list;
+ spinlock_t ctx_lock;
+ u32 sid;
+ u8 flags;
+ enum infoflow_class class;
+ char *label;
+ int label_len;
+};
+
+#define TYPE_RULE 0
+#define TYPE_FILTER 1
+struct infoflow_subj_desc {
+ struct list_head list;
+ struct infoflow_ctx *ctx;
+ int mask;
+ u8 denied;
+ const char *cause;
+};
+
+struct infoflow_audit_data {
+ struct infoflow_ctx *subj;
+ struct infoflow_ctx *obj;
+ int request;
+ int result;
+ char *cause;
+};
+
+static inline u16 infoflow_inode_class(umode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFSOCK:
+ return CLASS_SOCK_FILE;
+ case S_IFLNK:
+ return CLASS_LNK_FILE;
+ case S_IFREG:
+ return CLASS_REG_FILE;
+ case S_IFBLK:
+ return CLASS_BLK_FILE;
+ case S_IFDIR:
+ return CLASS_DIR;
+ case S_IFCHR:
+ return CLASS_CHR_FILE;
+ case S_IFIFO:
+ return CLASS_FIFO_FILE;
+ }
+
+ return CLASS_UNDEFINED;
+}
+
+static inline u32 infoflow_file_f_mode_to_mask(struct file *file)
+{
+ int mask = 0;
+
+ if (file->f_mode & FMODE_READ)
+ mask |= MAY_READ;
+ if (file->f_mode & FMODE_WRITE)
+ mask |= MAY_WRITE;
+
+ return mask;
+}
+
+static inline enum infoflow_class infoflow_lookup_class(char *class)
+{
+ int i;
+
+ for (i = 0; i < CLASS__LAST; i++) {
+ if (!strcmp(infoflow_class_array[i].name, class))
+ break;
+ }
+
+ return i;
+}
+
+struct file_security_struct {
+ u32 sid;
+ u32 isid;
+ int pseqno;
+};
+
+extern struct lsm_blob_sizes infoflow_blob_sizes;
+
+static inline u8 *infoflow_inode(const struct inode *inode)
+{
+ return inode->i_security + infoflow_blob_sizes.lbs_inode;
+}
+
+static inline struct file_security_struct *infoflow_file(
+ const struct file *file)
+{
+ return file->f_security + infoflow_blob_sizes.lbs_file;
+}
+
+struct infoflow_ctx *infoflow_ctx_find_sid(enum infoflow_class class,
+ u32 sid);
+struct infoflow_ctx *infoflow_ctx_insert_sid(enum infoflow_class class, u32 sid,
+ u8 flags);
+struct infoflow_ctx *infoflow_ctx_insert_label(enum infoflow_class class,
+ char *label, int label_len,
+ u8 flags);
+void infoflow_ctx_delete(void);
+void infoflow_ctx_update_sid(void);
+int infoflow_ctx_find_add_subj(enum infoflow_class sclass, u32 ssid,
+ struct infoflow_ctx **sctx, u8 sflags,
+ enum infoflow_class oclass, u32 osid,
+ struct infoflow_ctx **octx, u8 oflags,
+ int mask, u8 denied, const char *cause,
+ int type);
+
+int infoflow_allow_access(enum infoflow_class sclass, u32 ssid,
+ enum infoflow_class oclass, u32 osid,
+ u8 *inode_flags, int mask,
+ struct common_audit_data *a);
+
+#endif /*__INFOFLOW_H*/
diff --git a/security/infoflow/infoflow_access.c b/security/infoflow/infoflow_access.c
new file mode 100644
index 000000000000..de81f0bfaad7
--- /dev/null
+++ b/security/infoflow/infoflow_access.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018,2019 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: infoflow_access.c
+ * Functions to enforce Clark-Wilson policy and log decisions.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/cred.h>
+#include <linux/magic.h>
+#include <linux/audit.h>
+#include <linux/xattr.h>
+
+#include "infoflow.h"
+
+struct infoflow_ctx unknown_ctx = { .class = CLASS_UNDEFINED,
+ .label = "unknown_label" };
+
+static void infoflow_log_callback(struct audit_buffer *ab, void *a)
+{
+ struct common_audit_data *ad = a;
+ struct infoflow_audit_data *sad = ad->infoflow_audit_data;
+
+ audit_log_format(ab, "lsm=infoflow request=%d action=%s cause=%s",
+ sad->request, sad->result ? "denied" : "granted",
+ sad->cause);
+ audit_log_format(ab, " subject=%s object=%s oclass=%s",
+ sad->subj->label, sad->obj->label,
+ infoflow_class_array[sad->obj->class].name);
+}
+
+static void infoflow_log(struct infoflow_ctx *subj, struct infoflow_ctx *obj,
+ int request, int result, char *cause,
+ struct common_audit_data *a)
+{
+ struct infoflow_audit_data sad;
+
+ memset(&sad, 0, sizeof(sad));
+
+ sad.subj = subj;
+ if (!sad.subj)
+ sad.subj = &unknown_ctx;
+ sad.obj = obj;
+ if (!sad.obj)
+ sad.obj = &unknown_ctx;
+ sad.request = request;
+ sad.result = result;
+ sad.cause = cause;
+
+ a->infoflow_audit_data = &sad;
+
+ common_lsm_audit(a, infoflow_log_callback, NULL);
+}
+
+/**
+ * infoflow_allow_access - enforce Clark-Wilson policy
+ * @sclass: subject class
+ * @ssid: subject's security identifier
+ * @oclass: object class
+ * @osid: object's security identifier
+ * @inode_flags: inode's flags
+ * @mask: operations requested
+ * @a: audit structure
+ *
+ * Return 0 on success, a negative value on failure.
+ */
+int infoflow_allow_access(enum infoflow_class sclass, u32 ssid,
+ enum infoflow_class oclass, u32 osid,
+ u8 *inode_flags, int mask,
+ struct common_audit_data *a)
+{
+ struct infoflow_subj_desc *desc, *found_desc = NULL;
+ struct infoflow_ctx *sctx = NULL, *octx = NULL;
+ u8 sflags = 0, oflags = inode_flags ? *inode_flags : 0;
+ bool denied = false;
+ char *cause = "unknown";
+ int result;
+
+ if (infoflow_class_array[oclass].excluded)
+ return 0;
+
+ if (!(infoflow_init_flags & INFOFLOW_PARENT_LSM_INIT))
+ return 0;
+
+ if (infoflow_mode == INFOFLOW_DISCOVER) {
+ result = infoflow_ctx_find_add_subj(sclass, ssid, &sctx, 0,
+ oclass, osid, &octx, 0,
+ mask, 0, NULL, TYPE_RULE);
+ if (result < 0)
+ pr_err("Cannot add rule for sclass %d, ssid %d, "
+ "oclass %d, osid %d\n", sclass, ssid, oclass,
+ osid);
+
+ return 0;
+ }
+
+ if (!(infoflow_init_flags & INFOFLOW_POLICY_INIT))
+ return 0;
+
+ sctx = infoflow_ctx_find_sid(sclass, ssid);
+ if (sctx)
+ sflags |= sctx->flags;
+
+ octx = infoflow_ctx_find_sid(oclass, osid);
+ if (octx)
+ oflags |= octx->flags;
+
+ if (mask & MAY_WRITE && !(sflags & CTX_FLAG_TCB) &&
+ !(oflags & CTX_FLAG_TCB) && inode_flags)
+ *inode_flags |= CTX_FLAG_CANNOT_PROMOTE;
+
+ if ((mask & MAY_WRITE) && !(sflags & CTX_FLAG_TCB) &&
+ (oflags & CTX_FLAG_TCB) && !(oflags & CTX_FLAG_FILTER)) {
+ denied = true;
+ cause = "biba-write-up";
+ }
+
+ if ((mask & (MAY_READ | MAY_EXEC)) && (sflags & CTX_FLAG_TCB) &&
+ !(oflags & CTX_FLAG_TCB) && !(oflags & CTX_FLAG_FILTER)) {
+ if ((infoflow_init_flags & INFOFLOW_PROMOTE) &&
+ !(mask & MAY_WRITE) &&
+ !(oflags & CTX_FLAG_CANNOT_PROMOTE) && inode_flags) {
+ *inode_flags |= CTX_FLAG_TCB;
+ } else {
+ denied = true;
+ cause = "biba-read-down";
+ }
+ }
+
+ if ((mask & (MAY_READ | MAY_EXEC)) && (sflags & CTX_FLAG_TCB) &&
+ !(oflags & CTX_FLAG_TCB) && (oflags & CTX_FLAG_FILTER)) {
+ if (list_empty(&octx->filter_subjs)) {
+ found_desc = (void *)1;
+ } else {
+ list_for_each_entry(desc, &octx->filter_subjs, list)
+ if (desc->ctx == sctx) {
+ found_desc = desc;
+ break;
+ }
+ }
+
+ if (!found_desc) {
+ denied = true;
+ cause = "filtering-subj-not-found";
+ }
+ }
+
+ if (!denied)
+ return 0;
+
+ if (infoflow_mode == INFOFLOW_ENFORCE_AUDIT ||
+ infoflow_mode == INFOFLOW_PERMISSIVE_AUDIT) {
+ result = infoflow_ctx_find_add_subj(sclass, ssid, &sctx, 0,
+ oclass, osid, &octx, 0,
+ mask, denied, cause,
+ TYPE_RULE);
+ if (result < 0)
+ pr_err("Cannot add rule for sclass %d, ssid %d,"
+ "oclass %d, osid %d\n", sclass, ssid,
+ oclass, osid);
+ }
+
+ result = 0;
+
+ if (infoflow_mode == INFOFLOW_ENFORCE ||
+ infoflow_mode == INFOFLOW_ENFORCE_AUDIT)
+ result = -EACCES;
+
+ if (a)
+ infoflow_log(sctx, octx, mask, result, cause, a);
+
+ return result;
+}
diff --git a/security/infoflow/infoflow_ctx.c b/security/infoflow/infoflow_ctx.c
new file mode 100644
index 000000000000..debec9268100
--- /dev/null
+++ b/security/infoflow/infoflow_ctx.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Copyright (C) 2018,2019 Huawei Technologies Duesseldorf GmbH
+ *
+ * Authors: Roberto Sassu <roberto.sassu@huawei.com>
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: infoflow_ctx.c
+ * Functions to manage security contexts.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/rbtree.h>
+
+#include "infoflow.h"
+
+static struct rb_root infoflow_ctx_tree[CLASS__LAST] = { RB_ROOT };
+static DEFINE_RWLOCK(infoflow_ctx_lock);
+LIST_HEAD(contexts);
+
+static struct infoflow_ctx *__infoflow_ctx_find_sid(enum infoflow_class class,
+ u32 sid)
+{
+ struct infoflow_ctx *ctx;
+ struct rb_node *n = infoflow_ctx_tree[class].rb_node;
+
+ while (n) {
+ ctx = rb_entry(n, struct infoflow_ctx, rb_node);
+
+ if (sid < ctx->sid)
+ n = n->rb_left;
+ else if (sid > ctx->sid)
+ n = n->rb_right;
+ else
+ break;
+ }
+ if (!n)
+ return NULL;
+
+ return ctx;
+}
+
+/*
+ * infoflow_ctx_find_sid - return the ctx associated with the class and sid
+ * @class: object class
+ * @sid: object's security identifier
+ *
+ * Return infoflow_ctx if found, NULL otherwise.
+ */
+struct infoflow_ctx *infoflow_ctx_find_sid(enum infoflow_class class, u32 sid)
+{
+ struct infoflow_ctx *ctx;
+
+ read_lock(&infoflow_ctx_lock);
+ ctx = __infoflow_ctx_find_sid(class, sid);
+ read_unlock(&infoflow_ctx_lock);
+
+ return ctx;
+}
+
+static struct infoflow_ctx *__infoflow_ctx_find_label(enum infoflow_class class,
+ char *label,
+ int label_len)
+{
+ struct infoflow_ctx *ctx;
+
+ list_for_each_entry(ctx, &contexts, context_list)
+ if (ctx->class == class && ctx->label_len == label_len &&
+ !strncmp(ctx->label, label, label_len))
+ return ctx;
+
+ return NULL;
+}
+
+static struct infoflow_ctx *infoflow_ctx_find_label(enum infoflow_class class,
+ char *label, int label_len)
+{
+ struct infoflow_ctx *ctx;
+
+ read_lock(&infoflow_ctx_lock);
+ ctx = __infoflow_ctx_find_label(class, label, label_len);
+ read_unlock(&infoflow_ctx_lock);
+
+ return ctx;
+}
+
+static void infoflow_ctx_insert_to_rbtree(struct infoflow_ctx *ctx)
+{
+ struct rb_node **p;
+ struct rb_node *node, *parent = NULL;
+ struct infoflow_ctx *test_ctx;
+
+ p = &infoflow_ctx_tree[ctx->class].rb_node;
+ while (*p) {
+ parent = *p;
+ test_ctx = rb_entry(parent, struct infoflow_ctx, rb_node);
+ if (ctx->sid < test_ctx->sid)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ node = &ctx->rb_node;
+ rb_link_node(node, parent, p);
+ rb_insert_color(node, &infoflow_ctx_tree[ctx->class]);
+}
+
+static struct infoflow_ctx *infoflow_ctx_insert(enum infoflow_class class,
+ u32 sid, u8 flags, char *label,
+ int label_len)
+{
+ struct infoflow_ctx *ctx;
+
+ ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC | __GFP_NOWARN);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->sid = sid;
+ ctx->flags = flags;
+ ctx->class = class;
+ ctx->label = kmemdup_nul(label, label_len, GFP_ATOMIC | __GFP_NOWARN);
+ if (!ctx->label) {
+ kfree(ctx);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ctx->label_len = label_len;
+
+ INIT_LIST_HEAD(&ctx->access_subjs);
+ INIT_LIST_HEAD(&ctx->filter_subjs);
+ spin_lock_init(&ctx->ctx_lock);
+
+ write_lock(&infoflow_ctx_lock);
+ infoflow_ctx_insert_to_rbtree(ctx);
+ list_add_tail_rcu(&ctx->context_list, &contexts);
+ write_unlock(&infoflow_ctx_lock);
+
+ return ctx;
+}
+
+/**
+ * infoflow_ctx_insert_sid - insert ctx from sid
+ * @class: object class
+ * @sid: object's security identifier
+ * @flags: flags associated to the sid
+ *
+ * Return new infoflow_ctx on success, NULL on error.
+ */
+struct infoflow_ctx *infoflow_ctx_insert_sid(enum infoflow_class class, u32 sid,
+ u8 flags)
+{
+ char *label;
+ int label_len;
+ int result;
+ struct infoflow_ctx *ctx;
+
+ ctx = infoflow_ctx_find_sid(class, sid);
+ if (ctx) {
+ ctx->flags |= flags;
+ return ctx;
+ }
+
+ result = security_secid_to_secctx(sid, &label, &label_len);
+ if (result < 0) {
+ pr_err("Cannot retrieve label for sid %d\n", sid);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ctx = infoflow_ctx_insert(class, sid, flags, label, label_len);
+
+ security_release_secctx(label, label_len);
+ return ctx;
+}
+
+/**
+ * infoflow_ctx_insert_label - insert ctx from label
+ * @class: object class
+ * @label: label associated to the sid
+ * @label_len: label length
+ * @flags: flags associated to the sid
+ *
+ * Return new infoflow_ctx on success, NULL on error.
+ */
+struct infoflow_ctx *infoflow_ctx_insert_label(enum infoflow_class class,
+ char *label, int label_len,
+ u8 flags)
+{
+ struct infoflow_ctx *ctx;
+ u32 sid = 0;
+ int result;
+
+ ctx = infoflow_ctx_find_label(class, label, label_len);
+ if (ctx) {
+ ctx->flags |= flags;
+ return ctx;
+ }
+
+ if (!(infoflow_init_flags & INFOFLOW_PARENT_LSM_INIT))
+ goto out;
+
+ result = security_secctx_to_secid(label, label_len, &sid);
+ if (result < 0) {
+ pr_err("Cannot retrieve sid for label %s\n", label);
+ return ERR_PTR(-EINVAL);
+ }
+out:
+ return infoflow_ctx_insert(class, sid, flags, label, label_len);
+}
+
+/**
+ * infoflow_ctx_delete - delete all contexts
+ *
+ */
+void infoflow_ctx_delete(void)
+{
+ struct infoflow_ctx *ctx, *tmp_ctx;
+ int i;
+
+ write_lock(&infoflow_ctx_lock);
+ list_for_each_entry_safe(ctx, tmp_ctx, &contexts, context_list) {
+ list_del(&ctx->context_list);
+ kfree(ctx->label);
+ kfree(ctx);
+ }
+
+ for (i = 0; i < CLASS__LAST; i++)
+ infoflow_ctx_tree[i] = RB_ROOT;
+
+ write_unlock(&infoflow_ctx_lock);
+}
+
+/**
+ * infoflow_ctx_update_sid - update infoflow_ctx SID after policy change
+ *
+ */
+void infoflow_ctx_update_sid(void)
+{
+ struct infoflow_ctx *ctx;
+ u32 new_sid;
+ int result;
+
+ list_for_each_entry(ctx, &contexts, context_list) {
+ result = security_secctx_to_secid(ctx->label,
+ strlen(ctx->label),
+ &new_sid);
+ if (result < 0) {
+ pr_err("Cannot obtain SID for context %s\n",
+ ctx->label);
+ continue;
+ }
+
+ if (ctx->sid == new_sid)
+ continue;
+
+ write_lock(&infoflow_ctx_lock);
+ rb_erase(&ctx->rb_node, &infoflow_ctx_tree[ctx->class]);
+
+ ctx->sid = new_sid;
+ infoflow_ctx_insert_to_rbtree(ctx);
+ write_unlock(&infoflow_ctx_lock);
+ }
+}
+
+/**
+ * infoflow_ctx_find_add_subj - add discovered interaction or filtering subj
+ * @sclass: subject class
+ * @ssid: subject's security identifier
+ * @sctx: subject's infoflow_ctx
+ * @sflags: subject's flags
+ * @oclass: object class
+ * @osid: object's security identifier
+ * @octx: object's infoflow_ctx
+ * @oflags: object's flags
+ * @mask: operations requested
+ * @denied: whether access is denied
+ * @cause: reason if access is denied
+ * @type: type of information (rule, filtering subj)
+ *
+ * Return 0 on success, a negative value on failure.
+ */
+int infoflow_ctx_find_add_subj(enum infoflow_class sclass, u32 ssid,
+ struct infoflow_ctx **sctx, u8 sflags,
+ enum infoflow_class oclass, u32 osid,
+ struct infoflow_ctx **octx, u8 oflags,
+ int mask, u8 denied, const char *cause,
+ int type)
+{
+ struct infoflow_subj_desc *desc, *found_desc = NULL;
+ struct list_head *head;
+
+ if (!*sctx) {
+ *sctx = infoflow_ctx_insert_sid(sclass, ssid, sflags);
+ if (IS_ERR(*sctx))
+ return PTR_ERR(*sctx);
+ }
+
+ if (!*octx) {
+ *octx = infoflow_ctx_insert_sid(oclass, osid, oflags);
+ if (IS_ERR(*octx))
+ return PTR_ERR(*octx);
+ }
+
+ head = &(*octx)->access_subjs;
+ if (type == TYPE_FILTER)
+ head = &(*octx)->filter_subjs;
+
+ list_for_each_entry(desc, head, list) {
+ if (desc->ctx == *sctx) {
+ found_desc = desc;
+ break;
+ }
+ }
+
+ if (!found_desc) {
+ desc = kmalloc(sizeof(*desc), GFP_ATOMIC | __GFP_NOWARN);
+ if (!desc) {
+ pr_err("Cannot allocate memory for subject desc\n");
+ return -ENOMEM;
+ }
+
+ desc->ctx = *sctx;
+ desc->mask = 0;
+ desc->denied = 0;
+ desc->cause = cause;
+
+ spin_lock(&(*octx)->ctx_lock);
+ list_add_tail_rcu(&desc->list, head);
+ spin_unlock(&(*octx)->ctx_lock);
+ }
+
+ desc->mask |= mask;
+ desc->denied |= denied;
+ return 0;
+}
diff --git a/security/infoflow/infoflow_fs.c b/security/infoflow/infoflow_fs.c
new file mode 100644
index 000000000000..52be308d0358
--- /dev/null
+++ b/security/infoflow/infoflow_fs.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Copyright (C) 2018,2019 Huawei Technologies Duesseldorf GmbH
+ *
+ * Authors: Roberto Sassu <roberto.sassu@huawei.com>
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: infoflow_fs.c
+ * Functions implementing methods for securityfs.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/security.h>
+#include <linux/parser.h>
+
+#include "infoflow.h"
+
+static struct dentry *infoflow_dir;
+static struct dentry *infoflow_rules;
+static struct dentry *infoflow_policy;
+static struct dentry *infoflow_enforce;
+
+static void *infoflow_ctx_start(struct seq_file *m, loff_t *pos)
+{
+ loff_t l = *pos;
+ struct infoflow_ctx *ctx;
+
+ if (m->file->f_path.dentry == infoflow_policy &&
+ !(infoflow_init_flags & INFOFLOW_POLICY_INIT))
+ return NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ctx, &contexts, context_list) {
+ if (!l--) {
+ rcu_read_unlock();
+ return ctx;
+ }
+ }
+ rcu_read_unlock();
+ return NULL;
+}
+
+static void *infoflow_ctx_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct infoflow_ctx *ctx = v;
+
+ rcu_read_lock();
+ ctx = list_entry_rcu(ctx->context_list.next, struct infoflow_ctx,
+ context_list);
+ rcu_read_unlock();
+ (*pos)++;
+
+ return (&ctx->context_list == &contexts) ? NULL : ctx;
+}
+
+static void infoflow_ctx_stop(struct seq_file *m, void *v)
+{
+}
+
+int infoflow_rules_show(struct seq_file *m, void *v)
+{
+ struct infoflow_ctx *ctx = v;
+ struct infoflow_subj_desc *desc;
+
+ list_for_each_entry(desc, &ctx->access_subjs, list) {
+ seq_printf(m, "allow %s %s:%s {", desc->ctx->label, ctx->label,
+ infoflow_class_array[ctx->class].name);
+
+ if (desc->mask & MAY_READ || desc->mask & MAY_EXEC)
+ seq_printf(m, " read");
+ if (desc->mask & MAY_WRITE || desc->mask & MAY_APPEND)
+ seq_printf(m, " write");
+
+ seq_printf(m, " };");
+
+ if (desc->denied)
+ seq_printf(m, " [denied:%s]", desc->cause);
+
+ seq_printf(m, "\n");
+ }
+
+ return 0;
+}
+
+static const struct seq_operations infoflow_rules_seqops = {
+ .start = infoflow_ctx_start,
+ .next = infoflow_ctx_next,
+ .stop = infoflow_ctx_stop,
+ .show = infoflow_rules_show,
+};
+
+static int infoflow_rules_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &infoflow_rules_seqops);
+}
+
+static const struct file_operations infoflow_rules_ops = {
+ .open = infoflow_rules_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+int infoflow_policy_show(struct seq_file *m, void *v)
+{
+ struct infoflow_ctx *ctx = v;
+ struct infoflow_subj_desc *desc;
+
+ if (ctx->flags & CTX_FLAG_FILTER)
+ seq_printf(m, "filter");
+ else if (ctx->flags & CTX_FLAG_TCB)
+ seq_printf(m, "tcb");
+ else
+ return 0;
+
+ if (ctx->class == CLASS_PROCESS)
+ seq_printf(m, " subj=");
+ else
+ seq_printf(m, " obj=");
+
+ seq_printf(m, "%s:%s", ctx->label,
+ infoflow_class_array[ctx->class].name);
+
+ list_for_each_entry(desc, &ctx->filter_subjs, list)
+ seq_printf(m, " subj=%s:%s", desc->ctx->label,
+ infoflow_class_array[desc->ctx->class].name);
+
+ seq_printf(m, "\n");
+
+ return 0;
+}
+
+static const struct seq_operations policy_seq_ops = {
+ .start = infoflow_ctx_start,
+ .next = infoflow_ctx_next,
+ .stop = infoflow_ctx_stop,
+ .show = infoflow_policy_show,
+};
+
+enum {
+ Opt_err = -1, Opt_tcb = 1, Opt_filter, Opt_subj, Opt_obj,
+};
+
+static match_table_t policy_tokens = {
+ {Opt_tcb, "tcb"},
+ {Opt_filter, "filter"},
+ {Opt_subj, "subj=%s"},
+ {Opt_obj, "obj=%s"},
+};
+
+int valid_policy;
+
+static int infoflow_open_policy(struct inode *inode, struct file *file)
+{
+ if ((infoflow_init_flags & INFOFLOW_POLICY_INIT) &&
+ (file->f_mode & FMODE_WRITE))
+ return -EPERM;
+
+ valid_policy = 1;
+
+ return seq_open(file, &policy_seq_ops);
+}
+
+static struct infoflow_ctx *infoflow_add_ctx(char *label, u8 flags, bool obj)
+{
+ enum infoflow_class class = CLASS_PROCESS;
+ char *class_ptr;
+
+ if (!obj)
+ goto out;
+
+ class_ptr = strrchr(label, ':');
+ if (!class_ptr)
+ return ERR_PTR(-EINVAL);
+
+ *class_ptr++ = '\0';
+
+ class = infoflow_lookup_class(class_ptr);
+ if (class == CLASS__LAST) {
+ pr_err("Invalid class %s\n", class_ptr);
+ return ERR_PTR(-EINVAL);
+ }
+out:
+ return infoflow_ctx_insert_label(class, label, strlen(label), flags);
+}
+
+static int infoflow_parse_rule(char *rule)
+{
+ struct infoflow_ctx *subj = NULL, *obj = NULL;
+ LIST_HEAD(filtering_subjects);
+ struct infoflow_subj_desc *desc, *tmp_desc;
+ char *p;
+ u8 flags = 0;
+ int result = 0;
+
+ while ((p = strsep(&rule, " \t")) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+ int token;
+
+ if (result < 0)
+ break;
+ if ((*p == '\0') || (*p == ' ') || (*p == '\t'))
+ continue;
+ token = match_token(p, policy_tokens, args);
+ switch (token) {
+ case Opt_tcb:
+ if (flags) {
+ pr_err("Rule type already specified\n");
+ result = -EINVAL;
+ break;
+ }
+
+ flags |= CTX_FLAG_TCB;
+ break;
+ case Opt_filter:
+ if (flags) {
+ pr_err("Rule type already specified\n");
+ result = -EINVAL;
+ break;
+ }
+
+ flags |= CTX_FLAG_FILTER;
+ break;
+ case Opt_subj:
+ if (!flags) {
+ pr_err("Rule type not specified\n");
+ result = -EINVAL;
+ break;
+ }
+
+ if (!(flags & CTX_FLAG_FILTER) && subj) {
+ pr_err("Subject already specified\n");
+ result = -EINVAL;
+ break;
+ }
+
+ if ((flags & CTX_FLAG_FILTER) && !obj) {
+ pr_err("Object not specified\n");
+ result = -EINVAL;
+ break;
+ }
+
+ subj = infoflow_add_ctx(args[0].from,
+ (flags & CTX_FLAG_FILTER) ?
+ 0 : flags, false);
+ if (IS_ERR(subj)) {
+ result = PTR_ERR(subj);
+ break;
+ }
+
+ if (flags & CTX_FLAG_FILTER) {
+ result = infoflow_ctx_find_add_subj(0, 0, &subj,
+ 0, 0, 0, &obj, 0, 0, 0, NULL,
+ TYPE_FILTER);
+ if (result < 0)
+ break;
+ }
+
+ desc = kmalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc) {
+ result = -ENOMEM;
+ break;
+ }
+ desc->ctx = subj;
+ list_add_tail(&desc->list, &filtering_subjects);
+ break;
+ case Opt_obj:
+ if (!flags || obj) {
+ pr_err("Rule type not specified or "
+ "object specified\n");
+ result = -EINVAL;
+ break;
+ }
+
+ obj = infoflow_add_ctx(args[0].from, flags, true);
+ if (IS_ERR(obj)) {
+ result = -ENOMEM;
+ break;
+ }
+ break;
+ case Opt_err:
+ result = -EINVAL;
+ break;
+ }
+ }
+
+ if (!result && !flags) {
+ pr_err("Rule type not specified\n");
+ result = -EINVAL;
+ }
+
+ list_for_each_entry_safe(desc, tmp_desc, &filtering_subjects, list) {
+ if (!(desc->ctx->flags & CTX_FLAG_TCB)) {
+ pr_err("Filtering subject %s not added to TCB\n",
+ desc->ctx->label);
+ list_del(&desc->list);
+ kfree(desc);
+ result = -EINVAL;
+ }
+ }
+
+ if (result < 0)
+ infoflow_ctx_delete();
+
+ return result;
+}
+
+static ssize_t infoflow_write_policy(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *data, *data_ptr, *newline_ptr;
+ int rc = 0;
+
+ if (cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
+ CAP_OPT_NONE))
+ return -EACCES;
+
+ if (*ppos != 0)
+ return -EINVAL;
+
+ if (count >= PAGE_SIZE)
+ count = PAGE_SIZE - 1;
+
+ data_ptr = data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ while (*(newline_ptr = strchrnul(data_ptr, '\n')) == '\n') {
+ *newline_ptr = '\0';
+
+ rc = infoflow_parse_rule(data_ptr);
+ if (rc < 0)
+ break;
+
+ data_ptr = newline_ptr + 1;
+ }
+
+ kfree(data);
+
+ if (rc < 0) {
+ valid_policy = 0;
+ return rc;
+ }
+
+ return data_ptr - data;
+}
+
+static int infoflow_release_policy(struct inode *inode, struct file *file)
+{
+ if (!(file->f_mode & FMODE_WRITE))
+ return 0;
+
+ if (!valid_policy) {
+ pr_err("Cannot load infoflow policy\n");
+ return 0;
+ }
+
+ infoflow_init_flags |= INFOFLOW_POLICY_INIT;
+
+ pr_info("Successfully loaded Infoflow LSM policy\n");
+
+ return 0;
+}
+
+static const struct file_operations infoflow_policy_ops = {
+ .open = infoflow_open_policy,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = infoflow_write_policy,
+ .release = infoflow_release_policy,
+};
+
+
+static int infoflow_open_mode(struct inode *inode, struct file *file)
+{
+ if ((file->f_mode & FMODE_WRITE) &&
+ cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
+ CAP_OPT_NONE))
+ return -EACCES;
+
+ return 0;
+}
+
+#define TMPBUFLEN 32
+static ssize_t infoflow_read_mode(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, sizeof(tmpbuf), "%s",
+ infoflow_modes_str[infoflow_mode]);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t infoflow_write_mode(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *new_mode = NULL;
+ ssize_t length;
+ int i;
+
+ if (count >= PAGE_SIZE)
+ return -ENOMEM;
+
+ /* No partial writes. */
+ if (*ppos != 0)
+ return -EINVAL;
+
+ new_mode = memdup_user_nul(buf, count);
+ if (IS_ERR(new_mode))
+ return PTR_ERR(new_mode);
+
+ for (i = 0; i < INFOFLOW_MODE__LAST; i++) {
+ if (!strcmp(new_mode, infoflow_modes_str[i])) {
+ infoflow_mode = i;
+ break;
+ }
+ }
+
+ if (i == INFOFLOW_MODE__LAST) {
+ length = -EINVAL;
+ goto out;
+ }
+
+ length = count;
+out:
+ kfree(new_mode);
+ return length;
+}
+
+static const struct file_operations infoflow_enforce_ops = {
+ .open = infoflow_open_mode,
+ .read = infoflow_read_mode,
+ .write = infoflow_write_mode,
+ .llseek = generic_file_llseek,
+};
+
+static int __init init_infoflow_fs(void)
+{
+ infoflow_dir = securityfs_create_dir("infoflow", NULL);
+ if (IS_ERR(infoflow_dir))
+ goto out;
+
+ infoflow_rules = securityfs_create_file("rules", S_IRUGO,
+ infoflow_dir, NULL, &infoflow_rules_ops);
+ if (IS_ERR(infoflow_rules))
+ goto out;
+
+ infoflow_policy = securityfs_create_file("policy", S_IRUGO | S_IWUSR,
+ infoflow_dir, NULL, &infoflow_policy_ops);
+ if (IS_ERR(infoflow_policy))
+ goto out;
+
+ infoflow_enforce = securityfs_create_file("enforce",
+ S_IRUGO | S_IWUSR, infoflow_dir, NULL,
+ &infoflow_enforce_ops);
+ if (IS_ERR(infoflow_enforce))
+ goto out;
+
+ return 0;
+out:
+ securityfs_remove(infoflow_enforce);
+ securityfs_remove(infoflow_policy);
+ securityfs_remove(infoflow_rules);
+ securityfs_remove(infoflow_dir);
+ return -1;
+}
+
+__initcall(init_infoflow_fs);
diff --git a/security/infoflow/infoflow_lsm.c b/security/infoflow/infoflow_lsm.c
new file mode 100644
index 000000000000..0737b8e07c47
--- /dev/null
+++ b/security/infoflow/infoflow_lsm.c
@@ -0,0 +1,778 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018,2019 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: infoflow_lsm.c
+ * Function implementing LSM hooks.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/cred.h>
+#include <linux/magic.h>
+#include <linux/audit.h>
+#include <linux/xattr.h>
+
+#include "infoflow.h"
+
+struct infoflow_class_desc infoflow_class_array[CLASS__LAST] = {
+ [CLASS_LNK_FILE] = { .name = "lnk_file", .excluded = 1 },
+ [CLASS_REG_FILE] = { .name = "file" },
+ [CLASS_DIR] = { .name = "dir", .excluded = 1 },
+ [CLASS_CHR_FILE] = { .name = "chr_file" },
+ [CLASS_BLK_FILE] = { .name = "blk_file" },
+ [CLASS_FIFO_FILE] = { .name = "fifo_file" },
+ [CLASS_SOCK_FILE] = { .name = "sock_file", .excluded = 1 },
+ [CLASS_IPC] = { .name = "ipc" },
+ [CLASS_MSGQ] = { .name = "msgq" },
+ [CLASS_SHM] = { .name = "shm" },
+ [CLASS_SEM] = { .name = "sem" },
+ [CLASS_SOCKET] = { .name = "socket" },
+ [CLASS_KEY] = { .name = "key" },
+ [CLASS_PROCESS] = { .name = "process" },
+ [CLASS_KERNEL] = { .name = "kernel" },
+ [CLASS_MODULE] = { .name = "module" },
+ [CLASS_UNDEFINED] = { .name = "undefined" },
+};
+
+const char *infoflow_modes_str[INFOFLOW_MODE__LAST] = {
+ [INFOFLOW_DISABLED] = "disabled",
+ [INFOFLOW_DISCOVER] = "discover",
+ [INFOFLOW_ENFORCE] = "enforce",
+ [INFOFLOW_ENFORCE_AUDIT] = "enforce-audit",
+ [INFOFLOW_PERMISSIVE] = "permissive",
+ [INFOFLOW_PERMISSIVE_AUDIT] = "permissive-audit",
+};
+
+int infoflow_mode __lsm_ro_after_init = INFOFLOW_ENFORCE;
+static int __init infoflow_mode_setup(char *str)
+{
+ int i;
+
+ for (i = 0; i < INFOFLOW_MODE__LAST; i++) {
+ if (!strcmp(str, infoflow_modes_str[i])) {
+ infoflow_mode = i;
+ break;
+ }
+ }
+
+ if (i == INFOFLOW_MODE__LAST)
+ pr_err("Unknown mode %s\n", str);
+
+ return 1;
+}
+__setup("infoflow_mode=", infoflow_mode_setup);
+
+int infoflow_enabled __lsm_ro_after_init = 1;
+static int __init infoflow_enabled_setup(char *str)
+{
+ unsigned long enabled;
+
+ if (!kstrtoul(str, 0, &enabled))
+ infoflow_enabled = enabled ? 1 : 0;
+ return 1;
+}
+__setup("infoflow=", infoflow_enabled_setup);
+
+int infoflow_init_flags;
+static int __init infoflow_promote_setup(char *str)
+{
+ infoflow_init_flags |= INFOFLOW_PROMOTE;
+ return 1;
+}
+__setup("infoflow_promote", infoflow_promote_setup);
+
+static int infoflow_seqno;
+
+static int infoflow_security_change(struct notifier_block *nb,
+ unsigned long event, void *lsm_data)
+{
+ if (event != LSM_POLICY_CHANGE)
+ return NOTIFY_DONE;
+
+ infoflow_seqno++;
+ infoflow_ctx_update_sid();
+ infoflow_init_flags |= INFOFLOW_PARENT_LSM_INIT;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block infoflow_lsm_nb = {
+ .notifier_call = infoflow_security_change,
+};
+
+static int infoflow_inode_init_security(struct inode *inode, struct inode *dir,
+ const struct qstr *qstr,
+ const char **name, void **value,
+ size_t *len)
+{
+ const struct cred *cred = current_cred();
+ u8 *iflags = infoflow_inode(inode);
+ u16 class = infoflow_inode_class(inode->i_mode);
+ struct infoflow_ctx *sctx;
+ u8 flags = CTX_FLAG_TCB;
+ u32 sid;
+
+ if (name)
+ *name = XATTR_INFOFLOW_SUFFIX;
+
+ if (infoflow_class_array[class].excluded)
+ return 0;
+
+ if (!(infoflow_init_flags & INFOFLOW_POLICY_INIT))
+ return -EOPNOTSUPP;
+
+ validate_creds(cred);
+
+ security_cred_getsecid(cred, &sid);
+ sctx = infoflow_ctx_find_sid(CLASS_PROCESS, sid);
+
+ if (!sctx || !(sctx->flags & CTX_FLAG_TCB))
+ return 0;
+
+ *iflags |= (flags | CTX_FLAG_INITIALIZED);
+
+ if (value && len) {
+ *value = kmemdup(&flags, sizeof(flags), GFP_NOFS);
+ if (!*value)
+ return -ENOMEM;
+
+ *len = sizeof(flags);
+ }
+
+ return 0;
+}
+
+int infoflow_check_fs(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+
+ switch (sb->s_magic) {
+ case PROC_SUPER_MAGIC:
+ case SYSFS_MAGIC:
+ case DEBUGFS_MAGIC:
+ case RAMFS_MAGIC:
+ case DEVPTS_SUPER_MAGIC:
+ case BINFMTFS_MAGIC:
+ case SECURITYFS_MAGIC:
+ case SELINUX_MAGIC:
+ case SMACK_MAGIC:
+ case NSFS_MAGIC:
+ case CGROUP_SUPER_MAGIC:
+ case CGROUP2_SUPER_MAGIC:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static int infoflow_inode_init(struct dentry *opt_dentry, struct inode *inode,
+ bool may_sleep)
+{
+ u8 *iflags = infoflow_inode(inode);
+ u16 class = infoflow_inode_class(inode->i_mode);
+ struct dentry *dentry = NULL;
+ u8 flags = 0;
+ int rc;
+
+ might_sleep_if(may_sleep);
+
+ if (infoflow_class_array[class].excluded)
+ return 0;
+
+ if (!(infoflow_init_flags & INFOFLOW_POLICY_INIT))
+ return 0;
+
+ if (!may_sleep)
+ return -ECHILD;
+
+ if (*iflags & CTX_FLAG_INITIALIZED)
+ return 0;
+
+ if (!infoflow_check_fs(inode) || !(inode->i_opflags & IOP_XATTR)) {
+ *iflags |= CTX_FLAG_INITIALIZED;
+ return 0;
+ }
+
+ if (!opt_dentry) {
+ dentry = d_find_alias(inode);
+ if (!dentry)
+ dentry = d_find_any_alias(inode);
+ } else {
+ dentry = dget(opt_dentry);
+ }
+
+ if (!dentry)
+ return 0;
+
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_INFOFLOW, &flags,
+ sizeof(flags));
+ if (rc == sizeof(flags))
+ *iflags |= (flags & CTX_FLAG_TCB);
+
+ *iflags |= CTX_FLAG_INITIALIZED;
+
+ if (dentry)
+ dput(dentry);
+
+ return 0;
+}
+
+static void infoflow_d_instantiate(struct dentry *opt_dentry,
+ struct inode *inode)
+{
+ infoflow_inode_init(opt_dentry, inode, true);
+}
+
+static int inode_has_perm(const struct cred *cred,
+ struct inode *inode,
+ int mask,
+ struct common_audit_data *adp)
+{
+ u8 *iflags = infoflow_inode(inode);
+ u32 ssid, isid;
+
+ if (!infoflow_check_fs(inode))
+ return 0;
+
+ validate_creds(cred);
+
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
+ security_cred_getsecid(cred, &ssid);
+ security_inode_getsecid(inode, &isid);
+
+ return infoflow_allow_access(CLASS_PROCESS, ssid,
+ infoflow_inode_class(inode->i_mode), isid,
+ iflags, mask, adp);
+}
+
+static int infoflow_inode_permission(struct inode *inode, int mask)
+{
+ const struct cred *cred = current_cred();
+ struct common_audit_data ad;
+
+ mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
+
+ /* No permission to check. Existence test. */
+ if (!mask)
+ return 0;
+
+ ad.type = LSM_AUDIT_DATA_INODE;
+ ad.u.inode = inode;
+
+ return inode_has_perm(cred, inode, mask, &ad);
+}
+
+static int infoflow_inode_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = d_backing_inode(dentry);
+ const struct cred *cred = current_cred();
+
+ if (strcmp(name, XATTR_NAME_INFOFLOW))
+ return 0;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return inode_has_perm(cred, inode, MAY_WRITE, NULL);
+}
+
+static void infoflow_inode_post_setxattr(struct dentry *dentry,
+ const char *name, const void *value,
+ size_t size, int flags)
+{
+ struct inode *inode = d_backing_inode(dentry);
+ u8 *iflags = infoflow_inode(inode);
+
+ if (strcmp(name, XATTR_NAME_INFOFLOW) != 0)
+ return;
+
+ if (size != 1)
+ return;
+
+ *iflags = *(u8 *)value;
+}
+
+static int infoflow_inode_removexattr(struct dentry *dentry, const char *name)
+{
+ if (strcmp(name, XATTR_NAME_INFOFLOW))
+ return 0;
+
+ /* There is no inode_post_removexattr() hook, we cannot clear
+ * the inode flags.
+ */
+ return -EACCES;
+}
+
+static int infoflow_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ u8 *iflags = infoflow_inode(inode);
+
+ if (strcmp(name, XATTR_INFOFLOW_SUFFIX))
+ return -EOPNOTSUPP;
+
+ if (!value || !size)
+ return -EACCES;
+
+ if (size != 1)
+ return -EINVAL;
+
+ *iflags = *(u8 *)value;
+
+ return 0;
+}
+
+static int file_has_perm(const struct cred *cred,
+ struct file *file,
+ int mask)
+{
+ struct inode *inode = file_inode(file);
+ struct common_audit_data ad;
+ int rc;
+
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+
+ /* av is zero if only checking access to the descriptor. */
+ rc = 0;
+ if (mask)
+ rc = inode_has_perm(cred, inode, mask, &ad);
+
+ return rc;
+}
+
+/* Same as path_has_perm, but uses the inode from the file struct. */
+static inline int file_path_has_perm(const struct cred *cred,
+ struct file *file,
+ int mask)
+{
+ struct common_audit_data ad;
+
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+
+ return inode_has_perm(cred, file_inode(file), mask, &ad);
+}
+
+static int infoflow_revalidate_file_permission(struct file *file, int mask)
+{
+ const struct cred *cred = current_cred();
+
+ return file_has_perm(cred, file, mask);
+}
+
+static int infoflow_file_permission(struct file *file, int mask)
+{
+ const struct cred *cred = current_cred();
+ struct inode *inode = file_inode(file);
+ struct file_security_struct *fsec = infoflow_file(file);
+ u32 ssid, isid;
+
+ if (!mask)
+ /* No permission to check. Existence test. */
+ return 0;
+
+ validate_creds(cred);
+ security_cred_getsecid(cred, &ssid);
+
+ security_inode_getsecid(inode, &isid);
+
+ if (ssid == fsec->sid && fsec->isid == isid &&
+ fsec->pseqno == infoflow_seqno)
+ /* No change since file_open check. */
+ return 0;
+
+ return infoflow_revalidate_file_permission(file, mask);
+}
+
+static int infoflow_file_receive(struct file *file)
+{
+ const struct cred *cred = current_cred();
+
+ return file_has_perm(cred, file, infoflow_file_f_mode_to_mask(file));
+}
+
+static int infoflow_file_open(struct file *file)
+{
+ struct file_security_struct *fsec;
+ u32 isid;
+
+ fsec = infoflow_file(file);
+
+ security_inode_getsecid(file_inode(file), &isid);
+
+ fsec->isid = isid;
+ fsec->pseqno = infoflow_seqno;
+
+ return file_path_has_perm(file->f_cred, file,
+ infoflow_file_f_mode_to_mask(file));
+}
+
+static int infoflow_kernel_module_from_file(struct file *file, bool module)
+{
+ struct common_audit_data ad;
+ const struct cred *cred = current_cred();
+ enum infoflow_class class = CLASS_MODULE;
+ struct inode *inode;
+ u32 ssid, isid;
+ u8 *iflags;
+
+ validate_creds(cred);
+ security_cred_getsecid(cred, &ssid);
+
+ /* init_module */
+ if (file == NULL) {
+ if (!module)
+ class = CLASS_UNDEFINED;
+
+ return infoflow_allow_access(CLASS_KERNEL, ssid, class, ssid,
+ NULL, MAY_READ, NULL);
+ }
+
+ /* finit_module */
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+
+ inode = file_inode(file);
+ security_inode_getsecid(inode, &isid);
+ iflags = infoflow_inode(inode);
+
+ if (!module)
+ class = infoflow_inode_class(inode->i_mode);
+
+ return infoflow_allow_access(CLASS_KERNEL, ssid, class, isid, iflags,
+ MAY_READ, &ad);
+}
+
+static int infoflow_kernel_read_file(struct file *file,
+ enum kernel_read_file_id id)
+{
+ return infoflow_kernel_module_from_file(file, id == READING_MODULE);
+}
+
+static int infoflow_kernel_load_data(enum kernel_load_data_id id)
+{
+ return infoflow_kernel_module_from_file(NULL, id == LOADING_MODULE);
+}
+
+static int infoflow_setprocattr(const char *name, void *value, size_t size)
+{
+ const struct cred *cred = current_cred();
+ struct infoflow_ctx *old_ctx, *new_ctx;
+ u32 old_ssid, new_ssid;
+ int rc;
+
+ if (strcmp(name, "current"))
+ return 0;
+
+ validate_creds(cred);
+ security_cred_getsecid(cred, &old_ssid);
+
+ old_ctx = infoflow_ctx_find_sid(CLASS_PROCESS, old_ssid);
+
+ rc = security_secctx_to_secid((char *)value, size, &new_ssid);
+ if (rc < 0)
+ return -EPERM;
+
+ new_ctx = infoflow_ctx_find_sid(CLASS_PROCESS, new_ssid);
+
+ /* We cannot let a non-TCB process go inside the TCB, because
+ * load-time integrity of non-TCB processes cannot be determined.
+ */
+ if ((!old_ctx || !(old_ctx->flags & CTX_FLAG_TCB)) &&
+ new_ctx && (new_ctx->flags & CTX_FLAG_TCB))
+ return -EPERM;
+
+ return 0;
+}
+
+static int ipc_has_perm(const struct cred *cred, u32 sid,
+ enum infoflow_class oclass,
+ struct kern_ipc_perm *ipc_perms, int mask)
+{
+ struct common_audit_data ad;
+ u32 ssid, isid;
+
+ if (cred) {
+ validate_creds(cred);
+ security_cred_getsecid(cred, &ssid);
+ } else {
+ ssid = sid;
+ }
+
+ security_ipc_getsecid(ipc_perms, &isid);
+
+ ad.type = LSM_AUDIT_DATA_IPC;
+ ad.u.ipc_id = ipc_perms->key;
+
+ return infoflow_allow_access(CLASS_PROCESS, ssid, oclass, isid, NULL,
+ mask, &ad);
+}
+
+static int infoflow_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ const struct cred *cred = current_cred();
+ int mask = 0;
+
+ if (flag & S_IRUGO)
+ mask |= MAY_READ;
+ if (flag & S_IWUGO)
+ mask |= MAY_WRITE;
+
+ if (mask == 0)
+ return 0;
+
+ return ipc_has_perm(cred, 0, CLASS_IPC, ipcp, mask);
+}
+
+static int infoflow_msg_queue_msgsnd(struct kern_ipc_perm *msq,
+ struct msg_msg *msg, int msqflg)
+{
+ const struct cred *cred = current_cred();
+
+ return ipc_has_perm(cred, 0, CLASS_MSGQ, msq, MAY_WRITE);
+}
+
+static int infoflow_msg_queue_msgrcv(struct kern_ipc_perm *msq,
+ struct msg_msg *msg,
+ struct task_struct *target,
+ long type, int mode)
+{
+ u32 ssid;
+
+ security_task_getsecid(target, &ssid);
+
+ return ipc_has_perm(NULL, ssid, CLASS_MSGQ, msq, MAY_READ);
+}
+
+static int infoflow_shm_shmat(struct kern_ipc_perm *shp,
+ char __user *shmaddr, int shmflg)
+{
+ const struct cred *cred = current_cred();
+ int mask;
+
+ if (shmflg & SHM_RDONLY)
+ mask = MAY_READ;
+ else
+ mask = MAY_READ | MAY_WRITE;
+
+ return ipc_has_perm(cred, 0, CLASS_SHM, shp, mask);
+}
+
+/* Note, at this point, sma is locked down */
+static int infoflow_sem_semctl(struct kern_ipc_perm *sma, int cmd)
+{
+ const struct cred *cred = current_cred();
+ int mask;
+
+ switch (cmd) {
+ case GETVAL:
+ case GETALL:
+ mask = MAY_READ;
+ break;
+ case SETVAL:
+ case SETALL:
+ mask = MAY_WRITE;
+ break;
+ default:
+ return 0;
+ }
+
+ return ipc_has_perm(cred, 0, CLASS_SEM, sma, mask);
+}
+
+static int infoflow_sem_semop(struct kern_ipc_perm *sma,
+ struct sembuf *sops, unsigned nsops, int alter)
+{
+ const struct cred *cred = current_cred();
+ int mask;
+
+ if (alter)
+ mask = MAY_READ | MAY_WRITE;
+ else
+ mask = MAY_READ;
+
+ return ipc_has_perm(cred, 0, CLASS_SEM, sma, mask);
+}
+
+static int infoflow_socket_unix_may_send(struct socket *sock,
+ struct socket *other)
+{
+ struct common_audit_data ad;
+ struct lsm_network_audit net = {0,};
+ struct flowi fl_sock, fl_other;
+
+ ad.type = LSM_AUDIT_DATA_NET;
+ ad.u.net = &net;
+ ad.u.net->sk = other->sk;
+
+ security_sk_classify_flow(sock->sk, &fl_sock);
+ security_sk_classify_flow(other->sk, &fl_other);
+
+ return infoflow_allow_access(CLASS_PROCESS, fl_sock.flowi_secid,
+ CLASS_SOCKET, fl_other.flowi_secid,
+ NULL, MAY_WRITE, &ad);
+}
+
+static int sock_has_perm(struct sock *sk, int mask)
+{
+ const struct cred *cred = current_cred();
+ struct common_audit_data ad;
+ struct lsm_network_audit net = {0,};
+ struct flowi fl_sk;
+ u32 ssid;
+
+ ad.type = LSM_AUDIT_DATA_NET;
+ ad.u.net = &net;
+ ad.u.net->sk = sk;
+
+ validate_creds(cred);
+ security_cred_getsecid(cred, &ssid);
+
+ security_sk_classify_flow(sk, &fl_sk);
+
+ return infoflow_allow_access(CLASS_PROCESS, ssid, CLASS_SOCKET,
+ fl_sk.flowi_secid, NULL, mask, &ad);
+}
+
+static int infoflow_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+ int size)
+{
+ return sock_has_perm(sock->sk, MAY_WRITE);
+}
+
+static int infoflow_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+ int size, int flags)
+{
+ return sock_has_perm(sock->sk, MAY_READ);
+}
+
+static int infoflow_audit_rule_init(u32 field, u32 op, char *rulestr,
+ void **vrule)
+{
+ if (field != AUDIT_SUBJ_USER)
+ return -EINVAL;
+
+ if (op != Audit_equal && op != Audit_not_equal)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int infoflow_audit_rule_known(struct audit_krule *krule)
+{
+ struct audit_field *f;
+ int i;
+
+ for (i = 0; i < krule->field_count; i++) {
+ f = &krule->fields[i];
+
+ if (f->type == AUDIT_SUBJ_USER)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int infoflow_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
+{
+ struct infoflow_ctx *ctx;
+
+ if (field != AUDIT_SUBJ_USER)
+ return 0;
+
+ if (!(infoflow_init_flags & INFOFLOW_PARENT_LSM_INIT))
+ return (op == Audit_equal) ? true : false;
+
+ ctx = infoflow_ctx_find_sid(CLASS_PROCESS, sid);
+
+ if (op == Audit_equal)
+ return (ctx && (ctx->flags & CTX_FLAG_TCB));
+ if (op == Audit_not_equal)
+ return (!ctx || !(ctx->flags & CTX_FLAG_TCB));
+
+ return 0;
+}
+
+struct lsm_blob_sizes infoflow_blob_sizes = {
+ .lbs_inode = sizeof(u8),
+ .lbs_file = sizeof(struct file_security_struct),
+};
+
+static struct security_hook_list infoflow_hooks[] __lsm_ro_after_init = {
+ LSM_HOOK_INIT(inode_init_security, infoflow_inode_init_security),
+
+ LSM_HOOK_INIT(d_instantiate, infoflow_d_instantiate),
+
+ LSM_HOOK_INIT(inode_permission, infoflow_inode_permission),
+ LSM_HOOK_INIT(inode_setxattr, infoflow_inode_setxattr),
+ LSM_HOOK_INIT(inode_post_setxattr, infoflow_inode_post_setxattr),
+ LSM_HOOK_INIT(inode_removexattr, infoflow_inode_removexattr),
+ LSM_HOOK_INIT(inode_setsecurity, infoflow_inode_setsecurity),
+
+ LSM_HOOK_INIT(file_permission, infoflow_file_permission),
+ LSM_HOOK_INIT(file_receive, infoflow_file_receive),
+ LSM_HOOK_INIT(file_open, infoflow_file_open),
+
+ LSM_HOOK_INIT(kernel_read_file, infoflow_kernel_read_file),
+ LSM_HOOK_INIT(kernel_load_data, infoflow_kernel_load_data),
+
+ LSM_HOOK_INIT(setprocattr, infoflow_setprocattr),
+
+ LSM_HOOK_INIT(ipc_permission, infoflow_ipc_permission),
+
+ LSM_HOOK_INIT(msg_queue_msgsnd, infoflow_msg_queue_msgsnd),
+ LSM_HOOK_INIT(msg_queue_msgrcv, infoflow_msg_queue_msgrcv),
+
+ LSM_HOOK_INIT(shm_shmat, infoflow_shm_shmat),
+
+ LSM_HOOK_INIT(sem_semctl, infoflow_sem_semctl),
+ LSM_HOOK_INIT(sem_semop, infoflow_sem_semop),
+
+ LSM_HOOK_INIT(unix_may_send, infoflow_socket_unix_may_send),
+ LSM_HOOK_INIT(socket_sendmsg, infoflow_socket_sendmsg),
+ LSM_HOOK_INIT(socket_recvmsg, infoflow_socket_recvmsg),
+
+#ifdef CONFIG_AUDIT
+ LSM_HOOK_INIT(audit_rule_init, infoflow_audit_rule_init),
+ LSM_HOOK_INIT(audit_rule_known, infoflow_audit_rule_known),
+ LSM_HOOK_INIT(audit_rule_match, infoflow_audit_rule_match),
+#endif
+
+};
+
+static __init int infoflow_init(void)
+{
+ int rc;
+
+ if (!infoflow_enabled)
+ return 0;
+
+ rc = register_blocking_lsm_notifier(&infoflow_lsm_nb);
+ if (rc) {
+ pr_warn("Couldn't register LSM notifier. rc %d\n", rc);
+ return rc;
+ }
+
+ security_add_hooks(infoflow_hooks, ARRAY_SIZE(infoflow_hooks),
+ "infoflow");
+ return 0;
+}
+
+DEFINE_LSM(infoflow) = {
+ .name = "infoflow",
+ .flags = LSM_FLAG_LEGACY_MAJOR,
+ .blobs = &infoflow_blob_sizes,
+ .init = infoflow_init,
+};
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index fbe0bb5cd7c4..0a54e488409c 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -51,6 +51,7 @@ static struct xattr_list evm_config_default_xattrnames[] = {
#ifdef CONFIG_IMA_APPRAISE
{.name = XATTR_NAME_IMA},
#endif
+ {.name = XATTR_NAME_INFOFLOW},
{.name = XATTR_NAME_CAPS},
};
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Thomas Gleixner @ 2019-08-19 9:15 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Jordan Glover, Andy Lutomirski, Daniel Colascione, Song Liu,
Kees Cook, Networking, bpf, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Lorenz Bauer, Jann Horn, Greg KH, Linux API,
LSM List
In-Reply-To: <20190817150245.xxzxqjpvgqsxmloe@ast-mbp>
Alexei,
On Sat, 17 Aug 2019, Alexei Starovoitov wrote:
> On Fri, Aug 16, 2019 at 10:28:29PM +0200, Thomas Gleixner wrote:
> > On Fri, 16 Aug 2019, Alexei Starovoitov wrote:
> > While real usecases are helpful to understand a design decision, the design
> > needs to be usecase independent.
> >
> > The kernel provides mechanisms, not policies. My impression of this whole
> > discussion is that it is policy driven. That's the wrong approach.
>
> not sure what you mean by 'policy driven'.
> Proposed CAP_BPF is a policy?
I was referring to the discussion as a whole.
> Can kernel.unprivileged_bpf_disabled=1 be used now?
> Yes, but it will weaken overall system security because things that
> use unpriv to load bpf and CAP_NET_ADMIN to attach bpf would need
> to move to stronger CAP_SYS_ADMIN.
>
> With CAP_BPF both load and attach would happen under CAP_BPF
> instead of CAP_SYS_ADMIN.
I'm not arguing against that.
> > So let's look at the mechanisms which we have at hand:
> >
> > 1) Capabilities
> >
> > 2) SUID and dropping priviledges
> >
> > 3) Seccomp and LSM
> >
> > Now the real interesting questions are:
> >
> > A) What kind of restrictions does BPF allow? Is it a binary on/off or is
> > there a more finegrained control of BPF functionality?
> >
> > TBH, I can't tell.
> >
> > B) Depending on the answer to #A what is the control possibility for
> > #1/#2/#3 ?
>
> Can any of the mechanisms 1/2/3 address the concern in mds.rst?
Well, that depends. As with any other security policy which is implemented
via these mechanisms, the policy can be strict enough to prevent it by not
allowing certain operations. The more fine-grained the control is, it
allows the administrator who implements the policy to remove the
'dangerous' parts from an untrusted user.
So really question #A is important for this. Is BPF just providing a binary
ON/OFF knob or does it allow to disable/enable certain aspects of BPF
functionality in a more fine grained way? If the latter, then it might be
possible to control functionality which might be abused for exploits of
some sorts (including MDS) in a way which allows other parts of BBF to be
exposed to less priviledged contexts.
> I believe Andy wants to expand the attack surface when
> kernel.unprivileged_bpf_disabled=0
> Before that happens I'd like the community to work on addressing the text above.
Well, that text above can be removed when the BPF wizards are entirely sure
that BPF cannot be abused to exploit stuff.
Thanks,
tglx
^ permalink raw reply
* Re: [WIP][RFC][PATCH 1/3] security: introduce call_int_hook_and() macro
From: Casey Schaufler @ 2019-08-19 14:52 UTC (permalink / raw)
To: Roberto Sassu, linux-integrity
Cc: linux-security-module, zohar, dmitry.kasatkin, silviu.vlasceanu
In-Reply-To: <20190818235745.1417-2-roberto.sassu@huawei.com>
On 8/18/2019 4:57 PM, Roberto Sassu wrote:
> The LSM hooks audit_rule_known() and audit_rule_match() define 1 as
> result for successful operation. However, the security_ functions use
> call_int_hook() which stops iterating over LSMs if the result is not
> zero.
>
> Introduce call_int_hook_and(), so that the final result returned by the
> security_ functions is 1 if all LSMs return 1.
I don't think this is what you want. You want an audit record
generated if any of the security modules want one, not only if
all of the security modules want one.
>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
> security/security.c | 19 +++++++++++++++++--
> 1 file changed, 17 insertions(+), 2 deletions(-)
>
> diff --git a/security/security.c b/security/security.c
> index cbee0b7915d5..ff1736ee3410 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -634,6 +634,20 @@ static void __init lsm_early_task(struct task_struct *task)
> RC; \
> })
>
> +#define call_int_hook_and(FUNC, IRC, ...) ({ \
> + int RC = IRC; \
> + do { \
> + struct security_hook_list *P; \
> + \
> + hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
> + RC = P->hook.FUNC(__VA_ARGS__); \
> + if (!RC) \
> + break; \
> + } \
> + } while (0); \
> + RC; \
> +})
> +
> /* Security operations */
>
> int security_binder_set_context_mgr(struct task_struct *mgr)
> @@ -2339,7 +2353,7 @@ int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
>
> int security_audit_rule_known(struct audit_krule *krule)
> {
> - return call_int_hook(audit_rule_known, 0, krule);
> + return call_int_hook_and(audit_rule_known, 0, krule);
> }
>
> void security_audit_rule_free(void *lsmrule)
> @@ -2349,7 +2363,8 @@ void security_audit_rule_free(void *lsmrule)
>
> int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
> {
> - return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule);
> + return call_int_hook_and(audit_rule_match, 0, secid, field, op,
> + lsmrule);
> }
> #endif /* CONFIG_AUDIT */
>
^ permalink raw reply
* [PATCH] keys: Fix description size
From: David Howells @ 2019-08-19 15:02 UTC (permalink / raw)
To: torvalds
Cc: kernel test robot, dhowells, keyrings, linux-security-module,
linux-kernel
The maximum key description size is 4095. Commit f771fde82051
inadvertantly reduced that to 255 and made sizes between 256 and 4095 work
weirdly, and any size whereby size & 255 == 0 would cause an assertion in
__key_link_begin() at the following line:
BUG_ON(index_key->desc_len == 0);
This can be fixed by simply increasing the size of desc_len in struct
keyring_index_key to a u16.
Note the argument length test in keyutils only checked empty descriptions
and descriptions with a size around the limit (ie. 4095) and not for all
the values in between, so it missed this. This has been addressed and
https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/keyutils.git/commit/?id=066bf56807c26cd3045a25f355b34c1d8a20a5aa
now exhaustively tests all possible lengths of type, description and
payload and then some.
The assertion failure looks something like:
kernel BUG at security/keys/keyring.c:1245!
...
RIP: 0010:__key_link_begin+0x88/0xa0
...
Call Trace:
key_create_or_update+0x211/0x4b0
__x64_sys_add_key+0x101/0x200
do_syscall_64+0x5b/0x1e0
entry_SYSCALL_64_after_hwframe+0x44/0xa9
It can be triggered by:
keyctl add user "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" a @s
Fixes: f771fde82051 ("keys: Simplify key description management")
Reported-by: kernel test robot <rong.a.chen@intel.com>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Linus Torvalds <torvalds@linux-foundation.org>
---
include/linux/key.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/linux/key.h b/include/linux/key.h
index 91f391cd272e..50028338a4cc 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -94,11 +94,11 @@ struct keyring_index_key {
union {
struct {
#ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */
- u8 desc_len;
- char desc[sizeof(long) - 1]; /* First few chars of description */
+ u16 desc_len;
+ char desc[sizeof(long) - 2]; /* First few chars of description */
#else
- char desc[sizeof(long) - 1]; /* First few chars of description */
- u8 desc_len;
+ char desc[sizeof(long) - 2]; /* First few chars of description */
+ u16 desc_len;
#endif
};
unsigned long x;
^ permalink raw reply related
* Re: [PATCH] keys: Fix description size
From: Linus Torvalds @ 2019-08-19 16:44 UTC (permalink / raw)
To: David Howells
Cc: kernel test robot, keyrings, LSM List, Linux List Kernel Mailing
In-Reply-To: <156622692131.21558.12335114959426121841.stgit@warthog.procyon.org.uk>
On Mon, Aug 19, 2019 at 8:02 AM David Howells <dhowells@redhat.com> wrote:
>
> This can be fixed by simply increasing the size of desc_len in struct
> keyring_index_key to a u16.
Thanks, applied.
Linus
^ permalink raw reply
* Re: [RFC/RFT v4 0/5] Add generic trusted keys framework/subsystem
From: Jarkko Sakkinen @ 2019-08-19 16:54 UTC (permalink / raw)
To: Sumit Garg
Cc: keyrings, linux-integrity, linux-crypto, linux-security-module,
dhowells, herbert, davem, peterhuewe, jgg, jejb, arnd, gregkh,
zohar, jmorris, serge, casey, ard.biesheuvel, daniel.thompson,
linux-kernel, tee-dev
In-Reply-To: <1565682784-10234-1-git-send-email-sumit.garg@linaro.org>
On Tue, Aug 13, 2019 at 01:22:59PM +0530, Sumit Garg wrote:
> This patch-set is an outcome of discussion here [1]. It has evolved very
> much since v1 to create, consolidate and generalize trusted keys
> subsystem.
>
> This framework has been tested with trusted keys support provided via TEE
> but I wasn't able to test it with a TPM device as I don't possess one. It
> would be really helpful if others could test this patch-set using a TPM
> device.
I think 1/5-4/5 make up a non-RFC patch set that needs to reviewed,
tested and merged as a separate entity.
On the other hand 5/5 cannot be merged even if I fully agreed on
the code change as without TEE patch it does not add any value for
Linux.
To straighten up thing I would suggest that the next patch set
version would only consists of the first four patches and we meld
them to the shape so that we can land them to the mainline. Then
it should be way more easier to concentrate the actual problem you
are trying to resolve.
/Jarkko
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox