Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 24 Feb 2019 18:47:04 +0000 (UTC)
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r344494 - in head: sbin/sysctl sys/dev/evdev
Message-ID:  <201902241847.x1OIl4mW066694@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: wulf
Date: Sun Feb 24 18:47:04 2019
New Revision: 344494
URL: https://svnweb.freebsd.org/changeset/base/344494

Log:
  evdev: export event device properties through sysctl interface
  
  A big security advantage of Wayland is not allowing applications to read
  input devices all the time. Having /dev/input/* accessible to the user
  account subverts this advantage.
  
  libudev-devd was opening the evdev devices to detect their types (mouse,
  keyboard, touchpad, etc). This don't work if /dev/input/* is inaccessible.
  With the kernel exposing this information as sysctls (kern.evdev.input.*),
  we can work w/o /dev/input/* access, preserving the Wayland security model.
  
  Submitted by:		Greg V <greg@unrelenting.technology>
  Reviewed by:		wulf, imp
  MFC after:		2 weeks
  Differential Revision:	https://reviews.freebsd.org/D18694

Modified:
  head/sbin/sysctl/sysctl.c
  head/sys/dev/evdev/evdev.c
  head/sys/dev/evdev/evdev_private.h

Modified: head/sbin/sysctl/sysctl.c
==============================================================================
--- head/sbin/sysctl/sysctl.c	Sun Feb 24 17:23:55 2019	(r344493)
+++ head/sbin/sysctl/sysctl.c	Sun Feb 24 18:47:04 2019	(r344494)
@@ -49,6 +49,7 @@ static const char rcsid[] =
 #include <sys/stat.h>
 #include <sys/sysctl.h>
 #include <sys/vmmeter.h>
+#include <dev/evdev/input.h>
 
 #ifdef __amd64__
 #include <sys/efi.h>
@@ -680,6 +681,22 @@ S_vmtotal(size_t l2, void *p)
 	return (0);
 }
 
+static int
+S_input_id(size_t l2, void *p)
+{
+	struct input_id *id = p;
+
+	if (l2 != sizeof(*id)) {
+		warnx("S_input_id %zu != %zu", l2, sizeof(*id));
+		return (1);
+	}
+
+	printf("{ bustype = 0x%04x, vendor = 0x%04x, "
+	    "product = 0x%04x, version = 0x%04x }",
+	    id->bustype, id->vendor, id->product, id->version);
+	return (0);
+}
+
 #ifdef __amd64__
 static int
 S_efi_map(size_t l2, void *p)
@@ -983,6 +1000,8 @@ show_var(int *oid, int nlen)
 			func = S_loadavg;
 		else if (strcmp(fmt, "S,vmtotal") == 0)
 			func = S_vmtotal;
+		else if (strcmp(fmt, "S,input_id") == 0)
+			func = S_input_id;
 #ifdef __amd64__
 		else if (strcmp(fmt, "S,efi_map_header") == 0)
 			func = S_efi_map;

Modified: head/sys/dev/evdev/evdev.c
==============================================================================
--- head/sys/dev/evdev/evdev.c	Sun Feb 24 17:23:55 2019	(r344493)
+++ head/sys/dev/evdev/evdev.c	Sun Feb 24 18:47:04 2019	(r344494)
@@ -76,6 +76,8 @@ SYSCTL_INT(_kern_evdev, OID_AUTO, rcpt_mask, CTLFLAG_R
     "bit2 - mouse hardware, bit3 - keyboard hardware");
 SYSCTL_INT(_kern_evdev, OID_AUTO, sysmouse_t_axis, CTLFLAG_RW,
     &evdev_sysmouse_t_axis, 0, "Extract T-axis from 0-none, 1-ums, 2-psm");
+SYSCTL_NODE(_kern_evdev, OID_AUTO, input, CTLFLAG_RD, 0,
+    "Evdev input devices");
 #endif
 
 static void evdev_start_repeat(struct evdev_dev *, uint16_t);
@@ -196,6 +198,87 @@ evdev_estimate_report_size(struct evdev_dev *evdev)
 	return (size);
 }
 
+static void
+evdev_sysctl_create(struct evdev_dev *evdev)
+{
+	struct sysctl_oid *ev_sysctl_tree;
+	char ev_unit_str[8];
+
+	snprintf(ev_unit_str, sizeof(ev_unit_str), "%d", evdev->ev_unit);
+	sysctl_ctx_init(&evdev->ev_sysctl_ctx);
+
+	ev_sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&evdev->ev_sysctl_ctx,
+	    SYSCTL_STATIC_CHILDREN(_kern_evdev_input), OID_AUTO,
+	    ev_unit_str, CTLFLAG_RD, NULL, "", "device index");
+
+	SYSCTL_ADD_STRING(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "name", CTLFLAG_RD,
+	    evdev->ev_name, 0,
+	    "Input device name");
+
+	SYSCTL_ADD_STRUCT(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "id", CTLFLAG_RD,
+	    &evdev->ev_id, input_id,
+	    "Input device identification");
+
+	/* ioctl returns ENOENT if phys is not set. sysctl returns "" here */
+	SYSCTL_ADD_STRING(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "phys", CTLFLAG_RD,
+	    evdev->ev_shortname, 0,
+	    "Input device short name");
+
+	/* ioctl returns ENOENT if uniq is not set. sysctl returns "" here */
+	SYSCTL_ADD_STRING(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "uniq", CTLFLAG_RD,
+	    evdev->ev_serial, 0,
+	    "Input device unique number");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "props", CTLFLAG_RD,
+	    evdev->ev_prop_flags, sizeof(evdev->ev_prop_flags), "",
+	    "Input device properties");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "type_bits", CTLFLAG_RD,
+	    evdev->ev_type_flags, sizeof(evdev->ev_type_flags), "",
+	    "Input device supported events types");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "key_bits", CTLFLAG_RD,
+	    evdev->ev_key_flags, sizeof(evdev->ev_key_flags),
+	    "", "Input device supported keys");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "rel_bits", CTLFLAG_RD,
+	    evdev->ev_rel_flags, sizeof(evdev->ev_rel_flags), "",
+	    "Input device supported relative events");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "abs_bits", CTLFLAG_RD,
+	    evdev->ev_abs_flags, sizeof(evdev->ev_abs_flags), "",
+	    "Input device supported absolute events");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "msc_bits", CTLFLAG_RD,
+	    evdev->ev_msc_flags, sizeof(evdev->ev_msc_flags), "",
+	    "Input device supported miscellaneous events");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "led_bits", CTLFLAG_RD,
+	    evdev->ev_led_flags, sizeof(evdev->ev_led_flags), "",
+	    "Input device supported LED events");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "snd_bits", CTLFLAG_RD,
+	    evdev->ev_snd_flags, sizeof(evdev->ev_snd_flags), "",
+	    "Input device supported sound events");
+
+	SYSCTL_ADD_OPAQUE(&evdev->ev_sysctl_ctx,
+	    SYSCTL_CHILDREN(ev_sysctl_tree), OID_AUTO, "sw_bits", CTLFLAG_RD,
+	    evdev->ev_sw_flags, sizeof(evdev->ev_sw_flags), "",
+	    "Input device supported switch events");
+}
+
 static int
 evdev_register_common(struct evdev_dev *evdev)
 {
@@ -235,6 +318,12 @@ evdev_register_common(struct evdev_dev *evdev)
 
 	/* Create char device node */
 	ret = evdev_cdev_create(evdev);
+	if (ret != 0)
+		goto bail_out;
+
+	/* Create sysctls (for device enumeration without /dev/input access rights) */
+	evdev_sysctl_create(evdev);
+
 bail_out:
 	return (ret);
 }
