blob: 174d274b113eae922aa213f652b6292d799908c7 [file] [log] [blame]
/*
* 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 enpoint 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: 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: Newly created endpoint on success, ERR_PTR 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);
}