diff options
Diffstat (limited to 'src/shared/bus-util.c')
-rw-r--r-- | src/shared/bus-util.c | 249 |
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; + } +} |