core/service: support credentials refreshing on reload (#40093)
Closes https://github.com/systemd/systemd/issues/21099 Closes https://github.com/systemd/systemd/issues/35227
This commit is contained in:
@@ -262,6 +262,71 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int namespace_enter_one_idempotent(int nsfd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
/* Join a namespace, but only if we're not part of it already. This is important if we don't necessarily
|
||||
* own the namespace in question, as kernel would unconditionally return EPERM otherwise. */
|
||||
|
||||
assert(nsfd >= 0);
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
r = is_our_namespace(nsfd, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
if (setns(nsfd, namespace_info[type].clone_flag) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int namespace_enter_delegated(int userns_fd, int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
|
||||
int r;
|
||||
|
||||
/* Similar to namespace_enter(), but operates on a set of namespaces that are potentially owned
|
||||
* by the userns ("delegated"), in which case we'll need to gain CAP_SYS_ADMIN by joining
|
||||
* the userns first, and the rest later. */
|
||||
|
||||
assert(userns_fd >= 0);
|
||||
|
||||
/* Block dlopen() now, to avoid us inadvertently loading shared library from another namespace */
|
||||
block_dlopen();
|
||||
|
||||
if (setns(userns_fd, CLONE_NEWUSER) < 0)
|
||||
return -errno;
|
||||
|
||||
if (pidns_fd >= 0) {
|
||||
r = namespace_enter_one_idempotent(pidns_fd, NAMESPACE_PID);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (mntns_fd >= 0) {
|
||||
r = namespace_enter_one_idempotent(mntns_fd, NAMESPACE_MOUNT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (netns_fd >= 0) {
|
||||
r = namespace_enter_one_idempotent(netns_fd, NAMESPACE_NET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (root_fd >= 0) {
|
||||
if (fchdir(root_fd) < 0)
|
||||
return -errno;
|
||||
|
||||
if (chroot(".") < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return maybe_setgroups(/* size = */ 0, NULL);
|
||||
}
|
||||
|
||||
int fd_is_namespace(int fd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ int namespace_open(
|
||||
int *ret_root_fd);
|
||||
|
||||
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
|
||||
int namespace_enter_delegated(int userns_fd, int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
|
||||
|
||||
int fd_is_namespace(int fd, NamespaceType type);
|
||||
int is_our_namespace(int fd, NamespaceType type);
|
||||
|
||||
@@ -1778,6 +1778,7 @@ int namespace_fork_full(
|
||||
int netns_fd,
|
||||
int userns_fd,
|
||||
int root_fd,
|
||||
bool delegated,
|
||||
PidRef *ret) {
|
||||
|
||||
_cleanup_(pidref_done_sigkill_wait) PidRef pidref_outer = PIDREF_NULL;
|
||||
@@ -1823,7 +1824,10 @@ int namespace_fork_full(
|
||||
|
||||
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
|
||||
|
||||
r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
|
||||
if (delegated)
|
||||
r = namespace_enter_delegated(userns_fd, pidns_fd, mntns_fd, netns_fd, root_fd);
|
||||
else
|
||||
r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
|
||||
if (r < 0) {
|
||||
log_full_errno(prio, r, "Failed to join namespace: %m");
|
||||
report_errno_and_exit(errno_pipe_fd[1], r);
|
||||
|
||||
@@ -201,6 +201,7 @@ int namespace_fork_full(
|
||||
int netns_fd,
|
||||
int userns_fd,
|
||||
int root_fd,
|
||||
bool delegated,
|
||||
PidRef *ret);
|
||||
|
||||
static inline int namespace_fork(
|
||||
@@ -215,7 +216,7 @@ static inline int namespace_fork(
|
||||
PidRef *ret) {
|
||||
|
||||
return namespace_fork_full(outer_name, inner_name, NULL, 0, flags,
|
||||
pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd,
|
||||
pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd, false,
|
||||
ret);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_RUNNING] = "running",
|
||||
[SERVICE_EXITED] = "exited",
|
||||
[SERVICE_REFRESH_EXTENSIONS] = "refresh-extensions",
|
||||
[SERVICE_REFRESH_CREDENTIALS] = "refresh-credentials",
|
||||
[SERVICE_RELOAD] = "reload",
|
||||
[SERVICE_RELOAD_SIGNAL] = "reload-signal",
|
||||
[SERVICE_RELOAD_NOTIFY] = "reload-notify",
|
||||
|
||||
@@ -132,6 +132,7 @@ typedef enum ServiceState {
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
|
||||
SERVICE_REFRESH_EXTENSIONS, /* Refreshing extensions for a reload request */
|
||||
SERVICE_REFRESH_CREDENTIALS, /* ditto, but for credentials */
|
||||
SERVICE_RELOAD, /* Reloading via ExecReload= */
|
||||
SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */
|
||||
SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */
|
||||
|
||||
Reference in New Issue
Block a user