In this lesson, you will learn how to securely pass file descriptors (FDs) across namespace boundaries using Unix domain sockets without inadvertently granting ambient authority to the receiving process.
The Mechanism of FD Passing
When a process needs to grant a containerized application access to a specific resource—such as a socket, a pipe, or a specific file—without mapping the entire filesystem into the container, it can pass the file descriptor directly. This is performed using sendmsg() and recvmsg() over a Unix domain socket, utilizing the SCM_RIGHTS control message type.
The security challenge here lies in "ambient authority." When an FD is passed, the receiver gains access to the underlying kernel object (like an open file description) regardless of its current namespace restrictions. If a privileged process blindly passes a sensitive FD, the receiver gains the authority associated with that FD, potentially bypassing namespace isolation, AppArmor profiles, or SELinux constraints.
Secure Handling Patterns
To prevent privilege escalation during this transfer, adhere to these security constraints:
- Restrict the FD scope: Before passing, use
fcntl()withF_SETFDto set theFD_CLOEXECflag. This ensures that if the receiving process execs another binary, the sensitive FD does not leak further. - Verify Namespace Compatibility: Ensure the FD's underlying resource (e.g., a network socket) is compatible with the destination namespace. Passing a host-bound network socket to a process in a new
netnamespace can lead to unpredictable behavior or "leaked" communication paths. - Validate the Peer: Use
getpeercred()on the Unix socket to verify the UID/GID and PID of the receiving process before sending the FD. Never pass descriptors to untrusted processes that may be masquerading as your containerized application.
// Example: Sending an FD over a Unix domain socket
struct msghdr msg = {0};
char cmsg_buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = cmsg_buf;
msg.msg_controllen = sizeof(cmsg_buf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*((int *)CMSG_DATA(cmsg)) = target_fd;
sendmsg(sock, &msg, 0);
By verifying peer credentials and enforcing CLOEXEC flags, you ensure the FD transfer remains an atomic, controlled handover of specific access rather than a wholesale grant of system privileges.
Key Takeaway: Passing file descriptors via SCM_RIGHTS carries the authority of the FD itself, so you must always validate the receiver's identity and apply restrictive flags to prevent accidental privilege leakage.