Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp911291imm; Wed, 22 Aug 2018 15:02:34 -0700 (PDT) X-Google-Smtp-Source: ANB0VdYiu1VxV9Yqqa5A4PLnaSn3dwRkmiYQQUbzrxsCQ2P8bBiv5yEl6oSNyKk4pR3PfWEjrLAf X-Received: by 2002:a63:ed07:: with SMTP id d7-v6mr1280346pgi.429.1534975354178; Wed, 22 Aug 2018 15:02:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1534975354; cv=none; d=google.com; s=arc-20160816; b=cuyigjZIhgPCtAKhWPPO0/+lIyD6TEx8sdHP3oyqB05w2gYLeqTMP4g2AqJiCjstZO dr4Ow2KSdUj9RjoXEpKxTSh1/Gi3yRMsPC9wQO++zT/826Y1qGO4wgRuISKcES4gSLUz AV7ijebdM4IUjWtwlaNYuTrgbgScIvvnBgyXrzLfr8y+gojp7hdJL8WET9qTPqxiJe91 b4Ju6cW51hB4kFYTdIXSebTPwnLYbiZl0INcWQM5i7pmAMgmP1tXNW8tl2EqoPsuKifw Fxg52PqqtSSZd0GxktnVxAhNNB75qEy5hOfSQ5YwiORSXu3BFuL8wsc4b71RMxyIPkRa M8Lg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=YkAOEMn3R5I5U/6bxXsgtXVMzaJR4NyRX8Xu1mGiiHs=; b=R3PqNYp78S6obCBVJVF71O1u4TX+MwpGsRlyRWx6vEKn4lHUe1jV9rIX0rhi68s+cM q8/AAvqVis1Kg+1Az/l+/6EZCDU5xSl7yJa0/vBRNHHZKY04V+q/NL9VyDKnm5AJ8vgG m/o87jfSQD2D7L4zdlOb1cPtY2j46h3zZR/o+cGSsFbTqFEymQ/WSKkFMB4zItcPxchF tvNux/Gga5iqLTGBkjs+Sw7lvgwHwMG9xmNA9aE4+0OZlzlRHSligxCsPyFBlb4R9zZA osMsZsFchxR4O4Hh6hUxQUlyAyz3S6Gup3EFqUE1QSy+jKNbHvjwDsNq6g1Op7602Ykz y4Sg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b25-v6si2547594pgf.545.2018.08.22.15.02.18; Wed, 22 Aug 2018 15:02:34 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728317AbeHWB1D (ORCPT + 99 others); Wed, 22 Aug 2018 21:27:03 -0400 Received: from mslow2.mail.gandi.net ([217.70.178.242]:32892 "EHLO mslow2.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727612AbeHWB0w (ORCPT ); Wed, 22 Aug 2018 21:26:52 -0400 Received: from relay1-d.mail.gandi.net (unknown [217.70.183.193]) by mslow2.mail.gandi.net (Postfix) with ESMTP id 9C5AF3A0034 for ; Wed, 22 Aug 2018 23:43:38 +0200 (CEST) X-Originating-IP: 88.190.179.123 Received: from localhost (unknown [88.190.179.123]) (Authenticated sender: repk@triplefau.lt) by relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 4EAE7240002; Wed, 22 Aug 2018 21:43:36 +0000 (UTC) From: Remi Pommarel To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , Jiri Slaby , Alexander Viro , Kees Cook , Elie Roudninski , Remi Pommarel Subject: [RFC PATCH 4/4] drivers/tty/vt: add ioctl to manage input specific keyboard configs Date: Wed, 22 Aug 2018 23:50:35 +0200 Message-Id: <52a9a482f5b45fe37046f436012c6e426fdfca27.1534972814.git.repk@triplefau.lt> X-Mailer: git-send-email 2.18.0 In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Because user can use different keyboards with different layouts on the same tty, an input could have a different key map from another one. In order to use and modify this specific key map the user can call three new ioctls: 1) KDGKBIENT Get an input key map's entry. If the input does not have a specific keyboard config the requested entry is fetched from global key map. 2) KDSKBIENT Set an input key map's entry. If the input does not yet have a specific keyboard config, a new one is created with the current global config (the one modified with KDSKBENT) content copied in. 3) KDSKBIRST Reset an input key map. The input does not use a specific keyboard config anymore and keycode translations are done with global key map. In order to remain compatible with old behavior, an input uses global key map by default and KDGKENT/KDSKENT still get/set entries in the global key map. Below is an example of this ioctl usage: ------------------------- 8< ----------------------------- struct kbientry kbi = { .id = { /* Input is found by its input_id */ .bustype = 0x0003, .product = 0x00B2, .vendor = 0x2319, .version = 0x0200, }, .entry = { /* Entry to set or get */ .kb_table = 0, .kb_index = 0x10, .kb_value = 0x0B61, }, }; int ret; /* First set new entry for input 0003:00B2:2319:0200 */ ret = ioctl(fd, KDSKBIENT, &kbi); /* fd is a tty open file */ if(ret < 0) /* Error handling */ /* Get specific entry, on success kbi.entry.kb_value will be set */ ret = ioctl(fd, KDSKBIENT, &kbi); if(ret < 0) /* Error handling */ /* Reset input key map so that global key_maps is used */ ret = ioctl(fd, KDSKBIRST, &kbi.id); if(ret < 0) /* Error handling */ ------------------------- 8< ----------------------------- Signed-off-by: Remi Pommarel Tested-by: Elie Roudninski --- drivers/tty/vt/keyboard.c | 126 ++++++++++++++++++++++++++++++++++++++ drivers/tty/vt/vt_ioctl.c | 9 +++ fs/compat_ioctl.c | 3 + include/linux/vt_kern.h | 4 ++ include/uapi/linux/kd.h | 9 +++ 5 files changed, 151 insertions(+) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 7272e1828838..9707d1ebeab4 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -2180,6 +2180,132 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, return ret; } +/* + * Input keymap helper functions + */ +struct kbd_lookup_data { + struct input_id *id; + struct kbd_handle *kh; +}; + +static int _kbd_get_helper(struct input_handle *handle, void *data) +{ + struct kbd_handle *kh = hdl_to_kbd_handle(handle); + struct kbd_lookup_data *d = data; + + if (memcmp(d->id, &handle->dev->id, sizeof(*d->id)) != 0) + return 0; + + d->kh = kh; + kref_get(&kh->ref); + return 1; +} + +/* + * Get an input specific handle. + * The caller will hold a reference on the found kbd_handle, if it exists, and + * should deref it after usage (with kbd_put). + */ +static int kbd_get(struct kbd_handle **kh, struct input_id *id) +{ + struct kbd_lookup_data d = { + .id = id, + }; + int ret; + + ret = input_handler_for_each_handle(&kbd_handler, &d, _kbd_get_helper); + *kh = d.kh; + + return (ret == 0); +} + +static void kbd_put(struct kbd_handle *kh) +{ + kref_put(&kh->ref, kbd_destroy); +} + +int vt_do_kdski_ioctl(int cmd, struct kbientry __user *user_kbie, int perm, + int console) +{ + struct kbd_struct *kb = kbd_table + console; + struct kbd_handle *kh; + struct kbientry tmp; + ushort val; + int ret = 0; + + if (copy_from_user(&tmp, user_kbie, sizeof(struct kbientry))) { + ret = -EFAULT; + goto out; + } + + if (!capable(CAP_SYS_TTY_CONFIG)) + perm = 0; + + ret = kbd_get(&kh, &tmp.id); + if (ret != 0) { + ret = -EINVAL; + goto out; + } + + switch (cmd) { + case KDGKBIENT: + val = kc_getent(kb, kh->conf, &tmp.entry); + ret = put_user(val, &user_kbie->entry.kb_value); + break; + case KDSKBIENT: + if (!perm) { + ret = -EPERM; + break; + } + /* A keyconf's keymap is created only if global one is used */ + ret = kbd_detach_conf(kh); + if (ret != 0) + break; + + ret = kc_setent(kb, kh->conf, &tmp.entry); + break; + } + + kbd_put(kh); +out: + return ret; +} + +int vt_do_kdskirst_ioctl(int cmd, struct input_id __user *user_iid, int perm, + int console) +{ + struct kbd_handle *kh; + struct input_id tmp; + unsigned long flags; + int ret; + + if (copy_from_user(&tmp, user_iid, sizeof(struct input_id))) + return -EFAULT; + + if (!capable(CAP_SYS_TTY_CONFIG)) + perm = 0; + + if (!perm) + return -EPERM; + + ret = kbd_get(&kh, &tmp); + if (ret != 0) { + ret = -EINVAL; + goto out; + } + + /* Restore keyboard's configuration to global one */ + spin_lock_irqsave(&kbd_event_lock, flags); + kbd_destroy_conf(kh); + kbd_init_conf(kh); + spin_unlock_irqrestore(&kbd_event_lock, flags); + + kbd_put(kh); + +out: + return ret; +} + /* FIXME: This one needs untangling and locking */ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) { diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index a78ad10a119b..ec688d81bab3 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -542,6 +542,15 @@ int vt_ioctl(struct tty_struct *tty, ret = vt_do_kdsk_ioctl(cmd, up, perm, console); break; + case KDGKBIENT: + case KDSKBIENT: + ret = vt_do_kdski_ioctl(cmd, up, perm, console); + break; + + case KDSKBIRST: + ret = vt_do_kdskirst_ioctl(cmd, up, perm, console); + break; + case KDGKBSENT: case KDSKBSENT: ret = vt_do_kdgkb_ioctl(cmd, up, perm); diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a9b00942e87d..7863b142234c 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -789,6 +789,9 @@ COMPATIBLE_IOCTL(KDGKBDIACR) COMPATIBLE_IOCTL(KDSKBDIACR) COMPATIBLE_IOCTL(KDGKBDIACRUC) COMPATIBLE_IOCTL(KDSKBDIACRUC) +COMPATIBLE_IOCTL(KDGKBIENT) +COMPATIBLE_IOCTL(KDSKBIENT) +COMPATIBLE_IOCTL(KDSKBIRST) COMPATIBLE_IOCTL(KDKBDREP) COMPATIBLE_IOCTL(KDGKBLED) COMPATIBLE_IOCTL(KDGETLED) diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 3fd07912909c..0f17d305b840 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -175,6 +175,10 @@ extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm); extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, int console); +extern int vt_do_kdski_ioctl(int cmd, struct kbientry __user *user_kbie, + int perm, int console); +extern int vt_do_kdskirst_ioctl(int cmd, struct input_id __user *user_iid, + int perm, int console); extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm); extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm); diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h index 4616b31f84da..be048e9e3f02 100644 --- a/include/uapi/linux/kd.h +++ b/include/uapi/linux/kd.h @@ -3,6 +3,7 @@ #define _UAPI_LINUX_KD_H #include #include +#include /* 0x4B is 'K', to avoid collision with termios and vt */ @@ -137,6 +138,14 @@ struct kbdiacrsuc { #define KDGKBDIACRUC 0x4BFA /* read kernel accent table - UCS */ #define KDSKBDIACRUC 0x4BFB /* write kernel accent table - UCS */ +struct kbientry { + struct input_id id; + struct kbentry entry; +}; +#define KDGKBIENT 0x4B53 /* Get one entry in input's translation table */ +#define KDSKBIENT 0x4B54 /* Set one entry in input's translation table */ +#define KDSKBIRST 0x4B55 /* Set input to use global translation table */ + struct kbkeycode { unsigned int scancode, keycode; }; -- 2.18.0