Merge branch 'landlock-better-messages' into 'master'

libalpm: Better error messages when landlock setup fails

See merge request pacman/pacman!222
This commit is contained in:
Remi Gacogne 2025-08-02 13:10:38 +00:00
commit 055fae19cf
13 changed files with 134 additions and 15 deletions

View file

@ -206,6 +206,18 @@ Options
systems. Useful if experiencing landlock related failures while downloading systems. Useful if experiencing landlock related failures while downloading
files when running a Linux kernel that does not support this feature. files when running a Linux kernel that does not support this feature.
*\--disable-fs-sandbox*::
Disable the filesystem part of the sandbox applied to the process downloading
files on Linux systems. Useful if experiencing Landlock related failures
while downloading files when running a Linux kernel that does not support
this feature.
*\--disable-syscalls-sandbox*::
Disable the syscalls part of the sandbox applied to the process downloading
files on Linux systems. Useful if experiencing seccomp related failures
while downloading files when running a Linux kernel that does not support
this feature.
Transaction Options (apply to '-S', '-R' and '-U') Transaction Options (apply to '-S', '-R' and '-U')
-------------------------------------------------- --------------------------------------------------
*-d, \--nodeps*:: *-d, \--nodeps*::

View file

@ -216,6 +216,18 @@ Options
systems. Useful if experiencing landlock related failures while downloading systems. Useful if experiencing landlock related failures while downloading
files when running a Linux kernel that does not support this feature. files when running a Linux kernel that does not support this feature.
*DisableFilesystemSandbox*::
Disable the filesystem part of the sandbox applied to the process downloading
files on Linux systems. Useful if experiencing Landlock related failures
while downloading files when running a Linux kernel that does not support
this feature.
*DisableSyscallsSandbox*::
Disable the syscalls part of the sandbox applied to the process downloading
files on Linux systems. Useful if experiencing seccomp related failures
while downloading files when running a Linux kernel that does not support
this feature.
Repository Sections Repository Sections
------------------- -------------------
Each repository section defines a section name and at least one location where Each repository section defines a section name and at least one location where

View file

@ -37,6 +37,8 @@ CheckSpace
ParallelDownloads = 5 ParallelDownloads = 5
#DownloadUser = alpm #DownloadUser = alpm
#DisableSandbox #DisableSandbox
#DisableFilesystemSandbox
#DisableSyscallsSandbox
# PGP signature checking # PGP signature checking
#SigLevel = Optional #SigLevel = Optional

View file

@ -2317,6 +2317,32 @@ int alpm_option_get_disable_sandbox(alpm_handle_t *handle);
* @return 0 on success, -1 on error (pm_errno is set accordingly) * @return 0 on success, -1 on error (pm_errno is set accordingly)
*/ */
int alpm_option_set_disable_sandbox(alpm_handle_t *handle, unsigned short disable_sandbox); int alpm_option_set_disable_sandbox(alpm_handle_t *handle, unsigned short disable_sandbox);
/** Get the state of the filesystem part of the sandbox
* @param handle the context handle
* @return 0 for enabled, 1 for disabled
*/
int alpm_option_get_disable_filesystem_sandbox(alpm_handle_t *handle);
/** Enables/disables the filesystem part of the sandbox.
* @param handle the context handle
* @param disable_fs_sandbox 0 for enabled, 1 for disabled
* @return 0 on success, -1 on error (pm_errno is set accordingly)
*/
int alpm_option_set_disable_filesystem_sandbox(alpm_handle_t *handle, unsigned short disable_fs_sandbox);
/** Get the state of the syscalls part of the sandbox
* @param handle the context handle
* @return 0 for enabled, 1 for disabled
*/
int alpm_option_get_disable_syscalls_sandbox(alpm_handle_t *handle);
/** Enables/disables the syscalls part of the sandbox.
* @param handle the context handle
* @param disable_syscalls_sandbox 0 for enabled, 1 for disabled
* @return 0 on success, -1 on error (pm_errno is set accordingly)
*/
int alpm_option_set_disable_syscalls_sandbox(alpm_handle_t *handle, unsigned short disable_syscalls_sandbox);
/* End of disable_sandbox accessors */ /* End of disable_sandbox accessors */
/** @} */ /** @} */

