mirror of
https://github.com/systemd/systemd
synced 2026-03-18 19:14:46 +01:00
Compare commits
20 Commits
cf79f61238
...
fd8a1deb0b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd8a1deb0b | ||
|
|
2c751f3420 | ||
|
|
3a1231f4c7 | ||
|
|
75f04abf16 | ||
|
|
f8e1a7a66e | ||
|
|
4b23f4f4c2 | ||
|
|
8a27100d06 | ||
|
|
c1f873b4f4 | ||
|
|
2eaca3ea5f | ||
|
|
57682793da | ||
|
|
239903d44c | ||
|
|
4a4be1015b | ||
|
|
22aa8c4879 | ||
|
|
1ee73c884e | ||
|
|
b30f77cd12 | ||
|
|
1d26e7c1d5 | ||
|
|
abd17aa3f4 | ||
|
|
8e92910b98 | ||
|
|
2716906246 | ||
|
|
d7bc1e3be9 |
@ -90,7 +90,7 @@
|
||||
socket passing (i.e. sockets passed in via standard input and output, using
|
||||
<varname>StandardInput=socket</varname> in the service file).</para>
|
||||
|
||||
<para>All network sockets allocated through <filename>.socket</filename> units are allocated in the host's network
|
||||
<para>By default, network sockets allocated through <filename>.socket</filename> units are allocated in the host's network
|
||||
namespace (see <citerefentry
|
||||
project='man-pages'><refentrytitle>network_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>). This
|
||||
does not mean however that the service activated by a configured socket unit has to be part of the host's network
|
||||
@ -101,6 +101,11 @@
|
||||
the host's network namespace is only permitted through the activation sockets passed in while all sockets allocated
|
||||
from the service code itself will be associated with the service's own namespace, and thus possibly subject to a
|
||||
restrictive configuration.</para>
|
||||
|
||||
<para>Alternatively, it is possible to run a <filename>.socket</filename> unit in another network namespace
|
||||
by setting <option>PrivateNetwork=yes</option> in combination with <varname>JoinsNamespaceOf=</varname>, see
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@ -389,11 +389,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
|
||||
}
|
||||
|
||||
int pid_is_kernel_thread(pid_t pid) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
unsigned long long flags;
|
||||
size_t l, i;
|
||||
const char *p;
|
||||
char *q;
|
||||
int r;
|
||||
|
||||
if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
|
||||
@ -401,7 +396,8 @@ int pid_is_kernel_thread(pid_t pid) {
|
||||
if (!pid_is_valid(pid))
|
||||
return -EINVAL;
|
||||
|
||||
p = procfs_file_alloca(pid, "stat");
|
||||
const char *p = procfs_file_alloca(pid, "stat");
|
||||
_cleanup_free_ char *line = NULL;
|
||||
r = read_one_line_file(p, &line);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
@ -409,14 +405,14 @@ int pid_is_kernel_thread(pid_t pid) {
|
||||
return r;
|
||||
|
||||
/* Skip past the comm field */
|
||||
q = strrchr(line, ')');
|
||||
char *q = strrchr(line, ')');
|
||||
if (!q)
|
||||
return -EINVAL;
|
||||
q++;
|
||||
|
||||
/* Skip 6 fields to reach the flags field */
|
||||
for (i = 0; i < 6; i++) {
|
||||
l = strspn(q, WHITESPACE);
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
size_t l = strspn(q, WHITESPACE);
|
||||
if (l < 1)
|
||||
return -EINVAL;
|
||||
q += l;
|
||||
@ -428,7 +424,7 @@ int pid_is_kernel_thread(pid_t pid) {
|
||||
}
|
||||
|
||||
/* Skip preceding whitespace */
|
||||
l = strspn(q, WHITESPACE);
|
||||
size_t l = strspn(q, WHITESPACE);
|
||||
if (l < 1)
|
||||
return -EINVAL;
|
||||
q += l;
|
||||
@ -439,6 +435,7 @@ int pid_is_kernel_thread(pid_t pid) {
|
||||
return -EINVAL;
|
||||
q[l] = 0;
|
||||
|
||||
unsigned long long flags;
|
||||
r = safe_atollu(q, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -883,25 +880,23 @@ int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFl
|
||||
siginfo_t status;
|
||||
r = pidref_wait_for_terminate(pidref, &status);
|
||||
if (r < 0)
|
||||
return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
|
||||
return log_full_errno(prio, r, "Failed to wait for '%s': %m", strna(name));
|
||||
|
||||
if (status.si_code == CLD_EXITED) {
|
||||
if (status.si_status != EXIT_SUCCESS)
|
||||
log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
|
||||
"%s failed with exit status %i.", strna(name), status.si_status);
|
||||
"'%s' failed with exit status %i.", strna(name), status.si_status);
|
||||
else
|
||||
log_debug("%s succeeded.", name);
|
||||
log_debug("'%s' succeeded.", name);
|
||||
|
||||
return status.si_status;
|
||||
|
||||
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
|
||||
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED))
|
||||
return log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO),
|
||||
"'%s' terminated by signal %s.", strna(name), signal_to_string(status.si_status));
|
||||
|
||||
log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
log_full(prio, "%s failed due to unknown reason.", strna(name));
|
||||
return -EPROTO;
|
||||
return log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO),
|
||||
"'%s' failed due to unknown reason.", strna(name));
|
||||
}
|
||||
|
||||
int kill_and_sigcont(pid_t pid, int sig) {
|
||||
|
||||
@ -15,51 +15,71 @@
|
||||
#include "virt.h"
|
||||
|
||||
static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
|
||||
char utf8[DECIMAL_STR_MAX(usec_t)];
|
||||
char16_t *encoded;
|
||||
char buf[DECIMAL_STR_MAX(usec_t)];
|
||||
usec_t timeout;
|
||||
bool menu_disabled = false;
|
||||
uint64_t loader_features = 0;
|
||||
int r;
|
||||
|
||||
assert(arg1);
|
||||
assert(ret_timeout);
|
||||
assert(ret_timeout_size);
|
||||
|
||||
assert_cc(STRLEN("menu-disabled") < ELEMENTSOF(utf8));
|
||||
assert_cc(STRLEN("menu-force") < ELEMENTSOF(buf));
|
||||
assert_cc(STRLEN("menu-hidden") < ELEMENTSOF(buf));
|
||||
assert_cc(STRLEN("menu-disabled") < ELEMENTSOF(buf));
|
||||
|
||||
/* Note: Since there is no way to query if the bootloader supports the string tokens, we explicitly
|
||||
* set their numerical value(s) instead. This means that some of the sd-boot internal ABI has leaked
|
||||
* although the ship has sailed and the side-effects are self-contained.
|
||||
/* Use feature EFI_LOADER_FEATURE_MENU_DISABLE as a mark that the boot loader supports the other
|
||||
* string values too. When unsupported, convert to the timeout with the closest meaning.
|
||||
*/
|
||||
if (streq(arg1, "menu-force"))
|
||||
timeout = USEC_INFINITY;
|
||||
else if (streq(arg1, "menu-hidden"))
|
||||
timeout = 0;
|
||||
else if (streq(arg1, "menu-disabled")) {
|
||||
uint64_t loader_features = 0;
|
||||
|
||||
if (streq(arg1, "menu-force")) {
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
|
||||
if (!(loader_features & EFI_LOADER_FEATURE_MENU_DISABLE)) {
|
||||
log_debug("Using maximum timeout instead of '%s'.", arg1);
|
||||
timeout = USEC_INFINITY;
|
||||
arg1 = NULL;
|
||||
}
|
||||
|
||||
} else if (streq(arg1, "menu-hidden")) {
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
|
||||
if (!(loader_features & EFI_LOADER_FEATURE_MENU_DISABLE)) {
|
||||
log_debug("Using zero timeout instead of '%s'.", arg1);
|
||||
timeout = 0;
|
||||
arg1 = NULL; /* replace the arg by printed timeout value later */
|
||||
}
|
||||
|
||||
} else if (streq(arg1, "menu-disabled")) {
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
|
||||
if (!(loader_features & EFI_LOADER_FEATURE_MENU_DISABLE)) {
|
||||
if (arg_graceful() == ARG_GRACEFUL_NO)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Loader does not support 'menu-disabled'.");
|
||||
|
||||
log_warning("Loader does not support 'menu-disabled', setting anyway.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Loader does not support '%s'.", arg1);
|
||||
log_warning("Using zero timeout instead of '%s'.", arg1);
|
||||
timeout = 0;
|
||||
arg1 = NULL;
|
||||
}
|
||||
menu_disabled = true;
|
||||
|
||||
} else {
|
||||
r = parse_time(arg1, &timeout, USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
|
||||
if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
|
||||
log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
|
||||
|
||||
assert_cc(USEC_INFINITY > UINT32_MAX * USEC_PER_SEC);
|
||||
if (timeout > UINT32_MAX * USEC_PER_SEC && timeout != USEC_INFINITY)
|
||||
log_debug("Timeout is too long and will be clamped to maximum timeout.");
|
||||
|
||||
arg1 = NULL;
|
||||
}
|
||||
|
||||
if (menu_disabled)
|
||||
xsprintf(utf8, "menu-disabled");
|
||||
else
|
||||
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
|
||||
if (!arg1) {
|
||||
timeout = DIV_ROUND_UP(timeout, USEC_PER_SEC);
|
||||
xsprintf(buf, USEC_FMT, MIN(timeout, UINT32_MAX));
|
||||
}
|
||||
|
||||
encoded = utf8_to_utf16(utf8, SIZE_MAX);
|
||||
char16_t *encoded = utf8_to_utf16(arg1 ?: buf, SIZE_MAX);
|
||||
if (!encoded)
|
||||
return log_oom();
|
||||
|
||||
|
||||
@ -804,10 +804,6 @@ static int automount_start(Unit *u) {
|
||||
if (path_is_mount_point(a->where) > 0)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EEXIST), "Path %s is already a mount point, refusing start.", a->where);
|
||||
|
||||
r = unit_test_trigger_loaded(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_acquire_invocation_id(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1047,6 +1043,10 @@ static int automount_test_startable(Unit *u) {
|
||||
Automount *a = ASSERT_PTR(AUTOMOUNT(u));
|
||||
int r;
|
||||
|
||||
r = unit_test_trigger_loaded(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_test_start_limit(u);
|
||||
if (r < 0) {
|
||||
automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
|
||||
|
||||
@ -5463,17 +5463,14 @@ int exec_invoke(
|
||||
.sched_flags = context->cpu_sched_reset_on_fork ? SCHED_FLAG_RESET_ON_FORK : 0,
|
||||
};
|
||||
|
||||
r = sched_setattr(/* pid= */ 0, &attr, /* flags= */ 0);
|
||||
if (r < 0) {
|
||||
if (errno != EINVAL || sched_policy_supported(attr.sched_policy)) {
|
||||
*exit_status = EXIT_SETSCHEDULER;
|
||||
return log_error_errno(errno, "Failed to set up CPU scheduling: %m");
|
||||
}
|
||||
|
||||
r = RET_NERRNO(sched_setattr(/* pid= */ 0, &attr, /* flags= */ 0));
|
||||
if (r == -EINVAL && !sched_policy_supported(context->cpu_sched_policy)) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) sched_policy_to_string_alloc(context->cpu_sched_policy, &s);
|
||||
|
||||
log_warning_errno(errno, "CPU scheduling policy %s is not supported, proceeding without.", strna(s));
|
||||
log_warning_errno(r, "CPU scheduling policy %s is not supported, proceeding without.", strna(s));
|
||||
} else if (r < 0) {
|
||||
*exit_status = EXIT_SETSCHEDULER;
|
||||
return log_error_errno(r, "Failed to set up CPU scheduling: %m");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2605,6 +2605,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
dissected_image,
|
||||
/* root= */ NULL,
|
||||
/* passphrase= */ NULL,
|
||||
p->verity,
|
||||
p->root_image_policy,
|
||||
|
||||
@ -632,10 +632,6 @@ static int path_start(Unit *u) {
|
||||
|
||||
assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
|
||||
|
||||
r = unit_test_trigger_loaded(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_acquire_invocation_id(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -902,6 +898,10 @@ static int path_test_startable(Unit *u) {
|
||||
Path *p = ASSERT_PTR(PATH(u));
|
||||
int r;
|
||||
|
||||
r = unit_test_trigger_loaded(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_test_start_limit(u);
|
||||
if (r < 0) {
|
||||
path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
|
||||
|
||||
@ -2623,20 +2623,6 @@ static int socket_start(Unit *u) {
|
||||
Socket *s = ASSERT_PTR(SOCKET(u));
|
||||
int r;
|
||||
|
||||
/* Cannot run this without the service being around */
|
||||
if (UNIT_ISSET(s->service)) {
|
||||
Service *service = ASSERT_PTR(SERVICE(UNIT_DEREF(s->service)));
|
||||
|
||||
if (UNIT(service)->load_state != UNIT_LOADED)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT),
|
||||
"Socket service %s not loaded, refusing.", UNIT(service)->id);
|
||||
|
||||
/* If the service is already active we cannot start the socket */
|
||||
if (SOCKET_SERVICE_IS_ACTIVE(service, /* allow_finalize= */ false))
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY),
|
||||
"Socket service %s already active, refusing.", UNIT(service)->id);
|
||||
}
|
||||
|
||||
assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED));
|
||||
|
||||
r = unit_acquire_invocation_id(u);
|
||||
@ -3642,6 +3628,20 @@ static int socket_test_startable(Unit *u) {
|
||||
SOCKET_START_POST))
|
||||
return false;
|
||||
|
||||
/* Cannot run this without the service being around */
|
||||
if (UNIT_ISSET(s->service)) {
|
||||
Service *service = ASSERT_PTR(SERVICE(UNIT_DEREF(s->service)));
|
||||
|
||||
if (UNIT(service)->load_state != UNIT_LOADED)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT),
|
||||
"Socket service %s not loaded, refusing.", UNIT(service)->id);
|
||||
|
||||
/* If the service is already active we cannot start the socket */
|
||||
if (SOCKET_SERVICE_IS_ACTIVE(service, /* allow_finalize= */ false))
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY),
|
||||
"Socket service %s already active, refusing.", UNIT(service)->id);
|
||||
}
|
||||
|
||||
r = unit_test_start_limit(u);
|
||||
if (r < 0) {
|
||||
socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
|
||||
|
||||
@ -668,10 +668,6 @@ static int timer_start(Unit *u) {
|
||||
|
||||
assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED));
|
||||
|
||||
r = unit_test_trigger_loaded(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_acquire_invocation_id(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -917,6 +913,10 @@ static int timer_test_startable(Unit *u) {
|
||||
Timer *t = ASSERT_PTR(TIMER(u));
|
||||
int r;
|
||||
|
||||
r = unit_test_trigger_loaded(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_test_start_limit(u);
|
||||
if (r < 0) {
|
||||
timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
|
||||
|
||||
@ -1178,6 +1178,28 @@ static bool shall_stop_on_isolate(Transaction *tr, Unit *u) {
|
||||
if (hashmap_contains(tr->jobs, u))
|
||||
return false;
|
||||
|
||||
/* Keep units that are triggered by units we want to keep around. */
|
||||
Unit *other;
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_TRIGGERED_BY) {
|
||||
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
|
||||
continue;
|
||||
|
||||
/* Is the trigger about to go down? */
|
||||
Job *other_job = hashmap_get(tr->jobs, other);
|
||||
|
||||
bool has_stop = false;
|
||||
LIST_FOREACH(transaction, j, other_job)
|
||||
if (j->type == JOB_STOP) {
|
||||
has_stop = true;
|
||||
break;
|
||||
}
|
||||
if (has_stop)
|
||||
continue;
|
||||
|
||||
if (other->ignore_on_isolate || other_job)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1191,7 +1213,6 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
|
||||
|
||||
HASHMAP_FOREACH_KEY(u, k, m->units) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
|
||||
Unit *o;
|
||||
|
||||
/* Ignore aliases. */
|
||||
if (u->id != k)
|
||||
@ -1204,16 +1225,6 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
|
||||
if (!shall_stop_on_isolate(tr, u))
|
||||
continue;
|
||||
|
||||
/* Keep units that are triggered by units we want to keep around. */
|
||||
bool keep = false;
|
||||
UNIT_FOREACH_DEPENDENCY(o, u, UNIT_ATOM_TRIGGERED_BY)
|
||||
if (!shall_stop_on_isolate(tr, o)) {
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
if (keep)
|
||||
continue;
|
||||
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, TRANSACTION_MATTERS, &e);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %s", bus_error_message(&e, r));
|
||||
|
||||
@ -3927,8 +3927,7 @@ bool unit_active_or_pending(Unit *u) {
|
||||
if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
|
||||
return true;
|
||||
|
||||
if (u->job &&
|
||||
IN_SET(u->job->type, JOB_START, JOB_RELOAD_OR_START, JOB_RESTART))
|
||||
if (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@ -368,11 +368,11 @@ static int run(int argc, char *argv[]) {
|
||||
/* Child */
|
||||
execvp(arguments[0], arguments);
|
||||
log_open();
|
||||
log_error_errno(errno, "Failed to execute %s: %m", argv[optind]);
|
||||
log_error_errno(errno, "Failed to execute '%s': %m", arguments[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return pidref_wait_for_terminate_and_check(argv[optind], &pidref, WAIT_LOG);
|
||||
return pidref_wait_for_terminate_and_check(argv[optind], &pidref, WAIT_LOG_ABNORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -295,7 +295,7 @@ int bus_image_method_get_hostname(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@ -314,7 +314,7 @@ int bus_image_method_get_machine_id(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@ -343,7 +343,7 @@ int bus_image_method_get_machine_info(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@ -361,7 +361,7 @@ int bus_image_method_get_os_release(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
|
||||
@ -627,7 +627,7 @@ static int list_image_one_and_maybe_read_metadata(Manager *m, sd_varlink *link,
|
||||
assert(image);
|
||||
|
||||
if (should_acquire_metadata(am) && !image->metadata_valid) {
|
||||
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0 && am != ACQUIRE_METADATA_GRACEFUL)
|
||||
return log_debug_errno(r, "Failed to read image metadata: %m");
|
||||
if (r < 0)
|
||||
|
||||
@ -540,6 +540,7 @@ static int vl_method_mount_image(
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
di,
|
||||
/* root= */ NULL,
|
||||
p.password,
|
||||
&verity,
|
||||
use_policy,
|
||||
|
||||
@ -75,6 +75,8 @@ static const struct group root_group = {
|
||||
static const struct sgrp root_sgrp = {
|
||||
.sg_namp = (char*) "root",
|
||||
.sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
|
||||
.sg_adm = (char*[]) { NULL },
|
||||
.sg_mem = (char*[]) { NULL },
|
||||
};
|
||||
|
||||
static const struct group nobody_group = {
|
||||
@ -87,6 +89,8 @@ static const struct group nobody_group = {
|
||||
static const struct sgrp nobody_sgrp = {
|
||||
.sg_namp = (char*) NOBODY_GROUP_NAME,
|
||||
.sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
|
||||
.sg_adm = (char*[]) { NULL },
|
||||
.sg_mem = (char*[]) { NULL },
|
||||
};
|
||||
|
||||
typedef struct GetentData {
|
||||
@ -257,12 +261,18 @@ static enum nss_status copy_synthesized_sgrp(
|
||||
assert(src);
|
||||
assert(src->sg_namp);
|
||||
assert(src->sg_passwd);
|
||||
assert(src->sg_adm);
|
||||
assert(!*src->sg_adm); /* Our synthesized records' sg_adm is always just NULL... */
|
||||
assert(src->sg_mem);
|
||||
assert(!*src->sg_mem); /* Our synthesized records' sg_mem is always just NULL... */
|
||||
|
||||
size_t required =
|
||||
strlen(src->sg_namp) + 1 +
|
||||
strlen(src->sg_passwd) + 1;
|
||||
strlen(src->sg_passwd) + 1 +
|
||||
sizeof(char*) + /* NULL terminator storage for src->sg_adm */
|
||||
sizeof(char*); /* NULL terminator storage for src->sg_mem */
|
||||
|
||||
if (buflen < required) {
|
||||
if (buflen < ALIGN(required)) {
|
||||
*errnop = ERANGE;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
@ -274,7 +284,10 @@ static enum nss_status copy_synthesized_sgrp(
|
||||
/* String fields point into the user-provided buffer */
|
||||
dest->sg_namp = buffer;
|
||||
dest->sg_passwd = stpcpy(dest->sg_namp, src->sg_namp) + 1;
|
||||
strcpy(dest->sg_passwd, src->sg_passwd);
|
||||
dest->sg_adm = ALIGN_PTR(stpcpy(dest->sg_passwd, src->sg_passwd) + 1);
|
||||
*dest->sg_adm = NULL;
|
||||
dest->sg_mem = dest->sg_adm + 1;
|
||||
*dest->sg_mem = NULL;
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -411,6 +411,40 @@ enum nss_status userdb_getgrgid(
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Counts string pointers (including terminating NULL element) of given
|
||||
* string vector strv and stores amount of pointers in n and total
|
||||
* length of all contained strings including NUL bytes in len. */
|
||||
static void nss_count_strv(char * const *strv, size_t *n, size_t *len) {
|
||||
STRV_FOREACH(str, strv) {
|
||||
(*len) += sizeof(char*); /* space for array entry */
|
||||
(*len) += strlen(*str) + 1;
|
||||
(*n)++;
|
||||
}
|
||||
(*len) += sizeof(char*); /* trailing NULL in array entry */
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
/* Performs deep copy of given string vector src and stores content
|
||||
* of contained strings into buf with references to these strings
|
||||
* in dst. At dst location, a new NULL-terminated string vector is
|
||||
* created. The dst and buf locations are updated to point just behind
|
||||
* the last pointer or char respectively. Returns total amount of
|
||||
* pointers in newly created string vector in dst, including the
|
||||
* terminating NULL element. */
|
||||
static size_t nss_deep_copy_strv(char * const *src, char ***dst, char **buf) {
|
||||
char *p = *buf;
|
||||
size_t i = 0;
|
||||
|
||||
STRV_FOREACH(str, src) {
|
||||
(*dst)[i++] = p;
|
||||
p = stpcpy(p, *str) + 1;
|
||||
}
|
||||
(*dst)[i++] = NULL;
|
||||
*dst += i;
|
||||
*buf = p;
|
||||
return i;
|
||||
}
|
||||
|
||||
int nss_pack_group_record_shadow(
|
||||
GroupRecord *hr,
|
||||
struct sgrp *sgrp,
|
||||
@ -418,7 +452,8 @@ int nss_pack_group_record_shadow(
|
||||
size_t buflen) {
|
||||
|
||||
const char *hashed;
|
||||
size_t required;
|
||||
char **array = NULL, *p;
|
||||
size_t i = 0, n = 0, required;
|
||||
|
||||
assert(hr);
|
||||
assert(sgrp);
|
||||
@ -429,15 +464,26 @@ int nss_pack_group_record_shadow(
|
||||
assert_se(hashed = strv_isempty(hr->hashed_password) ? PASSWORD_LOCKED_AND_INVALID : hr->hashed_password[0]);
|
||||
required += strlen(hashed) + 1;
|
||||
|
||||
nss_count_strv(hr->administrators, &n, &required);
|
||||
nss_count_strv(hr->members, &n, &required);
|
||||
|
||||
if (buflen < required)
|
||||
return -ERANGE;
|
||||
|
||||
*sgrp = (struct sgrp) {
|
||||
.sg_namp = buffer,
|
||||
};
|
||||
|
||||
assert(buffer);
|
||||
|
||||
p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */
|
||||
array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
|
||||
|
||||
sgrp->sg_mem = array;
|
||||
i += nss_deep_copy_strv(hr->members, &array, &p);
|
||||
|
||||
sgrp->sg_adm = array;
|
||||
i += nss_deep_copy_strv(hr->administrators, &array, &p);
|
||||
|
||||
assert_se(i == n);
|
||||
|
||||
sgrp->sg_namp = p;
|
||||
sgrp->sg_passwd = stpcpy(sgrp->sg_namp, hr->group_name) + 1;
|
||||
strcpy(sgrp->sg_passwd, hashed);
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ int bus_image_common_get_os_release(
|
||||
return 1;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image, &image_policy_service, m->runtime_scope);
|
||||
r = image_read_metadata(image, /* root= */ NULL, &image_policy_service, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
|
||||
@ -2027,7 +2027,7 @@ int image_setup_pool(RuntimeScope scope, ImageClass class, bool use_btrfs_subvol
|
||||
return 0;
|
||||
}
|
||||
|
||||
int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope scope) {
|
||||
int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_policy, RuntimeScope scope) {
|
||||
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
|
||||
int r;
|
||||
|
||||
@ -2153,6 +2153,7 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
m,
|
||||
root,
|
||||
/* passphrase= */ NULL,
|
||||
&verity,
|
||||
image_policy,
|
||||
|
||||
@ -71,7 +71,7 @@ int image_get_pool_usage(RuntimeScope scope, ImageClass class, uint64_t *ret);
|
||||
int image_get_pool_limit(RuntimeScope scope, ImageClass class, uint64_t *ret);
|
||||
int image_setup_pool(RuntimeScope scope, ImageClass class, bool use_btrfs_subvol, bool use_btrfs_quota);
|
||||
|
||||
int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope scope);
|
||||
int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_policy, RuntimeScope scope);
|
||||
|
||||
bool image_in_search_path(RuntimeScope scope, ImageClass class, const char *root, const char *image);
|
||||
|
||||
|
||||
@ -3093,7 +3093,7 @@ static char* dm_deferred_remove_clean(char *name) {
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
|
||||
|
||||
static int validate_signature_userspace(const VeritySettings *verity, DissectImageFlags flags) {
|
||||
static int validate_signature_userspace(const VeritySettings *verity, const char *root, DissectImageFlags flags) {
|
||||
int r;
|
||||
|
||||
/* Returns > 0 if signature checks out, == 0 if not, < 0 on unexpected errors */
|
||||
@ -3138,7 +3138,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
||||
/* Because installing a signature certificate into the kernel chain is so messy, let's optionally do
|
||||
* userspace validation. */
|
||||
|
||||
r = conf_files_list_nulstr(&certs, ".crt", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, CONF_PATHS_NULSTR("verity.d"));
|
||||
r = conf_files_list_nulstr(&certs, ".crt", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, CONF_PATHS_NULSTR("verity.d"));
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to enumerate certificates: %m");
|
||||
if (strv_isempty(certs)) {
|
||||
@ -3200,6 +3200,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
||||
|
||||
static int do_crypt_activate_verity(
|
||||
struct crypt_device *cd,
|
||||
const char *root,
|
||||
const char *name,
|
||||
const VeritySettings *verity,
|
||||
DissectImageFlags flags,
|
||||
@ -3249,7 +3250,7 @@ static int do_crypt_activate_verity(
|
||||
|
||||
/* Preferably propagate the original kernel error, so that the fallback logic can work,
|
||||
* as the device-mapper is finicky around concurrent activations of the same volume */
|
||||
k = validate_signature_userspace(verity, flags);
|
||||
k = validate_signature_userspace(verity, root, flags);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k == 0) {
|
||||
@ -3309,6 +3310,7 @@ static int verity_partition(
|
||||
PartitionDesignator designator,
|
||||
DissectedPartition *m, /* data partition */
|
||||
DissectedPartition *v, /* verity partition */
|
||||
const char *root, /* The root to get user verity certs from (for a sysext) */
|
||||
const VeritySettings *verity,
|
||||
DissectImageFlags flags,
|
||||
PartitionPolicyFlags policy_flags,
|
||||
@ -3394,7 +3396,7 @@ static int verity_partition(
|
||||
goto check; /* The device already exists. Let's check it. */
|
||||
|
||||
/* The symlink to the device node does not exist yet. Assume not activated, and let's activate it. */
|
||||
r = do_crypt_activate_verity(cd, name, verity, flags, policy_flags);
|
||||
r = do_crypt_activate_verity(cd, root, name, verity, flags, policy_flags);
|
||||
if (r >= 0)
|
||||
goto try_open; /* The device is activated. Let's open it. */
|
||||
/* libdevmapper can return EINVAL when the device is already in the activation stage.
|
||||
@ -3488,7 +3490,7 @@ static int verity_partition(
|
||||
*/
|
||||
sym_crypt_free(cd);
|
||||
cd = NULL;
|
||||
return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, policy_flags, d);
|
||||
return verity_partition(designator, m, v, root, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, policy_flags, d);
|
||||
}
|
||||
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "All attempts to activate verity device %s failed.", name);
|
||||
@ -3508,6 +3510,7 @@ success:
|
||||
|
||||
int dissected_image_decrypt(
|
||||
DissectedImage *m,
|
||||
const char *root, /* The root to get user verity certs from (for a sysext) */
|
||||
const char *passphrase,
|
||||
const VeritySettings *verity,
|
||||
const ImagePolicy *policy,
|
||||
@ -3564,7 +3567,7 @@ int dissected_image_decrypt(
|
||||
|
||||
k = partition_verity_hash_of(i);
|
||||
if (k >= 0) {
|
||||
r = verity_partition(i, p, m->partitions + k, verity, flags, fl, d);
|
||||
r = verity_partition(i, p, m->partitions + k, root, verity, flags, fl, d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -3597,7 +3600,7 @@ int dissected_image_decrypt_interactively(
|
||||
n--;
|
||||
|
||||
for (;;) {
|
||||
r = dissected_image_decrypt(m, passphrase, verity, image_policy, flags);
|
||||
r = dissected_image_decrypt(m, /* root= */ NULL, passphrase, verity, image_policy, flags);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
if (r == -EKEYREJECTED)
|
||||
@ -4862,7 +4865,8 @@ int verity_dissect_and_mount(
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
dissected_image,
|
||||
NULL,
|
||||
/* root= */ NULL,
|
||||
/* passphrase= */ NULL,
|
||||
verity,
|
||||
image_policy,
|
||||
dissect_image_flags);
|
||||
|
||||
@ -171,7 +171,7 @@ void dissected_image_close(DissectedImage *m);
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
|
||||
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
|
||||
int dissected_image_decrypt(DissectedImage *m, const char *root, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
|
||||
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
|
||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
|
||||
|
||||
@ -1942,6 +1942,7 @@ int unit_file_verify_alias(
|
||||
|
||||
static int install_info_symlink_alias(
|
||||
RuntimeScope scope,
|
||||
UnitFileFlags file_flags,
|
||||
InstallInfo *info,
|
||||
const LookupPaths *lp,
|
||||
const char *config_path,
|
||||
@ -1996,6 +1997,10 @@ static int install_info_symlink_alias(
|
||||
broken = r == 0; /* symlink target does not exist? */
|
||||
|
||||
r = create_symlink(lp, alias_target ?: info->path, alias_path, force || broken, changes, n_changes);
|
||||
if (r == -EEXIST && FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE))
|
||||
/* We cannot realize the alias because a conflicting alias exists.
|
||||
* Do not propagate this as error. */
|
||||
continue;
|
||||
if (r != 0 && ret >= 0)
|
||||
ret = r;
|
||||
}
|
||||
@ -2160,7 +2165,7 @@ static int install_info_apply(
|
||||
* because they might would pointing to a non-existent or wrong unit. */
|
||||
return r;
|
||||
|
||||
r = install_info_symlink_alias(scope, info, lp, config_path, force, changes, n_changes);
|
||||
r = install_info_symlink_alias(scope, file_flags, info, lp, config_path, force, changes, n_changes);
|
||||
|
||||
q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes);
|
||||
if (q != 0 && r >= 0)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <grp.h>
|
||||
#include <gshadow.h>
|
||||
#include <netdb.h>
|
||||
#include <nss.h>
|
||||
#include <pwd.h>
|
||||
@ -291,3 +292,9 @@ typedef enum nss_status (*_nss_getgrgid_r_t)(
|
||||
struct group *gr,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop);
|
||||
|
||||
typedef enum nss_status (*_nss_getsgnam_r_t)(
|
||||
const char *name,
|
||||
struct sgrp *sg,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop);
|
||||
|
||||
@ -35,6 +35,6 @@ int vsock_get_local_cid_or_warn(unsigned *ret) {
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
|
||||
return log_error_errno(r, "Failed to query host's AF_VSOCK CID: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1923,7 +1923,7 @@ static int merge_subprocess(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissected_image_decrypt(m, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
|
||||
r = dissected_image_decrypt(m, arg_root, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2199,7 +2199,7 @@ static int image_discover_and_read_metadata(ImageClass image_class, Hashmap **re
|
||||
return log_error_errno(r, "Failed to discover images: %m");
|
||||
|
||||
HASHMAP_FOREACH(img, images) {
|
||||
r = image_read_metadata(img, image_class_info[image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
|
||||
r = image_read_metadata(img, arg_root, image_class_info[image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <gshadow.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -35,7 +36,22 @@ static void print_struct_group(const struct group *gr) {
|
||||
gr->gr_name, gr->gr_gid);
|
||||
log_info(" passwd=\"%s\"", gr->gr_passwd);
|
||||
|
||||
assert_se(members = strv_join(gr->gr_mem, ", "));
|
||||
assert_se(members = strv_join(ASSERT_PTR(gr->gr_mem), ", "));
|
||||
// FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available
|
||||
log_info(" members=%s", members);
|
||||
}
|
||||
|
||||
static void print_struct_sgrp(const struct sgrp *sg) {
|
||||
_cleanup_free_ char *administrators = NULL, *members = NULL;
|
||||
|
||||
log_info(" \"%s\"", sg->sg_namp);
|
||||
log_info(" passwd=\"%s\"", sg->sg_passwd);
|
||||
|
||||
assert_se(administrators = strv_join(ASSERT_PTR(sg->sg_adm), ", "));
|
||||
// FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available
|
||||
log_info(" administrators=%s", administrators);
|
||||
|
||||
assert_se(members = strv_join(ASSERT_PTR(sg->sg_mem), ", "));
|
||||
// FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available
|
||||
log_info(" members=%s", members);
|
||||
}
|
||||
@ -92,6 +108,32 @@ static void test_getgrnam_r(void *handle, const char *module, const char *name)
|
||||
print_struct_group(&gr);
|
||||
}
|
||||
|
||||
static void test_getsgnam_r(void *handle, const char *module, const char *name) {
|
||||
const char *fname;
|
||||
_nss_getsgnam_r_t f;
|
||||
char buffer[arg_bufsize];
|
||||
int errno1 = 999; /* nss-dns doesn't set those */
|
||||
enum nss_status status;
|
||||
char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
|
||||
struct sgrp sg;
|
||||
|
||||
fname = strjoina("_nss_", module, "_getsgnam_r");
|
||||
f = dlsym(handle, fname);
|
||||
log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
|
||||
if (!f) {
|
||||
log_info("%s not defined", fname);
|
||||
return;
|
||||
}
|
||||
|
||||
status = f(name, &sg, buffer, sizeof buffer, &errno1);
|
||||
log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s",
|
||||
fname, name,
|
||||
nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
|
||||
errno1, errno1 > 0 ? ERRNO_NAME(errno1) : "---");
|
||||
if (status == NSS_STATUS_SUCCESS)
|
||||
print_struct_sgrp(&sg);
|
||||
}
|
||||
|
||||
static void test_getpwuid_r(void *handle, const char *module, uid_t uid) {
|
||||
const char *fname;
|
||||
_nss_getpwuid_r_t f;
|
||||
@ -147,6 +189,7 @@ static void test_getgrgid_r(void *handle, const char *module, gid_t gid) {
|
||||
static void test_byname(void *handle, const char *module, const char *name) {
|
||||
test_getpwnam_r(handle, module, name);
|
||||
test_getgrnam_r(handle, module, name);
|
||||
test_getsgnam_r(handle, module, name);
|
||||
puts("");
|
||||
}
|
||||
|
||||
|
||||
@ -181,6 +181,52 @@ prepare_extension_image_raw() {
|
||||
prepend_trap "rm -rf ${ext_dir@Q}.raw"
|
||||
}
|
||||
|
||||
prepare_extension_image_raw_verity() {
|
||||
local root=${1:-}
|
||||
local hierarchy=${2:?}
|
||||
local ext_dir ext_release name tmpcrt
|
||||
|
||||
name="test-extension"
|
||||
ext_dir="$root/var/lib/extensions/$name"
|
||||
ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
|
||||
tmpcrt=$(mktemp --directory "/tmp/test-sysext.crt.XXXXXXXXXX")
|
||||
|
||||
prepend_trap "rm -rf ${ext_dir@Q} ${ext_dir@Q}.raw '$root/etc/verity.d/test-ext.crt' '$tmpcrt'"
|
||||
|
||||
mkdir -p "${ext_release%/*}"
|
||||
echo "ID=_any" >"$ext_release"
|
||||
mkdir -p "$ext_dir/$hierarchy"
|
||||
touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
|
||||
tee >"$tmpcrt/verity.openssl.cnf" <<EOF
|
||||
[ req ]
|
||||
prompt = no
|
||||
distinguished_name = req_distinguished_name
|
||||
[ req_distinguished_name ]
|
||||
C = DE
|
||||
ST = Test State
|
||||
L = Test Locality
|
||||
O = Org Name
|
||||
OU = Org Unit Name
|
||||
CN = Common Name
|
||||
emailAddress = test@email.com
|
||||
EOF
|
||||
openssl req \
|
||||
-config "$tmpcrt/verity.openssl.cnf" \
|
||||
-new -x509 \
|
||||
-newkey rsa:1024 \
|
||||
-keyout "$tmpcrt/test-ext.key" \
|
||||
-out "$tmpcrt/test-ext.crt" \
|
||||
-days 365 \
|
||||
-nodes
|
||||
systemd-repart --make-ddi=sysext \
|
||||
--private-key="$tmpcrt/test-ext.key" --certificate="$tmpcrt/test-ext.crt" \
|
||||
--copy-source="$ext_dir" "$ext_dir.raw"
|
||||
rm -rf "$ext_dir"
|
||||
mkdir -p "$root/etc/verity.d"
|
||||
mv "$tmpcrt/test-ext.crt" "$root/etc/verity.d/"
|
||||
rm -rf "$tmpcrt"
|
||||
}
|
||||
|
||||
prepare_extension_mutable_dir() {
|
||||
local dir=${1:?}
|
||||
|
||||
@ -1239,6 +1285,28 @@ extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
run_systemd_sysext "$fake_root" unmerge
|
||||
)
|
||||
|
||||
( init_trap
|
||||
: "Check if verity user certs get loaded from --root="
|
||||
fake_root=${roots_dir:+"$roots_dir/verity-user-cert-from-root"}
|
||||
hierarchy=/opt
|
||||
|
||||
# On OpenSUSE Tumbleweed EROFS is not supported
|
||||
if [ -e /usr/lib/modprobe.d/60-blacklist_fs-erofs.conf ]; then
|
||||
echo >&2 "Skipping test due to missing erofs support"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
prepare_root "$fake_root" "$hierarchy"
|
||||
prepare_extension_image_raw_verity "$fake_root" "$hierarchy"
|
||||
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
|
||||
|
||||
run_systemd_sysext "$fake_root" merge --image-policy=root=signed+absent:usr=signed+absent
|
||||
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
|
||||
run_systemd_sysext "$fake_root" unmerge
|
||||
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
|
||||
)
|
||||
|
||||
} # End of run_sysext_tests
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user