From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: David Herrmann <dh.herrmann@gmail.com>
Cc: linux-input@vger.kernel.org
Subject: Re: [PATCH v2] Input: evdev - add EVIOCREVOKE ioctl
Date: Tue, 27 Aug 2013 15:17:36 -0700 [thread overview]
Message-ID: <20130827221733.GA20726@core.coreip.homeip.net> (raw)
In-Reply-To: <1377603589-25501-1-git-send-email-dh.herrmann@gmail.com>
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@gmail.com>
> ---
> 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
next prev parent reply other threads:[~2013-08-27 22:22 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-08-27 11:25 [PATCH] Input: evdev - add EVIOCREVOKE ioctl David Herrmann
2013-08-27 11:39 ` [PATCH v2] " David Herrmann
2013-08-27 22:17 ` Dmitry Torokhov [this message]
2013-08-27 22:35 ` David Herrmann
2013-09-01 14:07 ` [PATCH v3] " David Herrmann
2013-09-06 17:12 ` David Herrmann
2013-09-06 18:04 ` Kristian Høgsberg
2013-09-07 11:00 ` [PATCH v4] " David Herrmann
2013-09-07 17:16 ` Dmitry Torokhov
2013-09-07 17:41 ` David Herrmann
2015-02-04 13:12 ` [PATCH] " Bruno Prémont
2015-02-04 13:16 ` David Herrmann
2015-02-04 14:55 ` Bruno Prémont
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20130827221733.GA20726@core.coreip.homeip.net \
--to=dmitry.torokhov@gmail.com \
--cc=dh.herrmann@gmail.com \
--cc=linux-input@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).