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/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
|
||||
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;
|
||||
|
|
Loading…
Add table
Reference in a new issue