[pacman-dev] [PATCH] Add optional sandboxing when downloading files
Remi Gacogne
rgacogne at archlinux.org
Fri Aug 27 07:39:46 UTC 2021
Hi all,
I would like to get some feedback on a optional sandboxing feature I would like
to implement in pacman. The gist of it is to use a separate process for some
sensitive operations, like downloading files, and to restrict the privileges
of that process to reduce the risk of an exploitable bug turning into a code
execution with full root privileges. I'm not too worried about the pacman code
itself, but downloading files over the internet involves parsing HTTP payloads,
as well as parsing X.509 certificates and doing cryptographic operations when
HTTPS is used, which is more error-prone.
For what it's worth, Debian's APT has similar options hidden behind the
APT::Sandbox::Seccomp and APT::Sandbox::User parameters.
The attached patch is clearly not in a finished state, but provides more details
about the kind of changes needed to support this new feature. In a nutshell,
what it currently does is that if the UseSandbox option is not set nothing
changes. Otherwise internal and external downloads trigger the spawning of a
new process, via fork(), whose result is communicated to the main process via a
pipe, as is already done today for external downloads. Then:
- If available, PR_SET_NO_NEW_PRIVS is called to ensure that the downloader will
not be able to gain new privileges in the future, for example by executing a
set-uid program.
- If pacman has been built with libseccomp support and the kernel supports it, a
list of system calls that are clearly of no use to a regular process
downloading files is denied via a seccomp filter. That list is currently
hard-coded but it would make more sense to make it configurable.
- If the SandboxUser option is set to a valid user, the downloader process
switches to that user via setgid, setgroups and setuid, dropping root
privileges. This part requires making the download folder owned by that user.
- If pacman has been built with libcap support and the downloader process still
has privileged capabilities at this point, these are dropped.
- If pacman has been built with landlock support (requires an updated
linux-api-headers, >= 5.13) and the kernel supports it, the whole filesystem
is set read-only with the exception of /tmp and DBPath. The list of writable
paths is currently hard-coded but it would make sense to make it configurable
as well.
Any feedback will be welcome. Please be aware that I tried to keep the new code
in line with the coding style and contributing guidelines but I'm not familiar
with pacman's source code so I'm sure I did not do everything the right way, and
that this code can be improved. What I would like to know first is whether there
is any interest in that feature, then to get feedback about the "big picture"
part of the design. If there is indeed interest and my approach is somewhat
sane, then I can work on making the code less ugly, write the much needed
documentation for the new feature and related options, and improve the logging
of errors.
Thanks!
Remi
Signed-off-by: Remi Gacogne <rgacogne at archlinux.org>
---
lib/libalpm/alpm.h | 10 ++
lib/libalpm/alpm_sandbox.c | 341 +++++++++++++++++++++++++++++++++++++
lib/libalpm/alpm_sandbox.h | 31 ++++
lib/libalpm/dload.c | 83 ++++++++-
lib/libalpm/handle.c | 20 +++
lib/libalpm/handle.h | 2 +
lib/libalpm/meson.build | 1 +
meson.build | 11 +-
src/pacman/conf.c | 23 ++-
src/pacman/conf.h | 2 +
src/pacman/pacman-conf.c | 6 +
11 files changed, 526 insertions(+), 4 deletions(-)
create mode 100644 lib/libalpm/alpm_sandbox.c
create mode 100644 lib/libalpm/alpm_sandbox.h
diff --git lib/libalpm/alpm.h lib/libalpm/alpm.h
index a5f4a6ae..f414d811 100644
--- lib/libalpm/alpm.h
+++ lib/libalpm/alpm.h
@@ -1837,6 +1837,11 @@ int alpm_option_set_gpgdir(alpm_handle_t *handle, const char *gpgdir);
/* End of gpdir accessors */
/** @} */
+/** Sets the user to switch to for sensitive operations like downloading a file.
+ * @param handle the context handle
+ * @param sandboxuser the user to set
+ */
+int alpm_option_set_sandboxuser(alpm_handle_t *handle, const char *sandboxuser);
/** @name Accessors for use syslog
*
@@ -1859,6 +1864,11 @@ int alpm_option_set_usesyslog(alpm_handle_t *handle, int usesyslog);
/* End of usesyslog accessors */
/** @} */
+/** Sets whether to use sandboxing for sensitive operations like downloading a file (0 is FALSE, TRUE otherwise).
+ * @param handle the context handle
+ * @param usesandbox whether to use the sandboxing (0 is FALSE, TRUE otherwise)
+ */
+int alpm_option_set_usesandbox(alpm_handle_t *handle, int usesandbox);
/** @name Accessors to the list of no-upgrade files.
* These functions modify the list of files which should
diff --git lib/libalpm/alpm_sandbox.c lib/libalpm/alpm_sandbox.c
new file mode 100644
index 00000000..29f46d58
--- /dev/null
+++ lib/libalpm/alpm_sandbox.c
@@ -0,0 +1,341 @@
+/*
+ * sandbox.c
+ *
+ * Copyright (c) 2021 Pacman Development Team <pacman-dev at 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 <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/capability.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef HAVE_LINUX_LANDLOCK_H
+# include <linux/landlock.h>
+# include <sys/prctl.h>
+# include <sys/syscall.h>
+#endif /* HAVE_LINUX_LANDLOCK_H */
+
+#ifdef HAVE_LIBSECCOMP
+# include <seccomp.h>
+#endif /* HAVE_LIBSECCOMP */
+
+#include "alpm_sandbox.h"
+
+#ifdef HAVE_LINUX_LANDLOCK_H
+#ifndef landlock_create_ruleset
+static inline int landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
+ const size_t size, const __u32 flags)
+{
+ return syscall(__NR_landlock_create_ruleset, attr, size, flags);
+}
+#endif /* landlock_create_ruleset */
+
+#ifndef landlock_add_rule
+static inline int landlock_add_rule(const int ruleset_fd,
+ const enum landlock_rule_type rule_type,
+ const void *const rule_attr, const __u32 flags)
+{
+ return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, flags);
+}
+#endif /* landlock_add_rule */
+
+#ifndef landlock_restrict_self
+static inline int landlock_restrict_self(const int ruleset_fd, const __u32 flags)
+{
+ return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
+}
+#endif /* landlock_restrict_self */
+
+#define _LANDLOCK_ACCESS_FS_WRITE ( \
+ 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)
+
+#define _LANDLOCK_ACCESS_FS_READ ( \
+ LANDLOCK_ACCESS_FS_READ_FILE | \
+ LANDLOCK_ACCESS_FS_READ_DIR)
+
+static int sandbox_write_only_beneath_cwd(void)
+{
+ const struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_fs = \
+ _LANDLOCK_ACCESS_FS_READ | \
+ _LANDLOCK_ACCESS_FS_WRITE | \
+ LANDLOCK_ACCESS_FS_EXECUTE,
+ };
+ struct landlock_path_beneath_attr path_beneath = {
+ .allowed_access = _LANDLOCK_ACCESS_FS_WRITE,
+ };
+ int result = 0;
+ int ruleset_fd;
+
+ ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+ if(ruleset_fd < 0) {
+ return ruleset_fd;
+ }
+
+ /* allow / as read-only */
+ path_beneath.parent_fd = open("/", O_PATH | O_CLOEXEC | O_DIRECTORY);
+ path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ | LANDLOCK_ACCESS_FS_EXECUTE;
+
+ if(landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) {
+ result = errno;
+ }
+
+ close(path_beneath.parent_fd);
+
+ if(result == 0) {
+ /* allow the current working directory as read-write */
+ path_beneath.parent_fd = open(".", O_PATH | O_CLOEXEC | O_DIRECTORY);
+ path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ | _LANDLOCK_ACCESS_FS_WRITE | LANDLOCK_ACCESS_FS_EXECUTE;
+
+ if(!landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) {
+ if(landlock_restrict_self(ruleset_fd, 0)) {
+ result = errno;
+ }
+ } else {
+ result = errno;
+ }
+
+ close(path_beneath.parent_fd);
+
+ if(result == 0) {
+ /* allow /tmp as well */
+ path_beneath.parent_fd = open("/tmp", O_PATH | O_CLOEXEC | O_DIRECTORY);
+ path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ | _LANDLOCK_ACCESS_FS_WRITE | LANDLOCK_ACCESS_FS_EXECUTE;
+
+ if(!landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) {
+ if(landlock_restrict_self(ruleset_fd, 0)) {
+ result = errno;
+ }
+ } else {
+ result = errno;
+ }
+
+ close(path_beneath.parent_fd);
+ }
+ }
+
+ close(ruleset_fd);
+ return result;
+}
+#endif /* HAVE_LINUX_LANDLOCK_H */
+
+#ifdef HAVE_LIBSECCOMP
+
+static int sandbox_filter_syscalls(void)
+{
+ int ret = 0;
+ /* 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",
+ "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",
+ "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",
+ "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);
+ if(ctx == NULL) {
+ return errno;
+ }
+
+ 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) {
+ /* Do not fail but log something like: _("Error blocking syscall %s\n"), denied_syscalls[idx]); */
+ }
+ }
+ }
+
+ if(seccomp_load(ctx) != 0) {
+ ret = errno;
+ }
+
+ seccomp_release(ctx);
+ return ret;
+}
+#endif /* HAVE_LIBSECCOMP */
+
+static int switch_to_user(const char *user)
+{
+ struct passwd const *pw = NULL;
+ if(getuid() != 0) {
+ return 1;
+ }
+ pw = getpwnam(user);
+ if(pw == NULL) {
+ return errno;
+ }
+ if(setgid(pw->pw_gid) != 0) {
+ return errno;
+ }
+ if(setgroups(0, NULL)) {
+ return errno;
+ }
+ if(setuid(pw->pw_uid) != 0) {
+ return errno;
+ }
+ return 0;
+}
+
+/* check exported library symbols with: nm -C -D <lib> */
+#define SYMEXPORT __attribute__((visibility("default")))
+
+int SYMEXPORT alpm_sandbox_child(const char* sandboxuser)
+{
+ int result = 0;
+#ifdef HAVE_LINUX_LANDLOCK_H
+ int ret = 0;
+#endif/* HAVE_LINUX_LANDLOCK_H */
+
+ /* make sure that we cannot gain more privileges later */
+ if(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+ result = errno;
+ }
+
+#ifdef HAVE_LIBSECCOMP
+ result = sandbox_filter_syscalls();
+#endif /* HAVE_LIBSECCOMP */
+
+ if(sandboxuser != NULL) {
+ result = switch_to_user(sandboxuser);
+ }
+
+#ifdef HAVE_LIBCAP
+ /* we might have some capabilities remaining,
+ * especially if sandboxuser is not set */
+ cap_t caps = cap_get_proc();
+ cap_clear(caps);
+ if(cap_set_mode(CAP_MODE_NOPRIV) != 0) {
+ cap_free(caps);
+ if(result == 0) {
+ result = errno;
+ }
+ }
+ if(cap_set_proc(caps) != 0) {
+ cap_free(caps);
+ if(result == 0) {
+ result = errno;
+ }
+ }
+ cap_free(caps);
+#endif /* HAVE_LIBCAP */
+
+#ifdef HAVE_LINUX_LANDLOCK_H
+ ret = sandbox_write_only_beneath_cwd();
+ if(result == 0) {
+ result = ret;
+ }
+#endif /* HAVE_LINUX_LANDLOCK_H */
+
+ return result;
+}
diff --git lib/libalpm/alpm_sandbox.h lib/libalpm/alpm_sandbox.h
new file mode 100644
index 00000000..47611575
--- /dev/null
+++ lib/libalpm/alpm_sandbox.h
@@ -0,0 +1,31 @@
+/*
+ * sandbox.h
+ *
+ * Copyright (c) 2021 Pacman Development Team <pacman-dev at 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_H
+#define ALPM_SANDBOX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int alpm_sandbox_child(const char *sandboxuser);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ALPM_SANDBOX_H */
diff --git lib/libalpm/dload.c lib/libalpm/dload.c
index ca6be7b6..6bb06b4c 100644
--- lib/libalpm/dload.c
+++ lib/libalpm/dload.c
@@ -28,6 +28,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <signal.h>
#ifdef HAVE_NETINET_IN_H
@@ -44,6 +45,7 @@
/* libalpm */
#include "dload.h"
#include "alpm_list.h"
+#include "alpm_sandbox.h"
#include "alpm.h"
#include "log.h"
#include "util.h"
@@ -898,6 +900,81 @@ static int curl_download_internal(alpm_handle_t *handle,
#endif
+static int curl_download_internal_sandboxed(alpm_handle_t *handle,
+ alpm_list_t *payloads /* struct dload_payload */,
+ const char *localpath)
+{
+ int pid, err = 0, ret = -1, err_fd[2];
+ sigset_t oldblock;
+ struct sigaction sa_ign = { .sa_handler = SIG_IGN }, oldint, oldquit;
+
+ if(pipe(err_fd) != 0) {
+ return -1;
+ }
+
+ sigaction(SIGINT, &sa_ign, &oldint);
+ sigaction(SIGQUIT, &sa_ign, &oldquit);
+ sigaddset(&sa_ign.sa_mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sa_ign.sa_mask, &oldblock);
+
+ pid = fork();
+
+ /* child */
+ if(pid == 0) {
+ close(err_fd[0]);
+ fcntl(err_fd[1], F_SETFD, FD_CLOEXEC);
+
+ /* restore signal handling for the child to inherit */
+ sigaction(SIGINT, &oldint, NULL);
+ sigaction(SIGQUIT, &oldquit, NULL);
+ sigprocmask(SIG_SETMASK, &oldblock, NULL);
+
+ /* cwd to the download directory */
+ ret = chdir(localpath);
+ if(ret != 0) {
+ _alpm_log(handle, ALPM_LOG_WARNING, _("could not chdir to download directory %s\n"), localpath);
+ ret = -1;
+ } else {
+ ret = alpm_sandbox_child(handle->sandboxuser);
+ if (ret != 0) {
+ _alpm_log(handle, ALPM_LOG_WARNING, _("sandboxing failed!\n"));
+ }
+
+ ret = curl_download_internal(handle, payloads, localpath);
+ }
+
+ /* pass the result back to the parent */
+ while(write(err_fd[1], &ret, sizeof(ret)) == -1 && errno == EINTR);
+ _Exit(ret < 0 ? 1 : 0);
+ }
+
+ /* parent */
+ close(err_fd[1]);
+
+ if(pid != -1) {
+ int wret;
+ while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR);
+ if(wret > 0) {
+ while(read(err_fd[0], &ret, sizeof(ret)) == -1 && errno == EINTR);
+ }
+ } else {
+ /* fork failed, make sure errno is preserved after cleanup */
+ err = errno;
+ }
+
+ close(err_fd[0]);
+
+ sigaction(SIGINT, &oldint, NULL);
+ sigaction(SIGQUIT, &oldquit, NULL);
+ sigprocmask(SIG_SETMASK, &oldblock, NULL);
+
+ if(err) {
+ errno = err;
+ ret = -1;
+ }
+ return ret;
+}
+
/* Returns -1 if an error happened for a required file
* Returns 0 if a payload was actually downloaded
* Returns 1 if no files were downloaded and all errors were non-fatal
@@ -908,7 +985,11 @@ int _alpm_download(alpm_handle_t *handle,
{
if(handle->fetchcb == NULL) {
#ifdef HAVE_LIBCURL
- return curl_download_internal(handle, payloads, localpath);
+ if(handle->usesandbox) {
+ return curl_download_internal_sandboxed(handle, payloads, localpath);
+ } else {
+ return curl_download_internal(handle, payloads, localpath);
+ }
#else
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
#endif
diff --git lib/libalpm/handle.c lib/libalpm/handle.c
index e6b683cb..10ae44b2 100644
--- lib/libalpm/handle.c
+++ lib/libalpm/handle.c
@@ -573,6 +573,19 @@ int SYMEXPORT alpm_option_set_gpgdir(alpm_handle_t *handle, const char *gpgdir)
return 0;
}
+int SYMEXPORT alpm_option_set_sandboxuser(alpm_handle_t *handle, const char *sandboxuser)
+{
+ CHECK_HANDLE(handle, return -1);
+ if(handle->sandboxuser) {
+ FREE(handle->sandboxuser);
+ }
+
+ STRDUP(handle->sandboxuser, sandboxuser, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
+
+ _alpm_log(handle, ALPM_LOG_DEBUG, "option 'sandboxuser' = %s\n", handle->sandboxuser);
+ return 0;
+}
+
int SYMEXPORT alpm_option_set_usesyslog(alpm_handle_t *handle, int usesyslog)
{
CHECK_HANDLE(handle, return -1);
@@ -580,6 +593,13 @@ int SYMEXPORT alpm_option_set_usesyslog(alpm_handle_t *handle, int usesyslog)
return 0;
}
+int SYMEXPORT alpm_option_set_usesandbox(alpm_handle_t *handle, int usesandbox)
+{
+ CHECK_HANDLE(handle, return -1);
+ handle->usesandbox = usesandbox;
+ return 0;
+}
+
static int _alpm_option_strlist_add(alpm_handle_t *handle, alpm_list_t **list, const char *str)
{
char *dup;
diff --git lib/libalpm/handle.h lib/libalpm/handle.h
index b1526c67..43550833 100644
--- lib/libalpm/handle.h
+++ lib/libalpm/handle.h
@@ -90,6 +90,7 @@ struct __alpm_handle_t {
char *logfile; /* Name of the log file */
char *lockfile; /* Name of the lock file */
char *gpgdir; /* Directory where GnuPG files are stored */
+ char *sandboxuser; /* User to switch to for sensitive operations like downloading files */
alpm_list_t *cachedirs; /* Paths to pacman cache directories */
alpm_list_t *hookdirs; /* Paths to hook directories */
alpm_list_t *overwrite_files; /* Paths that may be overwritten */
@@ -104,6 +105,7 @@ struct __alpm_handle_t {
/* options */
alpm_list_t *architectures; /* Architectures of packages we should allow */
int usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */
+ int usesandbox; /* Whether to enable sandboxing for sensitive operations like downloading files */
int checkspace; /* Check disk space before installing */
char *dbext; /* Sync DB extension */
int siglevel; /* Default signature verification level */
diff --git lib/libalpm/meson.build lib/libalpm/meson.build
index 607e91a3..c36ae516 100644
--- lib/libalpm/meson.build
+++ lib/libalpm/meson.build
@@ -2,6 +2,7 @@ libalpm_sources = files('''
add.h add.c
alpm.h alpm.c
alpm_list.h alpm_list.c
+ alpm_sandbox.h alpm_sandbox.c
backup.h backup.c
base64.h base64.c
be_local.c
diff --git meson.build meson.build
index 26c92b8e..88f0e3a1 100644
--- meson.build
+++ meson.build
@@ -91,6 +91,10 @@ libarchive = dependency('libarchive',
version : '>=3.0.0',
static : get_option('buildstatic'))
+libcap = dependency('libcap',
+ static : get_option('buildstatic'))
+conf.set('HAVE_LIBCAP', libcap.found())
+
libcurl = dependency('libcurl',
version : '>=7.55.0',
required : get_option('curl'),
@@ -120,7 +124,12 @@ else
error('unhandled crypto value @0@'.format(want_crypto))
endif
+libseccomp = dependency('libseccomp',
+ static : get_option('buildstatic'))
+conf.set('HAVE_LIBSECCOMP', libseccomp.found())
+
foreach header : [
+ 'linux/landlock.h',
'mntent.h',
'sys/mnttab.h',
'sys/mount.h',
@@ -305,7 +314,7 @@ libcommon = static_library(
gnu_symbol_visibility : 'hidden',
install : false)
-alpm_deps = [crypto_provider, libarchive, libcurl, libintl, gpgme]
+alpm_deps = [crypto_provider, libarchive, libcap, libcurl, libintl, libseccomp, gpgme]
libalpm_a = static_library(
'alpm_objlib',
diff --git src/pacman/conf.c src/pacman/conf.c
index 12fee64c..9480a492 100644
--- src/pacman/conf.c
+++ src/pacman/conf.c
@@ -33,6 +33,8 @@
#include <unistd.h>
#include <signal.h>
+#include <alpm_sandbox.h>
+
/* pacman */
#include "conf.h"
#include "ini.h"
@@ -215,7 +217,7 @@ static char *get_tempfile(const char *path, const char *filename)
* - not thread-safe
* - errno may be set by fork(), pipe(), or execvp()
*/
-static int systemvp(const char *file, char *const argv[])
+static int systemvp(const char *file, char *const argv[], bool sandbox, const char *sandboxuser)
{
int pid, err = 0, ret = -1, err_fd[2];
sigset_t oldblock;
@@ -242,6 +244,13 @@ static int systemvp(const char *file, char *const argv[])
sigaction(SIGQUIT, &oldquit, NULL);
sigprocmask(SIG_SETMASK, &oldblock, NULL);
+ if (sandbox) {
+ ret = alpm_sandbox_child(sandboxuser);
+ if (ret != 0) {
+ pm_printf(ALPM_LOG_WARNING, _("sandboxing failed!\n"));
+ }
+ }
+
execvp(file, argv);
/* execvp failed, pass the error back to the parent */
@@ -352,7 +361,7 @@ static int download_with_xfercommand(void *ctx, const char *url,
free(cmd);
}
}
- retval = systemvp(argv[0], (char**)argv);
+ retval = systemvp(argv[0], (char**)argv, config->usesandbox, config->sandboxuser);
if(retval == -1) {
pm_printf(ALPM_LOG_WARNING, _("running XferCommand: fork failed!\n"));
@@ -601,6 +610,9 @@ static int _parse_options(const char *key, char *value,
if(strcmp(key, "UseSyslog") == 0) {
config->usesyslog = 1;
pm_printf(ALPM_LOG_DEBUG, "config: usesyslog\n");
+ } else if(strcmp(key, "UseSandbox") == 0) {
+ config->usesandbox = 1;
+ pm_printf(ALPM_LOG_DEBUG, "config: usesandbox\n");
} else if(strcmp(key, "ILoveCandy") == 0) {
config->chomp = 1;
pm_printf(ALPM_LOG_DEBUG, "config: chomp\n");
@@ -668,6 +680,11 @@ static int _parse_options(const char *key, char *value,
config->logfile = strdup(value);
pm_printf(ALPM_LOG_DEBUG, "config: logfile: %s\n", value);
}
+ } else if(strcmp(key, "SandboxUser") == 0) {
+ if(!config->sandboxuser) {
+ config->sandboxuser = strdup(value);
+ pm_printf(ALPM_LOG_DEBUG, "config: sandboxuser: %s\n", value);
+ }
} else if(strcmp(key, "XferCommand") == 0) {
char **c;
if((config->xfercommand_argv = wordsplit(value)) == NULL) {
@@ -904,6 +921,8 @@ static int setup_libalpm(void)
alpm_option_set_architectures(handle, config->architectures);
alpm_option_set_checkspace(handle, config->checkspace);
alpm_option_set_usesyslog(handle, config->usesyslog);
+ alpm_option_set_usesandbox(handle, config->usesandbox);
+ alpm_option_set_sandboxuser(handle, config->sandboxuser);
alpm_option_set_ignorepkgs(handle, config->ignorepkg);
alpm_option_set_ignoregroups(handle, config->ignoregrp);
diff --git src/pacman/conf.h src/pacman/conf.h
index 04350d39..8fced284 100644
--- src/pacman/conf.h
+++ src/pacman/conf.h
@@ -55,6 +55,7 @@ typedef struct __config_t {
unsigned short print;
unsigned short checkspace;
unsigned short usesyslog;
+ unsigned short usesandbox;
unsigned short color;
unsigned short disable_dl_timeout;
char *print_format;
@@ -67,6 +68,7 @@ typedef struct __config_t {
char *logfile;
char *gpgdir;
char *sysroot;
+ char *sandboxuser;
alpm_list_t *hookdirs;
alpm_list_t *cachedirs;
alpm_list_t *architectures;
diff --git src/pacman/pacman-conf.c src/pacman/pacman-conf.c
index 600f1622..6da27937 100644
--- src/pacman/pacman-conf.c
+++ src/pacman/pacman-conf.c
@@ -251,6 +251,7 @@ static void dump_config(void)
show_list_str("HookDir", config->hookdirs);
show_str("GPGDir", config->gpgdir);
show_str("LogFile", config->logfile);
+ show_str("SandboxUser", config->sandboxuser);
show_list_str("HoldPkg", config->holdpkg);
show_list_str("IgnorePkg", config->ignorepkg);
@@ -268,6 +269,7 @@ static void dump_config(void)
show_bool("DisableDownloadTimeout", config->disable_dl_timeout);
show_bool("ILoveCandy", config->chomp);
show_bool("NoProgressBar", config->noprogressbar);
+ show_bool("UseSandbox", config->usesandbox);
show_int("ParallelDownloads", config->parallel_downloads);
@@ -349,6 +351,8 @@ static int list_directives(void)
show_str("GPGDir", config->gpgdir);
} else if(strcasecmp(i->data, "LogFile") == 0) {
show_str("LogFile", config->logfile);
+ } else if(strcasecmp(i->data, "SandboxUser") == 0) {
+ show_str("SandboxUser", config->sandboxuser);
} else if(strcasecmp(i->data, "HoldPkg") == 0) {
show_list_str("HoldPkg", config->holdpkg);
@@ -369,6 +373,8 @@ static int list_directives(void)
} else if(strcasecmp(i->data, "UseSyslog") == 0) {
show_bool("UseSyslog", config->usesyslog);
+ } else if(strcasecmp(i->data, "UseSandbox") == 0) {
+ show_bool("UseSandbox", config->usesandbox);
} else if(strcasecmp(i->data, "Color") == 0) {
show_bool("Color", config->color);
} else if(strcasecmp(i->data, "CheckSpace") == 0) {
--
2.33.0
More information about the pacman-dev
mailing list