@@ -271,6 +360,8 @@ evdev_unregister(struct evdev_dev *evdev)
 	int ret;
 	debugf(evdev, "%s: unregistered evdev provider: %s\n",
 	    evdev->ev_shortname, evdev->ev_name);
+
+	sysctl_ctx_free(&evdev->ev_sysctl_ctx);
 
 	EVDEV_LOCK(evdev);
 	evdev->ev_cdev->si_drv1 = NULL;

Modified: head/sys/dev/evdev/evdev_private.h
==============================================================================
--- head/sys/dev/evdev/evdev_private.h	Sun Feb 24 17:23:55 2019	(r344493)
+++ head/sys/dev/evdev/evdev_private.h	Sun Feb 24 18:47:04 2019	(r344494)
@@ -35,6 +35,7 @@
 #include <sys/malloc.h>
 #include <sys/queue.h>
 #include <sys/selinfo.h>
+#include <sys/sysctl.h>
 
 #include <dev/evdev/evdev.h>
 #include <dev/evdev/input.h>
@@ -131,6 +132,9 @@ struct evdev_dev
 	/* Parent driver callbacks: */
 	const struct evdev_methods * ev_methods;
 	void *			ev_softc;
+
+	/* Sysctl: */
+	struct sysctl_ctx_list	ev_sysctl_ctx;
 
 	LIST_ENTRY(evdev_dev) ev_link;
 	LIST_HEAD(, evdev_client) ev_clients;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201902241847.x1OIl4mW066694>