commit a9d11f81b94d08e2c75dac892056b1071b182f9e from: Omar Polo date: Fri Oct 08 17:07:09 2021 UTC add landlock support on linux landlock is applied only to the ui process to drop fs access and in the fs process to limit where telescope can read/write files. The network process is more difficult to landlock because while in theory it doesn't need *any* fs access, in practice it needs to read (at least) files inside /etc/ for DNS to work. commit - b9fcc0e904dfa51639d0c1c94de7a7b864b9a72a commit + a9d11f81b94d08e2c75dac892056b1071b182f9e blob - 1b8464890c0438df5aa42998caa609fb33326deb blob + 25c1073a8ebb61193630ca4cae7ff574723b52ee --- configure.ac +++ configure.ac @@ -112,6 +112,8 @@ AC_CHECK_LIB(util, ohash_init, [], [ AC_CHECK_FUNCS([asr_run]) +AC_CHECK_HEADERS([linux/landlock.h]) + # check compiler flags AC_DEFUN([CC_ADD_CHECK_FLAGS], [ AC_MSG_CHECKING([if $CC supports $1 flag]) blob - 97ffd1da7cadf630624d09ed379240f3c37acaf7 blob + c489ff7ecfe5e9d37bf0d81ffa97c7b5edd67fe6 --- sandbox.c +++ sandbox.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "compat.h" + #include "fs.h" #include "telescope.h" @@ -64,6 +66,160 @@ sandbox_fs_process(void) err(1, "pledge"); } +#elif HAVE_LINUX_LANDLOCK_H + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +/* + * What's the deal with landlock? While distro with linux >= 5.13 + * 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 +static inline int +landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size, + __u32 flags) +{ + return syscall(__NR_landlock_create_ruleset, attr, size, flags); +} +#endif + +#ifndef landlock_add_rule +static inline int +landlock_add_rule(int ruleset_fd, enum landlock_rule_type type, + const void *attr, __u32 flags) +{ + return syscall(__NR_landlock_add_rule, ruleset_fd, type, attr, flags); +} +#endif + +#ifndef landlock_restrict_self +static inline int +landlock_restrict_self(int ruleset_fd, __u32 flags) +{ + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); +} +#endif + +static int +open_landlock(void) +{ + struct landlock_ruleset_attr attr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_MAKE_DIR | + LANDLOCK_ACCESS_FS_MAKE_REG, + }; + + return landlock_create_ruleset(&attr, sizeof(attr), 0); +} + +static int +landlock_unveil(int landlock_fd, const char *path, int perms) +{ + struct landlock_path_beneath_attr pb; + int err, saved_errno; + + pb.allowed_access = perms; + + if ((pb.parent_fd = open(path, O_PATH)) == -1) + return -1; + + 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; +} + +static int +landlock_apply(int fd) +{ + int r, saved_errno; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + err(1, "%s: prctl(PR_SET_NO_NEW_PRIVS)", __func__); + + r = landlock_restrict_self(fd, 0); + saved_errno = errno; + close(fd); + errno = saved_errno; + return r ? -1 : 0; +} + +static int +landlock_no_fs(void) +{ + int fd; + + if ((fd = open_landlock()) == -1) + return -1; + + return landlock_apply(fd); +} + +void +sandbox_net_process(void) +{ + return; +} + +void +sandbox_ui_process(void) +{ + if (landlock_no_fs() == -1) + err(1, "landlock"); +} + +void +sandbox_fs_process(void) +{ + int fd, rwc; + char path[PATH_MAX]; + + if ((fd = open_landlock()) == -1) + err(1, "can't create landlock ruleset"); + + rwc = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_MAKE_DIR | + LANDLOCK_ACCESS_FS_MAKE_REG; + + if (landlock_unveil(fd, "/tmp", rwc) == -1) + err(1, "landlock_unveil(/tmp)"); + + strlcpy(path, getenv("HOME"), sizeof(path)); + strlcat(path, "/Downloads", sizeof(path)); + if (landlock_unveil(fd, path, rwc) == -1) + err(1, "landlock_unveil(%s)", path); + + if (landlock_unveil(fd, config_path_base, rwc) == -1) + err(1, "landlock_unveil(%s)", config_path_base); + + if (landlock_unveil(fd, data_path_base, rwc) == -1) + err(1, "landlock_unveil(%s)", data_path_base); + + if (landlock_unveil(fd, cache_path_base, rwc) == -1) + err(1, "landlock_unveil(%s)", cache_path_base); +} + #else #warning "No sandbox for this OS"