commit 7be09703f7d201cafeb2dc434f8d126475d39384 from: Omar Polo date: Mon Feb 12 19:26:42 2024 UTC sync landlock shim with gmid commit - 485796a93055f3ab431b76d802404ee1f07f5c5a commit + 7be09703f7d201cafeb2dc434f8d126475d39384 blob - e31ec2aab2c8e9ee7fb99c308ad6a6f3174affe0 blob + 18faddf332d53538a16d8ceeb2b47af6ba6be5ca --- sandbox.c +++ sandbox.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2021 Omar Polo + * Copyright (c) 2022 Claudio Jeker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -80,8 +81,6 @@ sandbox_ui_process(void) * have the struct declarations, libc wrappers are missing. The * sample landlock code provided by the authors includes these "shims" * in their example for the landlock API until libc provides them. - * - * Linux is such a mess sometimes. /rant */ #ifndef landlock_create_ruleset @@ -110,96 +109,149 @@ landlock_restrict_self(int ruleset_fd, __u32 flags) } #endif -static int -open_landlock(void) -{ - int fd; - struct landlock_ruleset_attr attr = { - .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE | - LANDLOCK_ACCESS_FS_READ_FILE | - LANDLOCK_ACCESS_FS_READ_DIR | - LANDLOCK_ACCESS_FS_WRITE_FILE | - LANDLOCK_ACCESS_FS_REMOVE_DIR | - LANDLOCK_ACCESS_FS_REMOVE_FILE | - LANDLOCK_ACCESS_FS_MAKE_CHAR | - LANDLOCK_ACCESS_FS_MAKE_DIR | - LANDLOCK_ACCESS_FS_MAKE_REG | - LANDLOCK_ACCESS_FS_MAKE_SOCK | - LANDLOCK_ACCESS_FS_MAKE_FIFO | - LANDLOCK_ACCESS_FS_MAKE_BLOCK | - LANDLOCK_ACCESS_FS_MAKE_SYM, - }; +/* + * Maybe we should ship with a full copy of the linux headers because + * you never know... + */ - fd = landlock_create_ruleset(&attr, sizeof(attr), 0); - if (fd == -1) { - switch (errno) { - case ENOSYS: - case EOPNOTSUPP: - return -1; - default: - err(1, "can't create landlock ruleset"); - } - } - return fd; -} +#ifndef LANDLOCK_ACCESS_FS_REFER +#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +#endif +#ifndef LANDLOCK_ACCESS_FS_TRUNCATE +#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) +#endif + +static int landlock_state; +static int landlock_fd; + +/* + * Initialize landlock, which is stupidly complicated. + */ static int -landlock_unveil(int landlock_fd, const char *path, int perms) +landlock_init(void) { - struct landlock_path_beneath_attr pb; - int err, saved_errno; + struct landlock_ruleset_attr rattr = { + /* + * List all capabilities currently defined by landlock. + * Failure in doing so will implicitly allow those actions + * (i.e. omitting READ_FILE will allow to read _any_ file.) + */ + .handled_access_fs = + LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_REMOVE_DIR | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_MAKE_CHAR | + LANDLOCK_ACCESS_FS_MAKE_DIR | + LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_MAKE_SOCK | + LANDLOCK_ACCESS_FS_MAKE_FIFO | + LANDLOCK_ACCESS_FS_MAKE_BLOCK | + LANDLOCK_ACCESS_FS_MAKE_SYM | + LANDLOCK_ACCESS_FS_REFER | + LANDLOCK_ACCESS_FS_TRUNCATE, + }; + int abi; - pb.allowed_access = perms; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + return -1; - if ((pb.parent_fd = open(path, O_PATH)) == -1) + abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi == -1) return -1; + if (abi < 2) + rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; + if (abi < 3) + rattr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; - err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, - &pb, 0); - saved_errno = errno; - close(pb.parent_fd); - errno = saved_errno; - return err ? -1 : 0; + landlock_state = 1; + return landlock_create_ruleset(&rattr, sizeof(rattr), 0); } static int -landlock_apply(int fd) +landlock_lock(void) { - int r, saved_errno; + int saved_errno; - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - err(1, "%s: prctl(PR_SET_NO_NEW_PRIVS)", __func__); + if (landlock_restrict_self(landlock_fd, 0)) { + saved_errno = errno; + close(landlock_fd); + errno = saved_errno; + landlock_state = -1; + return -1; + } - r = landlock_restrict_self(fd, 0); - saved_errno = errno; - close(fd); - errno = saved_errno; - return r ? -1 : 0; + landlock_state = 2; + close(landlock_fd); + return 0; } - static int -landlock_no_fs(void) +landlock_unveil(const char *path, const char *permissions) { - int fd; + struct landlock_path_beneath_attr lpba; + int fd, saved_errno; - /* - * XXX: landlock disabled at runtime, pretend everything's - * good. - */ - if ((fd = open_landlock()) == -1) + if (landlock_state == 0) { + if ((landlock_fd = landlock_init()) == -1) { + landlock_state = -1; + /* this kernel doesn't have landlock built in */ + if (errno == ENOSYS || errno == EOPNOTSUPP) + return 0; + return -1; + } + } + + /* no landlock available */ + if (landlock_state == -1) return 0; - return landlock_apply(fd); + if (path == NULL && permissions == NULL) + return landlock_lock(); + + if (path == NULL || permissions == NULL || landlock_state != 1) { + errno = EINVAL; + return -1; + } + + if (!strcmp(permissions, "r")) { + fd = open(path, O_PATH | O_CLOEXEC); + if (fd == -1) + return -1; + lpba = (struct landlock_path_beneath_attr){ + .allowed_access = + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR, + .parent_fd = fd, + }; + if (landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, + &lpba, 0) == -1) { + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; + } + close(fd); + } + + return 0; } void sandbox_net_process(void) { /* - * We don't know what paths are required for the TLS stack. + * We don't know what paths are required for the TLS stack and + * DNS, so allow accessing read-only the whole system. * Yes, it sucks. */ - return; + + if (landlock_unveil("/", "r") == -1) + err(1, "landlock_unveil(/, r)"); + if (landlock_unveil(NULL, NULL) == -1) + err(1, "landlock_unveil(NULL, NULL)"); } void