diff mbox

[RFC,v2,2/2] kernel: Add SELinux SCTP protocol support

Message ID 20170222170359.5433-1-richard_c_haines@btinternet.com (mailing list archive)
State New, archived
Headers show

Commit Message

Richard Haines Feb. 22, 2017, 5:03 p.m. UTC
Add SELinux support for the SCTP protocol. The SELinux-sctp.txt document
describes how the patch has been implemented.

Patches to assist the testing of this kernel patch are:
1) Support new SCTP portcon statement used by SCTP tests in the
selinux-testsuite [1].
2) Add SCTP tests to the selinux-testsuite [2].

Built and tested on Fedora 25 with linux-4.9.9 kernel.

[1] http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-Add-support-for-the-SCTP-portcon-keyword.patch
[2] http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-testsuite-Add-SCTP-test-support.patch

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 Documentation/security/SELinux-sctp.txt | 178 ++++++++++++++++++++++++++
 include/net/sctp/structs.h              |   7 ++
 net/sctp/sm_make_chunk.c                |  12 ++
 net/sctp/sm_statefuns.c                 |  20 +++
 net/sctp/socket.c                       |  42 ++++++-
 security/selinux/hooks.c                | 213 ++++++++++++++++++++++++++++++--
 security/selinux/include/classmap.h     |   3 +
 7 files changed, 466 insertions(+), 9 deletions(-)
 create mode 100644 Documentation/security/SELinux-sctp.txt

Comments

Stephen Smalley March 2, 2017, 8:45 p.m. UTC | #1
On Wed, 2017-02-22 at 17:03 +0000, Richard Haines wrote:
> Add SELinux support for the SCTP protocol. The SELinux-sctp.txt
> document
> describes how the patch has been implemented.
> 
> Patches to assist the testing of this kernel patch are:
> 1) Support new SCTP portcon statement used by SCTP tests in the
> selinux-testsuite [1].
> 2) Add SCTP tests to the selinux-testsuite [2].
> 
> Built and tested on Fedora 25 with linux-4.9.9 kernel.

Need to re-base and test on a suitable upstream tree (maybe security
next or selinux next).  Since the extended socket class policy
capability has been merged, you can leverage it and drop the duplicated
portions.

> 
> [1] http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-Ad
> d-support-for-the-SCTP-portcon-keyword.patch
> [2] http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-
> testsuite-Add-SCTP-test-support.patch

I wouldn't include URLs for these userspace patches in the patch
description or in-tree documentation; you can note them in your cover
letter posting as an aid to testing but they shouldn't be part of the
permanent history since they will presumably be upstreamed too.

> 
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> ---
>  Documentation/security/SELinux-sctp.txt | 178
> ++++++++++++++++++++++++++
>  include/net/sctp/structs.h              |   7 ++
>  net/sctp/sm_make_chunk.c                |  12 ++
>  net/sctp/sm_statefuns.c                 |  20 +++
>  net/sctp/socket.c                       |  42 ++++++-

I'd either move the net/sctp changes into the first patch that defines
the LSM hooks or move them into their own separate patch between these
two patches.

>  security/selinux/hooks.c                | 213
> ++++++++++++++++++++++++++++++--
>  security/selinux/include/classmap.h     |   3 +
>  7 files changed, 466 insertions(+), 9 deletions(-)
>  create mode 100644 Documentation/security/SELinux-sctp.txt
> 
> diff --git a/Documentation/security/SELinux-sctp.txt
> b/Documentation/security/SELinux-sctp.txt
> new file mode 100644
> index 0000000..ada666f
> --- /dev/null
> +++ b/Documentation/security/SELinux-sctp.txt
> @@ -0,0 +1,178 @@
> +                               SCTP SELinux Support
> +                              ======================
> +
> +Testing - selinux-testsuite
> +============================
> +There is a patch available that adds SCTP/SELinux tests to the
> +selinux-testsuite. This is available from:
> +
> +http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-tests
> uite-Add-SCTP-test-support.patch
> +
> +These tests require libsepol to support the new sctp portcon
> statement.
> +A patch is available from:
> +
> +http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-Add-
> support-for-the-SCTP-portcon-keyword.patch

Ditto here; I wouldn't include these patch URLs in the in-tree
documentation since the patches should get upstreamed.

> +
> +Before running these tests, read the selinux-testsuite/README.sctp
> as it is
> +also possible to run the lksctp-tools/src/func_tests that are
> available from:
> +
> +https://github.com/sctp/lksctp-tools
> +
> +
> +Security Hooks
> +===============
> +
> +The Documentation/security/LSM-sctp.txt document describes how the
> following
> +sctp security hooks are utilised:
> +    security_sctp_assoc_request()
> +    security_sctp_accept_conn()
> +    security_sctp_sk_clone()
> +    security_sctp_addr_list()
> +
> +
> +Policy Statements
> +==================
> +A new object class "sctp_socket" has been introduced with the
> following SCTP
> +specific permissions: association bindx_add connectx
> +
> +The permissions are explained in the sections below.
> +
> +Kernel policy language
> +-----------------------
> +class sctp_socket
> +class sctp_socket inherits socket { node_bind name_connect
> association
> +                                    bindx_add connectx }
> +
> +CIL policy language
> +--------------------
> +(classcommon sctp_socket socket)
> +(class sctp_socket (node_bind name_connect association bindx_add
> connectx))
> +(classorder (unordered sctp_socket))
> +
> +If the SELinux userspace tools have been updated, then the portcon
> statement
> +may be used as shown in the following example:
> +    (portcon sctp (1024 1035) (system_u object_r sctp_port_t ((s0)
> (s0))))

Not a strong objection, but not sure if CIL documentation belongs in
the kernel tree.

<snip>
> +
> +SCTP Peer Labeling and Permission Checks
> +=========================================
> +An SCTP socket will only have one peer label assigned to it. This
> will be
> +assigned during the establishment of the first association. Once the
> peer
> +label has been assigned, the "association" permission will be
> checked as
> +follows:
> +
> +       allow socket_t peer_t : sctp_socket { association };
> +
> +This allows policy to decide whether to allow or deny associations
> from peers,
> +as there can be multiple associations on a single socket. These
> associations
> +could come from any of the policy allowed peers, however it could be
> that
> +these are required by other services but sctp associations are not
> allowed
> +from all of them.

I'm still confused by this check.  We should already be performing a
peer recv check between the socket label and the peer label, so we
don't need to duplicate that check.  What would make sense would be
some kind of permission check between the peer label from the first
association, which is the one you save and return to userspace for
SO_PEERSEC, and the peer label of any subsequent associations
established on the socket.  That would allow policy to prohibit or
restrict mixing of associations with different peer labels on the same
socket (since effectively that permits impersonation of another peer
label to userspace components).  But instead you are always checking
between the socket label and the saved peer label from the first
association.  So you'll just keep repeating the same permission check
for every association.

> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 7f4387f..c0be892 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
<snip>
> @@ -4827,6 +4879,149 @@ static void selinux_sock_graft(struct sock
> *sk, struct socket *parent)
>  	sksec->sclass = isec->sclass;
>  }
>  
> +static int selinux_sctp_assoc_request(struct sock *sk, struct
> sk_buff *skb)
> +{
> +	struct sk_security_struct *sksec = sk->sk_security;
> +	struct common_audit_data ad;
> +	struct lsm_network_audit net = {0,};
> +	u8 peerlbl_active;
> +	int err;
> +
> +	peerlbl_active = selinux_peerlbl_enabled();
> +
> +	if (sksec->peer_sid == SECINITSID_UNLABELED &&
> peerlbl_active) {
> +		/* Here because this is the first association on
> this
> +		 * socket that is always unlabeled, therefore set
> +		 * sksec->peer_sid to new peer ctx. For further info
> see:
> +		 * Documentation/security/SELinux-sctp.txt
> +		 */
> +		err = selinux_skb_peerlbl_sid(skb, sk->sk_family,
> +					      &sksec->peer_sid);
> +		if (err)
> +			return err;
> +	}
> +
> +	ad.type = LSM_AUDIT_DATA_NET;
> +	ad.u.net = &net;
> +	ad.u.net->sk = sk;
> +
> +	err = avc_has_perm(sksec->sid, sksec->peer_sid, sksec-
> >sclass,
> +			   SCTP_SOCKET__ASSOCIATION, &ad);

As above, you'll end up performing the same permission check repeatedly
here for every association, even when the association itself would have
a different peer label.  And this permission check seems to be no
different than the peer recv check (same SID arguments).  What would
make sense is something like:

	u32 peer_sid = SECINITSID_UNLABELED;
	if (peerlbl_active) {
		err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peer_sid);
		if (err)
			return err;
	}
	if (sksec->peer_sid == SECINITSID_UNLABELED)
		sksec->peer_sid = peer_sid;
	else if (sksec->peer_sid != peer_sid) {
		err = avc_has_perm(sksec->peer_sid, peer_sid, sksec->sclass,
				SCTP_SOCKET_ASSOCIATION, &ad);
		if (err)
			return err;
	}
	return 0;

This would allow preventing multiple associations with different peer
labels, or controlling their inter-relationships.  You don't need a
socket-peer check here; that is already covered by the peer recv check.
		
> +	return err;
> +}
> +
> +static int selinux_sctp_accept_conn(struct sctp_endpoint *ep,
> +				    struct sk_buff *skb)
> +{
> +	struct sk_security_struct *sksec = ep->base.sk->sk_security;
> +	int err;
> +	u32 connsid;
> +	u32 peersid;
> +
> +	/* Have COOKIE ECHO so compute the MLS component for the
> connection
> +	 * and store the information in ep. This will only be used
> by
> +	 * TCP/peeloff connections as they cause a new socket to be
> generated.

Not sure why you say TCP above.  And won't this be true of accept()'d
sockets too in addition to peeloff ones?

> +	 * selinux_sctp_sk_clone() will then plug this into the new
> socket
> +	 * as described in Documentation/security/LSM-sctp.txt
> +	 */
> +	err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family,
> &peersid);
> +	if (err)
> +		return err;
> +
> +	err = selinux_conn_sid(sksec->sid, peersid, &connsid);
> +	if (err)
> +		return err;
> +
> +	ep->secid = connsid;
> +	ep->peer_secid = peersid;
> +
> +	return 0;
> +}
> +
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Marcelo Ricardo Leitner March 20, 2017, 5:23 p.m. UTC | #2
On Thu, Mar 02, 2017 at 03:45:40PM -0500, Stephen Smalley wrote:
> On Wed, 2017-02-22 at 17:03 +0000, Richard Haines wrote:
<snip>

> > +	return err;
> > +}
> > +
> > +static int selinux_sctp_accept_conn(struct sctp_endpoint *ep,
> > +				    struct sk_buff *skb)
> > +{
> > +	struct sk_security_struct *sksec = ep->base.sk->sk_security;
> > +	int err;
> > +	u32 connsid;
> > +	u32 peersid;
> > +
> > +	/* Have COOKIE ECHO so compute the MLS component for the
> > connection
> > +	 * and store the information in ep. This will only be used
> > by
> > +	 * TCP/peeloff connections as they cause a new socket to be
> > generated.
> 
> Not sure why you say TCP above.  And won't this be true of accept()'d

Probably just a typo, should be SCTP instead.

> sockets too in addition to peeloff ones?

Speaking of accept() path, I think we have an issue there with this
patch, because it's doing:
@@ -7683,8 +7717,6 @@ void sctp_copy_sock(struct sock *newsk, struct
sock *sk,
-       security_sk_clone(sk, newsk);
@@ -7829,6 +7862,11 @@ static void sctp_sock_migrate(struct sock *oldsk,
struct
+       security_sctp_sk_clone(oldep, oldsk, newsk);

But sctp_copy_sock() is called from places other than
sctp_sock_migrate, mainly:
net/sctp/ipv6.c:        sctp_copy_sock(newsk, sk, asoc);
net/sctp/protocol.c:    sctp_copy_sock(newsk, sk, asoc);
Which are on the accept() path.

Ideally it's better to keep the call to security_sctp_sk_clone in
sctp_copy_sock() to get those covered too.

  Marcelo

> 
> > +	 * selinux_sctp_sk_clone() will then plug this into the new
> > socket
> > +	 * as described in Documentation/security/LSM-sctp.txt
> > +	 */
> > +	err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family,
> > &peersid);
> > +	if (err)
> > +		return err;
> > +
> > +	err = selinux_conn_sid(sksec->sid, peersid, &connsid);
> > +	if (err)
> > +		return err;
> > +
> > +	ep->secid = connsid;
> > +	ep->peer_secid = peersid;
> > +
> > +	return 0;
> > +}
> > +
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Richard Haines March 22, 2017, 10:11 a.m. UTC | #3
On Thu, 2017-03-02 at 15:45 -0500, Stephen Smalley wrote:
> On Wed, 2017-02-22 at 17:03 +0000, Richard Haines wrote:
> > Add SELinux support for the SCTP protocol. The SELinux-sctp.txt
> > document
> > describes how the patch has been implemented.
> > 
> > Patches to assist the testing of this kernel patch are:
> > 1) Support new SCTP portcon statement used by SCTP tests in the
> > selinux-testsuite [1].
> > 2) Add SCTP tests to the selinux-testsuite [2].
> > 
> > Built and tested on Fedora 25 with linux-4.9.9 kernel.
> 
> Need to re-base and test on a suitable upstream tree (maybe security
> next or selinux next).  Since the extended socket class policy
> capability has been merged, you can leverage it and drop the
> duplicated
> portions.
> 
Okay I'll find a suitable kernel to build the next patch set

