bus-kernel.c revision 76877fb9ccb7f9f921ea921cf692f4d2774d590c
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include <fcntl.h>
#include <malloc.h>
#include <libgen.h>
#include "util.h"
#include "strv.h"
#include "memfd-util.h"
#include "capability.h"
#include "cgroup-util.h"
#include "fileio.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-kernel.h"
#include "bus-bloom.h"
#include "bus-util.h"
#include "bus-label.h"
int r;
assert(s);
if (!startswith(s, ":1."))
return 0;
if (r < 0)
return r;
return 1;
}
assert(d);
*d = ALIGN8_PTR(*d);
/* Note that p can be NULL, which encodes a region full of
* zeroes, which is useful to optimize certain padding
* conditions */
(*d)->type = KDBUS_ITEM_PAYLOAD_VEC;
}
assert(d);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD;
}
assert(d);
assert(s);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_DST_NAME;
}
struct kdbus_item *i;
assert(d);
i = ALIGN8_PTR(*d);
i->type = KDBUS_ITEM_BLOOM_FILTER;
return &i->bloom_filter;
}
assert(d);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_FDS;
}
char *e;
assert(i < 64);
assert(t);
if (i < 10)
*(e++) = '0' + (char) i;
else {
*(e++) = '0' + (char) (i / 10);
*(e++) = '0' + (char) (i % 10);
}
*e = 0;
strcpy(e, "-dot-prefix");
strcpy(e, "-slash-prefix");
}
void *data;
unsigned i;
int r;
assert(m);
bloom->generation = 0;
bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "message-type", bus_message_type_to_string(m->header->type));
if (m->interface)
if (m->member)
if (m->path) {
bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path, '/');
}
r = sd_bus_message_rewind(m, true);
if (r < 0)
return r;
for (i = 0; i < 64; i++) {
const char *t, *contents;
char type;
if (r < 0)
return r;
/* The bloom filter includes simple strings of any kind */
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
return r;
/* As well as array of simple strings of any kinds */
if (r < 0)
return r;
while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0)
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
} else
/* Stop adding to bloom filter as soon as we
* run into the first argument we cannot add
* to it. */
break;
}
return 0;
}
struct bus_body_part *part;
struct kdbus_item *d;
const char *destination;
bool well_known;
unsigned i;
int r;
assert(b);
assert(m);
/* We put this together only once, if this message is reused
* we reuse the earlier-built version */
if (m->kdbus)
return 0;
if (destination) {
if (r < 0)
return r;
well_known = r == 0;
} else
well_known = false;
/* Add in fixed header, fields header and payload */
sizeof(struct kdbus_memfd)));
/* Add space for bloom filter */
m->bus->bloom_size);
/* Add in well-known destination header */
if (well_known) {
}
/* Add space for unix fds */
if (m->n_fds > 0)
if (!m->kdbus) {
r = -ENOMEM;
goto fail;
}
m->free_kdbus = true;
if (well_known)
/* verify_destination_id will usually be 0, which makes the kernel driver only look
* at the provided well-known name. Otherwise, the kernel will make sure the provided
* destination id matches the owner of the provided weel-known-name, and fail if they
* differ. Currently, this is only needed for bus-proxyd. */
else
else {
m->timeout * NSEC_PER_USEC;
}
if (well_known)
MESSAGE_FOREACH_PART(part, i, m) {
/* If this is padding then simply send a
* vector with a NULL data pointer which the
* kernel will just pass through. This is the
* most efficient way to encode zeroes */
continue;
}
/* Try to send a memfd, if the part is
* sealed and this is not a broadcast. Since we can only */
continue;
}
/* Otherwise, let's send a vector to the actual data.
* For that, we need to map it first. */
r = bus_body_part_map(part);
if (r < 0)
goto fail;
}
struct kdbus_bloom_filter *bloom;
r = bus_message_setup_bloom(m, bloom);
if (r < 0)
goto fail;
}
if (m->n_fds > 0)
return 0;
fail:
m->poisoned = true;
return r;
}
assert(m);
m->creds.well_known_names_driver = true;
}
static void unset_memfds(struct sd_bus_message *m) {
struct bus_body_part *part;
unsigned i;
assert(m);
/* Make sure the memfds are not freed twice */
MESSAGE_FOREACH_PART(part, i, m)
}
static void message_set_timestamp(sd_bus *bus, sd_bus_message *m, const struct kdbus_timestamp *ts) {
assert(m);
if (!ts)
return;
return;
}
sd_bus_message *m = NULL;
struct kdbus_item *d;
unsigned n_fds = 0;
struct bus_header *h = NULL;
int r;
assert(k);
KDBUS_ITEM_FOREACH(d, k, items) {
size_t l;
switch (d->type) {
case KDBUS_ITEM_PAYLOAD_OFF:
if (!h) {
return -EBADMSG;
}
break;
case KDBUS_ITEM_PAYLOAD_MEMFD:
if (!h)
return -EBADMSG;
break;
case KDBUS_ITEM_FDS: {
int *f;
unsigned j;
j = l / sizeof(int);
if (!f)
return -ENOMEM;
fds = f;
n_fds += j;
break;
}
case KDBUS_ITEM_SECLABEL:
break;
}
}
if (!h)
return -EBADMSG;
r = bus_header_message_size(h, &total);
if (r < 0)
return r;
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;
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 */
KDBUS_ITEM_FOREACH(d, k, items) {
size_t l;
switch (d->type) {
case KDBUS_ITEM_PAYLOAD_OFF: {
struct bus_body_part *part;
/* Contains body material */
part = message_append_part(m);
if (!part) {
r = -ENOMEM;
goto fail;
}
/* A -1 offset is NUL padding. */
if (idx >= begin_body) {
} else {
}
}
break;
}
case KDBUS_ITEM_PAYLOAD_MEMFD: {
struct bus_body_part *part;
if (idx < BUS_MESSAGE_BODY_BEGIN(m)) {
r = -EBADMSG;
goto fail;
}
part = message_append_part(m);
if (!part) {
r = -ENOMEM;
goto fail;
}
break;
}
case KDBUS_ITEM_PIDS:
* is faked by some data bus proxy and it
* lacks that information about the real
* client since SO_PEERCRED is used for
* that. */
}
}
break;
case KDBUS_ITEM_CREDS:
}
}
}
}
}
}
}
}
break;
case KDBUS_ITEM_TIMESTAMP:
break;
case KDBUS_ITEM_PID_COMM:
break;
case KDBUS_ITEM_TID_COMM:
break;
case KDBUS_ITEM_EXE:
break;
case KDBUS_ITEM_CMDLINE:
m->creds.cmdline_size = l;
break;
case KDBUS_ITEM_CGROUP:
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 = bus_get_root_path(bus);
if (r < 0)
goto fail;
break;
case KDBUS_ITEM_AUDIT:
}
}
break;
case KDBUS_ITEM_CAPS:
r = -EBADMSG;
goto fail;
}
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;
case KDBUS_ITEM_DST_NAME:
if (!service_name_is_valid(d->str)) {
r = -EBADMSG;
goto fail;
}
destination = d->str;
break;
case KDBUS_ITEM_OWNED_NAME:
r = -EBADMSG;
goto fail;
}
char **wkn;
size_t n;
/* We just extend the array here, but
* do not allocate the strings inside
* of it, instead we just point to our
* buffer directly. */
if (!wkn) {
r = -ENOMEM;
goto fail;
}
}
break;
break;
case KDBUS_ITEM_AUXGROUPS:
}
break;
case KDBUS_ITEM_FDS:
case KDBUS_ITEM_SECLABEL:
break;
default:
}
}
/* If we requested the list of well-known names to be appended
* and the sender had none no item for it will be
* attached. However, this does *not* mean that the kernel
* didn't want to provide this information to us. Hence, let's
* explicitly mark this information as available if it was
* requested. */
r = bus_message_parse_fields(m);
if (r < 0)
goto fail;
/* Refuse messages if kdbus and dbus1 cookie doesn't match up */
r = -EBADMSG;
goto fail;
}
/* Refuse messages where the reply flag doesn't match up */
r = -EBADMSG;
goto fail;
}
/* Refuse reply messages where the reply cookie doesn't match up */
r = -EBADMSG;
goto fail;
}
/* Refuse messages where the autostart flag doesn't match up */
r = -EBADMSG;
goto fail;
}
/* Override information from the user header with data from the kernel */
if (k->src_id == KDBUS_SRC_ID_KERNEL)
else {
}
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)
else {
snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
m->destination = m->destination_buffer;
}
/* We take possession of the kmsg struct now */
m->kdbus = k;
m->release_kdbus = true;
m->free_fds = true;
return 1;
fail:
unset_memfds(m);
return r;
}
int bus_kernel_take_fd(sd_bus *b) {
struct kdbus_cmd_free cmd_free = {
.flags = 0,
};
struct kdbus_cmd_hello *hello;
struct kdbus_item_list *items;
struct kdbus_item *item;
_cleanup_free_ char *g = NULL;
const char *name;
int r;
assert(b);
if (b->is_server)
return -EINVAL;
b->use_memfd = 1;
if (b->description) {
g = bus_label_escape(b->description);
if (!g)
return -ENOMEM;
name = g;
} else {
char pr[17] = {};
/* If no name is explicitly set, we'll include a hint
* indicating the library implementation, a hint which
* kind of bus this is and the thread name */
} else {
_cleanup_free_ char *e = NULL;
e = bus_label_escape(pr);
if (!e)
return -ENOMEM;
e);
if (!g)
return -ENOMEM;
name = g;
}
if (!b->description)
return -ENOMEM;
}
if (b->fake_creds_valid)
if (b->fake_pids_valid)
if (b->fake_label) {
l = strlen(b->fake_label);
}
if (b->fake_creds_valid) {
}
if (b->fake_pids_valid) {
}
if (b->fake_label) {
}
if (r < 0)
return -errno;
if (!b->kdbus_buffer) {
if (b->kdbus_buffer == MAP_FAILED) {
b->kdbus_buffer = NULL;
r = -errno;
goto fail;
}
}
/* The higher 32bit of the bus_flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
r = -ENOTSUP;
goto fail;
}
/* extract bloom parameters from items */
break;
}
}
r = -ENOTSUP;
goto fail;
}
r = -ENOMEM;
goto fail;
}
b->is_kernel = true;
b->bus_client = true;
b->message_version = 2;
/* the kernel told us the UUID of the underlying bus */
/* free returned items */
return bus_start_running(b);
fail:
return r;
}
int bus_kernel_connect(sd_bus *b) {
assert(b);
if (b->is_server)
return -EINVAL;
if (b->input_fd < 0)
return -errno;
return bus_kernel_take_fd(b);
}
struct kdbus_cmd_free cmd = {
.flags = 0,
};
int r;
if (r < 0)
return -errno;
return 0;
}
struct kdbus_item *d;
assert(k);
KDBUS_ITEM_FOREACH(d, k, items) {
if (d->type == KDBUS_ITEM_FDS)
else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD)
}
}
struct kdbus_cmd_send cmd = { };
int r;
assert(m);
/* If we can't deliver, we want room for the error message */
r = bus_rqueue_make_room(bus);
if (r < 0)
return r;
r = bus_message_setup_kmsg(bus, m);
if (r < 0)
return r;
/* If this is a synchronous method call, then let's tell the
* kernel, so that it can pass CPU time/scheduling to the
* destination for the time, if it wants to. If we
* synchronously wait for the result anyway, we won't need CPU
* anyway. */
if (hint_sync_call) {
}
if (r < 0) {
return 0;
/* ENXIO: unique name not known
* ESRCH: well-known name not known */
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;
}
} else if (errno == EADDRNOTAVAIL) {
/* EADDRNOTAVAIL: activation is possible, but turned off in request flags */
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;
bus,
&error,
&reply);
if (r < 0)
return r;
if (r < 0)
return r;
} else if (hint_sync_call) {
struct kdbus_msg *k;
assert(k);
if (k->payload_type == KDBUS_PAYLOAD_DBUS) {
r = bus_kernel_make_message(bus, k);
if (r < 0) {
close_kdbus_msg(bus, k);
/* Anybody can send us invalid messages, let's just drop them. */
if (r == -EBADMSG || r == -EPROTOTYPE)
log_debug_errno(r, "Ignoring invalid message: %m");
else
return r;
}
} else {
log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type);
close_kdbus_msg(bus, k);
}
}
return 1;
}
static int push_name_owner_changed(
const char *name,
const char *old_owner,
const char *new_owner,
const struct kdbus_timestamp *ts) {
int r;
bus,
&m,
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameOwnerChanged");
if (r < 0)
return r;
if (r < 0)
return r;
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
m = NULL;
return 1;
}
static int translate_name_change(
const struct kdbus_msg *k,
const struct kdbus_item *d,
const struct kdbus_timestamp *ts) {
assert(k);
assert(d);
if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR)))
old_owner[0] = 0;
else
if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) {
return 0;
new_owner[0] = 0;
} else
}
static int translate_id_change(
const struct kdbus_msg *k,
const struct kdbus_item *d,
const struct kdbus_timestamp *ts) {
char owner[UNIQUE_NAME_MAX];
assert(k);
assert(d);
return push_name_owner_changed(
ts);
}
static int translate_reply(
const struct kdbus_msg *k,
const struct kdbus_item *d,
const struct kdbus_timestamp *ts) {
int r;
assert(k);
assert(d);
bus,
k->cookie_reply,
d->type == KDBUS_ITEM_REPLY_TIMEOUT ?
&m);
if (r < 0)
return r;
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
m = NULL;
return 1;
}
static int (* const translate[])(sd_bus *bus, const struct kdbus_msg *k, const struct kdbus_item *d, const struct kdbus_timestamp *ts) = {
};
assert(k);
KDBUS_ITEM_FOREACH(d, k, items) {
if (d->type == KDBUS_ITEM_TIMESTAMP)
if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) {
if (found)
return -EBADMSG;
found = d;
} else
}
if (!found) {
log_debug("Didn't find a kernel message to translate.");
return 0;
}
}
struct kdbus_msg *k;
int r;
r = bus_rqueue_make_room(bus);
if (r < 0)
return r;
if (hint_priority) {
}
if (r < 0) {
return 0;
log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs);
return 0;
}
return -errno;
}
if (k->payload_type == KDBUS_PAYLOAD_DBUS) {
r = bus_kernel_make_message(bus, k);
/* Anybody can send us invalid messages, let's just drop them. */
if (r == -EBADMSG || r == -EPROTOTYPE) {
log_debug_errno(r, "Ignoring invalid message: %m");
r = 0;
}
} else if (k->payload_type == KDBUS_PAYLOAD_KERNEL)
r = bus_kernel_translate_message(bus, k);
else {
log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type);
r = 0;
}
if (r <= 0)
close_kdbus_msg(bus, k);
return r < 0 ? r : 1;
}
struct memfd_cache *c;
int fd;
return -ENOTSUP;
if (bus->n_memfd_cache <= 0) {
int r;
if (r < 0)
return r;
*mapped = 0;
*allocated = 0;
return r;
}
return fd;
}
if (size > 0)
safe_close(fd);
}
struct memfd_cache *c;
return;
}
return;
}
/* If overly long, let's return a bit to the OS */
if (mapped > max_mapped) {
} else {
}
}
void bus_kernel_flush_memfd(sd_bus *b) {
unsigned i;
assert(b);
for (i = 0; i < b->n_memfd_cache; i++)
}
uint64_t f = 0;
if (flags & SD_BUS_NAME_QUEUE)
f |= KDBUS_NAME_QUEUE;
return f;
}
uint64_t m = 0;
m |= KDBUS_ATTACH_CREDS;
m |= KDBUS_ATTACH_PIDS;
if (mask & SD_BUS_CREDS_COMM)
m |= KDBUS_ATTACH_PID_COMM;
if (mask & SD_BUS_CREDS_TID_COMM)
m |= KDBUS_ATTACH_TID_COMM;
if (mask & SD_BUS_CREDS_EXE)
m |= KDBUS_ATTACH_EXE;
if (mask & SD_BUS_CREDS_CMDLINE)
m |= KDBUS_ATTACH_CMDLINE;
if (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))
m |= KDBUS_ATTACH_CGROUP;
if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))
m |= KDBUS_ATTACH_CAPS;
if (mask & SD_BUS_CREDS_SELINUX_CONTEXT)
m |= KDBUS_ATTACH_SECLABEL;
m |= KDBUS_ATTACH_AUDIT;
m |= KDBUS_ATTACH_NAMES;
if (mask & SD_BUS_CREDS_DESCRIPTION)
m |= KDBUS_ATTACH_AUXGROUPS;
return m;
}
struct kdbus_cmd_make *make;
struct kdbus_item *n;
size_t l;
int fd;
assert(s);
if (fd < 0)
return -errno;
8);
/* Set the bloom parameters */
sizeof(struct kdbus_bloom_parameter);
assert_cc(DEFAULT_BLOOM_SIZE > 0);
/* The busses we create make no restrictions on what metadata
* peers can read from incoming messages. */
n = KDBUS_ITEM_NEXT(n);
n->data64[0] = _KDBUS_ATTACH_ANY;
/* Provide all metadata via bus-owner queries */
n = KDBUS_ITEM_NEXT(n);
n->data64[0] = _KDBUS_ATTACH_ANY;
/* Set the a good name */
n = KDBUS_ITEM_NEXT(n);
n->type = KDBUS_ITEM_MAKE_NAME;
safe_close(fd);
return -errno;
}
if (s) {
char *p;
if (!p) {
safe_close(fd);
return -ENOMEM;
}
*s = p;
}
return fd;
}
char *p;
int fd;
if (path) {
if (!p)
return -ENOMEM;
} else
if (fd < 0) {
if (path)
free(p);
return -errno;
}
if (path)
*path = p;
return fd;
}
struct kdbus_cmd_make *make;
struct kdbus_item *n;
const char *name;
int fd;
if (fd < 0)
return fd;
8);
n->type = KDBUS_ITEM_MAKE_NAME;
safe_close(fd);
return -errno;
}
if (ep_path) {
char *p;
if (!p) {
safe_close(fd);
return -ENOMEM;
}
*ep_path = p;
}
return fd;
}
return -errno;
return 0;
}
int bus_kernel_drop_one(int fd) {
struct kdbus_cmd_recv recv = {
.flags = KDBUS_RECV_DROP,
};
return -errno;
return 0;
}
struct kdbus_cmd_update *update;
struct kdbus_item *n;
8);
return -errno;
return 0;
}
int bus_kernel_fix_attach_mask(void) {
int r;
/* By default we don't want any kdbus metadata fields to be
* suppressed, hence we reset the kernel mask for it to
* (uint64_t) -1. This is overridable via a kernel command
* line option, however. */
if (r < 0)
return log_warning_errno(r, "Failed to read kernel command line: %m");
if (mask) {
const char *p = mask;
if (startswith(p, "0x"))
p += 2;
log_warning("Couldn't parse systemd.kdbus_attach_flags_mask= kernel command line parameter.");
}
if (r < 0)
return log_full_errno(
"Failed to write kdbus attach mask: %m");
return 0;
}
struct kdbus_cmd_info cmd = {
.size = sizeof(struct kdbus_cmd_info),
};
struct kdbus_info *info;
struct kdbus_item *item;
char *n = NULL;
int r;
if (r < 0)
return -errno;
break;
}
if (r < 0)
return r;
if (!n)
return -EIO;
*name = n;
return 0;
}