/*
 * 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/file.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>

#include "bus.h"
#include "connection.h"
#include "endpoint.h"
#include "fs.h"
#include "handle.h"
#include "item.h"
#include "match.h"
#include "message.h"
#include "names.h"
#include "domain.h"
#include "policy.h"

static int kdbus_args_verify(struct kdbus_args *args)
{
	struct kdbus_item *item;
	size_t i;
	int ret;

	KDBUS_ITEMS_FOREACH(item, args->items, args->items_size) {
		struct kdbus_arg *arg = NULL;

		if (!KDBUS_ITEM_VALID(item, args->items, args->items_size))
			return -EINVAL;

		for (i = 0; i < args->argc; ++i)
			if (args->argv[i].type == item->type)
				break;
		if (i >= args->argc)
			return -EINVAL;

		arg = &args->argv[i];

		ret = kdbus_item_validate(item);
		if (ret < 0)
			return ret;

		if (arg->item && !arg->multiple)
			return -EINVAL;

		arg->item = item;
	}

	if (!KDBUS_ITEMS_END(item, args->items, args->items_size))
		return -EINVAL;

	return 0;
}

static int kdbus_args_negotiate(struct kdbus_args *args)
{
	struct kdbus_item __user *user;
	struct kdbus_item *negotiation;
	size_t i, j, num;

	/*
	 * If KDBUS_FLAG_NEGOTIATE is set, we overwrite the flags field with
	 * the set of supported flags. Furthermore, if an KDBUS_ITEM_NEGOTIATE
	 * item is passed, we iterate its payload (array of u64, each set to an
	 * item type) and clear all unsupported item-types to 0.
	 * The caller might do this recursively, if other flags or objects are
	 * embedded in the payload itself.
	 */

	if (args->cmd->flags & KDBUS_FLAG_NEGOTIATE) {
		if (put_user(args->allowed_flags & ~KDBUS_FLAG_NEGOTIATE,
			     &args->user->flags))
			return -EFAULT;
	}

	if (args->argc < 1 || args->argv[0].type != KDBUS_ITEM_NEGOTIATE ||
	    !args->argv[0].item)
		return 0;

	negotiation = args->argv[0].item;
	user = (struct kdbus_item __user *)
		((u8 __user *)args->user +
		 ((u8 *)negotiation - (u8 *)args->cmd));
	num = KDBUS_ITEM_PAYLOAD_SIZE(negotiation) / sizeof(u64);

	for (i = 0; i < num; ++i) {
		for (j = 0; j < args->argc; ++j)
			if (negotiation->data64[i] == args->argv[j].type)
				break;

		if (j < args->argc)
			continue;

		/* this item is not supported, clear it out */
		negotiation->data64[i] = 0;
		if (put_user(negotiation->data64[i], &user->data64[i]))
			return -EFAULT;
	}

	return 0;
}

/**
 * __kdbus_args_parse() - parse payload of kdbus command
 * @args:		object to parse data into
 * @is_cmd:		whether this is a command or msg payload
 * @argp:		user-space location of command payload to parse
 * @type_size:		overall size of command payload to parse
 * @items_offset:	offset of items array in command payload
 * @out:		output variable to store pointer to copied payload
 *
 * This parses the ioctl payload at user-space location @argp into @args. @args
 * must be pre-initialized by the caller to reflect the supported flags and
 * items of this command. This parser will then copy the command payload into
 * kernel-space, verify correctness and consistency and cache pointers to parsed
 * items and other data in @args.
 *
 * If this function succeeded, you must call kdbus_args_clear() to release
 * allocated resources before destroying @args.
 *
 * This can also be used to import kdbus_msg objects. In that case, @is_cmd must
 * be set to 'false' and the 'return_flags' field will not be touched (as it
 * doesn't exist on kdbus_msg).
 *
 * Return: On failure a negative error code is returned. Otherwise, 1 is
 * returned if negotiation was requested, 0 if not.
 */
