David Herrmann <[email protected]> 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 <[email protected]>
>---
>Hi Dmitry
>
>Given the recent ABS_*-regression, I wrote a bunch of more aggressive
>stress-tests for this. I didn't found any regressions if EVIOCREVOKE is
>not
>used, but one with revoke on an empty queue in evdev_read(). Now fixed.
>Please
>let me know what your plans for this patch are (-next or -rc1?) so we
>can
>schedule accordingly.
I think this one is safer than extending axis numbers as there is no concerns
about breaking stuff - it's brand new. So I think we can work it in -rc1.
> As a side note, will you attend LPC this year? We
>have a
>bunch of fancy stuff I'd like your opinion on (including
>device-properties, device-detection, ABS_* bit extension).
Yes. I'll get to new Orleans a couple days earlier (Sunday night) so if you are
in town we could meet for drinks or such.
>
>Thanks and cheers
>David
>
> drivers/input/evdev.c | 37 +++++++++++++++++++++++++++++++------
> include/uapi/linux/input.h | 1 +
> 2 files changed, 32 insertions(+), 6 deletions(-)
>
>diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
>index d2b34fb..b6ded17 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);
>
>@@ -240,7 +244,7 @@ static int evdev_flush(struct file *file,
>fl_owner_t id)
> if (retval)
> return retval;
>
>- if (!evdev->exist)
>+ if (!evdev->exist || client->revoked)
> retval = -ENODEV;
> else
> retval = input_flush_device(&evdev->handle, file);
>@@ -429,7 +433,7 @@ static ssize_t evdev_write(struct file *file, const
>char __user *buffer,
> if (retval)
> return retval;
>
>- if (!evdev->exist) {
>+ if (!evdev->exist || client->revoked) {
> retval = -ENODEV;
> goto out;
> }
>@@ -482,7 +486,7 @@ static ssize_t evdev_read(struct file *file, char
>__user *buffer,
> return -EINVAL;
>
> for (;;) {
>- if (!evdev->exist)
>+ if (!evdev->exist || client->revoked)
> return -ENODEV;
>
> if (client->packet_head == client->tail &&
>@@ -511,7 +515,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 +533,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 +803,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)
> {
>@@ -857,6 +876,12 @@ static long evdev_do_ioctl(struct file *file,
>unsigned int cmd,
> else
> return evdev_ungrab(evdev, client);
>
>+ case EVIOCREVOKE:
>+ if (p)
>+ return -EINVAL;
>+ else
>+ return evdev_revoke(evdev, client, file);
>+
> case EVIOCSCLOCKID:
> if (copy_from_user(&i, p, sizeof(unsigned int)))
> return -EFAULT;
>@@ -1002,7 +1027,7 @@ static long evdev_ioctl_handler(struct file
>*file, unsigned int cmd,
> if (retval)
> return retval;
>
>- if (!evdev->exist) {
>+ if (!evdev->exist || client->revoked) {
> retval = -ENODEV;
> goto out;
> }
>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 */
>
Hi David,
Thanks.
--
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html