selinux: Add SCTP support

The SELinux SCTP implementation is explained in:
Documentation/security/SELinux-sctp.rst

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
Richard Haines 2018-02-13 20:57:18 +00:00 committed by Paul Moore
parent 2277c7cd75
commit d452930fd3
6 changed files with 580 additions and 47 deletions

View File

@ -0,0 +1,157 @@
SCTP SELinux Support
=====================
Security Hooks
===============
``Documentation/security/LSM-sctp.rst`` describes the following SCTP security
hooks with the SELinux specifics expanded below::
security_sctp_assoc_request()
security_sctp_bind_connect()
security_sctp_sk_clone()
security_inet_conn_established()
security_sctp_assoc_request()
-----------------------------
Passes the ``@ep`` and ``@chunk->skb`` of the association INIT packet to the
security module. Returns 0 on success, error on failure.
::
@ep - pointer to sctp endpoint structure.
@skb - pointer to skbuff of association packet.
The security module performs the following operations:
IF this is the first association on ``@ep->base.sk``, then set the peer
sid to that in ``@skb``. This will ensure there is only one peer sid
assigned to ``@ep->base.sk`` that may support multiple associations.
ELSE validate the ``@ep->base.sk peer_sid`` against the ``@skb peer sid``
to determine whether the association should be allowed or denied.
Set the sctp ``@ep sid`` to socket's sid (from ``ep->base.sk``) with
MLS portion taken from ``@skb peer sid``. This will be used by SCTP
TCP style sockets and peeled off connections as they cause a new socket
to be generated.
If IP security options are configured (CIPSO/CALIPSO), then the ip
options are set on the socket.
security_sctp_bind_connect()
-----------------------------
Checks permissions required for ipv4/ipv6 addresses based on the ``@optname``
as follows::
------------------------------------------------------------------
| BIND Permission Checks |
| @optname | @address contains |
|----------------------------|-----------------------------------|
| SCTP_SOCKOPT_BINDX_ADD | One or more ipv4 / ipv6 addresses |
| SCTP_PRIMARY_ADDR | Single ipv4 or ipv6 address |
| SCTP_SET_PEER_PRIMARY_ADDR | Single ipv4 or ipv6 address |
------------------------------------------------------------------
------------------------------------------------------------------
| CONNECT Permission Checks |
| @optname | @address contains |
|----------------------------|-----------------------------------|
| SCTP_SOCKOPT_CONNECTX | One or more ipv4 / ipv6 addresses |
| SCTP_PARAM_ADD_IP | One or more ipv4 / ipv6 addresses |
| SCTP_SENDMSG_CONNECT | Single ipv4 or ipv6 address |
| SCTP_PARAM_SET_PRIMARY | Single ipv4 or ipv6 address |
------------------------------------------------------------------
``Documentation/security/LSM-sctp.rst`` gives a summary of the ``@optname``
entries and also describes ASCONF chunk processing when Dynamic Address
Reconfiguration is enabled.
security_sctp_sk_clone()
-------------------------
Called whenever a new socket is created by **accept**\(2) (i.e. a TCP style
socket) or when a socket is 'peeled off' e.g userspace calls
**sctp_peeloff**\(3). ``security_sctp_sk_clone()`` will set the new
sockets sid and peer sid to that contained in the ``@ep sid`` and
``@ep peer sid`` respectively.
::
@ep - pointer to current sctp endpoint structure.
@sk - pointer to current sock structure.
@sk - pointer to new sock structure.
security_inet_conn_established()
---------------------------------
Called when a COOKIE ACK is received where it sets the connection's peer sid
to that in ``@skb``::
@sk - pointer to sock structure.
@skb - pointer to skbuff of the COOKIE ACK packet.
Policy Statements
==================
The following class and permissions to support SCTP are available within the
kernel::
class sctp_socket inherits socket { node_bind }
whenever the following policy capability is enabled::
policycap extended_socket_class;
SELinux SCTP support adds the ``name_connect`` permission for connecting
to a specific port type and the ``association`` permission that is explained
in the section below.
If userspace tools have been updated, SCTP will support the ``portcon``
statement as shown in the following example::
portcon sctp 1024-1036 system_u:object_r:sctp_ports_t:s0
SCTP Peer Labeling
===================
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, any new associations will have the ``association``
permission validated by checking the socket peer sid against the received
packets peer sid to determine whether the association should be allowed or
denied.
NOTES:
1) If peer labeling is not enabled, then the peer context will always be
``SECINITSID_UNLABELED`` (``unlabeled_t`` in Reference Policy).
2) As SCTP can support more than one transport address per endpoint
(multi-homing) on a single socket, it is possible to configure policy
and NetLabel to provide different peer labels for each of these. As the
socket peer label is determined by the first associations transport
address, it is recommended that all peer labels are consistent.
3) **getpeercon**\(3) may be used by userspace to retrieve the sockets peer
context.
4) While not SCTP specific, be aware when using NetLabel that if a label
is assigned to a specific interface, and that interface 'goes down',
then the NetLabel service will remove the entry. Therefore ensure that
the network startup scripts call **netlabelctl**\(8) to set the required
label (see **netlabel-config**\(8) helper script for details).
5) The NetLabel SCTP peer labeling rules apply as discussed in the following
set of posts tagged "netlabel" at: http://www.paul-moore.com/blog/t.
6) CIPSO is only supported for IPv4 addressing: ``socket(AF_INET, ...)``
CALIPSO is only supported for IPv6 addressing: ``socket(AF_INET6, ...)``
Note the following when testing CIPSO/CALIPSO:
a) CIPSO will send an ICMP packet if an SCTP packet cannot be
delivered because of an invalid label.
b) CALIPSO does not send an ICMP packet, just silently discards it.
7) IPSEC is not supported as RFC 3554 - sctp/ipsec support has not been
implemented in userspace (**racoon**\(8) or **ipsec_pluto**\(8)),
although the kernel supports SCTP/IPSEC.

