From 662ece327863d9c8514450e4a0ee9277065a1b00 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Thu, 23 Sep 2021 08:50:19 +0200 Subject: [PATCH] add a landlock-based implementation of unveil for linux --- compat/Makefile.am | 1 + compat/unveil.c | 188 +++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 + include/got_compat.h | 5 +- lib/privsep.c | 15 +--- 5 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 compat/unveil.c diff --git a/compat/Makefile.am b/compat/Makefile.am index 29fcf763..e26eef57 100644 --- a/compat/Makefile.am +++ b/compat/Makefile.am @@ -30,6 +30,7 @@ libopenbsd_compat_a_SOURCES = \ strsep.c \ strtonum.c \ uuid.c \ + unveil.c \ imsg.h \ queue.h \ tree.h diff --git a/compat/unveil.c b/compat/unveil.c new file mode 100644 index 00000000..812e170a --- /dev/null +++ b/compat/unveil.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * FIXME: should really be + * + * #ifdef HAVE_LINUX_LANDLOCK_H + * + */ +#ifdef __linux__ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * This is not really OpenBSD' unveil() but rather a "unveil + * implementation" on top of Linux' landlock. + * + * -*-*- + * + * 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. + */ + +#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 landlock_fd = -1; + +static int +open_landlock(void) +{ + struct landlock_ruleset_attr rattr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + 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, + }; + + return landlock_create_ruleset(&rattr, sizeof(rattr), 0); +} + +static int +parse_permissions(const char *permission) +{ + int perm = 0; + + for (; *permission; ++permission) { + switch (*permission) { + case 'r': + perm |= LANDLOCK_ACCESS_FS_READ_FILE; + perm |= LANDLOCK_ACCESS_FS_READ_DIR; + break; + case 'w': + perm |= LANDLOCK_ACCESS_FS_WRITE_FILE; + break; + case 'x': + perm |= LANDLOCK_ACCESS_FS_EXECUTE; + break; + case 'c': + perm |= LANDLOCK_ACCESS_FS_REMOVE_DIR; + perm |= LANDLOCK_ACCESS_FS_REMOVE_FILE; + perm |= LANDLOCK_ACCESS_FS_MAKE_CHAR; + perm |= LANDLOCK_ACCESS_FS_MAKE_DIR; + perm |= LANDLOCK_ACCESS_FS_MAKE_REG; + perm |= LANDLOCK_ACCESS_FS_MAKE_SOCK; + perm |= LANDLOCK_ACCESS_FS_MAKE_FIFO; + perm |= LANDLOCK_ACCESS_FS_MAKE_BLOCK; + perm |= LANDLOCK_ACCESS_FS_MAKE_SYM; + break; + default: + return -1; + } + } + + return perm; +} + +int +unveil(const char *path, const char *permissions) +{ + struct landlock_path_beneath_attr pb; + int fd, err; + + if (landlock_fd == -1) + landlock_fd = open_landlock(); + + if (path == NULL && permissions == NULL) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + return -1; + + if (landlock_restrict_self(landlock_fd, 0)) + return -1; + + close(landlock_fd); + landlock_fd = -1; + return 0; + } + + if (path == NULL || + permissions == NULL || + (pb.allowed_access = parse_permissions(permissions)) == -1) { + errno = EINVAL; + return -1; + } + + if ((pb.parent_fd = open(path, O_PATH)) == -1) + return -1; + + err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, + &pb, 0); + if (err) + goto err; + + close(pb.parent_fd); + return 0; + +err: + close(pb.parent_fd); + return -1; +} + +#else + +int +unveil(const char *path, const char *permissions) +{ + return 0; +} + +#endif diff --git a/configure.ac b/configure.ac index cdd6e159..68d8ed68 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,7 @@ AC_CHECK_HEADERS([ \ fcntl.h \ langinfo.h \ limits.h \ + linux/landlock.h \ locale.h \ netdb.h \ netinet/in.h \ @@ -135,6 +136,7 @@ AC_REPLACE_FUNCS([ \ strndup \ strsep \ strtonum \ + unveil \ ]) # Always use our getopt because 1) glibc's doesn't enforce argument order 2) diff --git a/include/got_compat.h b/include/got_compat.h index 6270fd4e..062d88e7 100644 --- a/include/got_compat.h +++ b/include/got_compat.h @@ -35,7 +35,6 @@ #ifndef __OpenBSD__ #define pledge(s, p) (0) -#define unveil(s, p) (0) #endif #ifndef INFTIM @@ -211,3 +210,7 @@ int BSDgetopt(int, char *const *, const char *); /* mergesort.c */ int mergesort(void *, size_t, size_t, int (*)(const void *, const void *)); #endif + +#ifndef HAVE_UNVEIL +int unveil(const char *, const char *); +#endif diff --git a/lib/privsep.c b/lib/privsep.c index 05b4a3a9..1d70046f 100644 --- a/lib/privsep.c +++ b/lib/privsep.c @@ -2732,22 +2732,13 @@ const struct got_error * got_privsep_unveil_exec_helpers(void) { const char *helpers[] = { - GOT_PATH_PROG_READ_PACK, - GOT_PATH_PROG_READ_OBJECT, - GOT_PATH_PROG_READ_COMMIT, - GOT_PATH_PROG_READ_TREE, - GOT_PATH_PROG_READ_BLOB, - GOT_PATH_PROG_READ_TAG, - GOT_PATH_PROG_READ_GITCONFIG, - GOT_PATH_PROG_READ_GOTCONFIG, - GOT_PATH_PROG_FETCH_PACK, - GOT_PATH_PROG_INDEX_PACK, - GOT_PATH_PROG_SEND_PACK, + "/lib64", + GOT_STRINGVAL(GOT_LIBEXECDIR), }; size_t i; for (i = 0; i < nitems(helpers); i++) { - if (unveil(helpers[i], "x") == 0) + if (unveil(helpers[i], "rx") == 0) continue; return got_error_from_errno2("unveil", helpers[i]); } -- 2.31.1