mirror of
https://github.com/QubesOS/qubes-doc.git
synced 2024-12-18 04:04:39 -05:00
Merge branch 'open-in-dispvms' of github.com:DemiMarie/qubes-doc into DemiMarie-open-in-dispvms
This commit is contained in:
commit
5bb7efa472
@ -11,25 +11,25 @@ ref: 61
|
||||
title: GUI virtualization
|
||||
---
|
||||
|
||||
qubes_gui and qubes_guid processes
|
||||
`qubes-gui` and `qubes-guid` processes
|
||||
------------------------------------
|
||||
|
||||
All AppVM X applications connect to local (running in AppVM) Xorg servers that use the following "hardware" drivers:
|
||||
|
||||
- *dummyqsb_drv* - video driver, that paints onto a framebuffer located in RAM, not connected to real hardware
|
||||
- *qubes_drv* - it provides a virtual keyboard and mouse (in fact, more, see below)
|
||||
- *`dummyqsb_drv`* - video driver, that paints onto a framebuffer located in RAM, not connected to real hardware
|
||||
- *`qubes_drv`* - it provides a virtual keyboard and mouse (in fact, more, see below)
|
||||
|
||||
For each AppVM, there is a pair of *qubes_gui* (running in AppVM) and *qubes_guid* (running in dom0) processes connected over vchan.
|
||||
The main responsibilities of *qubes_gui* are:
|
||||
For each AppVM, there is a pair of `qubes-gui` (running in AppVM) and `qubes-guid` (running in the AppVM’s GuiVM, dom0 by default) processes connected over vchan.
|
||||
The main responsibilities of `qubes-gui` are:
|
||||
|
||||
- call XCompositeRedirectSubwindows on the root window, so that each window has its own composition buffer
|
||||
- instruct the local Xorg server to notify it about window creation, configuration and damage events; pass information on these events to dom0
|
||||
- receive information about keyboard and mouse events from dom0, tell *qubes_drv* to fake appropriate events
|
||||
- receive information about keyboard and mouse events from dom0, tell `qubes-drv` to fake appropriate events
|
||||
- receive information about window size/position change, apply them to the local window
|
||||
|
||||
The main responsibilities of *qubes_guid* are:
|
||||
The main responsibilities of `qubes-guid` are:
|
||||
|
||||
- create a window in dom0 whenever an information on window creation in AppVM is received from *qubes_gui*
|
||||
- create a window in dom0 whenever an information on window creation in AppVM is received from `qubes-gui`
|
||||
- whenever the local window receives XEvent, pass information on it to AppVM (particularly, mouse and keyboard data)
|
||||
- whenever AppVM signals damage event, tell local Xorg server to repaint a given window fragment
|
||||
- receive information about window size/position change, apply them to the local window
|
||||
@ -40,24 +40,27 @@ AppVM has no way to get information on keystrokes fed to other AppVMs (e.g. XTES
|
||||
Window content updates implementation
|
||||
-------------------------------------
|
||||
|
||||
Typical remote desktop applications, like *vnc*, pass information on all changed window content in-band (say, over tcp).
|
||||
Typical remote desktop applications, like VNC, pass information on all changed window content in-band (say, over tcp).
|
||||
As that channel has limited throughput, this impacts video performance.
|
||||
In the case of Qubes, *qubes_gui* does not transfer all changed pixels via vchan. Instead, for each window, upon its creation or size change, *qubes_gui*
|
||||
In the case of Qubes, `qubes-gui` does not transfer all changed pixels via vchan. Instead, for each window, upon its creation or size change:
|
||||
|
||||
- asks *qubes_drv* driver for the list of physical memory frames that hold the composition buffer of a window
|
||||
- passes this information via `MFNDUMP` message to *qubes_guid* in dom0
|
||||
- Old `qubes-gui` versions will ask `qubes-drv` driver for the list of
|
||||
physical memory frames that hold the composition buffer of a window,
|
||||
and pass this to dom0 via the deprecated `MFNDUMP` message.
|
||||
- New `qubes-gui` versions will rely on `qubes-drv` having allocated
|
||||
memory using gntalloc, and then pass the grant table indexes gntalloc
|
||||
has chosen to the GUI daemon using using the `WINDOW_DUMP` message.
|
||||
|
||||
Now, *qubes_guid* has to tell the dom0 Xorg server about the location of the buffer.
|
||||
Now, `qubes-guid` has to tell the dom0 Xorg server about the location of the buffer.
|
||||
There is no supported way (e.g. Xorg extension) to do this zero-copy style.
|
||||
The following method is used in Qubes:
|
||||
|
||||
- in dom0, the Xorg server is started with *LD_PRELOAD*-ed library named *shmoverride.so*. This library hooks all function calls related to shared memory.
|
||||
- *qubes_guid* creates a shared memory segment, and then tells Xorg to attach it via *MIT-SHM* extension
|
||||
- when Xorg tries to attach the segment (via glibc *shmat*) *shmoverride.so* intercepts this call and instead maps AppVM memory via *xc_map_foreign_pages*
|
||||
- since then, we can use MIT-SHM functions, e.g. *XShmPutImage* to draw onto a dom0 window. *XShmPutImage* will paint with DRAM speed; actually, many drivers use DMA for this.
|
||||
- in dom0, the Xorg server is started with `LD_PRELOAD`-ed library named `shmoverride.so`. This library hooks all function calls related to shared memory.
|
||||
- `qubes-guid` creates a shared memory segment, and then tells Xorg to attach it via `MIT-SHM` extension
|
||||
- when Xorg tries to attach the segment (via glibc `shmat`) `shmoverride.so` intercepts this call and instead maps AppVM memory via `xc_map_foreign_pages` for the deprecated `MFNDUMP` message, or `xengnttab_map_domain_grant_refs` for the `WINDOW_DUMP` message.
|
||||
- afterwards, we can use MIT-SHM functions, such as `XShmPutImage`, to draw onto a dom0 window. `XShmPutImage` will paint with DRAM speed, and many drivers use DMA to make this even faster.
|
||||
|
||||
The important detail is that *xc_map_foreign_pages* verifies that a given mfn range actually belongs to a given domain id (and the latter is provided by trusted *qubes_guid*).
|
||||
Therefore, rogue AppVM cannot gain anything by passing crafted mnfs in the `MFNDUMP` message.
|
||||
The important detail is that `xc_map_foreign_pages` verifies that a given mfn range actually belongs to a given domain id (and the latter is provided by trusted `qubes-guid`). Therefore, rogue AppVM cannot gain anything by passing crafted mnfs in the `MFNDUMP` message. Similarly, `xengnttab_map_domain_grant_refs` will only map grants from the specific domain ID specified by qubes-guid, so crafted `WINDOW_DUMP` messages are not helpful to an attacker.
|
||||
|
||||
To sum up, this solution has the following benefits:
|
||||
|
||||
@ -65,6 +68,13 @@ To sum up, this solution has the following benefits:
|
||||
- no changes to Xorg code
|
||||
- minimal size of the supporting code
|
||||
|
||||
There are two reasons that `WINDOW_DUMP` is preferred over `MFNDUMP`:
|
||||
|
||||
1. `xc_map_foreign_pages` can only be used by dom0, as it allows accessing all memory of any VM. Allowing any VM other than dom0 to do this would be a security vulnerability.
|
||||
2. `xc_map_foreign_pages` requires the guest physical address of the pages to map, but normal userspace processes (such as `qubes-gui` or Xorg) do not have access to that information. Therefore, the translation is done via the `u2mfn` out-of-tree kernel module.
|
||||
|
||||
Currently, using `WINDOW_DUMP` does come at a performance cost, because the AppVM’s X server must copy the pages from the application to the gntalloc-allocated memory. This will be solved by future improvements to gntalloc, which will allow exporting *any* page via gntalloc, including memory shared by another process.
|
||||
|
||||
![gui.png](/attachment/doc/gui.png)
|
||||
|
||||
Security markers on dom0 windows
|
||||
@ -72,7 +82,7 @@ Security markers on dom0 windows
|
||||
|
||||
It is important that the user knows which AppVM a given window belongs to. This prevents a rogue AppVM from painting a window pretending to belong to other AppVM or dom0 and trying to steal, for example, passwords.
|
||||
|
||||
In Qubes, a custom window decorator is used that paints a colourful frame (the colour is determined during AppVM creation) around decorated windows. Additionally, the window title always starts with **[name of the AppVM]**. If a window has an *override_redirect* attribute, meaning that it should not be treated by a window manager (typical case is menu windows), *qubes_guid* draws a two-pixel colourful frame around it manually.
|
||||
In Qubes, a custom window decorator is used that paints a colourful frame (the colour is determined during AppVM creation) around decorated windows. Additionally, the window title always starts with **[name of the AppVM]**. If a window has an `override_redirect` attribute, meaning that it should not be treated by a window manager (typical case is menu windows), `qubes-guid` draws a two-pixel colourful frame inside it manually.
|
||||
|
||||
Clipboard sharing implementation
|
||||
--------------------------------
|
||||
@ -81,34 +91,34 @@ Certainly, it would be insecure to allow AppVM to read/write the clipboards of o
|
||||
Therefore, the following mechanism is used:
|
||||
|
||||
- there is a "qubes clipboard" in dom0 - its contents are stored in a regular file in dom0.
|
||||
- if the user wants to copy local AppVM clipboard to qubes clipboard, she must focus on any window belonging to this AppVM, and press **Ctrl-Shift-C**. This combination is trapped by *qubes-guid*, and `CLIPBOARD_REQ` message is sent to AppVM. *qubes-gui* responds with *CLIPBOARD_DATA* message followed by clipboard contents.
|
||||
- the user focuses on other AppVM window, presses **Ctrl-Shift-V**. This combination is trapped by *qubes-guid*, and `CLIPBOARD_DATA` message followed by qubes clipboard contents is sent to AppVM; *qubes_gui* copies data to the local clipboard, and then user can paste its contents to local applications normally.
|
||||
- if the user wants to copy local AppVM clipboard to qubes clipboard, she must focus on any window belonging to this AppVM, and press **Ctrl-Shift-C**. This combination is trapped by `qubes-guid`, and `CLIPBOARD_REQ` message is sent to AppVM. `qubes-gui` responds with `CLIPBOARD_DATA` message followed by clipboard contents.
|
||||
- the user focuses on other AppVM window, presses **Ctrl-Shift-V**. This combination is trapped by `qubes-guid`, and `CLIPBOARD_DATA` message followed by qubes clipboard contents is sent to AppVM; `qubes-gui` copies data to the local clipboard, and then user can paste its contents to local applications normally.
|
||||
|
||||
This way, the user can quickly copy clipboards between AppVMs.
|
||||
This action is fully controlled by the user, it cannot be triggered/forced by any AppVM.
|
||||
|
||||
*qubes_gui* and *qubes_guid* code notes
|
||||
`qubes-gui` and `qubes-guid` code notes
|
||||
-----------------------------------------
|
||||
|
||||
Both applications are structured similarly. They use *select* function to wait for any of these two event sources:
|
||||
Both applications are structured similarly. They use `select` function to wait for any of these two event sources:
|
||||
|
||||
- messages from the local X server
|
||||
- messages from the vchan connecting to the remote party
|
||||
|
||||
The XEvents are handled by the *handle_xevent_eventname* function, and messages are handled by *handle_messagename* function. One should be very careful when altering the actual *select* loop, because both XEvents and vchan messages are buffered, and *select* will not wake for each message.
|
||||
The XEvents are handled by the `handle_xevent_eventname` function, and messages are handled by `handle_messagename` function. One should be very careful when altering the actual `select` loop, because both XEvents and vchan messages are buffered, and `select` will not wake for each message.
|
||||
|
||||
If one changes the number/order/signature of messages, one should increase the *QUBES_GUID_PROTOCOL_VERSION* constant in *messages.h* include file.
|
||||
If one changes the number/order/signature of messages, one should increase the `QUBES_GUID_PROTOCOL_VERSION` constant in `messages.h` include file.
|
||||
|
||||
*qubes_guid* writes debugging information to */var/log/qubes/qubes.domain_id.log* file; *qubes_gui* writes debugging information to */var/log/qubes/gui_agent.log*.
|
||||
`qubes-guid` writes debugging information to `/var/log/qubes/qubes.domain_id.log` file; `qubes-gui` writes debugging information to `/var/log/qubes/gui_agent.log`.
|
||||
Include these files when reporting a bug.
|
||||
|
||||
AppVM -> dom0 messages
|
||||
AppVM -> GuiVM messages
|
||||
-----------------------
|
||||
|
||||
Proper handling of the below messages is security-critical.
|
||||
Observe that beside two messages (`CLIPBOARD` and `MFNDUMP`) the rest have fixed size, so the parsing code can be small.
|
||||
Note that all messages except for `CLIPBOARD`, `MFNDUMP`, and `WINDOW_DUMP` have fixed size, so the parsing code can be small.
|
||||
|
||||
The *override_redirect* window attribute is explained at [Override Redirect Flag](https://tronche.com/gui/x/xlib/window/attributes/override-redirect.html). The *transient_for* attribute is explained at [Transient_for attribute](https://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR).
|
||||
The `override_redirect` window attribute is explained at [Override Redirect Flag](https://tronche.com/gui/x/xlib/window/attributes/override-redirect.html). The `transient_for` attribute is explained at [`transient_for` attribute](https://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR).
|
||||
|
||||
Window manager hints and flags are described in the [Extended Window Manager Hints (EWMH) spec](https://standards.freedesktop.org/wm-spec/latest/), especially under the `_NET_WM_STATE` section.
|
||||
|
||||
@ -118,11 +128,11 @@ Each message starts with the following header:
|
||||
struct msghdr {
|
||||
uint32_t type;
|
||||
uint32_t window;
|
||||
/* This field is intended for use by gui_agents to skip unknown
|
||||
* messages from the (trusted) guid. Guid, on the other hand,
|
||||
* should never rely on this field to calculate the actual len of
|
||||
* message to be read, as the (untrusted) agent can put here
|
||||
* whatever it wants! */
|
||||
/* This field is intended for use by GUI agents to skip unknown
|
||||
* messages from the (trusted) GUI daemon. GUI daemon, on the other
|
||||
* hand, should never rely on this field to calculate the actual len
|
||||
* of message to be read, as the (untrusted) agent can put whatever
|
||||
* it wants here! */
|
||||
uint32_t untrusted_len;
|
||||
};
|
||||
```
|
||||
@ -138,7 +148,7 @@ This header is followed by message-specific data:
|
||||
<tr>
|
||||
<td>MSG_CLIPBOARD_DATA</td>
|
||||
<td>amorphic blob (in protocol before 1.2, length determined by the "window" field, in 1.2 and later - by untrusted_len in the header)</td>
|
||||
<td>Store the received clipboard content (not parsing in any way)</td>
|
||||
<td>Store the received clipboard content (not parsed in any way)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_CREATE</td>
|
||||
@ -204,7 +214,7 @@ struct shm_cmd {
|
||||
</pre></td>
|
||||
<td>Retrieve the array of mfns that constitute the composition buffer of a remote window.
|
||||
|
||||
The "num_mfn" 32bit integers follow the shm_cmd structure; "off" is the offset of the composite buffer start in the first frame; "shmid" and "domid" parameters are just placeholders (to be filled by *qubes_guid*), so that we can use the same structure when talking to *shmoverride.so*|
|
||||
The "num_mfn" 32bit integers follow the shm_cmd structure; "off" is the offset of the composite buffer start in the first frame; "shmid" and "domid" parameters are just placeholders (to be filled by `qubes-guid`), so that we can use the same structure when talking to `shmoverride.so`.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -226,7 +236,7 @@ struct msg_wmname {
|
||||
char data[128];
|
||||
};
|
||||
</pre></td>
|
||||
<td>Set the window name; only printable characters are allowed</td>
|
||||
<td>Set the window name. Only printable characters are allowed, and by default non-ASCII characters are not allowed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_DOCK</td>
|
||||
@ -269,9 +279,41 @@ struct msg_cursor {
|
||||
</pre> </td>
|
||||
<td>Update cursor pointer for a window. Supported cursor IDs are default cursor (0) and <a href="https://tronche.com/gui/x/xlib/appendix/b/">X Font cursors</a> (with 0x100 bit set).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_WMCLASS</td>
|
||||
<td><pre>
|
||||
struct msg_wmclass {
|
||||
char res_class[64];
|
||||
char res_name[64];
|
||||
};
|
||||
</pre> </td>
|
||||
<td>Set the WM_CLASS property of a window.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_WINDOW_DUMP</td>
|
||||
<td><pre>
|
||||
struct msg_window_dump_hdr {
|
||||
uint32_t type;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bpp;
|
||||
};
|
||||
</pre> </td>
|
||||
<td>Header for shared memory dump command of type hdr.type. Currently only <pre>WINDOW_DUMP_TYPE_GRANT_REFS</pre> (0) is supported.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WINDOW_DUMP_TYPE_GRANT_REFS</td>
|
||||
<td><pre>
|
||||
struct msg_window_dump_grant_refs {
|
||||
uint32_t refs[0];
|
||||
};
|
||||
</pre> </td>
|
||||
<td>Grant references that should be mapped into the compositing buffer.</td>
|
||||
</tr>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Dom0 -> AppVM messages
|
||||
GuiVM -> AppVM messages
|
||||
-----------------------
|
||||
|
||||
Proper handling of the below messages is NOT security-critical.
|
||||
@ -304,7 +346,7 @@ struct msg_keypress {
|
||||
uint32_t keycode;
|
||||
};
|
||||
</pre> </td>
|
||||
<td>Tell *qubes_drv* driver to generate a keypress</td>
|
||||
<td>Tell <pre>qubes_drv</pre> driver to generate a keypress</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_BUTTON</td>
|
||||
@ -317,7 +359,7 @@ struct msg_button {
|
||||
uint32_t button;
|
||||
};
|
||||
</pre> </td>
|
||||
<td>Tell *qubes_drv* driver to generate mouseclick</td>
|
||||
<td>Tell <pre>qubes_drv</pre> driver to generate mouseclick</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_MOTION</td>
|
||||
@ -329,7 +371,7 @@ struct msg_motion {
|
||||
uint32_t is_hint;
|
||||
};
|
||||
</pre> </td>
|
||||
<td>Tell *qubes_drv* driver to generate motion event</td>
|
||||
<td>Tell <pre>qubes_drv</pre> driver to generate motion event</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MSG_CONFIGURE</td>
|
||||
|
@ -126,6 +126,18 @@ Use the `qvm-open-in-dvm` command from a terminal in your app qube:
|
||||
|
||||
Note that the `qvm-open-in-dvm` process will not exit until you close the application in the disposable.
|
||||
|
||||
## Making a particular application open everything in a disposable
|
||||
|
||||
You can use the `qvm-service` command or the services GUI to cause an application in a qube to open files and URLs in a disposable. To do this, enable a service named `app-dispvm.X` in that qube, where `X` is the application ID. For instance, to have Thunderbird open all attachments in a disposable, enable the `app-dispvm.thunderbird` service.
|
||||
|
||||
This feature is currently somewhat experimental, and only works for Linux qubes. It is known to work with Thunderbird and Wire, but it may fail to work with some applications that do not honor all XDG environment variables. If the feature does not work for you, please file a bug report.
|
||||
|
||||
## Opening particular types of files in a disposable
|
||||
|
||||
You can set `qvm-open-in-dvm.desktop` as the handler for a given MIME type. This will cause all files of that type to open in a disposable. This works in disposable templates too, but be careful: if your disposable template is set to use `qvm-open-in-dvm.desktop` to open a certain kind of file, every disposable based on it will be as well. If the disposable template is its own default disposable template (as is often the case), this will result in a loop: `qvm-open-in-dvm` will execute `qubes.OpenURL` in a new disposable, but that will in turn execute `qvm-open-in-dvm`. The cycle will repeat until no new disposables can be created, most likely because your system has run out of memory.
|
||||
|
||||
This will _not_ override the internal handling of PDF documents in Web browsers. This is typically okay, though: in-browser PDF viewers have a fairly good security record, especially when compared to non-browser PDF viewers. In particular, the attack surface of PDF viewing in Firefox is usually less than that of viewing an ordinary Web page.
|
||||
|
||||
## Starting an arbitrary program in a disposable from an app qube
|
||||
|
||||
Sometimes it can be useful to start an arbitrary program in a disposable.
|
||||
|
@ -10,7 +10,8 @@ ref: 204
|
||||
title: How to use optical discs
|
||||
---
|
||||
|
||||
Passthrough reading and recording (a.k.a., "burning") are not supported by Xen.
|
||||
Passthrough reading and recording (a.k.a., "burning") are not supported by Qubes OS. This is not a limitation of Xen, which provides scsiback and scsifront drivers, but of Qubes OS. It will be fixed in the future.
|
||||
|
||||
Currently, the only options for reading and recording optical discs (e.g., CDs, DVDs, BRDs) in Qubes are:
|
||||
|
||||
1. Use a USB optical drive.
|
||||
|
Loading…
Reference in New Issue
Block a user