diff mbox series

[v2,2/2] selinux: Implement mptcp_add_subflow hook

Message ID 3074022fdca04676443a9c74f57328eb729f150e.1671469167.git.pabeni@redhat.com (mailing list archive)
State Changes Requested
Delegated to: Paul Moore
Headers show
Series lsm: introduce and use security_mptcp_add_subflow() | expand

Commit Message

Paolo Abeni Dec. 19, 2022, 5:33 p.m. UTC
Newly added subflows should inherit the associated label
from the current process context, regarless of the sk_kern_sock
flag value.

This patch implements the above resetting the subflow sid, deleting
the existing subflow label, if any, and then re-creating a new one.

The new helper reuses the selinux_netlbl_sk_security_free() function,
and it can end-up being called multiple times with the same argument;
we additionally need to make it idempotent.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
v1 -> v2:
 - fix build issue with !CONFIG_NETLABEL
---
 security/selinux/hooks.c    | 27 +++++++++++++++++++++++++++
 security/selinux/netlabel.c |  4 +++-
 2 files changed, 30 insertions(+), 1 deletion(-)

Comments

Paul Moore Dec. 20, 2022, 10:07 p.m. UTC | #1
On Mon, Dec 19, 2022 at 12:34 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> Newly added subflows should inherit the associated label
> from the current process context, regarless of the sk_kern_sock
> flag value.
>
> This patch implements the above resetting the subflow sid, deleting
> the existing subflow label, if any, and then re-creating a new one.
>
> The new helper reuses the selinux_netlbl_sk_security_free() function,
> and it can end-up being called multiple times with the same argument;
> we additionally need to make it idempotent.
>
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
> v1 -> v2:
>  - fix build issue with !CONFIG_NETLABEL
> ---
>  security/selinux/hooks.c    | 27 +++++++++++++++++++++++++++
>  security/selinux/netlabel.c |  4 +++-
>  2 files changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 3c5be76a9199..f785600b666a 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -5476,6 +5476,32 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk
>         selinux_netlbl_sctp_sk_clone(sk, newsk);
>  }
>
> +static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> +{
> +       const struct task_security_struct *tsec = selinux_cred(current_cred());
> +       struct sk_security_struct *ssksec = ssk->sk_security;
> +       u16 sclass;
> +       u32 sid;
> +       int err;
> +
> +       /* create the sid using the current cred, regardless of the ssk kern
> +        * flag
> +        */
> +       sclass = socket_type_to_security_class(ssk->sk_family, ssk->sk_type,
> +                                              ssk->sk_protocol);
> +       err = socket_sockcreate_sid(tsec, sclass, &sid);
> +       if (err)
> +               return err;
> +
> +       ssksec->sid = sid;
> +
> +       /* replace the existing subflow label deleting the existing one
> +        * and re-recrating a new label using the current context
> +        */
> +       selinux_netlbl_sk_security_free(ssksec);
> +       return selinux_netlbl_socket_post_create(ssk, ssk->sk_family);
> +}

I thought the idea was to ensure that new subflows of an existing
MPTCP connection would be created with the same label as the main
MPTCP connection socket?  The code above labels the new subflow based
on the current process, not the main MPTCP connection; it matches the
commit description, but not what we had previously discussed - or I am
horribly mis-remembering something? :)

I was expecting something more like the following:

static int selinux_mptcp_add_subflow(...)
{
  struct sk_security_struct *sksec = sk->sk_security;
  struct sk_security_struct *ssksec = ssk->sk_security;

  ssksec->sclass = sksec->sclass;
  ssksec->sid = sksec->sid;

  selinux_netlbl_sk_security_free(ssksec);
  selinux_netlbl_socket_post_create(ssk, ssk->sk_family);
}

> diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
> index 1321f15799e2..8e0080b8a8ef 100644
> --- a/security/selinux/netlabel.c
> +++ b/security/selinux/netlabel.c
> @@ -155,8 +155,10 @@ void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
>   */
>  void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
>  {
> -       if (sksec->nlbl_secattr != NULL)
> +       if (sksec->nlbl_secattr != NULL) {
>                 netlbl_secattr_free(sksec->nlbl_secattr);
> +               sksec->nlbl_secattr = NULL;
> +       }
>  }

This is pretty nitpicky, but it might be a little cleaner to use the
pattern below.  At the very least I think it tends to better match a
lot of the various free helpers in the kernel.

