[pacman-dev] [PATCHv2] Add optional 'SandboxUser' option to drop privileges before downloading files
Andrew Gregory
andrew.gregory.8 at gmail.com
Mon Sep 6 00:42:26 UTC 2021
On 09/05/21 at 02:42pm, Remi Gacogne wrote:
> This second version contains only the portable part of the sandboxing,
> switching to a different user when a new option, 'SandboxUser', is set.
>
> To sum up the changes from the last version:
> - The non-portable parts (preventing new privileges from being gained, dropping
> capabilities, syscalls filtering and filesystem protection) are gone for now,
> I'll propose them via smaller patches if this change is accepted.
> - The value returned by the sub-process is now passed by the exit status only,
> instead of using a pipe.
> - The 'UseSandbox' option has been removed.
> - The alpm_sandbox.{c,h} files have been renamed to sandbox.{c,h}.
> - A short description of the 'SandboxUser' option has been added to
> pacman.conf.5.
>
> Cheers,
>
> Remi
>
> Signed-off-by: Remi Gacogne <rgacogne at archlinux.org>
> ---
Put notes here to avoid including them in the commit message.
> doc/pacman.conf.5.asciidoc | 5 ++
> lib/libalpm/alpm.h | 5 ++
> lib/libalpm/dload.c | 102 ++++++++++++++++++++++++++++++++++++-
> lib/libalpm/handle.c | 13 +++++
> lib/libalpm/handle.h | 1 +
> lib/libalpm/meson.build | 1 +
> lib/libalpm/sandbox.c | 63 +++++++++++++++++++++++
> lib/libalpm/sandbox.h | 31 +++++++++++
> src/pacman/conf.c | 19 ++++++-
> src/pacman/conf.h | 1 +
> src/pacman/pacman-conf.c | 3 ++
> 11 files changed, 241 insertions(+), 3 deletions(-)
> create mode 100644 lib/libalpm/sandbox.c
> create mode 100644 lib/libalpm/sandbox.h
...
> +/* Download the requested files by launching a process inside a sandbox.
> + * 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
> + */
> +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;
> + sigset_t oldblock;
> + struct sigaction sa_ign = { .sa_handler = SIG_IGN }, oldint, oldquit;
> +
> + 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) {
> + /* 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 */
> + if(ret == 0) {
> + /* a payload was actually downloaded */
> + _Exit(0);
> + }
> + else if(ret == 1) {
> + /* no files were downloaded and all errors were non-fatal */
> + _Exit(1);
> + }
> + else {
> + /* an error happened for a required file */
> + _Exit(2);
> + }
> + }
> +
> + /* parent */
> +
> + if(pid != -1) {
> + int wret;
> + while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR);
> + if(wret > 0) {
> + if(!WIFEXITED(ret)) {
> + /* the child did not terminate normally */
> + ret = -1;
> + }
> + else {
> + ret = WIFEXITED(ret);
> + if(ret != 0 && ret != 1) {
> + /* an error happened for a required file, or unexpected exit status */
> + ret = -1;
> + }
> + }
> + }
> + else {
> + /* waitpid failed */
> + err = errno;
> + }
> + } else {
> + /* fork failed, make sure errno is preserved after cleanup */
> + err = errno;
> + }
> +
> + sigaction(SIGINT, &oldint, NULL);
> + sigaction(SIGQUIT, &oldquit, NULL);
> + sigprocmask(SIG_SETMASK, &oldblock, NULL);
> +
> + if(err) {
> + errno = err;
> + ret = -1;
> + }
> + return ret;
> +}
> +
After thinking about this some more, I think this is far too simple. Just
running download_internal in an unprivileged fork will break anything that
relies on side effects. download_internal sets pm_errno, tracks server errors,
and calls a number of front-end callbacks. Losing server error tracking across
multiple downloads isn't a big deal, but losing pm_errno is significant and we
have no way of knowing what kind of state changes the front-end callbacks might
be making. I suspect this would massively break GUI front-ends.
apg
More information about the pacman-dev
mailing list