> > 
> > [1] http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-
> > Ad
> > d-support-for-the-SCTP-portcon-keyword.patch
> > [2] http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-
> > testsuite-Add-SCTP-test-support.patch
> 
> I wouldn't include URLs for these userspace patches in the patch
> description or in-tree documentation; you can note them in your cover
> letter posting as an aid to testing but they shouldn't be part of the
> permanent history since they will presumably be upstreamed too.
> 
I'll add any of these to the cover letter.

> > 
> > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> > ---
> >  Documentation/security/SELinux-sctp.txt | 178
> > ++++++++++++++++++++++++++
> >  include/net/sctp/structs.h              |   7 ++
> >  net/sctp/sm_make_chunk.c                |  12 ++
> >  net/sctp/sm_statefuns.c                 |  20 +++
> >  net/sctp/socket.c                       |  42 ++++++-
> 
> I'd either move the net/sctp changes into the first patch that
> defines
> the LSM hooks or move them into their own separate patch between
> these
> two patches.
I'll split these into their own patches (looks like I'll end up with
netlabel patches as well to get CIPSO/CALIPSO working fully !!)

> 
> >  security/selinux/hooks.c                | 213
> > ++++++++++++++++++++++++++++++--
> >  security/selinux/include/classmap.h     |   3 +
> >  7 files changed, 466 insertions(+), 9 deletions(-)
> >  create mode 100644 Documentation/security/SELinux-sctp.txt
> > 
> > diff --git a/Documentation/security/SELinux-sctp.txt
> > b/Documentation/security/SELinux-sctp.txt
> > new file mode 100644
> > index 0000000..ada666f
> > --- /dev/null
> > +++ b/Documentation/security/SELinux-sctp.txt
> > @@ -0,0 +1,178 @@
> > +                               SCTP SELinux Support
> > +                              ======================
> > +
> > +Testing - selinux-testsuite
> > +============================
> > +There is a patch available that adds SCTP/SELinux tests to the
> > +selinux-testsuite. This is available from:
> > +
> > +http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-tes
> > ts
> > uite-Add-SCTP-test-support.patch
> > +
> > +These tests require libsepol to support the new sctp portcon
> > statement.
> > +A patch is available from:
> > +
> > +http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-Add
> > -
> > support-for-the-SCTP-portcon-keyword.patch
> 
> Ditto here; I wouldn't include these patch URLs in the in-tree
> documentation since the patches should get upstreamed.
> 
> > +
> > +Before running these tests, read the selinux-testsuite/README.sctp
> > as it is
> > +also possible to run the lksctp-tools/src/func_tests that are
> > available from:
> > +
> > +https://github.com/sctp/lksctp-tools
> > +
> > +
> > +Security Hooks
> > +===============
> > +
> > +The Documentation/security/LSM-sctp.txt document describes how the
> > following
> > +sctp security hooks are utilised:
> > +    security_sctp_assoc_request()
> > +    security_sctp_accept_conn()
> > +    security_sctp_sk_clone()
> > +    security_sctp_addr_list()
> > +
> > +
> > +Policy Statements
> > +==================
> > +A new object class "sctp_socket" has been introduced with the
> > following SCTP
> > +specific permissions: association bindx_add connectx
> > +
> > +The permissions are explained in the sections below.
> > +
> > +Kernel policy language
> > +-----------------------
> > +class sctp_socket
> > +class sctp_socket inherits socket { node_bind name_connect
> > association
> > +                                    bindx_add connectx }
> > +
> > +CIL policy language
> > +--------------------
> > +(classcommon sctp_socket socket)
> > +(class sctp_socket (node_bind name_connect association bindx_add
> > connectx))
> > +(classorder (unordered sctp_socket))
> > +
> > +If the SELinux userspace tools have been updated, then the portcon
> > statement
> > +may be used as shown in the following example:
> > +    (portcon sctp (1024 1035) (system_u object_r sctp_port_t ((s0)
> > (s0))))
> 
> Not a strong objection, but not sure if CIL documentation belongs in
> the kernel tree.
I'll stick to kernel language statements

> 
> <snip>
> > +
> > +SCTP Peer Labeling and Permission Checks
> > +=========================================
> > +An SCTP socket will only have one peer label assigned to it. This
> > will be
> > +assigned during the establishment of the first association. Once
> > the
> > peer
> > +label has been assigned, the "association" permission will be
> > checked as
> > +follows:
> > +
> > +       allow socket_t peer_t : sctp_socket { association };
> > +
> > +This allows policy to decide whether to allow or deny associations
> > from peers,
> > +as there can be multiple associations on a single socket. These
> > associations
> > +could come from any of the policy allowed peers, however it could
> > be
> > that
> > +these are required by other services but sctp associations are not
> > allowed
> > +from all of them.
> 
> I'm still confused by this check.  We should already be performing a
> peer recv check between the socket label and the peer label, so we
> don't need to duplicate that check.  What would make sense would be
> some kind of permission check between the peer label from the first
> association, which is the one you save and return to userspace for
> SO_PEERSEC, and the peer label of any subsequent associations
> established on the socket.  That would allow policy to prohibit or
> restrict mixing of associations with different peer labels on the
> same
> socket (since effectively that permits impersonation of another peer
> label to userspace components).  But instead you are always checking
> between the socket label and the saved peer label from the first
> association.  So you'll just keep repeating the same permission check
> for every association.
See comment below.