void free_stuff(void *ptr)
{
  if (!ptr)
    return;
  free_properly(ptr);
}

I would probably also reset sk_security_struct::nlbl_state too, so
maybe something like the following:

void selinux_netlbl_sk_security_free(...)
{
  if (!sksec)
    return;
  netlbl_secattr_free(sksec->nlbl_secattr);
  sksec->nlbl_secattr = NULL;
  sksec->nlbl_state = NLBL_UNSET;
}
Paolo Abeni Dec. 21, 2022, 7:23 p.m. UTC | #2
On Tue, 2022-12-20 at 17:07 -0500, Paul Moore wrote:
> On Mon, Dec 19, 2022 at 12:34 PM Paolo Abeni <pabeni@redhat.com> wrote:
> > 
> > Newly added subflows should inherit the associated label
> > from the current process context, regarless of the sk_kern_sock
> > flag value.
> > 
> > This patch implements the above resetting the subflow sid, deleting
> > the existing subflow label, if any, and then re-creating a new one.
> > 
> > The new helper reuses the selinux_netlbl_sk_security_free() function,
> > and it can end-up being called multiple times with the same argument;
> > we additionally need to make it idempotent.
> > 
> > Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> > ---
> > v1 -> v2:
> >  - fix build issue with !CONFIG_NETLABEL
> > ---
> >  security/selinux/hooks.c    | 27 +++++++++++++++++++++++++++
> >  security/selinux/netlabel.c |  4 +++-
> >  2 files changed, 30 insertions(+), 1 deletion(-)
> > 
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index 3c5be76a9199..f785600b666a 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -5476,6 +5476,32 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk
> >         selinux_netlbl_sctp_sk_clone(sk, newsk);
> >  }
> > 
> > +static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> > +{
> > +       const struct task_security_struct *tsec = selinux_cred(current_cred());
> > +       struct sk_security_struct *ssksec = ssk->sk_security;
> > +       u16 sclass;
> > +       u32 sid;
> > +       int err;
> > +
> > +       /* create the sid using the current cred, regardless of the ssk kern
> > +        * flag
> > +        */
> > +       sclass = socket_type_to_security_class(ssk->sk_family, ssk->sk_type,
> > +                                              ssk->sk_protocol);
> > +       err = socket_sockcreate_sid(tsec, sclass, &sid);
> > +       if (err)
> > +               return err;
> > +
> > +       ssksec->sid = sid;
> > +
> > +       /* replace the existing subflow label deleting the existing one
> > +        * and re-recrating a new label using the current context
> > +        */
> > +       selinux_netlbl_sk_security_free(ssksec);
> > +       return selinux_netlbl_socket_post_create(ssk, ssk->sk_family);
> > +}
> 
> I thought the idea was to ensure that new subflows of an existing
> MPTCP connection would be created with the same label as the main
> MPTCP connection socket?  The code above labels the new subflow based
> on the current process, not the main MPTCP connection; it matches the
> commit description, but not what we had previously discussed - or I am
> horribly mis-remembering something? :)

You are right, I picked a wrong turn.

I just tested the other option and there is another problem :(

The first subflow creations happens inside af_inet->create, via the sk-
>sk_prot->init() hook. The security_socket_post_create() call on the
owning MPTCP sockets happens after that point. So we copy data from a
not yet initialized security context (and the test fail badly).

There are a few options to cope with that:
- [ugly hack] call  security_socket_post_create() on the mptcp code
before creating the subflow. I experimented this just to double the
problem and a possible solution.

- refactor the mptcp code to create the first subflow on later
syscalls, as needed. This will require quite a bit of refactoring in
the MPTCP protocol as we will need also to update the
shutdown/disconnect accordingly (currently we keep the first subflow
around, instead we will need to close it).

- use the code proposed in these patches as-is ;) 

WDYT?

Thanks,

Paolo
Paul Moore Dec. 22, 2022, 1:21 a.m. UTC | #3
On Wed, Dec 21, 2022 at 2:24 PM Paolo Abeni <pabeni@redhat.com> wrote:
> On Tue, 2022-12-20 at 17:07 -0500, Paul Moore wrote:
> > On Mon, Dec 19, 2022 at 12:34 PM Paolo Abeni <pabeni@redhat.com> wrote:
> > >
> > > Newly added subflows should inherit the associated label
> > > from the current process context, regarless of the sk_kern_sock
> > > flag value.
> > >
> > > This patch implements the above resetting the subflow sid, deleting
> > > the existing subflow label, if any, and then re-creating a new one.
> > >
> > > The new helper reuses the selinux_netlbl_sk_security_free() function,
> > > and it can end-up being called multiple times with the same argument;
> > > we additionally need to make it idempotent.
> > >
> > > Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> > > ---
> > > v1 -> v2:
> > >  - fix build issue with !CONFIG_NETLABEL
> > > ---
> > >  security/selinux/hooks.c    | 27 +++++++++++++++++++++++++++
> > >  security/selinux/netlabel.c |  4 +++-
> > >  2 files changed, 30 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > index 3c5be76a9199..f785600b666a 100644
> > > --- a/security/selinux/hooks.c
> > > +++ b/security/selinux/hooks.c
> > > @@ -5476,6 +5476,32 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk
> > >         selinux_netlbl_sctp_sk_clone(sk, newsk);
> > >  }
> > >
> > > +static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> > > +{
> > > +       const struct task_security_struct *tsec = selinux_cred(current_cred());
> > > +       struct sk_security_struct *ssksec = ssk->sk_security;
> > > +       u16 sclass;
> > > +       u32 sid;
> > > +       int err;
> > > +
> > > +       /* create the sid using the current cred, regardless of the ssk kern
> > > +        * flag
> > > +        */
> > > +       sclass = socket_type_to_security_class(ssk->sk_family, ssk->sk_type,
> > > +                                              ssk->sk_protocol);
> > > +       err = socket_sockcreate_sid(tsec, sclass, &sid);
> > > +       if (err)
> > > +               return err;
> > > +
> > > +       ssksec->sid = sid;
> > > +
> > > +       /* replace the existing subflow label deleting the existing one
> > > +        * and re-recrating a new label using the current context
> > > +        */
> > > +       selinux_netlbl_sk_security_free(ssksec);
> > > +       return selinux_netlbl_socket_post_create(ssk, ssk->sk_family);
> > > +}
> >
> > I thought the idea was to ensure that new subflows of an existing
> > MPTCP connection would be created with the same label as the main
> > MPTCP connection socket?  The code above labels the new subflow based
> > on the current process, not the main MPTCP connection; it matches the
> > commit description, but not what we had previously discussed - or I am
> > horribly mis-remembering something? :)
>
> You are right, I picked a wrong turn.
>
> I just tested the other option and there is another problem :(

It's never easy, is it? ;)

> The first subflow creations happens inside af_inet->create, via the sk-
> >sk_prot->init() hook. The security_socket_post_create() call on the
> owning MPTCP sockets happens after that point. So we copy data from a
> not yet initialized security context (and the test fail badly).

Hmmm.  Let's come back to this later on down this email.

> There are a few options to cope with that:
> - [ugly hack] call  security_socket_post_create() on the mptcp code
> before creating the subflow. I experimented this just to double the
> problem and a possible solution.

I'm guessing "[ugly hack]" is probably a bit of an understatement.
Let's see if we can do better before we explore this option too much
further.

> - refactor the mptcp code to create the first subflow on later
> syscalls, as needed. This will require quite a bit of refactoring in
> the MPTCP protocol as we will need also to update the
> shutdown/disconnect accordingly (currently we keep the first subflow
> around, instead we will need to close it).

Let's see if we can avoid having to do this too.

> - use the code proposed in these patches as-is ;)

That's a non-starter from a SELinux perspective as the label is
incorrect.  It would be similar to saying that for each fork() call
the resulting child process would always run as root since we couldn't
figure out how to assign the credentials properly :)

> WDYT?

Let's go back to the the inet_create() case for a little bit.  I'm
thinking we might be able to do something by leveraging the
sk_alloc()->sk_prot_alloc()->security_sk_alloc() code path.  As
inet_create() is going to be called from task context here, it seems
like we could do the sock's sid/sclass determination here, cached in
separate fields in the sk_security_struct if necessary, and use those
in a new MPTCP subflow hook.  We could also update
selinux_socket_post_create() to take advantage of this as well.  We
could also possibly pass the proto struct into security_sk_alloc() if
we needed to identify IPPROTO_MPTCP there as well.

I'll admit to not chasing down all the details, but I suspect this may
be the cleanest option - thoughts?

