diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
commit | 57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch) | |
tree | 5e910f0e82173f4ef4f51111366a3f1299037a7b /ipc/kdbus/endpoint.c |
Initial import
Diffstat (limited to 'ipc/kdbus/endpoint.c')
-rw-r--r-- | ipc/kdbus/endpoint.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c new file mode 100644 index 000000000..977964dbb --- /dev/null +++ b/ipc/kdbus/endpoint.c @@ -0,0 +1,275 @@ +/* + * 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 <tixxdz@opendz.org> + * + * 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/idr.h> +#include <linux/init.h> +#include <linux/module.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 "connection.h" +#include "domain.h" +#include "endpoint.h" +#include "handle.h" +#include "item.h" +#include "message.h" +#include "policy.h" + +static void kdbus_ep_free(struct kdbus_node *node) +{ + struct kdbus_ep *ep = container_of(node, struct kdbus_ep, node); + + WARN_ON(!list_empty(&ep->conn_list)); + + kdbus_policy_db_clear(&ep->policy_db); + kdbus_bus_unref(ep->bus); + kdbus_user_unref(ep->user); + kfree(ep); +} + +static void kdbus_ep_release(struct kdbus_node *node, bool was_active) +{ + struct kdbus_ep *ep = container_of(node, struct kdbus_ep, node); + + /* disconnect all connections to this endpoint */ + for (;;) { + struct kdbus_conn *conn; + + mutex_lock(&ep->lock); + conn = list_first_entry_or_null(&ep->conn_list, + struct kdbus_conn, + ep_entry); + if (!conn) { + mutex_unlock(&ep->lock); + break; + } + + /* take reference, release lock, disconnect without lock */ + kdbus_conn_ref(conn); + mutex_unlock(&ep->lock); + + kdbus_conn_disconnect(conn, false); + kdbus_conn_unref(conn); + } +} + +/** + * kdbus_ep_new() - create a new endpoint + * @bus: The bus this endpoint will be created for + * @name: The name of the endpoint + * @access: The access flags for this node (KDBUS_MAKE_ACCESS_*) + * @uid: The uid of the node + * @gid: The gid of the node + * @is_custom: Whether this is a custom endpoint + * + * This function will create a new endpoint with the given + * name and properties for a given bus. + * + * Return: a new kdbus_ep on success, ERR_PTR on failure. + */ +struct kdbus_ep *kdbus_ep_new(struct kdbus_bus *bus, const char *name, + unsigned int access, kuid_t uid, kgid_t gid, + bool is_custom) +{ + struct kdbus_ep *e; + int ret; + + /* + * Validate only custom endpoints names, default endpoints + * with a "bus" name are created when the bus is created + */ + if (is_custom) { + ret = kdbus_verify_uid_prefix(name, bus->domain->user_namespace, + uid); + if (ret < 0) + return ERR_PTR(ret); + } + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) + return ERR_PTR(-ENOMEM); + + kdbus_node_init(&e->node, KDBUS_NODE_ENDPOINT); + + e->node.free_cb = kdbus_ep_free; + e->node.release_cb = kdbus_ep_release; + e->node.uid = uid; + e->node.gid = gid; + e->node.mode = S_IRUSR | S_IWUSR; + if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD)) + e->node.mode |= S_IRGRP | S_IWGRP; + if (access & KDBUS_MAKE_ACCESS_WORLD) + e->node.mode |= S_IROTH | S_IWOTH; + + mutex_init(&e->lock); + INIT_LIST_HEAD(&e->conn_list); + kdbus_policy_db_init(&e->policy_db); + e->bus = kdbus_bus_ref(bus); + + ret = kdbus_node_link(&e->node, &bus->node, name); + if (ret < 0) + goto exit_unref; + + /* + * Transactions on custom endpoints are never accounted on the global + * user limits. Instead, for each custom endpoint, we create a custom, + * unique user, which all transactions are accounted on. Regardless of + * the user using that endpoint, it is always accounted on the same + * user-object. This budget is not shared with ordinary users on + * non-custom endpoints. + */ + if (is_custom) { + e->user = kdbus_user_lookup(bus->domain, INVALID_UID); + if (IS_ERR(e->user)) { + ret = PTR_ERR(e->user); + e->user = NULL; + goto exit_unref; + } + } + + return e; + +exit_unref: + kdbus_node_deactivate(&e->node); + kdbus_node_unref(&e->node); + return ERR_PTR(ret); +} + +/** + * kdbus_ep_ref() - increase the reference counter of a kdbus_ep + * @ep: The endpoint to reference + * + * Every user of an endpoint, except for its creator, must add a reference to + * the kdbus_ep instance using this function. + * + * Return: the ep itself + */ +struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep) +{ + if (ep) + kdbus_node_ref(&ep->node); + return ep; +} + +/** + * kdbus_ep_unref() - decrease the reference counter of a kdbus_ep + * @ep: The ep to unref + * + * Release a reference. If the reference count drops to 0, the ep will be + * freed. + * + * Return: NULL + */ +struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep) +{ + if (ep) + kdbus_node_unref(&ep->node); + return NULL; +} + +/** + * kdbus_cmd_ep_make() - handle KDBUS_CMD_ENDPOINT_MAKE + * @bus: bus to operate on + * @argp: command payload + * + * Return: NULL or newly created endpoint on success, ERR_PTR on failure. + */ +struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp) +{ + const char *item_make_name; + struct kdbus_ep *ep = NULL; + struct kdbus_cmd *cmd; + int ret; + + struct kdbus_arg argv[] = { + { .type = KDBUS_ITEM_NEGOTIATE }, + { .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true }, + }; + 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; + + item_make_name = argv[1].item->str; + + ep = kdbus_ep_new(bus, item_make_name, cmd->flags, + current_euid(), current_egid(), true); + if (IS_ERR(ep)) { + ret = PTR_ERR(ep); + ep = NULL; + goto exit; + } + + if (!kdbus_node_activate(&ep->node)) { + ret = -ESHUTDOWN; + goto exit; + } + +exit: + ret = kdbus_args_clear(&args, ret); + if (ret < 0) { + if (ep) { + kdbus_node_deactivate(&ep->node); + kdbus_ep_unref(ep); + } + return ERR_PTR(ret); + } + return ep; +} + +/** + * kdbus_cmd_ep_update() - handle KDBUS_CMD_ENDPOINT_UPDATE + * @ep: endpoint to operate on + * @argp: command payload + * + * Return: >=0 on success, negative error code on failure. + */ +int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp) +{ + struct kdbus_cmd *cmd; + int ret; + + struct kdbus_arg argv[] = { + { .type = KDBUS_ITEM_NEGOTIATE }, + { .type = KDBUS_ITEM_NAME, .multiple = true }, + { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true }, + }; + 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_policy_set(&ep->policy_db, args.items, args.items_size, + 0, true, ep); + return kdbus_args_clear(&args, ret); +} |