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 */