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:
parent
9667bc6b12
commit
93a796aa27
2 changed files with 165 additions and 4 deletions
|
@ -28,6 +28,7 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#ifdef HAVE_NETINET_IN_H
|
#ifdef HAVE_NETINET_IN_H
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "handle.h"
|
#include "handle.h"
|
||||||
|
#include "sandbox.h"
|
||||||
|
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
|
|
||||||
|
@ -972,6 +974,158 @@ static int curl_download_internal(alpm_handle_t *handle,
|
||||||
return ret;
|
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
|
#endif
|
||||||
|
|
||||||
static int payload_download_fetchcb(struct dload_payload *payload,
|
static int payload_download_fetchcb(struct dload_payload *payload,
|
||||||
|
@ -1001,7 +1155,11 @@ int _alpm_download(alpm_handle_t *handle,
|
||||||
{
|
{
|
||||||
if(handle->fetchcb == NULL) {
|
if(handle->fetchcb == NULL) {
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
|
if(handle->sandboxuser) {
|
||||||
|
return curl_download_internal_sandboxed(handle, payloads, localpath);
|
||||||
|
} else {
|
||||||
return curl_download_internal(handle, payloads, localpath);
|
return curl_download_internal(handle, payloads, localpath);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
|
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -57,7 +57,7 @@ static alpm_list_t *output = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct pacman_progress_bar {
|
struct pacman_progress_bar {
|
||||||
const char *filename;
|
char *filename;
|
||||||
off_t xfered; /* Current amount of transferred data */
|
off_t xfered; /* Current amount of transferred data */
|
||||||
off_t total_size;
|
off_t total_size;
|
||||||
size_t downloaded;
|
size_t downloaded;
|
||||||
|
@ -752,7 +752,8 @@ static void init_total_progressbar(void)
|
||||||
{
|
{
|
||||||
totalbar = calloc(1, sizeof(struct pacman_progress_bar));
|
totalbar = calloc(1, sizeof(struct pacman_progress_bar));
|
||||||
assert(totalbar);
|
assert(totalbar);
|
||||||
totalbar->filename = _("Total");
|
totalbar->filename = strdup(_("Total"));
|
||||||
|
assert(totalbar->filename);
|
||||||
totalbar->init_time = get_time_ms();
|
totalbar->init_time = get_time_ms();
|
||||||
totalbar->total_size = list_total;
|
totalbar->total_size = list_total;
|
||||||
totalbar->howmany = list_total_pkgs;
|
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));
|
struct pacman_progress_bar *bar = calloc(1, sizeof(struct pacman_progress_bar));
|
||||||
assert(bar);
|
assert(bar);
|
||||||
bar->filename = filename;
|
bar->filename = strdup(filename);
|
||||||
|
assert(bar->filename);
|
||||||
bar->init_time = get_time_ms();
|
bar->init_time = get_time_ms();
|
||||||
bar->rate = 0.0;
|
bar->rate = 0.0;
|
||||||
multibar_ui.active_downloads = alpm_list_add(multibar_ui.active_downloads, bar);
|
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 = alpm_list_remove_item(
|
||||||
multibar_ui.active_downloads, head);
|
multibar_ui.active_downloads, head);
|
||||||
free(head);
|
free(head);
|
||||||
|
free(j->filename);
|
||||||
free(j);
|
free(j);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue