Landlock Tcp Server Example

💾 tcpserver.c

Warning: The LandlockSocketTypeControl feature for restricting the use of socket(2) is not stable yet (as of October 2024).

The TCP server example runs a TCP server on a port as indicated on the command line, and handles incoming client connections. We make the TCP server restrict itself with Landlock, so that new sockets can not be created while processing client connections.

Analysis

The TCP server is configured through command line arguments, in this example. We consider these flags trustworthy, as they are passed in by a process which already has the same privileges as the TCP server does.

The main risk comes from outside users, who create new connections to the TCP server and talk to it. We want to process their requests in a constrained environment which is sufficient for serving the requests.

Approach

Let’s recall the steps to create a TCP socket server (compare Beej’s Guide to Network Programming):

We interleave the Landlock restriction step right after the bind(2) call, and restrict all further uses of socket(2) and bind(2).

The accept(2) call does technically return new socket file descriptors as well, but that call continues to work even when all uses of socket(2) are forbidden.

We arrive at the following approach:

flags  Parse args socket(2) & bind(2) Enable Landlock Policy listen(2) accept(2) process request 👨‍💻 Attacker incoming connections  client socket 💚 Initialization phase 🛡️ Drop rights 🔴 Process untrusted input

Enabling the Landlock sandbox

The LandlockRulesetEnforcement uses the usual steps and uses the helper function landlock_get_best_ruleset_attr().

It creates a ruleset with all ruleset attributes that Landlock ABI v6 provides, namely:

It does not add any exceptions for creating new TCP sockets, as the server socket was already created before the ruleset enforcement. The only way how new sockets should get created under the policy is through accept(2), which does not count as a socket creation in the sense of LANDLOCK_ACCESS_SOCKET_CREATE.

Remark: The LandlockNetworkPortControl has a known problem – it is still possible to listen(2) on a TCP socket without doing an explicit bind(2) before. In that case, Linux will pick an ephemeral port number and bind to that. This patch set should fix that issue, but until then it is still possible to disassociate one of the existing sockets (see the remark about AF_UNSPEC in connect(2)) and reuse it for an additional connection.