diff mbox

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

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

Commit Message

Richard Haines Dec. 14, 2016, 1:39 p.m. UTC
Add SELinux support for the SCTP protocol. The SELinux-sctp.txt document
describes how the patch has been implemented with an example policy and
tests using lkstcp-tools.

Patches to assist the testing of this kernel patch are:
1) Support the new SCTP portcon statement used in the test CIL policy
module shown in Documentation/security/SELinux-sctp.txt can be found
at [1].
2) Add SELinux support for the http://lksctp.sourceforge.net/ apps
sctp_test.c and sctp_darn.c can be found at [2].

Built and tested on Fedora 25 with linux-4.8 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/lksctp-tools-Add-SELinux-support-to-sctp_test-and-sc.patch

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 Documentation/security/SELinux-sctp.txt | 508 ++++++++++++++++++++++++++++++++
 include/linux/lsm_hooks.h               |  27 ++
 include/linux/security.h                |  16 +
 net/sctp/sm_statefuns.c                 |  12 +
 net/sctp/socket.c                       |  16 +
 security/security.c                     |  18 ++
 security/selinux/Makefile               |   2 +
 security/selinux/hooks.c                | 124 +++++++-
 security/selinux/include/classmap.h     |   4 +
 security/selinux/include/sctp.h         |  50 ++++
 security/selinux/include/sctp_private.h |  39 +++
 security/selinux/netlabel.c             |   3 +
 security/selinux/sctp.c                 | 194 ++++++++++++
 13 files changed, 1002 insertions(+), 11 deletions(-)
 create mode 100644 Documentation/security/SELinux-sctp.txt
 create mode 100644 security/selinux/include/sctp.h
 create mode 100644 security/selinux/include/sctp_private.h
 create mode 100644 security/selinux/sctp.c

Comments

David Laight Dec. 14, 2016, 2:01 p.m. UTC | #1
From: Richard Haines
> Sent: 14 December 2016 13:40
> Add SELinux support for the SCTP protocol. The SELinux-sctp.txt document
> describes how the patch has been implemented with an example policy and
> tests using lkstcp-tools.
...
> +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> +                         associated after (optionally) calling bind(2)
> +                         if given the "bind_add" permission.

Does restricting bindx make any sense at all?
The only addresses than can be specified are those of local interfaces.
If bindx isn't called then the default is to include the addresses of
all local interfaces.
So bindx only actually removes local addresses, it doesn't add them.

	David

--
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
Casey Schaufler Dec. 14, 2016, 5:02 p.m. UTC | #2
On 12/14/2016 5:39 AM, Richard Haines wrote:
> Add SELinux support for the SCTP protocol. The SELinux-sctp.txt document
> describes how the patch has been implemented with an example policy and
> tests using lkstcp-tools.

Please separate the LSM support from the SELinux support
into patches 1/2 and 2/2 respectively. The documentation
needs to be separated along the same lines as well. While
I am only mildly concerned about the SELinux implementation
I am very concerned about the LSM side.


> Patches to assist the testing of this kernel patch are:
> 1) Support the new SCTP portcon statement used in the test CIL policy
> module shown in Documentation/security/SELinux-sctp.txt can be found
> at [1].
> 2) Add SELinux support for the http://lksctp.sourceforge.net/ apps
> sctp_test.c and sctp_darn.c can be found at [2].
>
> Built and tested on Fedora 25 with linux-4.8 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/lksctp-tools-Add-SELinux-support-to-sctp_test-and-sc.patch
>
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> ---
>  Documentation/security/SELinux-sctp.txt | 508 ++++++++++++++++++++++++++++++++
>  include/linux/lsm_hooks.h               |  27 ++
>  include/linux/security.h                |  16 +
>  net/sctp/sm_statefuns.c                 |  12 +
>  net/sctp/socket.c                       |  16 +
>  security/security.c                     |  18 ++
>  security/selinux/Makefile               |   2 +
>  security/selinux/hooks.c                | 124 +++++++-
>  security/selinux/include/classmap.h     |   4 +
>  security/selinux/include/sctp.h         |  50 ++++
>  security/selinux/include/sctp_private.h |  39 +++
>  security/selinux/netlabel.c             |   3 +
>  security/selinux/sctp.c                 | 194 ++++++++++++
>  13 files changed, 1002 insertions(+), 11 deletions(-)
>  create mode 100644 Documentation/security/SELinux-sctp.txt
>  create mode 100644 security/selinux/include/sctp.h
>  create mode 100644 security/selinux/include/sctp_private.h
>  create mode 100644 security/selinux/sctp.c
>
> diff --git a/Documentation/security/SELinux-sctp.txt b/Documentation/security/SELinux-sctp.txt
> new file mode 100644
> index 0000000..dcad4b2
> --- /dev/null
> +++ b/Documentation/security/SELinux-sctp.txt

Break this into SELinux-sctp.txt and LSM-sctp.txt so that
maintainers of other security modules can review and possibly
use the information about the LSM interface.

> @@ -0,0 +1,508 @@
> +                               SCTP SELinux Support
> +                              ======================
> +
> +Security Hooks
> +===============
> +security_sk_setsockopt()
> +-------------------------
> +A new security hook security_sk_setsockopt() is introduced that checks
> +permissions before setting the options associated with sock @sk.
> +This is supported in security/selinux/hooks.c and net/sctp/socket.c
> +
> +An example usage is where sctp_getsockopt_connectx3() and
> +__sctp_setsockopt_connectx() manage the @optval data into a certain
> +state before security_sk_setsockopt is called for permission checks. This
> +means that the code to manage options does not need to be replicated in the
> +security module. See include/linux/lsm_hooks.h for details.
> +
> +security_sctp_assoc_request()
> +-----------------------------
> +security/selinux/hooks.c selinux_sctp_assoc_request() has been introduced to
> +support SCTP and obtains the sock peer context if first association and
> +also checks the association permission as shown in the "SCTP Peer Labeling
> +and Permission Checks" section below.

This hook needs to support permissions based on security module
data that may not be an SELinux context. A Smack label, for example,
or a combination in the proposed "extreme stacking" case. It's
possible that it does, but the description of the LSM interface
should make that clear.

> +
> +The security_sctp_assoc_request() security hook has been added to
> +net/sctp/sm_statefuns.c where it passes the sk and chunk->skb to the security
> +module.

What is this hook supposed to do?

> +
> +security_sk_clone()
> +--------------------
> +Added to net/sctp/socket.c sctp_sock_migrate() for cloning the security
> +context on a new socket.
> +
> +

All the following talk about SELinux policy needs to go in
the SELinux specific documentation.

> +Policy Statements
> +==================
> +A new object class "sctp_socket" has been introduced with the following SCTP
> +specific permissions: "association" "bindx_add" "bindx_rem" "connectx"
> +"peeloff" "set_addr" and "set_params". These are explained in the sections
> +below.
> +
> +Kernel policy language
> +-----------------------
> +class sctp_socket
> +class sctp_socket inherits socket { node_bind name_connect association
> +        bindx_add bindx_rem connectx peeloff set_addr set_params }
> +
> +CIL policy language
> +--------------------
> +(classcommon sctp_socket socket)
> +(class sctp_socket (node_bind name_connect association bindx_add bindx_rem
> +       connectx peeloff set_addr set_params))
> +(classorder (unordered sctp_socket))
> +
> +If userspace tools have been updated (see "Testing" section), then the portcon
> +statement may be used as shown in the following example:
> +(portcon sctp (2000 20000) (system_u object_r port_test_t ((s0) (s0))))
> +
> +Rule validation parameters used when 'network_peer_controls = 1':
> +-------------------------------------------------------------------------------
> +Rule  Source   Target     Class        Permissions
> +-------------------------------------------------------------------------------
> +allow domain_t self     : sctp_socket {connectx peeloff set_addr set_params};
> +allow domain_t socket_t : sctp_socket {bindx_add bindx_rem set_params peeloff};
> +allow socket_t port_t   : sctp_socket {name_bind name_connect};
> +allow socket_t node_t   : sctp_socket {node_bind};
> +allow socket_t peer_t   : sctp_socket {associate};
> +allow peer_t   netif_t  : netif       {ingress egress};
> +allow peer_t   node_t   : node        {recvfrom sendto};
> +allow socket_t peer_t   : peer        {recv};
> +allow domain_t packet_t : packet      {send recv relabelto}
> +
> +
> +SCTP Socket Option Permissions
> +===============================
> +The permissions consist of: "bindx_add" "bindx_rem" "connectx" "set_addr" and
> +"set_params" that are validated on setsockopt(2) calls, and "peeloff" that is
> +validated on getsockopt(2) calls.
> +
> +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> +                         associated after (optionally) calling bind(2)
> +                         if given the "bind_add" permission.
> +
> +SCTP_SOCKOPT_CONNECTX  - Allows the allocation of multiple
> +                         addresses for reaching a multi-homed peer
> +                         if given the "connectx" permission.
> +
> +  Together they are used to form SCTP associations with information being
> +  passed over the link to inform the peer of any changes. As these two options
> +  can support multiple addresses, each address is checked via
> +  selinux_socket_bind() or selinux_socket_connect() to determine whether they
> +  have the correct permissions:
> +    bindx_add: bind, name_bind, node_bind + node SID + port SID via the
> +               (portcon sctp port ctx) policy statement.
> +    connectx:  connect, name_connect + port SID via the
> +               (portcon sctp port ctx) policy statement.
> +
> +SCTP_SOCKOPT_BINDX_REM - Allows additional bind addresses to be removed
> +                         if given the "bind_rem" permission.
> +
> +SCTP_PEER_ADDR_PARAMS - Alter heartbeats and address max retransmissions.
> +SCTP_PEER_ADDR_THLDS  - Alter the thresholds.
> +SCTP_ASSOCINFO        - Alter association and endpoint parameters.
> + These require the "set_params" permission.
> +
> +SCTP_PRIMARY_ADDR          - Set local primary address.
> +SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as association primary.
> + These require the "set_addr" permission.
> +
> +SCTP_SOCKOPT_PEELOFF - Branch off an association into a new socket that
> +will be a one-to-one style socket. As SELinux already handles the creation
> +of new sockets, only the "peeloff" permission is checked.

Where is the SELinux specific decision? In the hook placement or the
SELinux code?

> +
> +
> +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 { associate };
> +
> +As SCTP supports multiple endpoints on a single socket it is possible that
> +each interface may be configured with a different peer label, however it is
> +recommended that the labels are consistent.
> +
> +NOTES:
> +   1) If peer labeling is not enabled, then the peer context will always be
> +      SECINITSID_UNLABELED (unlabeled_t in Reference Policy).
> +
> +   2) If using NetLabel fallback labeling "netlabelctl unlbl ..." be aware
> +      that if a label is assigned to a specific interface, and that interface
> +      'goes down' (as in the "Multi-homing Test" section), 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).
> +
> +   3) SCTP sockets inherit their labels from the creating process (unless
> +      there are policy rules to change this). They do NOT follow the TCP
> +      labeling method even for TCP-style sockets. For reference: TCP child
> +      sockets take the TE information from the parent server socket, but the
> +      MLS/MCS information from the connection when CIPSO is enabled.

Is this a detail of SCTP, of the SELinux implementation or of SELinux policy?

> +
> +   4) getpeercon(3) may be used by userspace apps to retrieve the sockets
> +      peer context.
> +
> +   5) The peer labeling rules apply as discussed in the following set of
> +      posts tagged "netlabel" at: http://www.paul-moore.com/blog/t

Is this SCTP, netlabel or SELinux?

