bus-kernel.c revision c32195e057e5bf903c2645e273ba82f32c0106d3
/*-*- 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 "bus-internal.h"
#include "bus-message.h"
#include "bus-kernel.h"
#include "bus-bloom.h"
#include "bus-util.h"
#include "bus-label.h"
#include "cgroup-util.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;
}
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++) {
char type;
const char *t;
char *e;
if (r < 0)
return r;
if (type != SD_BUS_TYPE_STRING &&
type != SD_BUS_TYPE_OBJECT_PATH &&
break;
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
return r;
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");
}
return 0;
}
struct bus_body_part *part;
struct kdbus_item *d;
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 (m->destination) {
if (r < 0)
return r;
well_known = r == 0;
} else
well_known = false;
/* Add in fixed header, fields header and payload */
/* 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;
well_known ? 0 :
else
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;
}
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)
}
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_CREDS:
* 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 */
}
}
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:
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;
case KDBUS_ITEM_DST_NAME:
if (!service_name_is_valid(d->str))
return -EBADMSG;
destination = d->str;
break;
case KDBUS_ITEM_NAME:
return -EBADMSG;
if (r < 0)
goto fail;
break;
case KDBUS_ITEM_CONN_NAME:
break;
case KDBUS_ITEM_FDS:
case KDBUS_ITEM_SECLABEL:
break;
default:
}
}
r = bus_message_parse_fields(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)
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_hello *hello;
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->connection_name) {
g = bus_label_escape(b->connection_name);
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->connection_name)
return -ENOMEM;
}
if (b->fake_creds_valid)
if (b->fake_label) {
l = strlen(b->fake_label);
}
if (b->fake_creds_valid) {
}
if (b->fake_label) {
}
if (r < 0)
return -errno;
if (!b->kdbus_buffer) {
if (b->kdbus_buffer == MAP_FAILED) {
b->kdbus_buffer = NULL;
return -errno;
}
}
/* The higher 32bit of both flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
return -ENOTSUP;
return -ENOTSUP;
return -ENOMEM;
b->is_kernel = true;
b->bus_client = true;
b->message_version = 2;
/* the kernel told us the UUID of the underlying bus */
return bus_start_running(b);
}
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;
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)
}
}
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)
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(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) {
int r;
bus,
&m,
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameOwnerChanged");
if (r < 0)
return r;
if (r < 0)
return r;
m->sender = "org.freedesktop.DBus";
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
m = NULL;
return 1;
}
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
}
char owner[UNIQUE_NAME_MAX];
assert(k);
assert(d);
return push_name_owner_changed(
}
int r;
assert(k);
assert(d);
bus,
k->cookie_reply,
d->type == KDBUS_ITEM_REPLY_TIMEOUT ?
&m);
if (r < 0)
return r;
m->sender = "org.freedesktop.DBus";
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
m = NULL;
return 1;
}
};
assert(k);
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
}
if (!found) {
log_debug("Didn't find a kernel message to translate.");
return 0;
}
}
struct kdbus_cmd_recv recv = {};
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;
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) {
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) {
_cleanup_free_ char *g = NULL;
int r;
if (!g)
return -ENOMEM;
r = memfd_create(g, MFD_ALLOW_SEALING);
if (r < 0)
return -errno;
*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;
*kdbus_flags = f;
return 0;
}
uint64_t m = 0;
if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID))
m |= KDBUS_ATTACH_CREDS;
m |= KDBUS_ATTACH_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_CONNECTION_NAME)
m |= KDBUS_ATTACH_CONN_NAME;
*kdbus_mask = m;
return 0;
}
struct kdbus_cmd_make *make;
struct kdbus_item *n;
int fd;
assert(s);
if (fd < 0)
return -errno;
8);
sizeof(struct kdbus_bloom_parameter);
assert_cc(DEFAULT_BLOOM_SIZE > 0);
n = KDBUS_ITEM_NEXT(n);
n->type = KDBUS_ITEM_MAKE_NAME;
safe_close(fd);
return -errno;
}
/* The features field are considered 'incompatible flags'.
* Refuse them all for now. */
safe_close(fd);
return -ENOTSUP;
}
if (s) {
char *p;
if (!p) {
safe_close(fd);
return -ENOMEM;
}
*s = p;
}
return fd;
}
switch (access) {
case BUS_POLICY_ACCESS_SEE:
return KDBUS_POLICY_SEE;
case BUS_POLICY_ACCESS_TALK:
return KDBUS_POLICY_TALK;
case BUS_POLICY_ACCESS_OWN:
return KDBUS_POLICY_OWN;
default:
assert_not_reached("Unknown policy access");
}
}
int r;
case BUSNAME_POLICY_TYPE_USER: {
if (r < 0)
return r;
break;
}
case BUSNAME_POLICY_TYPE_GROUP: {
if (r < 0)
return r;
break;
}
default:
assert_not_reached("Unknown policy type");
}
return 0;
}
char *p;
int fd;
if (path) {
if (!p)
return -ENOMEM;
*path = p;
} else
if (fd < 0)
return -errno;
return fd;
}
struct kdbus_cmd_make *make;
struct kdbus_item *n;
int fd;
if (fd < 0)
return fd;
n->type = KDBUS_ITEM_MAKE_NAME;
safe_close(fd);
return -errno;
}
/* The features field are considered 'incompatible flags'.
* Refuse them all for now. */
safe_close(fd);
return -ENOTSUP;
}
if (ep_path) {
char *p;
if (!p) {
safe_close(fd);
return -ENOMEM;
}
*ep_path = p;
}
return fd;
}
struct kdbus_cmd_update *update;
struct kdbus_item *n;
Iterator i;
int r;
}
n->type = KDBUS_ITEM_NAME;
n = KDBUS_ITEM_NEXT(n);
n->type = KDBUS_ITEM_POLICY_ACCESS;
n = KDBUS_ITEM_NEXT(n);
}
if (r < 0)
return -errno;
return 0;
}
int fd,
const char *name,
bool activating,
bool accept_fd,
struct kdbus_cmd_hello *hello;
struct kdbus_item *n;
size_t policy_cnt = 0;
int r;
policy_cnt++;
if (world_policy >= 0)
policy_cnt++;
policy_cnt * ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access));
n->type = KDBUS_ITEM_NAME;
n = KDBUS_ITEM_NEXT(n);
n->type = KDBUS_ITEM_POLICY_ACCESS;
r = bus_kernel_translate_policy(po, n);
if (r < 0)
return r;
n = KDBUS_ITEM_NEXT(n);
}
if (world_policy >= 0) {
n->type = KDBUS_ITEM_POLICY_ACCESS;
}
hello->conn_flags =
(accept_fd ? KDBUS_HELLO_ACCEPT_FD : 0);
return -errno;
/* The higher 32bit of both flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
return -ENOTSUP;
return -ENOTSUP;
return fd;
}
int bus_kernel_create_domain(const char *name, char **s) {
struct kdbus_cmd_make *make;
struct kdbus_item *n;
int fd;
assert(s);
if (fd < 0)
return -errno;
8);
n->type = KDBUS_ITEM_MAKE_NAME;
safe_close(fd);
return -errno;
}
/* The higher 32bit of the flags field are considered
* 'incompatible flags'. Refuse them all for now. */
safe_close(fd);
return -ENOTSUP;
}
if (s) {
char *p;
if (!p) {
safe_close(fd);
return -ENOMEM;
}
*s = p;
}
return fd;
}
return -errno;
return 0;
}
int bus_kernel_drop_one(int fd) {
struct kdbus_cmd_recv recv = {
};
return -errno;
return 0;
}