The Documentation/security/SELinux-sctp.txt describes how the patch has been implemented and tested. Patches to support the new SCTP portcon statement can be found at [1] and will apply to the latest release (rc7) found at [2]. Also minor updates for DCCP to retrieve peer context via getpeercon(3). Built and tested on Fedora kernel-3.17.3-200.fc20.x86_64 [1] http://marc.info/?l=selinux&m=141536871703018&w=2 [2] https://github.com/SELinuxProject/selinux/wiki/Releases Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx> --- Documentation/security/SELinux-sctp.txt | 247 ++++++++++++++++++++++++++++++++ include/linux/security.h | 42 ++++++ net/sctp/sm_statefuns.c | 10 ++ net/sctp/socket.c | 15 ++ security/capability.c | 13 ++ security/security.c | 13 ++ security/selinux/Makefile | 2 + security/selinux/hooks.c | 147 ++++++++++++++++--- security/selinux/include/classmap.h | 4 + security/selinux/include/sctp.h | 52 +++++++ security/selinux/include/sctp_private.h | 51 +++++++ security/selinux/netlabel.c | 3 + security/selinux/sctp.c | 185 ++++++++++++++++++++++++ 13 files changed, 768 insertions(+), 16 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..476ba24 --- /dev/null +++ b/Documentation/security/SELinux-sctp.txt @@ -0,0 +1,247 @@ + 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/security.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 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" +and "peeloff". 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 } + +CIL policy language +-------------------- +(classcommon sctp_socket socket) +(class sctp_socket (node_bind name_connect association bindx_add bindx_rem + connectx peeloff)) + +Last classorder from refpolicy base: + (classorder (db_language sctp_socket)) +Last classorder from Fedora targeted base: + (classorder (proxy sctp_socket)) + +If userspace tools have been updated, 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)))) + +NOTE: SELinux supports a maximum of 32 permissions per object class. SCTP + has already allocated 29 (22 socket + 7 sctp_socket permissions). + These are the permissions checked for sctp_socket in security/hooks.c: + read write create getattr bind connect listen accept getopt setopt + name_bind node_bind name_connect association bindx_add bindx_rem + connectx peeloff + +"allow" Rule Validation Parameters (network_peer_controls = 1) +------------------------------------------------------------------- +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}; + + +SCTP Socket Option Permissions +=============================== +These consist of: "bindx_add" "bindx_rem" "connectx" and "peeloff" and are +used as follows: + +SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be + associated after (optionally) calling bind(2). + +SCTP_SOCKOPT_CONNECTX* - Allows the allocation of multiple + addresses for reaching a multi-homed peer. + + 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. + +SCTP_SOCKOPT_BINDX_REM - As the addresses would have already been + allowed, only the bindx_rem permission is checked. + +SCTP_PEER_ADDR_PARAMS - Alter heartbeats and address max retransmissions. +SCTP_PEER_ADDR_THLDS - Alter the thresholds. + 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 consistant (see "Multi-homing Test" section +for examples). + +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. + + 4) getpeercon(3) may be used by userspace apps to retrieve the sockets + peer context. + + 5) The peer labeling rules still apply as discussed in the following set + of pages at: http://paulmoore.livejournal.com/tag/netlabel + + + 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 +======== +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. + +Multi-homing Test +------------------ + ------- Wireless Router ------- + / \ + / \ + / \ + 192.168.1.66 192.168.1.64 + / \ + ---------- Ethernet 193.168.1.65 ---------- + | CLIENT | <----------------------------> | SERVER | + ---------- 193.168.1.67 ---------- + sctp_darn sctp_darn + -H 192.168.1.66 -H 192.168.1.64 + -B 193.168.1.67 -B 193.168.1.65 + -P 19000 -P 19001 + -c 192.168.1.64 -l + -c 193.168.1.65 + -p 19001 + -s + +Set Netlabel fallback peer labeling on Client and Server as follows: + netlabelctl unlbl add interface:wlan0 address:192.168.1.0/24 \ + label:system_u:object_r:sctp_netlabel_peer_t:s0 + netlabelctl unlbl add interface:p2p1 address:193.168.1.0/24 \ + label:system_u:object_r:sctp_netlabel_peer_t:s0 + +Then: +Send data from Client to Server - ok - saddr 192.168.1.66 daddr 192.168.1.64 +Turn Server Wifi off (192.168.1.64) +Send data from Client to Server - ok - saddr 192.168.1.66 daddr 193.168.1.65 + +tshark(1) may be used to monitor traffic on each interface, for example: + tshark -O SCTP -P -x -i wlan0 + tshark -O SCTP -P -x -i p2p1 + diff --git a/include/linux/security.h b/include/linux/security.h index 623f90e..af62982 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -948,6 +948,23 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @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() and __sctp_setsockopt_connectx() + * manage the @optval data into a certain state before @sk_setsockopt + * is called for permission checks. This means that 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. @@ -997,6 +1014,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * authorizations. * @sock_graft: * Sets the socket's isec sid to the sock's sid. + * @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. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: @@ -1666,6 +1689,8 @@ struct security_operations { 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, char __user *optval, int __user *optlen, unsigned len); @@ -1675,6 +1700,7 @@ struct security_operations { void (*sk_clone_security) (const struct sock *sk, struct sock *newsk); void (*sk_getsecid) (struct sock *sk, u32 *secid); void (*sock_graft) (struct sock *sk, struct socket *parent); + int (*sctp_assoc_request) (struct sock *sk, struct sk_buff *skb); int (*inet_conn_request) (struct sock *sk, struct sk_buff *skb, struct request_sock *req); void (*inet_csk_clone) (struct sock *newsk, const struct request_sock *req); @@ -2650,6 +2676,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, @@ -2661,6 +2689,7 @@ void security_sk_clone(const struct sock *sk, struct sock *newsk); void security_sk_classify_flow(struct sock *sk, struct flowi *fl); void security_req_classify_flow(const struct request_sock *req, struct flowi *fl); void security_sock_graft(struct sock*sk, struct socket *parent); +int security_sctp_assoc_request(struct sock *sk, struct sk_buff *skb); int security_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req); void security_inet_csk_clone(struct sock *newsk, @@ -2767,6 +2796,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; @@ -2813,6 +2849,12 @@ static inline void security_sock_graft(struct sock *sk, struct socket *parent) { } +static inline int security_sctp_assoc_request(struct sock *sk, + struct sk_buff *skb) +{ + return 0; +} + static inline int security_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 3ee27b7..0e7fb3d 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -315,6 +315,11 @@ 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 +513,11 @@ 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 634a2ab..d0e0593 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1012,6 +1012,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; @@ -1327,9 +1333,16 @@ 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; @@ -7335,6 +7348,8 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, newsk->sk_shutdown |= RCV_SHUTDOWN; 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/capability.c b/security/capability.c index a74fde6..0272b05 100644 --- a/security/capability.c +++ b/security/capability.c @@ -643,6 +643,12 @@ static int cap_socket_setsockopt(struct socket *sock, int level, int optname) return 0; } +static int cap_sk_setsockopt(struct sock *sk, int level, int optname, + char *optval, int optlen) +{ + return 0; +} + static int cap_socket_getsockopt(struct socket *sock, int level, int optname) { return 0; @@ -692,6 +698,11 @@ static void cap_sock_graft(struct sock *sk, struct socket *parent) { } +static int cap_sctp_assoc_request(struct sock *sk, struct sk_buff *skb) +{ + return 0; +} + static int cap_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { @@ -1084,6 +1095,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, socket_getsockname); set_to_cap_if_null(ops, socket_getpeername); set_to_cap_if_null(ops, socket_setsockopt); + set_to_cap_if_null(ops, sk_setsockopt); set_to_cap_if_null(ops, socket_getsockopt); set_to_cap_if_null(ops, socket_shutdown); set_to_cap_if_null(ops, socket_sock_rcv_skb); @@ -1094,6 +1106,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, sk_clone_security); set_to_cap_if_null(ops, sk_getsecid); set_to_cap_if_null(ops, sock_graft); + set_to_cap_if_null(ops, sctp_assoc_request); set_to_cap_if_null(ops, inet_conn_request); set_to_cap_if_null(ops, inet_csk_clone); set_to_cap_if_null(ops, inet_conn_established); diff --git a/security/security.c b/security/security.c index e41b1a8..a03ee03 100644 --- a/security/security.c +++ b/security/security.c @@ -1207,6 +1207,13 @@ int security_socket_setsockopt(struct socket *sock, int level, int optname) return security_ops->socket_setsockopt(sock, level, optname); } +int security_sk_setsockopt(struct sock *sk, int level, int optname, + char *optval, int optlen) +{ + return security_ops->sk_setsockopt(sk, level, optname, optval, optlen); +} +EXPORT_SYMBOL(security_sk_setsockopt); + int security_socket_shutdown(struct socket *sock, int how) { return security_ops->socket_shutdown(sock, how); @@ -1264,6 +1271,12 @@ void security_sock_graft(struct sock *sk, struct socket *parent) } EXPORT_SYMBOL(security_sock_graft); +int security_sctp_assoc_request(struct sock *sk, struct sk_buff *skb) +{ + return security_ops->sctp_assoc_request(sk, skb); +} +EXPORT_SYMBOL(security_sctp_assoc_request); + int security_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { diff --git a/security/selinux/Makefile b/security/selinux/Makefile index ad5cd76..a450c85 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 := -Isecurity/selinux -Isecurity/selinux/include $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e03bad5..1440d9a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -66,6 +66,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 */ @@ -94,6 +95,7 @@ #include "netlabel.h" #include "audit.h" #include "avc_ss.h" +#include "sctp.h" extern struct security_operations *security_ops; @@ -1185,8 +1187,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: @@ -3726,6 +3731,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; } @@ -3799,6 +3821,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; @@ -3859,7 +3894,7 @@ okay: * Description: * Check the various different forms of network peer labeling and determine * the peer label/SID for the packet; most of the magic actually occurs in - * the security server function security_net_peersid_cmp(). The function + * the security server function security_net_peersid_resolve(). The function * returns zero if the value in @sid is valid (although it may be SECSID_NULL) * or -EACCES if @sid is invalid due to inconsistencies with the different * peer labels. @@ -3928,7 +3963,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; @@ -3998,7 +4033,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; @@ -4010,8 +4046,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_validate_sockopt(). */ family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { @@ -4069,6 +4105,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; @@ -4097,7 +4137,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; @@ -4108,10 +4149,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; @@ -4135,8 +4178,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; @@ -4208,13 +4255,39 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname if (err) return err; + /* SCTP options that do not require handling by SCTP. */ + err = selinux_sctp_validate_sockopt(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; + + /* SCTP options that require handling by SCTP first. */ + return selinux_sctp_validate_sockopt(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_validate_sockopt(sock->sk, level, optname, + NULL, 0); } static int selinux_socket_shutdown(struct socket *sock, int how) @@ -4407,7 +4480,9 @@ 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_DCCP_SOCKET || + sksec->sclass == SECCLASS_SCTP_SOCKET) peer_sid = sksec->peer_sid; if (peer_sid == SECSID_NULL) return -ENOPROTOOPT; @@ -4516,6 +4591,35 @@ 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) { @@ -4805,7 +4909,10 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, if (sk) { struct sk_security_struct *sksec; - if (sk->sk_state == TCP_LISTEN) + /* Note that TCP_LISTEN equates to SCTP_SS_LISTENING + * and DCCP_LISTEN socket states so also check protocol. */ + if (sk->sk_state == TCP_LISTEN && + sk->sk_protocol == IPPROTO_TCP) /* if the socket is the listening state then this * packet is a SYN-ACK packet which means it needs to * be labeled based on the connection/request_sock and @@ -4910,9 +5017,12 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, * TCP listening state we cannot wait until the XFRM processing * is done as we will miss out on the SA label if we do; * unfortunately, this means more work, but it is only once per - * connection. */ + * connection. + * NOTE: that TCP_LISTEN equates to SCTP_SS_LISTENING and DCCP_LISTEN + * socket states so also check protocol. */ if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && - !(sk != NULL && sk->sk_state == TCP_LISTEN)) + !(sk != NULL && sk->sk_state == TCP_LISTEN && + sk->sk_protocol == IPPROTO_TCP)) return NF_ACCEPT; #endif @@ -4929,7 +5039,10 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; peer_sid = SECINITSID_KERNEL; } - } else if (sk->sk_state == TCP_LISTEN) { + /* Note that TCP_LISTEN equates to SCTP_SS_LISTENING and DCCP_LISTEN + * socket states so also check protocol. */ + } else if (sk->sk_state == TCP_LISTEN && + sk->sk_protocol == IPPROTO_TCP) { /* Locally generated packet but the associated socket is in the * listening state which means this is a SYN-ACK packet. In * this particular case the correct security label is assigned @@ -5924,6 +6037,7 @@ static struct security_operations selinux_ops = { .socket_getpeername = selinux_socket_getpeername, .socket_getsockopt = selinux_socket_getsockopt, .socket_setsockopt = selinux_socket_setsockopt, + .sk_setsockopt = selinux_sk_setsockopt, .socket_shutdown = selinux_socket_shutdown, .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, .socket_getpeersec_stream = selinux_socket_getpeersec_stream, @@ -5933,6 +6047,7 @@ static struct security_operations selinux_ops = { .sk_clone_security = selinux_sk_clone_security, .sk_getsecid = selinux_sk_getsecid, .sock_graft = selinux_sock_graft, + .sctp_assoc_request = selinux_sctp_assoc_request, .inet_conn_request = selinux_inet_conn_request, .inet_csk_clone = selinux_inet_csk_clone, .inet_conn_established = selinux_inet_conn_established, diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index be491a7..09797ee 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -151,5 +151,9 @@ struct security_class_mapping secclass_map[] = { { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, "attach_queue", 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..81dd469 --- /dev/null +++ b/security/selinux/include/sctp.h @@ -0,0 +1,52 @@ +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#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_validate_sockopt(struct sock *sk, + int level, + int optname, + char *optval, + int optlen); +#else +static inline int selinux_sctp_validate_sockopt(struct sock *sk, + int level, + int optname, + char *optval, + int optlen) +{ + 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..f393f6f --- /dev/null +++ b/security/selinux/include/sctp_private.h @@ -0,0 +1,51 @@ +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#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 0364120..a6c6df1 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -396,6 +396,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..41c646d --- /dev/null +++ b/security/selinux/sctp.c @@ -0,0 +1,185 @@ +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "sctp_private.h" /* #include files + extern functions. */ + +/** + * selinux_sctp_validate_sockopt - Check get/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. + * + * Supports the following socket options: + * + * 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) - initiate 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. + * + * SCTP_SOCKOPT_BINDX_REM - As the addresses would have already been + * allowed, only the bindx_rem permission is checked. + * + * SCTP_PEER_ADDR_PARAMS - Alter heartbeats and address max + * retransmissions. + * SCTP_PEER_ADDR_THLDS - Alter the thresholds. + * 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. + * + */ +int selinux_sctp_validate_sockopt(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; + + /* This handles SCTP specific options. Note that SCTP has the optval + * in kernel space already therefore no copy is required. */ + if (level == SOL_SCTP) { + switch (optname) { + /* Note that for SCTP_SOCKOPT_BINDX_ADD and + * SCTP_SOCKOPT_CONNECTX SCTP has copied the optval to kernel + * space already therefore no copy is required. See + * net/sctp/socket.c security_sk_setsockopt() calls. */ + case SCTP_SOCKOPT_BINDX_ADD: + case SCTP_SOCKOPT_CONNECTX: + err = sock_has_perm(current, sk, + (optname == SCTP_SOCKOPT_BINDX_ADD ? + SCTP_SOCKET__BINDX_ADD : + SCTP_SOCKOPT_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; + + case SCTP_SOCKOPT_PEELOFF: + err = sock_has_perm(current, sk, + SCTP_SOCKET__PEELOFF); + if (err) + return err; + break; + + default: + break; + } + } else if (level == IPPROTO_SCTP) { + switch (optname) { + /* Alter heartbeats and address max retransmissions. */ + case SCTP_PEER_ADDR_PARAMS: + /* Alter the thresholds. */ + case SCTP_PEER_ADDR_THLDS: + 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; + + default: + break; + } + } + return 0; +} -- 1.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-sctp" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html