> +
> +
> +      SCTP endpoint "A"                                SCTP endpoint "Z"
> +      =================                                =================
> +    sctp_sf_do_prm_asoc()
> + Initiate an association to
> + SCTP peer endpoint "Z".
> + Send INIT first as we need to obtain
> + a peer label before checking whether
> + this is allowed or not. This will be
> + checked once the INIT ACK has been
> + received.
> +         INIT --------------------------------------------->
> +                                                   sctp_sf_do_5_1B_init()
> +                                                 Respond to an INIT chunk.
> +                                             SCTP peer endpoint "A" is
> +                                             asking for an association. Call
> +                                             security_sctp_assoc_request()
> +                                             to set the peer label if first
> +                                             association, then check ASSOCIATE
> +                                             permission:
> +                             allow socket_t peer_t : sctp_socket { associate };
> +                                             IF valid send:
> +          <----------------------------------------------- INIT ACK
> +          |                                  ELSE audit event and silently
> +          |                                       discard the packet.
> +    sctp_sf_do_5_1C_ack
> + Respond to an INIT ACK chunk.
> + SCTP peer endpoint"A" initiated
> + this association to SCTP peer
> + endpoint "Z". The security checks
> + are done now as we have a peer
> + label to check against, so call
> + security_sctp_assoc_request()
> + to set the peer label if first
> + association, then check ASSOCIATE
> + permission:
> +    allow socket_t peer_t : sctp_socket { associate };
> + IF valid send:
> +    COOKIE ECHO ------------------------------------------------>
> + ELSE audit event and silently                                  |
> +      discard the packet.                                       |
> +                                                                |
> +          <----------------------------------------------- COOKIE ACK
> +          |                                                     |
> +   sctp_sf_do_5_1E_ca                                  sctp_sf_do_5_1D_ce
> +      ESTABLISHED                                          ESTABLISHED
> +          |                                                     |
> +    ------------------------------------------------------------------
> +    |                     Association Established                    |
> +    ------------------------------------------------------------------
> +
> +
> +Testing
> +========
> +Requirements:
> + 1) libsepol 2.5 or greater. If the sctp portcon statement is required, then
> +    libsepol must be updated with the following patch:
> +         http://arctic.selinuxproject.org/~rhaines/selinux-sctp/
> +         selinux-Add-support-for-the-SCTP-portcon-keyword.patch
> +
> + 2) A patched version of lksctp-tools (1.0.17 used for testing) to support
> +    where sctp_test and sctp_darn have been modified to display the process,
> +    peer and socket fd SELinux contexts using the -Z option. This patch is
> +    available from:
> +         http://arctic.selinuxproject.org/~rhaines/selinux-sctp/
> +         lksctp-tools-Add-SELinux-support-to-sctp_test-and-sc.patch
> +
> +    lksctp-tools can be obtained by:
> +         git clone git://github.com/sctp/lksctp-tools.git
> +
> +    The tools can then be built by adding the patch first (as it modifies
> +    Makefile.am and configure.ac), then:
> +        ./bootstrap
> +        ./configure
> +        make
> +
> +
> +All lksctp-tools/src/func_tests run correctly in enforcing mode except when
> +specific permissions are denied (e.g. test_peeloff_v6 will fail with
> +"test_peeloff.c  1 BROK : sctp_peeloff: Permission denied").
> +
> +Tests involving removal of the "association" permission will wait, simply
> +because the INIT or INIT ACK packets will be silently discarded, however as
> +with all AVC denials they are audited in the audit log.
> +
> +The following sections describe the tests run using a CIL module added to the
> +Fedora 'targeted' policy. The CIL policy is listed at the end of this document.
> +
> +During tests the audit.log should be monitored as there are 'auditallow'
> +statements in the policy to show packet labels.
> +
> +The tests cover:
> + 1) All the lksctp-tools/src/func_tests. To check that permissions are
> +    correctly denied, use '(not ( <perm> ...' rules as shown in the
> +    CIL policy "Define SCTP class and permissions" section.
> +
> + 2) Using lksctp-tools/src/apps/sctp_test and sctp_darn that has been modified
> +    to display the process, peer and socket fd SELinux contexts using the -Z
> +    option.
> +
> + 3) Running lksctp-tools/src/apps/sctp_darn for multi-homing tests between
> +    client/server as shown in the following diagram:
> +
> +                      ------- Wireless Router -------
> +                     /                               \
> +                    /                                 \
> +                   /                                   \
> +             192.168.1.77                          192.168.1.66
> +                 /                                       \
> +           ----------  Ethernet         193.168.1.67 ----------
> +           | CLIENT | <----------------------------> | SERVER |
> +           ---------- 193.168.1.78                   ----------
> +
> +
> +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Testing Setup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> +
> +Tested on Fedora 25 with kernel 4.8.11, only the client side results are shown.
> +
> +Initial setup before running any tests (modifying interfaces and addresses
> +as required):
> +
> +1) Create and add the CIL policy module (see the "CIL policy module" section):
> +       semodule --priority 400 -i sctp_test_module.cil
> +
> +2) Update iptables to allow sctp traffic (MUST be run on client and server):
> +      iptables -I INPUT 1 -p sctp -j ACCEPT
> +
> +3) Set the fallback peer labels:
> +      netlabelctl unlbl add interface:lo address:127.0.0.0/8 label:system_u:object_r:netlabel_peer_lo_t:s0
> +      netlabelctl unlbl add interface:lo address:::1 label:system_u:object_r:netlabel_peer_lo_t:s0
> +      netlabelctl unlbl add interface:wlp6s0 address:192.168.1.0/24 label:system_u:object_r:netlabel_peer_wlan_t:s0
> +      netlabelctl unlbl add interface:enp7s0 address:193.168.1.0/24 label:system_u:object_r:netlabel_peer_eth_t:s0
> +
> +   The 'netlabelctl unlbl list' command can then be used to check the entries.
> +
> +4) Set SECMARK labels on SCTP packets. It is easier to paste the 'security' iptable
> +   entries below into a script:
> +
> +############################ SECMARK IPTABLE ENTRIES ########################
> +#
> +# Flush the security table first:
> +iptables -t security -F
> +
> +#-------------- INPUT IP Stream --------------------#
> +# This INPUT rule sets all packets to default_packet_t:
> +iptables -t security -A INPUT -j SECMARK --selctx system_u:object_r:default_packet_t:s0
> +
> +# These rules will replace the above context if sctp ports 1024:1035 are found in the packets:
> +iptables -t security -A INPUT -i lo -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_lo_t:s0
> +iptables -t security -A INPUT -i enp7s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_eth_t:s0
> +iptables -t security -A INPUT -i wlp6s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_wlan_t:s0
> +
> +iptables -t security -A INPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
> +
> +#-------------- OUTPUT IP Stream --------------------#
> +# This OUTPUT rule sets all packets to default_packet_t:
> +iptables -t security -A OUTPUT -j SECMARK --selctx system_u:object_r:default_packet_t:s0
> +
> +# These rules will replace the above context if sctp ports 1024:1035 are found in the packets:
> +iptables -t security -A OUTPUT -o lo -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_lo_t:s0
> +iptables -t security -A OUTPUT -o enp7s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_eth_t:s0
> +iptables -t security -A OUTPUT -o wlp6s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_wlan_t:s0
> +
> +iptables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
> +
> +iptables -t security -L
> +#
> +#################### END OF IPTABLES SECURITY TABLE ##################
> +
> +;
> +;;;;;;;;;;;;;;;;;;;; Running func_tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> +;
> +1) Paste below into script and run tests by passing over fqn path to lksctp-tools
> +   All tests should pass.
> +
> +   To test denial of permissions see the "Define SCTP class and permissions"
> +   section in the sample CIL policy.
> +
> +#!/bin/bash
> +
> +list="test_1_to_1_accept_close test_1_to_1_addrs test_1_to_1_connect test_1_to_1_connectx test_1_to_1_events test_1_to_1_initmsg_connect test_1_to_1_nonblock test_1_to_1_recvfrom test_1_to_1_recvmsg test_1_to_1_rtoinfo test_1_to_1_send test_1_to_1_sendmsg test_1_to_1_sendto test_1_to_1_shutdown test_1_to_1_socket_bind_listen test_1_to_1_sockopt test_1_to_1_threads test_assoc_abort test_assoc_shutdown test_autoclose test_basic test_basic_v6 test_connect test_connectx test_fragments test_fragments_v6 test_getname test_getname_v6 test_inaddr_any test_inaddr_any_v6 test_peeloff test_peeloff_v6 test_recvmsg test_sctp_sendrecvmsg test_sctp_sendrecvmsg_v6 test_sockopt test_sockopt_v6 test_tcp_style test_tcp_style_v6 test_timetolive test_timetolive_v6"
> +
> +if [ "$1" = "" ]; then
> +	echo "Require path to lksctp-tools"
> +	exit
> +fi
> +
> +for i in $list
> +    do  "$1/lksctp-tools/src/func_tests/$i"
> +        if [ $? != 0 ]; then
> +            echo -e "\nfunc_test $i FAILED\n"
> +            exit
> +       fi
> +    done
> +
> +echo -e "\nAll func_tests passed.\n"
> +
> +;
> +;;;;;;;;;;;;;;;;;;; Running sctp_test to check Peer contexts ;;;;;;;;;;;;;;;;;;
> +;
> +1) To check peer contexts run the following on the Server:
> +    /path/to/lksctp-tools/src/apps/sctp_test -H 192.168.1.66 -B 193.168.1.67 -P 1035 -l -Z
> +
> +2) To check WLAN peer context run the following on the Client:
> +    /path/to/lksctp-tools/src/apps/sctp_test -H 192.168.1.77 -P 1024 -C 192.168.1.66 -p 1035 -a 1 -c 4 -x 1 -o 2 -m 65515 -s -Z
> +
> +    The Client peer context should be:
> +       sendmsg peer context=system_u:object_r:netlabel_peer_wlan_t:s0
> +    The Client SECMARK context from the audit log should be:
> +       tcontext=system_u:object_r:sctp_packet_wlan_t:s0 tclass=packet
> +
> +3) To check ETH peer context run the following on the Client:
> +    /path/to/lksctp-tools/src/apps/sctp_test -H 193.168.1.78 -P 1024 -C 193.168.1.67 -p 1035 -a 1 -c 4 -x 1 -o 2 -m 65515 -s -Z
> +
> +    The Client peer context should be:
> +       sendmsg peer context=system_u:object_r:netlabel_peer_eth_t:s0
> +    The Client SECMARK context from the audit log should be:
> +       tcontext=system_u:object_r:sctp_packet_eth_t:s0 tclass=packet
> +
> +
> +;;;;;;;;;;;;;;;;;;;;; Running Multi-Homing Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> +
> +1) Run sctp_darn on server and client as follows:
> +    Server:
> +  /path/to/lksctp-tools/src/apps/sctp_darn -H 192.168.1.66 -B 193.168.1.67 -P 1035 -l -Z
> +    Client:
> +  /path/to/lksctp-tools/src/apps/sctp_darn -H 192.168.1.77 -B 193.168.1.78 -P 1024 -c 192.168.1.66 -c 193.168.1.67 -p 1035 -s -Z
> +
> +2) Send data from Client to Server - saddr 192.168.1.77 daddr 192.168.1.66
> +3) Turn Server Wifi off (192.168.1.66)
> +4) Send data from Client to Server - saddr 192.168.1.77 daddr 193.168.1.67
> +
> +Note that the peer context will be "netlabel_peer_wlan_t" even when the
> +Server side Wifi is turned off. This is because the first association
> +on the sctp socket sets the peer context to the first connection (in this
> +case from the Server's wireless lan addr (192.168.1.66). It is therefore
> +advised that the peer context is common across interfaces/addresses used
> +by SCTP, note however that the SECMARK packet contexts will reflect the
> +--selctx entry set in the iptables rules for interfaces/addresses.
> +
> +To set "netlabel_peer_eth_t", swap the peer socket 'connectx' (-c options)
> +addresses on the Client as follows:
> +
> +  /path/to/lksctp-tools/src/apps/sctp_darn -H 192.168.1.77 -B 193.168.1.78 -P 1024 -c 193.168.1.67 -c 192.168.1.66 -p 1035 -s -Z
> +
> +The first connection will then be from the Server's ethernet addr (193.168.1.67).
> +
> +tcpdump(8) or tshark(1) may be used to monitor traffic on each interface,
> +for example:
> +    tcpdump -v -x -i lo sctp
> +    tshark -O SCTP -P -x -i enp7s0
> +
> +;
> +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CIL policy module ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> +;
> +Paste the following CIL policy module into a file named "sctp_test_module.cil"
> +The policy needs to be modified if libsepol has NOT been updated to support the
> +new sctp portcon statement (see "Select SCTP portcon" section).
> +To load the policy use:
> +   semodule --priority 400 -i sctp_test_module.cil
> +
> +The policy MUST be loaded before attempting to set any netlabel or iptables
> +entries.
> +
> +Once testing is complete the CIL policy module may be removed by:
> +   semodule -r sctp_test_module
> +
> +;
> +;;;;;;;;;;;;;;;;;;;;;;;; CIL SCTP POLICY MODULE START ;;;;;;;;;;;;;;;;;;;;;;;;;
> +;
> +;;;;;;;;;;;;;;;;;;;;;;;; Define SCTP class and permissions ;;;;;;;;;;;;;;;;;;;;
> +;
> +; Add class for sctp_socket (requires libsepol 2.5+)
> +(classorder (unordered sctp_socket))
> +(classcommon sctp_socket socket)
> +(class sctp_socket (node_bind name_connect association bindx_add bindx_rem
> +    connectx peeloff set_addr set_params))
> +;
> +; Add permission for testing (see notes below)
> +(classpermission sctp_socket_all_perms)
> +;
> +; For testing whether the "bindx_add bindx_rem connectx peeloff set_addr
> +; set_params" permissions deny access use (not ( <perm> ... rules,
> +; for example:
> +;   (classpermissionset sctp_socket_all_perms (sctp_socket (not (set_params))))
> +;
> +; Once completed use this to grant all required permissions:
> +(classpermissionset sctp_socket_all_perms (sctp_socket (all)))
> +;
> +; To test that "node_bind name_connect association" permissions deny access
> +; remove the permissions from the applicable allow rules below.
> +;
> +;;;;;;;;;;;;;;;;;;;;;;;; Define peer labels and rules ;;;;;;;;;;;;;;;;;;;;;;;;;
> +;
> +(type netlabel_peer_lo_t)
> +(type netlabel_peer_wlan_t)
> +(type netlabel_peer_eth_t)
> +(roletype object_r netlabel_peer_wlan_t)
> +(roletype object_r netlabel_peer_eth_t)
> +(typeattribute sctp_peers)
> +(typeattributeset sctp_peers (netlabel_peer_lo_t netlabel_peer_wlan_t
> +    netlabel_peer_eth_t))
> +
> +(allow unconfined_t sctp_peers (sctp_socket (association)))
> +(allow unconfined_t sctp_peers (peer (recv)))
> +(allow sctp_peers netif_t (netif (ingress egress)))
> +(allow sctp_peers node_t (node (recvfrom)))
> +
> +;
> +;;;;;;;;;;;;;;;;;;; Define SECMARK packet labels and rules ;;;;;;;;;;;;;;;;;;;;
> +;
> +; All packets other than sctp with ports 1024 - 1035 are SECMARK'ed using
> +; iptables with default_packet_t. There is an 'allow' rule for this because
> +; SCTP func_tests try illegal addresses, so needed to pass tests, plus all
> +; other network traffic requires system access.
> +(type default_packet_t)
> +(type sctp_packet_lo_t)
> +(type sctp_packet_wlan_t)
> +(type sctp_packet_eth_t)
> +(roletype object_r default_packet_t)
> +(roletype object_r sctp_packet_lo_t)
> +(roletype object_r sctp_packet_wlan_t)
> +(roletype object_r sctp_packet_eth_t)
> +(typeattribute sctp_packets)
> +(typeattributeset sctp_packets (default_packet_t sctp_packet_lo_t
> +    sctp_packet_wlan_t sctp_packet_eth_t))
> +
> +(allow unconfined_t sctp_packets (packet(send recv relabelto)))
> +
> +; Add audit rule to monitor packet labeling:
> +(typeattribute audit_sctp_packets)
> +(typeattributeset audit_sctp_packets (sctp_packet_lo_t sctp_packet_wlan_t
> +    sctp_packet_eth_t))
> +(auditallow unconfined_t audit_sctp_packets (packet(send recv)))
> +
> +;
> +;;;;;;;;;;;;;;;;;;;;;;;;;;; Select SCTP portcon ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> +;
> +; If libsepol has been updated to support the "sctp" portcon keyword then
> +; enable the TRUE set of statements, else use FALSE statement.
> +;
> +; TRUE:
> +    (type sctp_port_t)
> +    (roletype object_r sctp_port_t)
> +    ; Set to (1024 1035) as func_tests start at 1024 with 10 clients (10 + 1).
> +    (portcon sctp (1024 1035) (system_u object_r sctp_port_t ((s0) (s0))))
> +    ; This allows port 0 otherwise test_1_to_1_connectx will fail as it
> +    ; tests illegal addr.
> +    (portcon sctp 0 (system_u object_r sctp_port_t ((s0) (s0))))
> +    (allow unconfined_t sctp_port_t (sctp_socket (name_bind name_connect)))
> +;
> +; FALSE:
> +;    ; need to allow port initial SID:
> +;    (allow unconfined_t port_t (sctp_socket (name_bind name_connect)))
> +
> +; Common allow rules:
> +(allow unconfined_t self sctp_socket_all_perms)
> +(allow unconfined_t node_t (sctp_socket (node_bind)))
> +
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index f2af2af..6a7ddaf 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -813,6 +813,22 @@
>   *	@level contains the protocol level to set options for.
>   *	@optname contains the name of the option to set.
>   *	Return 0 if permission is granted.
> + * @sk_setsockopt:
> + *	Check permissions before setting the options associated with sock @sk.
> + *	This is equivalent to @socket_setsockopt, except that it has the
> + *	option parameters, as it is intended to support permission checking
> + *	of options and their parameters within network services.
> + *	An example usage is in net/sctp/socket.c where
> + *	sctp_getsockopt_connectx3() manages the @optval data into a certain
> + *	state before calling __sctp_setsockopt_connectx() that calls
> + *	@sk_setsockopt for permission checks. This means the code to manage
> + *	@optval does not need to be replicated in the security module.
> + *	@sk contains the sock structure.
> + *	@level contains the protocol level to set options for.
> + *	@optname contains the name of the option to set.
> + *	@optval contains the value(s) to set (already copied from userspace).
> + *	@optlen contains the length of the value(s) to be set.
> + *	Return 0 if permission is granted.
>   * @socket_shutdown:
>   *	Checks permission before all or part of a connection on the socket
>   *	@sock is shut down.
> @@ -902,6 +918,12 @@
>   *	This hook can be used by the module to update any security state
>   *	associated with the TUN device's security structure.
>   *	@security pointer to the TUN devices's security structure.
> + * @sctp_assoc_request:
> + *	Update socket peer label if first association on @sk then check
> + *	whether association allowed.
> + *	@sk contains the sock structure.
> + *	@skb skbuff of association packet (INIT or INIT ACK) being queried.
> + *	Return 0 on success, error on failure.
>   *
>   * Security hooks for XFRM operations.
>   *
> @@ -1582,6 +1604,8 @@ union security_list_options {
>  	int (*socket_getpeername)(struct socket *sock);
>  	int (*socket_getsockopt)(struct socket *sock, int level, int optname);
>  	int (*socket_setsockopt)(struct socket *sock, int level, int optname);
> +	int (*sk_setsockopt)(struct sock *sk, int level, int optname,
> +				    char *optval, int optlen);
>  	int (*socket_shutdown)(struct socket *sock, int how);
>  	int (*socket_sock_rcv_skb)(struct sock *sk, struct sk_buff *skb);
>  	int (*socket_getpeersec_stream)(struct socket *sock,
> @@ -1610,6 +1634,7 @@ union security_list_options {
>  	int (*tun_dev_attach_queue)(void *security);
>  	int (*tun_dev_attach)(struct sock *sk, void *security);
>  	int (*tun_dev_open)(void *security);
> +	int (*sctp_assoc_request)(struct sock *sk, struct sk_buff *skb);
>  #endif	/* CONFIG_SECURITY_NETWORK */
>  
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
> @@ -1819,6 +1844,7 @@ struct security_hook_heads {
>  	struct list_head socket_getpeername;
>  	struct list_head socket_getsockopt;
>  	struct list_head socket_setsockopt;
> +	struct list_head sk_setsockopt;
>  	struct list_head socket_shutdown;
>  	struct list_head socket_sock_rcv_skb;
>  	struct list_head socket_getpeersec_stream;
> @@ -1841,6 +1867,7 @@ struct security_hook_heads {
>  	struct list_head tun_dev_attach_queue;
>  	struct list_head tun_dev_attach;
>  	struct list_head tun_dev_open;
> +	struct list_head sctp_assoc_request;
>  #endif	/* CONFIG_SECURITY_NETWORK */
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
>  	struct list_head xfrm_policy_alloc_security;
> diff --git a/include/linux/security.h b/include/linux/security.h
> index a6c6d5d..9572b8b 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1174,6 +1174,8 @@ int security_socket_getsockname(struct socket *sock);
>  int security_socket_getpeername(struct socket *sock);
>  int security_socket_getsockopt(struct socket *sock, int level, int optname);
>  int security_socket_setsockopt(struct socket *sock, int level, int optname);
> +int security_sk_setsockopt(struct sock *sk, int level, int optname,
> +				char *optval, int optlen);
>  int security_socket_shutdown(struct socket *sock, int how);
>  int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
>  int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
> @@ -1200,6 +1202,7 @@ int security_tun_dev_create(void);
>  int security_tun_dev_attach_queue(void *security);
>  int security_tun_dev_attach(struct sock *sk, void *security);
>  int security_tun_dev_open(void *security);
> +int security_sctp_assoc_request(struct sock *sk, struct sk_buff *skb);
>  
>  #else	/* CONFIG_SECURITY_NETWORK */
>  static inline int security_unix_stream_connect(struct sock *sock,
> @@ -1289,6 +1292,13 @@ static inline int security_socket_setsockopt(struct socket *sock,
>  	return 0;
>  }
>  
> +static inline int security_sk_setsockopt(struct sock *sk, int level,
> +					    int optname, char *optval,
> +					    int optlen)
> +{
> +	return 0;
> +}
> +
>  static inline int security_socket_shutdown(struct socket *sock, int how)
>  {
>  	return 0;
> @@ -1392,6 +1402,12 @@ static inline int security_tun_dev_open(void *security)
>  {
>  	return 0;
>  }
> +
> +static inline int security_sctp_assoc_request(struct sock *sk,
> +			struct sk_buff *skb)
> +{
> +	return 0;
> +}
>  #endif	/* CONFIG_SECURITY_NETWORK */
>  
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
> index 920469e..920b101 100644
> --- a/net/sctp/sm_statefuns.c
> +++ b/net/sctp/sm_statefuns.c
> @@ -315,6 +315,12 @@ 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.
> +	 */
> +	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 +514,12 @@ 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.
> +	 */
> +	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);
>  
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index 7b0e059..ff4f1a8 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_sk_setsockopt(sk, SOL_SCTP,
> +					     SCTP_SOCKOPT_BINDX_ADD,
> +					     (char *)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_sk_setsockopt(sk, SOL_SCTP,
> +					     SCTP_SOCKOPT_CONNECTX,
> +					    (char *)kaddrs, addrs_size);
> +		if (err)
> +			goto out_free;
> +
>  		err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
>  	}
>  
> +out_free:
>  	kfree(kaddrs);
>  
>  	return err;
> @@ -7826,6 +7840,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
>  		newsk->sk_state = SCTP_SS_ESTABLISHED;
>  	}
>  
> +	/* Ensure newsk has the same security attributes. */
> +	security_sk_clone(oldsk, newsk);
>  	release_sock(newsk);
>  }
>  
> diff --git a/security/security.c b/security/security.c
> index f825304..23ce9ea 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1307,6 +1307,14 @@ int security_socket_setsockopt(struct socket *sock, int level, int optname)
>  	return call_int_hook(socket_setsockopt, 0, sock, level, optname);
>  }
>  
> +int security_sk_setsockopt(struct sock *sk, int level, int optname,
> +				    char *optval, int optlen)
> +{
> +	return call_int_hook(sk_setsockopt, 0, sk, level, optname, optval,
> +				    optlen);
> +}
> +EXPORT_SYMBOL(security_sk_setsockopt);
> +
>  int security_socket_shutdown(struct socket *sock, int how)
>  {
>  	return call_int_hook(socket_shutdown, 0, sock, how);
> @@ -1439,6 +1447,12 @@ int security_tun_dev_open(void *security)
>  }
>  EXPORT_SYMBOL(security_tun_dev_open);
>  
> +int security_sctp_assoc_request(struct sock *sk, struct sk_buff *skb)
> +{
> +	return call_int_hook(sctp_assoc_request, 0, sk, skb);
> +}
> +EXPORT_SYMBOL(security_sctp_assoc_request);
> +
>  #endif	/* CONFIG_SECURITY_NETWORK */
>  
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
> @@ -1856,6 +1870,8 @@ struct security_hook_heads security_hook_heads = {
>  		LIST_HEAD_INIT(security_hook_heads.socket_getsockopt),
>  	.socket_setsockopt =
>  		LIST_HEAD_INIT(security_hook_heads.socket_setsockopt),
> +	.sk_setsockopt =
> +		LIST_HEAD_INIT(security_hook_heads.sk_setsockopt),
>  	.socket_shutdown =
>  		LIST_HEAD_INIT(security_hook_heads.socket_shutdown),
>  	.socket_sock_rcv_skb =
> @@ -1897,6 +1913,8 @@ struct security_hook_heads security_hook_heads = {
>  	.tun_dev_attach =
>  		LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
>  	.tun_dev_open =	LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
> +	.sctp_assoc_request =
> +		LIST_HEAD_INIT(security_hook_heads.sctp_assoc_request),
>  #endif	/* CONFIG_SECURITY_NETWORK */
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
>  	.xfrm_policy_alloc_security =
> diff --git a/security/selinux/Makefile b/security/selinux/Makefile
> index 3411c33..f60a8a3 100644
> --- a/security/selinux/Makefile
> +++ b/security/selinux/Makefile
> @@ -13,6 +13,8 @@ selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
>  
>  selinux-$(CONFIG_NETLABEL) += netlabel.o
>  
> +selinux-$(subst m,y,$(CONFIG_IP_SCTP)) += sctp.o
> +
>  ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
>  
>  $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e15e560..491599c 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -65,6 +65,7 @@
>  #include <linux/tcp.h>
>  #include <linux/udp.h>
>  #include <linux/dccp.h>
> +#include <linux/sctp.h>
>  #include <linux/quota.h>
>  #include <linux/un.h>		/* for Unix socket types */
>  #include <net/af_unix.h>	/* for Unix socket types */
> @@ -93,6 +94,7 @@
>  #include "netlabel.h"
>  #include "audit.h"
>  #include "avc_ss.h"
> +#include "sctp.h"
>  
>  /* SECMARK reference count */
>  static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
> @@ -1280,8 +1282,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:
> @@ -4034,6 +4039,23 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb,
>  		break;
>  	}
>  
> +#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
> +	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;
>  	}
> @@ -4107,6 +4129,19 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
>  		break;
>  	}
>  
> +#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
> +	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;
> @@ -4236,7 +4271,7 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
>  				       socksid);
>  }
>  
> -static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
> +int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
>  {
>  	struct sk_security_struct *sksec = sk->sk_security;
>  	struct common_audit_data ad;
> @@ -4306,7 +4341,8 @@ static int selinux_socket_post_create(struct socket *sock, int family,
>     Need to determine whether we should perform a name_bind
>     permission check between the socket and the port number. */
>  
> -static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
> +int selinux_socket_bind(struct socket *sock, struct sockaddr *address,
> +								int addrlen)
>  {
>  	struct sock *sk = sock->sk;
>  	u16 family;
> @@ -4318,8 +4354,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_setsockopt().
>  	 */
>  	family = sk->sk_family;
>  	if (family == PF_INET || family == PF_INET6) {
> @@ -4377,6 +4413,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;
> @@ -4405,7 +4445,8 @@ out:
>  	return err;
>  }
>  
> -static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
> +int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
> +								int addrlen)
>  {
>  	struct sock *sk = sock->sk;
>  	struct sk_security_struct *sksec = sk->sk_security;
> @@ -4416,10 +4457,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;
> @@ -4443,8 +4486,12 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
>  		if (err)
>  			goto out;
>  
> -		perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
> -		       TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
> +		if (sksec->sclass == SECCLASS_TCP_SOCKET)
> +			perm = TCP_SOCKET__NAME_CONNECT;
> +		else if (sksec->sclass == SECCLASS_DCCP_SOCKET)
> +			perm = DCCP_SOCKET__NAME_CONNECT;
> +		else if (sksec->sclass == SECCLASS_SCTP_SOCKET)
> +			perm = SCTP_SOCKET__NAME_CONNECT;
>  
>  		ad.type = LSM_AUDIT_DATA_NET;
>  		ad.u.net = &net;
> @@ -4516,13 +4563,35 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname
>  	if (err)
>  		return err;
>  
> +	err = selinux_sctp_setsockopt(sock->sk, level, optname, NULL, 0);
> +	if (err)
> +		return err;
> +
>  	return selinux_netlbl_socket_setsockopt(sock, level, optname);
>  }
>  
> +static int selinux_sk_setsockopt(struct sock *sk, int level, int optname,
> +					    char *optval, int optlen)
> +{
> +	int err;
> +
> +	err = sock_has_perm(current, sk, SOCKET__SETOPT);
> +	if (err)
> +		return err;
> +
> +	return selinux_sctp_setsockopt(sk, level, optname, optval, optlen);
> +}
> +
>  static int selinux_socket_getsockopt(struct socket *sock, int level,
>  				     int optname)
>  {
> -	return sock_has_perm(current, sock->sk, SOCKET__GETOPT);
> +	int err;
> +
> +	err = sock_has_perm(current, sock->sk, SOCKET__GETOPT);
> +	if (err)
> +		return err;
> +
> +	return selinux_sctp_getsockopt(sock->sk, level, optname);
>  }
>  
>  static int selinux_socket_shutdown(struct socket *sock, int how)
> @@ -4715,7 +4784,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;
> @@ -4828,6 +4898,36 @@ 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;
> +
> +	return avc_has_perm(sksec->sid, sksec->peer_sid, sksec->sclass,
> +				    SCTP_SOCKET__ASSOCIATION, &ad);
> +}
> +
>  static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
>  				     struct request_sock *req)
>  {
> @@ -6243,6 +6343,7 @@ static struct security_hook_list selinux_hooks[] = {
>  	LSM_HOOK_INIT(socket_getpeername, selinux_socket_getpeername),
>  	LSM_HOOK_INIT(socket_getsockopt, selinux_socket_getsockopt),
>  	LSM_HOOK_INIT(socket_setsockopt, selinux_socket_setsockopt),
> +	LSM_HOOK_INIT(sk_setsockopt, selinux_sk_setsockopt),
>  	LSM_HOOK_INIT(socket_shutdown, selinux_socket_shutdown),
>  	LSM_HOOK_INIT(socket_sock_rcv_skb, selinux_socket_sock_rcv_skb),
>  	LSM_HOOK_INIT(socket_getpeersec_stream,
> @@ -6253,6 +6354,7 @@ 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(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..353183a 100644
> --- a/security/selinux/include/classmap.h
> +++ b/security/selinux/include/classmap.h
> @@ -165,5 +165,9 @@ 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", "bindx_rem", "connectx", "peeloff", "set_addr",
> +	    "set_params", NULL } },
>  	{ NULL }
>    };
> diff --git a/security/selinux/include/sctp.h b/security/selinux/include/sctp.h
> new file mode 100644
> index 0000000..cee8e2d
> --- /dev/null
> +++ b/security/selinux/include/sctp.h
> @@ -0,0 +1,50 @@
> +/*
> + * SELinux SCTP Support
> + *
> + * Provides security checks for the SCTP protocol.
> + *
> + * Author: Richard Haines <richard_c_haines@btinternet.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; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +
> +#ifndef _SELINUX_SCTP_H_
> +#define _SELINUX_SCTP_H_
> +
> +#include <linux/types.h>
> +#include <linux/net.h>
> +#include <net/sock.h>
> +#include "objsec.h"
> +
> +#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
> +int selinux_sctp_setsockopt(struct sock *sk,
> +				    int level,
> +				    int optname,
> +				    char *optval,
> +				    int optlen);
> +
> +int selinux_sctp_getsockopt(struct sock *sk,
> +				    int level,
> +				    int optname);
> +#else
> +static inline int selinux_sctp_setsockopt(struct sock *sk,
> +				    int level,
> +				    int optname,
> +				    char *optval,
> +				    int optlen)
> +{
> +	return 0;
> +}
> +static inline int selinux_sctp_getsockopt(struct sock *sk,
> +				    int level,
> +				    int optname)
> +{
> +	return 0;
> +}
> +#endif  /* CONFIG_IP_SCTP */
> +
> +#endif
> diff --git a/security/selinux/include/sctp_private.h b/security/selinux/include/sctp_private.h
> new file mode 100644
> index 0000000..eaa9f4c
> --- /dev/null
> +++ b/security/selinux/include/sctp_private.h
> @@ -0,0 +1,39 @@
> +/*
> + * SELinux SCTP Support
> + *
> + * Provides security checks for the SCTP protocol.
> + *
> + * Author: Richard Haines <richard_c_haines@btinternet.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; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/types.h>
> +#include <linux/net.h>
> +#include <net/sock.h>
> +#include <linux/sctp.h>
> +#include <uapi/linux/sctp.h>	/* For bindx setsocket option checks */
> +#include <net/ip.h>
> +#include <linux/skbuff.h>
> +
> +#include "security.h"
> +#include "avc.h"
> +#include "objsec.h"
> +
> +extern int sock_has_perm(struct task_struct *task,
> +				    struct sock *sk, u32 perms);
> +
> +extern int selinux_socket_bind(struct socket *sock,
> +				    struct sockaddr *address,
> +					    int addrlen);
> +
> +extern int selinux_socket_connect(struct socket *sock,
> +				    struct sockaddr *address,
> +				    int addrlen);
> +
> diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
> index aaba667..300a195 100644
> --- a/security/selinux/netlabel.c
> +++ b/security/selinux/netlabel.c
> @@ -399,6 +399,9 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
>  	case SECCLASS_TCP_SOCKET:
>  		perm = TCP_SOCKET__RECVFROM;
>  		break;
> +	case SECCLASS_SCTP_SOCKET:
> +		perm = SCTP_SOCKET__RECVFROM;
> +		break;
>  	default:
>  		perm = RAWIP_SOCKET__RECVFROM;
>  	}
> diff --git a/security/selinux/sctp.c b/security/selinux/sctp.c
> new file mode 100644
> index 0000000..bd25712
> --- /dev/null
> +++ b/security/selinux/sctp.c
> @@ -0,0 +1,194 @@
> +/*
> + * SELinux SCTP Support
> + *
> + * Provides security checks for the SCTP protocol.
> + *
> + * Author: Richard Haines <richard_c_haines@btinternet.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; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +
> +#include "sctp_private.h"
> +
> +/**
> + * selinux_sctp_setsockopt - Check setsockopt values.
> + * @sk: the socket
> + * @level: contains the protocol level to validate
> + * @optname: contains the name of the option to validate
> + * @optval: contains the value(s) to set
> + * @optlen: contains the length of the value(s) to be set
> + *
> + * Description:
> + * Check whether SCTP socket options are allowed or not. Returns zero on
> + * success, negative values on failure.
> + *
> + * setsockopt(2) option support:
> + *
> + *    SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> + *                             associated after (optionally) calling bind(3).
> + *                             sctp_bindx(3) adds or removes 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.
> + *                            May also return an association id.
> + *
> + *   Together they form SCTP associations and will be passed over the
> + *   link to inform peer of any changes. As these two options can support
> + *   multiple addresses, each address is checked via selinux_socket_bind() or
> + *   selinux_socket_connect() to determine whether they have the correct
> + *   permissions:
> + *     bindx_add: bind, name_bind, node_bind + node SID + port SID via the
> + *                (portcon sctp port ctx) policy statement.
> + *     connectx:  connect, name_connect + port SID via the
> + *                (portcon sctp port ctx) policy statement.
> + *
> + *  These options require set_params permission:
> + *    SCTP_SOCKOPT_BINDX_REM - As the addresses would have already been
> + *                             allowed, only the bindx_rem permission
> + *                             is checked.
> + *
> + *    SCTP_PEER_ADDR_PARAMS  - Set heartbeats and address max
> + *                             retransmissions.
> + *
> + *    SCTP_PEER_ADDR_THLDS   - Set thresholds.
> + *
> + *    SCTP_ASSOCINFO         - Set association and endpoint parameters.
> + *
> + *  These options require the set_addr permission.
> + *    SCTP_PRIMARY_ADDR          - Set local primary address.
> + *
> + *    SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as
> + *                                 association primary.
> + */
> +int selinux_sctp_setsockopt(struct sock *sk, int level, int optname,
> +					    char *optval, int optlen)
> +{
> +	int err, addrlen;
> +	void *addr_buf;
> +	struct sockaddr *address;
> +	struct socket *sock;
> +	int walk_size = 0;
> +
> +	if (level != SOL_SCTP || level != IPPROTO_SCTP)
> +		return 0;
> +
> +	switch (optname) {
> +	case SCTP_SOCKOPT_BINDX_ADD:
> +	case SCTP_SOCKOPT_CONNECTX:
> +		/* Note that for SCTP_SOCKOPT_BINDX_ADD and
> +		 * SCTP_SOCKOPT_CONNECTX the sctp kernel code has already
> +		 * copied the optval to kernel space. See net/sctp/socket.c
> +		 * security_sk_setsockopt() calls.
> +		 */
> +		err = sock_has_perm(current, sk,
> +			    (optname == SCTP_SOCKOPT_BINDX_ADD ?
> +			     SCTP_SOCKET__BINDX_ADD :
> +			     SCTP_SOCKET__CONNECTX));
> +		if (err)
> +			return err;
> +
> +		sock = sk->sk_socket;
> +		addr_buf = optval;
> +		/* Process list - may contain IPv4 or IPv6 addr's */
> +		while (walk_size < optlen) {
> +			address = addr_buf;
> +
> +			switch (address->sa_family) {
> +			case PF_INET:
> +				addrlen = sizeof(struct sockaddr_in);
> +				break;
> +			case PF_INET6:
> +				addrlen = sizeof(struct sockaddr_in6);
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
> +
> +			err = -EINVAL;
> +			if (optname == SCTP_SOCKOPT_BINDX_ADD) {
> +				err = selinux_socket_bind(sock,
> +					    address, addrlen);
> +			} else if (optname == SCTP_SOCKOPT_CONNECTX) {
> +				err = selinux_socket_connect(sock,
> +					    address, addrlen);
> +			}
> +			if (err)
> +				return err;
> +
> +			addr_buf += addrlen;
> +			walk_size += addrlen;
> +		}
> +		break;
> +
> +	case SCTP_SOCKOPT_BINDX_REM:
> +		/* The addresses have been checked as they were
> +		 * added, so just see if allowed to be removed.
> +		 */
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__BINDX_REM);
> +		if (err)
> +			return err;
> +		break;
> +
> +	/* Set heartbeats and address max retransmissions. */
> +	case SCTP_PEER_ADDR_PARAMS:
> +	/* Set thresholds. */
> +	case SCTP_PEER_ADDR_THLDS:
> +	/* Set association and endpoint parameters */
> +	case SCTP_ASSOCINFO:
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__SET_PARAMS);
> +		if (err)
> +			return err;
> +		break;
> +
> +	/* Set local primary address. */
> +	case SCTP_PRIMARY_ADDR:
> +	/* Request peer sets address as association primary. */
> +	case SCTP_SET_PEER_PRIMARY_ADDR:
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__SET_ADDR);
> +		if (err)
> +			return err;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * selinux_sctp_getsockopt - Check getsockopt values.
> + * @sk: the socket
> + * @level: contains the protocol level to validate
> + * @optname: contains the name of the option to validate
> + *
> + * Description:
> + * Check whether SCTP socket options are allowed or not. Returns zero on
> + * success, negative values on failure.
> + *
> + *    SCTP_SOCKOPT_PEELOFF - Branch off an association into a new socket that
> + *                           will be a one-to-one style socket. As SELinux
> + *                           already handles the creation of new sockets, only
> + *                           the peeloff permission is checked.
> + */
> +int selinux_sctp_getsockopt(struct sock *sk, int level, int optname)
> +{
> +	int err;
> +
> +	if (level != SOL_SCTP || level != IPPROTO_SCTP)
> +		return 0;
> +
> +	switch (optname) {
> +	case SCTP_SOCKOPT_PEELOFF:
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__PEELOFF);
> +		if (err)
> +			return err;
> +		break;
> +	}
> +
> +	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
Stephen Smalley Dec. 14, 2016, 6:34 p.m. UTC | #3
On Wed, 2016-12-14 at 13:39 +0000, Richard Haines wrote:
> Add SELinux support for the SCTP protocol. The SELinux-sctp.txt
> document
> describes how the patch has been implemented with an example policy
> and
> tests using lkstcp-tools.
> 
> Patches to assist the testing of this kernel patch are:
> 1) Support the new SCTP portcon statement used in the test CIL policy
> module shown in Documentation/security/SELinux-sctp.txt can be found
> at [1].
> 2) Add SELinux support for the http://lksctp.sourceforge.net/ apps
> sctp_test.c and sctp_darn.c can be found at [2].
> 
> Built and tested on Fedora 25 with linux-4.8 kernel.
> 
> [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/lksctp-too
> ls-Add-SELinux-support-to-sctp_test-and-sc.patch
> 
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> ---
>  Documentation/security/SELinux-sctp.txt | 508
> ++++++++++++++++++++++++++++++++
>  include/linux/lsm_hooks.h               |  27 ++
>  include/linux/security.h                |  16 +
>  net/sctp/sm_statefuns.c                 |  12 +
>  net/sctp/socket.c                       |  16 +
>  security/security.c                     |  18 ++
>  security/selinux/Makefile               |   2 +
>  security/selinux/hooks.c                | 124 +++++++-
>  security/selinux/include/classmap.h     |   4 +
>  security/selinux/include/sctp.h         |  50 ++++
>  security/selinux/include/sctp_private.h |  39 +++
>  security/selinux/netlabel.c             |   3 +
>  security/selinux/sctp.c                 | 194 ++++++++++++
>  13 files changed, 1002 insertions(+), 11 deletions(-)
>  create mode 100644 Documentation/security/SELinux-sctp.txt
>  create mode 100644 security/selinux/include/sctp.h
>  create mode 100644 security/selinux/include/sctp_private.h
>  create mode 100644 security/selinux/sctp.c
> 
> diff --git a/Documentation/security/SELinux-sctp.txt
> b/Documentation/security/SELinux-sctp.txt
> new file mode 100644
> index 0000000..dcad4b2
> --- /dev/null
> +++ b/Documentation/security/SELinux-sctp.txt
> @@ -0,0 +1,508 @@
<snip>
> +Rule validation parameters used when 'network_peer_controls = 1':
> +------------------------------------------------------------------
> -------------
> +Rule  Source   Target     Class        Permissions
> +------------------------------------------------------------------
> -------------
> +allow domain_t self     : sctp_socket {connectx peeloff set_addr
> set_params};
> +allow domain_t socket_t : sctp_socket {bindx_add bindx_rem
> set_params peeloff};

What makes some of these checks against self/domain_t vs socket_t?
 Won't socket_t usually be the same as domain_t except when using
setsockcreatecon()?

Is it useful to distinguish all of these new sctp_socket permissions,
or should some of them be coalesced into equivalence classes?  How
often will we want to allow some but not all?  When will we want to
allow setopt but not all of the sctp-specific ones?

> +allow socket_t port_t   : sctp_socket {name_bind name_connect};

NB the port types may differ for these two checks.

> +allow socket_t node_t   : sctp_socket {node_bind};
> +allow socket_t peer_t   : sctp_socket {associate};

What do we gain from adding this associate check when we also check
peer recv permission on the same pair of contexts (below)?  Also,
earlier you referred to it as "association" rather than "associate";
needs to be consistent.

> +allow peer_t   netif_t  : netif       {ingress egress};
> +allow peer_t   node_t   : node        {recvfrom sendto};
> +allow socket_t peer_t   : peer        {recv};
> +allow domain_t packet_t : packet      {send recv relabelto}
> +
<snip>
> +   3) SCTP sockets inherit their labels from the creating process
> (unless
> +      there are policy rules to change this). They do NOT follow the
> TCP
> +      labeling method even for TCP-style sockets. For reference: TCP
> child
> +      sockets take the TE information from the parent server socket,
> but the
> +      MLS/MCS information from the connection when CIPSO is enabled.