--
paul-moore.com
Paolo Abeni Dec. 22, 2022, 3:57 p.m. UTC | #4
On Wed, 2022-12-21 at 20:21 -0500, Paul Moore wrote:
> On Wed, Dec 21, 2022 at 2:24 PM Paolo Abeni <pabeni@redhat.com> wrote:
> > I just tested the other option and there is another problem :(
> 
> It's never easy, is it? ;)
> 
> > The first subflow creations happens inside af_inet->create, via the sk-
> > > sk_prot->init() hook. The security_socket_post_create() call on the
> > owning MPTCP sockets happens after that point. So we copy data from a
> > not yet initialized security context (and the test fail badly).
> 
> Hmmm.  Let's come back to this later on down this email.
> 
> > There are a few options to cope with that:
> > - [ugly hack] call  security_socket_post_create() on the mptcp code
> > before creating the subflow. I experimented this just to double the
> > problem and a possible solution.
> 
> I'm guessing "[ugly hack]" is probably a bit of an understatement.
> Let's see if we can do better before we explore this option too much
> further.

Yup, I compiled the list in "brainstom-mode", trying to include
whatever would be possible even if clearly not suitable. 

[...]

> > WDYT?
> 
> Let's go back to the the inet_create() case for a little bit.  I'm
> thinking we might be able to do something by leveraging the
> sk_alloc()->sk_prot_alloc()->security_sk_alloc() code path.  As
> inet_create() is going to be called from task context here, it seems
> like we could do the sock's sid/sclass determination here, cached in
> separate fields in the sk_security_struct if necessary, and use those
> in a new MPTCP subflow hook.  We could also update
> selinux_socket_post_create() to take advantage of this as well.  We
> could also possibly pass the proto struct into security_sk_alloc() if
> we needed to identify IPPROTO_MPTCP there as well.
> 
> I'll admit to not chasing down all the details, but I suspect this may
> be the cleanest option - thoughts?

Thanks, I did not consider such possibility!

I think we should be careful to avoid increasing sk_security_struct
size. Currently it is 16 bytes, nicely matching a kmalloc slab, any
increase will move it on kmalloc-32 bytes slab possibly causing
performance and memory regressions).

More importantly, I think there is a problem with the 
sk_clone_lock() -> sk_prot_alloc() -> security_sk_alloc()
code path. 

sk_clone_lock() happens in BH context, if security_transition_sid()
needs process context that would be a problem - quickly skimming the
code it does not look so, I need to double check.

sk_clone_lock() is in a very critical path - socket creation for
incoming connections. The sid-related operation there will be
unnecessary/discarded by later the selinux_inet_csk_clone(), this will
likelly cause performance regressions even for plain TCP sockets.

Perhaps the cleanest option could be the one involving the mptcp
refactoring, moving subflow creation at a later stage. It could have
some minor side benefit for MPTCP, too - solving:

https://github.com/multipath-tcp/mptcp_net-next/issues/290

but I'm not fond of that option because it will require quite a bit of
time: we need first to have the mptcp refactor in place and then cook
the lsm patches. I guess such process will require at least 2 release
cycles, due to the needed mptcp(netdev)/lsm trees synchronization.

If that would prove to be the most reasonable option, could we consider
to transiently merge first something alike:

https://lore.kernel.org/mptcp/CAHC9VhSQnhH3UL4gqzu+YiA1Q3YyLLCv88gLJOvw-0+uw5Lvkw@mail.gmail.com/T/#m06c612f84f6b6fe759e670573b2c8092df71607b

to have a workable short-term solution, and later revert it when the
final solution would be in place?

Thanks,