View File

@ -67,6 +67,8 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/dccp.h>
#include <linux/sctp.h>
#include <net/sctp/structs.h>
#include <linux/quota.h>
#include <linux/un.h> /* for Unix socket types */
#include <net/af_unix.h> /* for Unix socket types */
@ -4134,6 +4136,23 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb,
break;
}
#if IS_ENABLED(CONFIG_IP_SCTP)
case IPPROTO_SCTP: {
struct sctphdr _sctph, *sh;
if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
if (sh == NULL)
break;
ad->u.net->sport = sh->source;
ad->u.net->dport = sh->dest;
break;
}
#endif
default:
break;
}
@ -4207,6 +4226,19 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
break;
}
#if IS_ENABLED(CONFIG_IP_SCTP)
case IPPROTO_SCTP: {
struct sctphdr _sctph, *sh;
sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
if (sh == NULL)
break;
ad->u.net->sport = sh->source;
ad->u.net->dport = sh->dest;
break;
}
#endif
/* includes fragments */
default:
break;
@ -4396,6 +4428,10 @@ static int selinux_socket_post_create(struct socket *sock, int family,
sksec = sock->sk->sk_security;
sksec->sclass = sclass;
sksec->sid = sid;
/* Allows detection of the first association on this socket */
if (sksec->sclass == SECCLASS_SCTP_SOCKET)
sksec->sctp_assoc_state = SCTP_ASSOC_UNSET;
err = selinux_netlbl_socket_post_create(sock->sk, family);
}
@ -4416,11 +4452,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (err)
goto out;
/*
* 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.
*/
/* If PF_INET or PF_INET6, check name_bind permission for the port. */
family = sk->sk_family;
if (family == PF_INET || family == PF_INET6) {
char *addrp;
@ -4432,7 +4464,13 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
unsigned short snum;
u32 sid, node_perm;
if (family == PF_INET) {
/*
* sctp_bindx(3) calls via selinux_sctp_bind_connect()
* that validates multiple binding addresses. Because of this
* need to check address->sa_family as it is possible to have
* sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
*/
if (address->sa_family == AF_INET) {
if (addrlen < sizeof(struct sockaddr_in)) {
err = -EINVAL;
goto out;
@ -4486,6 +4524,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;
@ -4500,7 +4542,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
ad.u.net->sport = htons(snum);
ad.u.net->family = family;
if (family == PF_INET)
if (address->sa_family == AF_INET)
ad.u.net->v4info.saddr = addr4->sin_addr.s_addr;
else
ad.u.net->v6info.saddr = addr6->sin6_addr;
@ -4514,7 +4556,11 @@ out:
return err;
}
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
/* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
* and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.txt
*/
static int selinux_socket_connect_helper(struct socket *sock,
struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
struct sk_security_struct *sksec = sk->sk_security;
@ -4525,10 +4571,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;
@ -4536,7 +4584,12 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
unsigned short snum;
u32 sid, perm;
if (sk->sk_family == PF_INET) {
/* sctp_connectx(3) calls via selinux_sctp_bind_connect()
* that validates multiple connect addresses. Because of this
* need to check address->sa_family as it is possible to have
* sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
*/
if (address->sa_family == AF_INET) {
addr4 = (struct sockaddr_in *)address;
if (addrlen < sizeof(struct sockaddr_in))
return -EINVAL;
@ -4550,10 +4603,19 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
err = sel_netport_sid(sk->sk_protocol, snum, &sid);
if (err)
goto out;
return err;
perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
switch (sksec->sclass) {
case SECCLASS_TCP_SOCKET:
perm = TCP_SOCKET__NAME_CONNECT;
break;
case SECCLASS_DCCP_SOCKET:
perm = DCCP_SOCKET__NAME_CONNECT;
break;
case SECCLASS_SCTP_SOCKET:
perm = SCTP_SOCKET__NAME_CONNECT;
break;
}
ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
@ -4561,13 +4623,24 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
ad.u.net->family = sk->sk_family;
err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad);
if (err)
goto out;
return err;
}
err = selinux_netlbl_socket_connect(sk, address);
return 0;
}
out:
return err;
/* Supports connect(2), see comments in selinux_socket_connect_helper() */
static int selinux_socket_connect(struct socket *sock,
struct sockaddr *address, int addrlen)
{
int err;
struct sock *sk = sock->sk;
err = selinux_socket_connect_helper(sock, address, addrlen);
if (err)
return err;
return selinux_netlbl_socket_connect(sk, address);
}
static int selinux_socket_listen(struct socket *sock, int backlog)
@ -4830,7 +4903,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;
@ -4943,6 +5017,171 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
sksec->sclass = isec->sclass;
}
/* Called whenever SCTP receives an INIT chunk. This happens when an incoming
* connect(2), sctp_connectx(3) or sctp_sendmsg(3) (with no association
* already present).
*/
static int selinux_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
struct sk_security_struct *sksec = ep->base.sk->sk_security;
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
u8 peerlbl_active;
u32 peer_sid = SECINITSID_UNLABELED;
u32 conn_sid;
int err = 0;
if (!selinux_policycap_extsockclass)
return 0;
peerlbl_active = selinux_peerlbl_enabled();
if (peerlbl_active) {
/* This will return peer_sid = SECSID_NULL if there are
* no peer labels, see security_net_peersid_resolve().
*/
err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family,
&peer_sid);
if (err)
return err;
if (peer_sid == SECSID_NULL)
peer_sid = SECINITSID_UNLABELED;
}
if (sksec->sctp_assoc_state == SCTP_ASSOC_UNSET) {
sksec->sctp_assoc_state = SCTP_ASSOC_SET;
/* Here as first association on socket. As the peer SID
* was allowed by peer recv (and the netif/node checks),
* then it is approved by policy and used as the primary
* peer SID for getpeercon(3).
*/
sksec->peer_sid = peer_sid;
} else if (sksec->peer_sid != peer_sid) {
/* Other association peer SIDs are checked to enforce
* consistency among the peer SIDs.
*/
ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->sk = ep->base.sk;
err = avc_has_perm(sksec->peer_sid, peer_sid, sksec->sclass,
SCTP_SOCKET__ASSOCIATION, &ad);
if (err)
return err;
}
/* Compute the MLS component for the connection and store
* the information in ep. This will be used by SCTP TCP type
* sockets and peeled off connections as they cause a new
* socket to be generated. selinux_sctp_sk_clone() will then
* plug this into the new socket.
*/
err = selinux_conn_sid(sksec->sid, peer_sid, &conn_sid);
if (err)
return err;
ep->secid = conn_sid;
ep->peer_secid = peer_sid;
/* Set any NetLabel labels including CIPSO/CALIPSO options. */
return selinux_netlbl_sctp_assoc_request(ep, skb);
}
/* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting
* based on their @optname.
*/
static int selinux_sctp_bind_connect(struct sock *sk, int optname,
struct sockaddr *address,
int addrlen)
{
int len, err = 0, walk_size = 0;
void *addr_buf;
struct sockaddr *addr;
struct socket *sock;
if (!selinux_policycap_extsockclass)
return 0;
/* Process one or more addresses that may be IPv4 or IPv6 */
sock = sk->sk_socket;
addr_buf = address;
while (walk_size < addrlen) {
addr = addr_buf;
switch (addr->sa_family) {
case AF_INET:
len = sizeof(struct sockaddr_in);
break;
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
default:
return -EAFNOSUPPORT;
}
err = -EINVAL;
switch (optname) {
/* Bind checks */
case SCTP_PRIMARY_ADDR:
case SCTP_SET_PEER_PRIMARY_ADDR:
case SCTP_SOCKOPT_BINDX_ADD:
err = selinux_socket_bind(sock, addr, len);
break;
/* Connect checks */
case SCTP_SOCKOPT_CONNECTX:
case SCTP_PARAM_SET_PRIMARY:
case SCTP_PARAM_ADD_IP:
case SCTP_SENDMSG_CONNECT:
err = selinux_socket_connect_helper(sock, addr, len);
if (err)
return err;
/* As selinux_sctp_bind_connect() is called by the
* SCTP protocol layer, the socket is already locked,
* therefore selinux_netlbl_socket_connect_locked() is
* is called here. The situations handled are:
* sctp_connectx(3), sctp_sendmsg(3), sendmsg(2),
* whenever a new IP address is added or when a new
* primary address is selected.
* Note that an SCTP connect(2) call happens before
* the SCTP protocol layer and is handled via
* selinux_socket_connect().
*/
err = selinux_netlbl_socket_connect_locked(sk, addr);
break;
}
if (err)
return err;
addr_buf += len;
walk_size += len;
}
return 0;
}
/* Called whenever a new socket is created by accept(2) or sctp_peeloff(3). */
static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
struct sock *newsk)
{
struct sk_security_struct *sksec = sk->sk_security;
struct sk_security_struct *newsksec = newsk->sk_security;
/* If policy does not support SECCLASS_SCTP_SOCKET then call
* the non-sctp clone version.
*/
if (!selinux_policycap_extsockclass)
return selinux_sk_clone_security(sk, newsk);
newsksec->sid = ep->secid;
newsksec->peer_sid = ep->peer_secid;
newsksec->sclass = sksec->sclass;
selinux_netlbl_sctp_sk_clone(sk, newsk);
}
static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
@ -6563,6 +6802,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security),
LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid),
LSM_HOOK_INIT(sock_graft, selinux_sock_graft),
LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request),
LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone),
LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect),
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),

View File

@ -176,7 +176,7 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_CAP2_PERMS, NULL } },
{ "sctp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },
"node_bind", "name_connect", "association", NULL } },
{ "icmp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },

View File

@ -32,6 +32,7 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/request_sock.h>
#include <net/sctp/structs.h>
#include "avc.h"
#include "objsec.h"
@ -52,9 +53,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
u16 family,
u32 sid);
int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb);
int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family);
void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family);
void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk);
int selinux_netlbl_socket_post_create(struct sock *sk, u16 family);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
@ -64,6 +67,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
int selinux_netlbl_socket_connect_locked(struct sock *sk,
struct sockaddr *addr);
#else
static inline void selinux_netlbl_cache_invalidate(void)
@ -113,6 +118,11 @@ static inline int selinux_netlbl_conn_setsid(struct sock *sk,
return 0;
}
static inline int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
return 0;
}
static inline int selinux_netlbl_inet_conn_request(struct request_sock *req,
u16 family)
{
@ -122,6 +132,10 @@ static inline void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
{
return;
}
static inline void selinux_netlbl_sctp_sk_clone(struct sock *sk, sock *newsk)
{
return;
}
static inline int selinux_netlbl_socket_post_create(struct sock *sk,
u16 family)
{
@ -145,6 +159,11 @@ static inline int selinux_netlbl_socket_connect(struct sock *sk,
{
return 0;
}
static inline int selinux_netlbl_socket_connect_locked(struct sock *sk,
struct sockaddr *addr)
{
return 0;
}
#endif /* CONFIG_NETLABEL */
#endif

View File

@ -130,6 +130,10 @@ struct sk_security_struct {
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
u16 sclass; /* sock security class */
enum { /* SCTP association state */
SCTP_ASSOC_UNSET = 0,
SCTP_ASSOC_SET,
} sctp_assoc_state;
};
struct tun_security_struct {

View File

@ -249,6 +249,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
sk = skb_to_full_sk(skb);
if (sk != NULL) {
struct sk_security_struct *sksec = sk->sk_security;
if (sksec->nlbl_state != NLBL_REQSKB)
return 0;
secattr = selinux_netlbl_sock_getattr(sk, sid);
@ -269,6 +270,61 @@ skbuff_setsid_return:
return rc;
}
/**
* selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.
* @ep: incoming association endpoint.
* @skb: the packet.
*
* Description:
* A new incoming connection is represented by @ep, ......
* Returns zero on success, negative values on failure.
*
*/
int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
int rc;
struct netlbl_lsm_secattr secattr;
struct sk_security_struct *sksec = ep->base.sk->sk_security;
struct sockaddr *addr;
struct sockaddr_in addr4;
#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 addr6;
#endif
if (ep->base.sk->sk_family != PF_INET &&
ep->base.sk->sk_family != PF_INET6)
return 0;
netlbl_secattr_init(&secattr);
rc = security_netlbl_sid_to_secattr(ep->secid, &secattr);
if (rc != 0)
goto assoc_request_return;
/* Move skb hdr address info to a struct sockaddr and then call
* netlbl_conn_setattr().
*/
if (ip_hdr(skb)->version == 4) {
addr4.sin_family = AF_INET;
addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;
addr = (struct sockaddr *)&addr4;
#if IS_ENABLED(CONFIG_IPV6)
} else {
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = ipv6_hdr(skb)->saddr;
addr = (struct sockaddr *)&addr6;
#endif
}
rc = netlbl_conn_setattr(ep->base.sk, addr, &secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_LABELED;
assoc_request_return:
netlbl_secattr_destroy(&secattr);
return rc;
}
/**
* selinux_netlbl_inet_conn_request - Label an incoming stream connection
* @req: incoming connection request socket
@ -318,6 +374,22 @@ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
sksec->nlbl_state = NLBL_UNSET;
}
/**
* selinux_netlbl_sctp_sk_clone - Copy state to the newly created sock
* @sk: current sock
* @newsk: the new sock
*
* Description:
* Called whenever a new socket is created by accept(2) or sctp_peeloff(3).
*/
void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
{
struct sk_security_struct *sksec = sk->sk_security;
struct sk_security_struct *newsksec = newsk->sk_security;
newsksec->nlbl_state = sksec->nlbl_state;
}
/**
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
* @sock: the socket to label
@ -468,6 +540,69 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
return rc;
}
/**
* selinux_netlbl_socket_connect_helper - Help label a client-side socket on
* connect
* @sk: the socket to label
* @addr: the destination address
*
* Description:
* Attempt to label a connected socket with NetLabel using the given address.
* Returns zero values on success, negative values on failure.
*
*/
static int selinux_netlbl_socket_connect_helper(struct sock *sk,
struct sockaddr *addr)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr *secattr;
/* connected sockets are allowed to disconnect when the address family
* is set to AF_UNSPEC, if that is what is happening we want to reset
* the socket */
if (addr->sa_family == AF_UNSPEC) {
netlbl_sock_delattr(sk);
sksec->nlbl_state = NLBL_REQSKB;
rc = 0;
return rc;
}
secattr = selinux_netlbl_sock_genattr(sk);
if (secattr == NULL) {
rc = -ENOMEM;
return rc;
}
rc = netlbl_conn_setattr(sk, addr, secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_CONNLABELED;
return rc;
}
/**
* selinux_netlbl_socket_connect_locked - Label a client-side socket on
* connect
* @sk: the socket to label
* @addr: the destination address
*
* Description:
* Attempt to label a connected socket that already has the socket locked
* with NetLabel using the given address.
* Returns zero values on success, negative values on failure.
*
*/
int selinux_netlbl_socket_connect_locked(struct sock *sk,
struct sockaddr *addr)
{
struct sk_security_struct *sksec = sk->sk_security;
if (sksec->nlbl_state != NLBL_REQSKB &&
sksec->nlbl_state != NLBL_CONNLABELED)
return 0;
return selinux_netlbl_socket_connect_helper(sk, addr);
}
/**
* selinux_netlbl_socket_connect - Label a client-side socket on connect
* @sk: the socket to label
@ -481,34 +616,10 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr *secattr;
if (sksec->nlbl_state != NLBL_REQSKB &&
sksec->nlbl_state != NLBL_CONNLABELED)
return 0;
lock_sock(sk);
/* connected sockets are allowed to disconnect when the address family
* is set to AF_UNSPEC, if that is what is happening we want to reset
* the socket */
if (addr->sa_family == AF_UNSPEC) {
netlbl_sock_delattr(sk);
sksec->nlbl_state = NLBL_REQSKB;
rc = 0;
goto socket_connect_return;
}
secattr = selinux_netlbl_sock_genattr(sk);
if (secattr == NULL) {
rc = -ENOMEM;
goto socket_connect_return;
}
rc = netlbl_conn_setattr(sk, addr, secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_CONNLABELED;
socket_connect_return:
rc = selinux_netlbl_socket_connect_locked(sk, addr);
release_sock(sk);
return rc;
}