pacman/lib/libalpm/sandbox.c
Allan McRae f13d7d480c Update copyright years
./build-aux/update-copyright 2024 2025

Signed-off-by: Allan McRae <allan@archlinux.org>
2025-04-02 11:35:34 +10:00

243 lines
7.1 KiB
C

/*
* sandbox.c
*
* Copyright (c) 2021-2025 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 "config.h"
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif /* HAVE_SYS_PRCTL_H */
#include <sys/types.h>
#include <unistd.h>
#include "alpm.h"
#include "log.h"
#include "sandbox.h"
#include "sandbox_fs.h"
#include "sandbox_syscalls.h"
#include "util.h"
int SYMEXPORT alpm_sandbox_setup_child(alpm_handle_t *handle, const char* sandboxuser, const char* sandbox_path, bool restrict_syscalls)
{
struct passwd const *pw = NULL;
ASSERT(sandboxuser != NULL, return -1);
ASSERT(getuid() == 0, return -1);
ASSERT((pw = getpwnam(sandboxuser)), return -1);
if(sandbox_path != NULL && !handle->disable_sandbox) {
_alpm_sandbox_fs_restrict_writes_to(handle, sandbox_path);
}
#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NO_NEW_PRIVS)
/* make sure that we cannot gain more privileges later, failure is fine */
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif /* HAVE_SYS_PRCTL && PR_SET_NO_NEW_PRIVS */
if(restrict_syscalls && !handle->disable_sandbox) {
_alpm_sandbox_syscalls_filter(handle);
}
ASSERT(setgid(pw->pw_gid) == 0, return -1);
ASSERT(setgroups(0, NULL) == 0, return -1);
ASSERT(setuid(pw->pw_uid) == 0, return -1);
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';
if(handle->dlcb) {
handle->dlcb(handle->dlcb_ctx, filename, type, &cb_data);
}
FREE(filename);
return true;
}