> 
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index 7f4387f..c0be892 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> 
> <snip>
> > @@ -4827,6 +4879,149 @@ static void selinux_sock_graft(struct sock
> > *sk, struct socket *parent)
> >  	sksec->sclass = isec->sclass;
> >  }
> >  
> > +static int selinux_sctp_assoc_request(struct sock *sk, struct
> > sk_buff *skb)
> > +{
> > +	struct sk_security_struct *sksec = sk->sk_security;
> > +	struct common_audit_data ad;
> > +	struct lsm_network_audit net = {0,};
> > +	u8 peerlbl_active;
> > +	int err;
> > +
> > +	peerlbl_active = selinux_peerlbl_enabled();
> > +
> > +	if (sksec->peer_sid == SECINITSID_UNLABELED &&
> > peerlbl_active) {
> > +		/* Here because this is the first association on
> > this
> > +		 * socket that is always unlabeled, therefore set
> > +		 * sksec->peer_sid to new peer ctx. For further
> > info
> > see:
> > +		 * Documentation/security/SELinux-sctp.txt
> > +		 */
> > +		err = selinux_skb_peerlbl_sid(skb, sk->sk_family,
> > +					      &sksec->peer_sid);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	ad.type = LSM_AUDIT_DATA_NET;
> > +	ad.u.net = &net;
> > +	ad.u.net->sk = sk;
> > +
> > +	err = avc_has_perm(sksec->sid, sksec->peer_sid, sksec-
> > > sclass,
> > 
> > +			   SCTP_SOCKET__ASSOCIATION, &ad);
> 
> As above, you'll end up performing the same permission check
> repeatedly
> here for every association, even when the association itself would
> have
> a different peer label.  And this permission check seems to be no
> different than the peer recv check (same SID arguments).  What would
> make sense is something like:
> 
> 	u32 peer_sid = SECINITSID_UNLABELED;
> 	if (peerlbl_active) {
> 		err = selinux_skb_peerlbl_sid(skb, sk->sk_family,
> &peer_sid);
> 		if (err)
> 			return err;
> 	}
> 	if (sksec->peer_sid == SECINITSID_UNLABELED)
> 		sksec->peer_sid = peer_sid;
> 	else if (sksec->peer_sid != peer_sid) {
> 		err = avc_has_perm(sksec->peer_sid, peer_sid, sksec-
> >sclass,
> 				SCTP_SOCKET_ASSOCIATION, &ad);
> 		if (err)
> 			return err;
> 	}
> 	return 0;
> 
> This would allow preventing multiple associations with different peer
> labels, or controlling their inter-relationships.  You don't need a
> socket-peer check here; that is already covered by the peer recv
> check.
> 

I've now changed this to emulate your suggestion. However instead of
just setting peer_sid to first association I've added an avc check
because I have a case where the packet label was
"deny_assoc_sctp_peer_t" and allowed by peer recv, however this was not
in my "sctp_assoc_peers" attribute list. Checking with:
(allow sctp_assoc_peers self (sctp_socket (association)))
would have denied this as the first association.
Does this seem reasonable ???

> 		
> > +	return err;
> > +}
> > +
> > +static int selinux_sctp_accept_conn(struct sctp_endpoint *ep,
> > +				    struct sk_buff *skb)
> > +{
> > +	struct sk_security_struct *sksec = ep->base.sk-
> > >sk_security;
> > +	int err;
> > +	u32 connsid;
> > +	u32 peersid;
> > +
> > +	/* Have COOKIE ECHO so compute the MLS component for the
> > connection
> > +	 * and store the information in ep. This will only be used
> > by
> > +	 * TCP/peeloff connections as they cause a new socket to
> > be
> > generated.
> 
> Not sure why you say TCP above.  And won't this be true of accept()'d
> sockets too in addition to peeloff ones?
Changed this to read "This will only be used by SCTP TCP type sockets
and peeled off connections"

> 
> > +	 * selinux_sctp_sk_clone() will then plug this into the
> > new
> > socket
> > +	 * as described in Documentation/security/LSM-sctp.txt
> > +	 */
> > +	err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family,
> > &peersid);
> > +	if (err)
> > +		return err;
> > +
> > +	err = selinux_conn_sid(sksec->sid, peersid, &connsid);
> > +	if (err)
> > +		return err;
> > +
> > +	ep->secid = connsid;
> > +	ep->peer_secid = peersid;
> > +
> > +	return 0;
> > +}
> > +
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-
> security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Richard Haines March 22, 2017, 10:22 a.m. UTC | #4
On Mon, 2017-03-20 at 14:23 -0300, Marcelo Ricardo Leitner wrote:
> On Thu, Mar 02, 2017 at 03:45:40PM -0500, Stephen Smalley wrote:
> > On Wed, 2017-02-22 at 17:03 +0000, Richard Haines wrote:
> 
> <snip>
> 
> > > +	return err;
> > > +}
> > > +
> > > +static int selinux_sctp_accept_conn(struct sctp_endpoint *ep,
> > > +				    struct sk_buff *skb)
> > > +{
> > > +	struct sk_security_struct *sksec = ep->base.sk-
> > > >sk_security;
> > > +	int err;
> > > +	u32 connsid;
> > > +	u32 peersid;
> > > +
> > > +	/* Have COOKIE ECHO so compute the MLS component for the
> > > connection
> > > +	 * and store the information in ep. This will only be
> > > used
> > > by
> > > +	 * TCP/peeloff connections as they cause a new socket to
> > > be
> > > generated.
> > 
> > Not sure why you say TCP above.  And won't this be true of
> > accept()'d
> 
> Probably just a typo, should be SCTP instead.
Yes so changed to "This will only be used by SCTP TCP type sockets
and peeled off connections".

> 
> > sockets too in addition to peeloff ones?
> 
> Speaking of accept() path, I think we have an issue there with this
> patch, because it's doing:
> @@ -7683,8 +7717,6 @@ void sctp_copy_sock(struct sock *newsk, struct
> sock *sk,
> -       security_sk_clone(sk, newsk);
> @@ -7829,6 +7862,11 @@ static void sctp_sock_migrate(struct sock
> *oldsk,
> struct
> +       security_sctp_sk_clone(oldep, oldsk, newsk);
> 
> But sctp_copy_sock() is called from places other than
> sctp_sock_migrate, mainly:
> net/sctp/ipv6.c:        sctp_copy_sock(newsk, sk, asoc);
> net/sctp/protocol.c:    sctp_copy_sock(newsk, sk, asoc);
> Which are on the accept() path.
> 
> Ideally it's better to keep the call to security_sctp_sk_clone in
> sctp_copy_sock() to get those covered too.

Thanks for pointing this out, I'll fix in next patch set.
> 
>   Marcelo
> 
> > 
> > > +	 * selinux_sctp_sk_clone() will then plug this into the
> > > new
> > > socket
> > > +	 * as described in Documentation/security/LSM-sctp.txt
> > > +	 */
> > > +	err = selinux_skb_peerlbl_sid(skb, ep->base.sk-
> > > >sk_family,
> > > &peersid);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	err = selinux_conn_sid(sksec->sid, peersid, &connsid);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	ep->secid = connsid;
> > > +	ep->peer_secid = peersid;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-
> > sctp" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" 
> in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/security/SELinux-sctp.txt b/Documentation/security/SELinux-sctp.txt
new file mode 100644
index 0000000..ada666f
--- /dev/null
+++ b/Documentation/security/SELinux-sctp.txt
@@ -0,0 +1,178 @@ 
+                               SCTP SELinux Support
+                              ======================
+
+Testing - selinux-testsuite
+============================
+There is a patch available that adds SCTP/SELinux tests to the
+selinux-testsuite. This is available from:
+
+http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-testsuite-Add-SCTP-test-support.patch
+
+These tests require libsepol to support the new sctp portcon statement.
+A patch is available from:
+
+http://arctic.selinuxproject.org/~rhaines/selinux-sctp/selinux-Add-support-for-the-SCTP-portcon-keyword.patch
+
+Before running these tests, read the selinux-testsuite/README.sctp as it is
+also possible to run the lksctp-tools/src/func_tests that are available from:
+
+https://github.com/sctp/lksctp-tools
+
+
+Security Hooks
+===============
+
+The Documentation/security/LSM-sctp.txt document describes how the following
+sctp security hooks are utilised:
+    security_sctp_assoc_request()
+    security_sctp_accept_conn()
+    security_sctp_sk_clone()
+    security_sctp_addr_list()
+
+
+Policy Statements
+==================
+A new object class "sctp_socket" has been introduced with the following SCTP
+specific permissions: association bindx_add connectx
+
+The permissions are explained in the sections below.
+
+Kernel policy language
+-----------------------
+class sctp_socket
+class sctp_socket inherits socket { node_bind name_connect association
+                                    bindx_add connectx }
+
+CIL policy language
+--------------------
+(classcommon sctp_socket socket)
+(class sctp_socket (node_bind name_connect association bindx_add connectx))
+(classorder (unordered sctp_socket))
+
+If the SELinux userspace tools have been updated, then the portcon statement
+may be used as shown in the following example:
+    (portcon sctp (1024 1035) (system_u object_r sctp_port_t ((s0) (s0))))
+
+Rule validation parameters used when 'network_peer_controls = 1':
+-------------------------------------------------------------------------------
+Rule  Source   Target     Class        Permissions
+-------------------------------------------------------------------------------
+allow socket_t socket_t : sctp_socket { bindx_add connectx }; [1]
+allow socket_t port_t   : sctp_socket { name_bind name_connect }; [2]
+allow socket_t node_t   : sctp_socket { node_bind };
+allow socket_t peer_t   : sctp_socket { association };
+
+[1] setsockcreatecon(3) may be used to create a new labeled socket.
+[2] The port types may differ for name_bind and name_connect.
+
+
+SCTP Bind, Connect and ASCONF Chunk Parameter Permission Checks
+================================================================
+The hook security_sctp_addr_list() is called by SCTP to check permissions
+required for ipv4/ipv6 addresses based on the @optname as follows:
+
+  ------------------------------------------------------------------
+  |                  BINDX_ADD Permission Check                    |
+  |       @optname             |         @address contains         |
+  |----------------------------|-----------------------------------|
+  | SCTP_SOCKOPT_BINDX_ADD     | One or more ipv4 / ipv6 addresses |
+  ------------------------------------------------------------------
+
+  ------------------------------------------------------------------
+  |                  BIND Permission Checks                        |
+  |       @optname             |         @address contains         |
+  |----------------------------|-----------------------------------|
+  | SCTP_PRIMARY_ADDR          | Single ipv4 or ipv6 address       |
+  | SCTP_SET_PEER_PRIMARY_ADDR | Single ipv4 or ipv6 address       |
+  ------------------------------------------------------------------
+
+  ------------------------------------------------------------------
+  |                 CONNECTX Permission Check                      |
+  |       @optname             |         @address contains         |
+  |----------------------------|-----------------------------------|
+  | SCTP_SOCKOPT_CONNECTX      | One or more ipv4 / ipv6 addresses |
+  ------------------------------------------------------------------
+
+  ------------------------------------------------------------------
+  |                 CONNECT Permission Checks                      |
+  |       @optname             |         @address contains         |
+  |----------------------------|-----------------------------------|
+  | SCTP_PARAM_ADD_IP          | One or more ipv4 / ipv6 addresses |
+  | SCTP_PARAM_SET_PRIMARY     | Single ipv4 or ipv6 address       |
+  ------------------------------------------------------------------
+
+A summary of the @optname entries is as follows:
+
+    SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
+                             associated after (optionally) calling
+                             bind(3).
+                             sctp_bindx(3) adds a set of bind
+	                     addresses on a socket.
+
+    SCTP_SOCKOPT_CONNECTX - Allows the allocation of multiple
+                            addresses for reaching a peer
+                            (multi-homed).
+                            sctp_connectx(3) initiates a connection
+                            on an SCTP socket using multiple
+                            destination addresses.
+
+    SCTP_PRIMARY_ADDR     - Set local primary address.
+
+    SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as
+                                 association primary.
+
+Note that to support Dynamic Address Reconfiguration the following
+parameters must be enabled on both endpoints (or the appropriate
+setsockopts):
+    /proc/sys/net/sctp/addip_enable
+    /proc/sys/net/sctp/addip_noauth_enable
+
+then the following *_PARAM_*'s are sent to the peer in an
+ASCONF chunk when the corresponding @optname's are present:
+
+          @optname                ASCONF Parameter
+    SCTP_SOCKOPT_BINDX_ADD     -> SCTP_PARAM_ADD_IP
+    SCTP_SET_PEER_PRIMARY_ADDR -> SCTP_PARAM_SET_PRIMARY
+
+
+SCTP Peer Labeling and Permission Checks
+=========================================
+An SCTP socket will only have one peer label assigned to it. This will be
+assigned during the establishment of the first association. Once the peer
+label has been assigned, the "association" permission will be checked as
+follows:
+
+       allow socket_t peer_t : sctp_socket { association };
+
+This allows policy to decide whether to allow or deny associations from peers,
+as there can be multiple associations on a single socket. These associations
+could come from any of the policy allowed peers, however it could be that
+these are required by other services but sctp associations are not allowed
+from all of them.
+
+NOTES:
+   1) If peer labeling is not enabled, then the peer context will always be
+      SECINITSID_UNLABELED (unlabeled_t in Reference Policy).
+
+   2) If peer labeling is supported, getpeercon(3) may be used by userspace
+      to retrieve the sockets peer context.
+
+   3) If using NetLabel be aware that if a label is assigned to a specific
+      interface, and that interface 'goes down', then the NetLabel service
+      will remove the entry. Therefore ensure that the network startup scripts
+      call netlabelctl(8) to set the required label (see netlabel-config(8)
+      helper script for details).
+
+   4) The NetLabel SCTP peer labeling rules apply as discussed in the following
+      set of posts tagged "netlabel" at: http://www.paul-moore.com/blog/t,
+      except for the following issue that requires further work to resolve:
+
+       a) SCTP does not support "cmsg_type == SCM_SECURITY" in datagrams for
+          ipv4 or ipv6, therefore CIPSO/CALIPSO will fail unless TCP type
+          sockets are used. See the SCTP tests in the selinux-testsuite where
+          pass/fail configurations are noted.
+
+   6) IPSEC is not supported as rfc3554 - sctp/ipsec support has not been
+      implemented in userspace (racoon(8) or ipsec_pluto(8)), although the
+      kernel supports SCTP/IPSEC.
+
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 11c3bf2..d54a767 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1287,6 +1287,13 @@  struct sctp_endpoint {
 	__u16 active_key_id;
 	__u8  auth_enable:1,
 	      prsctp_enable:1;
+
+	/* Security identifiers from incoming (COOKIE-ECHO) connection.
+	 * These are set by security_sctp_accept_conn() and used by
+	 * security_sctp_sk_clone() to set sids on newsock.
+	 */
+	u32 secid;
+	u32 peer_secid;
 };
 
 /* Recover the outter endpoint structure. */
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 9e9690b..648b63b 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3056,6 +3056,12 @@  static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
 		if (af->is_any(&addr))
 			memcpy(&addr, &asconf->source, sizeof(addr));
 
+		if (security_sctp_addr_list(asoc->ep->base.sk,
+					    SCTP_PARAM_ADD_IP,
+					    (struct sockaddr *)&addr,
+					    af->sockaddr_len))
+			return SCTP_ERROR_REQ_REFUSED;
+
 		/* ADDIP 4.3 D9) If an endpoint receives an ADD IP address
 		 * request and does not have the local resources to add this
 		 * new address to the association, it MUST return an Error
@@ -3122,6 +3128,12 @@  static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
 		if (af->is_any(&addr))
 			memcpy(&addr.v4, sctp_source(asconf), sizeof(addr));
 
+		if (security_sctp_addr_list(asoc->ep->base.sk,
+					    SCTP_PARAM_SET_PRIMARY,
+					    (struct sockaddr *)&addr,
+					    af->sockaddr_len))
+			return SCTP_ERROR_REQ_REFUSED;
+
 		peer = sctp_assoc_lookup_paddr(asoc, &addr);
 		if (!peer)
 			return SCTP_ERROR_DNS_FAILED;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 8ec20a6..363793c 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -315,6 +315,13 @@  sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net,
 	sctp_unrecognized_param_t *unk_param;
 	int len;
 
+	/* Update socket peer label if first association then check
+	 * whether association allowed.
+	 * See Documentation/security/LSM-sctp.txt for details.
+	 */
+	if (security_sctp_assoc_request(ep->base.sk, chunk->skb))
+		return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
 	/* 6.10 Bundling
 	 * An endpoint MUST NOT bundle INIT, INIT ACK or
 	 * SHUTDOWN COMPLETE with any other chunks.
@@ -508,6 +515,13 @@  sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net,
 	struct sctp_chunk *err_chunk;
 	struct sctp_packet *packet;
 
+	/* Update socket peer label if first association then check
+	 * whether association allowed.
+	 * See Documentation/security/LSM-sctp.txt for details.
+	 */
+	if (security_sctp_assoc_request(ep->base.sk, chunk->skb))
+		return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
 	if (!sctp_vtag_verify(chunk, asoc))
 		return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
 
@@ -812,6 +826,12 @@  sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net,
 			goto nomem_aiev;
 	}
 
+	/* Received COOKIE-ECHO so store security info in ep.
+	 * See Documentation/security/LSM-sctp.txt for details.
+	 */
+	if (security_sctp_accept_conn((struct sctp_endpoint *)ep, chunk->skb))
+		return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+
 	/* Add all the state machine commands now since we've created
 	 * everything.  This way we don't introduce memory corruptions
 	 * during side-effect processing and correclty count established
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 6cbe5bd..995de4b 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1009,6 +1009,12 @@  static int sctp_setsockopt_bindx(struct sock *sk,
 	/* Do the work. */
 	switch (op) {
 	case SCTP_BINDX_ADD_ADDR:
+		/* Allow security module to validate bindx addresses. */
+		err = security_sctp_addr_list(sk, SCTP_SOCKOPT_BINDX_ADD,
+					      (struct sockaddr *)kaddrs,
+					      addrs_size);
+		if (err)
+			goto out;
 		err = sctp_bindx_add(sk, kaddrs, addrcnt);
 		if (err)
 			goto out;
@@ -1329,9 +1335,17 @@  static int __sctp_setsockopt_connectx(struct sock *sk,
 	if (__copy_from_user(kaddrs, addrs, addrs_size)) {
 		err = -EFAULT;
 	} else {
+		/* Allow security module to validate connectx addresses. */
+		err = security_sctp_addr_list(sk, SCTP_SOCKOPT_CONNECTX,
+					      (struct sockaddr *)kaddrs,
+					      addrs_size);
+		if (err)
+			goto out_free;
+
 		err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
 	}
 
+out_free:
 	kfree(kaddrs);
 
 	return err;
@@ -2858,6 +2872,8 @@  static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
 {
 	struct sctp_prim prim;
 	struct sctp_transport *trans;
+	struct sctp_af *af;
+	int err;
 
 	if (optlen != sizeof(struct sctp_prim))
 		return -EINVAL;
@@ -2865,6 +2881,17 @@  static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
 	if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
 		return -EFAULT;
 
+	/* Allow security module to validate address but need address len. */
+	af = sctp_get_af_specific(prim.ssp_addr.ss_family);
+	if (!af)
+		return -EINVAL;
+
+	err = security_sctp_addr_list(sk, SCTP_PRIMARY_ADDR,
+				      (struct sockaddr *)&prim.ssp_addr,
+				      af->sockaddr_len);
+	if (err)
+		return err;
+
 	trans = sctp_addr_id2transport(sk, &prim.ssp_addr, prim.ssp_assoc_id);
 	if (!trans)
 		return -EINVAL;
@@ -3184,6 +3211,13 @@  static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva
 	if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim.sspp_addr))
 		return -EADDRNOTAVAIL;
 
+	/* Allow security module to validate address. */
+	err = security_sctp_addr_list(sk, SCTP_SET_PEER_PRIMARY_ADDR,
+				      (struct sockaddr *)&prim.sspp_addr,
+				      af->sockaddr_len);
+	if (err)
+		return err;
+
 	/* Create an ASCONF chunk with SET_PRIMARY parameter	*/
 	chunk = sctp_make_asconf_set_prim(asoc,
 					  (union sctp_addr *)&prim.sspp_addr);
@@ -7683,8 +7717,6 @@  void sctp_copy_sock(struct sock *newsk, struct sock *sk,
 
 	if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
 		net_enable_timestamp();
-
-	security_sk_clone(sk, newsk);
 }
 
 static inline void sctp_copy_descendant(struct sock *sk_to,
@@ -7714,6 +7746,7 @@  static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
 	struct sk_buff *skb, *tmp;
 	struct sctp_ulpevent *event;
 	struct sctp_bind_hashbucket *head;
+	struct sctp_endpoint *oldep = oldsp->ep;
 
 	/* Migrate socket buffer sizes and all the socket level options to the
 	 * new socket.
@@ -7829,6 +7862,11 @@  static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
 		newsk->sk_state = SCTP_SS_ESTABLISHED;
 	}
 
+	/* Set newsk security attributes from oldsk and connection
+	 * security attribute from ep as described in
+	 * Documentation/security/LSM-sctp.txt
+	 */
+	security_sctp_sk_clone(oldep, oldsk, newsk);
 	release_sock(newsk);
 }
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 7f4387f..c0be892 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -65,6 +65,8 @@ 
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/dccp.h>
+#include <linux/sctp.h>
+#include <net/sctp/structs.h>
 #include <linux/quota.h>
 #include <linux/un.h>		/* for Unix socket types */
 #include <net/af_unix.h>	/* for Unix socket types */
@@ -1284,8 +1286,11 @@  static inline u16 socket_type_to_security_class(int family, int type, int protoc
 	case PF_INET6:
 		switch (type) {
 		case SOCK_STREAM:
+		case SOCK_SEQPACKET:
 			if (default_protocol_stream(protocol))
 				return SECCLASS_TCP_SOCKET;
+			else if (protocol == IPPROTO_SCTP)
+				return SECCLASS_SCTP_SOCKET;
 			else
 				return SECCLASS_RAWIP_SOCKET;
 		case SOCK_DGRAM:
@@ -4033,6 +4038,23 @@  static int selinux_parse_skb_ipv4(struct sk_buff *skb,
 		break;
 	}
 
+#if IS_ENABLED(CONFIG_IP_SCTP)
+	case IPPROTO_SCTP: {
+		struct sctphdr _sctph, *sh;
+
+		if (ntohs(ih->frag_off) & IP_OFFSET)
+			break;
+
+		offset += ihlen;
+		sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
+		if (sh == NULL)
+			break;
+
+		ad->u.net->sport = sh->source;
+		ad->u.net->dport = sh->dest;
+		break;
+	}
+#endif
 	default:
 		break;
 	}
@@ -4106,6 +4128,19 @@  static int selinux_parse_skb_ipv6(struct sk_buff *skb,
 		break;
 	}
 
+#if IS_ENABLED(CONFIG_IP_SCTP)
+	case IPPROTO_SCTP: {
+		struct sctphdr _sctph, *sh;
+
+		sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
+		if (sh == NULL)
+			break;
+
+		ad->u.net->sport = sh->source;
+		ad->u.net->dport = sh->dest;
+		break;
+	}
+#endif
 	/* includes fragments */
 	default:
 		break;
@@ -4317,8 +4352,8 @@  static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
 
 	/*
 	 * If PF_INET or PF_INET6, check name_bind permission for the port.
-	 * Multiple address binding for SCTP is not supported yet: we just
-	 * check the first address now.
+	 * Multiple address binding for SCTP is supported via
+	 * selinux_sctp_addr_list().
 	 */
 	family = sk->sk_family;
 	if (family == PF_INET || family == PF_INET6) {
@@ -4376,6 +4411,10 @@  static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
 			node_perm = DCCP_SOCKET__NODE_BIND;
 			break;
 
+		case SECCLASS_SCTP_SOCKET:
+			node_perm = SCTP_SOCKET__NODE_BIND;
+			break;
+
 		default:
 			node_perm = RAWIP_SOCKET__NODE_BIND;
 			break;
@@ -4415,10 +4454,12 @@  static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
 		return err;
 
 	/*
-	 * If a TCP or DCCP socket, check name_connect permission for the port.
+	 * If a TCP, DCCP or SCTP socket, check name_connect permission
+	 * for the port.
 	 */
 	if (sksec->sclass == SECCLASS_TCP_SOCKET ||
-	    sksec->sclass == SECCLASS_DCCP_SOCKET) {
+	    sksec->sclass == SECCLASS_DCCP_SOCKET ||
+	    sksec->sclass == SECCLASS_SCTP_SOCKET) {
 		struct common_audit_data ad;
 		struct lsm_network_audit net = {0,};
 		struct sockaddr_in *addr4 = NULL;
@@ -4439,11 +4480,21 @@  static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
 		}
 
 		err = sel_netport_sid(sk->sk_protocol, snum, &sid);
+
 		if (err)
 			goto out;
 
-		perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
-		       TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
+		switch (sksec->sclass) {
+		case SECCLASS_TCP_SOCKET:
+			perm = TCP_SOCKET__NAME_CONNECT;
+			break;
+		case SECCLASS_DCCP_SOCKET:
+			perm = DCCP_SOCKET__NAME_CONNECT;
+			break;
+		case SECCLASS_SCTP_SOCKET:
+			perm = SCTP_SOCKET__NAME_CONNECT;
+			break;
+		}
 
 		ad.type = LSM_AUDIT_DATA_NET;
 		ad.u.net = &net;
@@ -4714,7 +4765,8 @@  static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
 	u32 peer_sid = SECSID_NULL;
 
 	if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
-	    sksec->sclass == SECCLASS_TCP_SOCKET)
+	    sksec->sclass == SECCLASS_TCP_SOCKET ||
+	    sksec->sclass == SECCLASS_SCTP_SOCKET)
 		peer_sid = sksec->peer_sid;
 	if (peer_sid == SECSID_NULL)
 		return -ENOPROTOOPT;
@@ -4827,6 +4879,149 @@  static void selinux_sock_graft(struct sock *sk, struct socket *parent)
 	sksec->sclass = isec->sclass;
 }
 
+static int selinux_sctp_assoc_request(struct sock *sk, struct sk_buff *skb)
+{
+	struct sk_security_struct *sksec = sk->sk_security;
+	struct common_audit_data ad;
+	struct lsm_network_audit net = {0,};
+	u8 peerlbl_active;
+	int err;
+
+	peerlbl_active = selinux_peerlbl_enabled();
+
+	if (sksec->peer_sid == SECINITSID_UNLABELED && peerlbl_active) {
+		/* Here because this is the first association on this
+		 * socket that is always unlabeled, therefore set
+		 * sksec->peer_sid to new peer ctx. For further info see:
+		 * Documentation/security/SELinux-sctp.txt
+		 */
+		err = selinux_skb_peerlbl_sid(skb, sk->sk_family,
+					      &sksec->peer_sid);
+		if (err)
+			return err;
+	}
+
+	ad.type = LSM_AUDIT_DATA_NET;
+	ad.u.net = &net;
+	ad.u.net->sk = sk;
+
+	err = avc_has_perm(sksec->sid, sksec->peer_sid, sksec->sclass,
+			   SCTP_SOCKET__ASSOCIATION, &ad);
+	return err;
+}
+
+static int selinux_sctp_accept_conn(struct sctp_endpoint *ep,
+				    struct sk_buff *skb)
+{
+	struct sk_security_struct *sksec = ep->base.sk->sk_security;
+	int err;
+	u32 connsid;
+	u32 peersid;
+
+	/* Have COOKIE ECHO so compute the MLS component for the connection
+	 * and store the information in ep. This will only be used by
+	 * TCP/peeloff connections as they cause a new socket to be generated.
+	 * selinux_sctp_sk_clone() will then plug this into the new socket
+	 * as described in Documentation/security/LSM-sctp.txt
+	 */
+	err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family, &peersid);
+	if (err)
+		return err;
+
+	err = selinux_conn_sid(sksec->sid, peersid, &connsid);
+	if (err)
+		return err;
+
+	ep->secid = connsid;
+	ep->peer_secid = peersid;
+
+	return 0;
+}
+
+static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
+				  struct sock *newsk)
+{
+	struct sk_security_struct *sksec = sk->sk_security;
+	struct sk_security_struct *newsksec = newsk->sk_security;
+
+	newsksec->sid = ep->secid;
+	newsksec->peer_sid = ep->peer_secid;
+	newsksec->sclass = sksec->sclass;
+	selinux_netlbl_sk_security_reset(newsksec);
+}
+
+/* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting based
+ * on their @optname.
+ */
+static int selinux_sctp_addr_list(struct sock *sk, int optname,
+				  struct sockaddr *address, int addrlen)
+{
+	int len, err = 0, walk_size = 0;
+	void *addr_buf;
+	struct sockaddr *addr;
+	struct socket *sock;
+
+	switch (optname) {
+	case SCTP_SOCKOPT_BINDX_ADD:
+		err = sock_has_perm(current, sk, SCTP_SOCKET__BINDX_ADD);
+		break;
+	case SCTP_SOCKOPT_CONNECTX:
+		err = sock_has_perm(current, sk, SCTP_SOCKET__CONNECTX);
+		break;
+	/* These need just BIND or CONNECT permissions. */
+	case SCTP_PRIMARY_ADDR:
+	case SCTP_SET_PEER_PRIMARY_ADDR:
+	case SCTP_PARAM_SET_PRIMARY:
+	case SCTP_PARAM_ADD_IP:
+		break;
+	default:
+		err = -EINVAL;
+	}
+	if (err)
+		return err;
+
+	/* Process one or more addresses that may be IPv4 or IPv6 */
+	sock = sk->sk_socket;
+	addr_buf = address;
+
+	while (walk_size < addrlen) {
+		addr = addr_buf;
+		switch (addr->sa_family) {
+		case PF_INET:
+			len = sizeof(struct sockaddr_in);
+			break;
+		case PF_INET6:
+			len = sizeof(struct sockaddr_in6);
+			break;
+		default:
+			return -EPROTONOSUPPORT;
+		}
+
+		err = -EINVAL;
+		switch (optname) {
+		/* Bind checks */
+		case SCTP_PRIMARY_ADDR:
+		case SCTP_SET_PEER_PRIMARY_ADDR:
+		case SCTP_SOCKOPT_BINDX_ADD:
+			err = selinux_socket_bind(sock, addr, len);
+			break;
+		/* Connect checks */
+		case SCTP_SOCKOPT_CONNECTX:
+		case SCTP_PARAM_SET_PRIMARY:
+		case SCTP_PARAM_ADD_IP:
+			err = selinux_socket_connect(sock, addr, len);
+			break;
+		}
+
+		if (err)
+			return err;
+
+		addr_buf += len;
+		walk_size += len;
+	}
+	return 0;
+}
+
 static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 				     struct request_sock *req)
 {
@@ -6252,6 +6447,10 @@  static struct security_hook_list selinux_hooks[] = {
 	LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security),
 	LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid),
 	LSM_HOOK_INIT(sock_graft, selinux_sock_graft),
+	LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request),
+	LSM_HOOK_INIT(sctp_accept_conn, selinux_sctp_accept_conn),
+	LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone),
+	LSM_HOOK_INIT(sctp_addr_list, selinux_sctp_addr_list),
 	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/include/classmap.h b/security/selinux/include/classmap.h
index 1f1f4b2..b76ed42 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -165,5 +165,8 @@  struct security_class_mapping secclass_map[] = {
 	  { COMMON_CAP_PERMS, NULL } },
 	{ "cap2_userns",
 	  { COMMON_CAP2_PERMS, NULL } },
+	{ "sctp_socket",
+	  { COMMON_SOCK_PERMS, "node_bind", "name_connect", "association",
+	    "bindx_add", "connectx", NULL } },
 	{ NULL }
   };