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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxx>
> ---
>  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@xxxxxxxxxxxxxx>
> + *
> + * 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@xxxxxxxxxxxxxx>
> + *
> + * 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@xxxxxxxxxxxxxx>
> + *
> + * 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;
> +}

_______________________________________________
Selinux mailing list
Selinux@xxxxxxxxxxxxx
To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx.
To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux