Skip to content
Snippets Groups Projects
Commit 93a796aa authored by Remi Gacogne's avatar Remi Gacogne Committed by Allan McRae
Browse files

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: default avatarRemi Gacogne <rgacogne@archlinux.org>
Signed-off-by: default avatarAllan McRae <allan@archlinux.org>
parent 9667bc6b
No related branches found
No related tags found
1 merge request!23Add SandboxUser configuration option
......@@ -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
......
......@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment