2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2014-11-11 20:05:40 +01:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2014 Ronny Chevalier
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd 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
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
2015-10-28 11:30:40 +01:00
|
|
|
#include <grp.h>
|
|
|
|
#include <pwd.h>
|
2015-10-29 14:22:23 +01:00
|
|
|
#include <stdio.h>
|
2016-01-05 12:34:41 +01:00
|
|
|
#include <sys/prctl.h>
|
2015-10-29 14:22:23 +01:00
|
|
|
#include <sys/types.h>
|
2014-11-11 20:05:40 +01:00
|
|
|
|
2017-11-11 13:39:02 +01:00
|
|
|
#include "errno-list.h"
|
2015-10-30 16:55:44 +01:00
|
|
|
#include "fileio.h"
|
2015-10-26 21:16:26 +01:00
|
|
|
#include "fs-util.h"
|
2014-11-11 20:05:40 +01:00
|
|
|
#include "macro.h"
|
2015-10-26 21:16:26 +01:00
|
|
|
#include "manager.h"
|
2014-11-11 20:05:40 +01:00
|
|
|
#include "mkdir.h"
|
2015-10-29 14:22:23 +01:00
|
|
|
#include "path-util.h"
|
2015-04-04 11:52:57 +02:00
|
|
|
#include "rm-rf.h"
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2016-08-22 21:40:58 +02:00
|
|
|
#include "seccomp-util.h"
|
|
|
|
#endif
|
2016-12-21 00:58:08 +01:00
|
|
|
#include "stat-util.h"
|
2015-12-02 04:35:16 +01:00
|
|
|
#include "test-helper.h"
|
2017-02-15 23:37:25 +01:00
|
|
|
#include "tests.h"
|
2015-10-26 21:16:26 +01:00
|
|
|
#include "unit.h"
|
|
|
|
#include "util.h"
|
2016-07-27 23:23:44 +02:00
|
|
|
#include "virt.h"
|
2014-11-11 20:05:40 +01:00
|
|
|
|
|
|
|
typedef void (*test_function_t)(Manager *m);
|
|
|
|
|
|
|
|
static void check(Manager *m, Unit *unit, int status_expected, int code_expected) {
|
|
|
|
Service *service = NULL;
|
|
|
|
usec_t ts;
|
2017-09-28 23:41:06 +02:00
|
|
|
usec_t timeout = 2 * USEC_PER_MINUTE;
|
2014-11-11 20:05:40 +01:00
|
|
|
|
|
|
|
assert_se(m);
|
|
|
|
assert_se(unit);
|
|
|
|
|
|
|
|
service = SERVICE(unit);
|
|
|
|
printf("%s\n", unit->id);
|
|
|
|
exec_context_dump(&service->exec_context, stdout, "\t");
|
|
|
|
ts = now(CLOCK_MONOTONIC);
|
2017-09-29 09:58:22 +02:00
|
|
|
while (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED)) {
|
2014-11-11 20:05:40 +01:00
|
|
|
int r;
|
|
|
|
usec_t n;
|
|
|
|
|
|
|
|
r = sd_event_run(m->event, 100 * USEC_PER_MSEC);
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
n = now(CLOCK_MONOTONIC);
|
|
|
|
if (ts + timeout < n) {
|
|
|
|
log_error("Test timeout when testing %s", unit->id);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exec_status_dump(&service->main_exec_status, stdout, "\t");
|
|
|
|
assert_se(service->main_exec_status.status == status_expected);
|
|
|
|
assert_se(service->main_exec_status.code == code_expected);
|
|
|
|
}
|
|
|
|
|
2016-10-25 14:51:01 +02:00
|
|
|
static bool is_inaccessible_available(void) {
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
FOREACH_STRING(p,
|
|
|
|
"/run/systemd/inaccessible/reg",
|
|
|
|
"/run/systemd/inaccessible/dir",
|
|
|
|
"/run/systemd/inaccessible/chr",
|
|
|
|
"/run/systemd/inaccessible/blk",
|
|
|
|
"/run/systemd/inaccessible/fifo",
|
|
|
|
"/run/systemd/inaccessible/sock"
|
|
|
|
) {
|
|
|
|
if (access(p, F_OK) < 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
static void test(Manager *m, const char *unit_name, int status_expected, int code_expected) {
|
|
|
|
Unit *unit;
|
|
|
|
|
|
|
|
assert_se(unit_name);
|
|
|
|
|
|
|
|
assert_se(manager_load_unit(m, unit_name, NULL, NULL, &unit) >= 0);
|
|
|
|
assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
|
|
|
|
check(m, unit, status_expected, code_expected);
|
|
|
|
}
|
|
|
|
|
2017-10-12 06:25:06 +02:00
|
|
|
static void test_exec_bind_paths(Manager *m) {
|
|
|
|
assert_se(mkdir_p("/tmp/test-exec_bind_paths", 0755) >= 0);
|
|
|
|
assert_se(mkdir_p("/tmp/test-exec_bind_readonly_paths", 0755) >= 0);
|
|
|
|
|
|
|
|
test(m, "exec-bind-paths.service", 0, CLD_EXITED);
|
|
|
|
|
|
|
|
(void) rm_rf("/tmp/test-exec_bind_paths", REMOVE_ROOT|REMOVE_PHYSICAL);
|
|
|
|
(void) rm_rf("/tmp/test-exec_bind_readonly_paths", REMOVE_ROOT|REMOVE_PHYSICAL);
|
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
static void test_exec_workingdirectory(Manager *m) {
|
|
|
|
assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
|
|
|
|
|
|
|
|
test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
|
|
|
|
|
2015-04-04 11:52:57 +02:00
|
|
|
(void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
|
2014-11-11 20:05:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_personality(Manager *m) {
|
|
|
|
#if defined(__x86_64__)
|
|
|
|
test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
|
2015-09-24 12:47:22 +02:00
|
|
|
|
|
|
|
#elif defined(__s390__)
|
|
|
|
test(m, "exec-personality-s390.service", 0, CLD_EXITED);
|
|
|
|
|
2016-08-02 16:22:56 +02:00
|
|
|
#elif defined(__powerpc64__)
|
|
|
|
# if __BYTE_ORDER == __BIG_ENDIAN
|
|
|
|
test(m, "exec-personality-ppc64.service", 0, CLD_EXITED);
|
|
|
|
# else
|
|
|
|
test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
|
|
|
|
# endif
|
|
|
|
|
|
|
|
#elif defined(__aarch64__)
|
|
|
|
test(m, "exec-personality-aarch64.service", 0, CLD_EXITED);
|
|
|
|
|
2016-02-23 13:17:00 +01:00
|
|
|
#elif defined(__i386__)
|
2015-09-24 12:47:22 +02:00
|
|
|
test(m, "exec-personality-x86.service", 0, CLD_EXITED);
|
2014-11-11 20:05:40 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_ignoresigpipe(Manager *m) {
|
|
|
|
test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_privatetmp(Manager *m) {
|
|
|
|
assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
|
|
|
|
|
|
|
|
test(m, "exec-privatetmp-yes.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-privatetmp-no.service", 0, CLD_EXITED);
|
|
|
|
|
|
|
|
unlink("/tmp/test-exec_privatetmp");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_privatedevices(Manager *m) {
|
2016-07-27 23:23:44 +02:00
|
|
|
if (detect_container() > 0) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_notice("testing in container, skipping %s", __func__);
|
2016-07-27 23:23:44 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-10-25 14:51:01 +02:00
|
|
|
if (!is_inaccessible_available()) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_notice("testing without inaccessible, skipping %s", __func__);
|
2016-10-25 14:51:01 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
test(m, "exec-privatedevices-yes.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2016-09-25 13:04:30 +02:00
|
|
|
static void test_exec_privatedevices_capabilities(Manager *m) {
|
2017-02-12 06:21:02 +01:00
|
|
|
int r;
|
|
|
|
|
2016-09-25 13:04:30 +02:00
|
|
|
if (detect_container() > 0) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_notice("testing in container, skipping %s", __func__);
|
2016-09-25 13:04:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-10-25 14:51:01 +02:00
|
|
|
if (!is_inaccessible_available()) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_notice("testing without inaccessible, skipping %s", __func__);
|
2016-10-25 14:51:01 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-12 06:21:02 +01:00
|
|
|
/* We use capsh to test if the capabilities are
|
|
|
|
* properly set, so be sure that it exists */
|
|
|
|
r = find_binary("capsh", NULL);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
|
2016-10-25 14:51:01 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-25 13:04:30 +02:00
|
|
|
test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
|
2016-10-07 20:41:38 +02:00
|
|
|
test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED);
|
2016-09-25 13:04:30 +02:00
|
|
|
}
|
|
|
|
|
2016-10-09 12:38:45 +02:00
|
|
|
static void test_exec_protectkernelmodules(Manager *m) {
|
2017-02-12 06:21:02 +01:00
|
|
|
int r;
|
|
|
|
|
2016-10-07 19:17:34 +02:00
|
|
|
if (detect_container() > 0) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_notice("testing in container, skipping %s", __func__);
|
2016-10-07 19:17:34 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-10-25 14:51:01 +02:00
|
|
|
if (!is_inaccessible_available()) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_notice("testing without inaccessible, skipping %s", __func__);
|
2016-10-25 14:51:01 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-10-07 19:17:34 +02:00
|
|
|
|
2017-02-12 06:21:02 +01:00
|
|
|
r = find_binary("capsh", NULL);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-07 19:17:34 +02:00
|
|
|
test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
|
2016-10-09 12:38:45 +02:00
|
|
|
test(m, "exec-protectkernelmodules-yes-mount-propagation.service", 0, CLD_EXITED);
|
2016-10-07 19:17:34 +02:00
|
|
|
}
|
|
|
|
|
2016-09-25 19:24:25 +02:00
|
|
|
static void test_exec_readonlypaths(Manager *m) {
|
2016-12-21 00:58:08 +01:00
|
|
|
|
|
|
|
if (path_is_read_only_fs("/var") > 0)
|
|
|
|
return;
|
|
|
|
|
2016-09-25 19:24:25 +02:00
|
|
|
test(m, "exec-readonlypaths.service", 0, CLD_EXITED);
|
2016-09-25 19:50:25 +02:00
|
|
|
test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED);
|
2017-10-28 08:35:19 +02:00
|
|
|
test(m, "exec-readonlypaths-with-bindpaths.service", 0, CLD_EXITED);
|
2016-09-25 19:50:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_readwritepaths(Manager *m) {
|
2016-12-21 00:58:08 +01:00
|
|
|
|
|
|
|
if (path_is_read_only_fs("/") > 0)
|
|
|
|
return;
|
|
|
|
|
2016-09-25 19:50:25 +02:00
|
|
|
test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_inaccessiblepaths(Manager *m) {
|
2016-12-21 00:58:08 +01:00
|
|
|
|
|
|
|
if (path_is_read_only_fs("/") > 0)
|
|
|
|
return;
|
|
|
|
|
2016-09-25 19:50:25 +02:00
|
|
|
test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED);
|
2016-09-25 19:24:25 +02:00
|
|
|
}
|
|
|
|
|
2017-05-25 06:47:08 +02:00
|
|
|
static void test_exec_inaccessiblepaths_proc(Manager *m) {
|
2017-06-01 19:58:37 +02:00
|
|
|
if (!is_inaccessible_available()) {
|
|
|
|
log_notice("testing without inaccessible, skipping %s", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-25 06:47:08 +02:00
|
|
|
test(m, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
static void test_exec_systemcallfilter(Manager *m) {
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2016-08-22 21:40:58 +02:00
|
|
|
if (!is_seccomp_available())
|
|
|
|
return;
|
2014-11-11 20:05:40 +01:00
|
|
|
test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
|
|
|
|
test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
|
2017-11-11 13:39:02 +01:00
|
|
|
test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
|
|
|
|
test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
|
2016-08-22 21:40:58 +02:00
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_systemcallerrornumber(Manager *m) {
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2017-11-11 13:41:05 +01:00
|
|
|
if (!is_seccomp_available())
|
|
|
|
return;
|
|
|
|
test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
|
|
|
|
test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
|
2014-11-11 20:05:40 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-11-15 15:50:19 +01:00
|
|
|
static void test_exec_restrict_namespaces(Manager *m) {
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2016-11-15 15:50:19 +01:00
|
|
|
if (!is_seccomp_available())
|
|
|
|
return;
|
|
|
|
|
|
|
|
test(m, "exec-restrict-namespaces-no.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-restrict-namespaces-yes.service", 1, CLD_EXITED);
|
|
|
|
test(m, "exec-restrict-namespaces-mnt.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-restrict-namespaces-mnt-blacklist.service", 1, CLD_EXITED);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-01-30 17:26:39 +01:00
|
|
|
static void test_exec_systemcall_system_mode_with_user(Manager *m) {
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2016-08-22 21:40:58 +02:00
|
|
|
if (!is_seccomp_available())
|
|
|
|
return;
|
2016-01-30 17:26:39 +01:00
|
|
|
if (getpwnam("nobody"))
|
|
|
|
test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
|
2016-02-28 15:00:18 +01:00
|
|
|
else if (getpwnam("nfsnobody"))
|
|
|
|
test(m, "exec-systemcallfilter-system-user-nfsnobody.service", 0, CLD_EXITED);
|
2016-01-30 17:26:39 +01:00
|
|
|
else
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
|
2016-01-30 17:26:39 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
static void test_exec_user(Manager *m) {
|
2015-10-28 11:30:40 +01:00
|
|
|
if (getpwnam("nobody"))
|
|
|
|
test(m, "exec-user.service", 0, CLD_EXITED);
|
2016-02-28 15:00:18 +01:00
|
|
|
else if (getpwnam("nfsnobody"))
|
|
|
|
test(m, "exec-user-nfsnobody.service", 0, CLD_EXITED);
|
2015-10-29 14:22:23 +01:00
|
|
|
else
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
|
2014-11-11 20:05:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_group(Manager *m) {
|
2015-10-28 11:30:40 +01:00
|
|
|
if (getgrnam("nobody"))
|
|
|
|
test(m, "exec-group.service", 0, CLD_EXITED);
|
2016-02-28 15:00:18 +01:00
|
|
|
else if (getgrnam("nfsnobody"))
|
|
|
|
test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED);
|
2015-10-29 14:22:23 +01:00
|
|
|
else
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
|
2014-11-11 20:05:40 +01:00
|
|
|
}
|
|
|
|
|
2016-10-14 10:32:27 +02:00
|
|
|
static void test_exec_supplementary_groups(Manager *m) {
|
|
|
|
test(m, "exec-supplementarygroups.service", 0, CLD_EXITED);
|
2016-10-17 10:06:18 +02:00
|
|
|
test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED);
|
2016-10-24 12:38:53 +02:00
|
|
|
test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED);
|
2016-10-14 10:32:27 +02:00
|
|
|
}
|
|
|
|
|
2016-11-02 22:59:41 +01:00
|
|
|
static void test_exec_dynamic_user(Manager *m) {
|
|
|
|
test(m, "exec-dynamicuser-fixeduser.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", 0, CLD_EXITED);
|
2016-11-02 23:02:28 +01:00
|
|
|
test(m, "exec-dynamicuser-supplementarygroups.service", 0, CLD_EXITED);
|
2017-09-28 23:41:06 +02:00
|
|
|
test(m, "exec-dynamicuser-state-dir.service", 0, CLD_EXITED);
|
2016-11-02 22:59:41 +01:00
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
static void test_exec_environment(Manager *m) {
|
|
|
|
test(m, "exec-environment.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-environment-multiple.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-environment-empty.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2015-10-30 16:55:44 +01:00
|
|
|
static void test_exec_environmentfile(Manager *m) {
|
|
|
|
static const char e[] =
|
|
|
|
"VAR1='word1 word2'\n"
|
|
|
|
"VAR2=word3 \n"
|
|
|
|
"# comment1\n"
|
|
|
|
"\n"
|
|
|
|
"; comment2\n"
|
|
|
|
" ; # comment3\n"
|
|
|
|
"line without an equal\n"
|
|
|
|
"VAR3='$word 5 6'\n";
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
|
|
|
test(m, "exec-environmentfile.service", 0, CLD_EXITED);
|
|
|
|
|
|
|
|
unlink("/tmp/test-exec_environmentfile.conf");
|
|
|
|
}
|
|
|
|
|
2015-11-08 19:37:05 +01:00
|
|
|
static void test_exec_passenvironment(Manager *m) {
|
2015-11-11 18:24:34 +01:00
|
|
|
/* test-execute runs under MANAGER_USER which, by default, forwards all
|
|
|
|
* variables present in the environment, but only those that are
|
|
|
|
* present _at the time it is created_!
|
|
|
|
*
|
|
|
|
* So these PassEnvironment checks are still expected to work, since we
|
|
|
|
* are ensuring the variables are not present at manager creation (they
|
|
|
|
* are unset explicitly in main) and are only set here.
|
|
|
|
*
|
|
|
|
* This is still a good approximation of how a test for MANAGER_SYSTEM
|
|
|
|
* would work.
|
|
|
|
*/
|
2015-11-08 19:37:05 +01:00
|
|
|
assert_se(setenv("VAR1", "word1 word2", 1) == 0);
|
|
|
|
assert_se(setenv("VAR2", "word3", 1) == 0);
|
|
|
|
assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
|
|
|
|
test(m, "exec-passenvironment.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
|
|
|
|
assert_se(unsetenv("VAR1") == 0);
|
|
|
|
assert_se(unsetenv("VAR2") == 0);
|
|
|
|
assert_se(unsetenv("VAR3") == 0);
|
|
|
|
test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2014-12-11 17:59:10 +01:00
|
|
|
static void test_exec_umask(Manager *m) {
|
|
|
|
test(m, "exec-umask-default.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-umask-0177.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2015-09-21 15:36:07 +02:00
|
|
|
static void test_exec_runtimedirectory(Manager *m) {
|
|
|
|
test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
|
2015-10-28 11:30:40 +01:00
|
|
|
if (getgrnam("nobody"))
|
|
|
|
test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
|
2016-02-28 15:00:18 +01:00
|
|
|
else if (getgrnam("nfsnobody"))
|
|
|
|
test(m, "exec-runtimedirectory-owner-nfsnobody.service", 0, CLD_EXITED);
|
2015-10-29 14:22:23 +01:00
|
|
|
else
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
|
2015-10-29 14:22:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_exec_capabilityboundingset(Manager *m) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = find_binary("capsh", NULL);
|
|
|
|
if (r < 0) {
|
2017-02-12 06:21:02 +01:00
|
|
|
log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
|
2015-10-29 14:22:23 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
|
2015-09-21 15:36:07 +02:00
|
|
|
}
|
|
|
|
|
2016-01-05 12:34:41 +01:00
|
|
|
static void test_exec_capabilityambientset(Manager *m) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* Check if the kernel has support for ambient capabilities. Run
|
|
|
|
* the tests only if that's the case. Clearing all ambient
|
|
|
|
* capabilities is fine, since we are expecting them to be unset
|
|
|
|
* in the first place for the tests. */
|
|
|
|
r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
|
|
|
|
if (r >= 0 || errno != EINVAL) {
|
2016-02-28 15:00:18 +01:00
|
|
|
if (getpwnam("nobody")) {
|
|
|
|
test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
|
2016-03-21 18:40:07 +01:00
|
|
|
} else if (getpwnam("nfsnobody")) {
|
|
|
|
test(m, "exec-capabilityambientset-nfsnobody.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-capabilityambientset-merge-nfsnobody.service", 0, CLD_EXITED);
|
2016-02-28 15:00:18 +01:00
|
|
|
} else
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
|
2016-02-28 15:00:18 +01:00
|
|
|
} else
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(errno, "Skipping %s, the kernel does not support ambient capabilities: %m", __func__);
|
2016-01-05 12:34:41 +01:00
|
|
|
}
|
|
|
|
|
2015-10-30 13:51:51 +01:00
|
|
|
static void test_exec_privatenetwork(Manager *m) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = find_binary("ip", NULL);
|
|
|
|
if (r < 0) {
|
2017-02-12 06:26:00 +01:00
|
|
|
log_error_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
|
2015-10-30 13:51:51 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
test(m, "exec-privatenetwork-yes.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2015-10-30 17:09:22 +01:00
|
|
|
static void test_exec_oomscoreadjust(Manager *m) {
|
|
|
|
test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2015-10-30 17:46:32 +01:00
|
|
|
static void test_exec_ioschedulingclass(Manager *m) {
|
|
|
|
test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
|
|
|
|
test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2016-02-17 23:20:56 +01:00
|
|
|
static void test_exec_spec_interpolation(Manager *m) {
|
|
|
|
test(m, "exec-spec-interpolation.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
core: skip ReadOnlyPaths= and other permission-related mounts on PermissionsStartOnly= (#5309)
ReadOnlyPaths=, ProtectHome=, InaccessiblePaths= and ProtectSystem= are
about restricting access and little more, hence they should be disabled
if PermissionsStartOnly= is used or ExecStart= lines are prefixed with a
"+". Do that.
(Note that we will still create namespaces and stuff, since that's about
a lot more than just permissions. We'll simply disable the effect of
the four options mentioned above, but nothing else mount related.)
This also adds a test for this, to ensure this works as intended.
No documentation updates, as the documentation are already vague enough
to support the new behaviour ("If true, the permission-related execution
options…"). We could clarify this further, but I think we might want to
extend the switches' behaviour a bit more in future, hence leave it at
this for now.
Fixes: #5308
2017-02-12 06:44:46 +01:00
|
|
|
static void test_exec_read_only_path_suceed(Manager *m) {
|
|
|
|
test(m, "exec-read-only-path-succeed.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2017-09-11 19:10:06 +02:00
|
|
|
static void test_exec_unset_environment(Manager *m) {
|
|
|
|
test(m, "exec-unset-environment.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2017-10-11 14:07:51 +02:00
|
|
|
static void test_exec_specifier(Manager *m) {
|
|
|
|
test(m, "exec-specifier.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2017-10-27 11:38:59 +02:00
|
|
|
static void test_exec_stdin_data(Manager *m) {
|
|
|
|
test(m, "exec-stdin-data.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2017-10-27 16:16:19 +02:00
|
|
|
static void test_exec_stdio_file(Manager *m) {
|
|
|
|
test(m, "exec-stdio-file.service", 0, CLD_EXITED);
|
|
|
|
}
|
|
|
|
|
2016-12-05 20:12:46 +01:00
|
|
|
static int run_tests(UnitFileScope scope, const test_function_t *tests) {
|
|
|
|
const test_function_t *test = NULL;
|
2016-01-30 17:26:39 +01:00
|
|
|
Manager *m = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_se(tests);
|
|
|
|
|
2017-09-16 11:19:43 +02:00
|
|
|
r = manager_new(scope, MANAGER_TEST_RUN_MINIMAL, &m);
|
2016-01-30 17:26:39 +01:00
|
|
|
if (MANAGER_SKIP_TEST(r)) {
|
2016-09-15 14:21:42 +02:00
|
|
|
log_notice_errno(r, "Skipping test: manager_new: %m");
|
2016-01-30 17:26:39 +01:00
|
|
|
return EXIT_TEST_SKIP;
|
|
|
|
}
|
|
|
|
assert_se(r >= 0);
|
|
|
|
assert_se(manager_startup(m, NULL, NULL) >= 0);
|
|
|
|
|
|
|
|
for (test = tests; test && *test; test++)
|
|
|
|
(*test)(m);
|
|
|
|
|
|
|
|
manager_free(m);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-11 20:05:40 +01:00
|
|
|
int main(int argc, char *argv[]) {
|
2016-12-05 20:12:46 +01:00
|
|
|
static const test_function_t user_tests[] = {
|
2017-10-12 06:25:06 +02:00
|
|
|
test_exec_bind_paths,
|
2014-11-11 20:05:40 +01:00
|
|
|
test_exec_workingdirectory,
|
|
|
|
test_exec_personality,
|
|
|
|
test_exec_ignoresigpipe,
|
|
|
|
test_exec_privatetmp,
|
|
|
|
test_exec_privatedevices,
|
2016-09-25 13:04:30 +02:00
|
|
|
test_exec_privatedevices_capabilities,
|
2016-10-09 12:38:45 +02:00
|
|
|
test_exec_protectkernelmodules,
|
2016-09-25 19:24:25 +02:00
|
|
|
test_exec_readonlypaths,
|
2016-09-25 19:50:25 +02:00
|
|
|
test_exec_readwritepaths,
|
|
|
|
test_exec_inaccessiblepaths,
|
2017-05-25 06:47:08 +02:00
|
|
|
test_exec_inaccessiblepaths_proc,
|
2015-10-30 13:51:51 +01:00
|
|
|
test_exec_privatenetwork,
|
2014-11-11 20:05:40 +01:00
|
|
|
test_exec_systemcallfilter,
|
|
|
|
test_exec_systemcallerrornumber,
|
2016-11-15 15:50:19 +01:00
|
|
|
test_exec_restrict_namespaces,
|
2014-11-11 20:05:40 +01:00
|
|
|
test_exec_user,
|
|
|
|
test_exec_group,
|
2016-10-14 10:32:27 +02:00
|
|
|
test_exec_supplementary_groups,
|
2014-11-11 20:05:40 +01:00
|
|
|
test_exec_environment,
|
2015-10-30 16:55:44 +01:00
|
|
|
test_exec_environmentfile,
|
2015-11-08 19:37:05 +01:00
|
|
|
test_exec_passenvironment,
|
2014-12-11 17:59:10 +01:00
|
|
|
test_exec_umask,
|
2015-09-21 15:36:07 +02:00
|
|
|
test_exec_runtimedirectory,
|
2015-10-29 14:22:23 +01:00
|
|
|
test_exec_capabilityboundingset,
|
2016-01-05 12:34:41 +01:00
|
|
|
test_exec_capabilityambientset,
|
2015-10-30 17:09:22 +01:00
|
|
|
test_exec_oomscoreadjust,
|
2015-10-30 17:46:32 +01:00
|
|
|
test_exec_ioschedulingclass,
|
2016-02-17 23:20:56 +01:00
|
|
|
test_exec_spec_interpolation,
|
core: skip ReadOnlyPaths= and other permission-related mounts on PermissionsStartOnly= (#5309)
ReadOnlyPaths=, ProtectHome=, InaccessiblePaths= and ProtectSystem= are
about restricting access and little more, hence they should be disabled
if PermissionsStartOnly= is used or ExecStart= lines are prefixed with a
"+". Do that.
(Note that we will still create namespaces and stuff, since that's about
a lot more than just permissions. We'll simply disable the effect of
the four options mentioned above, but nothing else mount related.)
This also adds a test for this, to ensure this works as intended.
No documentation updates, as the documentation are already vague enough
to support the new behaviour ("If true, the permission-related execution
options…"). We could clarify this further, but I think we might want to
extend the switches' behaviour a bit more in future, hence leave it at
this for now.
Fixes: #5308
2017-02-12 06:44:46 +01:00
|
|
|
test_exec_read_only_path_suceed,
|
2017-09-11 19:10:06 +02:00
|
|
|
test_exec_unset_environment,
|
2017-10-27 11:38:59 +02:00
|
|
|
test_exec_stdin_data,
|
2017-10-27 16:16:19 +02:00
|
|
|
test_exec_stdio_file,
|
2014-11-11 20:05:40 +01:00
|
|
|
NULL,
|
|
|
|
};
|
2016-12-05 20:12:46 +01:00
|
|
|
static const test_function_t system_tests[] = {
|
2016-01-30 17:26:39 +01:00
|
|
|
test_exec_systemcall_system_mode_with_user,
|
2017-09-28 23:41:06 +02:00
|
|
|
test_exec_dynamic_user,
|
2017-10-11 14:07:51 +02:00
|
|
|
test_exec_specifier,
|
2016-01-30 17:26:39 +01:00
|
|
|
NULL,
|
|
|
|
};
|
2014-11-11 20:05:40 +01:00
|
|
|
int r;
|
|
|
|
|
seccomp: rework seccomp code, to improve compat with some archs
This substantially reworks the seccomp code, to ensure better
compatibility with some architectures, including i386.
So far we relied on libseccomp's internal handling of the multiple
syscall ABIs supported on Linux. This is problematic however, as it does
not define clear semantics if an ABI is not able to support specific
seccomp rules we install.
This rework hence changes a couple of things:
- We no longer use seccomp_rule_add(), but only
seccomp_rule_add_exact(), and fail the installation of a filter if the
architecture doesn't support it.
- We no longer rely on adding multiple syscall architectures to a single filter,
but instead install a separate filter for each syscall architecture
supported. This way, we can install a strict filter for x86-64, while
permitting a less strict filter for i386.
- All high-level filter additions are now moved from execute.c to
seccomp-util.c, so that we can test them independently of the service
execution logic.
- Tests have been added for all types of our seccomp filters.
- SystemCallFilters= and SystemCallArchitectures= are now implemented in
independent filters and installation logic, as they semantically are
very much independent of each other.
Fixes: #4575
2016-12-27 15:28:25 +01:00
|
|
|
log_set_max_level(LOG_DEBUG);
|
2014-11-11 20:05:40 +01:00
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
2015-01-18 20:07:51 +01:00
|
|
|
/* It is needed otherwise cgroup creation fails */
|
|
|
|
if (getuid() != 0) {
|
2017-10-10 20:55:20 +02:00
|
|
|
puts("Skipping test: not root");
|
2015-01-18 20:07:51 +01:00
|
|
|
return EXIT_TEST_SKIP;
|
|
|
|
}
|
|
|
|
|
2017-10-10 20:55:20 +02:00
|
|
|
r = enter_cgroup_subroot();
|
|
|
|
if (r == -ENOMEDIUM) {
|
|
|
|
puts("Skipping test: cgroupfs not available");
|
|
|
|
return EXIT_TEST_SKIP;
|
|
|
|
}
|
tests: when running a manager object in a test, migrate to private cgroup subroot first (#6576)
Without this "meson test" will end up running all tests in the same
cgroup root, and they all will try to manage it. Which usually isn't too
bad, except when they end up clearing up each other's cgroups. This race
is hard to trigger but has caused various CI runs to fail spuriously.
With this change we simply move every test that runs a manager object
into their own private cgroup. Note that we don't clean up the cgroup at
the end, we leave that to the cgroup manager around it.
This fixes races that become visible by test runs throwing out errors
like this:
```
exec-systemcallfilter-failing.service: Passing 0 fds to service
exec-systemcallfilter-failing.service: About to execute: /bin/echo 'This should not be seen'
exec-systemcallfilter-failing.service: Forked /bin/echo as 5693
exec-systemcallfilter-failing.service: Changed dead -> start
exec-systemcallfilter-failing.service: Failed to attach to cgroup /exec-systemcallfilter-failing.service: No such file or directory
Received SIGCHLD from PID 5693 ((echo)).
Child 5693 ((echo)) died (code=exited, status=219/CGROUP)
exec-systemcallfilter-failing.service: Child 5693 belongs to exec-systemcallfilter-failing.service
exec-systemcallfilter-failing.service: Main process exited, code=exited, status=219/CGROUP
exec-systemcallfilter-failing.service: Changed start -> failed
exec-systemcallfilter-failing.service: Unit entered failed state.
exec-systemcallfilter-failing.service: Failed with result 'exit-code'.
exec-systemcallfilter-failing.service: cgroup is empty
Assertion 'service->main_exec_status.status == status_expected' failed at ../src/src/test/test-execute.c:71, function check(). Aborting.
```
BTW, I tracked this race down by using perf:
```
# perf record -e cgroup:cgroup_mkdir,cgroup_rmdir
…
# perf script
```
Thanks a lot @iaguis, @alban for helping me how to use perf for this.
Fixes #5895.
2017-08-09 15:42:49 +02:00
|
|
|
|
2015-09-21 15:36:07 +02:00
|
|
|
assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0);
|
2017-02-15 23:37:25 +01:00
|
|
|
assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0);
|
2014-11-11 20:05:40 +01:00
|
|
|
|
2015-11-11 18:24:34 +01:00
|
|
|
/* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
|
|
|
|
* cases, otherwise (and if they are present in the environment),
|
|
|
|
* `manager_default_environment` will copy them into the default
|
|
|
|
* environment which is passed to each created job, which will make the
|
|
|
|
* tests that expect those not to be present to fail.
|
|
|
|
*/
|
|
|
|
assert_se(unsetenv("VAR1") == 0);
|
|
|
|
assert_se(unsetenv("VAR2") == 0);
|
|
|
|
assert_se(unsetenv("VAR3") == 0);
|
|
|
|
|
2016-02-24 21:24:23 +01:00
|
|
|
r = run_tests(UNIT_FILE_USER, user_tests);
|
2016-01-30 17:26:39 +01:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2014-11-11 20:05:40 +01:00
|
|
|
|
2016-02-24 21:24:23 +01:00
|
|
|
return run_tests(UNIT_FILE_SYSTEM, system_tests);
|
2014-11-11 20:05:40 +01:00
|
|
|
}
|