Re: [PATCH v2] Input: evdev - add EVIOCREVOKE ioctl

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi David,

On Tue, Aug 27, 2013 at 01:39:49PM +0200, David Herrmann wrote:
> If we have multiple sessions on a system, we normally don't want
> background sessions to read input events. Otherwise, it could capture
> passwords and more entered by the user on the foreground session. This is
> a real world problem as the recent XMir development showed:
>   http://mjg59.dreamwidth.org/27327.html
> 
> We currently rely on sessions to release input devices when being
> deactivated. This relies on trust across sessions. But that's not given on
> usual systems. We therefore need a way to control which processes have
> access to input devices.
> 
> With VTs the kernel simply routed them through the active /dev/ttyX. This
> is not possible with evdev devices, though. Moreover, we want to avoid
> routing input-devices through some dispatcher-daemon in userspace (which
> would add some latency).
> 
> This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
> device-access irrecoverably for that *single* open-file. Hence, once you
> call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
> rather useless now (but still valid compared to close()!). This allows us
> to pass fds directly to session-processes from a trusted source. The
> source keeps a dup()ed fd and revokes access once the session-process is
> no longer active.
> Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
> restriction now as there is no way to revive the fd again. Hence, a user
> is free to call EVIOCREVOKE themself to kill the fd.
> 
> Additionally, this ioctl allows multi-layer access-control (again compared
> to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
> layer can simply request a new open-file from the layer above and pass it
> to the layer below. Now each layer can call EVIOCREVOKE on the fds to
> revoke access for all layers below, at the expense of one fd per layer.
> 
> There's already ongoing experimental user-space work which demonstrates
> how it can be used:
>   http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
> 
> Signed-off-by: David Herrmann <dh.herrmann@xxxxxxxxx>
> ---
> v2:
>  - ungrab device during revoke
>  - wake-up blocking read()s of the client
>  - return -EACCES from write() for revoked clients
>  - signal POLLHUP | POLLERR for revoked clients
>  - don't signal POLLOUT for revoked clients
> 
>  drivers/input/evdev.c      | 43 ++++++++++++++++++++++++++++++++++++++++---
>  include/uapi/linux/input.h |  1 +
>  2 files changed, 41 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
> index d2b34fb..f2abd76 100644
> --- a/drivers/input/evdev.c
> +++ b/drivers/input/evdev.c
> @@ -48,6 +48,7 @@ struct evdev_client {
>  	struct evdev *evdev;
>  	struct list_head node;
>  	int clkid;
> +	bool revoked;
>  	unsigned int bufsize;
>  	struct input_event buffer[];
>  };
> @@ -164,6 +165,9 @@ static void evdev_pass_values(struct evdev_client *client,
>  	struct input_event event;
>  	bool wakeup = false;
>  
> +	if (client->revoked)
> +		return;
> +
>  	event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
>  				      mono : real);
>  
> @@ -432,6 +436,9 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
>  	if (!evdev->exist) {
>  		retval = -ENODEV;
>  		goto out;
> +	} else if (client->revoked) {
> +		retval = -EACCES;
> +		goto out;

Why do we treat revoke differently form device going away? I'd return
-ENODEV in both cases.

>  	}
>  
>  	while (retval + input_event_size() <= count) {
> @@ -511,7 +518,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
>  		if (!(file->f_flags & O_NONBLOCK)) {
>  			error = wait_event_interruptible(evdev->wait,
>  					client->packet_head != client->tail ||
> -					!evdev->exist);
> +					!evdev->exist || client->revoked);
>  			if (error)
>  				return error;
>  		}
> @@ -529,7 +536,11 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
>  
>  	poll_wait(file, &evdev->wait, wait);
>  
> -	mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
> +	if (evdev->exist && !client->revoked)
> +		mask = POLLOUT | POLLWRNORM;
> +	else
> +		mask = POLLHUP | POLLERR;
> +
>  	if (client->packet_head != client->tail)
>  		mask |= POLLIN | POLLRDNORM;
>  
> @@ -795,6 +806,17 @@ static int evdev_handle_mt_request(struct input_dev *dev,
>  	return 0;
>  }
>  
> +static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
> +			struct file *file)
> +{
> +	client->revoked = true;
> +	evdev_ungrab(evdev, client);
> +	input_flush_device(&evdev->handle, file);
> +	wake_up_interruptible(&evdev->wait);
> +
> +	return 0;
> +}
> +
>  static long evdev_do_ioctl(struct file *file, unsigned int cmd,
>  			   void __user *p, int compat_mode)
>  {
> @@ -808,12 +830,27 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
>  	unsigned int size;
>  	int error;
>  
> -	/* First we check for fixed-length commands */
> +	/* First check for ioctls allowed while revoked */
>  	switch (cmd) {
>  
>  	case EVIOCGVERSION:
>  		return put_user(EV_VERSION, ip);
>  
> +	case EVIOCREVOKE:
> +		if (p)
> +			return -EINVAL;
> +		else
> +			return evdev_revoke(evdev, client, file);
> +
> +	default:
> +		if (client->revoked)
> +			return -EACCES;
> +		break;
> +	}

Here as well, I'd check revoked in the same place where we check exist
and bail if device gone away or our access was revoked.

> +
> +	/* Then check for fixed-length commands */
> +	switch (cmd) {
> +
>  	case EVIOCGID:
>  		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
>  			return -EFAULT;
> diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
> index 2fb6fae..d61c61c 100644
> --- a/include/uapi/linux/input.h
> +++ b/include/uapi/linux/input.h
> @@ -152,6 +152,7 @@ struct input_keymap_entry {
>  #define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
>  
>  #define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
> +#define EVIOCREVOKE		_IOW('E', 0x91, int)			/* Revoke device access */
>  
>  #define EVIOCSCLOCKID		_IOW('E', 0xa0, int)			/* Set clockid to be used for timestamps */
>  
> -- 
> 1.8.4
> 

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux