# user-space virtual terminal configuration

The components of user-space virtual terminals are configurable.

The configuration mechanism allows the system administrator flexibility in user virtual terminal configuration policy, ranging from a loose but risky configuration to a secure but tight one.

## Policy

### Countering malicious HIDs

The old model of kernel virtual terminals is that any and all attached input devices are automatically merged into a single input stream, as if there were a single keyboard and mouse. (See kbdmux(4), sysmouse(4), and moused(8) on FreeBSD, for example.)

This has several problems for the privacy and security conscious, in that any human-input device can be plugged into the system by anyone with merely exterior physical access to the system. Such devices can include "mouse jigglers" that generate tiny fake mouse motions to make it seem that a user is physically still present and "bad USB" devices that in various ways (too many to list here) act under the covers as illicit USB Human Input Devices that inject keystrokes and mouse events to perform various actions.

N.B.: The exterior physical access allowing attackers to (covertly) plug in a PS/2 keyboard or PS/2 mouse provides equal ability to generate malicious input. This is not a problem that is limited to USB. It is simply more commonly exploited, and made much easier to disguise from victims, in the USB world.

The configuration system for user-space virtual terminals allows one to set up things in a loose fashion, that risks malicious USB or PS/2 input, or in a tighter fashion, at the expense of having to adjust the system configuration every time that one physically connects USB or PS/2 devices differently.

### Accommodating hetereogeneous HIDs

The old model of kernel virtual terminals is that all keyboards share a single keyboard map, and affect a single modifier state. This means that a modifier key on any keyboard modifies the other keys on that and any other keyboard; that all keyboards share a single modifier lock (and thus keyboard lights) state.

The configuration system for user-space virtual terminals allows one to set up things such that which keyboard map is used for a device is based upon what the device's Vendor/Product IDs are, or on what position on the bus it is plugged in.

### Coping with oddball HIDs

There exist various kinds of USB HID — namely add-on numeric keypads, mice-with-keypads, "calculator keypads" that are fully functional standalone calculators, and similar "calculator mice" — that are sensitive to the NumLock LED state. They might not physically have lights, but pretend to do so in order to receive messages from the host whenever the NumLock state changes.

Some "calculator" devices, where the numeric keypad does not double up as cursor/editing keys in the same way as on a PC keyboard, will take the current state into account and attempt to fake NumLock keypresses in order to send the intended keystrokes. Other "calculator" devices will refuse to enter their host-controlled "PC" mode unless NumLock is on (but ironically lack a NumLock key of their own for turning it on — the shared modifier state when all keyboards are combined being their saving grace, allowing NumLock to be turned on by another keyboard).

The configuration system for user-space virtual terminals detects devices that declare that they have a NumLock LED but do not have a NumLock key, and automatically forces the NumLock state on for such devices. It also allows for such devices to have their own private keyboard modifier states if desired, so that this automatic force of NumLock does not propagate to other keyboard devices.

## Mechanisms

Reconfiguring user-space virtual terminal components is a matter of adjusting a service configuration and restarting the service, which can be done on the fly without affecting the applications running in any TUI login sessions.

### multiplexor services

A `console-multiplexor@name` service bundle expects to find symbolic links matching the shell wildcard `vc[0-9]*` in its service directory, pointing to the virtual terminals that it is multiplexing amongst. These are passed on the command line to `console-multiplexor` as the virtual terminals for it to multiplex into the `namemux` virtual terminal.

### input method services

A `console-input-method@upper` service bundle is configured with several (service-private) environment variables:

`lower`

  the name of the lower virtual terminal

`chinese1`

  the optional name of the CIN data file to use for the 1st Hanzi/Kanji/Hanja conversion mode

`chinese2`

  the optional name of the CIN data file to use for the 2nd Hanzi/Kanji/Hanja conversion mode

`kana`

  the optional names of the two CIN data files to use for the Hiragana and Katakana conversion modes

`hangeul`

  the optional names of the CIN data file to use for the Hangeul conversion mode

`romaji`

  the optional names of the CIN data file to use for the Romaji conversion mode

These and upper are combined and passed on the command line to `console-input-method`.

CIN data files are mainly third party terminal resources. that must be obtained and configured manually.

### realizer services

A `console-fb-realizer@fbdev` service bundle is configured with several (service-private) environment variables:

`FONTS`

  the optional command-line options and arguments specifying font files to load

  For example: One might want to use the 16×16 Commodore PET character set for regular medium characters, the 16×16 BBC Micro character set for regular bold characters (both BBC and PET character sets taken from the 2.1.1f fork of Unscii), fall back to Unscii version 2.1.1f where possible for other medium characters, fall back to GNU Unifont version 16 after that, and use the narrower UbuntuMono for faint and italic. For this, one would have a `FONTS` setting like (all one line):

  > `--vtfont-regular-r fonts/unscii-16-pet-and-bbcg.fnt --vtfont-regular-r fonts/unscii-16.fnt --vtfont-regular-r fonts/unifont-16.0.03.fnt --vtfont-regular-r fonts/unifont_upper-16.0.03.fnt --vtfont-regular-i fonts/UbuntuMono-r.fnt --vtfont-faint-r fonts/UbuntuMono-i.fnt`

  N.B.: The old home computer character sets were designed for square aspect ratio character cells, and work particularly well with the framebuffer realizer. The PET font has a thinner stroke width than the BBC font, and they together work well as a medium and bold pair. Unscii 2.1.1f employs other tricks like using the Dragon 32/64 font for Unicode's "monospace" mathematical-formula characters, giving them a visual distinction that GNU Unifont does not.

These and fbdev are combined and passed on the command line to `console-fb-realizer`.

A `console-kvt-realizer@kvt` service bundle is configured with several (service-private) environment variables:

`FONTS`

  the optional command-line options and arguments specifying font files to load

`FLAGS`

  any ancillary flags (e.g. `--mouse-primary`)

These and kvt are combined and passed on the command line to `console-kvt-realizer`.

Realizers also employ user-space virtual terminal configuration, thus relying upon `kbdmaps`, `fonts`, `keyboards-aggregate`, `mice-aggregate`, and `vcs` subdirectories of their service directories. Conventionally these are all symbolic links to subdirectories of the same names under `/etc/system-control/convert/user-vt/`, providing a single global place to tweak configuration. In turn, the global configuration is set up so that the relevant `/etc/system-control/convert/user-vt/vcs/something` autoconfiguration symbolic links point to `/run/dev/head0` for head0.

Keyboard files are mainly third party terminal resources. that are imported by the external configuration import mechanism.

## Choices of approach

### Replicating the old model, risks included

Replicating the old model involves creating all of the final default user-space virtual terminal configuration settings, so that all realizers are connected to a single keyboard state file, a single mouse state file, a single keyboard map, and the same user-space virtual terminal directory. The simplest way to achieve this is for every realizer's `kbdmaps`, `fonts`, `keyboards-aggregate`, `mice-aggregate`, and `vcs` subdirectories of their service directories to point to a single global set of such directories, where each has a `default` file (for state aggregation) or symbolic link (for keyboard maps and virtual terminal pointers).

More explicitly: In every realizer's service directory `kbdmaps`, `fonts`, `keyboards-aggregate`, `mice-aggregate`, and `vcs` are symbolic links to subdirectories of the same name under (for example) `/etc/system-control/convert/user-vt/`, and in that tree, in turn:

- `kbdmaps/default` is a symbolic link to the single all-keyboards keyboard map, e.g. `../../kbdmaps/uk.105.kbd`;

- `keyboards-aggregate/default` is a single shared regular file;

- `mice-aggregate/default` is a single shared regular file; and

- `vcs/default` is a symbolic link to the single all-HIDs virtual terminal, e.g. `/run/dev/head0` for head0.

N.B.: The problems of this loose accept-all-devices model have been known since the 2010s. The nosh toolkit does not configure this model out of the box. Out of the box, no (not already aggregated) USB, PC/AT, or PS/2 input device will be automatically attached to any virtual terminal.

Caution: HID aggregators like FreeBSD's `/dev/sysmouse` or `/dev/kbdmux0` have these security problems, and are best disabled wherever possible and avoided.

### The fully "opt-in" model

Since user-space virtual terminal configuration settings can be based upon specific device addresses, instead of using `default` settings everywhere, specific devices can be configured based upon their connectivity. So the system can be configured such that only a human-input device plugged into one exact single physical port will be connected to `/run/dev/head0` by (for example):

- `kbdmaps/usb.ugen.B0004.A0004` is a symbolic link to that specific port's keyboard map, e.g. `../../kbdmaps/jp.109.kbd`;

