Compare commits

...

14 Commits

Author SHA1 Message Date
Allan McRae
56c3b396df Restrict use of DownloadUser to during download
This removes the requirement for the DownloadUser to own DBPath/sync and
CacheDir.  Instead, files to be downloaded to are touched before switching
user, and then moved into place at the end of downloading.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 18:35:06 +10:00
Remi Gacogne
4041870a43 Fixup: Return a proper error code when failing to sandbox the downloader 2024-02-08 17:04:25 +10:00
Remi Gacogne
cd585e94db Fixup: Fix the return value from curl_download_internal_sandboxed() 2024-02-08 17:04:25 +10:00
Andrew Gregory
3c9d5f8192 pactest: test database downloads with optional signatures
Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Andrew Gregory
79783695e0 pactest: Add basic sandboxed download tests
Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Remi Gacogne
7953c45e16 Stop trusting the Content-Disposition HTTP header 2024-02-08 17:04:25 +10:00
Allan McRae
044c13d825 Remove random_partfile from payload struct
It is not used any more due to filling the payload structure earlier.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Allan McRae
8f4f7a2524 libalpm: fill in more payload information before passing to downloader
Filling in more of the payload fields before passing to the downloader ensures
that the these fields do not get lost during sandboxed operations. It also
fixes the use of -U with XferCommand.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Remi Gacogne
6cbdc74f31 Document DownloadUser
Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Remi Gacogne
95b7c5a613 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: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Allan McRae
f7ec77fa74 Expose _alpm_reset_signals() to library
Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Remi Gacogne
76b57a16f0 Add callbacks for sandboxed operations
Add log and download callbacks to use within a sandbox.  These are
designed to be passed from the sandbox to the parent through a file
descriptor and then processed into alpm callbacks to be passed to the
frontend.

Note, only callbacks used in libalpm are added. Other callbacks should
be set to NULL in the child process.
2024-02-08 17:04:25 +10:00
Remi Gacogne
29a4a22c70 Provide function for switching user in child processes
Add alpm_sandbox_child() function that will be used for switching to a
less priviledged user to run child processes.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
Remi Gacogne
e421e01326 Add DownloadUser configuration option
The DownloadUser option will be used to drop privledges to the
specified user when downloading files.

The intention is for this to be extended in the future  to a more
general sandbox configuration to cover operating on package and
database files prior to verification.

Add this option to pacman configuration and the various accessors into
the libalpm backend.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-08 17:04:25 +10:00
24 changed files with 819 additions and 257 deletions

View File

@@ -207,6 +207,10 @@ Options
positive integer. If this config option is not set then only one download
stream is used (i.e. downloads happen sequentially).
*DownloadUser =* username::
Specifies the user to switch to for downloading files. If this config
option is not set then the downloads are done as the user running pacman.
Repository Sections
-------------------

View File

@@ -1870,6 +1870,28 @@ int alpm_option_set_gpgdir(alpm_handle_t *handle, const char *gpgdir);
/** @} */
/** @name Accessors for use sandboxuser
*
* This controls the user that libalpm will use for sensitive operations like
* downloading files.
* @{
*/
/** Returns the user to switch to for sensitive operations.
* @return the user name
*/
const char *alpm_option_get_sandboxuser(alpm_handle_t *handle);
/** Sets the user to switch to for sensitive operations.
* @param handle the context handle
* @param sandboxuser the user to set
*/
int alpm_option_set_sandboxuser(alpm_handle_t *handle, const char *sandboxuser);
/* End of sandboxuser accessors */
/** @} */
/** @name Accessors for use syslog
*
* This controls whether libalpm will also use the syslog. Even if this option
@@ -2929,6 +2951,12 @@ const char *alpm_version(void);
* */
int alpm_capabilities(void);
/** Drop privileges by switching to a different user.
* @param sandboxuser the user to switch to
* @return 0 on success, -1 on failure
*/
int alpm_sandbox_setup_child(const char *sandboxuser);
/* End of libalpm_misc */
/** @} */

View File

