From 93a796aa27b9128490ac9aff57d3372b1a81cb6b Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 9 Nov 2022 11:06:49 +1000 Subject: [PATCH] Add sandboxed download for the internal downloader If the SandboxUser configure option is set, the internal downloader will fork of a child process and drop to the specified user to download the files. Signed-off-by: Remi Gacogne Signed-off-by: Allan McRae --- lib/libalpm/dload.c | 160 +++++++++++++++++++++++++++++++++++++++++- src/pacman/callback.c | 9 ++- 2 files changed, 165 insertions(+), 4 deletions(-) diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c index f2fa1a54..ee5f8d81 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #ifdef HAVE_NETINET_IN_H @@ -48,6 +49,7 @@ #include "log.h" #include "util.h" #include "handle.h" +#include "sandbox.h" #ifdef HAVE_LIBCURL @@ -972,6 +974,158 @@ static int curl_download_internal(alpm_handle_t *handle, return ret; } +/* 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, callbacks_fd[2]; + sigset_t oldblock; + struct sigaction sa_ign, oldint, oldquit; + _alpm_sandbox_callback_context callbacks_ctx; + + sigemptyset(&sa_ign.sa_mask); + sa_ign.sa_handler = SIG_IGN; + sa_ign.sa_flags=0; + + if(pipe(callbacks_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(); + if(pid == -1) { + /* fork failed, make sure errno is preserved after cleanup */ + err = errno; + } + + /* child */ + if(pid == 0) { + close(callbacks_fd[0]); + fcntl(callbacks_fd[1], F_SETFD, FD_CLOEXEC); + callbacks_ctx.callback_pipe = callbacks_fd[1]; + alpm_option_set_logcb(handle, _alpm_sandbox_cb_log, &callbacks_ctx); + alpm_option_set_dlcb(handle, _alpm_sandbox_cb_dl, &callbacks_ctx); + alpm_option_set_fetchcb(handle, NULL, NULL); + alpm_option_set_eventcb(handle, NULL, NULL); + alpm_option_set_questioncb(handle, NULL, NULL); + alpm_option_set_progresscb(handle, NULL, NULL); + + /* restore default signal handling in the child */ + _alpm_reset_signals(); + + /* cwd to the download directory */ + ret = chdir(localpath); + if(ret != 0) { + handle->pm_errno = ALPM_ERR_NOT_A_DIR; + _alpm_log(handle, ALPM_LOG_ERROR, _("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_ERROR, _("switching to sandbox user '%s' failed!\n"), handle->sandboxuser); + _Exit(2); + } + + 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 */ + close(callbacks_fd[1]); + + if(pid != -1) { + bool had_error = false; + while(true) { + _alpm_sandbox_callback_t callback_type; + ssize_t got = read(callbacks_fd[0], &callback_type, sizeof(callback_type)); + if(got < 0 || (size_t)got != sizeof(callback_type)) { + had_error = true; + break; + } + + if(callback_type == ALPM_SANDBOX_CB_DOWNLOAD) { + if(!_alpm_sandbox_process_cb_download(handle, callbacks_fd[0])) { + had_error = true; + break; + } + } + else if(callback_type == ALPM_SANDBOX_CB_LOG) { + if(!_alpm_sandbox_process_cb_log(handle, callbacks_fd[0])) { + had_error = true; + break; + } + } + } + + + if(had_error) { + kill(pid, SIGTERM); + } + + 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 = WEXITSTATUS(ret); + if(ret != 0) { + if(ret == 2) { + /* an error happened for a required file, or unexpected exit status */ + handle->pm_errno = ALPM_ERR_RETRIEVE; + ret = -1; + } + else { + handle->pm_errno = ALPM_ERR_RETRIEVE; + ret = 1; + } + } + } + } + else { + /* waitpid failed */ + err = errno; + } + } + + close(callbacks_fd[0]); + + sigaction(SIGINT, &oldint, NULL); + sigaction(SIGQUIT, &oldquit, NULL); + sigprocmask(SIG_SETMASK, &oldblock, NULL); + + if(err) { + errno = err; + ret = -1; + } + return ret; +} + #endif static int payload_download_fetchcb(struct dload_payload *payload, @@ -1001,7 +1155,11 @@ int _alpm_download(alpm_handle_t *handle, { if(handle->fetchcb == NULL) { #ifdef HAVE_LIBCURL - return curl_download_internal(handle, payloads, localpath); + if(handle->sandboxuser) { + 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 a/src/pacman/callback.c b/src/pacman/callback.c index 27a18a4f..05604a90 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -57,7 +57,7 @@ static alpm_list_t *output = NULL; #endif struct pacman_progress_bar { - const char *filename; + char *filename; off_t xfered; /* Current amount of transferred data */ off_t total_size; size_t downloaded; @@ -752,7 +752,8 @@ static void init_total_progressbar(void) { totalbar = calloc(1, sizeof(struct pacman_progress_bar)); assert(totalbar); - totalbar->filename = _("Total"); + totalbar->filename = strdup(_("Total")); + assert(totalbar->filename); totalbar->init_time = get_time_ms(); totalbar->total_size = list_total; totalbar->howmany = list_total_pkgs; @@ -889,7 +890,8 @@ static void dload_init_event(const char *filename, alpm_download_event_init_t *d struct pacman_progress_bar *bar = calloc(1, sizeof(struct pacman_progress_bar)); assert(bar); - bar->filename = filename; + bar->filename = strdup(filename); + assert(bar->filename); bar->init_time = get_time_ms(); bar->rate = 0.0; multibar_ui.active_downloads = alpm_list_add(multibar_ui.active_downloads, bar); @@ -1094,6 +1096,7 @@ static void dload_complete_event(const char *filename, alpm_download_event_compl multibar_ui.active_downloads = alpm_list_remove_item( multibar_ui.active_downloads, head); free(head); + free(j->filename); free(j); } else { break;