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
|
socket passing (i.e. sockets passed in via standard input and output, using
|
||||||
<varname>StandardInput=socket</varname> in the service file).</para>
|
<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
|
namespace (see <citerefentry
|
||||||
project='man-pages'><refentrytitle>network_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>). This
|
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
|
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
|
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
|
from the service code itself will be associated with the service's own namespace, and thus possibly subject to a
|
||||||
restrictive configuration.</para>
|
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>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|||||||
@ -389,11 +389,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int pid_is_kernel_thread(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;
|
int r;
|
||||||
|
|
||||||
if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
|
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))
|
if (!pid_is_valid(pid))
|
||||||
return -EINVAL;
|
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);
|
r = read_one_line_file(p, &line);
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
@ -409,14 +405,14 @@ int pid_is_kernel_thread(pid_t pid) {
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* Skip past the comm field */
|
/* Skip past the comm field */
|
||||||
q = strrchr(line, ')');
|
char *q = strrchr(line, ')');
|
||||||
if (!q)
|
if (!q)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
q++;
|
q++;
|
||||||
|
|
||||||
/* Skip 6 fields to reach the flags field */
|
/* Skip 6 fields to reach the flags field */
|
||||||
for (i = 0; i < 6; i++) {
|
for (size_t i = 0; i < 6; i++) {
|
||||||
l = strspn(q, WHITESPACE);
|
size_t l = strspn(q, WHITESPACE);
|
||||||
if (l < 1)
|
if (l < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
q += l;
|
q += l;
|
||||||
@ -428,7 +424,7 @@ int pid_is_kernel_thread(pid_t pid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Skip preceding whitespace */
|
/* Skip preceding whitespace */
|
||||||
l = strspn(q, WHITESPACE);
|
size_t l = strspn(q, WHITESPACE);
|
||||||
if (l < 1)
|
if (l < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
q += l;
|
q += l;
|
||||||
@ -439,6 +435,7 @@ int pid_is_kernel_thread(pid_t pid) {
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
q[l] = 0;
|
q[l] = 0;
|
||||||
|
|
||||||
|
unsigned long long flags;
|
||||||
r = safe_atollu(q, &flags);
|
r = safe_atollu(q, &flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -883,25 +880,23 @@ int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFl
|
|||||||
siginfo_t status;
|
siginfo_t status;
|
||||||
r = pidref_wait_for_terminate(pidref, &status);
|
r = pidref_wait_for_terminate(pidref, &status);
|
||||||
if (r < 0)
|
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_code == CLD_EXITED) {
|
||||||
if (status.si_status != EXIT_SUCCESS)
|
if (status.si_status != EXIT_SUCCESS)
|
||||||
log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
|
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
|
else
|
||||||
log_debug("%s succeeded.", name);
|
log_debug("'%s' succeeded.", name);
|
||||||
|
|
||||||
return status.si_status;
|
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 log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO),
|
||||||
return -EPROTO;
|
"'%s' failed due to unknown reason.", strna(name));
|
||||||
}
|
|
||||||
|
|
||||||
log_full(prio, "%s failed due to unknown reason.", strna(name));
|
|
||||||
return -EPROTO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int kill_and_sigcont(pid_t pid, int sig) {
|
int kill_and_sigcont(pid_t pid, int sig) {
|
||||||
|
|||||||
@ -15,51 +15,71 @@
|
|||||||
#include "virt.h"
|
#include "virt.h"
|
||||||
|
|
||||||
static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
|
static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
|
||||||
char utf8[DECIMAL_STR_MAX(usec_t)];
|
char buf[DECIMAL_STR_MAX(usec_t)];
|
||||||
char16_t *encoded;
|
|
||||||
usec_t timeout;
|
usec_t timeout;
|
||||||
bool menu_disabled = false;
|
uint64_t loader_features = 0;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(arg1);
|
assert(arg1);
|
||||||
assert(ret_timeout);
|
assert(ret_timeout);
|
||||||
assert(ret_timeout_size);
|
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
|
/* Use feature EFI_LOADER_FEATURE_MENU_DISABLE as a mark that the boot loader supports the other
|
||||||
* set their numerical value(s) instead. This means that some of the sd-boot internal ABI has leaked
|
* string values too. When unsupported, convert to the timeout with the closest meaning.
|
||||||
* although the ship has sailed and the side-effects are self-contained.
|
|
||||||
*/
|
*/
|
||||||
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);
|
(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 (!(loader_features & EFI_LOADER_FEATURE_MENU_DISABLE)) {
|
||||||
if (arg_graceful() == ARG_GRACEFUL_NO)
|
if (arg_graceful() == ARG_GRACEFUL_NO)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Loader does not support 'menu-disabled'.");
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||||
|
"Loader does not support '%s'.", arg1);
|
||||||
log_warning("Loader does not support 'menu-disabled', setting anyway.");
|
log_warning("Using zero timeout instead of '%s'.", arg1);
|
||||||
|
timeout = 0;
|
||||||
|
arg1 = NULL;
|
||||||
}
|
}
|
||||||
menu_disabled = true;
|
|
||||||
} else {
|
} else {
|
||||||
r = parse_time(arg1, &timeout, USEC_PER_SEC);
|
r = parse_time(arg1, &timeout, USEC_PER_SEC);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
|
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)
|
if (!arg1) {
|
||||||
xsprintf(utf8, "menu-disabled");
|
timeout = DIV_ROUND_UP(timeout, USEC_PER_SEC);
|
||||||
else
|
xsprintf(buf, USEC_FMT, MIN(timeout, UINT32_MAX));
|
||||||
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
|
}
|
||||||
|
|
||||||
encoded = utf8_to_utf16(utf8, SIZE_MAX);
|
char16_t *encoded = utf8_to_utf16(arg1 ?: buf, SIZE_MAX);
|
||||||
if (!encoded)
|
if (!encoded)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
|||||||
@ -804,10 +804,6 @@ static int automount_start(Unit *u) {
|
|||||||
if (path_is_mount_point(a->where) > 0)
|
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);
|
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);
|
r = unit_acquire_invocation_id(u);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -1047,6 +1043,10 @@ static int automount_test_startable(Unit *u) {
|
|||||||
Automount *a = ASSERT_PTR(AUTOMOUNT(u));
|
Automount *a = ASSERT_PTR(AUTOMOUNT(u));
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
r = unit_test_trigger_loaded(u);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = unit_test_start_limit(u);
|
r = unit_test_start_limit(u);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
|
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,
|
.sched_flags = context->cpu_sched_reset_on_fork ? SCHED_FLAG_RESET_ON_FORK : 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
r = sched_setattr(/* pid= */ 0, &attr, /* flags= */ 0);
|
r = RET_NERRNO(sched_setattr(/* pid= */ 0, &attr, /* flags= */ 0));
|
||||||
if (r < 0) {
|
if (r == -EINVAL && !sched_policy_supported(context->cpu_sched_policy)) {
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
_cleanup_free_ char *s = NULL;
|
_cleanup_free_ char *s = NULL;
|
||||||
(void) sched_policy_to_string_alloc(context->cpu_sched_policy, &s);
|
(void) sched_policy_to_string_alloc(context->cpu_sched_policy, &s);
|
||||||
|
log_warning_errno(r, "CPU scheduling policy %s is not supported, proceeding without.", strna(s));
|
||||||
log_warning_errno(errno, "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(
|
r = dissected_image_decrypt(
|
||||||
dissected_image,
|
dissected_image,
|
||||||
|
/* root= */ NULL,
|
||||||
/* passphrase= */ NULL,
|
/* passphrase= */ NULL,
|
||||||
p->verity,
|
p->verity,
|
||||||
p->root_image_policy,
|
p->root_image_policy,
|
||||||
|
|||||||
@ -632,10 +632,6 @@ static int path_start(Unit *u) {
|
|||||||
|
|
||||||
assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
|
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);
|
r = unit_acquire_invocation_id(u);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -902,6 +898,10 @@ static int path_test_startable(Unit *u) {
|
|||||||
Path *p = ASSERT_PTR(PATH(u));
|
Path *p = ASSERT_PTR(PATH(u));
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
r = unit_test_trigger_loaded(u);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = unit_test_start_limit(u);
|
r = unit_test_start_limit(u);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
|
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));
|
Socket *s = ASSERT_PTR(SOCKET(u));
|
||||||
int r;
|
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));
|
assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED));
|
||||||
|
|
||||||
r = unit_acquire_invocation_id(u);
|
r = unit_acquire_invocation_id(u);
|
||||||
@ -3642,6 +3628,20 @@ static int socket_test_startable(Unit *u) {
|
|||||||
SOCKET_START_POST))
|
SOCKET_START_POST))
|
||||||
return false;
|
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);
|
r = unit_test_start_limit(u);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
|
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));
|
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);
|
r = unit_acquire_invocation_id(u);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -917,6 +913,10 @@ static int timer_test_startable(Unit *u) {
|
|||||||
Timer *t = ASSERT_PTR(TIMER(u));
|
Timer *t = ASSERT_PTR(TIMER(u));
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
r = unit_test_trigger_loaded(u);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = unit_test_start_limit(u);
|
r = unit_test_start_limit(u);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
|
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))
|
if (hashmap_contains(tr->jobs, u))
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1191,7 +1213,6 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
|
|||||||
|
|
||||||
HASHMAP_FOREACH_KEY(u, k, m->units) {
|
HASHMAP_FOREACH_KEY(u, k, m->units) {
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
|
||||||
Unit *o;
|
|
||||||
|
|
||||||
/* Ignore aliases. */
|
/* Ignore aliases. */
|
||||||
if (u->id != k)
|
if (u->id != k)
|
||||||
@ -1204,16 +1225,6 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
|
|||||||
if (!shall_stop_on_isolate(tr, u))
|
if (!shall_stop_on_isolate(tr, u))
|
||||||
continue;
|
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);
|
r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, TRANSACTION_MATTERS, &e);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %s", bus_error_message(&e, r));
|
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)))
|
if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (u->job &&
|
if (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART))
|
||||||
IN_SET(u->job->type, JOB_START, JOB_RELOAD_OR_START, JOB_RESTART))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -368,11 +368,11 @@ static int run(int argc, char *argv[]) {
|
|||||||
/* Child */
|
/* Child */
|
||||||
execvp(arguments[0], arguments);
|
execvp(arguments[0], arguments);
|
||||||
log_open();
|
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);
|
_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;
|
int r;
|
||||||
|
|
||||||
if (!image->metadata_valid) {
|
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)
|
if (r < 0)
|
||||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
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;
|
int r;
|
||||||
|
|
||||||
if (!image->metadata_valid) {
|
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)
|
if (r < 0)
|
||||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
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;
|
int r;
|
||||||
|
|
||||||
if (!image->metadata_valid) {
|
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)
|
if (r < 0)
|
||||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
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;
|
int r;
|
||||||
|
|
||||||
if (!image->metadata_valid) {
|
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)
|
if (r < 0)
|
||||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
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);
|
assert(image);
|
||||||
|
|
||||||
if (should_acquire_metadata(am) && !image->metadata_valid) {
|
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)
|
if (r < 0 && am != ACQUIRE_METADATA_GRACEFUL)
|
||||||
return log_debug_errno(r, "Failed to read image metadata: %m");
|
return log_debug_errno(r, "Failed to read image metadata: %m");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|||||||
@ -540,6 +540,7 @@ static int vl_method_mount_image(
|
|||||||
|
|
||||||
r = dissected_image_decrypt(
|
r = dissected_image_decrypt(
|
||||||
di,
|
di,
|
||||||
|
/* root= */ NULL,
|
||||||
p.password,
|
p.password,
|
||||||
&verity,
|
&verity,
|
||||||
use_policy,
|
use_policy,
|
||||||
|
|||||||
@ -75,6 +75,8 @@ static const struct group root_group = {
|
|||||||
static const struct sgrp root_sgrp = {
|
static const struct sgrp root_sgrp = {
|
||||||
.sg_namp = (char*) "root",
|
.sg_namp = (char*) "root",
|
||||||
.sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
|
.sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
|
||||||
|
.sg_adm = (char*[]) { NULL },
|
||||||
|
.sg_mem = (char*[]) { NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct group nobody_group = {
|
static const struct group nobody_group = {
|
||||||
@ -87,6 +89,8 @@ static const struct group nobody_group = {
|
|||||||
static const struct sgrp nobody_sgrp = {
|
static const struct sgrp nobody_sgrp = {
|
||||||
.sg_namp = (char*) NOBODY_GROUP_NAME,
|
.sg_namp = (char*) NOBODY_GROUP_NAME,
|
||||||
.sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
|
.sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
|
||||||
|
.sg_adm = (char*[]) { NULL },
|
||||||
|
.sg_mem = (char*[]) { NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct GetentData {
|
typedef struct GetentData {
|
||||||
@ -257,12 +261,18 @@ static enum nss_status copy_synthesized_sgrp(
|
|||||||
assert(src);
|
assert(src);
|
||||||
assert(src->sg_namp);
|
assert(src->sg_namp);
|
||||||
assert(src->sg_passwd);
|
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 =
|
size_t required =
|
||||||
strlen(src->sg_namp) + 1 +
|
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;
|
*errnop = ERANGE;
|
||||||
return NSS_STATUS_TRYAGAIN;
|
return NSS_STATUS_TRYAGAIN;
|
||||||
}
|
}
|
||||||
@ -274,7 +284,10 @@ static enum nss_status copy_synthesized_sgrp(
|
|||||||
/* String fields point into the user-provided buffer */
|
/* String fields point into the user-provided buffer */
|
||||||
dest->sg_namp = buffer;
|
dest->sg_namp = buffer;
|
||||||
dest->sg_passwd = stpcpy(dest->sg_namp, src->sg_namp) + 1;
|
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;
|
return NSS_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -411,6 +411,40 @@ enum nss_status userdb_getgrgid(
|
|||||||
return NSS_STATUS_SUCCESS;
|
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(
|
int nss_pack_group_record_shadow(
|
||||||
GroupRecord *hr,
|
GroupRecord *hr,
|
||||||
struct sgrp *sgrp,
|
struct sgrp *sgrp,
|
||||||
@ -418,7 +452,8 @@ int nss_pack_group_record_shadow(
|
|||||||
size_t buflen) {
|
size_t buflen) {
|
||||||
|
|
||||||
const char *hashed;
|
const char *hashed;
|
||||||
size_t required;
|
char **array = NULL, *p;
|
||||||
|
size_t i = 0, n = 0, required;
|
||||||
|
|
||||||
assert(hr);
|
assert(hr);
|
||||||
assert(sgrp);
|
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]);
|
assert_se(hashed = strv_isempty(hr->hashed_password) ? PASSWORD_LOCKED_AND_INVALID : hr->hashed_password[0]);
|
||||||
required += strlen(hashed) + 1;
|
required += strlen(hashed) + 1;
|
||||||
|
|
||||||
|
nss_count_strv(hr->administrators, &n, &required);
|
||||||
|
nss_count_strv(hr->members, &n, &required);
|
||||||
|
|
||||||
if (buflen < required)
|
if (buflen < required)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
*sgrp = (struct sgrp) {
|
|
||||||
.sg_namp = buffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(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;
|
sgrp->sg_passwd = stpcpy(sgrp->sg_namp, hr->group_name) + 1;
|
||||||
strcpy(sgrp->sg_passwd, hashed);
|
strcpy(sgrp->sg_passwd, hashed);
|
||||||
|
|
||||||
|
|||||||
@ -61,7 +61,7 @@ int bus_image_common_get_os_release(
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!image->metadata_valid) {
|
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)
|
if (r < 0)
|
||||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
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;
|
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;
|
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -2153,6 +2153,7 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope
|
|||||||
|
|
||||||
r = dissected_image_decrypt(
|
r = dissected_image_decrypt(
|
||||||
m,
|
m,
|
||||||
|
root,
|
||||||
/* passphrase= */ NULL,
|
/* passphrase= */ NULL,
|
||||||
&verity,
|
&verity,
|
||||||
image_policy,
|
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_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_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);
|
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);
|
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;
|
int r;
|
||||||
|
|
||||||
/* Returns > 0 if signature checks out, == 0 if not, < 0 on unexpected errors */
|
/* 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
|
/* Because installing a signature certificate into the kernel chain is so messy, let's optionally do
|
||||||
* userspace validation. */
|
* 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)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to enumerate certificates: %m");
|
return log_debug_errno(r, "Failed to enumerate certificates: %m");
|
||||||
if (strv_isempty(certs)) {
|
if (strv_isempty(certs)) {
|
||||||
@ -3200,6 +3200,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
|||||||
|
|
||||||
static int do_crypt_activate_verity(
|
static int do_crypt_activate_verity(
|
||||||
struct crypt_device *cd,
|
struct crypt_device *cd,
|
||||||
|
const char *root,
|
||||||
const char *name,
|
const char *name,
|
||||||
const VeritySettings *verity,
|
const VeritySettings *verity,
|
||||||
DissectImageFlags flags,
|
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,
|
/* 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 */
|
* 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)
|
if (k < 0)
|
||||||
return k;
|
return k;
|
||||||
if (k == 0) {
|
if (k == 0) {
|
||||||
@ -3309,6 +3310,7 @@ static int verity_partition(
|
|||||||
PartitionDesignator designator,
|
PartitionDesignator designator,
|
||||||
DissectedPartition *m, /* data partition */
|
DissectedPartition *m, /* data partition */
|
||||||
DissectedPartition *v, /* verity partition */
|
DissectedPartition *v, /* verity partition */
|
||||||
|
const char *root, /* The root to get user verity certs from (for a sysext) */
|
||||||
const VeritySettings *verity,
|
const VeritySettings *verity,
|
||||||
DissectImageFlags flags,
|
DissectImageFlags flags,
|
||||||
PartitionPolicyFlags policy_flags,
|
PartitionPolicyFlags policy_flags,
|
||||||
@ -3394,7 +3396,7 @@ static int verity_partition(
|
|||||||
goto check; /* The device already exists. Let's check it. */
|
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. */
|
/* 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)
|
if (r >= 0)
|
||||||
goto try_open; /* The device is activated. Let's open it. */
|
goto try_open; /* The device is activated. Let's open it. */
|
||||||
/* libdevmapper can return EINVAL when the device is already in the activation stage.
|
/* 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);
|
sym_crypt_free(cd);
|
||||||
cd = NULL;
|
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);
|
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(
|
int dissected_image_decrypt(
|
||||||
DissectedImage *m,
|
DissectedImage *m,
|
||||||
|
const char *root, /* The root to get user verity certs from (for a sysext) */
|
||||||
const char *passphrase,
|
const char *passphrase,
|
||||||
const VeritySettings *verity,
|
const VeritySettings *verity,
|
||||||
const ImagePolicy *policy,
|
const ImagePolicy *policy,
|
||||||
@ -3564,7 +3567,7 @@ int dissected_image_decrypt(
|
|||||||
|
|
||||||
k = partition_verity_hash_of(i);
|
k = partition_verity_hash_of(i);
|
||||||
if (k >= 0) {
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -3597,7 +3600,7 @@ int dissected_image_decrypt_interactively(
|
|||||||
n--;
|
n--;
|
||||||
|
|
||||||
for (;;) {
|
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)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == -EKEYREJECTED)
|
if (r == -EKEYREJECTED)
|
||||||
@ -4862,7 +4865,8 @@ int verity_dissect_and_mount(
|
|||||||
|
|
||||||
r = dissected_image_decrypt(
|
r = dissected_image_decrypt(
|
||||||
dissected_image,
|
dissected_image,
|
||||||
NULL,
|
/* root= */ NULL,
|
||||||
|
/* passphrase= */ NULL,
|
||||||
verity,
|
verity,
|
||||||
image_policy,
|
image_policy,
|
||||||
dissect_image_flags);
|
dissect_image_flags);
|
||||||
|
|||||||
@ -171,7 +171,7 @@ void dissected_image_close(DissectedImage *m);
|
|||||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
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_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(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);
|
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(
|
static int install_info_symlink_alias(
|
||||||
RuntimeScope scope,
|
RuntimeScope scope,
|
||||||
|
UnitFileFlags file_flags,
|
||||||
InstallInfo *info,
|
InstallInfo *info,
|
||||||
const LookupPaths *lp,
|
const LookupPaths *lp,
|
||||||
const char *config_path,
|
const char *config_path,
|
||||||
@ -1996,6 +1997,10 @@ static int install_info_symlink_alias(
|
|||||||
broken = r == 0; /* symlink target does not exist? */
|
broken = r == 0; /* symlink target does not exist? */
|
||||||
|
|
||||||
r = create_symlink(lp, alias_target ?: info->path, alias_path, force || broken, changes, n_changes);
|
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)
|
if (r != 0 && ret >= 0)
|
||||||
ret = r;
|
ret = r;
|
||||||
}
|
}
|
||||||
@ -2160,7 +2165,7 @@ static int install_info_apply(
|
|||||||
* because they might would pointing to a non-existent or wrong unit. */
|
* because they might would pointing to a non-existent or wrong unit. */
|
||||||
return r;
|
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);
|
q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes);
|
||||||
if (q != 0 && r >= 0)
|
if (q != 0 && r >= 0)
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
#include <gshadow.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <nss.h>
|
#include <nss.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
@ -291,3 +292,9 @@ typedef enum nss_status (*_nss_getgrgid_r_t)(
|
|||||||
struct group *gr,
|
struct group *gr,
|
||||||
char *buffer, size_t buflen,
|
char *buffer, size_t buflen,
|
||||||
int *errnop);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
if (r < 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1923,7 +1923,7 @@ static int merge_subprocess(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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)
|
if (r < 0)
|
||||||
return r;
|
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");
|
return log_error_errno(r, "Failed to discover images: %m");
|
||||||
|
|
||||||
HASHMAP_FOREACH(img, images) {
|
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)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
|
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 */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <gshadow.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -35,7 +36,22 @@ static void print_struct_group(const struct group *gr) {
|
|||||||
gr->gr_name, gr->gr_gid);
|
gr->gr_name, gr->gr_gid);
|
||||||
log_info(" passwd=\"%s\"", gr->gr_passwd);
|
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
|
// FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available
|
||||||
log_info(" members=%s", members);
|
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);
|
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) {
|
static void test_getpwuid_r(void *handle, const char *module, uid_t uid) {
|
||||||
const char *fname;
|
const char *fname;
|
||||||
_nss_getpwuid_r_t f;
|
_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) {
|
static void test_byname(void *handle, const char *module, const char *name) {
|
||||||
test_getpwnam_r(handle, module, name);
|
test_getpwnam_r(handle, module, name);
|
||||||
test_getgrnam_r(handle, module, name);
|
test_getgrnam_r(handle, module, name);
|
||||||
|
test_getsgnam_r(handle, module, name);
|
||||||
puts("");
|
puts("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -181,6 +181,52 @@ prepare_extension_image_raw() {
|
|||||||
prepend_trap "rm -rf ${ext_dir@Q}.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() {
|
prepare_extension_mutable_dir() {
|
||||||
local dir=${1:?}
|
local dir=${1:?}
|
||||||
|
|
||||||
@ -1239,6 +1285,28 @@ extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
|||||||
run_systemd_sysext "$fake_root" unmerge
|
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
|
} # End of run_sysext_tests
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user