[pacman-dev] [PATCH] Add optional sandboxing when downloading files
Andrew Gregory
andrew.gregory.8 at gmail.com
Fri Sep 3 01:58:50 UTC 2021
On 08/30/21 at 11:37am, Remi Gacogne wrote:
> [re-sending because my initial e-mail was rejected by spam filters, sorry!]
>
> 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
This is a lot. Let's focus on the portable user switching first; if that gets
merged we can look at adding the extra Linux-specific stuff.
> + 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;
> + }
This is just returning an int, is there a need to pass it by pipe instead of
just using the _Exit value?
I'm also wondering if we could simplify the configuration. We could infer that
a sandbox should be used if a sandbox user is set or infer the user from the
cache folder being downloaded to.
apg
More information about the pacman-dev
mailing list