#include "bus-errors.h"
#include "strxcpyx.h"
#include "dbus-client-track.h"
+#include "bus-internal.h"
+#include "selinux-access.h"
#define CONNECTIONS_MAX 512
return 0;
}
-static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int selinux_filter(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *verb, *path;
+ Unit *u = NULL;
+ Job *j;
+ int r;
+
+ assert(bus);
+ assert(message);
+
+ /* Our own method calls are all protected individually with
+ * selinux checks, but the built-in interfaces need to be
+ * protected too. */
+
+ if (sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set"))
+ verb = "reload";
+ else if (sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", NULL) ||
+ sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Properties", NULL) ||
+ sd_bus_message_is_method_call(message, "org.freedesktop.DBus.ObjectManager", NULL) ||
+ sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Peer", NULL))
+ verb = "status";
+ else
+ return 0;
+
+ path = sd_bus_message_get_path(message);
+
+ if (object_path_startswith("/org/freedesktop/systemd1", path)) {
+
+ r = selinux_access_check(bus, message, verb, error);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ pid_t pid;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return 0;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return 0;
+
+ u = manager_get_unit_by_pid(m, pid);
+ } else {
+ r = manager_get_job_from_dbus_path(m, path, &j);
+ if (r >= 0)
+ u = j->unit;
+ else
+ manager_load_unit_from_dbus_path(m, path, NULL, &u);
+ }
+
+ if (!u)
+ return 0;
+
+ r = selinux_unit_access_check(u, bus, message, verb, error);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
Job *j;
int r;
return 1;
}
-static Unit *find_unit(Manager *m, sd_bus *bus, const char *path) {
+static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) {
Unit *u;
int r;
assert(path);
if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
sd_bus_message *message;
pid_t pid;
message = sd_bus_get_current(bus);
if (!message)
- return NULL;
+ return 0;
- r = sd_bus_get_owner_pid(bus, sd_bus_message_get_sender(message), &pid);
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
- return NULL;
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
u = manager_get_unit_by_pid(m, pid);
} else {
- r = manager_load_unit_from_dbus_path(m, path, NULL, &u);
+ r = manager_load_unit_from_dbus_path(m, path, error, &u);
if (r < 0)
- return NULL;
+ return 0;
}
- return u;
+ if (!u)
+ return 0;
+
+ *unit = u;
+ return 1;
}
-static int bus_unit_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int bus_unit_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
- Unit *u;
assert(bus);
assert(path);
assert(found);
assert(m);
- u = find_unit(m, bus, path);
- if (!u)
- return 0;
-
- *found = u;
- return 1;
+ return find_unit(m, bus, path, (Unit**) found, error);
}
-static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
Unit *u;
+ int r;
assert(bus);
assert(path);
assert(found);
assert(m);
- u = find_unit(m, bus, path);
- if (!u)
- return 0;
+ r = find_unit(m, bus, path, &u, error);
+ if (r <= 0)
+ return r;
if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
return 0;
return 1;
}
-static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
Unit *u;
+ int r;
assert(bus);
assert(path);
assert(found);
assert(m);
- u = find_unit(m, bus, path);
- if (!u)
- return 0;
+ r = find_unit(m, bus, path, &u, error);
+ if (r <= 0)
+ return r;
if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
return 0;
return 1;
}
-static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
CGroupContext *c;
Unit *u;
+ int r;
assert(bus);
assert(path);
assert(found);
assert(m);
- u = find_unit(m, bus, path);
- if (!u)
- return 0;
+ r = find_unit(m, bus, path, &u, error);
+ if (r <= 0)
+ return r;
if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
return 0;
return 1;
}
-static int bus_exec_context_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int bus_exec_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
ExecContext *c;
Unit *u;
+ int r;
assert(bus);
assert(path);
assert(found);
assert(m);
- u = find_unit(m, bus, path);
- if (!u)
- return 0;
+ r = find_unit(m, bus, path, &u, error);
+ if (r <= 0)
+ return r;
if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
return 0;
return 1;
}
-static int bus_kill_context_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
+static int bus_kill_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = userdata;
KillContext *c;
Unit *u;
+ int r;
assert(bus);
assert(path);
assert(found);
assert(m);
- u = find_unit(m, bus, path);
- if (!u)
- return 0;
+ r = find_unit(m, bus, path, &u, error);
+ if (r <= 0)
+ return r;
if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
return 0;
return 1;
}
-static int bus_job_enumerate(sd_bus *bus, const char *path, char ***nodes, void *userdata) {
+static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_free_ char **l = NULL;
Manager *m = userdata;
unsigned k = 0;
return k;
}
-static int bus_unit_enumerate(sd_bus *bus, const char *path, char ***nodes, void *userdata) {
+static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_free_ char **l = NULL;
Manager *m = userdata;
unsigned k = 0;
assert(m);
assert(bus);
+ r = sd_bus_add_filter(bus, selinux_filter, m);
+ if (r < 0) {
+ log_error("Failed to add SELinux access filter: %s", strerror(-r));
+ return r;
+ }
+
r = sd_bus_add_object_vtable(bus, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", bus_manager_vtable, m);
if (r < 0) {
log_error("Failed to register Manager vtable: %s", strerror(-r));
}
static int bus_list_names(Manager *m, sd_bus *bus) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- const char *name;
+ _cleanup_strv_free_ char **names = NULL;
+ char **i;
int r;
assert(m);
assert(bus);
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "ListNames",
- &error, &reply,
- NULL);
+ r = sd_bus_list_names(bus, &names, NULL);
if (r < 0) {
- log_error("Failed to get initial list of names: %s", bus_error_message(&error, r));
+ log_error("Failed to get initial list of names: %s", strerror(-r));
return r;
}
- r = sd_bus_message_enter_container(reply, 'a', "s");
- if (r < 0)
- return bus_log_parse_error(r);
-
/* This is a bit hacky, we say the owner of the name is the
* name itself, because we don't want the extra traffic to
* figure out the real owner. */
- while ((r = sd_bus_message_read(reply, "s", &name)) > 0)
- manager_dispatch_bus_name_owner_changed(m, name, NULL, name);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
+ STRV_FOREACH(i, names)
+ manager_dispatch_bus_name_owner_changed(m, *i, NULL, *i);
return 0;
}
* after the new connection is set up and the name installed
* to allow clients to synchronously wait for reexecution to
* finish */
- r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING);
+ r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_DO_NOT_QUEUE);
if (r < 0) {
log_error("Failed to register name: %s", strerror(-r));
return r;
}
- if (r != SD_BUS_NAME_PRIMARY_OWNER) {
- log_error("Failed to acquire name.");
- return -EEXIST;
- }
-
bus_list_names(m, bus);
log_debug("Successfully connected to API bus.");
if (m->private_listen_fd >= 0)
return 0;
+ /* We don't need the private socket if we have kdbus */
+ if (m->kdbus_fd >= 0)
+ return 0;
+
if (m->running_as == SYSTEMD_SYSTEM) {
/* We want the private bus only when running as init */