Paolo
Paul Moore Dec. 23, 2022, 5:11 p.m. UTC | #5
On Thu, Dec 22, 2022 at 10:57 AM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On Wed, 2022-12-21 at 20:21 -0500, Paul Moore wrote:
> > On Wed, Dec 21, 2022 at 2:24 PM Paolo Abeni <pabeni@redhat.com> wrote:
> > > I just tested the other option and there is another problem :(
> >
> > It's never easy, is it? ;)
> >
> > > The first subflow creations happens inside af_inet->create, via the sk-
> > > > sk_prot->init() hook. The security_socket_post_create() call on the
> > > owning MPTCP sockets happens after that point. So we copy data from a
> > > not yet initialized security context (and the test fail badly).
> >
> > Hmmm.  Let's come back to this later on down this email.
> >
> > > There are a few options to cope with that:
> > > - [ugly hack] call  security_socket_post_create() on the mptcp code
> > > before creating the subflow. I experimented this just to double the
> > > problem and a possible solution.
> >
> > I'm guessing "[ugly hack]" is probably a bit of an understatement.
> > Let's see if we can do better before we explore this option too much
> > further.
>
> Yup, I compiled the list in "brainstom-mode", trying to include
> whatever would be possible even if clearly not suitable.
>
> [...]
>
> > > WDYT?
> >
> > Let's go back to the the inet_create() case for a little bit.  I'm
> > thinking we might be able to do something by leveraging the
> > sk_alloc()->sk_prot_alloc()->security_sk_alloc() code path.  As
> > inet_create() is going to be called from task context here, it seems
> > like we could do the sock's sid/sclass determination here, cached in
> > separate fields in the sk_security_struct if necessary, and use those
> > in a new MPTCP subflow hook.  We could also update
> > selinux_socket_post_create() to take advantage of this as well.  We
> > could also possibly pass the proto struct into security_sk_alloc() if
> > we needed to identify IPPROTO_MPTCP there as well.
> >
> > I'll admit to not chasing down all the details, but I suspect this may
> > be the cleanest option - thoughts?
>
> Thanks, I did not consider such possibility!
>
> I think we should be careful to avoid increasing sk_security_struct
> size. Currently it is 16 bytes, nicely matching a kmalloc slab, any
> increase will move it on kmalloc-32 bytes slab possibly causing
> performance and memory regressions).

FWIW, it is likely that this will end up growing in the future to
support stacking LSMs.  It's unfortunate, messy, and generally ugly,
but inevitable.

See the selinux_inode() function as a similar example using
inode/inode_security_struct.

> More importantly, I think there is a problem with the
> sk_clone_lock() -> sk_prot_alloc() -> security_sk_alloc()
> code path.
>
> sk_clone_lock() happens in BH context, if security_transition_sid()
> needs process context that would be a problem - quickly skimming the
> code it does not look so, I need to double check.

The problem is that in both selinux_socket_create() and
selinux_socket_post_create() the credentials from @current are needed
to determine the sock/sk_security_stuct label.  In
selinux_socket_create() @current's credentials are also used to
determine if the socket can be created.

It's looking like doing labeling determinations in the
security_sk_alloc() struct is not going to work.  While
sk_clone_lock() will end up calling into
security_sk_clone()/selinux_sk_clone_security() via sock_copy(), if I
understand you correctly that won't help as the main MPTCP socket is
not yet setup (e.g. selinux_socket_post_create() has not yet been run
on the main socket).

> Perhaps the cleanest option could be the one involving the mptcp
> refactoring, moving subflow creation at a later stage. It could have
> some minor side benefit for MPTCP, too - solving:
>
> https://github.com/multipath-tcp/mptcp_net-next/issues/290
>
> but I'm not fond of that option because it will require quite a bit of
> time: we need first to have the mptcp refactor in place and then cook
> the lsm patches. I guess such process will require at least 2 release
> cycles, due to the needed mptcp(netdev)/lsm trees synchronization.

I generally take the long term view when it comes to Linux Kernel
development; given the nature of the kernel, and all the constraints
that come with it, I would much rather pursue solutions that we
believe have the longest staying power.

I'm also happy to work on, and/or review, LSM patches in conjunction
with a MPTCP refactor.  If the only reason to split the work over two
kernel releases is to soften the blow during the merge window, I think
we can work that out in a single release ... at least I say that now
:)

Basically when it comes down to it, I want to make sure that any fix
we come up with *works*.  In my mind that means doing the LSM fix in
conjunction with the rework; I would hate to see all of the rework
happen only to find out later that it still didn't solve the LSM
problem.

Does that make sense?

> If that would prove to be the most reasonable option, could we consider
> to transiently merge first something alike:
>
> https://lore.kernel.org/mptcp/CAHC9VhSQnhH3UL4gqzu+YiA1Q3YyLLCv88gLJOvw-0+uw5Lvkw@mail.gmail.com/T/#m06c612f84f6b6fe759e670573b2c8092df71607b
>
> to have a workable short-term solution, and later revert it when the
> final solution would be in place?

I would need to go back through that to make sure that it makes sense,
and ensure that the hook is idempotent for SELinux, AppArmor, and
Smack (current hook implementations), *aaaand* if we promise that this
is just a *temporary* hack I think I would be okay with that.
Paolo Abeni Jan. 9, 2023, 10:31 a.m. UTC | #6
Hello,

