Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kloak in dom0 #8541

Closed
DemiMarie opened this issue Sep 21, 2023 · 10 comments
Closed

Kloak in dom0 #8541

DemiMarie opened this issue Sep 21, 2023 · 10 comments
Labels
C: Whonix This issue impacts Qubes-Whonix P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. privacy This issue pertains to data or information privacy through technological means.

Comments

@DemiMarie
Copy link

How to file a helpful issue

The problem you're addressing (if any)

#8534 is going to break with the Wayland agent, and likely has synchronization problems with the X11 agent.

Xorg has its own idea of what the window stacking order is. It is absolutely not wanted here, but getting rid of it requires changes to Xorg that nobody is interested in doing. The (partial) workaround is to raise a window before sending events to it. However, this requires that sending events to the X server is synchronized with manipulating the X11 window stacking order. This is only possible if event delivery is synchronous, not if it is asynchronous. Additionally, the X11 GUI agent is going to eventually be replaced by a Wayland compositor, currently at https://github.com/DemiMarie/qubes-compositor. The Wayland compositor does not kernel input events at all, and does not even have a driver to communicate with the kernel input subsystem.

More fundamentally, the GUI agent is the wrong place to solve this — if someone compromises the VM, they will still be able to get precise input events, compromising anonymity.

The solution you'd like

Run Kloak on the host, or in the GUI daemon. This ensures that anonymization is done before the (potentially compromised) guest gets access to the events. It also solves the event routing problem.

The value to a user, and who that user might be

Qubes-Whonix users will benefit from improved anonymity.

@DemiMarie DemiMarie added T: enhancement C: Whonix This issue impacts Qubes-Whonix P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. labels Sep 21, 2023
@andrewdavidwong
Copy link
Member

Is this a duplicate of #1850?

@andrewdavidwong andrewdavidwong added the privacy This issue pertains to data or information privacy through technological means. label Sep 22, 2023
@adrelanos
Copy link
Member

Run Kloak on the host, or in the GUI daemon.

This would be nice but I wouldn't have dared to suggest it. Because many users may not want this for non-Whonix VMs. Reasons:

  • Some websites (such as banks) use keystroke dynamics as part of user identification / automatic compromise detection.
  • If kloak breaks, non-users of Whonix would not want their non-Whonix VM input devices being broken.
  • Even Whonix users wouldn't want their non-Whonix VMs broken in case of kloak issues.
  • We'd need Fedora packaging (probably doable) and the package maintained in Qubes dom0 repository?

Could the GUI agent interface with kloak and only apply keystroke anonmyization on an opt-in base (using qvm-service)?

Is this a duplicate of #1850?

Perhaps a sub ticket? Depends how much related it can be before being a duplicate. This issue seems complicated enough to warrant a separate ticket. How the input device is (not) accessible from the VM / dom0 could be independently designed from #1850.

@DemiMarie
Copy link
Author

Run Kloak on the host, or in the GUI daemon.

This would be nice but I wouldn't have dared to suggest it. Because many users may not want this for non-Whonix VMs. Reasons:

  • Some websites (such as banks) use keystroke dynamics as part of user identification / automatic compromise detection.
  • If kloak breaks, non-users of Whonix would not want their non-Whonix VM input devices being broken.
  • Even Whonix users wouldn't want their non-Whonix VMs broken in case of kloak issues.
  • We'd need Fedora packaging (probably doable) and the package maintained in Qubes dom0 repository?

If my understanding is correct, the current implementation of kloak fundamentally assumes that input events are routed via the kernel’s input event system. Qubes OS does not (and, in the long term, cannot) use the kernel event system outside of dom0 and sys-gui. #8534 will break once Qubes OS switches to Wayland. Therefore, kloak as it is currently implemented can only be used system-wide or not at all, hence this issue.

Could the GUI agent interface with kloak and only apply keystroke anonmyization on an opt-in base (using qvm-service)?

Yes, but this would require kloak be changed to not rely on the kernel event subsystem. A much better approach would be for kloak to either a library that the GUI daemon communicated with via function calls, or a separate process that the GUI daemon communicated with via pipes, sockets, or other IPC.

Is this a duplicate of #1850?

Perhaps a sub ticket? Depends how much related it can be before being a duplicate. This issue seems complicated enough to warrant a separate ticket. How the input device is (not) accessible from the VM / dom0 could be independently designed from #1850.

Qubes VMs have no input devices. The GUI daemon directs events to specific windows, not to specific coordinates in a global coordinate space. This is made much more obvious by Wayland, where there is no global coordinate space, and instead the Wayland compositor dispatches events directly to applications. Wayland, not X11, is the future, so kloak needs to fit in to Wayland. That means operating as a plugin for the GUI agent and/or GUI daemon.

@skyzzuu
Copy link

skyzzuu commented Sep 25, 2023

Would the following implementation be doable? I could be wrong, but it seems like it wouldn't require any changes to the gui daemon, and if kloak is kept separate from the gui daemon and new features are added, kloak could easily just be upgraded to the newest version instead of needing to change the gui daemon again to implement them.

kloak package that can be optionally installed in dom0
kloak package that can be optionally installed in the fedora templates that sys-usb is based off

qvm-service in sys-usb called kloak

In sys-usb some changes are needed as after qubes-input-sender has grabbed the input devices, kloak will then try to grab them and run into issues causing it to exit, but the output devices created by kloak still need qubes-input-sender.

I was thinking something like the following:

  1. If the kloak qvm-service is enabled and the kloak tag is not in udevreturn (original input device), start the new function that runs kloak
  2. else:
  3. run handle_service as usual to run qubes-input-sender

I haven't experimented with sys-gui yet so I'm not as familiar with how it works, but depending on whether it uses qubes-input-sender like sys-usb or not, I would think it could either just have kloak installed with no changes other than making sure the kloak qvm-service is enabled in it, or have the changes mentioned in the sys-usb section above.

@3hhh
Copy link

3hhh commented Jan 13, 2024

I don't understand the focus on kloak - it's a relatively simple application that randomizes the delay between input events.

With e.g. some plugin support for the Qubes OS keystroke handling code (wherever it may run) it should be fairly simple to replicate the kloak functionality.

Imho it's a bad idea to to force something to work with Qubes OS that was apparently not designed with Qubes OS in mind.

@3hhh
Copy link

3hhh commented Jan 13, 2024

