aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/evdev.c
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-09-07 15:23:05 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-09-07 15:53:20 -0400
commitc7dc65737c9a607d3e6f8478659876074ad129b8 (patch)
treeab5a3f751d28ae4d9c1be61b0877c95aa0e56338 /drivers/input/evdev.c
parent07176b988ebb20f46a317a60ee1d983fe1630203 (diff)
Input: evdev - add EVIOCREVOKE ioctl
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> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/evdev.c')
-rw-r--r--drivers/input/evdev.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index d2b34fbbc42e..b6ded17b3be3 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -48,6 +48,7 @@ struct evdev_client {
48 struct evdev *evdev; 48 struct evdev *evdev;
49 struct list_head node; 49 struct list_head node;
50 int clkid; 50 int clkid;
51 bool revoked;
51 unsigned int bufsize; 52 unsigned int bufsize;
52 struct input_event buffer[]; 53 struct input_event buffer[];
53}; 54};
@@ -164,6 +165,9 @@ static void evdev_pass_values(struct evdev_client *client,
164 struct input_event event; 165 struct input_event event;
165 bool wakeup = false; 166 bool wakeup = false;
166 167
168 if (client->revoked)
169 return;
170
167 event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? 171 event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
168 mono : real); 172 mono : real);
169 173
@@ -240,7 +244,7 @@ static int evdev_flush(struct file *file, fl_owner_t id)
240 if (retval) 244 if (retval)
241 return retval; 245 return retval;
242 246
243 if (!evdev->exist) 247 if (!evdev->exist || client->revoked)
244 retval = -ENODEV; 248 retval = -ENODEV;
245 else 249 else
246 retval = input_flush_device(&evdev->handle, file); 250 retval = input_flush_device(&evdev->handle, file);
@@ -429,7 +433,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
429 if (retval) 433 if (retval)
430 return retval; 434 return retval;
431 435
432 if (!evdev->exist) { 436 if (!evdev->exist || client->revoked) {
433 retval = -ENODEV; 437 retval = -ENODEV;
434 goto out; 438 goto out;
435 } 439 }
@@ -482,7 +486,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
482 return -EINVAL; 486 return -EINVAL;
483 487
484 for (;;) { 488 for (;;) {
485 if (!evdev->exist) 489 if (!evdev->exist || client->revoked)
486 return -ENODEV; 490 return -ENODEV;
487 491
488 if (client->packet_head == client->tail && 492 if (client->packet_head == client->tail &&
@@ -511,7 +515,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
511 if (!(file->f_flags & O_NONBLOCK)) { 515 if (!(file->f_flags & O_NONBLOCK)) {
512 error = wait_event_interruptible(evdev->wait, 516 error = wait_event_interruptible(evdev->wait,
513 client->packet_head != client->tail || 517 client->packet_head != client->tail ||
514 !evdev->exist); 518 !evdev->exist || client->revoked);
515 if (error) 519 if (error)
516 return error; 520 return error;
517 } 521 }
@@ -529,7 +533,11 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
529 533
530 poll_wait(file, &evdev->wait, wait); 534 poll_wait(file, &evdev->wait, wait);
531 535
532 mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; 536 if (evdev->exist && !client->revoked)
537 mask = POLLOUT | POLLWRNORM;
538 else
539 mask = POLLHUP | POLLERR;
540
533 if (client->packet_head != client->tail) 541 if (client->packet_head != client->tail)
534 mask |= POLLIN | POLLRDNORM; 542 mask |= POLLIN | POLLRDNORM;
535 543
@@ -795,6 +803,17 @@ static int evdev_handle_mt_request(struct input_dev *dev,
795 return 0; 803 return 0;
796} 804}
797 805
806static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
807 struct file *file)
808{
809 client->revoked = true;
810 evdev_ungrab(evdev, client);
811 input_flush_device(&evdev->handle, file);
812 wake_up_interruptible(&evdev->wait);
813
814 return 0;
815}
816
798static long evdev_do_ioctl(struct file *file, unsigned int cmd, 817static long evdev_do_ioctl(struct file *file, unsigned int cmd,
799 void __user *p, int compat_mode) 818 void __user *p, int compat_mode)
800{ 819{
@@ -857,6 +876,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
857 else 876 else
858 return evdev_ungrab(evdev, client); 877 return evdev_ungrab(evdev, client);
859 878
879 case EVIOCREVOKE:
880 if (p)
881 return -EINVAL;
882 else
883 return evdev_revoke(evdev, client, file);
884
860 case EVIOCSCLOCKID: 885 case EVIOCSCLOCKID:
861 if (copy_from_user(&i, p, sizeof(unsigned int))) 886 if (copy_from_user(&i, p, sizeof(unsigned int)))
862 return -EFAULT; 887 return -EFAULT;
@@ -1002,7 +1027,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
1002 if (retval) 1027 if (retval)
1003 return retval; 1028 return retval;
1004 1029
1005 if (!evdev->exist) { 1030 if (!evdev->exist || client->revoked) {
1006 retval = -ENODEV; 1031 retval = -ENODEV;
1007 goto out; 1032 goto out;
1008 } 1033 }