Controlling process communications using SELinux

Linux applications communicate with each other either directly or over a network. But the difference between direct communication and networked communication, from an application programmer’s point of view, is not always that big. Let’s look at the various communication methods that Linux supports and how SELinux aligns with them.

Using shared memory

The least network-like method is the use of shared memory. Applications can share certain parts of the memory with each other and use those shared segments to communicate between two (or more) processes. To govern access to the shared memory, application programmers can use mutual exclusions (mutexes) or semaphores. A semaphore is an atomically incremented or decremented integer (ensuring that two applications do not overwrite each other’s values without knowing about the value change), whereas a mutex can be interpreted as a special semaphore that only takes the values 0 or 1.

On Linux, two implementations exist for shared memory access and control: SysV-style and POSIX-style. We will not dwell on the advantages and disadvantages of each, but rather look at how SELinux governs access to these implementations.

SELinux controls the SysV-style primitives through specific classes: sem for semaphores and shm for shared memory. The semaphores, mutexes, and shared memory segments inherit the context of the first process that creates them.

Administrators who want to control the SysV-style primitives can use the various ipc* commands: ipcs (to list), ipcrm (to remove), and ipcmk (to create).

For instance, let’s first list the resources and then remove the listed shared memory:

# ipcs
...
------ Shared Memory Segments ------
key		shmid	owner		perms	bytes	nattch	status
0x0052e2c1	0	postgres	600	56	6
# ipcrm -m 0

When POSIX-style semaphores, mutexes, and shared memory segments are used, SELinux controls those operations through the file-based access controls. The POSIX-style approach uses regular files in /dev/shm, which is simpler for administrators to control and manage.

Communicating locally through pipes

A second large family of communication methods in operating systems is the use of pipes. As the name implies, pipes are generally one-way communication tunnels, with information flowing from one (or more) senders to one receiver (there are exceptions to this, such as Solaris pipes, which act as bidirectional channels, but those are not supported on Linux). Another name for a pipe is first-in, first-out (FIFO).

We have two types of pipes in Linux: anonymous pipes (also known as unnamed pipes) and named pipes. The difference is that a named pipe uses a file in the regular filesystem as its identification, whereas anonymous pipes are constructed through the applications with no representation in the regular filesystem.

In both cases, SELinux will see the pipes as files of the fifo_file class. Named pipes will have their path associated with the regular filesystem and are created using the mknod or mkfifo commands (or through the mkfifo() function when handled within applications). Anonymous pipes, however, will be shown as part of the pipefs filesystem. This is a pseudo filesystem, not accessible to users, but still represented as 
a filesystem through Linux’s virtual file system (VFS) abstraction.

From an SELinux policy point of view, the FIFO file is the target for which the access controls apply: two domains that both have the correct set of privileges toward the context of the FIFO file will be able to communicate with each other.

Administrators can find out which process is communicating over FIFOs with other processes through tools such as lsof, or by querying the /proc filesystem (as part of the /proc/<pid>/fd listings). The lsof tool supports the -Z option to show the SELinux context of the process, and even supports wildcards:

# lsof -Z *:postfix_*

In this example, lsof displays information about all processes that use a postfix_* label.

Conversing over UNIX domain sockets

With pipes supporting one-way communication only, any conversation between two processes would require two pipes. Also, true client/server-like communication with pipes is challenging to implement. To accomplish the more advanced communication flows, processes will use sockets.

Most administrators are aware that TCP and UDP communication occurs over sockets. Applications can bind to a socket and listen for incoming communications or use the socket to connect to other, remote services. But even on a single Linux system, sockets can be used to facilitate the communication flows. There are two socket types that can be used for process communication: UNIX domain sockets and netlink sockets. Netlink sockets are specific to the Linux operating system and are quite low-level, resembling the ioctl() system call usage. UNIX domain sockets, on the other hand, are higher-level and more directly accessible by administrators, which is why we explain them here in more detail.

We can distinguish between two UNIX domain socket definitions, as with pipes: unnamed sockets and named sockets. And like pipes, the distinction is in the path used to identify a socket. Named sockets are created on the regular filesystem, while unnamed sockets are part of the sockfs pseudo filesystem. Similarly, sockets can be queried through utilities such as lsof or through the /proc/<pid>/fd listings.

There is another distinction regarding UNIX domain sockets though, namely, the communication format that the UNIX domain socket allows. UNIX domain sockets can be created as datagram sockets (data sent to the socket retains its chunk size and format) or streaming sockets (data sent to the socket can be read in different-sized chunks). This has some repercussions for the SELinux policy rules.

For SELinux, communicating over UNIX domain sockets requires both domains to have the proper communication privileges toward the socket file type (open, read, and write), depending on the direction of the communication.

Additionally, the sending (client) domain requires additional privileges toward the receiving (server) domain:

  • The connectto privilege in the unix_stream_socket class in the case of stream sockets
  • The sendto privilege in the unix_dgram_socket class in the case of datagram sockets

As you can see, the privileges depend on the communication type used across the socket.

Understanding netlink sockets

