summaryrefslogtreecommitdiffstats
path: root/src/shared/bus-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/bus-util.c')
-rw-r--r--src/shared/bus-util.c249
1 files changed, 233 insertions, 16 deletions
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 4123152..30f9602 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -18,12 +18,17 @@
#include "bus-internal.h"
#include "bus-label.h"
#include "bus-util.h"
+#include "capsule-util.h"
+#include "chase.h"
+#include "daemon-util.h"
#include "data-fd-util.h"
#include "fd-util.h"
+#include "format-util.h"
#include "memstream-util.h"
#include "path-util.h"
#include "socket-util.h"
#include "stdio-util.h"
+#include "uid-classification.h"
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = ASSERT_PTR(userdata);
@@ -128,8 +133,8 @@ int bus_event_loop_with_idle(
if (r == 0 && !exiting && idle) {
/* Inform the service manager that we are going down, so that it will queue all
- * further start requests, instead of assuming we are already running. */
- sd_notify(false, "STOPPING=1");
+ * further start requests, instead of assuming we are still running. */
+ (void) sd_notify(false, NOTIFY_STOPPING);
r = bus_async_unregister_and_exit(e, bus, name);
if (r < 0)
@@ -267,6 +272,131 @@ int bus_connect_user_systemd(sd_bus **ret_bus) {
return 0;
}
+static int pin_capsule_socket(const char *capsule, const char *suffix, uid_t *ret_uid, gid_t *ret_gid) {
+ _cleanup_close_ int inode_fd = -EBADF;
+ _cleanup_free_ char *p = NULL;
+ struct stat st;
+ int r;
+
+ assert(capsule);
+ assert(suffix);
+ assert(ret_uid);
+ assert(ret_gid);
+
+ p = path_join("/run/capsules", capsule, suffix);
+ if (!p)
+ return -ENOMEM;
+
+ /* We enter territory owned by the user, hence let's be paranoid about symlinks and ownership */
+ r = chase(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &inode_fd);
+ if (r < 0)
+ return r;
+
+ if (fstat(inode_fd, &st) < 0)
+ return negative_errno();
+
+ /* Paranoid safety check */
+ if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid))
+ return -EPERM;
+
+ *ret_uid = st.st_uid;
+ *ret_gid = st.st_gid;
+
+ return TAKE_FD(inode_fd);
+}
+
+static int bus_set_address_capsule(sd_bus *bus, const char *capsule, const char *suffix, int *ret_pin_fd) {
+ _cleanup_close_ int inode_fd = -EBADF;
+ _cleanup_free_ char *pp = NULL;
+ uid_t uid;
+ gid_t gid;
+ int r;
+
+ assert(bus);
+ assert(capsule);
+ assert(suffix);
+ assert(ret_pin_fd);
+
+ /* Connects to a capsule's user bus. We need to do so under the capsule's UID/GID, otherwise
+ * the service manager might refuse our connection. Hence fake it. */
+
+ r = capsule_name_is_valid(capsule);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ inode_fd = pin_capsule_socket(capsule, suffix, &uid, &gid);
+ if (inode_fd < 0)
+ return inode_fd;
+
+ pp = bus_address_escape(FORMAT_PROC_FD_PATH(inode_fd));
+ if (!pp)
+ return -ENOMEM;
+
+ if (asprintf(&bus->address, "unix:path=%s,uid=" UID_FMT ",gid=" GID_FMT, pp, uid, gid) < 0)
+ return -ENOMEM;
+
+ *ret_pin_fd = TAKE_FD(inode_fd); /* This fd must be kept pinned until the connection has been established */
+ return 0;
+}
+
+int bus_set_address_capsule_bus(sd_bus *bus, const char *capsule, int *ret_pin_fd) {
+ return bus_set_address_capsule(bus, capsule, "bus", ret_pin_fd);
+}
+
+int bus_connect_capsule_systemd(const char *capsule, sd_bus **ret_bus) {
+ _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_close_ int inode_fd = -EBADF;
+ int r;
+
+ assert(capsule);
+ assert(ret_bus);
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = bus_set_address_capsule(bus, capsule, "systemd/private", &inode_fd);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return r;
+
+ *ret_bus = TAKE_PTR(bus);
+ return 0;
+}
+
+int bus_connect_capsule_bus(const char *capsule, sd_bus **ret_bus) {
+ _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_close_ int inode_fd = -EBADF;
+ int r;
+
+ assert(capsule);
+ assert(ret_bus);
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = bus_set_address_capsule_bus(bus, capsule, &inode_fd);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_bus_client(bus, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return r;
+
+ *ret_bus = TAKE_PTR(bus);
+ return 0;
+}
+
int bus_connect_transport(
BusTransport transport,
const char *host,
@@ -280,12 +410,10 @@ int bus_connect_transport(
assert(transport < _BUS_TRANSPORT_MAX);
assert(ret);
- assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport != BUS_TRANSPORT_REMOTE || runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
-
switch (transport) {
case BUS_TRANSPORT_LOCAL:
+ assert_return(!host, -EINVAL);
switch (runtime_scope) {
@@ -307,11 +435,12 @@ int bus_connect_transport(
break;
case BUS_TRANSPORT_REMOTE:
+ assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
+
r = sd_bus_open_system_remote(&bus, host);
break;
case BUS_TRANSPORT_MACHINE:
-
switch (runtime_scope) {
case RUNTIME_SCOPE_USER:
@@ -328,6 +457,12 @@ int bus_connect_transport(
break;
+ case BUS_TRANSPORT_CAPSULE:
+ assert_return(runtime_scope == RUNTIME_SCOPE_USER, -EINVAL);
+
+ r = bus_connect_capsule_bus(host, &bus);
+ break;
+
default:
assert_not_reached();
}
@@ -342,28 +477,32 @@ int bus_connect_transport(
return 0;
}
-int bus_connect_transport_systemd(BusTransport transport, const char *host, RuntimeScope runtime_scope, sd_bus **bus) {
+int bus_connect_transport_systemd(
+ BusTransport transport,
+ const char *host,
+ RuntimeScope runtime_scope,
+ sd_bus **ret_bus) {
+
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
-
- assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
+ assert(ret_bus);
switch (transport) {
case BUS_TRANSPORT_LOCAL:
+ assert_return(!host, -EINVAL);
+
switch (runtime_scope) {
case RUNTIME_SCOPE_USER:
- return bus_connect_user_systemd(bus);
+ return bus_connect_user_systemd(ret_bus);
case RUNTIME_SCOPE_SYSTEM:
if (sd_booted() <= 0)
/* Print a friendly message when the local system is actually not running systemd as PID 1. */
return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
"System has not been booted with systemd as init system (PID 1). Can't operate.");
- return bus_connect_system_systemd(bus);
+ return bus_connect_system_systemd(ret_bus);
default:
assert_not_reached();
@@ -372,10 +511,16 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, Runt
break;
case BUS_TRANSPORT_REMOTE:
- return sd_bus_open_system_remote(bus, host);
+ assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
+ return sd_bus_open_system_remote(ret_bus, host);
case BUS_TRANSPORT_MACHINE:
- return sd_bus_open_system_machine(bus, host);
+ assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
+ return sd_bus_open_system_machine(ret_bus, host);
+
+ case BUS_TRANSPORT_CAPSULE:
+ assert_return(runtime_scope == RUNTIME_SCOPE_USER, -EINVAL);
+ return bus_connect_capsule_systemd(host, ret_bus);
default:
assert_not_reached();
@@ -626,7 +771,7 @@ static int method_dump_memory_state_by_fd(sd_bus_message *message, void *userdat
if (r < 0)
return r;
- fd = acquire_data_fd(dump, dump_size, 0);
+ fd = acquire_data_fd_full(dump, dump_size, /* flags = */ 0);
if (fd < 0)
return fd;
@@ -709,3 +854,75 @@ int bus_property_get_string_set(
return bus_message_append_string_set(reply, *s);
}
+
+int bus_creds_get_pidref(
+ sd_bus_creds *c,
+ PidRef *ret) {
+
+ int pidfd = -EBADF;
+ pid_t pid;
+ int r;
+
+ assert(c);
+ assert(ret);
+
+ r = sd_bus_creds_get_pid(c, &pid);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pidfd_dup(c, &pidfd);
+ if (r < 0 && r != -ENODATA)
+ return r;
+
+ *ret = (PidRef) {
+ .pid = pid,
+ .fd = pidfd,
+ };
+
+ return 0;
+}
+
+int bus_query_sender_pidref(
+ sd_bus_message *m,
+ PidRef *ret) {
+
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_PID|SD_BUS_CREDS_PIDFD, &creds);
+ if (r < 0)
+ return r;
+
+ return bus_creds_get_pidref(creds, ret);
+}
+
+int bus_message_read_id128(sd_bus_message *m, sd_id128_t *ret) {
+ const void *a;
+ size_t sz;
+ int r;
+
+ assert(m);
+
+ r = sd_bus_message_read_array(m, 'y', &a, &sz);
+ if (r < 0)
+ return r;
+
+ switch (sz) {
+ case 0:
+ if (ret)
+ *ret = SD_ID128_NULL;
+ return 0;
+
+ case sizeof(sd_id128_t):
+ if (ret)
+ memcpy(ret, a, sz);
+ return !memeqzero(a, sz); /* This mimics sd_id128_is_null(), but ret may be NULL,
+ * and a may be misaligned, so use memeqzero() here. */
+
+ default:
+ return -EINVAL;
+ }
+}