I'm sorry for the long delay:  I was on PTO with limited internet
access.

On Fri, 2022-12-23 at 12:11 -0500, Paul Moore wrote:
> On Thu, Dec 22, 2022 at 10:57 AM Paolo Abeni <pabeni@redhat.com> wrote:
> > 
> > On Wed, 2022-12-21 at 20:21 -0500, Paul Moore wrote:
> > > On Wed, Dec 21, 2022 at 2:24 PM Paolo Abeni <pabeni@redhat.com> wrote:
> > > > I just tested the other option and there is another problem :(
> > > 
> > > It's never easy, is it? ;)
> > > 
> > > > The first subflow creations happens inside af_inet->create, via the sk-
> > > > > sk_prot->init() hook. The security_socket_post_create() call on the
> > > > owning MPTCP sockets happens after that point. So we copy data from a
> > > > not yet initialized security context (and the test fail badly).
> > > 
> > > Hmmm.  Let's come back to this later on down this email.
> > > 
> > > > There are a few options to cope with that:
> > > > - [ugly hack] call  security_socket_post_create() on the mptcp code
> > > > before creating the subflow. I experimented this just to double the
> > > > problem and a possible solution.
> > > 
> > > I'm guessing "[ugly hack]" is probably a bit of an understatement.
> > > Let's see if we can do better before we explore this option too much
> > > further.
> > 
> > Yup, I compiled the list in "brainstom-mode", trying to include
> > whatever would be possible even if clearly not suitable.
> > 
> > [...]
> > 
> > > > WDYT?
> > > 
> > > Let's go back to the the inet_create() case for a little bit.  I'm
> > > thinking we might be able to do something by leveraging the
> > > sk_alloc()->sk_prot_alloc()->security_sk_alloc() code path.  As
> > > inet_create() is going to be called from task context here, it seems
> > > like we could do the sock's sid/sclass determination here, cached in
> > > separate fields in the sk_security_struct if necessary, and use those
> > > in a new MPTCP subflow hook.  We could also update
> > > selinux_socket_post_create() to take advantage of this as well.  We
> > > could also possibly pass the proto struct into security_sk_alloc() if
> > > we needed to identify IPPROTO_MPTCP there as well.
> > > 
> > > I'll admit to not chasing down all the details, but I suspect this may
> > > be the cleanest option - thoughts?
> > 
> > Thanks, I did not consider such possibility!
> > 
> > I think we should be careful to avoid increasing sk_security_struct
> > size. Currently it is 16 bytes, nicely matching a kmalloc slab, any
> > increase will move it on kmalloc-32 bytes slab possibly causing
> > performance and memory regressions).
> 
> FWIW, it is likely that this will end up growing in the future to
> support stacking LSMs.  It's unfortunate, messy, and generally ugly,
> but inevitable.
> 
> See the selinux_inode() function as a similar example using
> inode/inode_security_struct.
> 
> > More importantly, I think there is a problem with the
> > sk_clone_lock() -> sk_prot_alloc() -> security_sk_alloc()
> > code path.
> > 
> > sk_clone_lock() happens in BH context, if security_transition_sid()
> > needs process context that would be a problem - quickly skimming the
> > code it does not look so, I need to double check.
> 
> The problem is that in both selinux_socket_create() and
> selinux_socket_post_create() the credentials from @current are needed
> to determine the sock/sk_security_stuct label.  In
> selinux_socket_create() @current's credentials are also used to
> determine if the socket can be created.
> 
> It's looking like doing labeling determinations in the
> security_sk_alloc() struct is not going to work.  While
> sk_clone_lock() will end up calling into
> security_sk_clone()/selinux_sk_clone_security() via sock_copy(), if I
> understand you correctly that won't help as the main MPTCP socket is
> not yet setup (e.g. selinux_socket_post_create() has not yet been run
> on the main socket).
> 
> > Perhaps the cleanest option could be the one involving the mptcp
> > refactoring, moving subflow creation at a later stage. It could have
> > some minor side benefit for MPTCP, too - solving:
> > 
> > https://github.com/multipath-tcp/mptcp_net-next/issues/290
> > 
> > but I'm not fond of that option because it will require quite a bit of
> > time: we need first to have the mptcp refactor in place and then cook
> > the lsm patches. I guess such process will require at least 2 release
> > cycles, due to the needed mptcp(netdev)/lsm trees synchronization.
> 
> I generally take the long term view when it comes to Linux Kernel
> development; given the nature of the kernel, and all the constraints
> that come with it, I would much rather pursue solutions that we
> believe have the longest staying power.
> 
> I'm also happy to work on, and/or review, LSM patches in conjunction
> with a MPTCP refactor.  If the only reason to split the work over two
> kernel releases is to soften the blow during the merge window, I think
> we can work that out in a single release ... at least I say that now
> :)