This seems problematic, given that the TCP child socket behavior was
specifically introduced to allow MLS connections to operate correctly.
Why diverge?  At some point, it would be useful to rework that to use
security_transition_sid() or similar to derive the child socket label
and let policy dictate how the MLS information is determined, but
that's a separate change.

<snip>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e15e560..491599c 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
<snip>
> @@ -4034,6 +4039,23 @@ static int selinux_parse_skb_ipv4(struct
> sk_buff *skb,
>  		break;
>  	}
>  
> +#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)

Replace throughout with #if IS_ENABLED(CONFIG_IP_SCTP)

<snip>
> @@ -4236,7 +4271,7 @@ static int socket_sockcreate_sid(const struct
> task_security_struct *tsec,
>  				       socksid);
>  }
>  
> -static int sock_has_perm(struct task_struct *task, struct sock *sk,
> u32 perms)
> +int sock_has_perm(struct task_struct *task, struct sock *sk, u32
> perms)

Likely need a prefix if you are making it non-static, e.g.
selinux_sock_has_perm() or similar.  Or maybe make it a static inline
in a header shared with your sctp.c.

>  {
>  	struct sk_security_struct *sksec = sk->sk_security;
>  	struct common_audit_data ad;
> @@ -4306,7 +4341,8 @@ static int selinux_socket_post_create(struct
> socket *sock, int family,
>     Need to determine whether we should perform a name_bind
>     permission check between the socket and the port number. */
>  
> -static int selinux_socket_bind(struct socket *sock, struct sockaddr
> *address, int addrlen)
> +int selinux_socket_bind(struct socket *sock, struct sockaddr
> *address,
> +								int
> addrlen)

Indentation

<snip>
> @@ -4405,7 +4445,8 @@ out:
>  	return err;
>  }
>  
> -static int selinux_socket_connect(struct socket *sock, struct
> sockaddr *address, int addrlen)
> +int selinux_socket_connect(struct socket *sock, struct sockaddr
> *address,
> +								int
> addrlen)

Ditto

>  {
>  	struct sock *sk = sock->sk;
>  	struct sk_security_struct *sksec = sk->sk_security;
> @@ -4416,10 +4457,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;
> @@ -4443,8 +4486,12 @@ static int selinux_socket_connect(struct
> socket *sock, struct sockaddr *address,
>  		if (err)
>  			goto out;
>  
> -		perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
> -		       TCP_SOCKET__NAME_CONNECT :
> DCCP_SOCKET__NAME_CONNECT;
> +		if (sksec->sclass == SECCLASS_TCP_SOCKET)
> +			perm = TCP_SOCKET__NAME_CONNECT;
> +		else if (sksec->sclass == SECCLASS_DCCP_SOCKET)
> +			perm = DCCP_SOCKET__NAME_CONNECT;
> +		else if (sksec->sclass == SECCLASS_SCTP_SOCKET)
> +			perm = SCTP_SOCKET__NAME_CONNECT;

Use a switch?

>  
>  		ad.type = LSM_AUDIT_DATA_NET;
>  		ad.u.net = &net;
> @@ -4516,13 +4563,35 @@ static int selinux_socket_setsockopt(struct
> socket *sock, int level, int optname
>  	if (err)
>  		return err;
>  
> +	err = selinux_sctp_setsockopt(sock->sk, level, optname,
> NULL, 0);
> +	if (err)
> +		return err;

This seems odd to me; we call selinux_sctp_setsockopt() twice, once
without the value here and once with the value below.  I would split it
into two functions, one which handles the cases where we are only
checking based on optname (available at the socket layer, called from
here) and one which handles the cases where we need to know the actual
optval (not yet copied from userspace here, so deferred to your hook
below).  Avoid duplication between the two.

> +
>  	return selinux_netlbl_socket_setsockopt(sock, level,
> optname);
>  }
>  
> +static int selinux_sk_setsockopt(struct sock *sk, int level, int
> optname,
> +					    char *optval, int
> optlen)
> +{
> +	int err;
> +
> +	err = sock_has_perm(current, sk, SOCKET__SETOPT);
> +	if (err)
> +		return err;

This check should already have occurred from the prior hook, right?

> +
> +	return selinux_sctp_setsockopt(sk, level, optname, optval,
> optlen);
> +}
> +
>  static int selinux_socket_getsockopt(struct socket *sock, int level,
>  				     int optname)
>  {
> -	return sock_has_perm(current, sock->sk, SOCKET__GETOPT);
> +	int err;
> +
> +	err = sock_has_perm(current, sock->sk, SOCKET__GETOPT);
> +	if (err)
> +		return err;
> +
> +	return selinux_sctp_getsockopt(sock->sk, level, optname);
>  }

Is there any use case where we would want to deny peeloff while
otherwise allowing use of the sctp socket?  If not, drop.

>  
>  static int selinux_socket_shutdown(struct socket *sock, int how)
> @@ -4715,7 +4784,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;
> @@ -4828,6 +4898,36 @@ 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;
> +
> +	return avc_has_perm(sksec->sid, sksec->peer_sid, sksec-
> >sclass,
> +				    SCTP_SOCKET__ASSOCIATION, &ad);

Seems redundant with peer recv permission check.  If it is just a
matter of ensuring that we enforce it earlier, then you could just
check peer recv here.  But what happens if we defer the denial to the
existing peer recv check?

> diff --git a/security/selinux/netlabel.c
> b/security/selinux/netlabel.c
> index aaba667..300a195 100644
> --- a/security/selinux/netlabel.c
> +++ b/security/selinux/netlabel.c
> @@ -399,6 +399,9 @@ int selinux_netlbl_sock_rcv_skb(struct
> sk_security_struct *sksec,
>  	case SECCLASS_TCP_SOCKET:
>  		perm = TCP_SOCKET__RECVFROM;
>  		break;
> +	case SECCLASS_SCTP_SOCKET:
> +		perm = SCTP_SOCKET__RECVFROM;
> +		break;

I'm not sure we should be updating the legacy (network_peer_controls=0)
checks with newer logic.  In fact, I'd be inclined to deprecate and
eventually remove them altogether.

--
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 Dec. 16, 2016, 1:31 p.m. UTC | #4
On Wed, 2016-12-14 at 09:02 -0800, Casey Schaufler wrote:
> On 12/14/2016 5:39 AM, Richard Haines wrote:
> > Add SELinux support for the SCTP protocol. The SELinux-sctp.txt
> > document
> > describes how the patch has been implemented with an example policy
> > and
> > tests using lkstcp-tools.
> 
> Please separate the LSM support from the SELinux support
> into patches 1/2 and 2/2 respectively. The documentation
> needs to be separated along the same lines as well. While
> I am only mildly concerned about the SELinux implementation
> I am very concerned about the LSM side.
> 
> 
Thanks for your comments. I do not see a problem with
resolving them all in the next RFC patch.


--
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 Dec. 16, 2016, 1:40 p.m. UTC | #5
On Wed, Dec 14, 2016 at 02:01:35PM +0000, David Laight wrote:
> From: Richard Haines
> > Sent: 14 December 2016 13:40
> > Add SELinux support for the SCTP protocol. The SELinux-sctp.txt document
> > describes how the patch has been implemented with an example policy and
> > tests using lkstcp-tools.
> ...
> > +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> > +                         associated after (optionally) calling bind(2)
> > +                         if given the "bind_add" permission.
> 
> Does restricting bindx make any sense at all?
> The only addresses than can be specified are those of local interfaces.
> If bindx isn't called then the default is to include the addresses of
> all local interfaces.
> So bindx only actually removes local addresses, it doesn't add them.

You could bind the socket while on a priviledged process and then drop
the priviledges, like daemons do for binding on lower ports. Then the
application wouldn't be able to bind on another address that it's not
expected to.

  Marcelo

--
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 Dec. 21, 2016, 12:26 p.m. UTC | #6
On Fri, 2016-12-16 at 11:40 -0200, Marcelo Ricardo Leitner wrote:
> On Wed, Dec 14, 2016 at 02:01:35PM +0000, David Laight wrote:
> > From: Richard Haines
> > > Sent: 14 December 2016 13:40
> > > Add SELinux support for the SCTP protocol. The SELinux-sctp.txt
> > > document
> > > describes how the patch has been implemented with an example
> > > policy and
> > > tests using lkstcp-tools.
> > 
> > ...
> > > +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> > > +                         associated after (optionally) calling
> > > bind(2)
> > > +                         if given the "bind_add" permission.
> > 
> > Does restricting bindx make any sense at all?
> > The only addresses than can be specified are those of local
> > interfaces.
> > If bindx isn't called then the default is to include the addresses
> > of
> > all local interfaces.
> > So bindx only actually removes local addresses, it doesn't add
> > them.
> 
> You could bind the socket while on a priviledged process and then
> drop
> the priviledges, like daemons do for binding on lower ports. Then the
> application wouldn't be able to bind on another address that it's not
> expected to.
> 
>   Marcelo
> 

It appears from Marcelo's comments that keeping bindx_add/bindx_rem
would be useful. However I will rename the permissions to a single
permission of "bindx_addr" if that's okay.

Any more sctp specific comments gratefully received (SELinux/LSM as
well of course). I plan to issue an updated patch mid-late Jan '17.

> --
> 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
Marcelo Ricardo Leitner Dec. 21, 2016, 4:09 p.m. UTC | #7
On Wed, Dec 14, 2016 at 01:39:59PM +0000, Richard Haines wrote:
> +SCTP Socket Option Permissions
> +===============================
> +The permissions consist of: "bindx_add" "bindx_rem" "connectx" "set_addr" and
> +"set_params" that are validated on setsockopt(2) calls, and "peeloff" that is
> +validated on getsockopt(2) calls.
> +
> +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> +                         associated after (optionally) calling bind(2)
> +                         if given the "bind_add" permission.
> +
> +SCTP_SOCKOPT_CONNECTX  - Allows the allocation of multiple
> +                         addresses for reaching a multi-homed peer
> +                         if given the "connectx" permission.
> +
> +  Together they are used to form SCTP associations with information being
> +  passed over the link to inform the peer of any changes. As these two options
> +  can support multiple addresses, each address is checked via
> +  selinux_socket_bind() or selinux_socket_connect() to determine whether they
> +  have the correct permissions:
> +    bindx_add: bind, name_bind, node_bind + node SID + port SID via the
> +               (portcon sctp port ctx) policy statement.
> +    connectx:  connect, name_connect + port SID via the
> +               (portcon sctp port ctx) policy statement.
> +
> +SCTP_SOCKOPT_BINDX_REM - Allows additional bind addresses to be removed
> +                         if given the "bind_rem" permission.
> +
> +SCTP_PEER_ADDR_PARAMS - Alter heartbeats and address max retransmissions.
> +SCTP_PEER_ADDR_THLDS  - Alter the thresholds.
> +SCTP_ASSOCINFO        - Alter association and endpoint parameters.
> + These require the "set_params" permission.
> +
> +SCTP_PRIMARY_ADDR          - Set local primary address.
> +SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as association primary.
> + These require the "set_addr" permission.
> +
> +SCTP_SOCKOPT_PEELOFF - Branch off an association into a new socket that
> +will be a one-to-one style socket. As SELinux already handles the creation
> +of new sockets, only the "peeloff" permission is checked.

...

> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index 7b0e059..ff4f1a8 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_sk_setsockopt(sk, SOL_SCTP,
> +					     SCTP_SOCKOPT_BINDX_ADD,
> +					     (char *)kaddrs, addrs_size);

Here, kaddrs is about the addresses that we are going to bind to.

> +		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_sk_setsockopt(sk, SOL_SCTP,
> +					     SCTP_SOCKOPT_CONNECTX,
> +					    (char *)kaddrs, addrs_size);

Here, kaddrs is about the remote addresses that we are connecting to.
Not sure how feasible this is for SELinux, to maintain a list of allowed
peers. But this being right, I think we are missing the hooks at ASCONF
handling side.

One SCTP peer can start/stop binding to another IP in runtime using
ASCONF chunks. So considering that peer A here validated that it can
associate to be peer B, if B is using ASCONF to inform A that it's now
also binding on address X, A should validate so before ACKing it.

This validation would be around sctp_process_asconf_param. Not sure you
can hook it on selinux_sctp_setsockopt too as it would be similar to the
validation done for CONNECT.

Richard, the other point we talked offline, was for validating that peer
A can actually request to add address X, that would be ok, yes.

Thanks,
  Marcelo

> +		if (err)
> +			goto out_free;
> +
>  		err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
>  	}
>  
> +out_free:
>  	kfree(kaddrs);
>  
>  	return err;



> +int selinux_sctp_setsockopt(struct sock *sk, int level, int optname,
> +					    char *optval, int optlen)
> +{
> +	int err, addrlen;
> +	void *addr_buf;
> +	struct sockaddr *address;
> +	struct socket *sock;
> +	int walk_size = 0;
> +
> +	if (level != SOL_SCTP || level != IPPROTO_SCTP)
> +		return 0;
> +
> +	switch (optname) {
> +	case SCTP_SOCKOPT_BINDX_ADD:
> +	case SCTP_SOCKOPT_CONNECTX:
> +		/* Note that for SCTP_SOCKOPT_BINDX_ADD and
> +		 * SCTP_SOCKOPT_CONNECTX the sctp kernel code has already
> +		 * copied the optval to kernel space. See net/sctp/socket.c
> +		 * security_sk_setsockopt() calls.
> +		 */
> +		err = sock_has_perm(current, sk,
> +			    (optname == SCTP_SOCKOPT_BINDX_ADD ?
> +			     SCTP_SOCKET__BINDX_ADD :
> +			     SCTP_SOCKET__CONNECTX));
> +		if (err)
> +			return err;
> +
> +		sock = sk->sk_socket;
> +		addr_buf = optval;
> +		/* Process list - may contain IPv4 or IPv6 addr's */
> +		while (walk_size < optlen) {
> +			address = addr_buf;
> +
> +			switch (address->sa_family) {
> +			case PF_INET:
> +				addrlen = sizeof(struct sockaddr_in);
> +				break;
> +			case PF_INET6:
> +				addrlen = sizeof(struct sockaddr_in6);
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
> +
> +			err = -EINVAL;
> +			if (optname == SCTP_SOCKOPT_BINDX_ADD) {
> +				err = selinux_socket_bind(sock,
> +					    address, addrlen);
> +			} else if (optname == SCTP_SOCKOPT_CONNECTX) {
> +				err = selinux_socket_connect(sock,
> +					    address, addrlen);
> +			}
> +			if (err)
> +				return err;
> +
> +			addr_buf += addrlen;
> +			walk_size += addrlen;
> +		}
> +		break;
> +
> +	case SCTP_SOCKOPT_BINDX_REM:
> +		/* The addresses have been checked as they were
> +		 * added, so just see if allowed to be removed.
> +		 */
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__BINDX_REM);
> +		if (err)
> +			return err;
> +		break;
> +
> +	/* Set heartbeats and address max retransmissions. */
> +	case SCTP_PEER_ADDR_PARAMS:
> +	/* Set thresholds. */
> +	case SCTP_PEER_ADDR_THLDS:
> +	/* Set association and endpoint parameters */
> +	case SCTP_ASSOCINFO:
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__SET_PARAMS);
> +		if (err)
> +			return err;
> +		break;
> +
> +	/* Set local primary address. */
> +	case SCTP_PRIMARY_ADDR:
> +	/* Request peer sets address as association primary. */
> +	case SCTP_SET_PEER_PRIMARY_ADDR:
> +		err = sock_has_perm(current, sk, SCTP_SOCKET__SET_ADDR);
> +		if (err)
> +			return err;
> +		break;
> +	}
> +
> +	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
Richard Haines Jan. 23, 2017, 1:19 p.m. UTC | #8
On Wed, 2016-12-14 at 13:34 -0500, Stephen Smalley wrote:
> On Wed, 2016-12-14 at 13:39 +0000, Richard Haines wrote:
> > Add SELinux support for the SCTP protocol. The SELinux-sctp.txt
> > document
> > describes how the patch has been implemented with an example policy
> > and
> > tests using lkstcp-tools.
> > 
> > Patches to assist the testing of this kernel patch are:
> > 1) Support the new SCTP portcon statement used in the test CIL
> > policy
> > module shown in Documentation/security/SELinux-sctp.txt can be
> > found
> > at [1].
> > 2) Add SELinux support for the http://lksctp.sourceforge.net/ apps
> > sctp_test.c and sctp_darn.c can be found at [2].
> > 
> > Built and tested on Fedora 25 with linux-4.8 kernel.
> > 
> > [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/lksctp-t
> > oo
> > ls-Add-SELinux-support-to-sctp_test-and-sc.patch
> > 
> > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> > ---
> >  Documentation/security/SELinux-sctp.txt | 508
> > ++++++++++++++++++++++++++++++++
> >  include/linux/lsm_hooks.h               |  27 ++
> >  include/linux/security.h                |  16 +
> >  net/sctp/sm_statefuns.c                 |  12 +
> >  net/sctp/socket.c                       |  16 +
> >  security/security.c                     |  18 ++
> >  security/selinux/Makefile               |   2 +
> >  security/selinux/hooks.c                | 124 +++++++-
> >  security/selinux/include/classmap.h     |   4 +
> >  security/selinux/include/sctp.h         |  50 ++++
> >  security/selinux/include/sctp_private.h |  39 +++
> >  security/selinux/netlabel.c             |   3 +
> >  security/selinux/sctp.c                 | 194 ++++++++++++
> >  13 files changed, 1002 insertions(+), 11 deletions(-)
> >  create mode 100644 Documentation/security/SELinux-sctp.txt
> >  create mode 100644 security/selinux/include/sctp.h
> >  create mode 100644 security/selinux/include/sctp_private.h
> >  create mode 100644 security/selinux/sctp.c
> > 
> > diff --git a/Documentation/security/SELinux-sctp.txt
> > b/Documentation/security/SELinux-sctp.txt
> > new file mode 100644
> > index 0000000..dcad4b2
> > --- /dev/null
> > +++ b/Documentation/security/SELinux-sctp.txt
> > @@ -0,0 +1,508 @@
> 
> <snip>
> > +Rule validation parameters used when 'network_peer_controls = 1':
> > +------------------------------------------------------------------
> > -------------
> > +Rule  Source   Target     Class        Permissions
> > +------------------------------------------------------------------
> > -------------
> > +allow domain_t self     : sctp_socket {connectx peeloff set_addr
> > set_params};
> > +allow domain_t socket_t : sctp_socket {bindx_add bindx_rem
> > set_params peeloff};
> 
> What makes some of these checks against self/domain_t vs socket_t?
>  Won't socket_t usually be the same as domain_t except when using
> setsockcreatecon()?
I'll correct and add comment about setsockcreatecon()

> 
> Is it useful to distinguish all of these new sctp_socket permissions,
> or should some of them be coalesced into equivalence classes?  How
> often will we want to allow some but not all?  When will we want to
> allow setopt but not all of the sctp-specific ones?
I've renamed {bindx_add bindx_rem} to bindx_addrs, removed {peeloff},
however after discussions with Marcelo I've added
{set_pri_addr set_peer_addr}

As regards how often policy writers will use some but not all,
I'm not sure, just gone for flexibility.
> 
> > +allow socket_t port_t   : sctp_socket {name_bind name_connect};
> 
> NB the port types may differ for these two checks.
> 
> > +allow socket_t node_t   : sctp_socket {node_bind};
> > +allow socket_t peer_t   : sctp_socket {associate};
> 
> What do we gain from adding this associate check when we also check
> peer recv permission on the same pair of contexts (below)?  Also,
> earlier you referred to it as "association" rather than "associate";
> needs to be consistent.

I added this so that policy could decide whether to allow or deny
associations as there can be multiple associations on a single socket.
These associations could come from any of the allowed peer {recv}
channels, however it could be that these are needed by other services
but sctp associations are not allowed on all of them.

This is my attempt to answer Paul's comments on a previous patch (see h
ttp://marc.info/?l=selinux&m=141801137004870&w=2) that I've added here:
------------------
With connectionless protocols, e.g. UDP, there is not peer label since
there is no connected peer.  With connection based protocols, e.g. TCP,
the peer label is set during the early stages of the connection
handshake, see selinux_inet_conn_request(), and never changes during
the lifetime of the socket/connection.  I suspect SCTP, due to the
association concept, is much closer to TCP than UDP; we do want to
set/track the peer label, but at the early stages of the association
setup, not here.

If there is an issue with multiple associations sharing a single socket
then we should look a bit more closely at how the associations are
established.  
Likely the first association on a socket would be the one that would
set the peer label, and then each additional association would need to
have the exact same security label, else the association would be
denied.
----------

> 
> > +allow peer_t   netif_t  : netif       {ingress egress};
> > +allow peer_t   node_t   : node        {recvfrom sendto};
> > +allow socket_t peer_t   : peer        {recv};
> > +allow domain_t packet_t : packet      {send recv relabelto}
> > +
> 
> <snip>
> > +   3) SCTP sockets inherit their labels from the creating process
> > (unless
> > +      there are policy rules to change this). They do NOT follow
> > the
> > TCP
> > +      labeling method even for TCP-style sockets. For reference:
> > TCP
> > child
> > +      sockets take the TE information from the parent server
> > socket,
> > but the
> > +      MLS/MCS information from the connection when CIPSO is
> > enabled.
> 
> This seems problematic, given that the TCP child socket behavior was
> specifically introduced to allow MLS connections to operate
> correctly.
> Why diverge?  At some point, it would be useful to rework that to use
> security_transition_sid() or similar to derive the child socket label
> and let policy dictate h
> that's a separate change.
I'll attempt to fix this, currently I've tested against equivalent in
the SELinux test suite:
CIPSO loopback full-labeling - ok
CIPSO - fails some tests
CALIPSO - fails some tests
NetLabel Fallback labeling - ok
iptables - ok
IPSEC - fails probably because rfc3554 (sctp/ipsec support) has
not been implemented yet.

> 
> <snip>
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index e15e560..491599c 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> 
> <snip>
> > @@ -4034,6 +4039,23 @@ static int selinux_parse_skb_ipv4(struct
> > sk_buff *skb,
> >  		break;
> >  	}
> >  
> > +#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
> 
> Replace throughout with #if IS_ENABLED(CONFIG_IP_SCTP)
Fixed

> 
> <snip>
> > @@ -4236,7 +4271,7 @@ static int socket_sockcreate_sid(const struct
> > task_security_struct *tsec,
> >  				       socksid);
> >  }
> >  
> > -static int sock_has_perm(struct task_struct *task, struct sock
> > *sk,
> > u32 perms)
> > +int sock_has_perm(struct task_struct *task, struct sock *sk, u32
> > perms)
> 
> Likely need a prefix if you are making it non-static, e.g.
> selinux_sock_has_perm() or similar.  Or maybe make it a static inline
> in a header shared with your sctp.c.
I've added forward declarations in hooks.c for these three and fixed
indentation.

