Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

samples/landlock: Add support for abstract UNIX socket scoping

The sandboxer can receive the character "a" as input from the
environment variable LL_SCOPE to restrict sandboxed processes from
connecting to an abstract UNIX socket created by a process outside of
the sandbox.

Example
=======

Create an abstract UNIX socket to listen with socat(1):
socat abstract-listen:mysocket -

Create a sandboxed shell and pass the character "a" to LL_SCOPED:
LL_FS_RO=/ LL_FS_RW=. LL_SCOPED="a" ./sandboxer /bin/bash

Note that any other form of input (e.g. "a:a", "aa", etc) is not
acceptable.

If the sandboxed process tries to connect to the listening socket, the
connection will fail:
socat - abstract-connect:mysocket

Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com>
Link: https://lore.kernel.org/r/d8af908f00b77415caa3eb0f4de631c3794e4909.1725494372.git.fahimitahera@gmail.com
[mic: Improve commit message, simplify check_ruleset_scope() with
inverted error code and only one scoped change, always unset environment
variable]
Signed-off-by: Mickaël Salaün <mic@digikod.net>

authored by

Tahera Fahimi and committed by
Mickaël Salaün
369b48b4 644a7285

+60 -4
+60 -4
samples/landlock/sandboxer.c
··· 14 14 #include <fcntl.h> 15 15 #include <linux/landlock.h> 16 16 #include <linux/prctl.h> 17 + #include <linux/socket.h> 17 18 #include <stddef.h> 18 19 #include <stdio.h> 19 20 #include <stdlib.h> ··· 23 22 #include <sys/stat.h> 24 23 #include <sys/syscall.h> 25 24 #include <unistd.h> 25 + #include <stdbool.h> 26 26 27 27 #ifndef landlock_create_ruleset 28 28 static inline int ··· 57 55 #define ENV_FS_RW_NAME "LL_FS_RW" 58 56 #define ENV_TCP_BIND_NAME "LL_TCP_BIND" 59 57 #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" 58 + #define ENV_SCOPED_NAME "LL_SCOPED" 60 59 #define ENV_DELIMITER ":" 61 60 62 61 static int parse_path(char *env_path, const char ***const path_list) ··· 187 184 return ret; 188 185 } 189 186 187 + /* Returns true on error, false otherwise. */ 188 + static bool check_ruleset_scope(const char *const env_var, 189 + struct landlock_ruleset_attr *ruleset_attr) 190 + { 191 + char *env_type_scope, *env_type_scope_next, *ipc_scoping_name; 192 + bool error = false; 193 + bool abstract_scoping = false; 194 + 195 + /* Scoping is not supported by Landlock ABI */ 196 + if (!(ruleset_attr->scoped & LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET)) 197 + goto out_unset; 198 + 199 + env_type_scope = getenv(env_var); 200 + /* Scoping is not supported by the user */ 201 + if (!env_type_scope || strcmp("", env_type_scope) == 0) 202 + goto out_unset; 203 + 204 + env_type_scope = strdup(env_type_scope); 205 + env_type_scope_next = env_type_scope; 206 + while ((ipc_scoping_name = 207 + strsep(&env_type_scope_next, ENV_DELIMITER))) { 208 + if (strcmp("a", ipc_scoping_name) == 0 && !abstract_scoping) { 209 + abstract_scoping = true; 210 + } else { 211 + fprintf(stderr, "Unknown or duplicate scope \"%s\"\n", 212 + ipc_scoping_name); 213 + error = true; 214 + goto out_free_name; 215 + } 216 + } 217 + 218 + out_free_name: 219 + free(env_type_scope); 220 + 221 + out_unset: 222 + if (!abstract_scoping) 223 + ruleset_attr->scoped &= ~LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET; 224 + 225 + unsetenv(env_var); 226 + return error; 227 + } 228 + 190 229 /* clang-format off */ 191 230 192 231 #define ACCESS_FS_ROUGHLY_READ ( \ ··· 253 208 254 209 /* clang-format on */ 255 210 256 - #define LANDLOCK_ABI_LAST 5 211 + #define LANDLOCK_ABI_LAST 6 257 212 258 213 int main(const int argc, char *const argv[], char *const *const envp) 259 214 { ··· 268 223 .handled_access_fs = access_fs_rw, 269 224 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 270 225 LANDLOCK_ACCESS_NET_CONNECT_TCP, 226 + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, 271 227 }; 272 228 273 229 if (argc < 2) { 274 230 fprintf(stderr, 275 - "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s " 231 + "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s " 276 232 "<cmd> [args]...\n\n", 277 233 ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, 278 - ENV_TCP_CONNECT_NAME, argv[0]); 234 + ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); 279 235 fprintf(stderr, 280 236 "Execute a command in a restricted environment.\n\n"); 281 237 fprintf(stderr, ··· 297 251 fprintf(stderr, 298 252 "* %s: list of ports allowed to connect (client).\n", 299 253 ENV_TCP_CONNECT_NAME); 254 + fprintf(stderr, "* %s: list of scoped IPCs.\n", 255 + ENV_SCOPED_NAME); 300 256 fprintf(stderr, 301 257 "\nexample:\n" 302 258 "%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" " 303 259 "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " 304 260 "%s=\"9418\" " 305 261 "%s=\"80:443\" " 262 + "%s=\"a\" " 306 263 "%s bash -i\n\n", 307 264 ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, 308 - ENV_TCP_CONNECT_NAME, argv[0]); 265 + ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); 309 266 fprintf(stderr, 310 267 "This sandboxer can use Landlock features " 311 268 "up to ABI version %d.\n", ··· 376 327 /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */ 377 328 ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; 378 329 330 + __attribute__((fallthrough)); 331 + case 5: 332 + /* Removes LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET for ABI < 6 */ 333 + ruleset_attr.scoped &= ~LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET; 379 334 fprintf(stderr, 380 335 "Hint: You should update the running kernel " 381 336 "to leverage Landlock features " ··· 410 357 ruleset_attr.handled_access_net &= 411 358 ~LANDLOCK_ACCESS_NET_CONNECT_TCP; 412 359 } 360 + 361 + if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr)) 362 + return 1; 413 363 414 364 ruleset_fd = 415 365 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);