LCOV - code coverage report
Current view: top level - util/sandbox - fd_sandbox.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1 0.0 %
Date: 2026-09-15 04:28:45 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_sandbox_fd_sandbox_h
       2             : #define HEADER_fd_src_util_sandbox_fd_sandbox_h
       3             : 
       4             : #if defined(__linux__)
       5             : 
       6             : #include "../fd_util_base.h"
       7             : 
       8             : #include <linux/filter.h> /* FIXME: HMMMM */
       9             : 
      10             : /* Maximum number of open file descriptors that may be retained when
      11             :    entering the sandbox. */
      12           0 : #define FD_SANDBOX_ALLOWED_FD_CNT_MAX (1024UL)
      13             : 
      14             : FD_PROTOTYPES_BEGIN
      15             : 
      16             : /* fd_sandbox_requires_cap_sys_admin checks if the current environment
      17             :    requires CAP_SYS_ADMIN to fully establish the sandbox.  Ideally this
      18             :    is not required, but certain Linux distributions restrict
      19             :    unprivileged user namespaces which are required.  AppArmor and
      20             :    SELinux potentially also restrict this, but aren't yet checked for
      21             :    here.
      22             : 
      23             :    See https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
      24             :    and https://ubuntu.com/blog/whats-new-in-security-for-ubuntu-24-04-lts
      25             :    for more information on the Ubuntu restrictions.
      26             : 
      27             :    desired_uid and desired_gid should be the UID and GID that will be
      28             :    switched to when entering the sandbox, as whether the namespace can
      29             :    be created unprivileged on Ubuntu depends on the AppArmor
      30             :    configuration of this pair. */
      31             : 
      32             : int
      33             : fd_sandbox_requires_cap_sys_admin( uint desired_uid,
      34             :                                    uint desired_gid );
      35             : 
      36             : /* fd_sandbox_enter takes various steps to enter the process into a
      37             :    fully sandboxed execution environment where it has very limited
      38             :    access to the system.
      39             : 
      40             :    Any errors encountered while sandboxing the process are fatal: the
      41             :    program will print an error and exit rather than continuing
      42             :    unsandboxed.  The sandbox must be entered while the process is single
      43             :    threaded, otherwise it is an error.
      44             : 
      45             :    Calling fd_sandbox_enter may require capabilities,
      46             : 
      47             :      (a) CAP_SETGID, CAP_SETUID are required to switch to the desired
      48             :          UID and GID, although these are only required if the user
      49             :          actually needs to be switched (we are not already the desired
      50             :          IDs).
      51             : 
      52             :      (b) CAP_SYS_ADMIN is required to unshare the user namespace on
      53             :          certain Linux distributions which restrict unprivileged
      54             :          user namespaces for security reasons.
      55             : 
      56             :    The security of the sandbox is more important than the security of
      57             :    code which runs before sandboxing.  It is strongly preferred to do
      58             :    privileged operations before sandboxing, rather than allowing the
      59             :    privileged operations to occur inside the sandbox.
      60             : 
      61             :    The specific list of things that happen when entering the sandbox
      62             :    are:
      63             : 
      64             :      (1) All environment variables (both key and value) are overwritten
      65             :          with zeros, and the environment is cleared.
      66             : 
      67             :      (2) The list of open file descriptors is checked to make sure it
      68             :          exactly matches the list provided in allowed_file_descriptor.
      69             : 
      70             :      (3) The supplementary groups of the process are checked to make
      71             :          sure the only group present is the effective one.
      72             : 
      73             :      (4) The session keyring is replaced with an anonymous keyring.
      74             : 
      75             :      (5) If keep_controlling_terminal is 0, the process is placed into a
      76             :           new process group and session, with no controlling terminal.
      77             :           This means Ctrl+C from a launching terminal will not deliver
      78             :           SIGINT.
      79             : 
      80             :      (6) The effective, real, and saved-set user ID and GID are switched
      81             :          to the desired_uid and desired_gid respectively if they are not
      82             :          already.
      83             : 
      84             :      (7) The CLONE_NEWNS, CLONE_NEWNET, CLONE_NEWCGROUP, CLONE_NEWIPC,
      85             :          and CLONE_NEWUTS namespaces are unshared.
      86             : 
      87             :      (8) The CLONE_NEWUSER namespace is unshared.  The new user
      88             :          namespace is set to deny the setgroups(2) syscall, and then a
      89             :          new UID and GID mapping is established: UID 1 and GID 1 in the
      90             :          namespace map to the desired_uid and desired_gid outside the
      91             :          namespace.
      92             : 
      93             :      (9) The /proc/sys/user/ sysctls are reduced to zero to prevent
      94             :          creation of any new namespaces, except one more user and one
      95             :          more mount namespace are allowed (to be created soon).
      96             : 
      97             :      (10) The CLONE_NEWUSER namespace is unshared again, to enter
      98             :           another nested user namespace.  This is required to prevent
      99             :           modification of the namespace sysctls set above.  The new
     100             :           nested namespace is also set to deny the setgroups(2) syscall
     101             :           and has a UID and GID mapping set up: UID 1 and GID 1 in the
     102             :           namespace map to UID 1 and GID 1 in the parent (and so map to
     103             :           the desired_uid and desired_gid outside the parent).
     104             : 
     105             :      (11) The process dumpable bit is cleared, unless the dumpable
     106             :           argument is nonzero in which case it is set.
     107             : 
     108             :      (12) The root filesystem is pivoted into a new empty directory
     109             :           created in /tmp.  This unmounts all other mounts, including
     110             :           the prior root.  The cwd is set to the new root with chdir(2).
     111             : 
     112             :      (13) Most resource limits are reduced to zero, except RLIMIT_NOFILE
     113             :           which is set to the provided rlimit_file_cnt argument,
     114             :           RLIMIT_ADDRESS_SPACE which is set to the provided
     115             :           rlimit_address_space argument, RLIMIT_DATA which is set to the
     116             :           provided rlimit_data argument, and RLIMIT_NICE which is set to
     117             :           1.  RLIMIT_CPU and RLIMIT_FSIZE are left unlimited.
     118             :           RLIMIT_LOCKS and RLIMIT_RSS are deprecated and left unchanged.
     119             : 
     120             :      (14) All capabilities in the nested user namespace are dropped: the
     121             :           effective, permitted, and inherited sets are all cleared.  The
     122             :           ambient capability set is cleared, and the capability bounding
     123             :           set is zeroed.  The securebits are set to be maximally
     124             :           restrictive: keep caps locked, noroot, etc...
     125             : 
     126             :      (15) The no_new_privs bit is set.
     127             : 
     128             :      (16) An empty landlock restriction is applied to prevent any and
     129             :           all filesystem operations, except the connect(2) syscall might
     130             :           continue to be allowed if allow_connect is nonzero.  Note that
     131             :           this restriction is separate from the seccomp-bpf filter, and
     132             :           the syscall might still be prevented by that.
     133             : 
     134             :      (17) Finally, a seccomp-bpf filter is installed to prevent most
     135             :           syscalls from being made.  The filter is provided in the
     136             :           seccomp_filter argument.
     137             : 
     138             :    The seccomp_filter argument is a list of BPF instructions which will
     139             :    get loaded into the kernel seccomp filter.  This filter should not be
     140             :    constructed by hand, and should be generated from a policy file with
     141             :    the script in contrib/generate_filters.py.
     142             : 
     143             :    Calling fd_sandbox_enter alone is not enough to sandbox a process, as
     144             :    it will not be in a PID namespace.  The caller must ensure the
     145             :    sandboxed process lives in its own PID namespace so it cannot attempt
     146             :    to send signals or ptrace (or be ptraced by) other processes. */
     147             : 
     148             : void
     149             : fd_sandbox_enter( uint                 desired_uid,                  /* User ID to switch the process to inside the sandbox */
     150             :                   uint                 desired_gid,                  /* Group ID to switch the process to inside the sandbox */
     151             :                   int                  keep_host_networking,         /* True to keep the host networking namespace and not unshare it */
     152             :                   int                  allow_connect,                /* True if the connect(2) syscall should be allowed via. landlock */
     153             :                   int                  allow_renameat,               /* True if the renameat(2) syscall should be allowed via. landlock */
     154             :                   int                  keep_controlling_terminal,    /* True to disconnect from the controlling terminal session */
     155             :                   int                  dumpable,                     /* True if the "dumpable" attribute of the process should be kept, otherwise it will be cleared */
     156             :                   ulong                rlimit_file_cnt,              /* Maximum open file value to provide to setrlimit(RLIMIT_NOFILE) */
     157             :                   ulong                rlimit_address_space,         /* Maximum address space size to provide to setrlimit(RLIMIT_AS) */
     158             :                   ulong                rlimit_data,                  /* Maximum address segment size to provide to setrlimit(RLIMIT_DATA) */
     159             :                   ulong                rlimit_nproc,                 /* Maximum threads (including kernel threads) to provide to setrlimit(RLIMIT_NPROC) */
     160             :                   ulong                allowed_file_descriptor_cnt,  /* Number of entries in the allowed_file_descriptor array */
     161             :                   int const *          allowed_file_descriptor,      /* Entries [0, allowed_file_descriptor_cnt) describe the allowed file descriptors */
     162             :                   ulong                seccomp_filter_cnt,           /* Number of entries in the seccomp_filter array */
     163             :                   struct sock_filter * seccomp_filter );             /* Entries [0, seccomp_filter_cnt) describe the instructions of the seccomp-bpf program to apply */
     164             : 
     165             : /* fd_sandbox_switch_uid_gid switches the calling process effective,
     166             :    real, and saved-set user ID and GID are switched to the desired_uid
     167             :    and desired_gid respectively if they are not already.
     168             : 
     169             :    Contrary to the POSIX specification (and the glibc implementation)
     170             :    this function only changes the IDs for the calling thread and not
     171             :    all threads in the process.  It can be called from a multi-threaded
     172             :    process, unlike fd_sandbox_enter.
     173             : 
     174             :    Calling fd_sandbox_switch_uid_gid may require CAP_SETGID and
     175             :    CAP_SETUID to switch to the desired UID and GID, although these are
     176             :    only required if the user actually needs to be switched (we are not
     177             :    already the desired IDs).
     178             : 
     179             :    The Linux kernel clears the dumpable bit on a thread when it
     180             :    switches UID or GID as a security measure, but this function restores
     181             :    the dumpable bit to true. */
     182             : 
     183             : void
     184             : fd_sandbox_switch_uid_gid( uint desired_uid,   /* User ID to switch the process to */
     185             :                            uint desired_gid ); /* Group ID to switch the process to */
     186             : 
     187             : /* fd_sandbox_getpid returns the true PID of the current process as it
     188             :    appears in the root PID namespace of the system.
     189             : 
     190             :    Calling `getpid(2)` from a process inside a PID namespace will
     191             :    return a renumbered PID within that namespace, not the PID seen from
     192             :    most other processes on the system.  For example, if you want to do a
     193             :    `kill -p <pid>` it should be the PID in the root namespace.
     194             : 
     195             :    This function cannot be called from within the sandbox (it will
     196             :    likely SIGSYS due to the seccomp filter) and should be called after
     197             :    entering a PID namespace but before entering the sandbox.
     198             : 
     199             :    This is retrieved by reading the value of /proc/self.  The calling
     200             :    process will be terminated with an error if the file cannot be read
     201             :    or is malformed. */
     202             : 
     203             : ulong
     204             : fd_sandbox_getpid( void );
     205             : 
     206             : /* fd_sandbox_gettid returns the true TID of the current process as it
     207             :    appears in the root PID namespace of the system.
     208             : 
     209             :    Calling `gettid(2)` from a process inside a PID namespace will
     210             :    return a renumbered TID within that namespace, not the TID seen from
     211             :    most other processes on the system.
     212             : 
     213             :    This function cannot be called from within the sandbox (it will
     214             :    likely SIGSYS due to the seccomp filter) and should be called after
     215             :    entering a PID namespace but before entering the sandbox.
     216             : 
     217             :    This is retrieved by reading the value of /proc/thread-self.  The
     218             :    calling process will be terminated with an error if the file cannot
     219             :    be read or is malformed. */
     220             : 
     221             : ulong
     222             : fd_sandbox_gettid( void );
     223             : 
     224             : FD_PROTOTYPES_END
     225             : 
     226             : #endif /* defined(__linux__) */
     227             : 
     228             : #endif /* HEADER_fd_src_util_sandbox_fd_sandbox_h */

Generated by: LCOV version 1.14