Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp4527772imm; Tue, 11 Sep 2018 13:20:59 -0700 (PDT) X-Google-Smtp-Source: ANB0VdaGXcscAmtyrIvAJHnvUmUSjI0Ee7YWC9S7JCrDvewMSUV/F1kZ54rvf4YmvWGV2ytVGzT2 X-Received: by 2002:a17:902:848d:: with SMTP id c13-v6mr29213598plo.194.1536697259121; Tue, 11 Sep 2018 13:20:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536697259; cv=none; d=google.com; s=arc-20160816; b=id8AiX9/cfLZkrf+pNrL/MR1NB3T+v8snfqfnKvwbjN6JQnzhc3eBe+oDy44+HUNr+ WR2B3nM0B54kzZLRFIwknQ/uZ5k5qMjJzZO6569UZJToYfdVGqjRjLxTUemy1+jmzsUL g+sjvCzl/2jkyCFqSnyhqZn4EGDC3H9Te2OnsaZa4AXCkFcBZF7uW2JeDnunS59oE8BJ WQarZSGMi33OddzE6XDiBifHamvsL66Fl+2auWhRsvlQNCPTgHhYJ8tLybGK2Lzz0GZm MpRn/NSRqGd8fhN5+Q3w8YwVGBddEfD/xJvEBQha9Gn9GkDAj4NRgtrO4+3eWRV6AjQW dAFg== 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; bh=YkAOEMn3R5I5U/6bxXsgtXVMzaJR4NyRX8Xu1mGiiHs=; b=MJ13A3iPdvaNuVMNgVKFe6JrQiwSl2pXwvcxH3eWakWKBR0cSzsgyg2FibjZVyUU7e HFX6UzYQ5CGScYgTatMwFbm8baWj2seXz8Cmwjthk2loU4mvDJlJrS7KNDepn6bkb75O anF7qjNGroHnIsuPVG1JGvXD5lnJnBeR4JbA4Ezoh/oI5Bccp9x2jufFtUYUF1YHYeLH EO3Lbm6QnCYH7HD8cevLr2SCSIdL0d5xtBvcUT6asnuv21mCSf7fMykNNvtC5b+QjwfS edHv0VpJrqFwzHRO0Tnt2kIaD8n0i28g5jF40O1y3rdmohZKHfPYoM9Agy5Fwt+47TGe hB6A== 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 a23-v6si21017803pgk.673.2018.09.11.13.20.43; Tue, 11 Sep 2018 13:20:59 -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 S1726932AbeILBTv (ORCPT + 99 others); Tue, 11 Sep 2018 21:19:51 -0400 Received: from relay3-d.mail.gandi.net ([217.70.183.195]:57377 "EHLO relay3-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726740AbeILBTv (ORCPT ); Tue, 11 Sep 2018 21:19:51 -0400 X-Originating-IP: 88.190.179.123 Received: from localhost (unknown [88.190.179.123]) (Authenticated sender: repk@triplefau.lt) by relay3-d.mail.gandi.net (Postfix) with ESMTPSA id D68D060005; Tue, 11 Sep 2018 20:18:52 +0000 (UTC) From: Remi Pommarel To: Greg Kroah-Hartman , Jiri Slaby , linux-kernel@vger.kernel.org Cc: Alexander Viro , Kees Cook , Elie Roudninski , Remi Pommarel Subject: [PATCH 4/4] drivers/tty/vt: add ioctl to manage input specific keyboard configs Date: Tue, 11 Sep 2018 22:23:59 +0200 Message-Id: <25bdc248f493fe89b29874c4627f361c2814e5b5.1536695071.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