summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2022-10-17 13:44:28 +0200
committerGitHub <noreply@github.com>2022-10-17 13:44:28 +0200
commitc39d0187746c0bfa3946ae10ce6c926c61d6e41f (patch)
tree42d49b0eb25c8b626e06225c5497a5ec4ef711b0 /src
parentMerge pull request #24986 from keszybz/news-systemd-measure (diff)
parentcore: only allow systemd-oomd to use SubscribeManagedOOMCGroups (diff)
downloadsystemd-c39d0187746c0bfa3946ae10ce6c926c61d6e41f.tar.xz
systemd-c39d0187746c0bfa3946ae10ce6c926c61d6e41f.zip
Merge pull request #24919 from anitazha/varlinkserialize
varlink/pid1 fixes for systemd-oomd (addresses #20330)
Diffstat (limited to 'src')
-rw-r--r--src/core/core-varlink.c64
-rw-r--r--src/core/core-varlink.h4
-rw-r--r--src/core/manager-serialize.c32
-rw-r--r--src/shared/meson.build1
-rw-r--r--src/shared/varlink-internal.h10
-rw-r--r--src/shared/varlink.c156
-rw-r--r--src/shared/varlink.h1
7 files changed, 239 insertions, 29 deletions
diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c
index 1fdcf885b8..843271593d 100644
--- a/src/core/core-varlink.c
+++ b/src/core/core-varlink.c
@@ -203,10 +203,25 @@ static int vl_method_subscribe_managed_oom_cgroups(
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
Manager *m = ASSERT_PTR(userdata);
+ pid_t pid;
+ Unit *u;
int r;
assert(link);
+ r = varlink_get_peer_pid(link, &pid);
+ if (r < 0)
+ return r;
+
+ u = manager_get_unit_by_pid(m, pid);
+ if (!u)
+ return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+ /* This is meant to be a deterrent and not actual security. The alternative is to check for the systemd-oom
+ * user that this unit runs as, but NSS lookups are blocking and not allowed from PID 1. */
+ if (!streq(u->id, "systemd-oomd.service"))
+ return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
if (json_variant_elements(parameters) > 0)
return varlink_error_invalid_parameter(link, parameters);
@@ -482,24 +497,9 @@ static int manager_varlink_init_system(Manager *m) {
if (!MANAGER_IS_SYSTEM(m))
return 0;
- r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate varlink server object: %m");
-
- varlink_server_set_userdata(s, m);
-
- r = varlink_server_bind_method_many(
- s,
- "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
- "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
- "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships,
- "io.systemd.ManagedOOM.SubscribeManagedOOMCGroups", vl_method_subscribe_managed_oom_cgroups);
- if (r < 0)
- return log_error_errno(r, "Failed to register varlink methods: %m");
-
- r = varlink_server_bind_disconnect(s, vl_disconnect);
+ r = manager_setup_varlink_server(m, &s);
if (r < 0)
- return log_error_errno(r, "Failed to register varlink disconnect handler: %m");
+ return log_error_errno(r, "Failed to set up varlink server: %m");
if (!MANAGER_IS_TEST_RUN(m)) {
(void) mkdir_p_label("/run/systemd/userdb", 0755);
@@ -582,6 +582,36 @@ static int manager_varlink_init_user(Manager *m) {
return 1;
}
+int manager_setup_varlink_server(Manager *m, VarlinkServer **ret) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to allocate varlink server object: %m");
+
+ varlink_server_set_userdata(s, m);
+
+ r = varlink_server_bind_method_many(
+ s,
+ "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
+ "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+ "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships,
+ "io.systemd.ManagedOOM.SubscribeManagedOOMCGroups", vl_method_subscribe_managed_oom_cgroups);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to register varlink methods: %m");
+
+ r = varlink_server_bind_disconnect(s, vl_disconnect);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to register varlink disconnect handler: %m");
+
+ *ret = TAKE_PTR(s);
+ return 0;
+}
+
int manager_varlink_init(Manager *m) {
return MANAGER_IS_SYSTEM(m) ? manager_varlink_init_system(m) : manager_varlink_init_user(m);
}
diff --git a/src/core/core-varlink.h b/src/core/core-varlink.h
index 20507a4187..7f810d1f25 100644
--- a/src/core/core-varlink.h
+++ b/src/core/core-varlink.h
@@ -6,6 +6,10 @@
int manager_varlink_init(Manager *m);
void manager_varlink_done(Manager *m);
+/* Creates a new VarlinkServer and binds methods. Does not set up sockets or attach events.
+ * Used for manager serialize/deserialize. */
+int manager_setup_varlink_server(Manager *m, VarlinkServer **ret_s);
+
/* The manager is expected to send an update to systemd-oomd if one of the following occurs:
* - The value of ManagedOOM*= properties change
* - A unit with ManagedOOM*= properties changes unit active state */
diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
index 914bd92e36..27cb0925ae 100644
--- a/src/core/manager-serialize.c
+++ b/src/core/manager-serialize.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "clean-ipc.h"
+#include "core-varlink.h"
#include "dbus.h"
#include "fd-util.h"
#include "fileio.h"
@@ -13,6 +14,7 @@
#include "syslog-util.h"
#include "unit-serialize.h"
#include "user-util.h"
+#include "varlink-internal.h"
int manager_open_serialization(Manager *m, FILE **ret_f) {
_cleanup_close_ int fd = -1;
@@ -175,6 +177,10 @@ int manager_serialize(
if (r < 0)
return r;
+ r = varlink_server_serialize(m->varlink_server, f, fds);
+ if (r < 0)
+ return r;
+
(void) fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units) {
@@ -290,6 +296,7 @@ static void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
}
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+ bool deserialize_varlink_sockets = false;
int r = 0;
assert(m);
@@ -516,7 +523,32 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
if (strv_extend(&m->deserialized_subscribed, val) < 0)
return -ENOMEM;
+ } else if ((val = startswith(l, "varlink-server-socket-address="))) {
+ if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+
+ r = manager_setup_varlink_server(m, &s);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to setup varlink server, ignoring: %m");
+ continue;
+ }
+
+ r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to attach varlink connection to event loop, ignoring: %m");
+ continue;
+ }
+
+ m->varlink_server = TAKE_PTR(s);
+ deserialize_varlink_sockets = true;
+ }
+ /* To void unnecessary deserialization (i.e. during reload vs. reexec) we only deserialize
+ * the FDs if we had to create a new m->varlink_server. The deserialize_varlink_sockets flag
+ * is initialized outside of the loop, is flipped after the VarlinkServer is setup, and
+ * remains set until all serialized contents are handled. */
+ if (deserialize_varlink_sockets)
+ (void) varlink_server_deserialize_one(m->varlink_server, val, fds);
} else {
ManagerTimestamp q;
diff --git a/src/shared/meson.build b/src/shared/meson.build
index e805e9b898..9e11e13934 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -326,6 +326,7 @@ shared_sources = files(
'utmp-wtmp.h',
'varlink.c',
'varlink.h',
+ 'varlink-internal.h',
'verb-log-control.c',
'verb-log-control.h',
'verbs.c',
diff --git a/src/shared/varlink-internal.h b/src/shared/varlink-internal.h
new file mode 100644
index 0000000000..715202a49e
--- /dev/null
+++ b/src/shared/varlink-internal.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdio.h>
+
+#include "fdset.h"
+#include "varlink.h"
+
+int varlink_server_serialize(VarlinkServer *s, FILE *f, FDSet *fds);
+int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *fds);
diff --git a/src/shared/varlink.c b/src/shared/varlink.c
index 580ebac4db..4f7ac97689 100644
--- a/src/shared/varlink.c
+++ b/src/shared/varlink.c
@@ -12,6 +12,7 @@
#include "list.h"
#include "process-util.h"
#include "selinux-util.h"
+#include "serialize.h"
#include "set.h"
#include "socket-util.h"
#include "string-table.h"
@@ -21,6 +22,7 @@
#include "umask-util.h"
#include "user-util.h"
#include "varlink.h"
+#include "varlink-internal.h"
#define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U
#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 1024U
@@ -2197,6 +2199,16 @@ int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret)
return 0;
}
+static VarlinkServerSocket *varlink_server_socket_free(VarlinkServerSocket *ss) {
+ if (!ss)
+ return NULL;
+
+ free(ss->address);
+ return mfree(ss);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServerSocket *, varlink_server_socket_free);
+
static int connect_callback(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
VarlinkServerSocket *ss = ASSERT_PTR(userdata);
_cleanup_close_ int cfd = -1;
@@ -2233,12 +2245,13 @@ static int connect_callback(sd_event_source *source, int fd, uint32_t revents, v
return 0;
}
-int varlink_server_listen_fd(VarlinkServer *s, int fd) {
- _cleanup_free_ VarlinkServerSocket *ss = NULL;
+static int varlink_server_create_listen_fd_socket(VarlinkServer *s, int fd, VarlinkServerSocket **ret_ss) {
+ _cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL;
int r;
- assert_return(s, -EINVAL);
- assert_return(fd >= 0, -EBADF);
+ assert(s);
+ assert(fd >= 0);
+ assert(ret_ss);
r = fd_nonblock(fd, true);
if (r < 0)
@@ -2263,11 +2276,27 @@ int varlink_server_listen_fd(VarlinkServer *s, int fd) {
return r;
}
+ *ret_ss = TAKE_PTR(ss);
+ return 0;
+}
+
+int varlink_server_listen_fd(VarlinkServer *s, int fd) {
+ _cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL;
+ int r;
+
+ assert_return(s, -EINVAL);
+ assert_return(fd >= 0, -EBADF);
+
+ r = varlink_server_create_listen_fd_socket(s, fd, &ss);
+ if (r < 0)
+ return r;
+
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
return 0;
}
int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t m) {
+ _cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL;
union sockaddr_union sockaddr;
socklen_t sockaddr_len;
_cleanup_close_ int fd = -1;
@@ -2299,10 +2328,15 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t
if (listen(fd, SOMAXCONN) < 0)
return -errno;
- r = varlink_server_listen_fd(s, fd);
+ r = varlink_server_create_listen_fd_socket(s, fd, &ss);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&ss->address, address);
if (r < 0)
return r;
+ LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
TAKE_FD(fd);
return 0;
}
@@ -2348,6 +2382,29 @@ int varlink_server_shutdown(VarlinkServer *s) {
return 0;
}
+static int varlink_server_add_socket_event_source(VarlinkServer *s, VarlinkServerSocket *ss, int64_t priority) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *es = NULL;
+
+ int r;
+
+ assert(s);
+ assert(s->event);
+ assert(ss);
+ assert(ss->fd >= 0);
+ assert(!ss->event_source);
+
+ r = sd_event_add_io(s->event, &es, ss->fd, EPOLLIN, connect_callback, ss);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(es, priority);
+ if (r < 0)
+ return r;
+
+ ss->event_source = TAKE_PTR(es);
+ return 0;
+}
+
int varlink_server_attach_event(VarlinkServer *s, sd_event *e, int64_t priority) {
int r;
@@ -2363,13 +2420,7 @@ int varlink_server_attach_event(VarlinkServer *s, sd_event *e, int64_t priority)
}
LIST_FOREACH(sockets, ss, s->sockets) {
- assert(!ss->event_source);
-
- r = sd_event_add_io(s->event, &ss->event_source, ss->fd, EPOLLIN, connect_callback, ss);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(ss->event_source, priority);
+ r = varlink_server_add_socket_event_source(s, ss, priority);
if (r < 0)
goto fail;
}
@@ -2528,3 +2579,84 @@ int varlink_server_set_description(VarlinkServer *s, const char *description) {
return free_and_strdup(&s->description, description);
}
+
+int varlink_server_serialize(VarlinkServer *s, FILE *f, FDSet *fds) {
+ assert(f);
+ assert(fds);
+
+ if (!s)
+ return 0;
+
+ LIST_FOREACH(sockets, ss, s->sockets) {
+ int copy;
+
+ assert(ss->address);
+ assert(ss->fd >= 0);
+
+ fprintf(f, "varlink-server-socket-address=%s", ss->address);
+
+ /* If we fail to serialize the fd, it will be considered an error during deserialization */
+ copy = fdset_put_dup(fds, ss->fd);
+ if (copy < 0)
+ return copy;
+
+ fprintf(f, " varlink-server-socket-fd=%i", copy);
+
+ fputc('\n', f);
+ }
+
+ return 0;
+}
+
+int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *fds) {
+ _cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL;
+ _cleanup_free_ char *address = NULL;
+ const char *v = ASSERT_PTR(value);
+ int r, fd = -1;
+ char *buf;
+ size_t n;
+
+ assert(s);
+ assert(fds);
+
+ n = strcspn(v, " ");
+ address = strndup(v, n);
+ if (!address)
+ return log_oom_debug();
+
+ if (v[n] != ' ')
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to deserialize VarlinkServerSocket: %s: %m", value);
+ v = startswith(v + n + 1, "varlink-server-socket-fd=");
+ if (!v)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to deserialize VarlinkServerSocket fd %s: %m", value);
+
+ n = strcspn(v, " ");
+ buf = strndupa_safe(v, n);
+
+ r = safe_atoi(buf, &fd);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
+
+ if (!fdset_contains(fds, fd))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
+ "VarlinkServerSocket varlink-server-socket-fd= has unknown fd %d: %m", fd);
+
+ ss = new(VarlinkServerSocket, 1);
+ if (!ss)
+ return log_oom_debug();
+
+ *ss = (VarlinkServerSocket) {
+ .server = s,
+ .address = TAKE_PTR(address),
+ .fd = fdset_remove(fds, fd),
+ };
+
+ r = varlink_server_add_socket_event_source(s, ss, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
+
+ LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
+ return 0;
+}
diff --git a/src/shared/varlink.h b/src/shared/varlink.h
index 66a1ff630e..9518cd9098 100644
--- a/src/shared/varlink.h
+++ b/src/shared/varlink.h
@@ -173,3 +173,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
#define VARLINK_ERROR_METHOD_NOT_IMPLEMENTED "org.varlink.service.MethodNotImplemented"
#define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter"
#define VARLINK_ERROR_SUBSCRIPTION_TAKEN "org.varlink.service.SubscriptionTaken"
+#define VARLINK_ERROR_PERMISSION_DENIED "org.varlink.service.PermissionDenied"