Hi Kristian

On Tue, Apr 16, 2013 at 3:58 PM, Kristian Høgsberg <k...@bitplanet.net> wrote:
> This commit adds a new ioctl to the evdev device: EVIOCMUTE.  The
> purpose of this ioctl it to temporarily block event delivery to a
> specific evdev client and prevent access to most of the ioctls.
>
> The use case is a setuid helper process for a display server, which
> opens input devices and passes the fds to the display server.  On VT
> switch away from the server, it should stop reading events from its
> evdev input devices.  However, if the display server runs as the user
> it can be ptraced or if the server loads user defined modules, the
> display server can no longer be trusted to not snoop on the evdev
> devices.
>
> The mute ioctl allows the setuid helper to mute evdev devices when
> switching away from the VT the server is running on.  The idea is that
> the helper listens for the VT switching signals and when VT switch happens
> it notifies the display server, waits for the server to clean up and then
> the helper drops drm master (which also requires root), mutes evdev
> devices and the acks the VT switch.
>
> Why don't we just use revoke?  The revoke syscall (when it's done) will
> work on filenames and shut down all fds open to the device.  This will
> break all other processes that listen on evdev devices for legitimate
> reasons.  It's the same reason we don't use the EVIOCGRAB ioctl for input
> devices.  EVIOCMUTE lets the helper mute just the fd it gave to the
> display server without affecting anything else potentially using the device.

Other use-cases include SAK, forced VT switches and
system-compositors. I'd really like seeing this feature.

> Signed-off-by: Kristian Høgsberg <k...@bitplanet.net>
> ---
>  drivers/input/evdev.c      | 37 ++++++++++++++++++++++++++++++++++---
>  include/uapi/linux/input.h |  1 +
>  2 files changed, 35 insertions(+), 3 deletions(-)
>
> This idea comes from work on Wayland and Weston [1], but the setuid helper
> should work and is required for a non-root X server to function correctly
> as well (ie, do proper vt switching).
>
> Kristian
>
> diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
> index f0f8928..cea6c35 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;
> +       int muted;

Use "bool" instead.

>         unsigned int bufsize;
>         struct input_event buffer[];
>  };
> @@ -130,8 +131,9 @@ static void evdev_events(struct input_handle *handle,
>                 evdev_pass_values(client, vals, count, time_mono, time_real);
>         else
>                 list_for_each_entry_rcu(client, &evdev->client_list, node)
> -                       evdev_pass_values(client, vals, count,
> -                                         time_mono, time_real);
> +                       if (!client->muted)
> +                               evdev_pass_values(client, vals, count,
> +                                                 time_mono, time_real);

Personally, I'd do this in evdev_pass_values(), but that's a matter of
taste.. And this would also allow muting grabbed devices. Because
currently input-grabs overwrite the mute flag which I think is odd. At
least mention this in the commit message so people know how it works
regarding input grabs.

>
>         rcu_read_unlock();
>  }
> @@ -674,6 +676,20 @@ static int evdev_handle_mt_request(struct input_dev *dev,
>         return 0;
>  }
>
> +static int evdev_mute(struct evdev *evdev, struct evdev_client *client)
> +{
> +       client->muted = 1;
> +
> +       return 0;
> +}
> +
> +static int evdev_unmute(struct evdev *evdev, struct evdev_client *client)
> +{
> +       client->muted = 0;
> +
> +       return 0;
> +}
> +

As I guess we want this feature to be atomic, we definitely need a
lock here. We also want, after EVIOCMUTE returns, that no other ioctl
will be allowed. Hence, we need a mutex that protects
evdev_do_ioctl(). A spinlock around "muted" won't help..
As we currently have no suitable lock in evdev_client I guess we need
to add one. In this case you can also remove these helpers and do:
  client->muted = !!p;
in evdev_do_ioctl().

Note that a reader-writer lock would allow parallel ioctl execution if
only the EVIOCMUTE command takes the write-lock and the other ioctls
take a read lock.

Regards
David

>  static long evdev_do_ioctl(struct file *file, unsigned int cmd,
>                            void __user *p, int compat_mode)
>  {
> @@ -687,12 +703,27 @@ static long evdev_do_ioctl(struct file *file, unsigned 
> int cmd,
>         unsigned int size;
>         int error;
>
> -       /* First we check for fixed-length commands */
> +       /* Handle ioctls allowed in muted mode first */
>         switch (cmd) {
> +       case EVIOCMUTE:
> +               if (!capable(CAP_SYS_ADMIN))
> +                   return -EACCES;
> +
> +               if (p)
> +                       return evdev_mute(evdev, client);
> +               else
> +                       return evdev_unmute(evdev, client);
>
>         case EVIOCGVERSION:
>                 return put_user(EV_VERSION, ip);
>
> +       default:
> +               if (client->muted)
> +                       return -EACCES;
> +       }
> +
> +       /* Now 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 935119c..75eda72 100644
> --- a/include/uapi/linux/input.h
> +++ b/include/uapi/linux/input.h
> @@ -154,6 +154,7 @@ struct input_keymap_entry {
>  #define EVIOCGRAB              _IOW('E', 0x90, int)                    /* 
> Grab/Release device */
>
>  #define EVIOCSCLOCKID          _IOW('E', 0xa0, int)                    /* 
> Set clockid to be used for timestamps */
> +#define EVIOCMUTE              _IOW('E', 0xb0, int)                    /* 
> Mute event delivery */
>
>  /*
>   * Device properties and quirks
> --
> 1.8.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to