> >  {
> >  	struct sk_security_struct *sksec = sk->sk_security;
> >  	struct common_audit_data ad;
> > @@ -4306,7 +4341,8 @@ static int selinux_socket_post_create(struct
> > socket *sock, int family,
> >     Need to determine whether we should perform a name_bind
> >     permission check between the socket and the port number. */
> >  
> > -static int selinux_socket_bind(struct socket *sock, struct
> > sockaddr
> > *address, int addrlen)
> > +int selinux_socket_bind(struct socket *sock, struct sockaddr
> > *address,
> > +								in
> > t
> > addrlen)
> 
> Indentation
> 
> <snip>
> > @@ -4405,7 +4445,8 @@ out:
> >  	return err;
> >  }
> >  
> > -static int selinux_socket_connect(struct socket *sock, struct
> > sockaddr *address, int addrlen)
> > +int selinux_socket_connect(struct socket *sock, struct sockaddr
> > *address,
> > +								in
> > t
> > addrlen)
> 
> Ditto
> 
> >  {
> >  	struct sock *sk = sock->sk;
> >  	struct sk_security_struct *sksec = sk->sk_security;
> > @@ -4416,10 +4457,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;
> > @@ -4443,8 +4486,12 @@ static int selinux_socket_connect(struct
> > socket *sock, struct sockaddr *address,
> >  		if (err)
> >  			goto out;
> >  
> > -		perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
> > -		       TCP_SOCKET__NAME_CONNECT :
> > DCCP_SOCKET__NAME_CONNECT;
> > +		if (sksec->sclass == SECCLASS_TCP_SOCKET)
> > +			perm = TCP_SOCKET__NAME_CONNECT;
> > +		else if (sksec->sclass == SECCLASS_DCCP_SOCKET)
> > +			perm = DCCP_SOCKET__NAME_CONNECT;
> > +		else if (sksec->sclass == SECCLASS_SCTP_SOCKET)
> > +			perm = SCTP_SOCKET__NAME_CONNECT;
> 
> Use a switch?
Fixed
> 
> >  
> >  		ad.type = LSM_AUDIT_DATA_NET;
> >  		ad.u.net = &net;
> > @@ -4516,13 +4563,35 @@ static int selinux_socket_setsockopt(struct
> > socket *sock, int level, int optname
> >  	if (err)
> >  		return err;
> >  
> > +	err = selinux_sctp_setsockopt(sock->sk, level, optname,
> > NULL, 0);
> > +	if (err)
> > +		return err;
> 
> This seems odd to me; we call selinux_sctp_setsockopt() twice, once
> without the value here and once with the value below.  I would split
> it
> into two functions, one which handles the cases where we are only
> checking based on optname (available at the socket layer, called from
> here) and one which handles the cases where we need to know the
> actual
> optval (not yet copied from userspace here, so deferred to your hook
> below).  Avoid duplication between the two.
> 
Fixed
> > +
> >  	return selinux_netlbl_socket_setsockopt(sock, level,
> > optname);
> >  }
> >  
> > +static int selinux_sk_setsockopt(struct sock *sk, int level, int
> > optname,
> > +					    char *optval, int
> > optlen)
> > +{
> > +	int err;
> > +
> > +	err = sock_has_perm(current, sk, SOCKET__SETOPT);
> > +	if (err)
> > +		return err;
> 
> This check should already have occurred from the prior hook, right?
Fixed
> 
> > +
> > +	return selinux_sctp_setsockopt(sk, level, optname, optval,
> > optlen);
> > +}
> > +
> >  static int selinux_socket_getsockopt(struct socket *sock, int
> > level,
> >  				     int optname)
> >  {
> > -	return sock_has_perm(current, sock->sk, SOCKET__GETOPT);
> > +	int err;
> > +
> > +	err = sock_has_perm(current, sock->sk, SOCKET__GETOPT);
> > +	if (err)
> > +		return err;
> > +
> > +	return selinux_sctp_getsockopt(sock->sk, level, optname);
> >  }
> 
> Is there any use case where we would want to deny peeloff while
> otherwise allowing use of the sctp socket?  If not, drop.
Removed peeloff as not required.
> 
> >  
> >  static int selinux_socket_shutdown(struct socket *sock, int how)
> > @@ -4715,7 +4784,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;
> > @@ -4828,6 +4898,36 @@ 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;
> > +
> > +	return avc_has_perm(sksec->sid, sksec->peer_sid, sksec-
> > > sclass,
> > 
> > +				    SCTP_SOCKET__ASSOCIATION,
> > &ad);
> 
> Seems redundant with peer recv permission check.  If it is just a
> matter of ensuring that we enforce it earlier, then you could just
> check peer recv here.  But what happens if we defer the denial to the
> existing peer recv check?
See earlier comment regarding associations.

> 
> > diff --git a/security/selinux/netlabel.c
> > b/security/selinux/netlabel.c
> > index aaba667..300a195 100644
> > --- a/security/selinux/netlabel.c
> > +++ b/security/selinux/netlabel.c
> > @@ -399,6 +399,9 @@ int selinux_netlbl_sock_rcv_skb(struct
> > sk_security_struct *sksec,
> >  	case SECCLASS_TCP_SOCKET:
> >  		perm = TCP_SOCKET__RECVFROM;
> >  		break;
> > +	case SECCLASS_SCTP_SOCKET:
> > +		perm = SCTP_SOCKET__RECVFROM;
> > +		break;
> 
> I'm not sure we should be updating the legacy
> (network_peer_controls=0)
> checks with newer logic.  In fact, I'd be inclined to deprecate and
> eventually remove them altogether.
I've removed this check.
> 
> --
> 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
Marcelo Ricardo Leitner Jan. 23, 2017, 6:58 p.m. UTC | #9
On Mon, Jan 23, 2017 at 01:19:02PM +0000, Richard Haines wrote:
> On Wed, 2016-12-14 at 13:34 -0500, Stephen Smalley wrote:
> > On Wed, 2016-12-14 at 13:39 +0000, Richard Haines wrote:
<snip>
> > > +   3) SCTP sockets inherit their labels from the creating process
> > > (unless
> > > +      there are policy rules to change this). They do NOT follow
> > > the
> > > TCP
> > > +      labeling method even for TCP-style sockets. For reference:
> > > TCP
> > > child
> > > +      sockets take the TE information from the parent server
> > > socket,
> > > but the
> > > +      MLS/MCS information from the connection when CIPSO is
> > > enabled.
> > 
> > This seems problematic, given that the TCP child socket behavior was
> > specifically introduced to allow MLS connections to operate
> > correctly.
> > Why diverge?  At some point, it would be useful to rework that to use
> > security_transition_sid() or similar to derive the child socket label
> > and let policy dictate h
> > that's a separate change.
> I'll attempt to fix this, currently I've tested against equivalent in
> the SELinux test suite:
> CIPSO loopback full-labeling - ok
> CIPSO - fails some tests
> CALIPSO - fails some tests
> NetLabel Fallback labeling - ok
> iptables - ok
> IPSEC - fails probably because rfc3554 (sctp/ipsec support) has
> not been implemented yet.

FWIW, the kernel side for SCTP/IPSEC is there, but the userspace bits
aren't. There is an initiative to do it in libreswan but it's just on
papers yet. And sure, bugs might be uncovered during so..

  Marcelo

--
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 Feb. 6, 2017, 2:30 p.m. UTC | #10
On Wed, 2016-12-21 at 14:09 -0200, Marcelo Ricardo Leitner wrote:
> On Wed, Dec 14, 2016 at 01:39:59PM +0000, Richard Haines wrote:
> > +SCTP Socket Option Permissions
> > +===============================
> > +The permissions consist of: "bindx_add" "bindx_rem" "connectx"
> > "set_addr" and
> > +"set_params" that are validated on setsockopt(2) calls, and
> > "peeloff" that is
> > +validated on getsockopt(2) calls.
> > +
> > +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
> > +                         associated after (optionally) calling
> > bind(2)
> > +                         if given the "bind_add" permission.
> > +
> > +SCTP_SOCKOPT_CONNECTX  - Allows the allocation of multiple
> > +                         addresses for reaching a multi-homed peer
> > +                         if given the "connectx" permission.
> > +
> > +  Together they are used to form SCTP associations with
> > information being
> > +  passed over the link to inform the peer of any changes. As these
> > two options
> > +  can support multiple addresses, each address is checked via
> > +  selinux_socket_bind() or selinux_socket_connect() to determine
> > whether they
> > +  have the correct permissions:
> > +    bindx_add: bind, name_bind, node_bind + node SID + port SID
> > via the
> > +               (portcon sctp port ctx) policy statement.
> > +    connectx:  connect, name_connect + port SID via the
> > +               (portcon sctp port ctx) policy statement.
> > +
> > +SCTP_SOCKOPT_BINDX_REM - Allows additional bind addresses to be
> > removed
> > +                         if given the "bind_rem" permission.
> > +
> > +SCTP_PEER_ADDR_PARAMS - Alter heartbeats and address max
> > retransmissions.
> > +SCTP_PEER_ADDR_THLDS  - Alter the thresholds.
> > +SCTP_ASSOCINFO        - Alter association and endpoint parameters.
> > + These require the "set_params" permission.
> > +
> > +SCTP_PRIMARY_ADDR          - Set local primary address.
> > +SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as
> > association primary.
> > + These require the "set_addr" permission.
> > +
> > +SCTP_SOCKOPT_PEELOFF - Branch off an association into a new socket
> > that
> > +will be a one-to-one style socket. As SELinux already handles the
> > creation
> > +of new sockets, only the "peeloff" permission is checked.
> 
> ...
> 
> > diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> > index 7b0e059..ff4f1a8 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_sk_setsockopt(sk, SOL_SCTP,
> > +					     SCTP_SOCKOPT_BINDX_AD
> > D,
> > +					     (char *)kaddrs,
> > addrs_size);
> 
> Here, kaddrs is about the addresses that we are going to bind to.
> 
> > +		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_sk_setsockopt(sk, SOL_SCTP,
> > +					     SCTP_SOCKOPT_CONNECTX
> > ,
> > +					    (char *)kaddrs,
> > addrs_size);
> 

Sorry for the delay but I now think I've resolved all but one of the
SCTP issues with tests to check them. The only area I'm having trouble
with is labeling TCP-style child sockets but hope to resolve.

> Here, kaddrs is about the remote addresses that we are connecting to.
> Not sure how feasible this is for SELinux, to maintain a list of
> allowed
> peers. 
SELinux does not maintain lists, however it can check whether the
addresses/ports are allowed or not (which is what I do for binds,
connects etc.).

> But this being right, I think we are missing the hooks at ASCONF
> handling side.
> One SCTP peer can start/stop binding to another IP in runtime using
> ASCONF chunks. So considering that peer A here validated that it can
> associate to be peer B, if B is using ASCONF to inform A that it's
> now
> also binding on address X, A should validate so before ACKing it.
> 
> This validation would be around sctp_process_asconf_param. Not sure
> you
> can hook it on selinux_sctp_setsockopt too as it would be similar to
> the
> validation done for CONNECT.

I now have this working after hooking into sctp_process_asconf_param
and checking permissions on address/port as required. Also have tests
as part of the selinux-testsuite.
> 
> Richard, the other point we talked offline, was for validating that
> peer
> A can actually request to add address X, that would be ok, yes.
> 

This was regarding handling ASCONF requests on receiver the side. Yes
this does seem to be resolved.

I hope to send out an updated patch in a few weeks so hopefully these
can be verified.

> Thanks,
>   Marcelo
> 
> > +		if (err)
> > +			goto out_free;
> > +
> >  		err = __sctp_connect(sk, kaddrs, addrs_size,
> > assoc_id);
> >  	}
> >  
> > +out_free:
> >  	kfree(kaddrs);
> >  
> >  	return err;
> 
> 
> 
> > +int selinux_sctp_setsockopt(struct sock *sk, int level, int
> > optname,
> > +					    char *optval, int
> > optlen)
> > +{
> > +	int err, addrlen;
> > +	void *addr_buf;
> > +	struct sockaddr *address;
> > +	struct socket *sock;
> > +	int walk_size = 0;
> > +
> > +	if (level != SOL_SCTP || level != IPPROTO_SCTP)
> > +		return 0;
> > +
> > +	switch (optname) {
> > +	case SCTP_SOCKOPT_BINDX_ADD:
> > +	case SCTP_SOCKOPT_CONNECTX:
> > +		/* Note that for SCTP_SOCKOPT_BINDX_ADD and
> > +		 * SCTP_SOCKOPT_CONNECTX the sctp kernel code has
> > already
> > +		 * copied the optval to kernel space. See
> > net/sctp/socket.c
> > +		 * security_sk_setsockopt() calls.
> > +		 */
> > +		err = sock_has_perm(current, sk,
> > +			    (optname == SCTP_SOCKOPT_BINDX_ADD ?
> > +			     SCTP_SOCKET__BINDX_ADD :
> > +			     SCTP_SOCKET__CONNECTX));
> > +		if (err)
> > +			return err;
> > +
> > +		sock = sk->sk_socket;
> > +		addr_buf = optval;
> > +		/* Process list - may contain IPv4 or IPv6 addr's
> > */
> > +		while (walk_size < optlen) {
> > +			address = addr_buf;
> > +
> > +			switch (address->sa_family) {
> > +			case PF_INET:
> > +				addrlen = sizeof(struct
> > sockaddr_in);
> > +				break;
> > +			case PF_INET6:
> > +				addrlen = sizeof(struct
> > sockaddr_in6);
> > +				break;
> > +			default:
> > +				return -EINVAL;
> > +			}
> > +
> > +			err = -EINVAL;
> > +			if (optname == SCTP_SOCKOPT_BINDX_ADD) {
> > +				err = selinux_socket_bind(sock,
> > +					    address, addrlen);
> > +			} else if (optname ==
> > SCTP_SOCKOPT_CONNECTX) {
> > +				err = selinux_socket_connect(sock,
> > +					    address, addrlen);
> > +			}
> > +			if (err)
> > +				return err;
> > +
> > +			addr_buf += addrlen;
> > +			walk_size += addrlen;
> > +		}
> > +		break;
> > +
> > +	case SCTP_SOCKOPT_BINDX_REM:
> > +		/* The addresses have been checked as they were
> > +		 * added, so just see if allowed to be removed.
> > +		 */
> > +		err = sock_has_perm(current, sk,
> > SCTP_SOCKET__BINDX_REM);
> > +		if (err)
> > +			return err;
> > +		break;
> > +
> > +	/* Set heartbeats and address max retransmissions. */
> > +	case SCTP_PEER_ADDR_PARAMS:
> > +	/* Set thresholds. */
> > +	case SCTP_PEER_ADDR_THLDS:
> > +	/* Set association and endpoint parameters */
> > +	case SCTP_ASSOCINFO:
> > +		err = sock_has_perm(current, sk,
> > SCTP_SOCKET__SET_PARAMS);
> > +		if (err)
> > +			return err;
> > +		break;
> > +
> > +	/* Set local primary address. */
> > +	case SCTP_PRIMARY_ADDR:
> > +	/* Request peer sets address as association primary. */
> > +	case SCTP_SET_PEER_PRIMARY_ADDR:
> > +		err = sock_has_perm(current, sk,
> > SCTP_SOCKET__SET_ADDR);
> > +		if (err)
> > +			return err;
> > +		break;
> > +	}
> > +
> > +	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
diff mbox

Patch

diff --git a/Documentation/security/SELinux-sctp.txt b/Documentation/security/SELinux-sctp.txt
new file mode 100644
index 0000000..dcad4b2
--- /dev/null
+++ b/Documentation/security/SELinux-sctp.txt
@@ -0,0 +1,508 @@ 
+                               SCTP SELinux Support
+                              ======================
+
+Security Hooks
+===============
+security_sk_setsockopt()
+-------------------------
+A new security hook security_sk_setsockopt() is introduced that checks
+permissions before setting the options associated with sock @sk.
+This is supported in security/selinux/hooks.c and net/sctp/socket.c
+
+An example usage is where sctp_getsockopt_connectx3() and
+__sctp_setsockopt_connectx() manage the @optval data into a certain
+state before security_sk_setsockopt is called for permission checks. This
+means that the code to manage options does not need to be replicated in the
+security module. See include/linux/lsm_hooks.h for details.
+
+security_sctp_assoc_request()
+-----------------------------
+security/selinux/hooks.c selinux_sctp_assoc_request() has been introduced to
+support SCTP and obtains the sock peer context if first association and
+also checks the association permission as shown in the "SCTP Peer Labeling
+and Permission Checks" section below.
+
+The security_sctp_assoc_request() security hook has been added to
+net/sctp/sm_statefuns.c where it passes the sk and chunk->skb to the security
+module.
+
+security_sk_clone()
+--------------------
+Added to net/sctp/socket.c sctp_sock_migrate() for cloning the security
+context on a new socket.
+
+
+Policy Statements
+==================
+A new object class "sctp_socket" has been introduced with the following SCTP
+specific permissions: "association" "bindx_add" "bindx_rem" "connectx"
+"peeloff" "set_addr" and "set_params". These are explained in the sections
+below.
+
+Kernel policy language
+-----------------------
+class sctp_socket
+class sctp_socket inherits socket { node_bind name_connect association
+        bindx_add bindx_rem connectx peeloff set_addr set_params }
+
+CIL policy language
+--------------------
+(classcommon sctp_socket socket)
+(class sctp_socket (node_bind name_connect association bindx_add bindx_rem
+       connectx peeloff set_addr set_params))
+(classorder (unordered sctp_socket))
+
+If userspace tools have been updated (see "Testing" section), then the portcon
+statement may be used as shown in the following example:
+(portcon sctp (2000 20000) (system_u object_r port_test_t ((s0) (s0))))
+
+Rule validation parameters used when 'network_peer_controls = 1':
+-------------------------------------------------------------------------------
+Rule  Source   Target     Class        Permissions
+-------------------------------------------------------------------------------
+allow domain_t self     : sctp_socket {connectx peeloff set_addr set_params};
+allow domain_t socket_t : sctp_socket {bindx_add bindx_rem set_params peeloff};
+allow socket_t port_t   : sctp_socket {name_bind name_connect};
+allow socket_t node_t   : sctp_socket {node_bind};
+allow socket_t peer_t   : sctp_socket {associate};
+allow peer_t   netif_t  : netif       {ingress egress};
+allow peer_t   node_t   : node        {recvfrom sendto};
+allow socket_t peer_t   : peer        {recv};
+allow domain_t packet_t : packet      {send recv relabelto}
+
+
+SCTP Socket Option Permissions
+===============================
+The permissions consist of: "bindx_add" "bindx_rem" "connectx" "set_addr" and
+"set_params" that are validated on setsockopt(2) calls, and "peeloff" that is
+validated on getsockopt(2) calls.
+
+SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
+                         associated after (optionally) calling bind(2)
+                         if given the "bind_add" permission.
+
+SCTP_SOCKOPT_CONNECTX  - Allows the allocation of multiple
+                         addresses for reaching a multi-homed peer
+                         if given the "connectx" permission.
+
+  Together they are used to form SCTP associations with information being
+  passed over the link to inform the peer of any changes. As these two options
+  can support multiple addresses, each address is checked via
+  selinux_socket_bind() or selinux_socket_connect() to determine whether they
+  have the correct permissions:
+    bindx_add: bind, name_bind, node_bind + node SID + port SID via the
+               (portcon sctp port ctx) policy statement.
+    connectx:  connect, name_connect + port SID via the
+               (portcon sctp port ctx) policy statement.
+
+SCTP_SOCKOPT_BINDX_REM - Allows additional bind addresses to be removed
+                         if given the "bind_rem" permission.
+
+SCTP_PEER_ADDR_PARAMS - Alter heartbeats and address max retransmissions.
+SCTP_PEER_ADDR_THLDS  - Alter the thresholds.
+SCTP_ASSOCINFO        - Alter association and endpoint parameters.
+ These require the "set_params" permission.
+
+SCTP_PRIMARY_ADDR          - Set local primary address.
+SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as association primary.
+ These require the "set_addr" permission.
+
+SCTP_SOCKOPT_PEELOFF - Branch off an association into a new socket that
+will be a one-to-one style socket. As SELinux already handles the creation
+of new sockets, only the "peeloff" permission is checked.
+
+
+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 { associate };
+
+As SCTP supports multiple endpoints on a single socket it is possible that
+each interface may be configured with a different peer label, however it is
+recommended that the labels are consistent.
+
+NOTES:
+   1) If peer labeling is not enabled, then the peer context will always be
+      SECINITSID_UNLABELED (unlabeled_t in Reference Policy).
+
+   2) If using NetLabel fallback labeling "netlabelctl unlbl ..." be aware
+      that if a label is assigned to a specific interface, and that interface
+      'goes down' (as in the "Multi-homing Test" section), 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).
+
+   3) SCTP sockets inherit their labels from the creating process (unless
+      there are policy rules to change this). They do NOT follow the TCP
+      labeling method even for TCP-style sockets. For reference: TCP child
+      sockets take the TE information from the parent server socket, but the
+      MLS/MCS information from the connection when CIPSO is enabled.
+
+   4) getpeercon(3) may be used by userspace apps to retrieve the sockets
+      peer context.
+
+   5) The peer labeling rules apply as discussed in the following set of
+      posts tagged "netlabel" at: http://www.paul-moore.com/blog/t
+
+
+      SCTP endpoint "A"                                SCTP endpoint "Z"
+      =================                                =================
+    sctp_sf_do_prm_asoc()
+ Initiate an association to
+ SCTP peer endpoint "Z".
+ Send INIT first as we need to obtain
+ a peer label before checking whether
+ this is allowed or not. This will be
+ checked once the INIT ACK has been
+ received.
+         INIT --------------------------------------------->
+                                                   sctp_sf_do_5_1B_init()
+                                                 Respond to an INIT chunk.
+                                             SCTP peer endpoint "A" is
+                                             asking for an association. Call
+                                             security_sctp_assoc_request()
+                                             to set the peer label if first
+                                             association, then check ASSOCIATE
+                                             permission:
+                             allow socket_t peer_t : sctp_socket { associate };
+                                             IF valid send:
+          <----------------------------------------------- INIT ACK
+          |                                  ELSE audit event and silently
+          |                                       discard the packet.
+    sctp_sf_do_5_1C_ack
+ Respond to an INIT ACK chunk.
+ SCTP peer endpoint"A" initiated
+ this association to SCTP peer
+ endpoint "Z". The security checks
+ are done now as we have a peer
+ label to check against, so call
+ security_sctp_assoc_request()
+ to set the peer label if first
+ association, then check ASSOCIATE
+ permission:
+    allow socket_t peer_t : sctp_socket { associate };
+ IF valid send:
+    COOKIE ECHO ------------------------------------------------>
+ ELSE audit event and silently                                  |
+      discard the packet.                                       |
+                                                                |
+          <----------------------------------------------- COOKIE ACK
+          |                                                     |
+   sctp_sf_do_5_1E_ca                                  sctp_sf_do_5_1D_ce
+      ESTABLISHED                                          ESTABLISHED
+          |                                                     |
+    ------------------------------------------------------------------
+    |                     Association Established                    |
+    ------------------------------------------------------------------
+
+
+Testing
+========
+Requirements:
+ 1) libsepol 2.5 or greater. If the sctp portcon statement is required, then
+    libsepol must be updated with the following patch:
+         http://arctic.selinuxproject.org/~rhaines/selinux-sctp/
+         selinux-Add-support-for-the-SCTP-portcon-keyword.patch
+
+ 2) A patched version of lksctp-tools (1.0.17 used for testing) to support
+    where sctp_test and sctp_darn have been modified to display the process,
+    peer and socket fd SELinux contexts using the -Z option. This patch is
+    available from:
+         http://arctic.selinuxproject.org/~rhaines/selinux-sctp/
+         lksctp-tools-Add-SELinux-support-to-sctp_test-and-sc.patch
+
+    lksctp-tools can be obtained by:
+         git clone git://github.com/sctp/lksctp-tools.git
+
+    The tools can then be built by adding the patch first (as it modifies
+    Makefile.am and configure.ac), then:
+        ./bootstrap
+        ./configure
+        make
+
+
+All lksctp-tools/src/func_tests run correctly in enforcing mode except when
+specific permissions are denied (e.g. test_peeloff_v6 will fail with
+"test_peeloff.c  1 BROK : sctp_peeloff: Permission denied").
+
+Tests involving removal of the "association" permission will wait, simply
+because the INIT or INIT ACK packets will be silently discarded, however as
+with all AVC denials they are audited in the audit log.
+
+The following sections describe the tests run using a CIL module added to the
+Fedora 'targeted' policy. The CIL policy is listed at the end of this document.
+
+During tests the audit.log should be monitored as there are 'auditallow'
+statements in the policy to show packet labels.
+
+The tests cover:
+ 1) All the lksctp-tools/src/func_tests. To check that permissions are
+    correctly denied, use '(not ( <perm> ...' rules as shown in the
+    CIL policy "Define SCTP class and permissions" section.
+
+ 2) Using lksctp-tools/src/apps/sctp_test and sctp_darn that has been modified
+    to display the process, peer and socket fd SELinux contexts using the -Z
+    option.
+
+ 3) Running lksctp-tools/src/apps/sctp_darn for multi-homing tests between
+    client/server as shown in the following diagram:
+
+                      ------- Wireless Router -------
+                     /                               \
+                    /                                 \
+                   /                                   \
+             192.168.1.77                          192.168.1.66
+                 /                                       \
+           ----------  Ethernet         193.168.1.67 ----------
+           | CLIENT | <----------------------------> | SERVER |
+           ---------- 193.168.1.78                   ----------
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Testing Setup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+Tested on Fedora 25 with kernel 4.8.11, only the client side results are shown.
+
+Initial setup before running any tests (modifying interfaces and addresses
+as required):
+
+1) Create and add the CIL policy module (see the "CIL policy module" section):
+       semodule --priority 400 -i sctp_test_module.cil
+
+2) Update iptables to allow sctp traffic (MUST be run on client and server):
+      iptables -I INPUT 1 -p sctp -j ACCEPT
+
+3) Set the fallback peer labels:
+      netlabelctl unlbl add interface:lo address:127.0.0.0/8 label:system_u:object_r:netlabel_peer_lo_t:s0
+      netlabelctl unlbl add interface:lo address:::1 label:system_u:object_r:netlabel_peer_lo_t:s0
+      netlabelctl unlbl add interface:wlp6s0 address:192.168.1.0/24 label:system_u:object_r:netlabel_peer_wlan_t:s0
+      netlabelctl unlbl add interface:enp7s0 address:193.168.1.0/24 label:system_u:object_r:netlabel_peer_eth_t:s0
+
+   The 'netlabelctl unlbl list' command can then be used to check the entries.
+
+4) Set SECMARK labels on SCTP packets. It is easier to paste the 'security' iptable
+   entries below into a script:
+
+############################ SECMARK IPTABLE ENTRIES ########################
+#
+# Flush the security table first:
+iptables -t security -F
+
+#-------------- INPUT IP Stream --------------------#
+# This INPUT rule sets all packets to default_packet_t:
+iptables -t security -A INPUT -j SECMARK --selctx system_u:object_r:default_packet_t:s0
+
+# These rules will replace the above context if sctp ports 1024:1035 are found in the packets:
+iptables -t security -A INPUT -i lo -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_lo_t:s0
+iptables -t security -A INPUT -i enp7s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_eth_t:s0
+iptables -t security -A INPUT -i wlp6s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_wlan_t:s0
+
+iptables -t security -A INPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
+
+#-------------- OUTPUT IP Stream --------------------#
+# This OUTPUT rule sets all packets to default_packet_t:
+iptables -t security -A OUTPUT -j SECMARK --selctx system_u:object_r:default_packet_t:s0
+
+# These rules will replace the above context if sctp ports 1024:1035 are found in the packets:
+iptables -t security -A OUTPUT -o lo -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_lo_t:s0
+iptables -t security -A OUTPUT -o enp7s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_eth_t:s0
+iptables -t security -A OUTPUT -o wlp6s0 -p sctp -m multiport --port 1024:1035 -j SECMARK --selctx system_u:object_r:sctp_packet_wlan_t:s0
+
+iptables -t security -A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNSECMARK --save
+
+iptables -t security -L
+#
+#################### END OF IPTABLES SECURITY TABLE ##################
+
+;
+;;;;;;;;;;;;;;;;;;;; Running func_tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+1) Paste below into script and run tests by passing over fqn path to lksctp-tools
+   All tests should pass.
+
+   To test denial of permissions see the "Define SCTP class and permissions"
+   section in the sample CIL policy.
+
+#!/bin/bash
+
+list="test_1_to_1_accept_close test_1_to_1_addrs test_1_to_1_connect test_1_to_1_connectx test_1_to_1_events test_1_to_1_initmsg_connect test_1_to_1_nonblock test_1_to_1_recvfrom test_1_to_1_recvmsg test_1_to_1_rtoinfo test_1_to_1_send test_1_to_1_sendmsg test_1_to_1_sendto test_1_to_1_shutdown test_1_to_1_socket_bind_listen test_1_to_1_sockopt test_1_to_1_threads test_assoc_abort test_assoc_shutdown test_autoclose test_basic test_basic_v6 test_connect test_connectx test_fragments test_fragments_v6 test_getname test_getname_v6 test_inaddr_any test_inaddr_any_v6 test_peeloff test_peeloff_v6 test_recvmsg test_sctp_sendrecvmsg test_sctp_sendrecvmsg_v6 test_sockopt test_sockopt_v6 test_tcp_style test_tcp_style_v6 test_timetolive test_timetolive_v6"
+
+if [ "$1" = "" ]; then
+	echo "Require path to lksctp-tools"
+	exit
+fi
+
+for i in $list
+    do  "$1/lksctp-tools/src/func_tests/$i"
+        if [ $? != 0 ]; then
+            echo -e "\nfunc_test $i FAILED\n"
+            exit
+       fi
+    done
+
+echo -e "\nAll func_tests passed.\n"
+
+;
+;;;;;;;;;;;;;;;;;;; Running sctp_test to check Peer contexts ;;;;;;;;;;;;;;;;;;
+;
+1) To check peer contexts run the following on the Server:
+    /path/to/lksctp-tools/src/apps/sctp_test -H 192.168.1.66 -B 193.168.1.67 -P 1035 -l -Z
+
+2) To check WLAN peer context run the following on the Client:
+    /path/to/lksctp-tools/src/apps/sctp_test -H 192.168.1.77 -P 1024 -C 192.168.1.66 -p 1035 -a 1 -c 4 -x 1 -o 2 -m 65515 -s -Z
+
+    The Client peer context should be:
+       sendmsg peer context=system_u:object_r:netlabel_peer_wlan_t:s0
+    The Client SECMARK context from the audit log should be:
+       tcontext=system_u:object_r:sctp_packet_wlan_t:s0 tclass=packet
+
+3) To check ETH peer context run the following on the Client:
+    /path/to/lksctp-tools/src/apps/sctp_test -H 193.168.1.78 -P 1024 -C 193.168.1.67 -p 1035 -a 1 -c 4 -x 1 -o 2 -m 65515 -s -Z
+
+    The Client peer context should be:
+       sendmsg peer context=system_u:object_r:netlabel_peer_eth_t:s0
+    The Client SECMARK context from the audit log should be:
+       tcontext=system_u:object_r:sctp_packet_eth_t:s0 tclass=packet
+
+
+;;;;;;;;;;;;;;;;;;;;; Running Multi-Homing Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+1) Run sctp_darn on server and client as follows:
+    Server:
+  /path/to/lksctp-tools/src/apps/sctp_darn -H 192.168.1.66 -B 193.168.1.67 -P 1035 -l -Z
+    Client:
+  /path/to/lksctp-tools/src/apps/sctp_darn -H 192.168.1.77 -B 193.168.1.78 -P 1024 -c 192.168.1.66 -c 193.168.1.67 -p 1035 -s -Z
+
+2) Send data from Client to Server - saddr 192.168.1.77 daddr 192.168.1.66
+3) Turn Server Wifi off (192.168.1.66)
+4) Send data from Client to Server - saddr 192.168.1.77 daddr 193.168.1.67
+
+Note that the peer context will be "netlabel_peer_wlan_t" even when the
+Server side Wifi is turned off. This is because the first association
+on the sctp socket sets the peer context to the first connection (in this
+case from the Server's wireless lan addr (192.168.1.66). It is therefore
+advised that the peer context is common across interfaces/addresses used
+by SCTP, note however that the SECMARK packet contexts will reflect the
+--selctx entry set in the iptables rules for interfaces/addresses.
+
+To set "netlabel_peer_eth_t", swap the peer socket 'connectx' (-c options)
+addresses on the Client as follows:
+
+  /path/to/lksctp-tools/src/apps/sctp_darn -H 192.168.1.77 -B 193.168.1.78 -P 1024 -c 193.168.1.67 -c 192.168.1.66 -p 1035 -s -Z
+
+The first connection will then be from the Server's ethernet addr (193.168.1.67).
+
+tcpdump(8) or tshark(1) may be used to monitor traffic on each interface,
+for example:
+    tcpdump -v -x -i lo sctp
+    tshark -O SCTP -P -x -i enp7s0
+
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CIL policy module ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+Paste the following CIL policy module into a file named "sctp_test_module.cil"
+The policy needs to be modified if libsepol has NOT been updated to support the
+new sctp portcon statement (see "Select SCTP portcon" section).
+To load the policy use:
+   semodule --priority 400 -i sctp_test_module.cil
+
+The policy MUST be loaded before attempting to set any netlabel or iptables
+entries.
+
+Once testing is complete the CIL policy module may be removed by:
+   semodule -r sctp_test_module
+
+;
+;;;;;;;;;;;;;;;;;;;;;;;; CIL SCTP POLICY MODULE START ;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+;;;;;;;;;;;;;;;;;;;;;;;; Define SCTP class and permissions ;;;;;;;;;;;;;;;;;;;;
+;
+; Add class for sctp_socket (requires libsepol 2.5+)
+(classorder (unordered sctp_socket))
+(classcommon sctp_socket socket)
+(class sctp_socket (node_bind name_connect association bindx_add bindx_rem
+    connectx peeloff set_addr set_params))
+;
+; Add permission for testing (see notes below)
+(classpermission sctp_socket_all_perms)
+;
+; For testing whether the "bindx_add bindx_rem connectx peeloff set_addr
+; set_params" permissions deny access use (not ( <perm> ... rules,
+; for example:
+;   (classpermissionset sctp_socket_all_perms (sctp_socket (not (set_params))))
+;
+; Once completed use this to grant all required permissions:
+(classpermissionset sctp_socket_all_perms (sctp_socket (all)))
+;
+; To test that "node_bind name_connect association" permissions deny access
+; remove the permissions from the applicable allow rules below.
+;
+;;;;;;;;;;;;;;;;;;;;;;;; Define peer labels and rules ;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(type netlabel_peer_lo_t)
+(type netlabel_peer_wlan_t)
+(type netlabel_peer_eth_t)
+(roletype object_r netlabel_peer_wlan_t)
+(roletype object_r netlabel_peer_eth_t)
+(typeattribute sctp_peers)
+(typeattributeset sctp_peers (netlabel_peer_lo_t netlabel_peer_wlan_t
+    netlabel_peer_eth_t))
+
+(allow unconfined_t sctp_peers (sctp_socket (association)))
+(allow unconfined_t sctp_peers (peer (recv)))
+(allow sctp_peers netif_t (netif (ingress egress)))
+(allow sctp_peers node_t (node (recvfrom)))
+
+;
+;;;;;;;;;;;;;;;;;;; Define SECMARK packet labels and rules ;;;;;;;;;;;;;;;;;;;;
+;
+; All packets other than sctp with ports 1024 - 1035 are SECMARK'ed using
+; iptables with default_packet_t. There is an 'allow' rule for this because
+; SCTP func_tests try illegal addresses, so needed to pass tests, plus all
+; other network traffic requires system access.
+(type default_packet_t)
+(type sctp_packet_lo_t)
+(type sctp_packet_wlan_t)
+(type sctp_packet_eth_t)
+(roletype object_r default_packet_t)
+(roletype object_r sctp_packet_lo_t)
+(roletype object_r sctp_packet_wlan_t)
+(roletype object_r sctp_packet_eth_t)
+(typeattribute sctp_packets)
+(typeattributeset sctp_packets (default_packet_t sctp_packet_lo_t
+    sctp_packet_wlan_t sctp_packet_eth_t))
+
+(allow unconfined_t sctp_packets (packet(send recv relabelto)))
+
+; Add audit rule to monitor packet labeling:
+(typeattribute audit_sctp_packets)
+(typeattributeset audit_sctp_packets (sctp_packet_lo_t sctp_packet_wlan_t
+    sctp_packet_eth_t))
+(auditallow unconfined_t audit_sctp_packets (packet(send recv)))
+
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;; Select SCTP portcon ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; If libsepol has been updated to support the "sctp" portcon keyword then
+; enable the TRUE set of statements, else use FALSE statement.
+;
+; TRUE:
+    (type sctp_port_t)
+    (roletype object_r sctp_port_t)
+    ; Set to (1024 1035) as func_tests start at 1024 with 10 clients (10 + 1).
+    (portcon sctp (1024 1035) (system_u object_r sctp_port_t ((s0) (s0))))
+    ; This allows port 0 otherwise test_1_to_1_connectx will fail as it
+    ; tests illegal addr.
+    (portcon sctp 0 (system_u object_r sctp_port_t ((s0) (s0))))
+    (allow unconfined_t sctp_port_t (sctp_socket (name_bind name_connect)))
+;
+; FALSE:
+;    ; need to allow port initial SID:
+;    (allow unconfined_t port_t (sctp_socket (name_bind name_connect)))
+
+; Common allow rules:
+(allow unconfined_t self sctp_socket_all_perms)
+(allow unconfined_t node_t (sctp_socket (node_bind)))
+
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index f2af2af..6a7ddaf 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -813,6 +813,22 @@ 
  *	@level contains the protocol level to set options for.
  *	@optname contains the name of the option to set.
  *	Return 0 if permission is granted.
+ * @sk_setsockopt:
+ *	Check permissions before setting the options associated with sock @sk.
+ *	This is equivalent to @socket_setsockopt, except that it has the
+ *	option parameters, as it is intended to support permission checking
+ *	of options and their parameters within network services.
+ *	An example usage is in net/sctp/socket.c where
+ *	sctp_getsockopt_connectx3() manages the @optval data into a certain
+ *	state before calling __sctp_setsockopt_connectx() that calls
+ *	@sk_setsockopt for permission checks. This means the code to manage
+ *	@optval does not need to be replicated in the security module.
+ *	@sk contains the sock structure.
+ *	@level contains the protocol level to set options for.
+ *	@optname contains the name of the option to set.
+ *	@optval contains the value(s) to set (already copied from userspace).
+ *	@optlen contains the length of the value(s) to be set.
+ *	Return 0 if permission is granted.
  * @socket_shutdown:
  *	Checks permission before all or part of a connection on the socket
  *	@sock is shut down.
@@ -902,6 +918,12 @@ 
  *	This hook can be used by the module to update any security state
  *	associated with the TUN device's security structure.
  *	@security pointer to the TUN devices's security structure.
+ * @sctp_assoc_request:
+ *	Update socket peer label if first association on @sk then check
+ *	whether association allowed.
+ *	@sk contains the sock structure.
+ *	@skb skbuff of association packet (INIT or INIT ACK) being queried.
+ *	Return 0 on success, error on failure.
  *
  * Security hooks for XFRM operations.
  *
@@ -1582,6 +1604,8 @@  union security_list_options {
 	int (*socket_getpeername)(struct socket *sock);
 	int (*socket_getsockopt)(struct socket *sock, int level, int optname);
 	int (*socket_setsockopt)(struct socket *sock, int level, int optname);
+	int (*sk_setsockopt)(struct sock *sk, int level, int optname,
+				    char *optval, int optlen);
 	int (*socket_shutdown)(struct socket *sock, int how);
 	int (*socket_sock_rcv_skb)(struct sock *sk, struct sk_buff *skb);
 	int (*socket_getpeersec_stream)(struct socket *sock,
@@ -1610,6 +1634,7 @@  union security_list_options {
 	int (*tun_dev_attach_queue)(void *security);
 	int (*tun_dev_attach)(struct sock *sk, void *security);
 	int (*tun_dev_open)(void *security);
+	int (*sctp_assoc_request)(struct sock *sk, struct sk_buff *skb);
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -1819,6 +1844,7 @@  struct security_hook_heads {
 	struct list_head socket_getpeername;
 	struct list_head socket_getsockopt;
 	struct list_head socket_setsockopt;
+	struct list_head sk_setsockopt;
 	struct list_head socket_shutdown;
 	struct list_head socket_sock_rcv_skb;
 	struct list_head socket_getpeersec_stream;
@@ -1841,6 +1867,7 @@  struct security_hook_heads {
 	struct list_head tun_dev_attach_queue;
 	struct list_head tun_dev_attach;
 	struct list_head tun_dev_open;
+	struct list_head sctp_assoc_request;
 #endif	/* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	struct list_head xfrm_policy_alloc_security;
diff --git a/include/linux/security.h b/include/linux/security.h
index a6c6d5d..9572b8b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1174,6 +1174,8 @@  int security_socket_getsockname(struct socket *sock);
 int security_socket_getpeername(struct socket *sock);
 int security_socket_getsockopt(struct socket *sock, int level, int optname);
 int security_socket_setsockopt(struct socket *sock, int level, int optname);
+int security_sk_setsockopt(struct sock *sk, int level, int optname,
+				char *optval, int optlen);
 int security_socket_shutdown(struct socket *sock, int how);
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
@@ -1200,6 +1202,7 @@  int security_tun_dev_create(void);
 int security_tun_dev_attach_queue(void *security);
 int security_tun_dev_attach(struct sock *sk, void *security);
 int security_tun_dev_open(void *security);
+int security_sctp_assoc_request(struct sock *sk, struct sk_buff *skb);
 
 #else	/* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct sock *sock,
@@ -1289,6 +1292,13 @@  static inline int security_socket_setsockopt(struct socket *sock,
 	return 0;
 }
 
+static inline int security_sk_setsockopt(struct sock *sk, int level,
+					    int optname, char *optval,
+					    int optlen)
+{
+	return 0;
+}
+
 static inline int security_socket_shutdown(struct socket *sock, int how)
 {
 	return 0;
@@ -1392,6 +1402,12 @@  static inline int security_tun_dev_open(void *security)
 {
 	return 0;
 }
+
+static inline int security_sctp_assoc_request(struct sock *sk,
+			struct sk_buff *skb)
+{
+	return 0;
+}
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 920469e..920b101 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -315,6 +315,12 @@  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.
+	 */
+	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 +514,12 @@  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.
+	 */
+	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);
 
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 7b0e059..ff4f1a8 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_sk_setsockopt(sk, SOL_SCTP,
+					     SCTP_SOCKOPT_BINDX_ADD,
+					     (char *)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_sk_setsockopt(sk, SOL_SCTP,
+					     SCTP_SOCKOPT_CONNECTX,
+					    (char *)kaddrs, addrs_size);
+		if (err)
+			goto out_free;
+
 		err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
 	}
 
+out_free:
 	kfree(kaddrs);
 
 	return err;
@@ -7826,6 +7840,8 @@  static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
 		newsk->sk_state = SCTP_SS_ESTABLISHED;
 	}
 
+	/* Ensure newsk has the same security attributes. */
+	security_sk_clone(oldsk, newsk);
 	release_sock(newsk);
 }
 
diff --git a/security/security.c b/security/security.c
index f825304..23ce9ea 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1307,6 +1307,14 @@  int security_socket_setsockopt(struct socket *sock, int level, int optname)
 	return call_int_hook(socket_setsockopt, 0, sock, level, optname);
 }
 
+int security_sk_setsockopt(struct sock *sk, int level, int optname,
+				    char *optval, int optlen)
+{
+	return call_int_hook(sk_setsockopt, 0, sk, level, optname, optval,
+				    optlen);
+}
+EXPORT_SYMBOL(security_sk_setsockopt);
+
 int security_socket_shutdown(struct socket *sock, int how)
 {
 	return call_int_hook(socket_shutdown, 0, sock, how);
@@ -1439,6 +1447,12 @@  int security_tun_dev_open(void *security)
 }
 EXPORT_SYMBOL(security_tun_dev_open);
 
+int security_sctp_assoc_request(struct sock *sk, struct sk_buff *skb)
+{
+	return call_int_hook(sctp_assoc_request, 0, sk, skb);
+}
+EXPORT_SYMBOL(security_sctp_assoc_request);
+
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -1856,6 +1870,8 @@  struct security_hook_heads security_hook_heads = {
 		LIST_HEAD_INIT(security_hook_heads.socket_getsockopt),
 	.socket_setsockopt =
 		LIST_HEAD_INIT(security_hook_heads.socket_setsockopt),
+	.sk_setsockopt =
+		LIST_HEAD_INIT(security_hook_heads.sk_setsockopt),
 	.socket_shutdown =
 		LIST_HEAD_INIT(security_hook_heads.socket_shutdown),
 	.socket_sock_rcv_skb =
@@ -1897,6 +1913,8 @@  struct security_hook_heads security_hook_heads = {
 	.tun_dev_attach =
 		LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
 	.tun_dev_open =	LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
+	.sctp_assoc_request =
+		LIST_HEAD_INIT(security_hook_heads.sctp_assoc_request),
 #endif	/* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	.xfrm_policy_alloc_security =
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 3411c33..f60a8a3 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -13,6 +13,8 @@  selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
 
 selinux-$(CONFIG_NETLABEL) += netlabel.o
 
+selinux-$(subst m,y,$(CONFIG_IP_SCTP)) += sctp.o
+
 ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
 
 $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e15e560..491599c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -65,6 +65,7 @@ 
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/dccp.h>
+#include <linux/sctp.h>
 #include <linux/quota.h>
 #include <linux/un.h>		/* for Unix socket types */
 #include <net/af_unix.h>	/* for Unix socket types */
@@ -93,6 +94,7 @@ 
 #include "netlabel.h"
 #include "audit.h"
 #include "avc_ss.h"
+#include "sctp.h"
 
 /* SECMARK reference count */
 static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
@@ -1280,8 +1282,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:
@@ -4034,6 +4039,23 @@  static int selinux_parse_skb_ipv4(struct sk_buff *skb,
 		break;
 	}
 
+#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
+	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;
 	}
@@ -4107,6 +4129,19 @@  static int selinux_parse_skb_ipv6(struct sk_buff *skb,
 		break;
 	}
 
+#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
+	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;
@@ -4236,7 +4271,7 @@  static int socket_sockcreate_sid(const struct task_security_struct *tsec,
 				       socksid);
 }
 
-static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
+int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
 {
 	struct sk_security_struct *sksec = sk->sk_security;
 	struct common_audit_data ad;
@@ -4306,7 +4341,8 @@  static int selinux_socket_post_create(struct socket *sock, int family,
    Need to determine whether we should perform a name_bind
    permission check between the socket and the port number. */
 
-static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+int selinux_socket_bind(struct socket *sock, struct sockaddr *address,
+								int addrlen)
 {
 	struct sock *sk = sock->sk;
 	u16 family;
@@ -4318,8 +4354,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_setsockopt().
 	 */
 	family = sk->sk_family;
 	if (family == PF_INET || family == PF_INET6) {
@@ -4377,6 +4413,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;
@@ -4405,7 +4445,8 @@  out:
 	return err;
 }
 
-static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
+int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
+								int addrlen)
 {
 	struct sock *sk = sock->sk;
 	struct sk_security_struct *sksec = sk->sk_security;
@@ -4416,10 +4457,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;
@@ -4443,8 +4486,12 @@  static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
 		if (err)
 			goto out;
 
-		perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
-		       TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
+		if (sksec->sclass == SECCLASS_TCP_SOCKET)
+			perm = TCP_SOCKET__NAME_CONNECT;
+		else if (sksec->sclass == SECCLASS_DCCP_SOCKET)
+			perm = DCCP_SOCKET__NAME_CONNECT;
+		else if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+			perm = SCTP_SOCKET__NAME_CONNECT;
 
 		ad.type = LSM_AUDIT_DATA_NET;
 		ad.u.net = &net;
@@ -4516,13 +4563,35 @@  static int selinux_socket_setsockopt(struct socket *sock, int level, int optname
 	if (err)
 		return err;
 
+	err = selinux_sctp_setsockopt(sock->sk, level, optname, NULL, 0);
+	if (err)
+		return err;
+
 	return selinux_netlbl_socket_setsockopt(sock, level, optname);
 }
 
+static int selinux_sk_setsockopt(struct sock *sk, int level, int optname,
+					    char *optval, int optlen)
+{
+	int err;
+
+	err = sock_has_perm(current, sk, SOCKET__SETOPT);
+	if (err)
+		return err;
+
+	return selinux_sctp_setsockopt(sk, level, optname, optval, optlen);
+}
+
 static int selinux_socket_getsockopt(struct socket *sock, int level,
 				     int optname)
 {
-	return sock_has_perm(current, sock->sk, SOCKET__GETOPT);
+	int err;
+
+	err = sock_has_perm(current, sock->sk, SOCKET__GETOPT);
+	if (err)
+		return err;
+
+	return selinux_sctp_getsockopt(sock->sk, level, optname);
 }
 
 static int selinux_socket_shutdown(struct socket *sock, int how)
@@ -4715,7 +4784,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;
@@ -4828,6 +4898,36 @@  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;
+
+	return avc_has_perm(sksec->sid, sksec->peer_sid, sksec->sclass,
+				    SCTP_SOCKET__ASSOCIATION, &ad);
+}
+
 static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 				     struct request_sock *req)
 {
@@ -6243,6 +6343,7 @@  static struct security_hook_list selinux_hooks[] = {
 	LSM_HOOK_INIT(socket_getpeername, selinux_socket_getpeername),
 	LSM_HOOK_INIT(socket_getsockopt, selinux_socket_getsockopt),
 	LSM_HOOK_INIT(socket_setsockopt, selinux_socket_setsockopt),
+	LSM_HOOK_INIT(sk_setsockopt, selinux_sk_setsockopt),
 	LSM_HOOK_INIT(socket_shutdown, selinux_socket_shutdown),
 	LSM_HOOK_INIT(socket_sock_rcv_skb, selinux_socket_sock_rcv_skb),
 	LSM_HOOK_INIT(socket_getpeersec_stream,
@@ -6253,6 +6354,7 @@  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(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..353183a 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -165,5 +165,9 @@  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", "bindx_rem", "connectx", "peeloff", "set_addr",
+	    "set_params", NULL } },
 	{ NULL }
   };
diff --git a/security/selinux/include/sctp.h b/security/selinux/include/sctp.h
new file mode 100644
index 0000000..cee8e2d
--- /dev/null
+++ b/security/selinux/include/sctp.h
@@ -0,0 +1,50 @@ 
+/*
+ * SELinux SCTP Support
+ *
+ * Provides security checks for the SCTP protocol.
+ *
+ * Author: Richard Haines <richard_c_haines@btinternet.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef _SELINUX_SCTP_H_
+#define _SELINUX_SCTP_H_
+
+#include <linux/types.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include "objsec.h"
+
+#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
+int selinux_sctp_setsockopt(struct sock *sk,
+				    int level,
+				    int optname,
+				    char *optval,
+				    int optlen);
+
+int selinux_sctp_getsockopt(struct sock *sk,
+				    int level,
+				    int optname);
+#else
+static inline int selinux_sctp_setsockopt(struct sock *sk,
+				    int level,
+				    int optname,
+				    char *optval,
+				    int optlen)
+{
+	return 0;
+}
+static inline int selinux_sctp_getsockopt(struct sock *sk,
+				    int level,
+				    int optname)
+{
+	return 0;
+}
+#endif  /* CONFIG_IP_SCTP */
+
+#endif
diff --git a/security/selinux/include/sctp_private.h b/security/selinux/include/sctp_private.h
new file mode 100644
index 0000000..eaa9f4c
--- /dev/null
+++ b/security/selinux/include/sctp_private.h
@@ -0,0 +1,39 @@ 
+/*
+ * SELinux SCTP Support
+ *
+ * Provides security checks for the SCTP protocol.
+ *
+ * Author: Richard Haines <richard_c_haines@btinternet.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/sctp.h>
+#include <uapi/linux/sctp.h>	/* For bindx setsocket option checks */
+#include <net/ip.h>
+#include <linux/skbuff.h>
+
+#include "security.h"
+#include "avc.h"
+#include "objsec.h"
+
+extern int sock_has_perm(struct task_struct *task,
+				    struct sock *sk, u32 perms);
+
+extern int selinux_socket_bind(struct socket *sock,
+				    struct sockaddr *address,
+					    int addrlen);
+
+extern int selinux_socket_connect(struct socket *sock,
+				    struct sockaddr *address,
+				    int addrlen);
+
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index aaba667..300a195 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -399,6 +399,9 @@  int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
 	case SECCLASS_TCP_SOCKET:
 		perm = TCP_SOCKET__RECVFROM;
 		break;
+	case SECCLASS_SCTP_SOCKET:
+		perm = SCTP_SOCKET__RECVFROM;
+		break;
 	default:
 		perm = RAWIP_SOCKET__RECVFROM;
 	}
diff --git a/security/selinux/sctp.c b/security/selinux/sctp.c
new file mode 100644
index 0000000..bd25712
--- /dev/null
+++ b/security/selinux/sctp.c
@@ -0,0 +1,194 @@ 
+/*
+ * SELinux SCTP Support
+ *
+ * Provides security checks for the SCTP protocol.
+ *
+ * Author: Richard Haines <richard_c_haines@btinternet.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "sctp_private.h"
+
+/**
+ * selinux_sctp_setsockopt - Check setsockopt values.
+ * @sk: the socket
+ * @level: contains the protocol level to validate
+ * @optname: contains the name of the option to validate
+ * @optval: contains the value(s) to set
+ * @optlen: contains the length of the value(s) to be set
+ *
+ * Description:
+ * Check whether SCTP socket options are allowed or not. Returns zero on
+ * success, negative values on failure.
+ *
+ * setsockopt(2) option support:
+ *
+ *    SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
+ *                             associated after (optionally) calling bind(3).
+ *                             sctp_bindx(3) adds or removes 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.
+ *                            May also return an association id.
+ *
+ *   Together they form SCTP associations and will be passed over the
+ *   link to inform peer of any changes. As these two options can support
+ *   multiple addresses, each address is checked via selinux_socket_bind() or
+ *   selinux_socket_connect() to determine whether they have the correct
+ *   permissions:
+ *     bindx_add: bind, name_bind, node_bind + node SID + port SID via the
+ *                (portcon sctp port ctx) policy statement.
+ *     connectx:  connect, name_connect + port SID via the
+ *                (portcon sctp port ctx) policy statement.
+ *
+ *  These options require set_params permission:
+ *    SCTP_SOCKOPT_BINDX_REM - As the addresses would have already been
+ *                             allowed, only the bindx_rem permission
+ *                             is checked.
+ *
+ *    SCTP_PEER_ADDR_PARAMS  - Set heartbeats and address max
+ *                             retransmissions.
+ *
+ *    SCTP_PEER_ADDR_THLDS   - Set thresholds.
+ *
+ *    SCTP_ASSOCINFO         - Set association and endpoint parameters.
+ *
+ *  These options require the set_addr permission.
+ *    SCTP_PRIMARY_ADDR          - Set local primary address.
+ *
+ *    SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as
+ *                                 association primary.
+ */
+int selinux_sctp_setsockopt(struct sock *sk, int level, int optname,
+					    char *optval, int optlen)
+{
+	int err, addrlen;
+	void *addr_buf;
+	struct sockaddr *address;
+	struct socket *sock;
+	int walk_size = 0;
+
+	if (level != SOL_SCTP || level != IPPROTO_SCTP)
+		return 0;
+
+	switch (optname) {
+	case SCTP_SOCKOPT_BINDX_ADD:
+	case SCTP_SOCKOPT_CONNECTX:
+		/* Note that for SCTP_SOCKOPT_BINDX_ADD and
+		 * SCTP_SOCKOPT_CONNECTX the sctp kernel code has already
+		 * copied the optval to kernel space. See net/sctp/socket.c
+		 * security_sk_setsockopt() calls.
+		 */
+		err = sock_has_perm(current, sk,
+			    (optname == SCTP_SOCKOPT_BINDX_ADD ?
+			     SCTP_SOCKET__BINDX_ADD :
+			     SCTP_SOCKET__CONNECTX));
+		if (err)
+			return err;
+
+		sock = sk->sk_socket;
+		addr_buf = optval;
+		/* Process list - may contain IPv4 or IPv6 addr's */
+		while (walk_size < optlen) {
+			address = addr_buf;
+
+			switch (address->sa_family) {
+			case PF_INET:
+				addrlen = sizeof(struct sockaddr_in);
+				break;
+			case PF_INET6:
+				addrlen = sizeof(struct sockaddr_in6);
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			err = -EINVAL;
+			if (optname == SCTP_SOCKOPT_BINDX_ADD) {
+				err = selinux_socket_bind(sock,
+					    address, addrlen);
+			} else if (optname == SCTP_SOCKOPT_CONNECTX) {
+				err = selinux_socket_connect(sock,
+					    address, addrlen);
+			}
+			if (err)
+				return err;
+
+			addr_buf += addrlen;
+			walk_size += addrlen;
+		}
+		break;
+
+	case SCTP_SOCKOPT_BINDX_REM:
+		/* The addresses have been checked as they were
+		 * added, so just see if allowed to be removed.
+		 */
+		err = sock_has_perm(current, sk, SCTP_SOCKET__BINDX_REM);
+		if (err)
+			return err;
+		break;
+
+	/* Set heartbeats and address max retransmissions. */
+	case SCTP_PEER_ADDR_PARAMS:
+	/* Set thresholds. */
+	case SCTP_PEER_ADDR_THLDS:
+	/* Set association and endpoint parameters */
+	case SCTP_ASSOCINFO:
+		err = sock_has_perm(current, sk, SCTP_SOCKET__SET_PARAMS);
+		if (err)
+			return err;
+		break;
+
+	/* Set local primary address. */
+	case SCTP_PRIMARY_ADDR:
+	/* Request peer sets address as association primary. */
+	case SCTP_SET_PEER_PRIMARY_ADDR:
+		err = sock_has_perm(current, sk, SCTP_SOCKET__SET_ADDR);
+		if (err)
+			return err;
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * selinux_sctp_getsockopt - Check getsockopt values.
+ * @sk: the socket
+ * @level: contains the protocol level to validate
+ * @optname: contains the name of the option to validate
+ *
+ * Description:
+ * Check whether SCTP socket options are allowed or not. Returns zero on
+ * success, negative values on failure.
+ *
+ *    SCTP_SOCKOPT_PEELOFF - Branch off an association into a new socket that
+ *                           will be a one-to-one style socket. As SELinux
+ *                           already handles the creation of new sockets, only
+ *                           the peeloff permission is checked.
+ */
+int selinux_sctp_getsockopt(struct sock *sk, int level, int optname)
+{
+	int err;
+
+	if (level != SOL_SCTP || level != IPPROTO_SCTP)
+		return 0;
+
+	switch (optname) {
+	case SCTP_SOCKOPT_PEELOFF:
+		err = sock_has_perm(current, sk, SCTP_SOCKET__PEELOFF);
+		if (err)
+			return err;
+		break;
+	}
+
+	return 0;
+}