From cb67f7184e61fafc984c4cd1e921d71a7e67c2ae Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 18 Jan 2014 20:06:21 +0100 Subject: [PATCH] bus-kernel: move bus_kernel_make_message This makes future commits more readable. --- src/libsystemd/bus-kernel.c | 930 ++++++++++++++++++------------------ 1 file changed, 465 insertions(+), 465 deletions(-) diff --git a/src/libsystemd/bus-kernel.c b/src/libsystemd/bus-kernel.c index d3eeb5242..f85b4d567 100644 --- a/src/libsystemd/bus-kernel.c +++ b/src/libsystemd/bus-kernel.c @@ -320,630 +320,630 @@ fail: return r; } -int bus_kernel_take_fd(sd_bus *b) { - struct kdbus_cmd_hello *hello; - struct kdbus_item *item; - size_t l = 0, sz; +static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { + sd_bus_message *m = NULL; + struct kdbus_item *d; + unsigned n_fds = 0; + _cleanup_free_ int *fds = NULL; + struct bus_header *h = NULL; + size_t total, n_bytes = 0, idx = 0; + const char *destination = NULL, *seclabel = NULL; int r; - assert(b); + assert(bus); + assert(k); + assert(k->payload_type == KDBUS_PAYLOAD_DBUS); - if (b->is_server) - return -EINVAL; + KDBUS_ITEM_FOREACH(d, k, items) { + size_t l; - b->use_memfd = 1; + l = d->size - offsetof(struct kdbus_item, data); - sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)); + switch (d->type) { - if (b->fake_creds_valid) - sz += ALIGN8(offsetof(struct kdbus_item, creds)) + sizeof(struct kdbus_creds); + case KDBUS_ITEM_PAYLOAD_OFF: + if (!h) { + h = (struct bus_header *)((uint8_t *)k + d->vec.offset); - if (b->fake_label) { - l = strlen(b->fake_label); - sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); - } + if (!bus_header_is_complete(h, d->vec.size)) + return -EBADMSG; + } - hello = alloca0(sz); - hello->size = sz; - hello->conn_flags = b->hello_flags; - hello->attach_flags = b->attach_flags; - hello->pool_size = KDBUS_POOL_SIZE; + n_bytes += d->vec.size; + break; - item = hello->items; + case KDBUS_ITEM_PAYLOAD_MEMFD: + if (!h) + return -EBADMSG; - if (b->fake_creds_valid) { - item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds); - item->type = KDBUS_ITEM_CREDS; - item->creds = b->fake_creds; + n_bytes += d->memfd.size; + break; - item = KDBUS_ITEM_NEXT(item); - } + case KDBUS_ITEM_FDS: { + int *f; + unsigned j; - if (b->fake_label) { - item->size = offsetof(struct kdbus_item, str) + l + 1; - memcpy(item->str, b->fake_label, l+1); - } + j = l / sizeof(int); + f = realloc(fds, sizeof(int) * (n_fds + j)); + if (!f) + return -ENOMEM; - r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); - if (r < 0) - return -errno; + fds = f; + memcpy(fds + n_fds, d->fds, sizeof(int) * j); + n_fds += j; + break; + } - if (!b->kdbus_buffer) { - b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0); - if (b->kdbus_buffer == MAP_FAILED) { - b->kdbus_buffer = NULL; - return -errno; + case KDBUS_ITEM_SECLABEL: + seclabel = d->str; + break; } } - /* The higher 32bit of both flags fields are considered - * 'incompatible flags'. Refuse them all for now. */ - if (hello->bus_flags > 0xFFFFFFFFULL || - hello->conn_flags > 0xFFFFFFFFULL) - return -ENOTSUP; - - if (hello->bloom_size != BLOOM_SIZE) - return -ENOTSUP; - - if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) - return -ENOMEM; + if (!h) + return -EBADMSG; - b->unique_id = hello->id; + r = bus_header_message_size(h, &total); + if (r < 0) + return r; - b->is_kernel = true; - b->bus_client = true; - b->can_fds = !!(hello->conn_flags & KDBUS_HELLO_ACCEPT_FD); - b->message_version = 2; - b->message_endian = BUS_NATIVE_ENDIAN; + if (n_bytes != total) + return -EBADMSG; - /* the kernel told us the UUID of the underlying bus */ - memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes)); + /* on kdbus we only speak native endian gvariant, never dbus1 + * marshalling or reverse endian */ + if (h->version != 2 || + h->endian != BUS_NATIVE_ENDIAN) + return -EPROTOTYPE; - return bus_start_running(b); -} + r = bus_message_from_header(bus, h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m); + if (r < 0) + return r; -int bus_kernel_connect(sd_bus *b) { - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->kernel); + /* The well-known names list is different from the other + credentials. If we asked for it, but nothing is there, this + means that the list of well-known names is simply empty, not + that we lack any data */ - if (b->is_server) - return -EINVAL; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; - b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (b->input_fd < 0) - return -errno; + KDBUS_ITEM_FOREACH(d, k, items) { + size_t l; - b->output_fd = b->input_fd; + l = d->size - offsetof(struct kdbus_item, data); - return bus_kernel_take_fd(b); -} + switch (d->type) { -int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) { - int r; + case KDBUS_ITEM_PAYLOAD_OFF: { + size_t begin_body; - assert(bus); - assert(m); - assert(bus->state == BUS_RUNNING); + begin_body = BUS_MESSAGE_BODY_BEGIN(m); - /* If we can't deliver, we want room for the error message */ - r = bus_rqueue_make_room(bus); - if (r < 0) - return r; + if (idx + d->vec.size > begin_body) { + struct bus_body_part *part; - r = bus_message_setup_kmsg(bus, m); - if (r < 0) - return r; + /* Contains body material */ - r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus); - if (r < 0) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus_message *reply; + part = message_append_part(m); + if (!part) { + r = -ENOMEM; + goto fail; + } - if (errno == EAGAIN || errno == EINTR) - return 0; - else if (errno == ENXIO || errno == ESRCH) { + /* A -1 offset is NUL padding. */ + part->is_zero = d->vec.offset == ~0ULL; - /* ENXIO: unique name not known - * ESRCH: well-known name not known */ + if (idx >= begin_body) { + if (!part->is_zero) + part->data = (uint8_t *)k + d->vec.offset; + part->size = d->vec.size; + } else { + if (!part->is_zero) + part->data = (uint8_t *)k + d->vec.offset + (begin_body - idx); + part->size = d->vec.size - (begin_body - idx); + } - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination); - else { - log_debug("Could not deliver message to %s as destination is not known. Ignoring.", m->destination); - return 0; + part->sealed = true; } - } else if (errno == EADDRNOTAVAIL) { + idx += d->vec.size; + break; + } - /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */ + case KDBUS_ITEM_PAYLOAD_MEMFD: { + struct bus_body_part *part; - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination); - else { - log_debug("Could not deliver message to %s as destination is not activated. Ignoring.", m->destination); - return 0; + if (idx < BUS_MESSAGE_BODY_BEGIN(m)) { + r = -EBADMSG; + goto fail; } - } else - return -errno; - r = bus_message_new_synthetic_error( - bus, - BUS_MESSAGE_COOKIE(m), - &error, - &reply); + part = message_append_part(m); + if (!part) { + r = -ENOMEM; + goto fail; + } - if (r < 0) - return r; + part->memfd = d->memfd.fd; + part->size = d->memfd.size; + part->sealed = true; - r = bus_seal_synthetic_message(bus, reply); - if (r < 0) - return r; + idx += d->memfd.size; + break; + } - bus->rqueue[bus->rqueue_size++] = reply; + case KDBUS_ITEM_CREDS: + /* UID/GID/PID are always valid */ + m->creds.uid = (uid_t) d->creds.uid; + m->creds.gid = (gid_t) d->creds.gid; + m->creds.pid = (pid_t) d->creds.pid; + m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID) & bus->creds_mask; - return 0; - } + /* The PID starttime/TID might be missing + * however, when the data is faked by some + * data bus proxy and it lacks that + * information about the real client since + * SO_PEERCRED is used for that */ - return 1; -} + if (d->creds.starttime > 0) { + m->creds.pid_starttime = d->creds.starttime / NSEC_PER_USEC; + m->creds.mask |= SD_BUS_CREDS_PID_STARTTIME & bus->creds_mask; + } -static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { - uint64_t off; - struct kdbus_item *d; + if (d->creds.tid > 0) { + m->creds.tid = (pid_t) d->creds.tid; + m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; + } + break; - assert(bus); - assert(k); + case KDBUS_ITEM_TIMESTAMP: + m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC; + m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC; + break; - off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer; - ioctl(bus->input_fd, KDBUS_CMD_FREE, &off); + case KDBUS_ITEM_PID_COMM: + m->creds.comm = d->str; + m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask; + break; - KDBUS_ITEM_FOREACH(d, k, items) { + case KDBUS_ITEM_TID_COMM: + m->creds.tid_comm = d->str; + m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask; + break; - if (d->type == KDBUS_ITEM_FDS) - close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int)); - else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD) - close_nointr_nofail(d->memfd.fd); - } -} + case KDBUS_ITEM_EXE: + m->creds.exe = d->str; + m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask; + break; -static int push_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - int r; + case KDBUS_ITEM_CMDLINE: + m->creds.cmdline = d->str; + m->creds.cmdline_size = l; + m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask; + break; - assert(bus); + case KDBUS_ITEM_CGROUP: + m->creds.cgroup = d->str; + m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask; - r = sd_bus_message_new_signal( - bus, - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameOwnerChanged", - &m); - if (r < 0) - return r; + if (!bus->cgroup_root) { + r = cg_get_root_path(&bus->cgroup_root); + if (r < 0) + goto fail; + } - r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); - if (r < 0) - return r; + m->creds.cgroup_root = bus->cgroup_root; - m->sender = "org.freedesktop.DBus"; + break; - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; + case KDBUS_ITEM_AUDIT: + m->creds.audit_session_id = (uint32_t) d->audit.sessionid; + m->creds.audit_login_uid = (uid_t) d->audit.loginuid; + m->creds.mask |= (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID) & bus->creds_mask; + break; - bus->rqueue[bus->rqueue_size++] = m; - m = NULL; + case KDBUS_ITEM_CAPS: + m->creds.capability = d->data; + m->creds.capability_size = l; + m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask; + break; - return 1; -} + case KDBUS_ITEM_DST_NAME: + if (!service_name_is_valid(d->str)) + return -EBADMSG; -static int translate_name_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) { - char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX]; + destination = d->str; + break; - assert(bus); - assert(k); - assert(d); + case KDBUS_ITEM_NAME: + if (!service_name_is_valid(d->name.name)) + return -EBADMSG; - if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) - old_owner[0] = 0; - else - sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old.id); + r = strv_extend(&m->creds.well_known_names, d->name.name); + if (r < 0) + goto fail; + break; - if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) { + case KDBUS_ITEM_FDS: + case KDBUS_ITEM_SECLABEL: + break; - if (isempty(old_owner)) - return 0; + default: + log_debug("Got unknown field from kernel %llu", d->type); + } + } - new_owner[0] = 0; - } else - sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new.id); + r = bus_message_parse_fields(m); + if (r < 0) + goto fail; - return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner); -} + /* Override information from the user header with data from the kernel */ + if (k->src_id == KDBUS_SRC_ID_KERNEL) + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; + else { + snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id); + m->sender = m->creds.unique_name = m->sender_buffer; + } -static int translate_id_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) { - char owner[UNIQUE_NAME_MAX]; + if (destination) + m->destination = destination; + else if (k->dst_id == KDBUS_DST_ID_BROADCAST) + m->destination = NULL; + else if (k->dst_id == KDBUS_DST_ID_NAME) + m->destination = bus->unique_name; /* fill in unique name if the well-known name is missing */ + else { + snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id); + m->destination = m->destination_buffer; + } - assert(bus); - assert(k); - assert(d); + /* We take possession of the kmsg struct now */ + m->kdbus = k; + m->release_kdbus = true; + m->free_fds = true; + fds = NULL; - sprintf(owner, ":1.%llu", d->id_change.id); + bus->rqueue[bus->rqueue_size++] = m; - return push_name_owner_changed( - bus, owner, - d->type == KDBUS_ITEM_ID_ADD ? NULL : owner, - d->type == KDBUS_ITEM_ID_ADD ? owner : NULL); -} + return 1; -static int translate_reply(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - int r; +fail: + if (m) { + struct bus_body_part *part; + unsigned i; - assert(bus); - assert(k); - assert(d); + /* Make sure the memfds are not freed twice */ + MESSAGE_FOREACH_PART(part, i, m) + if (part->memfd >= 0) + part->memfd = -1; - r = bus_message_new_synthetic_error( - bus, - k->cookie_reply, - d->type == KDBUS_ITEM_REPLY_TIMEOUT ? - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") : - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"), - &m); - if (r < 0) - return r; + sd_bus_message_unref(m); + } - m->sender = "org.freedesktop.DBus"; + return r; +} - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; +int bus_kernel_take_fd(sd_bus *b) { + struct kdbus_cmd_hello *hello; + struct kdbus_item *item; + size_t l = 0, sz; + int r; - bus->rqueue[bus->rqueue_size++] = m; - m = NULL; + assert(b); - return 1; -} + if (b->is_server) + return -EINVAL; -static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) { - struct kdbus_item *d, *found = NULL; + b->use_memfd = 1; - static int (* const translate[])(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) = { - [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, - [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, - [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, + sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)); - [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, - [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, + if (b->fake_creds_valid) + sz += ALIGN8(offsetof(struct kdbus_item, creds)) + sizeof(struct kdbus_creds); - [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, - [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, - }; + if (b->fake_label) { + l = strlen(b->fake_label); + sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); + } - assert(bus); - assert(k); - assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); + hello = alloca0(sz); + hello->size = sz; + hello->conn_flags = b->hello_flags; + hello->attach_flags = b->attach_flags; + hello->pool_size = KDBUS_POOL_SIZE; - KDBUS_ITEM_FOREACH(d, k, items) { - if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) { - if (found) - return -EBADMSG; - found = d; - } else - log_debug("Got unknown field from kernel %llu", d->type); + item = hello->items; + + if (b->fake_creds_valid) { + item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds); + item->type = KDBUS_ITEM_CREDS; + item->creds = b->fake_creds; + + item = KDBUS_ITEM_NEXT(item); } - if (!found) { - log_debug("Didn't find a kernel message to translate."); - return 0; + if (b->fake_label) { + item->size = offsetof(struct kdbus_item, str) + l + 1; + memcpy(item->str, b->fake_label, l+1); } - return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found); -} + r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); + if (r < 0) + return -errno; -static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { - sd_bus_message *m = NULL; - struct kdbus_item *d; - unsigned n_fds = 0; - _cleanup_free_ int *fds = NULL; - struct bus_header *h = NULL; - size_t total, n_bytes = 0, idx = 0; - const char *destination = NULL, *seclabel = NULL; - int r; + if (!b->kdbus_buffer) { + b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0); + if (b->kdbus_buffer == MAP_FAILED) { + b->kdbus_buffer = NULL; + return -errno; + } + } - assert(bus); - assert(k); - assert(k->payload_type == KDBUS_PAYLOAD_DBUS); + /* The higher 32bit of both flags fields are considered + * 'incompatible flags'. Refuse them all for now. */ + if (hello->bus_flags > 0xFFFFFFFFULL || + hello->conn_flags > 0xFFFFFFFFULL) + return -ENOTSUP; - KDBUS_ITEM_FOREACH(d, k, items) { - size_t l; + if (hello->bloom_size != BLOOM_SIZE) + return -ENOTSUP; - l = d->size - offsetof(struct kdbus_item, data); + if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) + return -ENOMEM; - switch (d->type) { + b->unique_id = hello->id; - case KDBUS_ITEM_PAYLOAD_OFF: - if (!h) { - h = (struct bus_header *)((uint8_t *)k + d->vec.offset); + b->is_kernel = true; + b->bus_client = true; + b->can_fds = !!(hello->conn_flags & KDBUS_HELLO_ACCEPT_FD); + b->message_version = 2; + b->message_endian = BUS_NATIVE_ENDIAN; - if (!bus_header_is_complete(h, d->vec.size)) - return -EBADMSG; - } + /* the kernel told us the UUID of the underlying bus */ + memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes)); - n_bytes += d->vec.size; - break; + return bus_start_running(b); +} - case KDBUS_ITEM_PAYLOAD_MEMFD: - if (!h) - return -EBADMSG; +int bus_kernel_connect(sd_bus *b) { + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->kernel); - n_bytes += d->memfd.size; - break; + if (b->is_server) + return -EINVAL; - case KDBUS_ITEM_FDS: { - int *f; - unsigned j; + b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (b->input_fd < 0) + return -errno; - j = l / sizeof(int); - f = realloc(fds, sizeof(int) * (n_fds + j)); - if (!f) - return -ENOMEM; + b->output_fd = b->input_fd; - fds = f; - memcpy(fds + n_fds, d->fds, sizeof(int) * j); - n_fds += j; - break; - } + return bus_kernel_take_fd(b); +} - case KDBUS_ITEM_SECLABEL: - seclabel = d->str; - break; - } - } +int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) { + int r; - if (!h) - return -EBADMSG; + assert(bus); + assert(m); + assert(bus->state == BUS_RUNNING); - r = bus_header_message_size(h, &total); + /* If we can't deliver, we want room for the error message */ + r = bus_rqueue_make_room(bus); if (r < 0) return r; - if (n_bytes != total) - return -EBADMSG; - - /* on kdbus we only speak native endian gvariant, never dbus1 - * marshalling or reverse endian */ - if (h->version != 2 || - h->endian != BUS_NATIVE_ENDIAN) - return -EPROTOTYPE; - - r = bus_message_from_header(bus, h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m); + r = bus_message_setup_kmsg(bus, m); if (r < 0) return r; - /* The well-known names list is different from the other - credentials. If we asked for it, but nothing is there, this - means that the list of well-known names is simply empty, not - that we lack any data */ - - m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; + r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus); + if (r < 0) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply; - KDBUS_ITEM_FOREACH(d, k, items) { - size_t l; + if (errno == EAGAIN || errno == EINTR) + return 0; + else if (errno == ENXIO || errno == ESRCH) { - l = d->size - offsetof(struct kdbus_item, data); + /* ENXIO: unique name not known + * ESRCH: well-known name not known */ - switch (d->type) { + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination); + else { + log_debug("Could not deliver message to %s as destination is not known. Ignoring.", m->destination); + return 0; + } - case KDBUS_ITEM_PAYLOAD_OFF: { - size_t begin_body; + } else if (errno == EADDRNOTAVAIL) { - begin_body = BUS_MESSAGE_BODY_BEGIN(m); + /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */ - if (idx + d->vec.size > begin_body) { - struct bus_body_part *part; + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination); + else { + log_debug("Could not deliver message to %s as destination is not activated. Ignoring.", m->destination); + return 0; + } + } else + return -errno; - /* Contains body material */ + r = bus_message_new_synthetic_error( + bus, + BUS_MESSAGE_COOKIE(m), + &error, + &reply); - part = message_append_part(m); - if (!part) { - r = -ENOMEM; - goto fail; - } + if (r < 0) + return r; - /* A -1 offset is NUL padding. */ - part->is_zero = d->vec.offset == ~0ULL; + r = bus_seal_synthetic_message(bus, reply); + if (r < 0) + return r; - if (idx >= begin_body) { - if (!part->is_zero) - part->data = (uint8_t *)k + d->vec.offset; - part->size = d->vec.size; - } else { - if (!part->is_zero) - part->data = (uint8_t *)k + d->vec.offset + (begin_body - idx); - part->size = d->vec.size - (begin_body - idx); - } + bus->rqueue[bus->rqueue_size++] = reply; - part->sealed = true; - } + return 0; + } - idx += d->vec.size; - break; - } + return 1; +} - case KDBUS_ITEM_PAYLOAD_MEMFD: { - struct bus_body_part *part; +static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { + uint64_t off; + struct kdbus_item *d; - if (idx < BUS_MESSAGE_BODY_BEGIN(m)) { - r = -EBADMSG; - goto fail; - } + assert(bus); + assert(k); - part = message_append_part(m); - if (!part) { - r = -ENOMEM; - goto fail; - } + off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer; + ioctl(bus->input_fd, KDBUS_CMD_FREE, &off); - part->memfd = d->memfd.fd; - part->size = d->memfd.size; - part->sealed = true; + KDBUS_ITEM_FOREACH(d, k, items) { - idx += d->memfd.size; - break; - } + if (d->type == KDBUS_ITEM_FDS) + close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int)); + else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD) + close_nointr_nofail(d->memfd.fd); + } +} - case KDBUS_ITEM_CREDS: - /* UID/GID/PID are always valid */ - m->creds.uid = (uid_t) d->creds.uid; - m->creds.gid = (gid_t) d->creds.gid; - m->creds.pid = (pid_t) d->creds.pid; - m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID) & bus->creds_mask; +static int push_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + int r; - /* The PID starttime/TID might be missing - * however, when the data is faked by some - * data bus proxy and it lacks that - * information about the real client since - * SO_PEERCRED is used for that */ + assert(bus); - if (d->creds.starttime > 0) { - m->creds.pid_starttime = d->creds.starttime / NSEC_PER_USEC; - m->creds.mask |= SD_BUS_CREDS_PID_STARTTIME & bus->creds_mask; - } + r = sd_bus_message_new_signal( + bus, + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + &m); + if (r < 0) + return r; - if (d->creds.tid > 0) { - m->creds.tid = (pid_t) d->creds.tid; - m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; - } - break; + r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); + if (r < 0) + return r; - case KDBUS_ITEM_TIMESTAMP: - m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC; - m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC; - break; + m->sender = "org.freedesktop.DBus"; - case KDBUS_ITEM_PID_COMM: - m->creds.comm = d->str; - m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask; - break; + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; - case KDBUS_ITEM_TID_COMM: - m->creds.tid_comm = d->str; - m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask; - break; + bus->rqueue[bus->rqueue_size++] = m; + m = NULL; - case KDBUS_ITEM_EXE: - m->creds.exe = d->str; - m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask; - break; + return 1; +} - case KDBUS_ITEM_CMDLINE: - m->creds.cmdline = d->str; - m->creds.cmdline_size = l; - m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask; - break; +static int translate_name_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) { + char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX]; - case KDBUS_ITEM_CGROUP: - m->creds.cgroup = d->str; - m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask; + assert(bus); + assert(k); + assert(d); - if (!bus->cgroup_root) { - r = cg_get_root_path(&bus->cgroup_root); - if (r < 0) - goto fail; - } + if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) + old_owner[0] = 0; + else + sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old.id); - m->creds.cgroup_root = bus->cgroup_root; + if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) { - break; + if (isempty(old_owner)) + return 0; - case KDBUS_ITEM_AUDIT: - m->creds.audit_session_id = (uint32_t) d->audit.sessionid; - m->creds.audit_login_uid = (uid_t) d->audit.loginuid; - m->creds.mask |= (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID) & bus->creds_mask; - break; + new_owner[0] = 0; + } else + sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new.id); - case KDBUS_ITEM_CAPS: - m->creds.capability = d->data; - m->creds.capability_size = l; - m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask; - break; + return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner); +} - case KDBUS_ITEM_DST_NAME: - if (!service_name_is_valid(d->str)) - return -EBADMSG; +static int translate_id_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) { + char owner[UNIQUE_NAME_MAX]; - destination = d->str; - break; + assert(bus); + assert(k); + assert(d); - case KDBUS_ITEM_NAME: - if (!service_name_is_valid(d->name.name)) - return -EBADMSG; + sprintf(owner, ":1.%llu", d->id_change.id); - r = strv_extend(&m->creds.well_known_names, d->name.name); - if (r < 0) - goto fail; - break; + return push_name_owner_changed( + bus, owner, + d->type == KDBUS_ITEM_ID_ADD ? NULL : owner, + d->type == KDBUS_ITEM_ID_ADD ? owner : NULL); +} - case KDBUS_ITEM_FDS: - case KDBUS_ITEM_SECLABEL: - break; +static int translate_reply(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + int r; - default: - log_debug("Got unknown field from kernel %llu", d->type); - } - } + assert(bus); + assert(k); + assert(d); - r = bus_message_parse_fields(m); + r = bus_message_new_synthetic_error( + bus, + k->cookie_reply, + d->type == KDBUS_ITEM_REPLY_TIMEOUT ? + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") : + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"), + &m); if (r < 0) - goto fail; - - /* Override information from the user header with data from the kernel */ - if (k->src_id == KDBUS_SRC_ID_KERNEL) - m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; - else { - snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id); - m->sender = m->creds.unique_name = m->sender_buffer; - } + return r; - if (destination) - m->destination = destination; - else if (k->dst_id == KDBUS_DST_ID_BROADCAST) - m->destination = NULL; - else if (k->dst_id == KDBUS_DST_ID_NAME) - m->destination = bus->unique_name; /* fill in unique name if the well-known name is missing */ - else { - snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id); - m->destination = m->destination_buffer; - } + m->sender = "org.freedesktop.DBus"; - /* We take possession of the kmsg struct now */ - m->kdbus = k; - m->release_kdbus = true; - m->free_fds = true; - fds = NULL; + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; bus->rqueue[bus->rqueue_size++] = m; + m = NULL; return 1; +} -fail: - if (m) { - struct bus_body_part *part; - unsigned i; +static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) { + struct kdbus_item *d, *found = NULL; - /* Make sure the memfds are not freed twice */ - MESSAGE_FOREACH_PART(part, i, m) - if (part->memfd >= 0) - part->memfd = -1; + static int (* const translate[])(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) = { + [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, + [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, + [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, - sd_bus_message_unref(m); + [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, + [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, + + [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, + [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, + }; + + assert(bus); + assert(k); + assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); + + KDBUS_ITEM_FOREACH(d, k, items) { + if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) { + if (found) + return -EBADMSG; + found = d; + } else + log_debug("Got unknown field from kernel %llu", d->type); } - return r; + if (!found) { + log_debug("Didn't find a kernel message to translate."); + return 0; + } + + return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found); } int bus_kernel_read_message(sd_bus *bus) { -- 2.30.2