View file

@ -971,3 +971,31 @@ int SYMEXPORT alpm_option_set_disable_sandbox(alpm_handle_t *handle,
handle->disable_sandbox = disable_sandbox; handle->disable_sandbox = disable_sandbox;
return 0; return 0;
} }
int SYMEXPORT alpm_option_get_disable_filesystem_sandbox(alpm_handle_t *handle)
{
CHECK_HANDLE(handle, return -1);
return handle->disable_fs_sandbox;
}
int SYMEXPORT alpm_option_set_disable_filesystem_sandbox(alpm_handle_t *handle,
unsigned short disable_fs_sandbox)
{
CHECK_HANDLE(handle, return -1);
handle->disable_fs_sandbox = disable_fs_sandbox;
return 0;
}
int SYMEXPORT alpm_option_get_disable_syscalls_sandbox(alpm_handle_t *handle)
{
CHECK_HANDLE(handle, return -1);
return handle->disable_syscalls_sandbox;
}
int SYMEXPORT alpm_option_set_disable_syscalls_sandbox(alpm_handle_t *handle,
unsigned short disable_syscalls_sandbox)
{
CHECK_HANDLE(handle, return -1);
handle->disable_syscalls_sandbox = disable_syscalls_sandbox;
return 0;
}

View file

@ -65,7 +65,9 @@ struct _alpm_handle_t {
#endif #endif
unsigned short disable_dl_timeout; unsigned short disable_dl_timeout;
unsigned short disable_fs_sandbox;
unsigned short disable_sandbox; unsigned short disable_sandbox;
unsigned short disable_syscalls_sandbox;
unsigned int parallel_downloads; /* number of download streams */ unsigned int parallel_downloads; /* number of download streams */
#ifdef HAVE_LIBGPGME #ifdef HAVE_LIBGPGME

View file

@ -42,15 +42,15 @@ int SYMEXPORT alpm_sandbox_setup_child(alpm_handle_t *handle, const char* sandbo
ASSERT(sandboxuser != NULL, return -1); ASSERT(sandboxuser != NULL, return -1);
ASSERT(getuid() == 0, return -1); ASSERT(getuid() == 0, return -1);
ASSERT((pw = getpwnam(sandboxuser)), return -1); ASSERT((pw = getpwnam(sandboxuser)), return -1);
if(sandbox_path != NULL && !handle->disable_sandbox) { if(sandbox_path != NULL && !handle->disable_sandbox && !handle->disable_fs_sandbox) {
_alpm_sandbox_fs_restrict_writes_to(handle, sandbox_path); ASSERT(_alpm_sandbox_fs_restrict_writes_to(handle, sandbox_path), return -1);
} }
#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NO_NEW_PRIVS) #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NO_NEW_PRIVS)
/* make sure that we cannot gain more privileges later, failure is fine */ /* make sure that we cannot gain more privileges later, failure is fine */
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif /* HAVE_SYS_PRCTL && PR_SET_NO_NEW_PRIVS */ #endif /* HAVE_SYS_PRCTL && PR_SET_NO_NEW_PRIVS */
if(restrict_syscalls && !handle->disable_sandbox) { if(restrict_syscalls && !handle->disable_sandbox && !handle->disable_syscalls_sandbox) {
_alpm_sandbox_syscalls_filter(handle); ASSERT(_alpm_sandbox_syscalls_filter(handle), return -1);
} }
ASSERT(setgid(pw->pw_gid) == 0, return -1); ASSERT(setgid(pw->pw_gid) == 0, return -1);
ASSERT(setgroups(0, NULL) == 0, return -1); ASSERT(setgroups(0, NULL) == 0, return -1);

View file

@ -110,35 +110,39 @@ bool _alpm_sandbox_fs_restrict_writes_to(alpm_handle_t *handle, const char *path
abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
if(abi < 0) { if(abi < 0) {
/* landlock is not supported/enabled in the kernel */ /* Landlock is not supported/enabled in the kernel */
_alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because landlock is not supported by the kernel!\n")); _alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because Landlock is not supported by the kernel!\n"));
return true; return false;
} }
#ifdef LANDLOCK_ACCESS_FS_REFER #ifdef LANDLOCK_ACCESS_FS_REFER
if(abi < 2) { if(abi < 2) {
_alpm_log(handle, ALPM_LOG_DEBUG, _("landlock ABI < 2, LANDLOCK_ACCESS_FS_REFER is not supported\n")); _alpm_log(handle, ALPM_LOG_DEBUG, _("Landlock ABI < 2, LANDLOCK_ACCESS_FS_REFER is not supported\n"));
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
} }
#endif /* LANDLOCK_ACCESS_FS_REFER */ #endif /* LANDLOCK_ACCESS_FS_REFER */
#ifdef LANDLOCK_ACCESS_FS_TRUNCATE #ifdef LANDLOCK_ACCESS_FS_TRUNCATE
if(abi < 3) { if(abi < 3) {
_alpm_log(handle, ALPM_LOG_DEBUG, _("landlock ABI < 3, LANDLOCK_ACCESS_FS_TRUNCATE is not supported\n")); _alpm_log(handle, ALPM_LOG_DEBUG, _("Landlock ABI < 3, LANDLOCK_ACCESS_FS_TRUNCATE is not supported\n"));
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
} }
#endif /* LANDLOCK_ACCESS_FS_TRUNCATE */ #endif /* LANDLOCK_ACCESS_FS_TRUNCATE */
ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if(ruleset_fd < 0) { if(ruleset_fd < 0) {
_alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the landlock ruleset could not be created!\n")); _alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the Landlock ruleset could not be created: %s\n"), strerror(errno));
return false; return false;
} }
/* allow / as read-only */ /* allow / as read-only */
path_beneath.parent_fd = open("/", O_PATH | O_CLOEXEC | O_DIRECTORY); path_beneath.parent_fd = open("/", O_PATH | O_CLOEXEC | O_DIRECTORY);
if(path_beneath.parent_fd == -1) {
_alpm_log(handle, ALPM_LOG_ERROR, _("opening the root filesystem to make it read-only via Landlock failed: %s\n"), strerror(errno));
return false;
}
path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ; path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ;
if(landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0) != 0) { if(landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0) != 0) {
_alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the landlock rule for / could not be added!\n")); _alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the Landlock rule for / could not be added: %s\n"), strerror(errno));
close(path_beneath.parent_fd); close(path_beneath.parent_fd);
close(ruleset_fd); close(ruleset_fd);
return false; return false;
@ -148,6 +152,10 @@ bool _alpm_sandbox_fs_restrict_writes_to(alpm_handle_t *handle, const char *path
/* allow read-write access to the directory passed as parameter */ /* allow read-write access to the directory passed as parameter */
path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC | O_DIRECTORY); path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC | O_DIRECTORY);
if(path_beneath.parent_fd == -1) {
_alpm_log(handle, ALPM_LOG_ERROR, _("opening the download directory to make it writable via Landlock failed: %s\n"), strerror(errno));
return false;
}
path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ | _LANDLOCK_ACCESS_FS_WRITE | _LANDLOCK_ACCESS_FS_TRUNCATE; path_beneath.allowed_access = _LANDLOCK_ACCESS_FS_READ | _LANDLOCK_ACCESS_FS_WRITE | _LANDLOCK_ACCESS_FS_TRUNCATE;
/* make sure allowed_access is a subset of handled_access_fs, which may change for older landlock ABI */ /* make sure allowed_access is a subset of handled_access_fs, which may change for older landlock ABI */
@ -155,18 +163,18 @@ bool _alpm_sandbox_fs_restrict_writes_to(alpm_handle_t *handle, const char *path
if(landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0) == 0) { if(landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0) == 0) {
if(landlock_restrict_self(ruleset_fd, 0)) { if(landlock_restrict_self(ruleset_fd, 0)) {
_alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the landlock ruleset could not be applied!\n"));
result = errno; result = errno;
_alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the Landlock ruleset could not be applied: %s\n"), strerror(result));
} }
} else { } else {
result = errno; result = errno;
_alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the landlock rule for the temporary download directory could not be added!\n")); _alpm_log(handle, ALPM_LOG_ERROR, _("restricting filesystem access failed because the Landlock rule for the temporary download directory could not be added: %s\n"), strerror(result));
} }
close(path_beneath.parent_fd); close(path_beneath.parent_fd);
close(ruleset_fd); close(ruleset_fd);
if(result == 0) { if(result == 0) {
_alpm_log(handle, ALPM_LOG_DEBUG, _("filesystem access has been restricted to %s, landlock ABI is %d\n"), path, abi); _alpm_log(handle, ALPM_LOG_DEBUG, _("filesystem access has been restricted to %s, Landlock ABI is %d\n"), path, abi);
return true; return true;
} }
return false; return false;

View file

@ -136,6 +136,7 @@ bool _alpm_sandbox_syscalls_filter(alpm_handle_t *handle)
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
int restrictedSyscallsCount = 0; int restrictedSyscallsCount = 0;
if(ctx == NULL) { if(ctx == NULL) {
_alpm_log(handle, ALPM_LOG_ERROR, _("error initializing seccomp to filter system calls in the download sandbox!\n"));
return false; return false;
} }

View file

@ -629,8 +629,12 @@ static int _parse_options(const char *key, char *value,
config->noprogressbar = 1; config->noprogressbar = 1;
} else if(strcmp(key, "DisableDownloadTimeout") == 0) { } else if(strcmp(key, "DisableDownloadTimeout") == 0) {
config->disable_dl_timeout = 1; config->disable_dl_timeout = 1;
} else if(strcmp(key, "DisableFilesystemSandbox") == 0) {
config->disable_fs_sandbox = 1;
} else if(strcmp(key, "DisableSandbox") == 0) { } else if(strcmp(key, "DisableSandbox") == 0) {
config->disable_sandbox = 1; config->disable_sandbox = 1;
} else if(strcmp(key, "DisableSyscallsSandbox") == 0) {
config->disable_syscalls_sandbox = 1;
} else { } else {
pm_printf(ALPM_LOG_WARNING, pm_printf(ALPM_LOG_WARNING,
_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"), _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
@ -944,6 +948,8 @@ static int setup_libalpm(void)
} }
alpm_option_set_disable_sandbox(handle, config->disable_sandbox); alpm_option_set_disable_sandbox(handle, config->disable_sandbox);
alpm_option_set_disable_filesystem_sandbox(handle, config->disable_fs_sandbox);
alpm_option_set_disable_syscalls_sandbox(handle, config->disable_syscalls_sandbox);
alpm_option_set_ignorepkgs(handle, config->ignorepkg); alpm_option_set_ignorepkgs(handle, config->ignorepkg);
alpm_option_set_ignoregroups(handle, config->ignoregrp); alpm_option_set_ignoregroups(handle, config->ignoregrp);

View file

@ -58,7 +58,9 @@ typedef struct __config_t {
unsigned short usesyslog; unsigned short usesyslog;
unsigned short color; unsigned short color;
unsigned short disable_dl_timeout; unsigned short disable_dl_timeout;
unsigned short disable_fs_sandbox;
unsigned short disable_sandbox; unsigned short disable_sandbox;
unsigned short disable_syscalls_sandbox;
char *print_format; char *print_format;
/* unfortunately, we have to keep track of paths both here and in the library /* unfortunately, we have to keep track of paths both here and in the library
* because they can come from both the command line or config file, and we * because they can come from both the command line or config file, and we
@ -214,7 +216,9 @@ enum {
OP_REFRESH, OP_REFRESH,
OP_ASSUMEINSTALLED, OP_ASSUMEINSTALLED,
OP_DISABLEDLTIMEOUT, OP_DISABLEDLTIMEOUT,
OP_DISABLESANDBOX OP_DISABLEFSSANDBOX,
OP_DISABLESANDBOX,
OP_DISABLESYSCALLSSANDBOX
}; };
/* clean method */ /* clean method */

View file

@ -280,7 +280,9 @@ static void dump_config(void)
show_bool("DisableDownloadTimeout", config->disable_dl_timeout); show_bool("DisableDownloadTimeout", config->disable_dl_timeout);
show_bool("ILoveCandy", config->chomp); show_bool("ILoveCandy", config->chomp);
show_bool("NoProgressBar", config->noprogressbar); show_bool("NoProgressBar", config->noprogressbar);
show_bool("DisableFilesystemSandbox", config->disable_fs_sandbox);
show_bool("DisableSandbox", config->disable_sandbox); show_bool("DisableSandbox", config->disable_sandbox);
show_bool("DisableSyscallsSandbox", config->disable_syscalls_sandbox);
show_int("ParallelDownloads", config->parallel_downloads); show_int("ParallelDownloads", config->parallel_downloads);
@ -398,8 +400,12 @@ static int list_directives(void)
show_bool("ILoveCandy", config->chomp); show_bool("ILoveCandy", config->chomp);
} else if(strcasecmp(i->data, "NoProgressBar") == 0) { } else if(strcasecmp(i->data, "NoProgressBar") == 0) {
show_bool("NoProgressBar", config->noprogressbar); show_bool("NoProgressBar", config->noprogressbar);
} else if(strcasecmp(i->data, "DisableFilesystemSandbox") == 0) {
show_bool("DisableFilesystemSandbox", config->disable_fs_sandbox);
} else if(strcasecmp(i->data, "DisableSandbox") == 0) { } else if(strcasecmp(i->data, "DisableSandbox") == 0) {
show_bool("DisableSandbox", config->disable_sandbox); show_bool("DisableSandbox", config->disable_sandbox);
} else if(strcasecmp(i->data, "DisableSyscallsSandbox") == 0) {
show_bool("DisableSyscallsSandbox", config->disable_syscalls_sandbox);
} else if(strcasecmp(i->data, "ParallelDownloads") == 0) { } else if(strcasecmp(i->data, "ParallelDownloads") == 0) {
show_int("ParallelDownloads", config->parallel_downloads); show_int("ParallelDownloads", config->parallel_downloads);

View file

@ -228,6 +228,10 @@ static void usage(int op, const char * const myname)
" use relaxed timeouts for download\n")); " use relaxed timeouts for download\n"));
addlist(_(" --disable-sandbox\n" addlist(_(" --disable-sandbox\n"
" disable the sandbox used for the downloader process\n")); " disable the sandbox used for the downloader process\n"));
addlist(_(" --disable-fs-sandbox\n"
" disable the filesystem part of the sandbox used for the downloader process\n"));
addlist(_(" --disable-syscalls-sandbox\n"
" disable the syscalls part of the sandbox used for the downloader process\n"));
} }
list = alpm_list_msort(list, alpm_list_count(list), options_cmp); list = alpm_list_msort(list, alpm_list_count(list), options_cmp);
for(i = list; i; i = alpm_list_next(i)) { for(i = list; i; i = alpm_list_next(i)) {
@ -492,9 +496,15 @@ static int parsearg_global(int opt)
case OP_DISABLEDLTIMEOUT: case OP_DISABLEDLTIMEOUT:
config->disable_dl_timeout = 1; config->disable_dl_timeout = 1;
break; break;
case OP_DISABLEFSSANDBOX:
config->disable_fs_sandbox = 1;
break;
case OP_DISABLESANDBOX: case OP_DISABLESANDBOX:
config->disable_sandbox = 1; config->disable_sandbox = 1;
break; break;
case OP_DISABLESYSCALLSSANDBOX:
config->disable_syscalls_sandbox = 1;
break;
case OP_VERBOSE: case OP_VERBOSE:
case 'v': case 'v':
(config->verbose)++; (config->verbose)++;
@ -981,7 +991,9 @@ static int parseargs(int argc, char *argv[])
{"dbonly", no_argument, 0, OP_DBONLY}, {"dbonly", no_argument, 0, OP_DBONLY},
{"color", required_argument, 0, OP_COLOR}, {"color", required_argument, 0, OP_COLOR},
{"disable-download-timeout", no_argument, 0, OP_DISABLEDLTIMEOUT}, {"disable-download-timeout", no_argument, 0, OP_DISABLEDLTIMEOUT},
{"disable-fs-sandbox", no_argument, 0, OP_DISABLEFSSANDBOX},
{"disable-sandbox", no_argument, 0, OP_DISABLESANDBOX}, {"disable-sandbox", no_argument, 0, OP_DISABLESANDBOX},
{"disable-syscalls-sandbox", no_argument, 0, OP_DISABLESYSCALLSSANDBOX},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };