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;
+}