@@ -189,6 +189,18 @@ int SYMEXPORT alpm_db_update(alpm_handle_t *handle, alpm_list_t *dbs, int force)
MALLOC(payload->filepath, len,
FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
snprintf(payload->filepath, len, "%s%s", db->treename, dbext);
STRDUP(payload->remote_name, payload->filepath,
_alpm_dload_payload_reset(payload); FREE(payload);
GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
payload->destfile_name = _alpm_get_fullpath(syncpath, payload->remote_name, "");
payload->tempfile_name = _alpm_get_fullpath(syncpath, payload->remote_name, ".part");
if(!payload->destfile_name || !payload->tempfile_name) {
_alpm_dload_payload_reset(payload);
FREE(payload);
GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup);
}
payload->handle = handle;
payload->force = dbforce;
payload->unlink_on_fail = 1;

View File

@@ -28,7 +28,9 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <pwd.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> /* IPPROTO_TCP */
@@ -48,6 +50,145 @@
#include "log.h"
#include "util.h"
#include "handle.h"
#include "sandbox.h"
static const char *get_filename(const char *url)
{
char *filename = strrchr(url, '/');
if(filename != NULL) {
return filename + 1;
}
/* no slash found, it's a filename */
return url;
}
/* prefix to avoid possible future clash with getumask(3) */
static mode_t _getumask(void)
{
mode_t mask = umask(0);
umask(mask);
return mask;
}
static int touch_and_own_file(const char *filename, const char *user)
{
int fd;
struct passwd const *pw = NULL;
ASSERT(filename != NULL, return -1);
ASSERT(user != NULL, return -1);
ASSERT((fd = open(filename, O_CREAT | O_WRONLY, 0644)) != -1, return -1);
close(fd);
ASSERT((pw = getpwnam(user)) != NULL, return -1);
ASSERT(chown(filename, pw->pw_uid, pw->pw_gid) != -1, return -1);
return 0;
}
static int intialize_download_file(struct dload_payload *p)
{
int ret = 0;
ret = touch_and_own_file(p->tempfile_name, p->handle->sandboxuser);
if(ret == 0 && p->download_signature) {
int len = strlen(p->destfile_name) + strlen(".sig") + strlen(".part") + 1;
char * sig;
MALLOC(sig, len, FREE(sig); RET_ERR(p->handle, ALPM_ERR_MEMORY, -1));
snprintf(sig, len, "%s.sig.part", p->destfile_name);
ret = touch_and_own_file(sig, p->handle->sandboxuser);
free(sig);
}
return ret;
}
static int unlink_or_move(const char *file, const char* dest) {
struct stat st;
ASSERT(stat(file, &st) == 0, return -1);
if(st.st_size == 0) {
unlink(file);
} else {
ASSERT(chown(file, 0, 0) != -1, return -1);
if(rename(file, dest)) {
return -1;
}
}
return 0;
}
static int finalize_download_file(struct dload_payload *p)
{
int ret = 0;
ret = unlink_or_move(p->tempfile_name, p->destfile_name);
if(ret == -1) {
_alpm_log(p->handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
p->tempfile_name, p->destfile_name, strerror(errno));
return -1;
}
if(p->download_signature) {
char *sig_temp, *sig_dest;
int len = strlen(p->destfile_name) + strlen(".sig") + 1;
MALLOC(sig_dest, len, RET_ERR(p->handle, ALPM_ERR_MEMORY, -1));
snprintf(sig_dest, len, "%s.sig", p->destfile_name);
len = len + 5;
MALLOC(sig_temp, len, FREE(sig_dest); RET_ERR(p->handle, ALPM_ERR_MEMORY, -1));
snprintf(sig_temp, len, "%s.sig.part", p->destfile_name);
ret = unlink_or_move(sig_temp, sig_dest);
if(ret == -1) {
_alpm_log(p->handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
sig_temp, sig_dest, strerror(errno));
}
free(sig_dest);
free(sig_temp);
}
return ret;
}
static FILE *create_tempfile(struct dload_payload *payload, const char *localpath)
{
int fd;
FILE *fp;
char *randpath;
size_t len;
/* create a random filename, which is opened with O_EXCL */
len = strlen(localpath) + 14 + 1;
MALLOC(randpath, len, RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
snprintf(randpath, len, "%salpmtmp.XXXXXX", localpath);
if((fd = mkstemp(randpath)) == -1 ||
fchmod(fd, ~(_getumask()) & 0666) ||
!(fp = fdopen(fd, payload->tempfile_openmode))) {
unlink(randpath);
close(fd);
_alpm_log(payload->handle, ALPM_LOG_ERROR,
_("failed to create temporary file for download\n"));
free(randpath);
return NULL;
}
/* fp now points to our alpmtmp.XXXXXX */
free(payload->tempfile_name);
payload->tempfile_name = randpath;
free(payload->remote_name);
STRDUP(payload->remote_name, strrchr(randpath, '/') + 1,
fclose(fp); RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
return fp;
}
#ifdef HAVE_LIBCURL
@@ -55,7 +196,7 @@
#define HOSTNAME_SIZE 256
static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
struct dload_payload *payload, const char *localpath);
struct dload_payload *payload);
static int curl_gethost(const char *url, char *buffer, size_t buf_len);
/* number of "soft" errors required to blacklist a server, set to 0 to disable
@@ -173,29 +314,6 @@ static const char *payload_next_server(struct dload_payload *payload)
return NULL;
}
static const char *get_filename(const char *url)
{
char *filename = strrchr(url, '/');
if(filename != NULL) {
return filename + 1;
}
/* no slash found, it's a filename */
return url;
}
static char *get_fullpath(const char *path, const char *filename,
const char *suffix)
{
char *filepath;
/* len = localpath len + filename len + suffix len + null */
size_t len = strlen(path) + strlen(filename) + strlen(suffix) + 1;
MALLOC(filepath, len, return NULL);
snprintf(filepath, len, "%s%s%s", path, filename, suffix);
return filepath;
}
enum {
ABORT_OVER_MAXFILESIZE = 1,
};
@@ -306,45 +424,12 @@ static int utimes_long(const char *path, long seconds)
return 0;
}
/* prefix to avoid possible future clash with getumask(3) */
static mode_t _getumask(void)
{
mode_t mask = umask(0);
umask(mask);
return mask;
}
static size_t dload_parseheader_cb(void *ptr, size_t size, size_t nmemb, void *user)
{
size_t realsize = size * nmemb;
const char *fptr, *endptr = NULL;
const char * const cd_header = "Content-Disposition:";
const char * const fn_key = "filename=";
struct dload_payload *payload = (struct dload_payload *)user;
long respcode;
if(_alpm_raw_ncmp(cd_header, ptr, strlen(cd_header)) == 0) {
if((fptr = strstr(ptr, fn_key))) {
fptr += strlen(fn_key);
/* find the end of the field, which is either a semi-colon, or the end of
* the data. As per curl_easy_setopt(3), we cannot count on headers being
* null terminated, so we look for the closing \r\n */
endptr = fptr + strcspn(fptr, ";\r\n") - 1;
/* remove quotes */
if(*fptr == '"' && *endptr == '"') {
fptr++;
endptr--;
}
/* avoid information leakage with badly formed headers */
if(endptr > fptr) {
STRNDUP(payload->content_disp_name, fptr, endptr - fptr + 1,
RET_ERR(payload->handle, ALPM_ERR_MEMORY, realsize));
}
}
}
(void) ptr;
curl_easy_getinfo(payload->curl, CURLINFO_RESPONSE_CODE, &respcode);
if(payload->respcode != respcode) {
@@ -418,37 +503,6 @@ static void curl_set_handle_opts(CURL *curl, struct dload_payload *payload)
}
}
static FILE *create_tempfile(struct dload_payload *payload, const char *localpath)
{
int fd;
FILE *fp;
char *randpath;
size_t len;
/* create a random filename, which is opened with O_EXCL */
len = strlen(localpath) + 14 + 1;
MALLOC(randpath, len, RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
snprintf(randpath, len, "%salpmtmp.XXXXXX", localpath);
if((fd = mkstemp(randpath)) == -1 ||
fchmod(fd, ~(_getumask()) & 0666) ||
!(fp = fdopen(fd, payload->tempfile_openmode))) {
unlink(randpath);
close(fd);
_alpm_log(payload->handle, ALPM_LOG_ERROR,
_("failed to create temporary file for download\n"));
free(randpath);
return NULL;
}
/* fp now points to our alpmtmp.XXXXXX */
free(payload->tempfile_name);
payload->tempfile_name = randpath;
free(payload->remote_name);
STRDUP(payload->remote_name, strrchr(randpath, '/') + 1,
fclose(fp); RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
return fp;
}
/* Return 0 if retry was successful, -1 otherwise */
static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload *payload)
{
@@ -512,7 +566,7 @@ static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload
* Returns -2 if an error happened for an optional file
*/
static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CURLMsg *msg,
const char *localpath, int *active_downloads_num)
int *active_downloads_num)
{
struct dload_payload *payload = NULL;
CURL *curl = msg->easy_handle;
@@ -626,31 +680,6 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &timecond);
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
if(payload->trust_remote_name) {
if(payload->content_disp_name) {
/* content-disposition header has a better name for our file */
free(payload->destfile_name);
payload->destfile_name = get_fullpath(localpath,
get_filename(payload->content_disp_name), "");
} else {
const char *effective_filename = strrchr(effective_url, '/');
if(effective_filename && strlen(effective_filename) > 2) {
effective_filename++;
/* if destfile was never set, we wrote to a tempfile. even if destfile is
* set, we may have followed some redirects and the effective url may
* have a better suggestion as to what to name our file. in either case,
* refactor destfile to this newly derived name. */
if(!payload->destfile_name || strcmp(effective_filename,
strrchr(payload->destfile_name, '/') + 1) != 0) {
free(payload->destfile_name);
payload->destfile_name = get_fullpath(localpath, effective_filename, "");
}
}
}
}
/* Let's check if client requested downloading accompanion *.sig file */
if(!payload->signature && payload->download_signature && curlerr == CURLE_OK && payload->respcode < 400) {
struct dload_payload *sig = NULL;
@@ -682,22 +711,23 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
MALLOC(sig->fileurl, len, FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
snprintf(sig->fileurl, len, "%s.sig", url);
if(payload->trust_remote_name) {
/* In this case server might provide a new name for the main payload.
* Choose *.sig filename based on this new name.
*/
const char *final_file = get_filename(realname);
int remote_name_len = strlen(final_file) + 5;
MALLOC(sig->remote_name, remote_name_len, FREE(sig->fileurl); FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
snprintf(sig->remote_name, remote_name_len, "%s.sig", final_file);
}
int remote_name_len = strlen(payload->remote_name) + 5;
MALLOC(sig->remote_name, remote_name_len, _alpm_dload_payload_reset(sig);
FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
snprintf(sig->remote_name, remote_name_len, "%s.sig", payload->remote_name);
/* force the filename to be realname + ".sig" */
int destfile_name_len = strlen(realname) + 5;
MALLOC(sig->destfile_name, destfile_name_len, FREE(sig->remote_name);
FREE(sig->fileurl); FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
MALLOC(sig->destfile_name, destfile_name_len, _alpm_dload_payload_reset(sig);
FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
snprintf(sig->destfile_name, destfile_name_len, "%s.sig", realname);
int tempfile_name_len = strlen(realname) + 10;
MALLOC(sig->tempfile_name, tempfile_name_len, _alpm_dload_payload_reset(sig);
FREE(sig); GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
snprintf(sig->tempfile_name, tempfile_name_len, "%s.sig.part", realname);
sig->signature = 1;
sig->handle = handle;
sig->force = payload->force;
@@ -706,7 +736,7 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
/* set hard upper limit of 16KiB */
sig->max_size = 16 * 1024;
curl_add_payload(handle, curlm, sig, localpath);
curl_add_payload(handle, curlm, sig);
(*active_downloads_num)++;
}
@@ -745,7 +775,7 @@ cleanup:
}
if(ret == 0) {
if(payload->destfile_name) {
if(payload->destfile_name && handle->sandboxuser == NULL) {
if(rename(payload->tempfile_name, payload->destfile_name)) {
_alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
payload->tempfile_name, payload->destfile_name, strerror(errno));
@@ -789,7 +819,7 @@ cleanup:
* Returns -1 if am error happened while starting a new download
*/
static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
struct dload_payload *payload, const char *localpath)
struct dload_payload *payload)
{
size_t len;
CURL *curl = NULL;
@@ -815,35 +845,11 @@ static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
}
payload->tempfile_openmode = "wb";
if(!payload->remote_name) {
STRDUP(payload->remote_name, get_filename(payload->fileurl),
GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
}
if(curl_gethost(payload->fileurl, hostname, sizeof(hostname)) != 0) {
_alpm_log(handle, ALPM_LOG_ERROR, _("url '%s' is invalid\n"), payload->fileurl);
GOTO_ERR(handle, ALPM_ERR_SERVER_BAD_URL, cleanup);
}
if(!payload->random_partfile && payload->remote_name && strlen(payload->remote_name) > 0) {
if(!payload->destfile_name) {
payload->destfile_name = get_fullpath(localpath, payload->remote_name, "");
}
payload->tempfile_name = get_fullpath(localpath, payload->remote_name, ".part");
if(!payload->destfile_name || !payload->tempfile_name) {
goto cleanup;
}
} else {
/* We want a random filename or the URL does not contain a filename, so download to a
* temporary location. We can not support resuming this kind of download; any partial
* transfers will be destroyed */
payload->unlink_on_fail = 1;
payload->localf = create_tempfile(payload, localpath);
if(payload->localf == NULL) {
goto cleanup;
}
}
curl_set_handle_opts(curl, payload);
if(payload->max_size == payload->initial_size && payload->max_size != 0) {
@@ -901,8 +907,7 @@ static int compare_dload_payload_sizes(const void *left_ptr, const void *right_p
* Returns 1 if no files were downloaded and all errors were non-fatal
*/
static int curl_download_internal(alpm_handle_t *handle,
alpm_list_t *payloads /* struct dload_payload */,
const char *localpath)
alpm_list_t *payloads /* struct dload_payload */)
{
int active_downloads_num = 0;
int err = 0;
@@ -920,7 +925,7 @@ static int curl_download_internal(alpm_handle_t *handle,
for(; active_downloads_num < max_streams && payloads; active_downloads_num++) {
struct dload_payload *payload = payloads->data;
if(curl_add_payload(handle, curlm, payload, localpath) == 0) {
if(curl_add_payload(handle, curlm, payload) == 0) {
payloads = payloads->next;
} else {
/* The payload failed to start. Do not start any new downloads.
@@ -951,7 +956,7 @@ static int curl_download_internal(alpm_handle_t *handle,
}
if(msg->msg == CURLMSG_DONE) {
int ret = curl_check_finished_download(handle, curlm, msg,
localpath, &active_downloads_num);
&active_downloads_num);
if(ret == -1) {
/* if current payload failed to download then stop adding new payloads but wait for the
* current ones
@@ -967,8 +972,159 @@ static int curl_download_internal(alpm_handle_t *handle,
}
}
int ret = err ? -1 : updated ? 0 : 1;
_alpm_log(handle, ALPM_LOG_DEBUG, "curl_download_internal return code is %d\n", ret);
_alpm_log(handle, ALPM_LOG_DEBUG, "curl_download_internal return code is %d\n", err);
return err ? -1 : updated ? 0 : 1;
}
/* 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 = errno;
_alpm_log(handle, ALPM_LOG_ERROR, _("could not chdir to download directory %s\n"), localpath);
ret = -1;
} else {
ret = alpm_sandbox_setup_child(handle->sandboxuser);
if (ret != 0) {
_alpm_log(handle, ALPM_LOG_ERROR, _("switching to sandbox user '%s' failed!\n"), handle->sandboxuser);
_Exit(ret | 128);
}
ret = curl_download_internal(handle, payloads);
}
/* 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(handle->pm_errno);
}
else {
/* an error happened for a required file */
_Exit(handle->pm_errno | 128);
}
}
/* 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 & 128) {
/* an error happened for a required file, or unexpected exit status */
handle->pm_errno = ret & ~128;
ret = -1;
}
else {
handle->pm_errno = ret;
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;
}
@@ -1001,7 +1157,26 @@ int _alpm_download(alpm_handle_t *handle,
{
if(handle->fetchcb == NULL) {
#ifdef HAVE_LIBCURL
return curl_download_internal(handle, payloads, localpath);
if(handle->sandboxuser) {
int ret = 0;
alpm_list_t *i;
for(i = payloads; i; i = i->next) {
struct dload_payload *p = i->data;
intialize_download_file(p);
}
ret = curl_download_internal_sandboxed(handle, payloads, localpath);
for(i = payloads; i; i = i->next) {
struct dload_payload *p = i->data;
finalize_download_file(p);
}
return ret;
} else {
return curl_download_internal(handle, payloads);
}
#else
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
#endif
@@ -1080,16 +1255,34 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
CALLOC(payload, 1, sizeof(*payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
STRDUP(payload->fileurl, url, FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
STRDUP(payload->remote_name, get_filename(payload->fileurl),
GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
c = strrchr(url, '/');
if(strstr(c, ".pkg")) {
if(payload->remote_name && strlen(payload->remote_name) > 0 && strstr(c, ".pkg")) {
/* we probably have a usable package filename to download to */
payload->destfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, "");
payload->tempfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, ".part");
payload->allow_resume = 1;
if(!payload->destfile_name || !payload->tempfile_name) {
goto err;
}
} else {
payload->random_partfile = 1;
/* The URL does not contain a filename, so download to a temporary location.
* We can not support resuming this kind of download; any partial transfers
* will be destroyed */
payload->unlink_on_fail = 1;
payload->tempfile_openmode = "wb";
payload->localf = create_tempfile(payload, cachedir);
if(payload->localf == NULL) {
goto err;
}
}
payload->handle = handle;
payload->trust_remote_name = 1;
payload->download_signature = (handle->siglevel & ALPM_SIG_PACKAGE);
payload->signature_optional = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL);
payloads = alpm_list_add(payloads, payload);
@@ -1150,7 +1343,6 @@ void _alpm_dload_payload_reset(struct dload_payload *payload)
FREE(payload->remote_name);
FREE(payload->tempfile_name);
FREE(payload->destfile_name);
FREE(payload->content_disp_name);
FREE(payload->fileurl);
FREE(payload->filepath);
*payload = (struct dload_payload){0};

View File

@@ -26,10 +26,12 @@
struct dload_payload {
alpm_handle_t *handle;
const char *tempfile_openmode;
/* name of the remote file */
char *remote_name;
/* temporary file name, to which the payload is downloaded */
char *tempfile_name;
/* name to which the downloaded file will be renamed */
char *destfile_name;
char *content_disp_name;
/* client has to provide either
* 1) fileurl - full URL to the file
* 2) pair of (servers, filepath), in this case ALPM iterates over the
@@ -45,19 +47,17 @@ struct dload_payload {
off_t prevprogress;
int force;
int allow_resume;
int random_partfile;
int errors_ok;
int unlink_on_fail;
int trust_remote_name;
int download_signature; /* specifies if an accompanion *.sig file need to be downloaded*/
int signature_optional; /* *.sig file is optional */
#ifdef HAVE_LIBCURL
CURL *curl;
char error_buffer[CURL_ERROR_SIZE];
FILE *localf; /* temp download file */
int signature; /* specifies if this payload is for a signature file */
int request_errors_ok; /* per-request errors-ok */
#endif
FILE *localf; /* temp download file */
};
void _alpm_dload_payload_reset(struct dload_payload *payload);

View File

@@ -101,6 +101,7 @@ void _alpm_handle_free(alpm_handle_t *handle)
FREE(handle->lockfile);
FREELIST(handle->architectures);
FREE(handle->gpgdir);
FREE(handle->sandboxuser);
FREELIST(handle->noupgrade);
FREELIST(handle->noextract);
FREELIST(handle->ignorepkg);
@@ -292,6 +293,12 @@ const char SYMEXPORT *alpm_option_get_gpgdir(alpm_handle_t *handle)
return handle->gpgdir;
}
const char SYMEXPORT *alpm_option_get_sandboxuser(alpm_handle_t *handle)
{
CHECK_HANDLE(handle, return NULL);
return handle->sandboxuser;
}
int SYMEXPORT alpm_option_get_usesyslog(alpm_handle_t *handle)
{
CHECK_HANDLE(handle, return -1);
@@ -595,6 +602,19 @@ int SYMEXPORT alpm_option_set_gpgdir(alpm_handle_t *handle, const char *gpgdir)
return 0;
}
int SYMEXPORT alpm_option_set_sandboxuser(alpm_handle_t *handle, const char *sandboxuser)
{
CHECK_HANDLE(handle, return -1);
if(handle->sandboxuser) {
FREE(handle->sandboxuser);
}
STRDUP(handle->sandboxuser, sandboxuser, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
_alpm_log(handle, ALPM_LOG_DEBUG, "option 'sandboxuser' = %s\n", handle->sandboxuser);
return 0;
}
int SYMEXPORT alpm_option_set_usesyslog(alpm_handle_t *handle, int usesyslog)
{
CHECK_HANDLE(handle, return -1);

View File

@@ -91,6 +91,7 @@ struct _alpm_handle_t {
char *logfile; /* Name of the log file */
char *lockfile; /* Name of the lock file */
char *gpgdir; /* Directory where GnuPG files are stored */
char *sandboxuser; /* User to switch to for sensitive operations */
alpm_list_t *cachedirs; /* Paths to pacman cache directories */
alpm_list_t *hookdirs; /* Paths to hook directories */
alpm_list_t *overwrite_files; /* Paths that may be overwritten */

View File

@@ -24,6 +24,7 @@ libalpm_sources = files('''
pkghash.h pkghash.c
rawstr.c
remove.h remove.c
sandbox.h sandbox.c
signing.c signing.h
sync.h sync.c
trans.h trans.c

224
lib/libalpm/sandbox.c Normal file
View File

@@ -0,0 +1,224 @@
/*
* sandbox.c
*
* Copyright (c) 2021-2022 Pacman Development Team <pacman-dev@lists.archlinux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include "alpm.h"
#include "log.h"
#include "sandbox.h"
#include "util.h"
int SYMEXPORT alpm_sandbox_setup_child(const char* sandboxuser)
{
struct passwd const *pw = NULL;
ASSERT(sandboxuser != NULL, return EINVAL);
ASSERT(getuid() == 0, return EPERM);
ASSERT((pw = getpwnam(sandboxuser)), return errno);
ASSERT(setgid(pw->pw_gid) == 0, return errno);
ASSERT(setgroups(0, NULL) == 0, return errno);
ASSERT(setuid(pw->pw_uid) == 0, return errno);
return 0;
}
static int should_retry(int errnum)
{
return errnum == EINTR;
}
static int read_from_pipe(int fd, void *buf, size_t count)
{
size_t nread = 0;
ASSERT(count > 0, return -1);
while(nread < count) {
ssize_t r = read(fd, (char *)buf + nread, count-nread);
if(r < 0) {
if(!should_retry(errno)) {
return -1;
}
continue;
}
if(r == 0) {
/* we hit EOF unexpectedly - bail */
return -1;
}
nread += r;
}
return 0;
}
static int write_to_pipe(int fd, const void *buf, size_t count)
{
size_t nwrite = 0;
ASSERT(count > 0, return -1);
while(nwrite < count) {
ssize_t r = write(fd, (char *)buf + nwrite, count-nwrite);
if(r < 0) {
if(!should_retry(errno)) {
return -1;
}
continue;
}
nwrite += r;
}
return 0;
}
void _alpm_sandbox_cb_log(void *ctx, alpm_loglevel_t level, const char *fmt, va_list args)
{
_alpm_sandbox_callback_t type = ALPM_SANDBOX_CB_LOG;
_alpm_sandbox_callback_context *context = ctx;
char *string = NULL;
int string_size = 0;
if(!context || context->callback_pipe == -1) {
return;
}
/* compute the required size, as allowed by POSIX.1-2001 and C99 */
/* first we need to copy the va_list as it will be consumed by the first call */
va_list copy;
va_copy(copy, args);
string_size = vsnprintf(NULL, 0, fmt, copy);
if(string_size <= 0) {
va_end(copy);
return;
}
MALLOC(string, string_size + 1, return);
string_size = vsnprintf(string, string_size + 1, fmt, args);
if(string_size > 0) {
write_to_pipe(context->callback_pipe, &type, sizeof(type));
write_to_pipe(context->callback_pipe, &level, sizeof(level));
write_to_pipe(context->callback_pipe, &string_size, sizeof(string_size));
write_to_pipe(context->callback_pipe, string, string_size);
}
va_end(copy);
FREE(string);
}
void _alpm_sandbox_cb_dl(void *ctx, const char *filename, alpm_download_event_type_t event, void *data)
{
_alpm_sandbox_callback_t type = ALPM_SANDBOX_CB_DOWNLOAD;
_alpm_sandbox_callback_context *context = ctx;
size_t filename_len;
if(!context || context->callback_pipe == -1) {
return;
}
ASSERT(filename != NULL, return);
ASSERT(event == ALPM_DOWNLOAD_INIT || event == ALPM_DOWNLOAD_PROGRESS || event == ALPM_DOWNLOAD_RETRY || event == ALPM_DOWNLOAD_COMPLETED, return);
filename_len = strlen(filename);
write_to_pipe(context->callback_pipe, &type, sizeof(type));
write_to_pipe(context->callback_pipe, &event, sizeof(event));
switch(event) {
case ALPM_DOWNLOAD_INIT:
write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_init_t));
break;
case ALPM_DOWNLOAD_PROGRESS:
write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_progress_t));
break;
case ALPM_DOWNLOAD_RETRY:
write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_retry_t));
break;
case ALPM_DOWNLOAD_COMPLETED:
write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_completed_t));
break;
}
write_to_pipe(context->callback_pipe, &filename_len, sizeof(filename_len));
write_to_pipe(context->callback_pipe, filename, filename_len);
}
bool _alpm_sandbox_process_cb_log(alpm_handle_t *handle, int callback_pipe) {
alpm_loglevel_t level;
char *string = NULL;
int string_size = 0;
ASSERT(read_from_pipe(callback_pipe, &level, sizeof(level)) != -1, return false);
ASSERT(read_from_pipe(callback_pipe, &string_size, sizeof(string_size)) != -1, return false);
MALLOC(string, string_size + 1, return false);
ASSERT(read_from_pipe(callback_pipe, string, string_size) != -1, FREE(string); return false);
string[string_size] = '\0';
_alpm_log(handle, level, "%s", string);
FREE(string);
return true;
}
bool _alpm_sandbox_process_cb_download(alpm_handle_t *handle, int callback_pipe) {
alpm_download_event_type_t type;
char *filename = NULL;
size_t filename_size, cb_data_size;
union {
alpm_download_event_init_t init;
alpm_download_event_progress_t progress;
alpm_download_event_retry_t retry;
alpm_download_event_completed_t completed;
} cb_data;
ASSERT(read_from_pipe(callback_pipe, &type, sizeof(type)) != -1, return false);
switch (type) {
case ALPM_DOWNLOAD_INIT:
cb_data_size = sizeof(alpm_download_event_init_t);
ASSERT(read_from_pipe(callback_pipe, &cb_data.init, cb_data_size) != -1, return false);
break;
case ALPM_DOWNLOAD_PROGRESS:
cb_data_size = sizeof(alpm_download_event_progress_t);
ASSERT(read_from_pipe(callback_pipe, &cb_data.progress, cb_data_size) != -1, return false);
break;
case ALPM_DOWNLOAD_RETRY:
cb_data_size = sizeof(alpm_download_event_retry_t);
ASSERT(read_from_pipe(callback_pipe, &cb_data.retry, cb_data_size) != -1, return false);
break;
case ALPM_DOWNLOAD_COMPLETED:
cb_data_size = sizeof(alpm_download_event_completed_t);
ASSERT(read_from_pipe(callback_pipe, &cb_data.completed, cb_data_size) != -1, return false);
break;
default:
return false;
}
ASSERT(read_from_pipe(callback_pipe, &filename_size, sizeof(filename_size)) != -1, return false);;
MALLOC(filename, filename_size + 1, return false);
ASSERT(read_from_pipe(callback_pipe, filename, filename_size) != -1, FREE(filename); return false);
filename[filename_size] = '\0';
handle->dlcb(handle->dlcb_ctx, filename, type, &cb_data);
FREE(filename);
return true;
}

51
lib/libalpm/sandbox.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* sandbox.h
*
* Copyright (c) 2021-2022 Pacman Development Team <pacman-dev@lists.archlinux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ALPM_SANDBOX_H
#define ALPM_SANDBOX_H
#include <stdbool.h>
/* The type of callbacks that can happen during a sandboxed operation */
typedef enum {
ALPM_SANDBOX_CB_LOG,
ALPM_SANDBOX_CB_DOWNLOAD
} _alpm_sandbox_callback_t;
typedef struct {
int callback_pipe;
} _alpm_sandbox_callback_context;
/* Sandbox callbacks */
__attribute__((format(printf, 3, 0)))
void _alpm_sandbox_cb_log(void *ctx, alpm_loglevel_t level, const char *fmt, va_list args);
void _alpm_sandbox_cb_dl(void *ctx, const char *filename, alpm_download_event_type_t event, void *data);
/* Functions to capture sandbox callbacks and convert them to alpm callbacks */
bool _alpm_sandbox_process_cb_log(alpm_handle_t *handle, int callback_pipe);
bool _alpm_sandbox_process_cb_download(alpm_handle_t *handle, int callback_pipe);
#endif /* ALPM_SANDBOX_H */

View File

@@ -767,6 +767,7 @@ static int find_dl_candidates(alpm_handle_t *handle, alpm_list_t **files)
return 0;
}
static int download_files(alpm_handle_t *handle)
{
const char *cachedir;
@@ -825,8 +826,15 @@ static int download_files(alpm_handle_t *handle)
CALLOC(payload, 1, sizeof(*payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, finish));
STRDUP(payload->remote_name, pkg->filename, FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, finish));
STRDUP(payload->filepath, pkg->filename,
FREE(payload->remote_name); FREE(payload);
_alpm_dload_payload_reset(payload); FREE(payload);
GOTO_ERR(handle, ALPM_ERR_MEMORY, finish));
payload->destfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, "");
payload->tempfile_name = _alpm_get_fullpath(cachedir, payload->remote_name, ".part");
if(!payload->destfile_name || !payload->tempfile_name) {
_alpm_dload_payload_reset(payload);
FREE(payload);
GOTO_ERR(handle, ALPM_ERR_MEMORY, finish);
}
payload->max_size = pkg->size;
payload->cache_servers = pkg->origin_data.db->cache_servers;
payload->servers = pkg->origin_data.db->servers;

View File

@@ -197,6 +197,23 @@ cleanup:
return ret;
}
/** Combines a directory, filename and suffix to provide full path of a file
* @param path directory path
* @param filename file name
* @param suffix suffix
* @return file path
*/
char *_alpm_get_fullpath(const char *path, const char *filename, const char *suffix)
{
char *filepath;
/* len = localpath len + filename len + suffix len + null */
size_t len = strlen(path) + strlen(filename) + strlen(suffix) + 1;
MALLOC(filepath, len, return NULL);
snprintf(filepath, len, "%s%s%s", path, filename, suffix);
return filepath;
}
/** Trim trailing newlines from a string (if any exist).
* @param str a single line of text
* @param len size of str, if known, else 0
@@ -555,7 +572,7 @@ static int _alpm_chroot_read_from_child(alpm_handle_t *handle, int fd,
return 0;
}
static void _alpm_reset_signals(void)
void _alpm_reset_signals(void)
{
/* reset POSIX defined signals (see signal.h) */
/* there are likely more but there is no easy way

View File

@@ -116,6 +116,7 @@ struct archive_read_buffer {
int _alpm_makepath(const char *path);
int _alpm_makepath_mode(const char *path, mode_t mode);
int _alpm_copyfile(const char *src, const char *dest);
char *_alpm_get_fullpath(const char *path, const char *filename, const char *suffix);
size_t _alpm_strip_newline(char *str, size_t len);
int _alpm_open_archive(alpm_handle_t *handle, const char *path,
@@ -129,6 +130,7 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, int fu
typedef ssize_t (*_alpm_cb_io)(void *buf, ssize_t len, void *ctx);
void _alpm_reset_signals(void);
int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[],
_alpm_cb_io in_cb, void *in_ctx);
int _alpm_ldconfig(alpm_handle_t *handle);

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;
@@ -745,7 +745,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;
@@ -882,7 +883,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);
@@ -1087,6 +1089,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;

View File

@@ -155,6 +155,7 @@ int config_free(config_t *oldconfig)
free(oldconfig->dbpath);
free(oldconfig->logfile);
free(oldconfig->gpgdir);
free(oldconfig->sandboxuser);
FREELIST(oldconfig->hookdirs);
FREELIST(oldconfig->cachedirs);
free(oldconfig->xfercommand);
@@ -669,6 +670,11 @@ static int _parse_options(const char *key, char *value,
config->logfile = strdup(value);
pm_printf(ALPM_LOG_DEBUG, "config: logfile: %s\n", value);
}
} else if(strcmp(key, "DownloadUser") == 0) {
if(!config->sandboxuser) {
config->sandboxuser = strdup(value);
pm_printf(ALPM_LOG_DEBUG, "config: sandboxuser: %s\n", value);
}
} else if(strcmp(key, "XferCommand") == 0) {
char **c;
if((config->xfercommand_argv = wordsplit(value)) == NULL) {
@@ -921,6 +927,7 @@ static int setup_libalpm(void)
alpm_option_set_architectures(handle, config->architectures);
alpm_option_set_checkspace(handle, config->checkspace);
alpm_option_set_usesyslog(handle, config->usesyslog);
alpm_option_set_sandboxuser(handle, config->sandboxuser);
alpm_option_set_ignorepkgs(handle, config->ignorepkg);
alpm_option_set_ignoregroups(handle, config->ignoregrp);

View File

@@ -68,6 +68,7 @@ typedef struct __config_t {
char *logfile;
char *gpgdir;
char *sysroot;
char *sandboxuser;
alpm_list_t *hookdirs;
alpm_list_t *cachedirs;
alpm_list_t *architectures;

View File

@@ -252,6 +252,7 @@ static void dump_config(void)
show_list_str("HookDir", config->hookdirs);
show_str("GPGDir", config->gpgdir);
show_str("LogFile", config->logfile);
show_str("DownloadUser", config->sandboxuser);
show_list_str("HoldPkg", config->holdpkg);
show_list_str("IgnorePkg", config->ignorepkg);
@@ -352,6 +353,8 @@ static int list_directives(void)
show_str("GPGDir", config->gpgdir);
} else if(strcasecmp(i->data, "LogFile") == 0) {
show_str("LogFile", config->logfile);
} else if(strcasecmp(i->data, "DownloadUser") == 0) {
show_str("DownloadUser", config->sandboxuser);
} else if(strcasecmp(i->data, "HoldPkg") == 0) {
show_list_str("HoldPkg", config->holdpkg);

View File

@@ -155,6 +155,8 @@ pacman_tests = [
'tests/replace103.py',
'tests/replace104.py',
'tests/replace110.py',
'tests/sandbox-download-upgrade.py',
'tests/sandbox-download-basic.py',
'tests/scriptlet001.py',
'tests/scriptlet002.py',
'tests/scriptlet-signal-handling.py',
@@ -334,7 +336,6 @@ pacman_tests = [
'tests/xfercommand001.py',
'tests/upgrade-download-404.py',
'tests/upgrade-download-pkg-and-sig-with-filename.py',
'tests/upgrade-download-pkg-and-sig-without-filename.py',
'tests/upgrade-download-with-xfercommand.py',
]
@@ -347,7 +348,6 @@ xfail_tests = {
'tests/sync403.py': true,
'tests/sync406.py': true,
'tests/upgrade078.py': true,
'tests/upgrade-download-with-xfercommand.py': true,
}
foreach input : pacman_tests

View File

@@ -0,0 +1,11 @@
self.description = "refresh databases with Optional siglevel"
self.require_capability("curl")
p1 = pmpkg('pkg1', '1.0-1')
self.addpkg2db('sync', p1)
self.db['sync'].option['SigLevel'] = ["Optional"]
self.args = '-Syy'
self.addrule("PACMAN_RETCODE=0")

View File

@@ -0,0 +1,20 @@
self.description = "--sync with DownloadUser set"
self.require_capability("curl")
p1 = pmpkg('pkg1', '1.0-1')
self.addpkg2db('sync', p1)
url = self.add_simple_http_server({
'/{}'.format(p1.filename()): p1.makepkg_bytes(),
})
self.option['DownloadUser'] = ['root']
self.db['sync'].option['Server'] = [ url ]
self.db['sync'].syncdir = False
self.cachepkgs = False
self.args = '-S pkg1'
self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=pkg1")
self.addrule("CACHE_EXISTS=pkg1|1.0-1")

View File

@@ -0,0 +1,17 @@
self.description = "--upgrade with DownloadUser set"
self.require_capability("curl")
self.option['DownloadUser'] = ['root']
p1 = pmpkg('pkg1', '1.0-1')
self.addpkg(p1)
url = self.add_simple_http_server({
'/{}'.format(p1.filename()): p1.makepkg_bytes(),
})
self.args = '-U {url}/{}'.format(p1.filename(), url=url)
self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=pkg1")
self.addrule("CACHE_EXISTS=pkg1|1.0-1")

View File

@@ -10,7 +10,7 @@ url = self.add_simple_http_server({
'body': 'simple.sig',
},
# content-disposition filename
# content-disposition filename is now ignored
'/cd.pkg': {
'headers': { 'Content-Disposition': 'attachment; filename="cd-alt.pkg"' },
'body': 'cd'
@@ -64,19 +64,26 @@ self.addrule('!PACMAN_RETCODE=0')
self.addrule('CACHE_FCONTENTS=simple.pkg|simple')
self.addrule('CACHE_FCONTENTS=simple.pkg.sig|simple.sig')
self.addrule('!CACHE_FEXISTS=cd.pkg')
self.addrule('!CACHE_FEXISTS=cd.pkg.sig')
self.addrule('CACHE_FCONTENTS=cd-alt.pkg|cd')
self.addrule('CACHE_FCONTENTS=cd-alt.pkg.sig|cd.sig')
self.addrule('!CACHE_FEXISTS=cd-alt.pkg')
self.addrule('!CACHE_FEXISTS=cd-alt.pkg.sig')
self.addrule('CACHE_FCONTENTS=cd.pkg|cd')
self.addrule('CACHE_FCONTENTS=cd.pkg.sig|cd.sig')
self.addrule('!CACHE_FEXISTS=redir.pkg')
self.addrule('CACHE_FCONTENTS=redir-dest.pkg|redir-dest')
self.addrule('CACHE_FCONTENTS=redir-dest.pkg.sig|redir-dest.sig')
self.addrule('!CACHE_FEXISTS=redir-dest.pkg')
self.addrule('CACHE_FCONTENTS=redir.pkg|redir-dest')
self.addrule('CACHE_FCONTENTS=redir.pkg.sig|redir-dest.sig')
self.addrule('!CACHE_FEXISTS=cd-redir.pkg')
self.addrule('!CACHE_FEXISTS=cd-redir-dest.pkg')
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg|cd-redir-dest')
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg.sig|cd-redir-dest.sig')
self.addrule('CACHE_FCONTENTS=redir-cdn.pkg|redir-dest')
self.addrule('CACHE_FCONTENTS=redir-cdn.pkg.sig|redir-dest.sig')
self.addrule('CACHE_FCONTENTS=cdn-alt.pkg|cdn-alt')
self.addrule('CACHE_FCONTENTS=cdn-alt.pkg.sig|cdn-alt.sig')
self.addrule('!CACHE_FEXISTS=cd-redir-dest-alt.pkg')
self.addrule('!CACHE_FEXISTS=cd-redir-dest-alt.pkg')
self.addrule('CACHE_FCONTENTS=cd-redir.pkg|cd-redir-dest')
self.addrule('CACHE_FCONTENTS=cd-redir.pkg.sig|cd-redir-dest.sig')
self.addrule('!CACHE_FEXISTS=cdn-3')
self.addrule('!CACHE_FEXISTS=cdn-4')
self.addrule('!CACHE_FEXISTS=cdn-alt.pkg')
self.addrule('!CACHE_FEXISTS=cdn-alt.pkg.sig')
self.addrule('CACHE_FCONTENTS=cd-redir-cdn.pkg|cdn-alt')
self.addrule('CACHE_FCONTENTS=cd-redir-cdn.pkg.sig|cdn-alt.sig')

View File

@@ -1,63 +0,0 @@
self.description = 'download remote packages with -U without a URL filename'
self.require_capability("gpg")
self.require_capability("curl")
url = self.add_simple_http_server({
# simple
'/simple.pkg/': 'simple',
'/simple.pkg/.sig': 'simple.sig',
# content-disposition filename
'/cd.pkg/': {
'headers': { 'Content-Disposition': 'attachment; filename="cd-alt.pkg"' },
'body': 'cd'
},
'/cd.pkg/.sig': {
'headers': { 'Content-Disposition': 'attachment; filename="cd-alt-bad.pkg.sig"' },
'body': 'cd.sig'
},
# redirect
'/redir.pkg/': { 'code': 303, 'headers': { 'Location': '/redir-dest.pkg' } },
'/redir-dest.pkg': 'redir-dest',
'/redir-dest.pkg.sig': 'redir-dest.sig',
# content-disposition and redirect
'/cd-redir.pkg/': { 'code': 303, 'headers': { 'Location': '/cd-redir-dest.pkg' } },
'/cd-redir-dest.pkg': {
'headers': { 'Content-Disposition': 'attachment; filename="cd-redir-dest-alt.pkg"' },
'body': 'cd-redir-dest'
},
'/cd-redir-dest.pkg.sig': 'cd-redir-dest.sig',
# TODO: absolutely terrible hack to prevent pacman from attempting to
# validate packages, which causes failure under --valgrind thanks to
# a memory leak in gpgme that is too general for inclusion in valgrind.supp
'/404': { 'code': 404 },
'': 'fallback',
})
self.args = '-Uw {url}/simple.pkg/ {url}/cd.pkg/ {url}/redir.pkg/ {url}/cd-redir.pkg/ {url}/404'.format(url=url)
# packages/sigs are not valid, error is expected
self.addrule('!PACMAN_RETCODE=0')
# TODO: use a predictable file name
#self.addrule('CACHE_FCONTENTS=simple.pkg|simple')
#self.addrule('CACHE_FCONTENTS=simple.pkg.sig|simple.sig')
self.addrule('!CACHE_FEXISTS=cd.pkg')
self.addrule('CACHE_FCONTENTS=cd-alt.pkg|cd')
self.addrule('CACHE_FCONTENTS=cd-alt.pkg.sig|cd.sig')
self.addrule('!CACHE_FEXISTS=redir.pkg')
self.addrule('CACHE_FCONTENTS=redir-dest.pkg|redir-dest')
self.addrule('CACHE_FCONTENTS=redir-dest.pkg.sig|redir-dest.sig')
self.addrule('!CACHE_FEXISTS=cd-redir.pkg')
self.addrule('!CACHE_FEXISTS=cd-redir-dest.pkg')
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg|cd-redir-dest')
self.addrule('CACHE_FCONTENTS=cd-redir-dest-alt.pkg.sig|cd-redir-dest.sig')
self.addrule('!CACHE_FEXISTS=.sig')

View File

@@ -20,7 +20,3 @@ self.addrule("PKG_EXIST=pkg1")
self.addrule("PKG_EXIST=pkg2")
self.addrule("CACHE_EXISTS=pkg1|1.0-1")
self.addrule("CACHE_EXISTS=pkg2|2.0-2")
# --upgrade fails hard with XferCommand because the fetch callback has no way
# to return the file path to alpm
self.expectfailure = True