int __kdbus_args_parse(struct kdbus_args *args, bool is_cmd, void __user *argp,
		       size_t type_size, size_t items_offset, void **out)
{
	u64 user_size;
	int ret, i;

	ret = kdbus_copy_from_user(&user_size, argp, sizeof(user_size));
	if (ret < 0)
		return ret;

	if (user_size < type_size)
		return -EINVAL;
	if (user_size > KDBUS_CMD_MAX_SIZE)
		return -EMSGSIZE;

	if (user_size <= sizeof(args->cmd_buf)) {
		if (copy_from_user(args->cmd_buf, argp, user_size))
			return -EFAULT;
		args->cmd = (void*)args->cmd_buf;
	} else {
		args->cmd = memdup_user(argp, user_size);
		if (IS_ERR(args->cmd))
			return PTR_ERR(args->cmd);
	}

	if (args->cmd->size != user_size) {
		ret = -EINVAL;
		goto error;
	}

	if (is_cmd)
		args->cmd->return_flags = 0;
	args->user = argp;
	args->items = (void *)((u8 *)args->cmd + items_offset);
	args->items_size = args->cmd->size - items_offset;
	args->is_cmd = is_cmd;

	if (args->cmd->flags & ~args->allowed_flags) {
		ret = -EINVAL;
		goto error;
	}

	ret = kdbus_args_verify(args);
	if (ret < 0)
		goto error;

	ret = kdbus_args_negotiate(args);
	if (ret < 0)
		goto error;

	/* mandatory items must be given (but not on negotiation) */
	if (!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE)) {
		for (i = 0; i < args->argc; ++i)
			if (args->argv[i].mandatory && !args->argv[i].item) {
				ret = -EINVAL;
				goto error;
			}
	}

	*out = args->cmd;
	return !!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE);

error:
	return kdbus_args_clear(args, ret);
}

/**
 * kdbus_args_clear() - release allocated command resources
 * @args:	object to release resources of
 * @ret:	return value of this command
 *
 * This frees all allocated resources on @args and copies the command result
 * flags into user-space. @ret is usually returned unchanged by this function,
 * so it can be used in the final 'return' statement of the command handler.
 *
 * Return: -EFAULT if return values cannot be copied into user-space, otherwise
 *         @ret is returned unchanged.
 */
int kdbus_args_clear(struct kdbus_args *args, int ret)
{
	if (!args)
		return ret;

	if (!IS_ERR_OR_NULL(args->cmd)) {
		if (args->is_cmd && put_user(args->cmd->return_flags,
					     &args->user->return_flags))
			ret = -EFAULT;
		if (args->cmd != (void*)args->cmd_buf)
			kfree(args->cmd);
		args->cmd = NULL;
	}

	return ret;
}

/**
 * enum kdbus_handle_type - type an handle can be of
 * @KDBUS_HANDLE_NONE:		no type set, yet
 * @KDBUS_HANDLE_BUS_OWNER:	bus owner
 * @KDBUS_HANDLE_EP_OWNER:	endpoint owner
 * @KDBUS_HANDLE_CONNECTED:	endpoint connection after HELLO
 */
enum kdbus_handle_type {
	KDBUS_HANDLE_NONE,
	KDBUS_HANDLE_BUS_OWNER,
	KDBUS_HANDLE_EP_OWNER,
	KDBUS_HANDLE_CONNECTED,
};

/**
 * struct kdbus_handle - handle to the kdbus system
 * @lock:		handle lock
 * @type:		type of this handle (KDBUS_HANDLE_*)
 * @bus_owner:		bus this handle owns
 * @ep_owner:		endpoint this handle owns
 * @conn:		connection this handle owns
 * @privileged:		Flag to mark a handle as privileged
 */
struct kdbus_handle {
	struct mutex lock;

	enum kdbus_handle_type type;
	union {
		struct kdbus_bus *bus_owner;
		struct kdbus_ep *ep_owner;
		struct kdbus_conn *conn;
	};

	bool privileged:1;
};

static int kdbus_handle_open(struct inode *inode, struct file *file)
{
	struct kdbus_handle *handle;
	struct kdbus_node *node;
	int ret;

	node = kdbus_node_from_inode(inode);
	if (!kdbus_node_acquire(node))
		return -ESHUTDOWN;

	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
	if (!handle) {
		ret = -ENOMEM;
		goto exit;
	}

	mutex_init(&handle->lock);
	handle->type = KDBUS_HANDLE_NONE;

	if (node->type == KDBUS_NODE_ENDPOINT) {
		struct kdbus_ep *ep = kdbus_ep_from_node(node);
		struct kdbus_bus *bus = ep->bus;

		/*
		 * A connection is privileged if it is opened on an endpoint
		 * without custom policy and either:
		 *   * the user has CAP_IPC_OWNER in the domain user namespace
		 * or
		 *   * the callers euid matches the uid of the bus creator
		 */
		if (!ep->user &&
		    (ns_capable(bus->domain->user_namespace, CAP_IPC_OWNER) ||
		     uid_eq(file->f_cred->euid, bus->node.uid)))
			handle->privileged = true;
	}

	file->private_data = handle;
	ret = 0;

exit:
	kdbus_node_release(node);
	return ret;
}

static int kdbus_handle_release(struct inode *inode, struct file *file)
{
	struct kdbus_handle *handle = file->private_data;

	switch (handle->type) {
	case KDBUS_HANDLE_BUS_OWNER:
		if (handle->bus_owner) {
			kdbus_node_deactivate(&handle->bus_owner->node);
			kdbus_bus_unref(handle->bus_owner);
		}
		break;
	case KDBUS_HANDLE_EP_OWNER:
		if (handle->ep_owner) {
			kdbus_node_deactivate(&handle->ep_owner->node);
			kdbus_ep_unref(handle->ep_owner);
		}
		break;
	case KDBUS_HANDLE_CONNECTED:
		kdbus_conn_disconnect(handle->conn, false);
		kdbus_conn_unref(handle->conn);
		break;
	case KDBUS_HANDLE_NONE:
		/* nothing to clean up */
		break;
	}

	kfree(handle);

	return 0;
}

static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd,
				       void __user *argp)
{
	struct kdbus_handle *handle = file->private_data;
	struct kdbus_node *node = file_inode(file)->i_private;
	struct kdbus_domain *domain;
	int ret = 0;

	if (!kdbus_node_acquire(node))
		return -ESHUTDOWN;

	/*
	 * The parent of control-nodes is always a domain, make sure to pin it
	 * so the parent is actually valid.
	 */
	domain = kdbus_domain_from_node(node->parent);
	if (!kdbus_node_acquire(&domain->node)) {
		kdbus_node_release(node);
		return -ESHUTDOWN;
	}

	switch (cmd) {
	case KDBUS_CMD_BUS_MAKE: {
		struct kdbus_bus *bus;

		bus = kdbus_cmd_bus_make(domain, argp);
		if (IS_ERR_OR_NULL(bus)) {
			ret = PTR_ERR_OR_ZERO(bus);
			break;
		}

		handle->bus_owner = bus;
		ret = KDBUS_HANDLE_BUS_OWNER;
		break;
	}

	default:
		ret = -EBADFD;
		break;
	}

	kdbus_node_release(&domain->node);
	kdbus_node_release(node);
	return ret;
}

static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd,
				  void __user *buf)
{
	struct kdbus_handle *handle = file->private_data;
	struct kdbus_node *node = file_inode(file)->i_private;
	struct kdbus_ep *ep, *file_ep = kdbus_ep_from_node(node);
	struct kdbus_conn *conn;
	int ret = 0;

	if (!kdbus_node_acquire(node))
		return -ESHUTDOWN;

	switch (cmd) {
	case KDBUS_CMD_ENDPOINT_MAKE:
		/* creating custom endpoints is a privileged operation */
		if (!handle->privileged) {
			ret = -EPERM;
			break;
		}

		ep = kdbus_cmd_ep_make(file_ep->bus, buf);
		if (IS_ERR_OR_NULL(ep)) {
			ret = PTR_ERR_OR_ZERO(ep);
			break;
		}

		handle->ep_owner = ep;
		ret = KDBUS_HANDLE_EP_OWNER;
		break;

	case KDBUS_CMD_HELLO:
		conn = kdbus_cmd_hello(file_ep, handle->privileged, buf);
		if (IS_ERR_OR_NULL(conn)) {
			ret = PTR_ERR_OR_ZERO(conn);
			break;
		}

		handle->conn = conn;
		ret = KDBUS_HANDLE_CONNECTED;
		break;

	default:
		ret = -EBADFD;
		break;
	}

	kdbus_node_release(node);
	return ret;
}

static long kdbus_handle_ioctl_ep_owner(struct file *file, unsigned int command,
					void __user *buf)
{
	struct kdbus_handle *handle = file->private_data;
	struct kdbus_ep *ep = handle->ep_owner;
	int ret;

	if (!kdbus_node_acquire(&ep->node))
		return -ESHUTDOWN;

	switch (command) {
	case KDBUS_CMD_ENDPOINT_UPDATE:
		ret = kdbus_cmd_ep_update(ep, buf);
		break;
	default:
		ret = -EBADFD;
		break;
	}

	kdbus_node_release(&ep->node);
	return ret;
}

static long kdbus_handle_ioctl_connected(struct file *file,
					 unsigned int command, void __user *buf)
{
	struct kdbus_handle *handle = file->private_data;
	struct kdbus_conn *conn = handle->conn;
	struct kdbus_conn *release_conn = NULL;
	int ret;

	release_conn = conn;
	ret = kdbus_conn_acquire(release_conn);
	if (ret < 0)
		return ret;

	switch (command) {
	case KDBUS_CMD_BYEBYE:
		/*
		 * BYEBYE is special; we must not acquire a connection when
		 * calling into kdbus_conn_disconnect() or we will deadlock,
		 * because kdbus_conn_disconnect() will wait for all acquired
		 * references to be dropped.
		 */
		kdbus_conn_release(release_conn);
		release_conn = NULL;
		ret = kdbus_cmd_byebye_unlocked(conn, buf);
		break;
	case KDBUS_CMD_NAME_ACQUIRE:
		ret = kdbus_cmd_name_acquire(conn, buf);
		break;
	case KDBUS_CMD_NAME_RELEASE:
		ret = kdbus_cmd_name_release(conn, buf);
		break;
	case KDBUS_CMD_LIST:
		ret = kdbus_cmd_list(conn, buf);
		break;
	case KDBUS_CMD_CONN_INFO:
		ret = kdbus_cmd_conn_info(conn, buf);
		break;
	case KDBUS_CMD_BUS_CREATOR_INFO:
		ret = kdbus_cmd_bus_creator_info(conn, buf);
		break;
	case KDBUS_CMD_UPDATE:
		ret = kdbus_cmd_update(conn, buf);
		break;
	case KDBUS_CMD_MATCH_ADD:
		ret = kdbus_cmd_match_add(conn, buf);
		break;
	case KDBUS_CMD_MATCH_REMOVE:
		ret = kdbus_cmd_match_remove(conn, buf);
		break;
	case KDBUS_CMD_SEND:
		ret = kdbus_cmd_send(conn, file, buf);
		break;
	case KDBUS_CMD_RECV:
		ret = kdbus_cmd_recv(conn, buf);
		break;
	case KDBUS_CMD_FREE:
		ret = kdbus_cmd_free(conn, buf);
		break;
	default:
		ret = -EBADFD;
		break;
	}

	kdbus_conn_release(release_conn);
	return ret;
}

static long kdbus_handle_ioctl(struct file *file, unsigned int cmd,
			       unsigned long arg)
{
	struct kdbus_handle *handle = file->private_data;
	struct kdbus_node *node = kdbus_node_from_inode(file_inode(file));
	void __user *argp = (void __user *)arg;
	long ret = -EBADFD;

	switch (cmd) {
	case KDBUS_CMD_BUS_MAKE:
	case KDBUS_CMD_ENDPOINT_MAKE:
	case KDBUS_CMD_HELLO:
		mutex_lock(&handle->lock);
		if (handle->type == KDBUS_HANDLE_NONE) {
			if (node->type == KDBUS_NODE_CONTROL)
				ret = kdbus_handle_ioctl_control(file, cmd,
								 argp);
			else if (node->type == KDBUS_NODE_ENDPOINT)
				ret = kdbus_handle_ioctl_ep(file, cmd, argp);

			if (ret > 0) {
				/*
				 * The data given via open() is not sufficient
				 * to setup a kdbus handle. Hence, we require
				 * the user to perform a setup ioctl. This setup
				 * can only be performed once and defines the
				 * type of the handle. The different setup
				 * ioctls are locked against each other so they
				 * cannot race. Once the handle type is set,
				 * the type-dependent ioctls are enabled. To
				 * improve performance, we don't lock those via
				 * handle->lock. Instead, we issue a
				 * write-barrier before performing the
				 * type-change, which pairs with smp_rmb() in
				 * all handlers that access the type field. This
				 * guarantees the handle is fully setup, if
				 * handle->type is set. If handle->type is
				 * unset, you must not make any assumptions
				 * without taking handle->lock.
				 * Note that handle->type is only set once. It
				 * will never change afterwards.
				 */
				smp_wmb();
				handle->type = ret;
			}
		}
		mutex_unlock(&handle->lock);
		break;

	case KDBUS_CMD_ENDPOINT_UPDATE:
	case KDBUS_CMD_BYEBYE:
	case KDBUS_CMD_NAME_ACQUIRE:
	case KDBUS_CMD_NAME_RELEASE:
	case KDBUS_CMD_LIST:
	case KDBUS_CMD_CONN_INFO:
	case KDBUS_CMD_BUS_CREATOR_INFO:
	case KDBUS_CMD_UPDATE:
	case KDBUS_CMD_MATCH_ADD:
	case KDBUS_CMD_MATCH_REMOVE:
	case KDBUS_CMD_SEND:
	case KDBUS_CMD_RECV:
	case KDBUS_CMD_FREE: {
		enum kdbus_handle_type type;

		/*
		 * This read-barrier pairs with smp_wmb() of the handle setup.
		 * it guarantees the handle is fully written, in case the
		 * type has been set. It allows us to access the handle without
		 * taking handle->lock, given the guarantee that the type is
		 * only ever set once, and stays constant afterwards.
		 * Furthermore, the handle object itself is not modified in any
		 * way after the type is set. That is, the type-field is the
		 * last field that is written on any handle. If it has not been
		 * set, we must not access the handle here.
		 */
		type = handle->type;
		smp_rmb();

		if (type == KDBUS_HANDLE_EP_OWNER)
			ret = kdbus_handle_ioctl_ep_owner(file, cmd, argp);
		else if (type == KDBUS_HANDLE_CONNECTED)
			ret = kdbus_handle_ioctl_connected(file, cmd, argp);

		break;
	}
	default:
		ret = -ENOTTY;
		break;
	}

	return ret < 0 ? ret : 0;
}

static unsigned int kdbus_handle_poll(struct file *file,
				      struct poll_table_struct *wait)
{
	struct kdbus_handle *handle = file->private_data;
	enum kdbus_handle_type type;
	unsigned int mask = POLLOUT | POLLWRNORM;

	/*
	 * This pairs with smp_wmb() during handle setup. It guarantees that
	 * _iff_ the handle type is set, handle->conn is valid. Furthermore,
	 * _iff_ the type is set, the handle object is constant and never
	 * changed again. If it's not set, we must not access the handle but
	 * bail out. We also must assume no setup has taken place, yet.
	 */
	type = handle->type;
	smp_rmb();

	/* Only a connected endpoint can read/write data */
	if (type != KDBUS_HANDLE_CONNECTED)
		return POLLERR | POLLHUP;

	poll_wait(file, &handle->conn->wait, wait);

	/*
	 * Verify the connection hasn't been deactivated _after_ adding the
	 * wait-queue. This guarantees, that if the connection is deactivated
	 * after we checked it, the waitqueue is signaled and we're called
	 * again.
	 */
	if (!kdbus_conn_active(handle->conn))
		return POLLERR | POLLHUP;

	if (!list_empty(&handle->conn->queue.msg_list) ||
	    atomic_read(&handle->conn->lost_count) > 0)
		mask |= POLLIN | POLLRDNORM;

	return mask;
}

static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct kdbus_handle *handle = file->private_data;
	enum kdbus_handle_type type;
	int ret = -EBADFD;

	/*
	 * This pairs with smp_wmb() during handle setup. It guarantees that
	 * _iff_ the handle type is set, handle->conn is valid. Furthermore,
	 * _iff_ the type is set, the handle object is constant and never
	 * changed again. If it's not set, we must not access the handle but
	 * bail out. We also must assume no setup has taken place, yet.
	 */
	type = handle->type;
	smp_rmb();

	/* Only connected handles have a pool we can map */
	if (type == KDBUS_HANDLE_CONNECTED)
		ret = kdbus_pool_mmap(handle->conn->pool, vma);

	return ret;
}

const struct file_operations kdbus_handle_ops = {
	.owner =		THIS_MODULE,
	.open =			kdbus_handle_open,
	.release =		kdbus_handle_release,
	.poll =			kdbus_handle_poll,
	.llseek =		noop_llseek,
	.unlocked_ioctl =	kdbus_handle_ioctl,
	.mmap =			kdbus_handle_mmap,
#ifdef CONFIG_COMPAT
	.compat_ioctl =		kdbus_handle_ioctl,
#endif
};