summaryrefslogtreecommitdiff
path: root/ipc/kdbus/bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/kdbus/bus.c')
-rw-r--r--ipc/kdbus/bus.c514
1 files changed, 0 insertions, 514 deletions
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
deleted file mode 100644
index e636d3478..000000000
--- a/ipc/kdbus/bus.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * Copyright (C) 2013-2015 Kay Sievers
- * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
- * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org>
- * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com>
- * Copyright (C) 2013-2015 Linux Foundation
- * Copyright (C) 2014-2015 Djalal Harouni
- *
- * kdbus is free software; you can redistribute it and/or modify it 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.
- */
-
-#include <linux/fs.h>
-#include <linux/hashtable.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/random.h>
-#include <linux/sched.h>
-#include <linux/sizes.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/uio.h>
-
-#include "bus.h"
-#include "notify.h"
-#include "connection.h"
-#include "domain.h"
-#include "endpoint.h"
-#include "handle.h"
-#include "item.h"
-#include "match.h"
-#include "message.h"
-#include "metadata.h"
-#include "names.h"
-#include "policy.h"
-#include "util.h"
-
-static void kdbus_bus_free(struct kdbus_node *node)
-{
- struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node);
-
- WARN_ON(!list_empty(&bus->monitors_list));
- WARN_ON(!hash_empty(bus->conn_hash));
-
- kdbus_notify_free(bus);
-
- kdbus_user_unref(bus->creator);
- kdbus_name_registry_free(bus->name_registry);
- kdbus_domain_unref(bus->domain);
- kdbus_policy_db_clear(&bus->policy_db);
- kdbus_meta_proc_unref(bus->creator_meta);
- kfree(bus);
-}
-
-static void kdbus_bus_release(struct kdbus_node *node, bool was_active)
-{
- struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node);
-
- if (was_active)
- atomic_dec(&bus->creator->buses);
-}
-
-static struct kdbus_bus *kdbus_bus_new(struct kdbus_domain *domain,
- const char *name,
- struct kdbus_bloom_parameter *bloom,
- const u64 *pattach_owner,
- u64 flags, kuid_t uid, kgid_t gid)
-{
- struct kdbus_bus *b;
- u64 attach_owner;
- int ret;
-
- if (bloom->size < 8 || bloom->size > KDBUS_BUS_BLOOM_MAX_SIZE ||
- !KDBUS_IS_ALIGNED8(bloom->size) || bloom->n_hash < 1)
- return ERR_PTR(-EINVAL);
-
- ret = kdbus_sanitize_attach_flags(pattach_owner ? *pattach_owner : 0,
- &attach_owner);
- if (ret < 0)
- return ERR_PTR(ret);
-
- ret = kdbus_verify_uid_prefix(name, domain->user_namespace, uid);
- if (ret < 0)
- return ERR_PTR(ret);
-
- b = kzalloc(sizeof(*b), GFP_KERNEL);
- if (!b)
- return ERR_PTR(-ENOMEM);
-
- kdbus_node_init(&b->node, KDBUS_NODE_BUS);
-
- b->node.free_cb = kdbus_bus_free;
- b->node.release_cb = kdbus_bus_release;
- b->node.uid = uid;
- b->node.gid = gid;
- b->node.mode = S_IRUSR | S_IXUSR;
-
- if (flags & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD))
- b->node.mode |= S_IRGRP | S_IXGRP;
- if (flags & KDBUS_MAKE_ACCESS_WORLD)
- b->node.mode |= S_IROTH | S_IXOTH;
-
- b->id = atomic64_inc_return(&domain->last_id);
- b->bus_flags = flags;
- b->attach_flags_owner = attach_owner;
- generate_random_uuid(b->id128);
- b->bloom = *bloom;
- b->domain = kdbus_domain_ref(domain);
-
- kdbus_policy_db_init(&b->policy_db);
-
- init_rwsem(&b->conn_rwlock);
- hash_init(b->conn_hash);
- INIT_LIST_HEAD(&b->monitors_list);
-
- INIT_LIST_HEAD(&b->notify_list);
- spin_lock_init(&b->notify_lock);
- mutex_init(&b->notify_flush_lock);
-
- ret = kdbus_node_link(&b->node, &domain->node, name);
- if (ret < 0)
- goto exit_unref;
-
- /* cache the metadata/credentials of the creator */
- b->creator_meta = kdbus_meta_proc_new();
- if (IS_ERR(b->creator_meta)) {
- ret = PTR_ERR(b->creator_meta);
- b->creator_meta = NULL;
- goto exit_unref;
- }
-
- ret = kdbus_meta_proc_collect(b->creator_meta,
- KDBUS_ATTACH_CREDS |
- KDBUS_ATTACH_PIDS |
- KDBUS_ATTACH_AUXGROUPS |
- KDBUS_ATTACH_TID_COMM |
- KDBUS_ATTACH_PID_COMM |
- KDBUS_ATTACH_EXE |
- KDBUS_ATTACH_CMDLINE |
- KDBUS_ATTACH_CGROUP |
- KDBUS_ATTACH_CAPS |
- KDBUS_ATTACH_SECLABEL |
- KDBUS_ATTACH_AUDIT);
- if (ret < 0)
- goto exit_unref;
-
- b->name_registry = kdbus_name_registry_new();
- if (IS_ERR(b->name_registry)) {
- ret = PTR_ERR(b->name_registry);
- b->name_registry = NULL;
- goto exit_unref;
- }
-
- /*
- * Bus-limits of the creator are accounted on its real UID, just like
- * all other per-user limits.
- */
- b->creator = kdbus_user_lookup(domain, current_uid());
- if (IS_ERR(b->creator)) {
- ret = PTR_ERR(b->creator);
- b->creator = NULL;
- goto exit_unref;
- }
-
- return b;
-
-exit_unref:
- kdbus_node_drain(&b->node);
- kdbus_node_unref(&b->node);
- return ERR_PTR(ret);
-}
-
-/**
- * kdbus_bus_ref() - increase the reference counter of a kdbus_bus
- * @bus: The bus to reference
- *
- * Every user of a bus, except for its creator, must add a reference to the
- * kdbus_bus using this function.
- *
- * Return: the bus itself
- */
-struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus)
-{
- if (bus)
- kdbus_node_ref(&bus->node);
- return bus;
-}
-
-/**
- * kdbus_bus_unref() - decrease the reference counter of a kdbus_bus
- * @bus: The bus to unref
- *
- * Release a reference. If the reference count drops to 0, the bus will be
- * freed.
- *
- * Return: NULL
- */
-struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus)
-{
- if (bus)
- kdbus_node_unref(&bus->node);
- return NULL;
-}
-
-/**
- * kdbus_bus_find_conn_by_id() - find a connection with a given id
- * @bus: The bus to look for the connection
- * @id: The 64-bit connection id
- *
- * Looks up a connection with a given id. The returned connection
- * is ref'ed, and needs to be unref'ed by the user. Returns NULL if
- * the connection can't be found.
- */
-struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id)
-{
- struct kdbus_conn *conn, *found = NULL;
-
- down_read(&bus->conn_rwlock);
- hash_for_each_possible(bus->conn_hash, conn, hentry, id)
- if (conn->id == id) {
- found = kdbus_conn_ref(conn);
- break;
- }
- up_read(&bus->conn_rwlock);
-
- return found;
-}
-
-/**
- * kdbus_bus_broadcast() - send a message to all subscribed connections
- * @bus: The bus the connections are connected to
- * @conn_src: The source connection, may be %NULL for kernel notifications
- * @staging: Staging object containing the message to send
- *
- * Send message to all connections that are currently active on the bus.
- * Connections must still have matches installed in order to let the message
- * pass.
- *
- * The caller must hold the name-registry lock of @bus.
- */
-void kdbus_bus_broadcast(struct kdbus_bus *bus,
- struct kdbus_conn *conn_src,
- struct kdbus_staging *staging)
-{
- struct kdbus_conn *conn_dst;
- unsigned int i;
- int ret;
-
- lockdep_assert_held(&bus->name_registry->rwlock);
-
- /*
- * Make sure broadcast are queued on monitors before we send it out to
- * anyone else. Otherwise, connections might react to broadcasts before
- * the monitor gets the broadcast queued. In the worst case, the
- * monitor sees a reaction to the broadcast before the broadcast itself.
- * We don't give ordering guarantees across connections (and monitors
- * can re-construct order via sequence numbers), but we should at least
- * try to avoid re-ordering for monitors.
- */
- kdbus_bus_eavesdrop(bus, conn_src, staging);
-
- down_read(&bus->conn_rwlock);
- hash_for_each(bus->conn_hash, i, conn_dst, hentry) {
- if (!kdbus_conn_is_ordinary(conn_dst))
- continue;
-
- /*
- * Check if there is a match for the kmsg object in
- * the destination connection match db
- */
- if (!kdbus_match_db_match_msg(conn_dst->match_db, conn_src,
- staging))
- continue;
-
- if (conn_src) {
- /*
- * Anyone can send broadcasts, as they have no
- * destination. But a receiver needs TALK access to
- * the sender in order to receive broadcasts.
- */
- if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src))
- continue;
- } else {
- /*
- * Check if there is a policy db that prevents the
- * destination connection from receiving this kernel
- * notification
- */
- if (!kdbus_conn_policy_see_notification(conn_dst, NULL,
- staging->msg))
- continue;
- }
-
- ret = kdbus_conn_entry_insert(conn_src, conn_dst, staging,
- NULL, NULL);
- if (ret < 0)
- kdbus_conn_lost_message(conn_dst);
- }
- up_read(&bus->conn_rwlock);
-}
-
-/**
- * kdbus_bus_eavesdrop() - send a message to all subscribed monitors
- * @bus: The bus the monitors are connected to
- * @conn_src: The source connection, may be %NULL for kernel notifications
- * @staging: Staging object containing the message to send
- *
- * Send message to all monitors that are currently active on the bus. Monitors
- * must still have matches installed in order to let the message pass.
- *
- * The caller must hold the name-registry lock of @bus.
- */
-void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
- struct kdbus_conn *conn_src,
- struct kdbus_staging *staging)
-{
- struct kdbus_conn *conn_dst;
- int ret;
-
- /*
- * Monitor connections get all messages; ignore possible errors
- * when sending messages to monitor connections.
- */
-
- lockdep_assert_held(&bus->name_registry->rwlock);
-
- down_read(&bus->conn_rwlock);
- list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) {
- ret = kdbus_conn_entry_insert(conn_src, conn_dst, staging,
- NULL, NULL);
- if (ret < 0)
- kdbus_conn_lost_message(conn_dst);
- }
- up_read(&bus->conn_rwlock);
-}
-
-/**
- * kdbus_cmd_bus_make() - handle KDBUS_CMD_BUS_MAKE
- * @domain: domain to operate on
- * @argp: command payload
- *
- * Return: NULL or newly created bus on success, ERR_PTR on failure.
- */
-struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
- void __user *argp)
-{
- struct kdbus_bus *bus = NULL;
- struct kdbus_cmd *cmd;
- struct kdbus_ep *ep = NULL;
- int ret;
-
- struct kdbus_arg argv[] = {
- { .type = KDBUS_ITEM_NEGOTIATE },
- { .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true },
- { .type = KDBUS_ITEM_BLOOM_PARAMETER, .mandatory = true },
- { .type = KDBUS_ITEM_ATTACH_FLAGS_SEND },
- };
- struct kdbus_args args = {
- .allowed_flags = KDBUS_FLAG_NEGOTIATE |
- KDBUS_MAKE_ACCESS_GROUP |
- KDBUS_MAKE_ACCESS_WORLD,
- .argv = argv,
- .argc = ARRAY_SIZE(argv),
- };
-
- ret = kdbus_args_parse(&args, argp, &cmd);
- if (ret < 0)
- return ERR_PTR(ret);
- if (ret > 0)
- return NULL;
-
- bus = kdbus_bus_new(domain,
- argv[1].item->str, &argv[2].item->bloom_parameter,
- argv[3].item ? argv[3].item->data64 : NULL,
- cmd->flags, current_euid(), current_egid());
- if (IS_ERR(bus)) {
- ret = PTR_ERR(bus);
- bus = NULL;
- goto exit;
- }
-
- if (atomic_inc_return(&bus->creator->buses) > KDBUS_USER_MAX_BUSES) {
- atomic_dec(&bus->creator->buses);
- ret = -EMFILE;
- goto exit;
- }
-
- if (!kdbus_node_activate(&bus->node)) {
- atomic_dec(&bus->creator->buses);
- ret = -ESHUTDOWN;
- goto exit;
- }
-
- ep = kdbus_ep_new(bus, "bus", cmd->flags, bus->node.uid, bus->node.gid,
- false);
- if (IS_ERR(ep)) {
- ret = PTR_ERR(ep);
- ep = NULL;
- goto exit;
- }
-
- if (!kdbus_node_activate(&ep->node)) {
- ret = -ESHUTDOWN;
- goto exit;
- }
-
- /*
- * Drop our own reference, effectively causing the endpoint to be
- * deactivated and released when the parent bus is.
- */
- ep = kdbus_ep_unref(ep);
-
-exit:
- ret = kdbus_args_clear(&args, ret);
- if (ret < 0) {
- if (ep) {
- kdbus_node_drain(&ep->node);
- kdbus_ep_unref(ep);
- }
- if (bus) {
- kdbus_node_drain(&bus->node);
- kdbus_bus_unref(bus);
- }
- return ERR_PTR(ret);
- }
- return bus;
-}
-
-/**
- * kdbus_cmd_bus_creator_info() - handle KDBUS_CMD_BUS_CREATOR_INFO
- * @conn: connection to operate on
- * @argp: command payload
- *
- * Return: >=0 on success, negative error code on failure.
- */
-int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
-{
- struct kdbus_cmd_info *cmd;
- struct kdbus_bus *bus = conn->ep->bus;
- struct kdbus_pool_slice *slice = NULL;
- struct kdbus_item *meta_items = NULL;
- struct kdbus_item_header item_hdr;
- struct kdbus_info info = {};
- size_t meta_size, name_len, cnt = 0;
- struct kvec kvec[6];
- u64 attach_flags, size = 0;
- int ret;
-
- struct kdbus_arg argv[] = {
- { .type = KDBUS_ITEM_NEGOTIATE },
- };
- struct kdbus_args args = {
- .allowed_flags = KDBUS_FLAG_NEGOTIATE,
- .argv = argv,
- .argc = ARRAY_SIZE(argv),
- };
-
- ret = kdbus_args_parse(&args, argp, &cmd);
- if (ret != 0)
- return ret;
-
- ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags);
- if (ret < 0)
- goto exit;
-
- attach_flags &= bus->attach_flags_owner;
-
- ret = kdbus_meta_emit(bus->creator_meta, NULL, NULL, conn,
- attach_flags, &meta_items, &meta_size);
- if (ret < 0)
- goto exit;
-
- name_len = strlen(bus->node.name) + 1;
- info.id = bus->id;
- info.flags = bus->bus_flags;
- item_hdr.type = KDBUS_ITEM_MAKE_NAME;
- item_hdr.size = KDBUS_ITEM_HEADER_SIZE + name_len;
-
- kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &size);
- kdbus_kvec_set(&kvec[cnt++], &item_hdr, sizeof(item_hdr), &size);
- kdbus_kvec_set(&kvec[cnt++], bus->node.name, name_len, &size);
- cnt += !!kdbus_kvec_pad(&kvec[cnt], &size);
- if (meta_size > 0) {
- kdbus_kvec_set(&kvec[cnt++], meta_items, meta_size, &size);
- cnt += !!kdbus_kvec_pad(&kvec[cnt], &size);
- }
-
- info.size = size;
-
- slice = kdbus_pool_slice_alloc(conn->pool, size, false);
- if (IS_ERR(slice)) {
- ret = PTR_ERR(slice);
- slice = NULL;
- goto exit;
- }
-
- ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, size);
- if (ret < 0)
- goto exit;
-
- kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size);
-
- if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
- kdbus_member_set_user(&cmd->info_size, argp,
- typeof(*cmd), info_size))
- ret = -EFAULT;
-
-exit:
- kdbus_pool_slice_release(slice);
- kfree(meta_items);
- return kdbus_args_clear(&args, ret);
-}