Another socket type that can be used for process communication is netlink. Netlink sockets are sockets that allow user space applications to communicate and interact with kernel processes, and, in special cases (where network management is delegated to a user space process by the Linux kernel), also communicate with another user space application. Unlike the regular UNIX domain sockets, whose target context associates with the owner of that socket, netlink sockets are always local to the SELinux context.

Put differently, when a domain such as sysadm_t wants to manipulate the kernel’s routing information, it will open and communicate with the kernel through a netlink route socket, identified through the netlink_route_socket class:

$ sesearch -s sysadm_t -t sysadm_t -c netlink_route_socket -A
allow sysadm_t domain:netlink_route_socket getattr;
allow sysadm_t sysadm_t:netlink_route_socket { append bind ... };

As applications gain more features, it might be that some of these features are no longer allowed by the current SELinux policy. Administrators will then need to update the SELinux policy to allow the netlink communication.

An overview of supported netlink sockets can be devised from the netlink information on the manual page (man netlink), from which the SELinux classes can easily be derived. For instance, the NETLINK_XFRM socket is supported through the SELinux netlink_xfrm_socket class.

Dealing with TCP, UDP, and SCTP sockets

When we go further up the chain, we look at socket communication over the network. In this case, rather than communicating directly between processes (and thus in Linux terminology between SELinux domains), the flows are from, and to, TCP, UDP, and Stream Control Transmission Protocol (SCTP) sockets.

SELinux will assign types to these ports as well, and these types are then the types to use for socket communication. For SELinux, a client application connecting to the DNS port (TCP port 53, which receives the dns_port_t type in most SELinux policies) uses the name_connect permission within the tcp_socket class toward the port type. The SCTP protocol (with the sctp_socket class) uses the same permission. For UDP services (and thus the udp_socket class), name_connect is not used. Daemon applications use the name_bind privileges to bind themselves to their associated port.

Note:

Support for SCTP has only been recently introduced in SELinux, and not all Linux distributions have updated their policies accordingly. To see whether SCTP support is active, check the value of the /sys/fs/selinux/policy_capabilities/extended_socket_class file. A value of 1 means that the policy has SCTP support included, whereas a value of 0 (or an absent file) means that the system does not yet support SCTP.

Administrators can fine-tune which label to assign to which TCP, UDP, or SCTP port. For this, the semanage port command can be used. For instance, to list the current port definitions, you’d use this command:

# semanage port -l
SELinux Port Type	Proto	Port Number
afs3_callback_port_t	tcp	7001
...
http_port_t		tcp	80, 81, 443, 488, 8008, 8009, ...

In this example, we see that the http_port_t label is assigned to a set of TCP ports. Web server domains that can bind to http_port_t are, as such, allowed to bind to any of the mentioned ports.

To allow a daemon, such as an SSH server, to bind to other (or additional) ports, we need to tell SELinux to map this port to the appropriate label. For instance, to allow the SSH server to bind to port 10122, we first check whether this port already holds a dedicated label. This can be accomplished using the sepolicy command:

$ sepolicy network -p 10122
10122: udp unreserved_port_t 1024-32767
10122: tcp unreserved_port_t 1024-32767
10122: sctp unreserved_port_t 1024-32767

The unreserved_port_t label is not a dedicated one, so we can assign the ssh_port_t label to it:

# semanage port -a -t ssh_port_t -p tcp 10122

Removing a port definition works similarly:

# semanage port -d -t ssh_port_t -p tcp 10122

When a specific port type is already assigned, then the utility will give the following error:

# semanage port -a -t ssh_port_t -p tcp 80
ValueError: Port tcp/80 already defined

If this is the case and another port cannot be used, then no option exists other than to modify the SELinux policy.

Listing connection contexts

Many of the tools in an administrator’s arsenal can display security context information. As with the core utilities, most of these tools use the -Z option for this. For instance, to list the running network-bound services, netstat can be used:

# netstat -naptZ | grep ':80'
tcp  0  0 0.0.0.0:80  0.0.0.0:* LISTEN 17655/nginx: master system_u:system_r:httpd_t:s0

Even lsof displays the context when asked to:

# lsof -i :80 -Z
COMMAND PID   SECURITY-CONTEXT             USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx   17655 system_u:system_r:httpd_t:s0 root 8u IPv4 31230  0t0      *:http (LISTEN)

Another advanced command for querying connections is the ss command. Just calling ss will display all the connections of the current system. When adding -Z, it adds the context information as well.

For instance, the following command queries for listening TCP services:

# ss -ltnZ

More advanced queries can be called as well — consult the ss manual page for more information.

Note:

The use of the -Z option to show SELinux context information or consider SELinux context information in the activity that is requested by the user is a general but not mandatory practice amongst application developers. It is recommended to check the manual page of the application to confirm whether, and how, SELinux is supported by a tool. For instance, to get the ss manual page, run man ss.

All these interactions are still quite primitive in nature, with the last set (which focuses on sockets) being more network-related than the others. Once we look into interaction between systems, we might not have enough control through just the sockets though. To enable more fine-grained control, we’ll look at firewall capabilities and their SECMARK support next.

Related Articles

No Results Found

The page you requested could not be found. Try refining your search, or use the navigation above to locate the post.

Lorem ipsum dolor sit amet consectetur

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

8 + 16 =