I thought about doing the MPTCP and selinux patches sequentially to
both avoid the possibly untrivial conflicts resultion issues and to
ensure that the mptcp patches are in place when the selinux ones are
applied, as there is a fuctional dependency. 

> Basically when it comes down to it, I want to make sure that any fix
> we come up with *works*.  In my mind that means doing the LSM fix in
> conjunction with the rework; I would hate to see all of the rework
> happen only to find out later that it still didn't solve the LSM
> problem.
> 
> Does that make sense?

Indeed it makes sense to me.

I think we can address that concern in a quite consolidated way. We
usually include in the MPTCP tree a (very limited) number of patches
that will not be submitted to the netdev because belong to other trees
and/or are handled/owned by others devel. 

We use the above e.g. to fix build and/or functional issues in our
self-tests caused by other subsystems without the need to wait for the
proper fix to land into vanilla. When such event happen, we simply drop
the local copy of the fixup patch.

We could use a similar schema in this scenario. We can include the the
LSM patches to the mptcp in our tree while the rework is in progress to
ensure that overall the effort really addresses the LSM issue.

We can rebase the LSM patches as needed to address conflicts as
needed/if/when they pops up.

Once that the mptcp patches will land into the LSM tree, we will submit
formally the LSM ones to you. During the process I'll check and ensure
that the LSM issue is really/still fixed. Would that work for you?

> > If that would prove to be the most reasonable option, could we consider
> > to transiently merge first something alike:
> > 
> > https://lore.kernel.org/mptcp/CAHC9VhSQnhH3UL4gqzu+YiA1Q3YyLLCv88gLJOvw-0+uw5Lvkw@mail.gmail.com/T/#m06c612f84f6b6fe759e670573b2c8092df71607b
> > 
> > to have a workable short-term solution, and later revert it when the
> > final solution would be in place?
> 
> I would need to go back through that to make sure that it makes sense,
> and ensure that the hook is idempotent for SELinux, AppArmor, and
> Smack (current hook implementations), *aaaand* if we promise that this
> is just a *temporary* hack I think I would be okay with that.

I would appreciate that addtional extra mile a lot, as it will allow a
(temporary!) fix to be delivered quite earlier than what the above
process will provide. 

Of course the deal is that I'll take ownership of pursuing the complete
fix till resolution.

Many thanks!

Paolo
Paul Moore Jan. 11, 2023, 11:17 p.m. UTC | #7
On Mon, Jan 9, 2023 at 5:31 AM Paolo Abeni <pabeni@redhat.com> wrote:
> Hello,
>
> I'm sorry for the long delay:  I was on PTO with limited internet
> access.

No worries, I hear even kernel developers are allowed to take time off
occasionally ... at least that's what they tell me ;)

(I hope you're enjoying the time away!)

> On Fri, 2022-12-23 at 12:11 -0500, Paul Moore wrote:
> > On Thu, Dec 22, 2022 at 10:57 AM Paolo Abeni <pabeni@redhat.com> wrote:
> > > On Wed, 2022-12-21 at 20:21 -0500, Paul Moore wrote:
> > > > On Wed, Dec 21, 2022 at 2:24 PM Paolo Abeni <pabeni@redhat.com> wrote:

...

> > I generally take the long term view when it comes to Linux Kernel
> > development; given the nature of the kernel, and all the constraints
> > that come with it, I would much rather pursue solutions that we
> > believe have the longest staying power.
> >
> > I'm also happy to work on, and/or review, LSM patches in conjunction
> > with a MPTCP refactor.  If the only reason to split the work over two
> > kernel releases is to soften the blow during the merge window, I think
> > we can work that out in a single release ... at least I say that now
> > :)
>
> I thought about doing the MPTCP and selinux patches sequentially to
> both avoid the possibly untrivial conflicts resultion issues and to
> ensure that the mptcp patches are in place when the selinux ones are
> applied, as there is a fuctional dependency.

Sure, when working across subsystems there is usually some sort of
dependency challenge which dictates which side is tackled first, I
just wanted to make sure we don't consider one side "done" until we
finish both sides.

