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 <rgacogne@archlinux.org>
Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
Remi Gacogne 2022-11-09 11:06:49 +10:00 committed by Allan McRae
parent 9667bc6b12
commit 93a796aa27
2 changed files with 165 additions and 4 deletions

View file

@ -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
@ -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

View file

@ -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;