- `keyboards-aggregate/usb.ugen.B0004.A0004` is a regular file;

- `mice-aggregate/usb.ugen.B0004.A0004` is a regular file; and

- `vcs/usb.ugen.B0004,A0004` is a symbolic link to `/run/dev/head0`.

N.B.: Although all of the configuration items are necessary, the primary line of defence against malicious HIDs is not configuring a virtual terminal setting. However, one should not use loose `default` (or even unqualified `usb.ugen` and `usb.hid`) configurations for keyboard and mouse state files. Malicious devices should not be able even to affect keyboard and mouse states.

Extending this to additional specific keyboards/mice is a simple matter of adding other bus/address combinations:

- `kbdmaps/usb.ugen.B0004.A0003` is a symbolic link to that specific port's keyboard map, e.g. `../../kbdmaps/jp.109.kbd`;

- `keyboards-aggregate/usb.ugen.B0004.A0003` is a regular file;

- `mice-aggregate/usb.ugen.B0004.A0003` is a regular file; and

- `vcs/usb.ugen.B0004.A0003` is a symbolic link to `/run/dev/head0`.

This fully isolates the additional keyboard/mouse device. It shares no state with the original. If this is not desired, then the two `usb.ugen.B0004.A0004` and `usb.ugen.B0004.A0003` state files in each of `keyboards-aggregate/` and `mice-aggregate/` should be links to a single multiply-linked regular file.

Indeed, if this is not done then modifier keys (stored in the keyboard state file) will not affect mouse input unless a device is itself a combined keyboard+mouse device in one; and even then only modifier keys (if any) on the device itself will affect its mouse input. A general rule of thumb is that if the `vcs/configpath` symbolic links for multiple HIDs point to the same user-space virtual terminal, generally the keyboard and mouse state files for those HIDs will be links to a single regular file (each).

Caution: Keyboard and mouse state files are not interchangeable. Do not link them to each other. Only link multiple keyboard state files together; and likewise for mouse state files.

### When to use different keyboard maps for different keyboards

In the aforegiven fully "opt-in" examples, each device has its own keyboard map. If all keyboards ever attached are likely to be the same type (out of 104-key, 105-key, 106-key, 107-key, and 109-key) and country layout, then this can be reduced to a single `kbdmaps/default` symbolic link as the old all-merged model. However, one might have two or more keyboards of different types. These can be auto-assigned individual layouts.

If, for example, one had both an ABNT2 keyboard from one vendor and a Japanese keyboard from another:

- `kbdmaps/usb.ugen.B0004.A0003.V03f0.P0024` would be a symbolic link to the Brazilian 106-key keyboard map, i.e. `../../kbdmaps/br.106.kbd`; and

- `kbdmaps/usb.ugen.B0004.A0004.V04f3.P0103` would be a symbolic link to the Japanese 109-key keyboard map, i.e. `../../kbdmaps/jp.109.kbd`; and

N.B.: This is an extreme example for illustrative purposes, although it was actually a test case in development. But more realistic examples are more common than one might think. In particular, whilst one might have (say) a full-sized European 105-key keyboard with a German keyboard map, many add-on "air mouse" and "wireless miniature keyboard+trackball" devices only come in U.S. 104-key forms. (The 105th key is the one nominally at ISO/IEC 9995-3 position B00.) A single universal map would mean either applying the German layout to the 104-key device, or applying a U.S. layout to the 105-key device.

### Shared and unshared frame buffers, and the primary mouse

As the console-kvt-realizer/console-fb-realizer manual explains, sometimes the framebuffer is dedicated to only realizing a user-space virtual terminal, and sometimes it is a shared resource that is also accessed by either an X11 server or an actual kernel virtual terminal subsystem. The latter is almost always the case on FreeBSD; although on Linux it is possible to have isolated framebuffer devices not assigned to either X11 or KVTs.

When multiple realizers share a single mouse state file, exactly one realizer service must be run with the `--mouse-primary` command-line option. This is the realizer that transmits the actual mouse events to the input FIFO of the virtual terminal. With fewer than one primary mouse, no mouse events will be sent; with more than one primary mouse, duplicate mouse events, especially clicks, will be sent.

When console-kvt-realizer is being employed, this is the obvious choice for primary mouse realizer.