> > Basically when it comes down to it, I want to make sure that any fix
> > we come up with *works*.  In my mind that means doing the LSM fix in
> > conjunction with the rework; I would hate to see all of the rework
> > happen only to find out later that it still didn't solve the LSM
> > problem.
> >
> > Does that make sense?
>
> Indeed it makes sense to me.
>
> I think we can address that concern in a quite consolidated way. We
> usually include in the MPTCP tree a (very limited) number of patches
> that will not be submitted to the netdev because belong to other trees
> and/or are handled/owned by others devel.
>
> We use the above e.g. to fix build and/or functional issues in our
> self-tests caused by other subsystems without the need to wait for the
> proper fix to land into vanilla. When such event happen, we simply drop
> the local copy of the fixup patch.
>
> We could use a similar schema in this scenario. We can include the the
> LSM patches to the mptcp in our tree while the rework is in progress to
> ensure that overall the effort really addresses the LSM issue.
>
> We can rebase the LSM patches as needed to address conflicts as
> needed/if/when they pops up.
>
> Once that the mptcp patches will land into the LSM tree, we will submit
> formally the LSM ones to you. During the process I'll check and ensure
> that the LSM issue is really/still fixed. Would that work for you?

Sure, I'm pretty flexible on how we do these things, I just want it to
end up fixed in Linus' tree and there are plenty of ways we can make
that happen :)

> > > If that would prove to be the most reasonable option, could we consider
> > > to transiently merge first something alike:
> > >
> > > https://lore.kernel.org/mptcp/CAHC9VhSQnhH3UL4gqzu+YiA1Q3YyLLCv88gLJOvw-0+uw5Lvkw@mail.gmail.com/T/#m06c612f84f6b6fe759e670573b2c8092df71607b
> > >
> > > to have a workable short-term solution, and later revert it when the
> > > final solution would be in place?
> >
> > I would need to go back through that to make sure that it makes sense,
> > and ensure that the hook is idempotent for SELinux, AppArmor, and
> > Smack (current hook implementations), *aaaand* if we promise that this
> > is just a *temporary* hack I think I would be okay with that.
>
> I would appreciate that addtional extra mile a lot, as it will allow a
> (temporary!) fix to be delivered quite earlier than what the above
> process will provide.
>
> Of course the deal is that I'll take ownership of pursuing the complete
> fix till resolution.

Okay, let me do the investigation mentioned above to make sure this is
possible and report back (I need to refresh my memory first, the
holiday break kinda flushed this out of my mind ;).
diff mbox series

Patch

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3c5be76a9199..f785600b666a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5476,6 +5476,32 @@  static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk
 	selinux_netlbl_sctp_sk_clone(sk, newsk);
 }
 
+static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
+{
+	const struct task_security_struct *tsec = selinux_cred(current_cred());
+	struct sk_security_struct *ssksec = ssk->sk_security;
+	u16 sclass;
+	u32 sid;
+	int err;
+
+	/* create the sid using the current cred, regardless of the ssk kern
+	 * flag
+	 */
+	sclass = socket_type_to_security_class(ssk->sk_family, ssk->sk_type,
+					       ssk->sk_protocol);
+	err = socket_sockcreate_sid(tsec, sclass, &sid);
+	if (err)
+		return err;
+
+	ssksec->sid = sid;
+
+	/* replace the existing subflow label deleting the existing one
+	 * and re-recrating a new label using the current context
+	 */
+	selinux_netlbl_sk_security_free(ssksec);
+	return selinux_netlbl_socket_post_create(ssk, ssk->sk_family);
+}
+
 static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
 				     struct request_sock *req)
 {
@@ -7216,6 +7242,7 @@  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone),
 	LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect),
 	LSM_HOOK_INIT(sctp_assoc_established, selinux_sctp_assoc_established),
+	LSM_HOOK_INIT(mptcp_add_subflow, selinux_mptcp_add_subflow),
 	LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request),
 	LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone),
 	LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established),
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 1321f15799e2..8e0080b8a8ef 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -155,8 +155,10 @@  void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
  */
 void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
 {
-	if (sksec->nlbl_secattr != NULL)
+	if (sksec->nlbl_secattr != NULL) {
 		netlbl_secattr_free(sksec->nlbl_secattr);
+		sksec->nlbl_secattr = NULL;
+	}
 }
 
 /**