diff options
author | Remi Gacogne <rgacogne@archlinux.org> | 2024-04-03 17:05:18 +0200 |
---|---|---|
committer | Allan McRae <allan@archlinux.org> | 2024-08-02 00:39:45 +0000 |
commit | f142df92c7b85065d503d8a11cd295cc93d78a6d (patch) | |
tree | 315ad76628fa7a01db95ce16a56b68220ae27e4f | |
parent | c3aa1bc12367a8c29ddac310d8bb86ae10719bd2 (diff) |
Restrict syscalls for the download process whenever possible
Signed-off-by: Remi Gacogne <rgacogne@archlinux.org>
-rw-r--r-- | lib/libalpm/alpm.h | 4 | ||||
-rw-r--r-- | lib/libalpm/dload.c | 2 | ||||
-rw-r--r-- | lib/libalpm/meson.build | 1 | ||||
-rw-r--r-- | lib/libalpm/sandbox.c | 6 | ||||
-rw-r--r-- | lib/libalpm/sandbox_syscalls.c | 165 | ||||
-rw-r--r-- | lib/libalpm/sandbox_syscalls.h | 26 | ||||
-rw-r--r-- | meson.build | 6 | ||||
-rw-r--r-- | src/pacman/conf.c | 2 |
8 files changed, 207 insertions, 5 deletions
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index d80d9e19..8fa4076f 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -34,6 +34,7 @@ extern "C" { #endif +#include <stdbool.h> /* bool */ #include <stdint.h> /* int64_t */ #include <sys/types.h> /* off_t */ #include <stdarg.h> /* va_list */ @@ -2971,9 +2972,10 @@ int alpm_capabilities(void); * @param handle the context handle * @param sandboxuser the user to switch to * @param sandbox_path if non-NULL, restrict writes to this filesystem path + * @param restrict_syscalls whether to deny access to a list of dangerous syscalls * @return 0 on success, -1 on failure */ -int alpm_sandbox_setup_child(alpm_handle_t *handle, const char *sandboxuser, const char *sandbox_path); +int alpm_sandbox_setup_child(alpm_handle_t *handle, const char *sandboxuser, const char *sandbox_path, bool restrict_syscalls); /* End of libalpm_misc */ /** @} */ diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c index 77c4cea9..e6796711 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -963,7 +963,7 @@ static int curl_download_internal_sandboxed(alpm_handle_t *handle, _alpm_log(handle, ALPM_LOG_ERROR, _("could not chdir to download directory %s\n"), localpath); ret = -1; } else { - ret = alpm_sandbox_setup_child(handle, handle->sandboxuser, localpath); + ret = alpm_sandbox_setup_child(handle, handle->sandboxuser, localpath, true); if (ret != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("switching to sandbox user '%s' failed!\n"), handle->sandboxuser); _Exit(2); diff --git a/lib/libalpm/meson.build b/lib/libalpm/meson.build index 1a6672da..c2c605f9 100644 --- a/lib/libalpm/meson.build +++ b/lib/libalpm/meson.build @@ -26,6 +26,7 @@ libalpm_sources = files(''' remove.h remove.c sandbox.h sandbox.c sandbox_fs.h sandbox_fs.c + sandbox_syscalls.h sandbox_syscalls.c signing.c signing.h sync.h sync.c trans.h trans.c diff --git a/lib/libalpm/sandbox.c b/lib/libalpm/sandbox.c index d8e01e44..7650c119 100644 --- a/lib/libalpm/sandbox.c +++ b/lib/libalpm/sandbox.c @@ -27,9 +27,10 @@ #include "log.h" #include "sandbox.h" #include "sandbox_fs.h" +#include "sandbox_syscalls.h" #include "util.h" -int SYMEXPORT alpm_sandbox_setup_child(alpm_handle_t *handle, const char* sandboxuser, const char* sandbox_path) +int SYMEXPORT alpm_sandbox_setup_child(alpm_handle_t *handle, const char* sandboxuser, const char* sandbox_path, bool restrict_syscalls) { struct passwd const *pw = NULL; @@ -39,6 +40,9 @@ int SYMEXPORT alpm_sandbox_setup_child(alpm_handle_t *handle, const char* sandbo if(sandbox_path != NULL && !handle->disable_sandbox) { _alpm_sandbox_fs_restrict_writes_to(handle, sandbox_path); } + if(restrict_syscalls && !handle->disable_sandbox) { + _alpm_sandbox_syscalls_filter(handle); + } ASSERT(setgid(pw->pw_gid) == 0, return -1); ASSERT(setgroups(0, NULL) == 0, return -1); ASSERT(setuid(pw->pw_uid) == 0, return -1); diff --git a/lib/libalpm/sandbox_syscalls.c b/lib/libalpm/sandbox_syscalls.c new file mode 100644 index 00000000..ce0ee6ef --- /dev/null +++ b/lib/libalpm/sandbox_syscalls.c @@ -0,0 +1,165 @@ +/* + * sandbox_syscalls.c + * + * Copyright (c) 2021-2022 Pacman Development Team <pacman-dev@lists.archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <errno.h> +#include <stddef.h> + +#include "config.h" +#include "log.h" +#include "sandbox_syscalls.h" +#include "util.h" + +#ifdef HAVE_LIBSECCOMP +# include <seccomp.h> +#endif /* HAVE_LIBSECCOMP */ + +bool _alpm_sandbox_syscalls_filter(alpm_handle_t *handle) +{ + int ret = 0; +#ifdef HAVE_LIBSECCOMP + /* see https://docs.docker.com/engine/security/seccomp/ for inspiration, + as well as systemd's src/shared/seccomp-util.c */ + const char *denied_syscalls[] = { + /* kernel modules */ + "delete_module", + "finit_module", + "init_module", + /* mount */ + "chroot", + "fsconfig", + "fsmount", + "fsopen", + "fspick", + "mount", + "mount_setattr", + "move_mount", + "open_tree", + "pivot_root", + "umount", + "umount2", + /* keyring */ + "add_key", + "keyctl", + "request_key", + /* CPU emulation */ + "modify_ldt", + "subpage_prot", + "switch_endian", + "vm86", + "vm86old", + /* debug */ + "kcmp", + "lookup_dcookie", + "perf_event_open", + "pidfd_getfd", + "ptrace", + "rtas", + "sys_debug_setcontext", + /* set clock */ + "adjtimex", + "clock_adjtime", + "clock_adjtime64", + "clock_settime", + "clock_settime64", + "settimeofday", + /* raw IO */ + "ioperm", + "iopl", + "pciconfig_iobase", + "pciconfig_read", + "pciconfig_write", + /* kexec */ + "kexec_file_load", + "kexec_load", + /* reboot */ + "reboot", + /* privileged */ + "acct", + "bpf", + "capset", + "chroot", + "fanotify_init", + "fanotify_mark", + "nfsservctl", + "open_by_handle_at", + "pivot_root", + "personality", + /* obsolete */ + "_sysctl", + "afs_syscall", + "bdflush", + "break", + "create_module", + "ftime", + "get_kernel_syms", + "getpmsg", + "gtty", + "idle", + "lock", + "mpx", + "prof", + "profil", + "putpmsg", + "query_module", + "security", + "sgetmask", + "ssetmask", + "stime", + "stty", + "sysfs", + "tuxcall", + "ulimit", + "uselib", + "ustat", + "vserver", + /* swap */ + "swapon", + "swapoff", + }; + /* allow all syscalls that are not listed */ + size_t idx; + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); + int restrictedSyscallsCount = 0; + if(ctx == NULL) { + return false; + } + + for(idx = 0; idx < sizeof(denied_syscalls) / sizeof(*denied_syscalls); idx++) { + int syscall = seccomp_syscall_resolve_name(denied_syscalls[idx]); + if(syscall != __NR_SCMP_ERROR) { + if(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), syscall, 0) != 0) { + _alpm_log(handle, ALPM_LOG_ERROR, _("error restricting syscall %s via seccomp!\n"), denied_syscalls[idx]); + } + else { + restrictedSyscallsCount++; + } + } + } + + if(seccomp_load(ctx) != 0) { + ret = errno; + _alpm_log(handle, ALPM_LOG_ERROR, _("error restricting syscalls via seccomp: %d!\n"), ret); + } + else { + _alpm_log(handle, ALPM_LOG_DEBUG, _("successfully restricted %d syscalls via seccomp\n"), restrictedSyscallsCount); + } + + seccomp_release(ctx); +#endif /* HAVE_LIBSECCOMP */ + return ret == 0; +} diff --git a/lib/libalpm/sandbox_syscalls.h b/lib/libalpm/sandbox_syscalls.h new file mode 100644 index 00000000..f8650d7a --- /dev/null +++ b/lib/libalpm/sandbox_syscalls.h @@ -0,0 +1,26 @@ +/* + * sandbox_syscalls.h + * + * Copyright (c) 2021-2022 Pacman Development Team <pacman-dev@lists.archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef ALPM_SANDBOX_SYSCALLS_H +#define ALPM_SANDBOX_SYSCALLS_H + +#include <stdbool.h> + +bool _alpm_sandbox_syscalls_filter(alpm_handle_t *handle); + +#endif /* ALPM_SANDBOX_SYSCALLS_H */ diff --git a/meson.build b/meson.build index 1f64d1e1..e48120e8 100644 --- a/meson.build +++ b/meson.build @@ -122,6 +122,10 @@ else error('unhandled crypto value @0@'.format(want_crypto)) endif +libseccomp = dependency('libseccomp', + static : get_option('buildstatic'), + required : false) +conf.set('HAVE_LIBSECCOMP', libseccomp.found()) foreach header : [ 'linux/landlock.h', 'mntent.h', @@ -327,7 +331,7 @@ libcommon = static_library( gnu_symbol_visibility : 'hidden', install : false) -alpm_deps = [crypto_provider, libarchive, libcurl, libintl, gpgme] +alpm_deps = [crypto_provider, libarchive, libcurl, libintl, libseccomp, gpgme] libalpm_a = static_library( 'alpm_objlib', diff --git a/src/pacman/conf.c b/src/pacman/conf.c index d0966eea..6bb50326 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -246,7 +246,7 @@ static int systemvp(const char *file, char *const argv[]) sigprocmask(SIG_SETMASK, &oldblock, NULL); if (config->sandboxuser) { - ret = alpm_sandbox_setup_child(config->handle, config->sandboxuser, NULL); + ret = alpm_sandbox_setup_child(config->handle, config->sandboxuser, NULL, false); if (ret != 0) { pm_printf(ALPM_LOG_ERROR, _("switching to sandbox user '%s' failed!\n"), config->sandboxuser); _Exit(ret); |