On a side note: If qrexec is used to transport the keystrokes (I currently don't know), it may even be possible to use something like my https://github.com/3hhh/qubes-qrexec-proxy already.

@ArrayBolt3
Copy link

@DemiMarie

Yes, but this would require kloak be changed to not rely on the kernel event subsystem. A much better approach would be for kloak to either a library that the GUI daemon communicated with via function calls, or a separate process that the GUI daemon communicated with via pipes, sockets, or other IPC.

It sounds like what you're describing here is running kloak "as a service" in dom0 that provides one-way communication to individual qubes as needed?

The current architecture of kloak requires the use of evdev because there simply isn't any better spot to put an input device "mangler". The X server, to my awareness, lacks a way to put a process like kloak between the server and the applications, so you have to go underneath the server. The server communicates via evdev, so kloak has to be inserted there. The problem with that is that individual VMs won't have evdev anymore it sounds like, and so kloak can't be put there. Putting it straight on the host is all-or-nothing - you either obfuscate input everywhere or not at all. Trying to make kloak capture all input but then selectively deliver it to VMs sounds prohibitively difficult since then it has to "figure out" what VM the user is trying to communicate with, which is very difficult since it resides at the evdev layer and currently has no knowledge of what goes on above.

I think I have to agree with @3hhh here - the functionality offered by kloak should probably be replicated but in a way that works with Qubes OS's architecture. So, with that in mind, I did some digging, and found the following function in the GUI daemon:

// In qubes-gui-daemon/gui-daemon/xside.c
/* handle local Xserver event: XKeyEvent
 * send it to relevant window in VM
 */
static void process_xevent_keypress(Ghandles * g, const XKeyEvent * ev)
{
    struct msg_hdr hdr;
    struct msg_keypress k;
    CHECK_NONMANAGED_WINDOW(g, ev->window);
    update_wm_user_time(g, ev->window, ev->time);
    if (is_special_keypress(g, ev, vm_window->remote_winid))
        return;
    k.type = ev->type;
    k.x = ev->x;
    k.y = ev->y;
    k.state = ev->state;
    k.keycode = ev->keycode;
    hdr.type = MSG_KEYPRESS;
    hdr.window = vm_window->remote_winid;
    write_message(g->vchan, hdr, k);
//      fprintf(stderr, "win 0x%x(0x%x) type=%d keycode=%d\n",
//              (int) ev->window, hdr.window, k.type, k.keycode);
}

Similar handlers exist for motion and click events.

To me it seems like all that has to be done to add kloak functionality here is to just make a single master "send" function that the other event handlers call instead of write_message - this function would check a config key for the VM that specified whether it was "kloaked" or not, immediately write the event if not, and store it (with a scheduled release time) in a master event queue if so. Then some other function would periodically check the queue for events that were ready to be released and write them to their intended VMs if ready. This could be implemented entirely within the GUI daemon, which would probably be simpler than making it a library or separate process since the GUI daemon will need substantial modifications to work with kloak-like functionality anyway.

@adrelanos
Copy link
Member

marmarek added a commit to QubesOS/qubes-gui-daemon that referenced this issue Nov 18, 2024
* origin/pr/149:
  Add event buffering for cloaking user input patterns
  Replace magic quotes with normal quotes in parse_vm_config
  Typo fix, change "canot" to "cannot"

Pull request description:

# Goal

Implement the functionality of [kloak](https://github.com/Whonix/kloak) (a tool designed to hide biometric behavior patterns in keystrokes and mouse movements) in qubes-gui-daemon. This PR will implement the functionality requested in QubesOS/qubes-issues#1850 and fleshed out further in QubesOS/qubes-issues#8541. It will also close QubesOS/qubes-issues#8534 as it will no longer be necessary.

# TODOs

* Test rigorously on Qubes R4.3 (an earlier iteration of the code has been smoke-tested on Qubes R4.2, this hasn't been tested at all on R4.3 yet)

# Fixed TODOs:

* Figure out why the domU window occasionally freezes until another input event is sent - we aren't buffering info coming from domU to dom0 so why this is happening is a mystery to me, and something for later investigation. (Solved, #149 (comment))
* Potentially change how events are treated (do some events have to operate in pairs for best results?). (Lots of X events are now not buffered in the latest implementation. Only ones that look valuable to buffer are buffered.)
* Make the delay duration user-configurable (right now it's hardcoded to 150 milliseconds). (Implemented.)
* Allow configuring event delay duration for individual VMs buffering (right now it is applied equally to all VMs). (Implemented.)
* Get the configuration code working and test it. (Solved, this ended up requiring a change to [core-admin-client](https://forum.qubes-os.org/t/cannot-change-qubes-gui-daemon-settings-using-qvm-features/29345/3) which I will be submitting as a separate PR.)
* Ensure all new code adheres to Qubes OS standards (didn't have time to finish that up) (should be done now)

# Rationale

Kloak, the inspiration for this PR, is a user input buffering and obfuscation tool. It intercepts keyboard and mouse events at the evdev layer, holds them in a queue for release at a later scheduled time, then releases them to the applications they were intended for periodically. By adding random noise into the user's input patterns, kloak aims to make otherwise recognizable patterns in user behavior (such as keystroke rhythm and mouse movement patterns) too erratic to be used as a method of identifying the user. This is potentially very useful especially for Whonix Workstation domUs, as it denies an adversary access to a remarkably effective biometric fingerprinting mechanism they could otherwise access without specialized tools.

Kloak is currently able to operate directly in Qubes domUs if (and only if!) `gui-agent-virtual-input-device` is enabled for the domU in question. Even in these instances, only keyboard events are anonymized, and additionally the domU must have an evdev X driver installed. This is less than ideal from a functionality standpoint, and as @DemiMarie has explained in QubesOS/qubes-issues#8541 it will eventually stop working entirely. There's also the possibility of malware compromise in the domU resulting in the deanonymization of the user. For these reasons, enabling the use of evdev in domUs and running kloak in the domU is not a good solution.

The other obvious option is to run kloak directly in dom0. This has several disadvantages:

* kloak can now potentially wreak havoc on the user's ability to use their computer. If a bug in kloak locks up the keyboard, or the user does something inadvisable like setting a 20-second event delay, regaining control of the system could be difficult or impossible without doing a hard reset (or worse, booting an external USB in order to chroot into dom0 and disable kloak).
* Application of kloak's functionality becomes all-or-nothing - you either anonymize all keyboard and mouse input everywhere, or you anonymize none of it. This could make management of dom0 annoying with larger delay times, and it could prevent the user from making use of applications or websites that require input pattern telemetry to function (such as some bank websites).
* kloak's configuration options similarly apply globally. One might want a comfortable delay of only 25ms in a domU they expect to be safe, but wish to use an extremely long one like 1000ms in a domU they believe is compromised and actively exfiltrating data. With kloak running directly in dom0, this is impossible.

This PR implements a third option - *inserting the functionality of kloak directly into qubes-gui-daemon.* kloak upstream never needs to be involved, only the functionality of it must be. This functionality I have termed "event buffering", and as this implementation works with X server events I have called it "event buffering", or "ebuf" for short (which is the term used for it in the code). Previously I had called this "X event buffering" and used "xbuf" for short, but as @3hhh pointed out that name would become inaccurate when this is ported to Wayland, so I changed it to "ebuf" so as to make the name be display server agnostic.

By working inside the GUI daemon, the following advantages are gained:

* No evdev support needed at all, we can work with X events instead.
* The amount of additional code needed is smaller.
* Per-VM application and configuration of event buffering is now possible - some VMs can use a small, comfortable delay, others can use a very long one, and others can skip delays entirely.
* Even if something goes very wrong and buffering prevents the user from inputting anything into any Qube, the user retains control of dom0 and can recover their system from there.
* A compromised domU with event buffering enabled will be less likely to leak valuable biometric info to the malware within the VM.

# How it works

Most of the code should be fairly self-explanatory. In a nutshell, we use a tail queue to store a list of delayed, scheduled X events. As events come from dom0 to a domU, they are captured, scheduled for release at a later time, and thrown into the queue. Events in the queue are regularly checked to see if their scheduled release time has arrived, and those events are released when appropriate. The scheduler inserts some random noise into the delays, making it difficult to uniquely identify the user's typing and mouse movement/usage patterns.

By default, event buffering is disabled and all events are passed through without buffering. To enable it, one must use `qvm-features` to set `gui-ebuf-max-delay` to a value greater than 0. It is worth noting that 0 is interpreted not as a "don't add any delay when buffering events", but rather it is interpreted as "don't buffer events at all". This configuration feature **does not work** without the `ebuf_max_events` setting being added to the list of GUI daemon configuration settings in qubes-core-admin-client. The pull request for that is at QubesOS/qubes-core-admin-client#309.

This PR needs more testing (especially on Qubes R4.3), but it is solid enough that I feel comfortable asking for a review on it. Thanks for your help!
@ArrayBolt3
Copy link

@DemiMarie I believe this can be closed, since the event buffering PR that was merged implements this in the GUI daemon.

@andrewdavidwong
Copy link
Member

Closing as completed. If anyone believes this issue is not yet completed, or if anyone is still affected by this issue, please leave a comment saying so, and we'll be happy to reopen it. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C: Whonix This issue impacts Qubes-Whonix P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. privacy This issue pertains to data or information privacy through technological means.
Projects
None yet
Development

No branches or pull requests

6 participants