The SELinux socket hook function implementations manage the security fields of socket structures and perform access control for socket operations. This section describes these hooks and their helper functions. The section concludes by describing the optional hook function processing for the extended socket calls.
Security information can be attached to two additional kernel objects,
the kernel socket (struct sock
) and the
open request information block (struct open_request
).
The security fields attached to these objects are used to reliably store
the remote (peer) SID for a connection, and to label server sockets with
the client SID when extended socket calls are used.
The sock_security_struct
is
used to store security information about the peer during connection
establishment when the user socket is not yet allocated for the new connection.
struct sock_security_struct { unsigned long magic; /* magic number for this module */ struct sock *sk; /* back pointer to sock object */ struct list_head list; /* list of sock_security_struct */ security_id_t sid; /* SID of the sock */ security_id_t peer_sid; /* SID of the network peer */ }
Table 31. sock_security_struct
Field | Description |
---|---|
magic | Module id for the SELinux module. |
sk | Back pointer to the associated sock structure. |
list | Pointer used to maintain the list of allocated sock security structures. |
sid | SID of the socket; equal to user space socket SID. |
peer_sid | SID of the peer socket. |
The socket_sock_alloc_security
and
socket_sock_free_security
hooks are used to allocate
and free the
security structure associated with the kernel socket. Security information
is stored in the kernel socket in order to propagate the SID
for a client to the user socket that is ultimately created on the server.
However, because the new server socket is not created until the connection
has been established, the SID for the client is stored in the
kernel socket which is always present.
The kernel object, struct open_request
, has an
LSM security field as well. SELinux uses this field to store security
information about the TCP client during connection establishment.
See Section 17.4 for information on the definition of the
open request security structure and it's use.
The SELinux module
uses the security structure for the inode
associated with the user space socket, so the
inode_alloc_security
,
inode_free_security
and
inode_precondition
functions are also applicable
to sockets. See Section 14.1 for a discussion of
these functions. However, additional socket-specific hook functions
are necessary to initialize and manage the information in these
inode security structures for sockets. These hook functions are
described below.
After a socket
structure has been
successfully created, this hook function is called to update the inode
security field with information that was not previously available.
By default, the inode SID is set to the SID of the creating task.
The socket object class is refined into separate object classes
for the different types of sockets, as determined by the type and
protocol family specified as parameters to the
socket
system call. The security class is
assigned according to Table 32. If the socket
does not match any of the specified types, it defaults to the generic
socket security class. The kernel socket (struct sock
)
associated with the socket will have it's SID set to the user socket SID.
This SID is used to label outgoing packets from
a socket that has no user space socket structure associated with it.
Table 32. Socket Security Classes
Protocol Family | Type | Security Class |
---|---|---|
PF_UNIX | SOCK_STREAM | unix_stream_socket |
PF_UNIX | SOCK_DGRAM | unix_dgram_socket |
PF_INET/PF_INET6 | SOCK_STREAM | tcp_socket |
PF_INET/PF_INET6 | SOCK_DGRAM | udp_socket |
PF_INET/PF_INET6 | SOCK_RAW | rawip_socket |
PF_NETLINK | * | netlink_socket |
PF_PACKET | * | packet_socket |
PF_KEY | * | key_socket |
This hook function is called after a new
socket
has been created for the connection
but prior to calling the protocol family's accept function. In
addition to checking permission (discussed further in Section 17.3), this hook function sets the SID and security
class for the new socket. The new socket always inherits the security
class of the listening socket. By default, the new socket SID
is initialized to the SID of the listening socket. The
new socket initialization must occur in this hook, since traffic can
occur on the socket before the post_accept
hook
is called.
This hook function is called after calling the protocol family's
accept
function. This hook calls the
extsocket_post_accept
function
(see Section 17.4).
A new connection is being requested on a listening socket. This hook allows
the LSM module to maintain security information about the client during the
connection establishment. The only function performed by this hook is to call
the extsocket_tcp_connection_request
hook.
A reply SYN/ACK is being sent for a connection request. This hook allows
the LSM module to label the SYN/ACK packet. For SELinux, the label used
will be the client SID or the listening socket SID, depending on the
use of extended socket functionality. This hook is called after the
skb_set_owner_w
hook, and therefore, will override
any labeling done by that hook. The only function performed by this hook is to
call the extsocket_tcp_synack
hook.
This hook is called when a new TCP kernel socket is created,
typically during the accept
system call. The
security data associated with the listening socket is preserved in
the new kernel socket, and later used to label packets that are sent
from the socket after the user space socket has been detached.
After labeling the new socket, this hook calls the
extsocket_tcp_create_openreq_child
hook.
This helper function checks whether a task has a particular permission to a socket. It first calls the precondition functions for the task and the socket's inode. It then calls the AVC to check the permission.
The socket layer access control hook functions first check a
permission between the current task and the socket using the
socket_has_perm
helper function (or inlining
the logic of this function when the task and/or inode security structures
are needed for additional processing). Some of the
hook functions perform additional processing. The hook functions and
the initial permission that they check are shown in Table 33. Any additional processing for the hook
functions, excluding the optional extended socket call processing, is
then described in the following subsections.
Table 33. Socket Layer Hook Permission Checks
Hook Function | Source | Target | Permission |
---|---|---|---|
selinux_socket_create | Current | NewSocket | create |
selinux_socket_bind | Current | Socket | bind |
selinux_socket_connect | Current | Socket | connect |
selinux_socket_listen | Current | Socket | listen |
selinux_socket_accept | Current | Socket | accept |
selinux_socket_sendmsg | Current | Socket | write |
selinux_socket_recvmsg | Current | Socket | read |
selinux_socket_getsockname | Current | Socket | getattr |
selinux_socket_getpeername | Current | Socket | getattr |
selinux_socket_setsockopt | Current | Socket | setopt |
selinux_socket_getsockopt | Current | Socket | getopt |
selinux_socket_shutdown | Current | Socket | shutdown |
The selinux_socket_bind
hook function
performs an additional name_bind
permission
check between the socket and the SID associated with the port number
for ports that are outside the range used to automatically bind.
Prior to returning, this hook function calls the NSID hook
nsid_sock_sendmsg
to adjust the maximum segment
size (MSS) for the IP packet to account for the IP
options. See Section 20 for a description of the NSID
functions.
This hook function is called by the transport layer network protocols
(e.g. UDP, TCP, raw IP, etc) to control receipt of individual packets
on a socket at a point where the destination socket and the receiving
network device information is available. Unlike the previously
discussed socket hook functions, this hook is passed a pointer to a
kernel socket (sock
) structure rather than a
socket
structure. This hook function must
first dereference the socket
field of the
sock
structure and then dereference the
inode
field of the resulting
socket
structure in order to obtain security
information about the receiving socket. However, security information
is not always available. If the socket is in a TCP
TIME_WAIT
state, then the
sock
structure pointer actually refers to a
tcp_tw_bucket
structure. The
tcp_tw_bucket
structure does not contain a
socket
field, so the
socket
field cannot be accessed in this
case. In other cases, the socket
field can
be accessed but may be NULL
, indicating that the
socket has not yet been associated with an active user socket. In
these cases, the hook function merely returns success. Further study
of these cases is needed to determine whether this behavior is safe.
After obtaining the socket security information, the hook function must also obtain security information for the packet (network buffer). If no receiving network device is set for the packet, then the hook function merely returns success, since this implies that the communication is local and this hook function is not applicable. Otherwise, if the network buffer is still unlabeled, then this hook initializes the network buffer to the default message SID for the receiving network device. Normally, the network buffer is labeled during IP input processing, but an unlabeled network buffer might reach this hook if the kernel was configured without the LSM IP hooks or if SELinux was dynamically inserted into a running kernel with network buffers that had already been processed by the IP layer.
The hook function then checks recvfrom
permission
between the socket and the packet's source socket SID to control the
receipt of the packet on the socket. Depending on the type and state
of the socket and the kind of packet, additional processing may be
performed. The additional processing is described below, and the
additional permission checks are shown in Table 34. The optional extended socket call processing is
described separately in Section 17.4.
If the socket is a TCP socket in the TCP_LISTEN
state (server) and the packet has the SYN
bit set, then
the acceptfrom
permission is checked between the
listening socket SID and the packet's source socket SID (i.e. the
client socket SID). If the socket is a TCP socket in the
TCP_SYN_SENT
state (client) and the packet has the
ACK
or SYN
bits set (without
the RST
bit), then the
connectto
permission is checked between the
client socket SID and packet's source socket SID (i.e. the server
socket SID).
LSM places calls to two hooks,
unix_stream_connect
and
unix_may_send
, within the Unix domain socket code
to provide consistent control over Unix domain socket IPC. These
hooks are placed into the Unix domain socket code in order to have
access to the destination socket, which is not available to the socket
layer hooks. For sockets that use the file namespace, the inode hook
functions could be used to control IPC, but this would not address
sockets that use the abstract namespace. Hence, these two hooks were
added by LSM.
The selinux_socket_unix_stream_connect
hook
function is called for Unix stream connections. It checks the
connectto
permission between the client socket
and the listening socket. The
selinux_socket_unix_may_send
hook function is
called for Unix datagram communications. It checks the
sendto
permission between the sending socket and
the receiving socket. These permission checks are summarized in
Table 35.
The original SELinux kernel patch implemented a set of extended socket calls that could be used to specify and obtain SIDs for sockets, connections, and datagrams. The implementation of these calls for the LSM-based SELinux module is not yet complete and several unresolved issues still remain. The calls and their processing can be completely disabled via a separate kernel configuration option without any affect on the enforcement of the network policy by the kernel. No applications have been modified yet to use these calls, so they can be disabled without harm for now.
This section describes the current state of the extended socket call implementation in the SELinux module. The extended socket call processing is implemented within inline functions defined in the extsocket.h file. These functions are called by the appropriate hook functions. This section begins by describing the fields added to the inode security and open request structures to support the extended socket calls. It then describes each of the inline functions in extsocket.h.
When the extended socket call option is enabled, the
inode_security_struct
structure is extended
to include additional fields related to the extended socket calls.
The additional fields are defined as shown below.
security_id_t msid; /* SID of message on the socket */ security_id_t dsid; /* SID of desired destination socket */ security_id_t peer_sid; /* SID of the peer socket */ security_id_t newconn_sid; /* SID to use for new connections */ int useclient; /* Use client SID for connections */ access_vector_t conn_perm; /* connection permission */
When the extended socket call option is enabled, the
open_request_security_struct
structure is available.
This structure is used to store security information for during connection
requests, before the new socket is created.
struct open_request_security_struct { unsigned long magic; /* magic number for this module */ struct open_request *req; /* back pointer to open request object */ struct list_head list; /* list of open_request_security_struct*/ security_id_t newconn_sid; /* SID of the new connection */ };
Table 37. open_request_security_struct
Field | Description |
---|---|
magic | Module id for the SELinux module. |
sk | Back pointer to the associated open_request structure. |
list | Pointer used to maintain the list of allocated open_request security structures. |
peer_sid | SID of the new connection; either the listening socket SID, or the client SID |
The optional hook function processing for the extended socket calls is implemented in a set of inline functions in extsocket.h. Each function is described below.
Allocate and initialize the
open_request_security_struct
security structure
for the open request kernel object. Called by
selinux_open_request_alloc_security
.
Free the
open_request_security_struct
security structure
for the open request kernel object. Called by
selinux_open_request_free_security
.
This function is called by inode_alloc_security
to initialize the additional fields as necessary. The
socket peer SID field is set to the any_socket
initial SID.
This function is called by selinux_socket_create
and selinux_socket_post_create
to obtain the
SID for the new socket. If the socket_secure
call was used, then the SID given in that call is returned. Otherwise, the
SID of the creating task is returned. If the extended socket option is
disabled, then this function always returns the SID of the creating task.
This function is called by
selinux_socket_connect
. If a particular
destination socket SID was specified via the
connect_secure
call, then additional processing
is performed. If the socket is an INET socket, then an additional
enforce_dest
permission check is performed
between the destination socket SID and the destination node SID. This
check ensures that the destination node is trusted to enforce the
restriction on the destination socket. For all sockets, the
destination socket SID is copied to the
dsid
field of the socket's inode in order to pass
it to the extsocket_skb_set_owner_w
function for
labeling the outgoing packet. The peer SID of the socket
is also set to the destination socket SID.
This function is called by selinux_socket_listen
.
If both a new connection SID and the useclient flag are set, then an
error is returned.
For non-stream sockets, use of the client SID is not supported, so an error
is returned. Also, if the new connection SID is given and is not equal to
the socket SID, and error is returned. Otherwise, the socket's use client
flag is cleared and the new connection SID is set to the socket SID. No
further processing is performed for non-stream sockets.
For stream sockets, if a new connection SID was specified via
listen_secure
, then an additional
newconn
permission check is performed between the
socket SID and the new connection SID.
The new connection SID is then copied into the socket's new connection
SID. Otherwise, the socket new connection SID is set to the SID of the socket.
The use client flag is also copied into the socket's
useclient
field.
This function is called by selinux_socket_accept
to set the connection permission of the new socket
to acceptfrom
for subsequent revalidation.
This function is called by
selinux_socket_post_accept
.
The peer SID of the new connection socket is set to the peer SID
field of the kernel socket. This field was set during
extsocket_tcp_create_openreq_child
for INET sockets,
or during extsocket_unix_stream_connect
for Unix sockets.
If the listening socket's use client flag is set, then the
SID of the new connection socket is changed to the peer SID, i.e. the
client socket SID. The peer SID is also copied into the
out SID array of the current task, so that
it is accessible to the accept_secure
system call
and can be passed back to the application.
This function is called by selinux_socket_sendmsg
.
If the socket is a stream socket, then this function verifies
that the message SID and destination socket SID are valid if they were
specified using the extended socket calls. For stream sockets, the
message SID must equal the sending socket SID, and the destination
socket SID must equal the peer SID. For TCP sockets, this function also
revalidates the connection permission between the socket and its peer.
For client sockets, the connection permission and the peer SID are set
during connection establishment by extsocket_sock_rcv_skb
.
For server sockets, the connection permission is set by
extsocket_accept
and the peer SID is set by
extsocket_post_accept
.
If the socket is a non-stream socket and a message SID was
specified, then send_msg
permission is checked
between the socket SID and the message SID. If the socket is a
non-stream INET socket (e.g. UDP, raw IP), then this function also checks
sendto
permission between the socket and the
destination socket SID. By default, the destination socket SID is set
to the peer SID for the socket, which defaults to the
any_socket
initial SID unless specified by a
prior connect_secure
call. If a particular
destination socket SID was specified via
send*_secure
, then the
enforce_dest
permission is checked between the
destination socket SID and the destination node SID.
For all sockets, the destination socket SID, if specified, is copied
to the dsid
field of the socket's inode
security structure in order to pass it to the
extsocket_skb_set_owner_w
function for labeling
the outgoing packet. For non-stream sockets, the message SID is
similarly copied to the msid
field if it
was specified.
[XXX Need to bind the (msid, dsid) pair to the particular message in
some manner so that extsocket_skb_set_owner_w
can
ensure that it is only applied to the corresponding network buffers.
Possibly maintain a list of (message identifier, msid, dsid) triples
on the socket in the extsocket_sendmsg
function
that can be consumed by
extsocket_skb_set_owner_w
, but not clear how to
identify the message uniquely and consistently across both functions.
Possibly bind security data to struct msghdr
via a security field or control data, but a security field would break
application compatibility (msghdr is an exported structure) and
control data may interfere with application-specified control data.
The original SELinux kernel patch required invasive changes to
propagate the SIDs down to the skb allocation.]
This function is called by selinux_socket_recvmsg
.
For stream sockets, this function copies the peer SID into both
elements of the out SID array of the
current task's security structure so that the
recv*_secure
calls can return this SID as the
source socket SID and message SID to the application. For datagram
sockets, the SIDs are copied from the individual datagram by the
extsocket_skb_recv_datagram
function.
This function is called by selinux_socket_getsockname
.
This function copies the socket SID into the
out SID array of the current task's
security structure so that the socket SID can be returned via the
getsockname_secure
extended system call.
This function is called by
selinux_socket_getpeername
. This function copies
the peer socket SID into the out SID array
of the current task's security structure so that the peer socket SID
can be returned via the getpeername_secure
extended system call. For client sockets, the peer SID are set during
connection establishment by
extsocket_sock_rcv_skb
. For server sockets, the
peer SID is set by extsocket_post_accept
.
This function is called by selinux_sock_rcv_skb
.
If the socket is a TCP socket in the TCP_LISTEN
state (server socket) and the packet has the SYN
bit set,
then a connection is being requested, and several checks are performed.
If the listening socket was set to use the client socket SID for new
connection sockets (via a listen_secure
call on
the server), then the newconn
permission is
checked between the listening socket SID and the packet's source
socket SID (i.e. the client socket SID) to ensure that the listening
socket is allowed to create a new connection socket with the same SID
as the client socket.
At this point, the new connection SID will be either the client SID
(when the listening socket was set to use client) or the listening
socket SID. Next, the acceptfrom
permission is checked between the new connection SID and the SID of the
packet.
If the packet's destination socket SID
is set (due to a connect_secure
call on the
client) and this SID does not match the listening socket's new connection SID,
the connection is refused.
(XXX The listening socket's peer SID is set to the
packet's source socket SID, but this will be overwritten by subsequent
connections. This is unreliable.)
If the socket is a TCP socket in the TCP_SYN_SENT
state (client socket), and the packet has the ACK
or
SYN
bits set (without the
RST
bit), then the client is receiving connection
acknowledgment from the server. Several checks are made and the
peer SID is saved. If the socket's peer SID is set (via a
connect_secure
call) and this SID does not match
the source socket SID of the packet, then the connection is reset.
This check parallels the server-side check for the same condition.
The client socket's peer SID is set to the source socket
SID of the packet, and the connection permission is set to
connectto
for subsequent revalidation.
If the TCP socket is in the TCP_ESTABLISHED
state,
then the connection permission (either acceptfrom
or connectto
) is revalidated so that policy changes
can be reflected by the permission checks.
For non-stream sockets, if the packet's destination socket
SID is set (via send*_secure
) and it does not
match the receiving socket's SID, then the packet is rejected.
Likewise, if the receiving socket's peer SID has been set (via
connect_secure
), and it does not match the source
socket SID of the packet, then the packet is rejected.
This hook is called by selinux_tcp_connection_request
.
The purpose of this hook is to set the new connection SID
for the open request associated
with the requested connection. If the listening socket is set to use the
client SID on new connections, the new connection SID
is set to the SID of the packet that initiated the connection request.
In this manner the SID of the new server socket will be reliably set with
the client SID when multiple connections are being established on a
busy server socket. Otherwise, new connection SID is set to the listening
socket's new connection SID.
This hook is called by selinux_tcp_synack
to label the outgoing network packet for the SYN/ACK with the
new connection SID taken from the open request structure.
This SID was set by the extsocket_tcp_connection_request
hook.
This function is called by
selinux_tcp_create_openreq_child
.
When the listening socket is set to use the client SID for new connections,
this hook sets the SID of the newly created kernel socket to the SID from the
open request structure. This SID is used to label outgoing packets from
a socket that has no user space socket structure associated with it
(as can happen during the socket shutdown operation).
The hook also copies the SID of the network packet that established the
connection into the kernel socket peer SID field. This peer SID is used
by extsocket_post_accept
to reliably set the peer SID
of the user socket structure.
This function is called by
selinux_unix_stream_connect
. If the listening
socket was set to use the client socket SID for new connection sockets
(via a listen_secure
call on the server), then
the newconn
permission is checked between the
listening socket SID and the client socket SID to ensure that the
listening socket is allowed to create a new connection socket with the
same SID as the client socket. If the new connection SID does not match the
listening socket SID, then the connectto
permission is rechecked based on the new connection socket SID
rather than the listening socket SID. If the destination socket SID is set
(due to a connect_secure
call on the client) and
this SID does not match the new connection socket SID, then access is
denied. The peer SID of the kernel socket associated with the new connection
is set to the sending socket SID. This peer SID can be used by
extsocket_post_accept
to reliably set the peer SID
of the user socket structure.
The connection permission is
set for subsequent revalidation [XXX Revalidation for Unix stream
traffic is not yet implemented].
This function is called by selinux_unix_may_send
.
If the receiving socket's peer SID has been set (via
connect_secure
), and it does not match the
sending socket SID, then access is denied.
Likewise, if the destination socket SID is set (via
send*_secure
) and it does not match the receiving
socket's SID, then access is denied.
This function is called by selinux_skb_set_owner_w
.
If the message SID (non-stream only) or destination socket SID are set
for the socket, then these SIDs are copied into the network buffer and
then cleared from the socket. These SID fields in the socket's inode
security structure are set during
selinux_socket_sendmsg
. [XXX This is unreliable,
see Section 17.4.3.9.]
This function is called by selinux_skb_recv_datagram
.
This function copies the SIDs from a network buffer into the
out SID array of the task security
structure when a datagram is received by a task. This enables
the extended socket calls to return these SIDs to applications.