From bdffd0fa59c80837dd9f5ce43b5b4ff2b5432c18 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 12:00:38 -0500 Subject: [PATCH 01/24] begin standardizing markdown lineation of qrexec3 docs --- developer/services/qrexec3.md | 292 ++++++++++++---------------------- 1 file changed, 104 insertions(+), 188 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index f46227ac..ebc5c1b4 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -18,92 +18,68 @@ redirect_from: # Command execution in VMs # -(*This page is about qrexec v3. For qrexec v2, see -[here](/doc/qrexec2/).*) +(*This page is about qrexec v3. For qrexec v2, see [here](/doc/qrexec2/).*) -The **qrexec** framework is used by core Qubes components to implement -communication between domains. Qubes domains are isolated by design, but -there is a need for a mechanism to allow the administrative domain (dom0) to -force command execution in another domain (VM). For instance, when user -selects an application from the KDE menu, it should be started in the selected -VM. Also, it is often useful to be able to pass stdin/stdout/stderr from an -application running in a VM to dom0 (and the other way around). In specific -circumstances, Qubes allows VMs to be initiators of such communications (so, -for example, a VM can notify dom0 that there are updates available for it). +The **qrexec** framework is used by core Qubes components to implement communication between domains. +Qubes domains are isolated by design, but there is a need for a mechanism to allow the administrative domain (dom0) to force command execution in another domain (VM). +For instance, when user selects an application from the KDE menu, it should be started in the selected VM. +Also, it is often useful to be able to pass stdin/stdout/stderr from an application running in a VM to dom0 (and the other way around). +In specific circumstances, Qubes allows VMs to be initiators of such communications (so, for example, a VM can notify dom0 that there are updates available for it). ## Qrexec basics ## -Qrexec is built on top of vchan (a library providing data links between -VMs). During domain creation a process named `qrexec-daemon` is started -in dom0, and a process named `qrexec-agent` is started in the VM. They are -connected over **vchan** channel. `qrexec-daemon` listens for connections -from dom0 utility named `qrexec-client`. Typically, the first thing that a -`qrexec-client` instance does is to send a request to `qrexec-daemon` to -start a process (let's name it `VMprocess`) with a given command line in -a specified VM (`someVM`). `qrexec-daemon` assigns unique vchan connection -details and sends them both to `qrexec-client` (in dom0) and `qrexec-agent` -(in `someVM`). `qrexec-client` starts a vchan server which `qrexec-agent` -connects to. Since then, stdin/stdout/stderr from the VMprocess is passed -via vchan between `qrexec-agent` and the `qrexec-client` process. +Qrexec is built on top of vchan (a library providing data links between VMs). +During domain creation a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. +They are connected over **vchan** channel. +`qrexec-daemon` listens for connections from dom0 utility named `qrexec-client`. +Typically, the first thing that a `qrexec-client` instance does is to send a request to `qrexec-daemon` to start a process (let's name it `VMprocess`) with a given command line in a specified VM (`someVM`). +`qrexec-daemon` assigns unique vchan connection details and sends them both to `qrexec-client` (in dom0) and `qrexec-agent` (in `someVM`). +`qrexec-client` starts a vchan server which `qrexec-agent` connects to. +Since then, stdin/stdout/stderr from the VMprocess is passed via vchan between `qrexec-agent` and the `qrexec-client` process. So, for example, executing in dom0: qrexec-client -d someVM user:bash -allows to work with the remote shell. The string before the first -semicolon specifies what user to run the command as. Adding `-e` on the -`qrexec-client` command line results in mere command execution (no data -passing), and `qrexec-client` exits immediately after sending the execution -request and receiving status code from `qrexec-agent` (whether the process -creation succeeded). There is also the `-l local_program` flag -- with it, -`qrexec-client` passes stdin/stdout of the remote process to the (spawned -for this purpose) `local_program`, not to its own stdin/stdout. +allows to work with the remote shell. +The string before the first semicolon specifies what user to run the command as. +Adding `-e` on the `qrexec-client` command line results in mere command execution (no data passing), and `qrexec-client` exits immediately after sending the execution request and receiving status code from `qrexec-agent` (whether the process creation succeeded). +There is also the `-l local_program` flag -- with it, `qrexec-client` passes stdin/stdout of the remote process to the (spawned for this purpose) `local_program`, not to its own stdin/stdout. -The `qvm-run` command is heavily based on `qrexec-client`. It also takes care -of additional activities, e.g. starting the domain if it is not up yet and -starting the GUI daemon. Thus, it is usually more convenient to use `qvm-run`. +The `qvm-run` command is heavily based on `qrexec-client`. +It also takes care of additional activities, e.g. starting the domain if it is not up yet and starting the GUI daemon. +Thus, it is usually more convenient to use `qvm-run`. -There can be almost arbitrary number of `qrexec-client` processes for a -domain (so, connected to the same `qrexec-daemon`, same domain) -- their -data is multiplexed independently. Number of available vchan channels is -the limiting factor here, it depends on the underlying hypervisor. +There can be almost arbitrary number of `qrexec-client` processes for a domain (so, connected to the same `qrexec-daemon`, same domain) -- their data is multiplexed independently. +Number of available vchan channels is the limiting factor here, it depends on the underlying hypervisor. ## Qubes RPC services ## -Some tasks (like inter-vm file copy) share the same rpc-like structure: -a process in one VM (say, file sender) needs to invoke and send/receive -data to some process in other VM (say, file receiver). Thus, the Qubes RPC -framework was created, facilitating such actions. +Some tasks (like inter-vm file copy) share the same rpc-like structure: a process in one VM (say, file sender) needs to invoke and send/receive data to some process in other VM (say, file receiver). +Thus, the Qubes RPC framework was created, facilitating such actions. -Obviously, inter-VM communication must be tightly controlled to prevent one -VM from taking control over other, possibly more privileged, VM. Therefore -the design decision was made to pass all control communication via dom0, -that can enforce proper authorization. Then, it is natural to reuse the -already-existing qrexec framework. +Obviously, inter-VM communication must be tightly controlled to prevent one VM from taking control over other, possibly more privileged, VM. +Therefore the design decision was made to pass all control communication via dom0, that can enforce proper authorization. +Then, it is natural to reuse the already-existing qrexec framework. -Also, note that bare qrexec provides `VM <-> dom0` connectivity, but the -command execution is always initiated by dom0. There are cases when VM needs -to invoke and send data to a command in dom0 (e.g. to pass information on -newly installed `.desktop` files). Thus, the framework allows dom0 to be -the rpc target as well. +Also, note that bare qrexec provides `VM <-> dom0` connectivity, but the command execution is always initiated by dom0. +There are cases when VM needs to invoke and send data to a command in dom0 (e.g. to pass information on newly installed `.desktop` files). +Thus, the framework allows dom0 to be the rpc target as well. -Thanks to the framework, RPC programs are very simple -- both rpc client -and server just use their stdin/stdout to pass data. The framework does all -the inner work to connect these processes to each other via `qrexec-daemon` -and `qrexec-agent`. Additionally, disposable VMs are tightly integrated -- -rpc to a DisposableVM is identical to rpc to a normal domain, all one needs -is to pass `$dispvm` as the remote domain name. +Thanks to the framework, RPC programs are very simple -- both rpc client and server just use their stdin/stdout to pass data. +The framework does all the inner work to connect these processes to each other via `qrexec-daemon` and `qrexec-agent`. +Additionally, disposable VMs are tightly integrated -- rpc to a DisposableVM is identical to rpc to a normal domain, all one needs is to pass `$dispvm` as the remote domain name. ## Qubes RPC administration ## (*TODO: fix for non-linux dom0*) -In dom0, there is a bunch of files in `/etc/qubes-rpc/policy` directory, -whose names describe the available rpc actions. Their content is the rpc -access policy database. Currently defined actions are: +In dom0, there is a bunch of files in `/etc/qubes-rpc/policy` directory, whose names describe the available rpc actions. +Their content is the rpc access policy database. +Currently defined actions are: qubes.ClipboardPaste qubes.Filecopy @@ -128,47 +104,31 @@ These files contain lines with the following format: srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to] -You can specify srcvm and destvm by name, or by one of `$anyvm`, `$dispvm`, -`dom0` reserved keywords (note string `dom0` does not match the `$anyvm` -pattern; all other names do). Only `$anyvm` keyword makes sense in srcvm -field (service calls from dom0 are currently always allowed, `$dispvm` -means "new VM created for this particular request," so it is never a -source of request). Currently there is no way to specify source VM by -type. Whenever a rpc request for action X is received, the first line in -`/etc/qubes-rpc/policy/X` that match srcvm/destvm is consulted to determine -whether to allow rpc, what user account the program should run in target VM -under, and what VM to redirect the execution to. Note that if the request is -redirected (`target=` parameter), policy action remains the same - even if -there is another rule which would otherwise deny such request. If the policy -file does not exist, user is prompted to create one; if still there is no -policy file after prompting, the action is denied. +You can specify srcvm and destvm by name, or by one of `$anyvm`, `$dispvm`, `dom0` reserved keywords (note string `dom0` does not match the `$anyvm` pattern; all other names do). +Only `$anyvm` keyword makes sense in srcvm field (service calls from dom0 are currently always allowed, `$dispvm` means "new VM created for this particular request," so it is never a source of request). +Currently there is no way to specify source VM by type. +Whenever a rpc request for action X is received, the first line in `/etc/qubes-rpc/policy/X` that match srcvm/destvm is consulted to determine whether to allow rpc, what user account the program should run in target VM under, and what VM to redirect the execution to. +Note that if the request is redirected (`target=` parameter), policy action remains the same - even if there is another rule which would otherwise deny such request. +If the policy file does not exist, user is prompted to create one; if still there is no policy file after prompting, the action is denied. -In the target VM, the `/etc/qubes-rpc/RPC_ACTION_NAME` must exist, containing -the file name of the program that will be invoked, or being that program itself -- in which case it must have executable permission set (`chmod +x`). +In the target VM, the `/etc/qubes-rpc/RPC_ACTION_NAME` must exist, containing the file name of the program that will be invoked, or being that program itself - in which case it must have executable permission set (`chmod +x`). In the src VM, one should invoke the client via: /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME rpc_client_path client arguments -Note that only stdin/stdout is passed between rpc server and client -- -notably, no command line arguments are passed. Source VM name is specified by -`QREXEC_REMOTE_DOMAIN` environment variable. By default, stderr of client -and server is logged to respective `/var/log/qubes/qrexec.XID` files. -It is also possible to call service without specific client program - in which -case server stdin/out will be connected with the terminal: +Note that only stdin/stdout is passed between rpc server and client -- notably, no command line arguments are passed. +Source VM name is specified by `QREXEC_REMOTE_DOMAIN` environment variable. +By default, stderr of client and server is logged to respective `/var/log/qubes/qrexec.XID` files. +It is also possible to call service without specific client program - in which case server stdin/out will be connected with the terminal: /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME -Be very careful when coding and adding a new rpc service. Unless the -offered functionality equals full control over the target (it is the case -with e.g. `qubes.VMShell` action), any vulnerability in an rpc server can -be fatal to Qubes security. On the other hand, this mechanism allows to -delegate processing of untrusted input to less privileged (or disposable) -AppVMs, thus wise usage of it increases security. +Be very careful when coding and adding a new rpc service. +Unless the offered functionality equals full control over the target (it is the case with e.g. `qubes.VMShell` action), any vulnerability in an rpc server can be fatal to Qubes security. +On the other hand, this mechanism allows to delegate processing of untrusted input to less privileged (or disposable) AppVMs, thus wise usage of it increases security. -For example, this command will run the `firefox` command in a DisposableVM based -on `work`: +For example, this command will run the `firefox` command in a DisposableVM based on `work`: ``` $ qvm-run --dispvm=work firefox @@ -180,133 +140,95 @@ By contrast, consider this command: $ qvm-run --dispvm=work --service qubes.StartApp+firefox ``` -This will look for a `firefox.desktop` file in a standard location in a -DisposableVM based on `work`, then launch the application described by that -file. The practical difference is that the bare `qvm-run` command uses the -`qubes.VMShell` service, which allows you to run an arbitrary command with -arbitrary arguments, essentially providing full control over the target VM. By -contrast, the `qubes.StartApp` service allows you to run only applications that -are advertised in `/usr/share/applications` (or other standard locations) -*without* control over the arguments, so giving a VM access to `qubes.StartApp` -is much safer. While there isn't much practical difference between the two -commands above when starting an application from dom0 in Qubes 4.0, there is a -significant security risk when launching applications from a domU (e.g., from -a separate GUI domain). This is why `qubes.StartApp` uses our standard `qrexec` -argument grammar to strictly filter the permissible grammar of the `Exec=` lines -in `.desktop` files that are passed from untrusted domUs to dom0, thereby -protecting dom0 from command injection by maliciously-crafted `.desktop` files. +This will look for a `firefox.desktop` file in a standard location in a DisposableVM based on `work`, then launch the application described by that file. +The practical difference is that the bare `qvm-run` command uses the `qubes.VMShell` service, which allows you to run an arbitrary command with arbitrary arguments, essentially providing full control over the target VM. +By contrast, the `qubes.StartApp` service allows you to run only applications that are advertised in `/usr/share/applications` (or other standard locations) *without* control over the arguments, so giving a VM access to `qubes.StartApp` is much safer. +While there isn't much practical difference between the two commands above when starting an application from dom0 in Qubes 4.0, there is a significant security risk when launching applications from a domU (e.g., from a separate GUI domain). +This is why `qubes.StartApp` uses our standard `qrexec` argument grammar to strictly filter the permissible grammar of the `Exec=` lines in `.desktop` files that are passed from untrusted domUs to dom0, thereby protecting dom0 from command injection by maliciously-crafted `.desktop` files. ### Extra keywords available in Qubes 4.0 and later **This section is about a not-yet-released version, some details may change** -In Qubes 4.0, target VM can be specified also as `$dispvm:DISP_VM`, which is -very similar to `$dispvm` but forces using a particular VM (`DISP_VM`) as a base -VM to be started as DisposableVM. For example: +In Qubes 4.0, target VM can be specified also as `$dispvm:DISP_VM`, which is very similar to `$dispvm` but forces using a particular VM (`DISP_VM`) as a base VM to be started as DisposableVM. +For example: anon-whonix $dispvm:anon-whonix-dvm allow -Adding such policy itself will not force usage of this particular `DISP_VM` - -it will only allow it when specified by the caller. But `$dispvm:DISP_VM` can -also be used as target in request redirection, so _it is possible_ to force -particular `DISP_VM` usage, when caller didn't specify it: +Adding such policy itself will not force usage of this particular `DISP_VM` - it will only allow it when specified by the caller. +But `$dispvm:DISP_VM` can also be used as target in request redirection, so _it is possible_ to force particular `DISP_VM` usage, when caller didn't specify it: anon-whonix $dispvm allow,target=$dispvm:anon-whonix-dvm -Note that without redirection, this rule would allow using default Disposable -VM (`default_dispvm` VM property, which itself defaults to global -`default_dispvm` property). -Also note that the request will be allowed (`allow` action) even if there is no -second rule allowing calls to `$dispvm:anon-whonix-dvm`, or even if -there is a rule explicitly denying it. This is because the redirection happens -_after_ considering the action. +Note that without redirection, this rule would allow using default Disposable VM (`default_dispvm` VM property, which itself defaults to global `default_dispvm` property). +Also note that the request will be allowed (`allow` action) even if there is no second rule allowing calls to `$dispvm:anon-whonix-dvm`, or even if there is a rule explicitly denying it. +This is because the redirection happens _after_ considering the action. In Qubes 4.0 there are also additional methods to specify source/target VM: * `$tag:some-tag` - meaning a VM with tag `some-tag` * `$type:type` - meaning a VM of `type` (like `AppVM`, `TemplateVM` etc) -Target VM can be also specified as `$default`, which matches the case when -calling VM didn't specified any particular target (either by using `$default` -target, or empty target). +Target VM can be also specified as `$default`, which matches the case when calling VM didn't specified any particular target (either by using `$default` target, or empty target). -In Qubes 4.0 policy confirmation dialog (`ask` action) allow the user to -specify target VM. User can choose from VMs that, according to policy, would -lead to `ask` or `allow` actions. It is not possible to select VM that policy -would deny. By default no VM is selected, even if the caller provided some, but -policy can specify default value using `default_target=` parameter. For -example: +In Qubes 4.0 policy confirmation dialog (`ask` action) allow the user to specify target VM. +User can choose from VMs that, according to policy, would lead to `ask` or `allow` actions. +It is not possible to select VM that policy would deny. +By default no VM is selected, even if the caller provided some, but policy can specify default value using `default_target=` parameter. +For example: work-mail work-archive allow work-mail $tag:work ask,default_target=work-files work-mail $default ask,default_target=work-files -The first rule allow call from `work-mail` to `work-archive`, without any -confirmation. -The second rule will ask the user about calls from `work-mail` VM to any VM with -tag `work`. And the confirmation dialog will have `work-files` VM chosen by -default, regardless of the VM specified by the caller (`work-mail` VM). The -third rule allow the caller to not specify target VM at all and let the user -choose, still - from VMs with tag `work` (and `work-archive`, regardless of -tag), and with `work-files` as default. +The first rule allow call from `work-mail` to `work-archive`, without any confirmation. +The second rule will ask the user about calls from `work-mail` VM to any VM with tag `work`. +And the confirmation dialog will have `work-files` VM chosen by default, regardless of the VM specified by the caller (`work-mail` VM). +The third rule allow the caller to not specify target VM at all and let the user choose, still - from VMs with tag `work` (and `work-archive`, regardless of tag), and with `work-files` as default. ### Service argument in policy -Sometimes just service name isn't enough to make reasonable qrexec policy. One -example of such a situation is [qrexec-based USB -passthrough](https://github.com/qubesos/qubes-issues/issues/531) - using just -service name isn't possible to express the policy "allow access to device X and -deny to others". It also isn't feasible to create a separate service for every -device... +Sometimes just service name isn't enough to make reasonable qrexec policy. +One example of such a situation is [qrexec-based USB passthrough](https://github.com/qubesos/qubes-issues/issues/531) - using just service name isn't possible to express the policy "allow access to device X and deny to others". +It also isn't feasible to create a separate service for every device... -For this reason, starting with Qubes 3.2, it is possible to specify a service -argument, which will be subject to policy. Besides the above example of USB -passthrough, a service argument can make many service policies more fine-grained -and easier to write precise policy with "allow" and "deny" actions, instead of -"ask" (offloading additional decisions to the user). And generally the less -choices the user must make, the lower the chance to make a mistake. +For this reason, starting with Qubes 3.2, it is possible to specify a service argument, which will be subject to policy. +Besides the above example of USB passthrough, a service argument can make many service policies more fine-grained and easier to write precise policy with "allow" and "deny" actions, instead of "ask" (offloading additional decisions to the user). +And generally the less choices the user must make, the lower the chance to make a mistake. -The syntax is simple: when calling a service, add an argument to the service name -separated with `+` sign, for example: +The syntax is simple: when calling a service, add an argument to the service name separated with `+` sign, for example: /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME+ARGUMENT -Then create a policy as usual, including the argument -(`/etc/qubes-rpc/policy/RPC_ACTION_NAME+ARGUMENT`). If the policy for the specific -argument is not set (file does not exist), then the default policy for this service -is loaded (`/etc/qubes-rpc/policy/RPC_ACTION_NAME`). +Then create a policy as usual, including the argument (`/etc/qubes-rpc/policy/RPC_ACTION_NAME+ARGUMENT`). +If the policy for the specific argument is not set (file does not exist), then the default policy for this service is loaded (`/etc/qubes-rpc/policy/RPC_ACTION_NAME`). In target VM (when the call is allowed) the service file will searched as: - `/etc/qubes-rpc/RPC_ACTION_NAME+ARGUMENT` - `/etc/qubes-rpc/RPC_ACTION_NAME` -In any case, the script will receive `ARGUMENT` as its argument and additionally as -`QREXEC_SERVICE_ARGUMENT` environment variable. This means it is also possible -to install a different script for a particular service argument. +In any case, the script will receive `ARGUMENT` as its argument and additionally as `QREXEC_SERVICE_ARGUMENT` environment variable. +This means it is also possible to install a different script for a particular service argument. See below for an example service using an argument. ### Revoking "Yes to All" authorization ### -Qubes RPC policy supports "ask" action. This will prompt the user whether given -RPC call should be allowed. That prompt window also has a "Yes to All" option, -which will allow the action and add a new entry to the policy file, which will -unconditionally allow further calls for the given service-srcVM-dstVM tuple. +Qubes RPC policy supports "ask" action. +This will prompt the user whether given RPC call should be allowed. +That prompt window also has a "Yes to All" option, which will allow the action and add a new entry to the policy file, which will unconditionally allow further calls for the given service-srcVM-dstVM tuple. -In order to remove such authorization, issue this command from a dom0 terminal -(for `qubes.Filecopy` service): +In order to remove such authorization, issue this command from a dom0 terminal (for `qubes.Filecopy` service): sudo nano /etc/qubes-rpc/policy/qubes.Filecopy -and then remove the first line(s) (before the first `##` comment) which are -the "Yes to All" results. +and then remove the first line(s) (before the first `##` comment) which are the "Yes to All" results. ### Qubes RPC example ### -We will show the necessary files to create an rpc call that adds two integers -on the target and returns back the result to the invoker. +We will show the necessary files to create an rpc call that adds two integers on the target and returns back the result to the invoker. * rpc client code (`/usr/bin/our_test_add_client`): @@ -334,17 +256,14 @@ on the target and returns back the result to the invoker. and we should get "3" as answer, after dom0 allows it. -**Note:** For a real world example of writing a qrexec service, see this -[blog post](https://blog.invisiblethings.org/2013/02/21/converting-untrusted-pdfs-into-trusted.html). +**Note:** For a real world example of writing a qrexec service, see this [blog post](https://blog.invisiblethings.org/2013/02/21/converting-untrusted-pdfs-into-trusted.html). ### Qubes RPC example - with argument usage ### -We will show the necessary files to create an rpc call that reads a specific file -from a predefined directory on the target. Besides really naive storage, it may -be a very simple password manager. -Additionally, in this example a simplified workflow will be used - server code -placed directly in the service definition file (in `/etc/qubes-rpc` directory). And -no separate client script will be used. +We will show the necessary files to create an rpc call that reads a specific file from a predefined directory on the target. +Besides really naive storage, it may be a very simple password manager. +Additionally, in this example a simplified workflow will be used - server code placed directly in the service definition file (in `/etc/qubes-rpc` directory). +And no separate client script will be used. * rpc server code (*/etc/qubes-rpc/test.File*) @@ -385,13 +304,11 @@ no separate client script will be used. # Qubes RPC internals # -(*This is about the implementation of qrexec v3. For the implementation of -qrexec v2, see [here](/doc/qrexec2/#qubes-rpc-internals).*) +(*This is about the implementation of qrexec v3. +For the implementation of qrexec v2, see [here](/doc/qrexec2/#qubes-rpc-internals).*) -Qrexec framework consists of a number of processes communicating with each -other using common IPC protocol (described in detail below). Components -residing in the same domain (`qrexec-client-vm` to `qrexec-agent`, `qrexec-client` to `qrexec-daemon`) use pipes as the underlying transport medium, -while components in separate domains (`qrexec-daemon` to `qrexec-agent`, data channel between `qrexec-agent`s) use vchan link. +Qrexec framework consists of a number of processes communicating with each other using common IPC protocol (described in detail below). +Components residing in the same domain (`qrexec-client-vm` to `qrexec-agent`, `qrexec-client` to `qrexec-daemon`) use pipes as the underlying transport medium, while components in separate domains (`qrexec-daemon` to `qrexec-agent`, data channel between `qrexec-agent`s) use vchan link. Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951), it is not possible to establish qrexec connection back to the source domain. @@ -457,16 +374,15 @@ by an optional data packet. uint32_t len; /* data length */ }; -When two peers establish connection, the server sends `MSG_HELLO` followed by -`peer_info` struct: +When two peers establish connection, the server sends `MSG_HELLO` followed by `peer_info` struct: struct peer_info { uint32_t version; /* qrexec protocol version */ }; -The client then should reply with its own `MSG_HELLO` and `peer_info`. The -lower of two versions define protocol used for this connection. If either side -does not support this version, the connection is closed. +The client then should reply with its own `MSG_HELLO` and `peer_info`. +The lower of two versions define protocol used for this connection. +If either side does not support this version, the connection is closed. Details of all possible use cases and the messages involved are described below. From 474d15f87ecfdc5b228417850131136bf73ff7d5 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 14:39:08 -0500 Subject: [PATCH 02/24] Revise qrexec introduction --- developer/services/qrexec3.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index ebc5c1b4..a19bcda2 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -16,16 +16,19 @@ redirect_from: - /wiki/Qrexec3Implementation/ --- -# Command execution in VMs # +# Secure communication between VMs: qrexec # (*This page is about qrexec v3. For qrexec v2, see [here](/doc/qrexec2/).*) The **qrexec** framework is used by core Qubes components to implement communication between domains. -Qubes domains are isolated by design, but there is a need for a mechanism to allow the administrative domain (dom0) to force command execution in another domain (VM). -For instance, when user selects an application from the KDE menu, it should be started in the selected VM. +Qubes domains are strictly isolated by design. +However, the OS needs a mechanism to allow the administrative domain (dom0) to force command execution in another domain (VM). +For instance, when a user selects an application from the KDE menu, it should start in the selected VM. Also, it is often useful to be able to pass stdin/stdout/stderr from an application running in a VM to dom0 (and the other way around). -In specific circumstances, Qubes allows VMs to be initiators of such communications (so, for example, a VM can notify dom0 that there are updates available for it). - +(For example, so that a VM can notify dom0 that there are updates available for it). +By default, Qubes allows VMs initiate such communications in specific circumstances. +The qrexec framework generalizes this process. +It allows users and developers to use and design secure inter-VM tools. ## Qrexec basics ## From 4ad035262db74d813d122e5db1fa8892d7881030 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 14:52:36 -0500 Subject: [PATCH 03/24] Revise "Qrexec basics" section --- developer/services/qrexec3.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index a19bcda2..e2c5a361 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -32,14 +32,15 @@ It allows users and developers to use and design secure inter-VM tools. ## Qrexec basics ## -Qrexec is built on top of vchan (a library providing data links between VMs). -During domain creation a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. -They are connected over **vchan** channel. -`qrexec-daemon` listens for connections from dom0 utility named `qrexec-client`. -Typically, the first thing that a `qrexec-client` instance does is to send a request to `qrexec-daemon` to start a process (let's name it `VMprocess`) with a given command line in a specified VM (`someVM`). -`qrexec-daemon` assigns unique vchan connection details and sends them both to `qrexec-client` (in dom0) and `qrexec-agent` (in `someVM`). -`qrexec-client` starts a vchan server which `qrexec-agent` connects to. -Since then, stdin/stdout/stderr from the VMprocess is passed via vchan between `qrexec-agent` and the `qrexec-client` process. +Qrexec is built on top of *vchan*, a Xen library providing data links between VMs. +During domain creation, a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. +They are connected over a **vchan** channel. +`qrexec-daemon` listens for connections from a dom0 utility named `qrexec-client`. +Let's say we want to start a process (call it `VMprocess`) in a VM (`someVM`). +Typically, the first thing that a `qrexec-client` instance does is to send a request to the `qrexec-daemon`, which in turn relays it to `qrexec-agent` running in `someVM`. +`qrexec-daemon` assigns unique vchan connection details and sends them to both `qrexec-client` (in dom0) and `qrexec-agent` (in `someVM`). +`qrexec-client` starts a vchan server, which `qrexec-agent` then connects to. +Once this channel is established, stdin/stdout/stderr from the VMprocess is passed between `qrexec-agent` and the `qrexec-client` process. So, for example, executing in dom0: From 56a9ca22995f1457195c50d488d84b9f1016e3fe Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 17:58:45 -0500 Subject: [PATCH 04/24] Capitalize "RPC" in main text throughout qrexec doc also capitalizes the term in bash comments, but maintains the string as in in file paths and other terms --- developer/services/qrexec3.md | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index e2c5a361..478ea4a4 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -61,7 +61,7 @@ Number of available vchan channels is the limiting factor here, it depends on th ## Qubes RPC services ## -Some tasks (like inter-vm file copy) share the same rpc-like structure: a process in one VM (say, file sender) needs to invoke and send/receive data to some process in other VM (say, file receiver). +Some tasks (like inter-vm file copy) share the same RPC-like structure: a process in one VM (say, file sender) needs to invoke and send/receive data to some process in other VM (say, file receiver). Thus, the Qubes RPC framework was created, facilitating such actions. Obviously, inter-VM communication must be tightly controlled to prevent one VM from taking control over other, possibly more privileged, VM. @@ -70,19 +70,19 @@ Then, it is natural to reuse the already-existing qrexec framework. Also, note that bare qrexec provides `VM <-> dom0` connectivity, but the command execution is always initiated by dom0. There are cases when VM needs to invoke and send data to a command in dom0 (e.g. to pass information on newly installed `.desktop` files). -Thus, the framework allows dom0 to be the rpc target as well. +Thus, the framework allows dom0 to be the RPC target as well. -Thanks to the framework, RPC programs are very simple -- both rpc client and server just use their stdin/stdout to pass data. +Thanks to the framework, RPC programs are very simple -- both RPC client and server just use their stdin/stdout to pass data. The framework does all the inner work to connect these processes to each other via `qrexec-daemon` and `qrexec-agent`. -Additionally, disposable VMs are tightly integrated -- rpc to a DisposableVM is identical to rpc to a normal domain, all one needs is to pass `$dispvm` as the remote domain name. +Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is identical to RPC to a normal domain, all one needs is to pass `$dispvm` as the remote domain name. ## Qubes RPC administration ## (*TODO: fix for non-linux dom0*) -In dom0, there is a bunch of files in `/etc/qubes-rpc/policy` directory, whose names describe the available rpc actions. -Their content is the rpc access policy database. +In dom0, there is a bunch of files in `/etc/qubes-rpc/policy` directory, whose names describe the available RPC actions. +Their content is the RPC access policy database. Currently defined actions are: qubes.ClipboardPaste @@ -111,7 +111,7 @@ These files contain lines with the following format: You can specify srcvm and destvm by name, or by one of `$anyvm`, `$dispvm`, `dom0` reserved keywords (note string `dom0` does not match the `$anyvm` pattern; all other names do). Only `$anyvm` keyword makes sense in srcvm field (service calls from dom0 are currently always allowed, `$dispvm` means "new VM created for this particular request," so it is never a source of request). Currently there is no way to specify source VM by type. -Whenever a rpc request for action X is received, the first line in `/etc/qubes-rpc/policy/X` that match srcvm/destvm is consulted to determine whether to allow rpc, what user account the program should run in target VM under, and what VM to redirect the execution to. +Whenever a RPC request for action X is received, the first line in `/etc/qubes-rpc/policy/X` that match srcvm/destvm is consulted to determine whether to allow RPC, what user account the program should run in target VM under, and what VM to redirect the execution to. Note that if the request is redirected (`target=` parameter), policy action remains the same - even if there is another rule which would otherwise deny such request. If the policy file does not exist, user is prompted to create one; if still there is no policy file after prompting, the action is denied. @@ -121,15 +121,15 @@ In the src VM, one should invoke the client via: /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME rpc_client_path client arguments -Note that only stdin/stdout is passed between rpc server and client -- notably, no command line arguments are passed. +Note that only stdin/stdout is passed between RPC server and client -- notably, no command line arguments are passed. Source VM name is specified by `QREXEC_REMOTE_DOMAIN` environment variable. By default, stderr of client and server is logged to respective `/var/log/qubes/qrexec.XID` files. It is also possible to call service without specific client program - in which case server stdin/out will be connected with the terminal: /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME -Be very careful when coding and adding a new rpc service. -Unless the offered functionality equals full control over the target (it is the case with e.g. `qubes.VMShell` action), any vulnerability in an rpc server can be fatal to Qubes security. +Be very careful when coding and adding a new RPC service. +Unless the offered functionality equals full control over the target (it is the case with e.g. `qubes.VMShell` action), any vulnerability in an RPC server can be fatal to Qubes security. On the other hand, this mechanism allows to delegate processing of untrusted input to less privileged (or disposable) AppVMs, thus wise usage of it increases security. For example, this command will run the `firefox` command in a DisposableVM based on `work`: @@ -232,19 +232,19 @@ and then remove the first line(s) (before the first `##` comment) which are the ### Qubes RPC example ### -We will show the necessary files to create an rpc call that adds two integers on the target and returns back the result to the invoker. +We will show the necessary files to create an RPC call that adds two integers on the target and returns back the result to the invoker. - * rpc client code (`/usr/bin/our_test_add_client`): + * RPC client code (`/usr/bin/our_test_add_client`): #!/bin/sh - echo $1 $2 # pass data to rpc server - exec cat >&$SAVED_FD_1 # print result to the original stdout, not to the other rpc endpoint + echo $1 $2 # pass data to RPC server + exec cat >&$SAVED_FD_1 # print result to the original stdout, not to the other RPC endpoint - * rpc server code (*/usr/bin/our\_test\_add\_server*) + * RPC server code (*/usr/bin/our\_test\_add\_server*) #!/bin/sh - read arg1 arg2 # read from stdin, which is received from the rpc client - echo $(($arg1+$arg2)) # print to stdout - so, pass to the rpc client + read arg1 arg2 # read from stdin, which is received from the RPC client + echo $(($arg1+$arg2)) # print to stdout - so, pass to the RPC client * policy file in dom0 (*/etc/qubes-rpc/policy/test.Add* ) @@ -254,7 +254,7 @@ We will show the necessary files to create an rpc call that adds two integers on /usr/bin/our_test_add_server - * invoke rpc via + * invoke RPC via /usr/lib/qubes/qrexec-client-vm target_vm test.Add /usr/bin/our_test_add_client 1 2 @@ -264,12 +264,12 @@ and we should get "3" as answer, after dom0 allows it. ### Qubes RPC example - with argument usage ### -We will show the necessary files to create an rpc call that reads a specific file from a predefined directory on the target. +We will show the necessary files to create an RPC call that reads a specific file from a predefined directory on the target. Besides really naive storage, it may be a very simple password manager. Additionally, in this example a simplified workflow will be used - server code placed directly in the service definition file (in `/etc/qubes-rpc` directory). And no separate client script will be used. - * rpc server code (*/etc/qubes-rpc/test.File*) + * RPC server code (*/etc/qubes-rpc/test.File*) #!/bin/sh argument="$1" # service argument, also available as $QREXEC_SERVICE_ARGUMENT @@ -294,13 +294,13 @@ And no separate client script will be used. $anyvm $anyvm deny - * invoke rpc from `source_vm1` via + * invoke RPC from `source_vm1` via /usr/lib/qubes/qrexec-client-vm target_vm test.File+testfile1 and we should get content of `/home/user/rpc-file-storage/testfile1` as answer. - * also possible to invoke rpc from `source_vm2` via + * also possible to invoke RPC from `source_vm2` via /usr/lib/qubes/qrexec-client-vm target_vm test.File+testfile2 From a2b6838a989c58060d97e3171c76f0beca3ee746 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 18:03:09 -0500 Subject: [PATCH 05/24] Comment out todo item in qrexec3.md --- developer/services/qrexec3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index 478ea4a4..a9559811 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -79,7 +79,7 @@ Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is ## Qubes RPC administration ## -(*TODO: fix for non-linux dom0*) + In dom0, there is a bunch of files in `/etc/qubes-rpc/policy` directory, whose names describe the available RPC actions. Their content is the RPC access policy database. From 724b8a32f0ebad58d24a4bfea43d2540e1433a16 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 18:35:57 -0500 Subject: [PATCH 06/24] Revise `qvm-client` command description and examples --- developer/services/qrexec3.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index a9559811..fea7b2ea 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -16,7 +16,7 @@ redirect_from: - /wiki/Qrexec3Implementation/ --- -# Secure communication between VMs: qrexec # +# Qrexec: command execution in VMs # (*This page is about qrexec v3. For qrexec v2, see [here](/doc/qrexec2/).*) @@ -30,7 +30,7 @@ By default, Qubes allows VMs initiate such communications in specific circumstan The qrexec framework generalizes this process. It allows users and developers to use and design secure inter-VM tools. -## Qrexec basics ## +## Qrexec basics: architecture and examples ## Qrexec is built on top of *vchan*, a Xen library providing data links between VMs. During domain creation, a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. @@ -42,15 +42,20 @@ Typically, the first thing that a `qrexec-client` instance does is to send a req `qrexec-client` starts a vchan server, which `qrexec-agent` then connects to. Once this channel is established, stdin/stdout/stderr from the VMprocess is passed between `qrexec-agent` and the `qrexec-client` process. -So, for example, executing in dom0: +The `qrexec-client` command is used to make connections to VMs from dom0. +For example, the following command + + qrexec-client -e -d someVM user:'touch hello-world.txt' + +creates an empty file called `hello-world.txt` in the home folder of `someVM`. + +The string before the colon specifies what user to run the command as. +The `-e` flag tells `qrexec-client` to exit immediately after sending the execution request and receiving a status code from `qrexec-agent` (whether the process creation succeeded). +With this option, no further data is passed between the domains. +By contrast, the following command demonstrates an open channel between two VMs: in this case, a remote shell. qrexec-client -d someVM user:bash -allows to work with the remote shell. -The string before the first semicolon specifies what user to run the command as. -Adding `-e` on the `qrexec-client` command line results in mere command execution (no data passing), and `qrexec-client` exits immediately after sending the execution request and receiving status code from `qrexec-agent` (whether the process creation succeeded). -There is also the `-l local_program` flag -- with it, `qrexec-client` passes stdin/stdout of the remote process to the (spawned for this purpose) `local_program`, not to its own stdin/stdout. - The `qvm-run` command is heavily based on `qrexec-client`. It also takes care of additional activities, e.g. starting the domain if it is not up yet and starting the GUI daemon. Thus, it is usually more convenient to use `qvm-run`. From e2333b4e768dcc5822eb1be84925c6b2c9899045 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 18:46:21 -0500 Subject: [PATCH 07/24] Remove extra characters in qrexec doc section titles (cosmetic) --- developer/services/qrexec3.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index fea7b2ea..442d88f9 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -16,7 +16,7 @@ redirect_from: - /wiki/Qrexec3Implementation/ --- -# Qrexec: command execution in VMs # +# Qrexec: command execution in VMs (*This page is about qrexec v3. For qrexec v2, see [here](/doc/qrexec2/).*) @@ -30,7 +30,7 @@ By default, Qubes allows VMs initiate such communications in specific circumstan The qrexec framework generalizes this process. It allows users and developers to use and design secure inter-VM tools. -## Qrexec basics: architecture and examples ## +## Qrexec basics: architecture and examples Qrexec is built on top of *vchan*, a Xen library providing data links between VMs. During domain creation, a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. @@ -64,7 +64,7 @@ There can be almost arbitrary number of `qrexec-client` processes for a domain ( Number of available vchan channels is the limiting factor here, it depends on the underlying hypervisor. -## Qubes RPC services ## +## Qubes RPC services Some tasks (like inter-vm file copy) share the same RPC-like structure: a process in one VM (say, file sender) needs to invoke and send/receive data to some process in other VM (say, file receiver). Thus, the Qubes RPC framework was created, facilitating such actions. @@ -82,7 +82,7 @@ The framework does all the inner work to connect these processes to each other v Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is identical to RPC to a normal domain, all one needs is to pass `$dispvm` as the remote domain name. -## Qubes RPC administration ## +## Qubes RPC administration @@ -222,7 +222,7 @@ This means it is also possible to install a different script for a particular se See below for an example service using an argument. -### Revoking "Yes to All" authorization ### +### Revoking "Yes to All" authorization Qubes RPC policy supports "ask" action. This will prompt the user whether given RPC call should be allowed. @@ -235,7 +235,7 @@ In order to remove such authorization, issue this command from a dom0 terminal ( and then remove the first line(s) (before the first `##` comment) which are the "Yes to All" results. -### Qubes RPC example ### +### Qubes RPC example We will show the necessary files to create an RPC call that adds two integers on the target and returns back the result to the invoker. @@ -267,7 +267,7 @@ and we should get "3" as answer, after dom0 allows it. **Note:** For a real world example of writing a qrexec service, see this [blog post](https://blog.invisiblethings.org/2013/02/21/converting-untrusted-pdfs-into-trusted.html). -### Qubes RPC example - with argument usage ### +### Qubes RPC example - with argument usage We will show the necessary files to create an RPC call that reads a specific file from a predefined directory on the target. Besides really naive storage, it may be a very simple password manager. @@ -321,7 +321,7 @@ Components residing in the same domain (`qrexec-client-vm` to `qrexec-agent`, `q Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951), it is not possible to establish qrexec connection back to the source domain. -## Dom0 tools implementation ## +## Dom0 tools implementation * `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active domain. Responsible for: @@ -355,7 +355,7 @@ Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951 **Note:** None of the above tools are designed to be used by users directly. -## VM tools implementation ## +## VM tools implementation * `qrexec-agent`: One instance runs in each active domain. Responsible for: * Handling service requests from `qrexec-client-vm` and passing them to @@ -372,7 +372,7 @@ Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951 are connected to the remote service endpoint. -## Qrexec protocol details ## +## Qrexec protocol details Qrexec protocol is message-based. All messages share a common header followed by an optional data packet. @@ -396,7 +396,7 @@ If either side does not support this version, the connection is closed. Details of all possible use cases and the messages involved are described below. -### dom0: request execution of `some_command` in domX and pass stdin/stdout ### +### dom0: request execution of `some_command` in domX and pass stdin/stdout - **dom0**: `qrexec-client` is invoked in **dom0** as follows: @@ -462,7 +462,7 @@ associated input/output pipe. (**int**). `qrexec-agent` then disconnects from the data vchan. -### domY: invoke execution of qubes service `qubes.SomeRpc` in domX and pass stdin/stdout ### +### domY: invoke execution of qubes service `qubes.SomeRpc` in domX and pass stdin/stdout - **domY**: `qrexec-client-vm` is invoked as follows: From 53e647ca7057dcfc4755f2695e18b1b0013acd79 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 19:20:50 -0500 Subject: [PATCH 08/24] Fix lineation in Qubes RPC implementation and protocol details --- developer/services/qrexec3.md | 216 ++++++++++------------------------ 1 file changed, 61 insertions(+), 155 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index 442d88f9..38350b1f 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -63,7 +63,6 @@ Thus, it is usually more convenient to use `qvm-run`. There can be almost arbitrary number of `qrexec-client` processes for a domain (so, connected to the same `qrexec-daemon`, same domain) -- their data is multiplexed independently. Number of available vchan channels is the limiting factor here, it depends on the underlying hypervisor. - ## Qubes RPC services Some tasks (like inter-vm file copy) share the same RPC-like structure: a process in one VM (say, file sender) needs to invoke and send/receive data to some process in other VM (say, file receiver). @@ -81,7 +80,6 @@ Thanks to the framework, RPC programs are very simple -- both RPC client and ser The framework does all the inner work to connect these processes to each other via `qrexec-daemon` and `qrexec-agent`. Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is identical to RPC to a normal domain, all one needs is to pass `$dispvm` as the remote domain name. - ## Qubes RPC administration @@ -234,7 +232,6 @@ In order to remove such authorization, issue this command from a dom0 terminal ( and then remove the first line(s) (before the first `##` comment) which are the "Yes to All" results. - ### Qubes RPC example We will show the necessary files to create an RPC call that adds two integers on the target and returns back the result to the invoker. @@ -311,71 +308,49 @@ And no separate client script will be used. But when invoked with other argument or from different VM, it should be denied. -# Qubes RPC internals # +# Qubes RPC internals -(*This is about the implementation of qrexec v3. -For the implementation of qrexec v2, see [here](/doc/qrexec2/#qubes-rpc-internals).*) +(*This is about the implementation of qrexec v3. For the implementation of qrexec v2, see [here](/doc/qrexec2/#qubes-rpc-internals).*) Qrexec framework consists of a number of processes communicating with each other using common IPC protocol (described in detail below). Components residing in the same domain (`qrexec-client-vm` to `qrexec-agent`, `qrexec-client` to `qrexec-daemon`) use pipes as the underlying transport medium, while components in separate domains (`qrexec-daemon` to `qrexec-agent`, data channel between `qrexec-agent`s) use vchan link. Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951), it is not possible to establish qrexec connection back to the source domain. - ## Dom0 tools implementation - * `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active - domain. Responsible for: - * Handling execution and service requests from **dom0** (source: - `qrexec-client`). - * Handling service requests from the associated domain (source: - `qrexec-client-vm`, then `qrexec-agent`). + * `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active domain. Responsible for: + * Handling execution and service requests from **dom0** (source: `qrexec-client`). + * Handling service requests from the associated domain (source: `qrexec-client-vm`, then `qrexec-agent`). * Command line: `qrexec-daemon domain-id domain-name [default user]` * `domain-id`: Numeric Qubes ID assigned to the associated domain. * `domain-name`: Associated domain name. - * `default user`: Optional. If passed, `qrexec-daemon` uses this user as - default for all execution requests that don't specify one. - * `/usr/lib/qubes/qrexec-policy`: Internal program used to evaluate the - RPC policy and deciding whether a RPC call should be allowed. - * `/usr/lib/qubes/qrexec-client`: Used to pass execution and service requests - to `qrexec-daemon`. Command line parameters: - * `-d target-domain-name`: Specifies the target for the execution/service - request. - * `-l local-program`: Optional. If present, `local-program` is executed - and its stdout/stdin are used when sending/receiving data to/from the - remote peer. - * `-e`: Optional. If present, stdout/stdin are not connected to the remote - peer. Only process creation status code is received. - * `-c `: used for connecting - a VM-VM service request by `qrexec-policy`. Details described below in - the service example. - * `cmdline`: Command line to pass to `qrexec-daemon` as the - execution/service request. Service request format is described below in - the service example. + * `default user`: Optional. If passed, `qrexec-daemon` uses this user as default for all execution requests that don't specify one. + * `/usr/lib/qubes/qrexec-policy`: Internal program used to evaluate the RPC policy and deciding whether a RPC call should be allowed. + * `/usr/lib/qubes/qrexec-client`: Used to pass execution and service requests to `qrexec-daemon`. Command line parameters: + * `-d target-domain-name`: Specifies the target for the execution/service request. + * `-l local-program`: Optional. If present, `local-program` is executed and its stdout/stdin are used when sending/receiving data to/from the remote peer. + * `-e`: Optional. If present, stdout/stdin are not connected to the remote peer. Only process creation status code is received. + * `-c `: used for connecting a VM-VM service request by `qrexec-policy`. Details described below in the service example. + * `cmdline`: Command line to pass to `qrexec-daemon` as the execution/service request. Service request format is described below in the service example. **Note:** None of the above tools are designed to be used by users directly. - ## VM tools implementation * `qrexec-agent`: One instance runs in each active domain. Responsible for: - * Handling service requests from `qrexec-client-vm` and passing them to - connected `qrexec-daemon` in dom0. + * Handling service requests from `qrexec-client-vm` and passing them to connected `qrexec-daemon` in dom0. * Executing associated `qrexec-daemon` execution/service requests. * Command line parameters: none. - * `qrexec-client-vm`: Runs in an active domain. Used to pass service requests - to `qrexec-agent`. + * `qrexec-client-vm`: Runs in an active domain. Used to pass service requests to `qrexec-agent`. * Command line: `qrexec-client-vm target-domain-name service-name local-program [local program arguments]` - * `target-domain-name`: Target domain for the service request. Source is - the current domain. + * `target-domain-name`: Target domain for the service request. Source is the current domain. * `service-name`: Requested service name. - * `local-program`: `local-program` is executed locally and its stdin/stdout - are connected to the remote service endpoint. - + * `local-program`: `local-program` is executed locally and its stdin/stdout are connected to the remote service endpoint. ## Qrexec protocol details -Qrexec protocol is message-based. All messages share a common header followed -by an optional data packet. +Qrexec protocol is message-based. +All messages share a common header followed by an optional data packet. /* uniform for all peers, data type depends on message type */ struct msg_header { @@ -395,28 +370,19 @@ If either side does not support this version, the connection is closed. Details of all possible use cases and the messages involved are described below. - ### dom0: request execution of `some_command` in domX and pass stdin/stdout -- **dom0**: `qrexec-client` is invoked in **dom0** as follows: +- **dom0**: `qrexec-client` is invoked in **dom0** as follows: - `qrexec-client -d domX [-l local_program] user:some_command` + qrexec-client -d domX [-l local_program] user:some_command` - - `user` may be substituted with the literal `DEFAULT`. In that case, - default Qubes user will be used to execute `some_command`. - -- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable -to **domX**. -- **dom0**: If `local_program` is set, `qrexec-client` executes it and uses -that child's stdin/stdout in place of its own when exchanging data with -`qrexec-agent` later. + `user` may be substituted with the literal `DEFAULT`. In that case, default Qubes user will be used to execute `some_command`. +- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable to **domX**. +- **dom0**: If `local_program` is set, `qrexec-client` executes it and uses that child's stdin/stdout in place of its own when exchanging data with `qrexec-agent` later. - **dom0**: `qrexec-client` connects to **domX**'s `qrexec-daemon`. -- **dom0**: `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` -to `qrexec-client`. -- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by -`peer_info` to `qrexec-daemon`. -- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by -`exec_params` to `qrexec-daemon`. +- **dom0**: `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` to `qrexec-client`. +- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by `peer_info` to `qrexec-daemon`. +- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to `qrexec-daemon`. /* variable size */ struct exec_params { @@ -427,90 +393,50 @@ to `qrexec-client`. In this case, `connect_domain` and `connect_port` are set to 0. -- **dom0**: `qrexec-daemon` replies to `qrexec-client` with -`MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` -field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` -is set to a vchan port allocated by `qrexec-daemon`. -- **dom0**: `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed -by `exec_params` to the associated **domX** `qrexec-agent` over -vchan. `connect_domain` is set to 0 (**dom0**), `connect_port` is the same -as sent to `qrexec-client`. `cmdline` is unchanged except that the literal -`DEFAULT` is replaced with actual user name, if present. +- **dom0**: `qrexec-daemon` replies to `qrexec-client` with `MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` is set to a vchan port allocated by `qrexec-daemon`. +- **dom0**: `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to the associated **domX** `qrexec-agent` over vchan. `connect_domain` is set to 0 (**dom0**), `connect_port` is the same as sent to `qrexec-client`. `cmdline` is unchanged except that the literal `DEFAULT` is replaced with actual user name, if present. - **dom0**: `qrexec-client` disconnects from `qrexec-daemon`. -- **dom0**: `qrexec-client` starts a vchan server using the details received -from `qrexec-daemon` and waits for connection from **domX**'s `qrexec-agent`. -- **domX**: `qrexec-agent` receives `MSG_EXEC_CMDLINE` header followed by -`exec_params` from `qrexec-daemon` over vchan. -- **domX**: `qrexec-agent` connects to `qrexec-client` over vchan using the -details from `exec_params`. -- **domX**: `qrexec-agent` executes `some_command` in **domX** and connects -the child's stdin/stdout to the data vchan. If the process creation fails, -`qrexec-agent` sends `MSG_DATA_EXIT_CODE` to `qrexec-client` followed by -the status code (**int**) and disconnects from the data vchan. -- Data read from `some_command`'s stdout is sent to the data vchan using -`MSG_DATA_STDOUT` by `qrexec-agent`. `qrexec-client` passes data received as -`MSG_DATA_STDOUT` to its own stdout (or to `local_program`'s stdin if used). -- `qrexec-client` sends data read from local stdin (or `local_program`'s -stdout if used) to `qrexec-agent` over the data vchan using -`MSG_DATA_STDIN`. `qrexec-agent` passes data received as `MSG_DATA_STDIN` -to `some_command`'s stdin. -- `MSG_DATA_STDOUT` or `MSG_DATA_STDIN` with data `len` field set to 0 in -`msg_header` is an EOF marker. Peer receiving such message should close the -associated input/output pipe. -- When `some_command` terminates, **domX**'s `qrexec-agent` sends -`MSG_DATA_EXIT_CODE` header to `qrexec-client` followed by the exit code -(**int**). `qrexec-agent` then disconnects from the data vchan. - +- **dom0**: `qrexec-client` starts a vchan server using the details received from `qrexec-daemon` and waits for connection from **domX**'s `qrexec-agent`. +- **domX**: `qrexec-agent` receives `MSG_EXEC_CMDLINE` header followed by `exec_params` from `qrexec-daemon` over vchan. +- **domX**: `qrexec-agent` connects to `qrexec-client` over vchan using the details from `exec_params`. +- **domX**: `qrexec-agent` executes `some_command` in **domX** and connects the child's stdin/stdout to the data vchan. If the process creation fails, `qrexec-agent` sends `MSG_DATA_EXIT_CODE` to `qrexec-client` followed by the status code (**int**) and disconnects from the data vchan. +- Data read from `some_command`'s stdout is sent to the data vchan using `MSG_DATA_STDOUT` by `qrexec-agent`. `qrexec-client` passes data received as `MSG_DATA_STDOUT` to its own stdout (or to `local_program`'s stdin if used). +- `qrexec-client` sends data read from local stdin (or `local_program`'s stdout if used) to `qrexec-agent` over the data vchan using `MSG_DATA_STDIN`. `qrexec-agent` passes data received as `MSG_DATA_STDIN` to `some_command`'s stdin. +- `MSG_DATA_STDOUT` or `MSG_DATA_STDIN` with data `len` field set to 0 in `msg_header` is an EOF marker. Peer receiving such message should close the associated input/output pipe. +- When `some_command` terminates, **domX**'s `qrexec-agent` sends `MSG_DATA_EXIT_CODE` header to `qrexec-client` followed by the exit code (**int**). `qrexec-agent` then disconnects from the data vchan. ### domY: invoke execution of qubes service `qubes.SomeRpc` in domX and pass stdin/stdout -- **domY**: `qrexec-client-vm` is invoked as follows: +- **domY**: `qrexec-client-vm` is invoked as follows: - `qrexec-client-vm domX qubes.SomeRpc local_program [params]` + qrexec-client-vm domX qubes.SomeRpc local_program [params] -- **domY**: `qrexec-client-vm` connects to `qrexec-agent` (via local -socket/named pipe). -- **domY**: `qrexec-client-vm` sends `trigger_service_params` data to -`qrexec-agent` (without filling the `request_id` field): +- **domY**: `qrexec-client-vm` connects to `qrexec-agent` (via local socket/named pipe). +- **domY**: `qrexec-client-vm` sends `trigger_service_params` data to `qrexec-agent` (without filling the `request_id` field): struct trigger_service_params { char service_name[64]; char target_domain[32]; struct service_params request_id; /* service request id */ }; - + struct service_params { char ident[32]; }; -- **domY**: `qrexec-agent` allocates a locally-unique (for this domain) -`request_id` (let's say `13`) and fills it in the `trigger_service_params` -struct received from `qrexec-client-vm`. -- **domY**: `qrexec-agent` sends `MSG_TRIGGER_SERVICE` header followed by -`trigger_service_params` to `qrexec-daemon` in **dom0** via vchan. -- **dom0**: **domY**'s `qrexec-daemon` executes `qrexec-policy`: `qrexec-policy -domY_id domY domX qubes.SomeRpc 13`. -- **dom0**: `qrexec-policy` evaluates if the RPC should be allowed or -denied. If the action is allowed it returns `0`, if the action is denied it -returns `1`. +- **domY**: `qrexec-agent` allocates a locally-unique (for this domain) `request_id` (let's say `13`) and fills it in the `trigger_service_params` struct received from `qrexec-client-vm`. +- **domY**: `qrexec-agent` sends `MSG_TRIGGER_SERVICE` header followed by `trigger_service_params` to `qrexec-daemon` in **dom0** via vchan. +- **dom0**: **domY**'s `qrexec-daemon` executes `qrexec-policy`: `qrexec-policy domY_id domY domX qubes.SomeRpc 13`. +- **dom0**: `qrexec-policy` evaluates if the RPC should be allowed or denied. If the action is allowed it returns `0`, if the action is denied it returns `1`. - **dom0**: **domY**'s `qrexec-daemon` checks the exit code of `qrexec-policy`. - - If `qrexec-policy` returned **not** `0`: **domY**'s `qrexec-daemon` - sends `MSG_SERVICE_REFUSED` header followed by `service_params` to - **domY**'s `qrexec-agent`. `service_params.ident` is identical to the one - received. **domY**'s `qrexec-agent` disconnects its `qrexec-client-vm` - and RPC processing is finished. + - If `qrexec-policy` returned **not** `0`: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_REFUSED` header followed by `service_params` to **domY**'s `qrexec-agent`. `service_params.ident` is identical to the one received. **domY**'s `qrexec-agent` disconnects its `qrexec-client-vm` and RPC processing is finished. - If `qrexec-policy` returned `0`, RPC processing continues. -- **dom0**: if `qrexec-policy` allowed the RPC, it executed `qrexec-client --d domX -c 13,domY,domY_id user:QUBESRPC qubes.SomeRpc domY`. -- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable -to **domX**. +- **dom0**: if `qrexec-policy` allowed the RPC, it executed `qrexec-client -d domX -c 13,domY,domY_id user:QUBESRPC qubes.SomeRpc domY`. +- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable to **domX**. - **dom0**: `qrexec-client` connects to **domX**'s `qrexec-daemon`. -- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_HELLO` header followed by -`peer_info` to `qrexec-client`. -- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by -`peer_info` to **domX**'s`qrexec-daemon`. -- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by -`exec_params` to **domX**'s`qrexec-daemon` +- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` to `qrexec-client`. +- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by `peer_info` to **domX**'s`qrexec-daemon`. +- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to **domX**'s`qrexec-daemon` /* variable size */ struct exec_params { @@ -519,35 +445,15 @@ to **domX**. char cmdline[0]; /* command line to execute, size = msg_header.len - sizeof(struct exec_params) */ }; - In this case, `connect_domain` is set to id of **domY** (from the `-c` - parameter) and `connect_port` is set to 0. `cmdline` field contains the - RPC to execute, in this case `user:QUBESRPC qubes.SomeRpc domY`. + In this case, `connect_domain` is set to id of **domY** (from the `-c` parameter) and `connect_port` is set to 0. `cmdline` field contains the RPC to execute, in this case `user:QUBESRPC qubes.SomeRpc domY`. -- **dom0**: **domX**'s `qrexec-daemon` replies to `qrexec-client` with -`MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` -field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` -is set to a vchan port allocated by **domX**'s `qrexec-daemon`. -- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header -followed by `exec_params` to **domX**'s `qrexec-agent`. `connect_domain` -and `connect_port` fields are the same as in the step above. `cmdline` is -set to the one received from `qrexec-client`, in this case `user:QUBESRPC -qubes.SomeRpc domY`. -- **dom0**: `qrexec-client` disconnects from **domX**'s `qrexec-daemon` -after receiving connection details. -- **dom0**: `qrexec-client` connects to **domY**'s `qrexec-daemon` and -exchanges `MSG_HELLO` as usual. -- **dom0**: `qrexec-client` sends `MSG_SERVICE_CONNECT` header followed by -`exec_params` to **domY**'s `qrexec-daemon`. `connect_domain` is set to ID -of **domX** (received from **domX**'s `qrexec-daemon`) and `connect_port` is -the one received as well. `cmdline` is set to request ID (`13` in this case). -- **dom0**: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_CONNECT` header -followed by `exec_params` to **domY**'s `qrexec-agent`. Data fields are -unchanged from the step above. -- **domY**: `qrexec-agent` starts a vchan server on the port received in -the step above. It acts as a `qrexec-client` in this case because this is -a VM-VM connection. -- **domX**: `qrexec-agent` connects to the vchan server of **domY**'s -`qrexec-agent` (connection details were received before from **domX**'s -`qrexec-daemon`). +- **dom0**: **domX**'s `qrexec-daemon` replies to `qrexec-client` with `MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` is set to a vchan port allocated by **domX**'s `qrexec-daemon`. +- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to **domX**'s `qrexec-agent`. `connect_domain` and `connect_port` fields are the same as in the step above. `cmdline` is set to the one received from `qrexec-client`, in this case `user:QUBESRPC qubes.SomeRpc domY`. +- **dom0**: `qrexec-client` disconnects from **domX**'s `qrexec-daemon` after receiving connection details. +- **dom0**: `qrexec-client` connects to **domY**'s `qrexec-daemon` and exchanges `MSG_HELLO` as usual. +- **dom0**: `qrexec-client` sends `MSG_SERVICE_CONNECT` header followed by `exec_params` to **domY**'s `qrexec-daemon`. `connect_domain` is set to ID of **domX** (received from **domX**'s `qrexec-daemon`) and `connect_port` is the one received as well. `cmdline` is set to request ID (`13` in this case). +- **dom0**: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_CONNECT` header followed by `exec_params` to **domY**'s `qrexec-agent`. Data fields are unchanged from the step above. +- **domY**: `qrexec-agent` starts a vchan server on the port received in the step above. It acts as a `qrexec-client` in this case because this is a VM-VM connection. +- **domX**: `qrexec-agent` connects to the vchan server of **domY**'s `qrexec-agent` (connection details were received before from **domX**'s `qrexec-daemon`). - After that, connection follows the flow of the previous example (dom0-VM). From 0b6b4bb2735e8effd4c9c0db24d102fb8b221c99 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 19:25:32 -0500 Subject: [PATCH 09/24] qrexec3.md: remove extra whitespace --- developer/services/qrexec3.md | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index 38350b1f..f169665b 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -318,34 +318,34 @@ Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951 ## Dom0 tools implementation - * `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active domain. Responsible for: - * Handling execution and service requests from **dom0** (source: `qrexec-client`). - * Handling service requests from the associated domain (source: `qrexec-client-vm`, then `qrexec-agent`). - * Command line: `qrexec-daemon domain-id domain-name [default user]` - * `domain-id`: Numeric Qubes ID assigned to the associated domain. - * `domain-name`: Associated domain name. - * `default user`: Optional. If passed, `qrexec-daemon` uses this user as default for all execution requests that don't specify one. - * `/usr/lib/qubes/qrexec-policy`: Internal program used to evaluate the RPC policy and deciding whether a RPC call should be allowed. - * `/usr/lib/qubes/qrexec-client`: Used to pass execution and service requests to `qrexec-daemon`. Command line parameters: - * `-d target-domain-name`: Specifies the target for the execution/service request. - * `-l local-program`: Optional. If present, `local-program` is executed and its stdout/stdin are used when sending/receiving data to/from the remote peer. - * `-e`: Optional. If present, stdout/stdin are not connected to the remote peer. Only process creation status code is received. - * `-c `: used for connecting a VM-VM service request by `qrexec-policy`. Details described below in the service example. - * `cmdline`: Command line to pass to `qrexec-daemon` as the execution/service request. Service request format is described below in the service example. +* `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active domain. Responsible for: + * Handling execution and service requests from **dom0** (source: `qrexec-client`). + * Handling service requests from the associated domain (source: `qrexec-client-vm`, then `qrexec-agent`). +* Command line: `qrexec-daemon domain-id domain-name [default user]` +* `domain-id`: Numeric Qubes ID assigned to the associated domain. +* `domain-name`: Associated domain name. +* `default user`: Optional. If passed, `qrexec-daemon` uses this user as default for all execution requests that don't specify one. +* `/usr/lib/qubes/qrexec-policy`: Internal program used to evaluate the RPC policy and deciding whether a RPC call should be allowed. +* `/usr/lib/qubes/qrexec-client`: Used to pass execution and service requests to `qrexec-daemon`. Command line parameters: + * `-d target-domain-name`: Specifies the target for the execution/service request. + * `-l local-program`: Optional. If present, `local-program` is executed and its stdout/stdin are used when sending/receiving data to/from the remote peer. + * `-e`: Optional. If present, stdout/stdin are not connected to the remote peer. Only process creation status code is received. + * `-c `: used for connecting a VM-VM service request by `qrexec-policy`. Details described below in the service example. + * `cmdline`: Command line to pass to `qrexec-daemon` as the execution/service request. Service request format is described below in the service example. **Note:** None of the above tools are designed to be used by users directly. ## VM tools implementation - * `qrexec-agent`: One instance runs in each active domain. Responsible for: - * Handling service requests from `qrexec-client-vm` and passing them to connected `qrexec-daemon` in dom0. - * Executing associated `qrexec-daemon` execution/service requests. - * Command line parameters: none. - * `qrexec-client-vm`: Runs in an active domain. Used to pass service requests to `qrexec-agent`. - * Command line: `qrexec-client-vm target-domain-name service-name local-program [local program arguments]` - * `target-domain-name`: Target domain for the service request. Source is the current domain. - * `service-name`: Requested service name. - * `local-program`: `local-program` is executed locally and its stdin/stdout are connected to the remote service endpoint. +* `qrexec-agent`: One instance runs in each active domain. Responsible for: + * Handling service requests from `qrexec-client-vm` and passing them to connected `qrexec-daemon` in dom0. + * Executing associated `qrexec-daemon` execution/service requests. +* Command line parameters: none. +* `qrexec-client-vm`: Runs in an active domain. Used to pass service requests to `qrexec-agent`. +* Command line: `qrexec-client-vm target-domain-name service-name local-program [local program arguments]` +* `target-domain-name`: Target domain for the service request. Source is the current domain. +* `service-name`: Requested service name. +* `local-program`: `local-program` is executed locally and its stdin/stdout are connected to the remote service endpoint. ## Qrexec protocol details From c1d2df7ec90050c9bc591de8f5f11c5cf9d5bda0 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 19:39:28 -0500 Subject: [PATCH 10/24] Revise Qubes RPC administration section of qrexec3.md --- developer/services/qrexec3.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index f169665b..e908a2ed 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -84,9 +84,9 @@ Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is -In dom0, there is a bunch of files in `/etc/qubes-rpc/policy` directory, whose names describe the available RPC actions. -Their content is the RPC access policy database. -Currently defined actions are: +The dom0 directory `/etc/qubes-rpc/policy/` contains a group of files for available RPC actions. +Together their contents make up the RPC access policy database. +Currently, the defined actions are: qubes.ClipboardPaste qubes.Filecopy @@ -111,12 +111,15 @@ These files contain lines with the following format: srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to] -You can specify srcvm and destvm by name, or by one of `$anyvm`, `$dispvm`, `dom0` reserved keywords (note string `dom0` does not match the `$anyvm` pattern; all other names do). -Only `$anyvm` keyword makes sense in srcvm field (service calls from dom0 are currently always allowed, `$dispvm` means "new VM created for this particular request," so it is never a source of request). +You can specify srcvm and destvm by name or by one of three reserved keywords: `$anyvm`, `$dispvm`, and `dom0` (without the `$`). +Only `$anyvm` keyword makes sense in srcvm field. +(Service calls from dom0 are currently always allowed, `$dispvm` means "new VM created for this particular request," so it is never a source of request.) Currently there is no way to specify source VM by type. -Whenever a RPC request for action X is received, the first line in `/etc/qubes-rpc/policy/X` that match srcvm/destvm is consulted to determine whether to allow RPC, what user account the program should run in target VM under, and what VM to redirect the execution to. +Whenever a RPC request for an action is received, the domain checks the first matching line of the relevant file in `/etc/qubes-rpc/policy/` to determine access: +whether to allow the request, what VM to redirect the execution to, and what user account the program should run under. Note that if the request is redirected (`target=` parameter), policy action remains the same - even if there is another rule which would otherwise deny such request. -If the policy file does not exist, user is prompted to create one; if still there is no policy file after prompting, the action is denied. +If the policy file does not exist, the user is prompted to create one. +If still there is no policy file after prompting, the action is denied. In the target VM, the `/etc/qubes-rpc/RPC_ACTION_NAME` must exist, containing the file name of the program that will be invoked, or being that program itself - in which case it must have executable permission set (`chmod +x`). From b4550e6c215161995794bdb19a0b151731f9cf69 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 8 Aug 2019 19:48:38 -0500 Subject: [PATCH 11/24] Edit RPC services section of qrexec docs --- developer/services/qrexec3.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index e908a2ed..3b99ef10 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -65,10 +65,10 @@ Number of available vchan channels is the limiting factor here, it depends on th ## Qubes RPC services -Some tasks (like inter-vm file copy) share the same RPC-like structure: a process in one VM (say, file sender) needs to invoke and send/receive data to some process in other VM (say, file receiver). -Thus, the Qubes RPC framework was created, facilitating such actions. +Some common tasks (like copying files between VMs) have an RPC-like structure: a process in one VM (say, the file sender) needs to invoke and send/receive data to some process in other VM (say, the file receiver). +The Qubes RPC framework was created to securely facilite a range of such actions. -Obviously, inter-VM communication must be tightly controlled to prevent one VM from taking control over other, possibly more privileged, VM. +Obviously, inter-VM communication must be tightly controlled to prevent one VM from taking control of another, possibly more privileged, VM. Therefore the design decision was made to pass all control communication via dom0, that can enforce proper authorization. Then, it is natural to reuse the already-existing qrexec framework. @@ -84,7 +84,7 @@ Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is -The dom0 directory `/etc/qubes-rpc/policy/` contains a group of files for available RPC actions. +The dom0 directory `/etc/qubes-rpc/policy/` contains files for each available RPC action. Together their contents make up the RPC access policy database. Currently, the defined actions are: From ebf34a63906ad03b67c0f387efb9666f655a5a6e Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Fri, 9 Aug 2019 09:12:15 -0500 Subject: [PATCH 12/24] Clarify description of qrexec-client example --- developer/services/qrexec3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index 3b99ef10..b932ca89 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -52,7 +52,7 @@ creates an empty file called `hello-world.txt` in the home folder of `someVM`. The string before the colon specifies what user to run the command as. The `-e` flag tells `qrexec-client` to exit immediately after sending the execution request and receiving a status code from `qrexec-agent` (whether the process creation succeeded). With this option, no further data is passed between the domains. -By contrast, the following command demonstrates an open channel between two VMs: in this case, a remote shell. +By contrast, the following command demonstrates an open channel between dom0 and someVM (in this case, a remote shell): qrexec-client -d someVM user:bash From 64769962271aef1eae3e7bc3380822f65f9f5025 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Fri, 9 Aug 2019 17:18:00 -0500 Subject: [PATCH 13/24] Rewrite toy example of creating a qrexec service Gave the section more structure. Lightly edited the example code. --- developer/services/qrexec3.md | 39 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/developer/services/qrexec3.md b/developer/services/qrexec3.md index b932ca89..be639f86 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec3.md @@ -237,33 +237,38 @@ and then remove the first line(s) (before the first `##` comment) which are the ### Qubes RPC example -We will show the necessary files to create an RPC call that adds two integers on the target and returns back the result to the invoker. +As a demonstration, we can create an RPC service that adds two integers in a target domain (the server, call it "anotherVM") and returns back the result to the invoker (the client, "someVM"). +In someVM, create a file with the following contents and save it with the path `/usr/bin/our_test_add_client`: - * RPC client code (`/usr/bin/our_test_add_client`): + #!/bin/sh + echo $1 $2 # pass data to RPC server + exec cat >&$SAVED_FD_1 # print result to the original stdout, not to the other RPC endpoint - #!/bin/sh - echo $1 $2 # pass data to RPC server - exec cat >&$SAVED_FD_1 # print result to the original stdout, not to the other RPC endpoint +Our server will be anotherVM at `/usr/bin/our_test_add_server`. +The code for this file is: - * RPC server code (*/usr/bin/our\_test\_add\_server*) + #!/bin/sh + read arg1 arg2 # read from stdin, which is received from the RPC client + echo $(($arg1+$arg2)) # print to stdout, which is passed to the RPC client - #!/bin/sh - read arg1 arg2 # read from stdin, which is received from the RPC client - echo $(($arg1+$arg2)) # print to stdout - so, pass to the RPC client +We'll need to create a service called `test.Add` with its own definition and policy file in dom0. +In dom0 add the following test to `/etc/qubes-rpc/policy/test.Add`: - * policy file in dom0 (*/etc/qubes-rpc/policy/test.Add* ) + $anyvm $anyvm ask - $anyvm $anyvm ask +Now we need to define what the service does. +In this case, it should call our additing script. +We define the service with another one-line file, `/etc/qubes-rpc/test.Add`: - * server path definition ( */etc/qubes-rpc/test.Add*) + /usr/bin/our_test_add_server - /usr/bin/our_test_add_server +Before we make the call, ensure that the client and server scripts have executable permissions. +Now, invoke the RPC service! - * invoke RPC via + qrexec-client-vm anotherVM test.Add /usr/bin/our_test_add_client 1 2 - /usr/lib/qubes/qrexec-client-vm target_vm test.Add /usr/bin/our_test_add_client 1 2 - -and we should get "3" as answer, after dom0 allows it. +We should get "3" as answer. +(dom0 will ask for confirmation first.) **Note:** For a real world example of writing a qrexec service, see this [blog post](https://blog.invisiblethings.org/2013/02/21/converting-untrusted-pdfs-into-trusted.html). From 32621ef94f2ab38ce3ad8732e84ec009c71c4c98 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Sat, 10 Aug 2019 15:02:59 -0500 Subject: [PATCH 14/24] Split qrexec docs into multiple pages - Moves the "Qubes RPC internals" section to qrexec-internals.md. - Fixes redirects for new pages. - Retitles docs to reflect deprecation of qrexec2. - Revises the texts and links referring to deprecated qrexec2 docs. --- developer/services/qrexec-internals.md | 162 +++++++++++++++++++ developer/services/{qrexec3.md => qrexec.md} | 161 +----------------- developer/services/qrexec2.md | 3 +- doc.md | 3 +- 4 files changed, 169 insertions(+), 160 deletions(-) create mode 100644 developer/services/qrexec-internals.md rename developer/services/{qrexec3.md => qrexec.md} (58%) diff --git a/developer/services/qrexec-internals.md b/developer/services/qrexec-internals.md new file mode 100644 index 00000000..e13be781 --- /dev/null +++ b/developer/services/qrexec-internals.md @@ -0,0 +1,162 @@ +--- +layout: doc +title: Qubes RPC internals +permalink: /doc/qrexec-internals/ +redirect_from: +- /doc/qrexec3-implementation/ +- /en/doc/qrexec3-implementation/ +- /doc/Qrexec3Implementation/ +- /wiki/Qrexec3Implementation/ +--- + +# Qubes RPC internals + +(*This page details the current implementation of qrexec (qrexec3). +A [general introduction](/doc/qrexec/) to qrexec is also available. +For the implementation of qrexec2, see [here](/doc/qrexec2/#qubes-rpc-internals).*) + +Qrexec framework consists of a number of processes communicating with each other using common IPC protocol (described in detail below). +Components residing in the same domain (`qrexec-client-vm` to `qrexec-agent`, `qrexec-client` to `qrexec-daemon`) use pipes as the underlying transport medium, while components in separate domains (`qrexec-daemon` to `qrexec-agent`, data channel between `qrexec-agent`s) use vchan link. +Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951), it is not possible to establish qrexec connection back to the source domain. + +## Dom0 tools implementation + +* `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active domain. Responsible for: + * Handling execution and service requests from **dom0** (source: `qrexec-client`). + * Handling service requests from the associated domain (source: `qrexec-client-vm`, then `qrexec-agent`). +* Command line: `qrexec-daemon domain-id domain-name [default user]` +* `domain-id`: Numeric Qubes ID assigned to the associated domain. +* `domain-name`: Associated domain name. +* `default user`: Optional. If passed, `qrexec-daemon` uses this user as default for all execution requests that don't specify one. +* `/usr/lib/qubes/qrexec-policy`: Internal program used to evaluate the RPC policy and deciding whether a RPC call should be allowed. +* `/usr/lib/qubes/qrexec-client`: Used to pass execution and service requests to `qrexec-daemon`. Command line parameters: + * `-d target-domain-name`: Specifies the target for the execution/service request. + * `-l local-program`: Optional. If present, `local-program` is executed and its stdout/stdin are used when sending/receiving data to/from the remote peer. + * `-e`: Optional. If present, stdout/stdin are not connected to the remote peer. Only process creation status code is received. + * `-c `: used for connecting a VM-VM service request by `qrexec-policy`. Details described below in the service example. + * `cmdline`: Command line to pass to `qrexec-daemon` as the execution/service request. Service request format is described below in the service example. + +**Note:** None of the above tools are designed to be used by users directly. + +## VM tools implementation + +* `qrexec-agent`: One instance runs in each active domain. Responsible for: + * Handling service requests from `qrexec-client-vm` and passing them to connected `qrexec-daemon` in dom0. + * Executing associated `qrexec-daemon` execution/service requests. +* Command line parameters: none. +* `qrexec-client-vm`: Runs in an active domain. Used to pass service requests to `qrexec-agent`. +* Command line: `qrexec-client-vm target-domain-name service-name local-program [local program arguments]` +* `target-domain-name`: Target domain for the service request. Source is the current domain. +* `service-name`: Requested service name. +* `local-program`: `local-program` is executed locally and its stdin/stdout are connected to the remote service endpoint. + +## Qrexec protocol details + +Qrexec protocol is message-based. +All messages share a common header followed by an optional data packet. + + /* uniform for all peers, data type depends on message type */ + struct msg_header { + uint32_t type; /* message type */ + uint32_t len; /* data length */ + }; + +When two peers establish connection, the server sends `MSG_HELLO` followed by `peer_info` struct: + + struct peer_info { + uint32_t version; /* qrexec protocol version */ + }; + +The client then should reply with its own `MSG_HELLO` and `peer_info`. +The lower of two versions define protocol used for this connection. +If either side does not support this version, the connection is closed. + +Details of all possible use cases and the messages involved are described below. + +### dom0: request execution of `some_command` in domX and pass stdin/stdout + +- **dom0**: `qrexec-client` is invoked in **dom0** as follows: + + qrexec-client -d domX [-l local_program] user:some_command` + + `user` may be substituted with the literal `DEFAULT`. In that case, default Qubes user will be used to execute `some_command`. +- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable to **domX**. +- **dom0**: If `local_program` is set, `qrexec-client` executes it and uses that child's stdin/stdout in place of its own when exchanging data with `qrexec-agent` later. +- **dom0**: `qrexec-client` connects to **domX**'s `qrexec-daemon`. +- **dom0**: `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` to `qrexec-client`. +- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by `peer_info` to `qrexec-daemon`. +- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to `qrexec-daemon`. + + /* variable size */ + struct exec_params { + uint32_t connect_domain; /* target domain id */ + uint32_t connect_port; /* target vchan port for i/o exchange */ + char cmdline[0]; /* command line to execute, size = msg_header.len - sizeof(struct exec_params) */ + }; + + In this case, `connect_domain` and `connect_port` are set to 0. + +- **dom0**: `qrexec-daemon` replies to `qrexec-client` with `MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` is set to a vchan port allocated by `qrexec-daemon`. +- **dom0**: `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to the associated **domX** `qrexec-agent` over vchan. `connect_domain` is set to 0 (**dom0**), `connect_port` is the same as sent to `qrexec-client`. `cmdline` is unchanged except that the literal `DEFAULT` is replaced with actual user name, if present. +- **dom0**: `qrexec-client` disconnects from `qrexec-daemon`. +- **dom0**: `qrexec-client` starts a vchan server using the details received from `qrexec-daemon` and waits for connection from **domX**'s `qrexec-agent`. +- **domX**: `qrexec-agent` receives `MSG_EXEC_CMDLINE` header followed by `exec_params` from `qrexec-daemon` over vchan. +- **domX**: `qrexec-agent` connects to `qrexec-client` over vchan using the details from `exec_params`. +- **domX**: `qrexec-agent` executes `some_command` in **domX** and connects the child's stdin/stdout to the data vchan. If the process creation fails, `qrexec-agent` sends `MSG_DATA_EXIT_CODE` to `qrexec-client` followed by the status code (**int**) and disconnects from the data vchan. +- Data read from `some_command`'s stdout is sent to the data vchan using `MSG_DATA_STDOUT` by `qrexec-agent`. `qrexec-client` passes data received as `MSG_DATA_STDOUT` to its own stdout (or to `local_program`'s stdin if used). +- `qrexec-client` sends data read from local stdin (or `local_program`'s stdout if used) to `qrexec-agent` over the data vchan using `MSG_DATA_STDIN`. `qrexec-agent` passes data received as `MSG_DATA_STDIN` to `some_command`'s stdin. +- `MSG_DATA_STDOUT` or `MSG_DATA_STDIN` with data `len` field set to 0 in `msg_header` is an EOF marker. Peer receiving such message should close the associated input/output pipe. +- When `some_command` terminates, **domX**'s `qrexec-agent` sends `MSG_DATA_EXIT_CODE` header to `qrexec-client` followed by the exit code (**int**). `qrexec-agent` then disconnects from the data vchan. + +### domY: invoke execution of qubes service `qubes.SomeRpc` in domX and pass stdin/stdout + +- **domY**: `qrexec-client-vm` is invoked as follows: + + qrexec-client-vm domX qubes.SomeRpc local_program [params] + +- **domY**: `qrexec-client-vm` connects to `qrexec-agent` (via local socket/named pipe). +- **domY**: `qrexec-client-vm` sends `trigger_service_params` data to `qrexec-agent` (without filling the `request_id` field): + + struct trigger_service_params { + char service_name[64]; + char target_domain[32]; + struct service_params request_id; /* service request id */ + }; + + struct service_params { + char ident[32]; + }; + +- **domY**: `qrexec-agent` allocates a locally-unique (for this domain) `request_id` (let's say `13`) and fills it in the `trigger_service_params` struct received from `qrexec-client-vm`. +- **domY**: `qrexec-agent` sends `MSG_TRIGGER_SERVICE` header followed by `trigger_service_params` to `qrexec-daemon` in **dom0** via vchan. +- **dom0**: **domY**'s `qrexec-daemon` executes `qrexec-policy`: `qrexec-policy domY_id domY domX qubes.SomeRpc 13`. +- **dom0**: `qrexec-policy` evaluates if the RPC should be allowed or denied. If the action is allowed it returns `0`, if the action is denied it returns `1`. +- **dom0**: **domY**'s `qrexec-daemon` checks the exit code of `qrexec-policy`. + - If `qrexec-policy` returned **not** `0`: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_REFUSED` header followed by `service_params` to **domY**'s `qrexec-agent`. `service_params.ident` is identical to the one received. **domY**'s `qrexec-agent` disconnects its `qrexec-client-vm` and RPC processing is finished. + - If `qrexec-policy` returned `0`, RPC processing continues. +- **dom0**: if `qrexec-policy` allowed the RPC, it executed `qrexec-client -d domX -c 13,domY,domY_id user:QUBESRPC qubes.SomeRpc domY`. +- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable to **domX**. +- **dom0**: `qrexec-client` connects to **domX**'s `qrexec-daemon`. +- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` to `qrexec-client`. +- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by `peer_info` to **domX**'s`qrexec-daemon`. +- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to **domX**'s`qrexec-daemon` + + /* variable size */ + struct exec_params { + uint32_t connect_domain; /* target domain id */ + uint32_t connect_port; /* target vchan port for i/o exchange */ + char cmdline[0]; /* command line to execute, size = msg_header.len - sizeof(struct exec_params) */ + }; + + In this case, `connect_domain` is set to id of **domY** (from the `-c` parameter) and `connect_port` is set to 0. `cmdline` field contains the RPC to execute, in this case `user:QUBESRPC qubes.SomeRpc domY`. + +- **dom0**: **domX**'s `qrexec-daemon` replies to `qrexec-client` with `MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` is set to a vchan port allocated by **domX**'s `qrexec-daemon`. +- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to **domX**'s `qrexec-agent`. `connect_domain` and `connect_port` fields are the same as in the step above. `cmdline` is set to the one received from `qrexec-client`, in this case `user:QUBESRPC qubes.SomeRpc domY`. +- **dom0**: `qrexec-client` disconnects from **domX**'s `qrexec-daemon` after receiving connection details. +- **dom0**: `qrexec-client` connects to **domY**'s `qrexec-daemon` and exchanges `MSG_HELLO` as usual. +- **dom0**: `qrexec-client` sends `MSG_SERVICE_CONNECT` header followed by `exec_params` to **domY**'s `qrexec-daemon`. `connect_domain` is set to ID of **domX** (received from **domX**'s `qrexec-daemon`) and `connect_port` is the one received as well. `cmdline` is set to request ID (`13` in this case). +- **dom0**: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_CONNECT` header followed by `exec_params` to **domY**'s `qrexec-agent`. Data fields are unchanged from the step above. +- **domY**: `qrexec-agent` starts a vchan server on the port received in the step above. It acts as a `qrexec-client` in this case because this is a VM-VM connection. +- **domX**: `qrexec-agent` connects to the vchan server of **domY**'s `qrexec-agent` (connection details were received before from **domX**'s `qrexec-daemon`). +- After that, connection follows the flow of the previous example (dom0-VM). + diff --git a/developer/services/qrexec3.md b/developer/services/qrexec.md similarity index 58% rename from developer/services/qrexec3.md rename to developer/services/qrexec.md index be639f86..5c8bc11d 100644 --- a/developer/services/qrexec3.md +++ b/developer/services/qrexec.md @@ -1,22 +1,19 @@ --- layout: doc -title: Qrexec3 -permalink: /doc/qrexec3/ +title: Qrexec +permalink: /doc/qrexec/ redirect_from: - /en/doc/qrexec3/ - /doc/Qrexec3/ +- /doc/qrexec3/ - /wiki/Qrexec3/ - /doc/qrexec/ - /en/doc/qrexec/ - /doc/Qrexec/ - /wiki/Qrexec/ -- /doc/qrexec3-implementation/ -- /en/doc/qrexec3-implementation/ -- /doc/Qrexec3Implementation/ -- /wiki/Qrexec3Implementation/ --- -# Qrexec: command execution in VMs +# Qrexec: secure communication across domains (*This page is about qrexec v3. For qrexec v2, see [here](/doc/qrexec2/).*) @@ -315,153 +312,3 @@ And no separate client script will be used. /usr/lib/qubes/qrexec-client-vm target_vm test.File+testfile2 But when invoked with other argument or from different VM, it should be denied. - -# Qubes RPC internals - -(*This is about the implementation of qrexec v3. For the implementation of qrexec v2, see [here](/doc/qrexec2/#qubes-rpc-internals).*) - -Qrexec framework consists of a number of processes communicating with each other using common IPC protocol (described in detail below). -Components residing in the same domain (`qrexec-client-vm` to `qrexec-agent`, `qrexec-client` to `qrexec-daemon`) use pipes as the underlying transport medium, while components in separate domains (`qrexec-daemon` to `qrexec-agent`, data channel between `qrexec-agent`s) use vchan link. -Because of [vchan limitation](https://github.com/qubesos/qubes-issues/issues/951), it is not possible to establish qrexec connection back to the source domain. - -## Dom0 tools implementation - -* `/usr/lib/qubes/qrexec-daemon`: One instance is required for every active domain. Responsible for: - * Handling execution and service requests from **dom0** (source: `qrexec-client`). - * Handling service requests from the associated domain (source: `qrexec-client-vm`, then `qrexec-agent`). -* Command line: `qrexec-daemon domain-id domain-name [default user]` -* `domain-id`: Numeric Qubes ID assigned to the associated domain. -* `domain-name`: Associated domain name. -* `default user`: Optional. If passed, `qrexec-daemon` uses this user as default for all execution requests that don't specify one. -* `/usr/lib/qubes/qrexec-policy`: Internal program used to evaluate the RPC policy and deciding whether a RPC call should be allowed. -* `/usr/lib/qubes/qrexec-client`: Used to pass execution and service requests to `qrexec-daemon`. Command line parameters: - * `-d target-domain-name`: Specifies the target for the execution/service request. - * `-l local-program`: Optional. If present, `local-program` is executed and its stdout/stdin are used when sending/receiving data to/from the remote peer. - * `-e`: Optional. If present, stdout/stdin are not connected to the remote peer. Only process creation status code is received. - * `-c `: used for connecting a VM-VM service request by `qrexec-policy`. Details described below in the service example. - * `cmdline`: Command line to pass to `qrexec-daemon` as the execution/service request. Service request format is described below in the service example. - -**Note:** None of the above tools are designed to be used by users directly. - -## VM tools implementation - -* `qrexec-agent`: One instance runs in each active domain. Responsible for: - * Handling service requests from `qrexec-client-vm` and passing them to connected `qrexec-daemon` in dom0. - * Executing associated `qrexec-daemon` execution/service requests. -* Command line parameters: none. -* `qrexec-client-vm`: Runs in an active domain. Used to pass service requests to `qrexec-agent`. -* Command line: `qrexec-client-vm target-domain-name service-name local-program [local program arguments]` -* `target-domain-name`: Target domain for the service request. Source is the current domain. -* `service-name`: Requested service name. -* `local-program`: `local-program` is executed locally and its stdin/stdout are connected to the remote service endpoint. - -## Qrexec protocol details - -Qrexec protocol is message-based. -All messages share a common header followed by an optional data packet. - - /* uniform for all peers, data type depends on message type */ - struct msg_header { - uint32_t type; /* message type */ - uint32_t len; /* data length */ - }; - -When two peers establish connection, the server sends `MSG_HELLO` followed by `peer_info` struct: - - struct peer_info { - uint32_t version; /* qrexec protocol version */ - }; - -The client then should reply with its own `MSG_HELLO` and `peer_info`. -The lower of two versions define protocol used for this connection. -If either side does not support this version, the connection is closed. - -Details of all possible use cases and the messages involved are described below. - -### dom0: request execution of `some_command` in domX and pass stdin/stdout - -- **dom0**: `qrexec-client` is invoked in **dom0** as follows: - - qrexec-client -d domX [-l local_program] user:some_command` - - `user` may be substituted with the literal `DEFAULT`. In that case, default Qubes user will be used to execute `some_command`. -- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable to **domX**. -- **dom0**: If `local_program` is set, `qrexec-client` executes it and uses that child's stdin/stdout in place of its own when exchanging data with `qrexec-agent` later. -- **dom0**: `qrexec-client` connects to **domX**'s `qrexec-daemon`. -- **dom0**: `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` to `qrexec-client`. -- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by `peer_info` to `qrexec-daemon`. -- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to `qrexec-daemon`. - - /* variable size */ - struct exec_params { - uint32_t connect_domain; /* target domain id */ - uint32_t connect_port; /* target vchan port for i/o exchange */ - char cmdline[0]; /* command line to execute, size = msg_header.len - sizeof(struct exec_params) */ - }; - - In this case, `connect_domain` and `connect_port` are set to 0. - -- **dom0**: `qrexec-daemon` replies to `qrexec-client` with `MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` is set to a vchan port allocated by `qrexec-daemon`. -- **dom0**: `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to the associated **domX** `qrexec-agent` over vchan. `connect_domain` is set to 0 (**dom0**), `connect_port` is the same as sent to `qrexec-client`. `cmdline` is unchanged except that the literal `DEFAULT` is replaced with actual user name, if present. -- **dom0**: `qrexec-client` disconnects from `qrexec-daemon`. -- **dom0**: `qrexec-client` starts a vchan server using the details received from `qrexec-daemon` and waits for connection from **domX**'s `qrexec-agent`. -- **domX**: `qrexec-agent` receives `MSG_EXEC_CMDLINE` header followed by `exec_params` from `qrexec-daemon` over vchan. -- **domX**: `qrexec-agent` connects to `qrexec-client` over vchan using the details from `exec_params`. -- **domX**: `qrexec-agent` executes `some_command` in **domX** and connects the child's stdin/stdout to the data vchan. If the process creation fails, `qrexec-agent` sends `MSG_DATA_EXIT_CODE` to `qrexec-client` followed by the status code (**int**) and disconnects from the data vchan. -- Data read from `some_command`'s stdout is sent to the data vchan using `MSG_DATA_STDOUT` by `qrexec-agent`. `qrexec-client` passes data received as `MSG_DATA_STDOUT` to its own stdout (or to `local_program`'s stdin if used). -- `qrexec-client` sends data read from local stdin (or `local_program`'s stdout if used) to `qrexec-agent` over the data vchan using `MSG_DATA_STDIN`. `qrexec-agent` passes data received as `MSG_DATA_STDIN` to `some_command`'s stdin. -- `MSG_DATA_STDOUT` or `MSG_DATA_STDIN` with data `len` field set to 0 in `msg_header` is an EOF marker. Peer receiving such message should close the associated input/output pipe. -- When `some_command` terminates, **domX**'s `qrexec-agent` sends `MSG_DATA_EXIT_CODE` header to `qrexec-client` followed by the exit code (**int**). `qrexec-agent` then disconnects from the data vchan. - -### domY: invoke execution of qubes service `qubes.SomeRpc` in domX and pass stdin/stdout - -- **domY**: `qrexec-client-vm` is invoked as follows: - - qrexec-client-vm domX qubes.SomeRpc local_program [params] - -- **domY**: `qrexec-client-vm` connects to `qrexec-agent` (via local socket/named pipe). -- **domY**: `qrexec-client-vm` sends `trigger_service_params` data to `qrexec-agent` (without filling the `request_id` field): - - struct trigger_service_params { - char service_name[64]; - char target_domain[32]; - struct service_params request_id; /* service request id */ - }; - - struct service_params { - char ident[32]; - }; - -- **domY**: `qrexec-agent` allocates a locally-unique (for this domain) `request_id` (let's say `13`) and fills it in the `trigger_service_params` struct received from `qrexec-client-vm`. -- **domY**: `qrexec-agent` sends `MSG_TRIGGER_SERVICE` header followed by `trigger_service_params` to `qrexec-daemon` in **dom0** via vchan. -- **dom0**: **domY**'s `qrexec-daemon` executes `qrexec-policy`: `qrexec-policy domY_id domY domX qubes.SomeRpc 13`. -- **dom0**: `qrexec-policy` evaluates if the RPC should be allowed or denied. If the action is allowed it returns `0`, if the action is denied it returns `1`. -- **dom0**: **domY**'s `qrexec-daemon` checks the exit code of `qrexec-policy`. - - If `qrexec-policy` returned **not** `0`: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_REFUSED` header followed by `service_params` to **domY**'s `qrexec-agent`. `service_params.ident` is identical to the one received. **domY**'s `qrexec-agent` disconnects its `qrexec-client-vm` and RPC processing is finished. - - If `qrexec-policy` returned `0`, RPC processing continues. -- **dom0**: if `qrexec-policy` allowed the RPC, it executed `qrexec-client -d domX -c 13,domY,domY_id user:QUBESRPC qubes.SomeRpc domY`. -- **dom0**: `qrexec-client` sets `QREXEC_REMOTE_DOMAIN` environment variable to **domX**. -- **dom0**: `qrexec-client` connects to **domX**'s `qrexec-daemon`. -- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_HELLO` header followed by `peer_info` to `qrexec-client`. -- **dom0**: `qrexec-client` replies with `MSG_HELLO` header followed by `peer_info` to **domX**'s`qrexec-daemon`. -- **dom0**: `qrexec-client` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to **domX**'s`qrexec-daemon` - - /* variable size */ - struct exec_params { - uint32_t connect_domain; /* target domain id */ - uint32_t connect_port; /* target vchan port for i/o exchange */ - char cmdline[0]; /* command line to execute, size = msg_header.len - sizeof(struct exec_params) */ - }; - - In this case, `connect_domain` is set to id of **domY** (from the `-c` parameter) and `connect_port` is set to 0. `cmdline` field contains the RPC to execute, in this case `user:QUBESRPC qubes.SomeRpc domY`. - -- **dom0**: **domX**'s `qrexec-daemon` replies to `qrexec-client` with `MSG_EXEC_CMDLINE` header followed by `exec_params`, but with empty `cmdline` field. `connect_domain` is set to Qubes ID of **domX** and `connect_port` is set to a vchan port allocated by **domX**'s `qrexec-daemon`. -- **dom0**: **domX**'s `qrexec-daemon` sends `MSG_EXEC_CMDLINE` header followed by `exec_params` to **domX**'s `qrexec-agent`. `connect_domain` and `connect_port` fields are the same as in the step above. `cmdline` is set to the one received from `qrexec-client`, in this case `user:QUBESRPC qubes.SomeRpc domY`. -- **dom0**: `qrexec-client` disconnects from **domX**'s `qrexec-daemon` after receiving connection details. -- **dom0**: `qrexec-client` connects to **domY**'s `qrexec-daemon` and exchanges `MSG_HELLO` as usual. -- **dom0**: `qrexec-client` sends `MSG_SERVICE_CONNECT` header followed by `exec_params` to **domY**'s `qrexec-daemon`. `connect_domain` is set to ID of **domX** (received from **domX**'s `qrexec-daemon`) and `connect_port` is the one received as well. `cmdline` is set to request ID (`13` in this case). -- **dom0**: **domY**'s `qrexec-daemon` sends `MSG_SERVICE_CONNECT` header followed by `exec_params` to **domY**'s `qrexec-agent`. Data fields are unchanged from the step above. -- **domY**: `qrexec-agent` starts a vchan server on the port received in the step above. It acts as a `qrexec-client` in this case because this is a VM-VM connection. -- **domX**: `qrexec-agent` connects to the vchan server of **domY**'s `qrexec-agent` (connection details were received before from **domX**'s `qrexec-daemon`). -- After that, connection follows the flow of the previous example (dom0-VM). - diff --git a/developer/services/qrexec2.md b/developer/services/qrexec2.md index 6fc85e9c..79ea7d14 100644 --- a/developer/services/qrexec2.md +++ b/developer/services/qrexec2.md @@ -11,8 +11,7 @@ redirect_from: # Command execution in VMs # -(*This page is about qrexec v2. For qrexec v3, see -[here](/doc/qrexec3/).*) +(*This page is about qrexec v2. For qrexec v3, see [here](/doc/qrexec3/).*) Qubes **qrexec** is a framework for implementing inter-VM (incl. Dom0-VM) services. It offers a mechanism to start programs in VMs, redirect their diff --git a/doc.md b/doc.md index 2e5440fb..815cf4f9 100644 --- a/doc.md +++ b/doc.md @@ -155,7 +155,6 @@ Core documentation for Qubes developers and advanced users. * [Qubes Core Admin Client](https://dev.qubes-os.org/projects/core-admin-client/en/latest/) * [Qubes Admin API](/news/2017/06/27/qubes-admin-api/) * [Qubes Core Stack](/news/2017/10/03/core3/) - * [Qrexec: command execution in VMs](/doc/qrexec3/) * [Qubes GUI virtualization protocol](/doc/gui/) * [Networking in Qubes](/doc/networking/) * [Implementation of template sharing and updating](/doc/template-implementation/) @@ -168,6 +167,8 @@ Core documentation for Qubes developers and advanced users. * [Dynamic memory management in Qubes](/doc/qmemman/) * [Implementation of DisposableVMs](/doc/dvm-impl/) * [Dom0 secure update mechanism](/doc/dom0-secure-updates/) + * [Qrexec: secure communication across domains](/doc/qrexec/) + * [Qubes RPC internals](/doc/qrexec-internals/) ### Debugging From 57937e97e5eab1f54a3913d218c8a6dc4dd75879 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Sun, 11 Aug 2019 23:02:52 -0500 Subject: [PATCH 15/24] Work definition of term "RPC" into qrexec docs introduction --- developer/services/qrexec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 5c8bc11d..9ef5f2bc 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -17,14 +17,14 @@ redirect_from: (*This page is about qrexec v3. For qrexec v2, see [here](/doc/qrexec2/).*) -The **qrexec** framework is used by core Qubes components to implement communication between domains. +The **qrexec framework** is used by core Qubes components to implement communication between domains. Qubes domains are strictly isolated by design. However, the OS needs a mechanism to allow the administrative domain (dom0) to force command execution in another domain (VM). For instance, when a user selects an application from the KDE menu, it should start in the selected VM. Also, it is often useful to be able to pass stdin/stdout/stderr from an application running in a VM to dom0 (and the other way around). (For example, so that a VM can notify dom0 that there are updates available for it). By default, Qubes allows VMs initiate such communications in specific circumstances. -The qrexec framework generalizes this process. +The qrexec framework generalizes this process by providing a remote procedure call (RPC) protocol for the Qubes architecture. It allows users and developers to use and design secure inter-VM tools. ## Qrexec basics: architecture and examples From acf10da43650f07c3e082721151f43d1664bc174 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Sun, 11 Aug 2019 13:45:36 -0500 Subject: [PATCH 16/24] Revise "Qubes RPC administration" section of qrexec docs - Remove section on "Yes to All" RPC authorization (and add TODO item for if this feature is reintroduced) - Replace `$` keywords with `@` in qrexec policy docs - Incorporate Qubes 4.0 "Extra keywords" material into RPC admin docs - Move some material to a new section on RPC security concerns --- developer/services/qrexec.md | 95 ++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 9ef5f2bc..6ff54d18 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -108,9 +108,9 @@ These files contain lines with the following format: srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to] -You can specify srcvm and destvm by name or by one of three reserved keywords: `$anyvm`, `$dispvm`, and `dom0` (without the `$`). -Only `$anyvm` keyword makes sense in srcvm field. -(Service calls from dom0 are currently always allowed, `$dispvm` means "new VM created for this particular request," so it is never a source of request.) +You can specify srcvm and destvm by name or by one of three reserved keywords: `@anyvm`, `@dispvm`, and `dom0` (without the `@`). +Only `@anyvm` keyword makes sense in srcvm field. +(Service calls from dom0 are currently always allowed, `@dispvm` means "new VM created for this particular request," so it is never a source of request.) Currently there is no way to specify source VM by type. Whenever a RPC request for an action is received, the domain checks the first matching line of the relevant file in `/etc/qubes-rpc/policy/` to determine access: whether to allow the request, what VM to redirect the execution to, and what user account the program should run under. @@ -131,6 +131,44 @@ It is also possible to call service without specific client program - in which c /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME +Target VM can be specified also as `@dispvm:DISP_VM`, which is very similar to `@dispvm` but forces using a particular VM (`DISP_VM`) as a base VM to be started as DisposableVM. +For example: + + anon-whonix @dispvm:anon-whonix-dvm allow + +Adding such policy itself will not force usage of this particular `DISP_VM` - it will only allow it when specified by the caller. +But `@dispvm:DISP_VM` can also be used as target in request redirection, so _it is possible_ to force particular `DISP_VM` usage, when caller didn't specify it: + + anon-whonix @dispvm allow,target=@dispvm:anon-whonix-dvm + +Note that without redirection, this rule would allow using default Disposable VM (`default_dispvm` VM property, which itself defaults to global `default_dispvm` property). +Also note that the request will be allowed (`allow` action) even if there is no second rule allowing calls to `@dispvm:anon-whonix-dvm`, or even if there is a rule explicitly denying it. +This is because the redirection happens _after_ considering the action. + +There are also additional methods to specify source/target VM: + + * `@tag:some-tag` - meaning a VM with tag `some-tag` + * `@type:type` - meaning a VM of `type` (like `AppVM`, `TemplateVM` etc) + +Target VM can be also specified as `@default`, which matches the case when calling VM didn't specified any particular target (either by using `@default` target, or empty target). + +The policy confirmation dialog (`ask` action) allows the user to specify target VM. +User can choose from VMs that, according to policy, would lead to `ask` or `allow` actions. +It is not possible to select VM that policy would deny. +By default no VM is selected, even if the caller provided some, but policy can specify default value using `default_target=` parameter. +For example: + + work-mail work-archive allow + work-mail @tag:work ask,default_target=work-files + work-mail @default ask,default_target=work-files + +The first rule allow call from `work-mail` to `work-archive`, without any confirmation. +The second rule will ask the user about calls from `work-mail` VM to any VM with tag `work`. +And the confirmation dialog will have `work-files` VM chosen by default, regardless of the VM specified by the caller (`work-mail` VM). +The third rule allow the caller to not specify target VM at all and let the user choose, still - from VMs with tag `work` (and `work-archive`, regardless of tag), and with `work-files` as default. + +### RPC services and security + Be very careful when coding and adding a new RPC service. Unless the offered functionality equals full control over the target (it is the case with e.g. `qubes.VMShell` action), any vulnerability in an RPC server can be fatal to Qubes security. On the other hand, this mechanism allows to delegate processing of untrusted input to less privileged (or disposable) AppVMs, thus wise usage of it increases security. @@ -153,45 +191,6 @@ By contrast, the `qubes.StartApp` service allows you to run only applications th While there isn't much practical difference between the two commands above when starting an application from dom0 in Qubes 4.0, there is a significant security risk when launching applications from a domU (e.g., from a separate GUI domain). This is why `qubes.StartApp` uses our standard `qrexec` argument grammar to strictly filter the permissible grammar of the `Exec=` lines in `.desktop` files that are passed from untrusted domUs to dom0, thereby protecting dom0 from command injection by maliciously-crafted `.desktop` files. -### Extra keywords available in Qubes 4.0 and later - -**This section is about a not-yet-released version, some details may change** - -In Qubes 4.0, target VM can be specified also as `$dispvm:DISP_VM`, which is very similar to `$dispvm` but forces using a particular VM (`DISP_VM`) as a base VM to be started as DisposableVM. -For example: - - anon-whonix $dispvm:anon-whonix-dvm allow - -Adding such policy itself will not force usage of this particular `DISP_VM` - it will only allow it when specified by the caller. -But `$dispvm:DISP_VM` can also be used as target in request redirection, so _it is possible_ to force particular `DISP_VM` usage, when caller didn't specify it: - - anon-whonix $dispvm allow,target=$dispvm:anon-whonix-dvm - -Note that without redirection, this rule would allow using default Disposable VM (`default_dispvm` VM property, which itself defaults to global `default_dispvm` property). -Also note that the request will be allowed (`allow` action) even if there is no second rule allowing calls to `$dispvm:anon-whonix-dvm`, or even if there is a rule explicitly denying it. -This is because the redirection happens _after_ considering the action. - -In Qubes 4.0 there are also additional methods to specify source/target VM: - - * `$tag:some-tag` - meaning a VM with tag `some-tag` - * `$type:type` - meaning a VM of `type` (like `AppVM`, `TemplateVM` etc) - -Target VM can be also specified as `$default`, which matches the case when calling VM didn't specified any particular target (either by using `$default` target, or empty target). - -In Qubes 4.0 policy confirmation dialog (`ask` action) allow the user to specify target VM. -User can choose from VMs that, according to policy, would lead to `ask` or `allow` actions. -It is not possible to select VM that policy would deny. -By default no VM is selected, even if the caller provided some, but policy can specify default value using `default_target=` parameter. -For example: - - work-mail work-archive allow - work-mail $tag:work ask,default_target=work-files - work-mail $default ask,default_target=work-files - -The first rule allow call from `work-mail` to `work-archive`, without any confirmation. -The second rule will ask the user about calls from `work-mail` VM to any VM with tag `work`. -And the confirmation dialog will have `work-files` VM chosen by default, regardless of the VM specified by the caller (`work-mail` VM). -The third rule allow the caller to not specify target VM at all and let the user choose, still - from VMs with tag `work` (and `work-archive`, regardless of tag), and with `work-files` as default. ### Service argument in policy @@ -220,17 +219,7 @@ This means it is also possible to install a different script for a particular se See below for an example service using an argument. -### Revoking "Yes to All" authorization - -Qubes RPC policy supports "ask" action. -This will prompt the user whether given RPC call should be allowed. -That prompt window also has a "Yes to All" option, which will allow the action and add a new entry to the policy file, which will unconditionally allow further calls for the given service-srcVM-dstVM tuple. - -In order to remove such authorization, issue this command from a dom0 terminal (for `qubes.Filecopy` service): - - sudo nano /etc/qubes-rpc/policy/qubes.Filecopy - -and then remove the first line(s) (before the first `##` comment) which are the "Yes to All" results. + ### Qubes RPC example From 60d60c5ca71b3d6b2072c11ca9a87947edb64ae2 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Sun, 11 Aug 2019 23:45:41 -0500 Subject: [PATCH 17/24] Remove outdated sentence in qrexec doc --- developer/services/qrexec.md | 1 - 1 file changed, 1 deletion(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 6ff54d18..c0200566 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -111,7 +111,6 @@ These files contain lines with the following format: You can specify srcvm and destvm by name or by one of three reserved keywords: `@anyvm`, `@dispvm`, and `dom0` (without the `@`). Only `@anyvm` keyword makes sense in srcvm field. (Service calls from dom0 are currently always allowed, `@dispvm` means "new VM created for this particular request," so it is never a source of request.) -Currently there is no way to specify source VM by type. Whenever a RPC request for an action is received, the domain checks the first matching line of the relevant file in `/etc/qubes-rpc/policy/` to determine access: whether to allow the request, what VM to redirect the execution to, and what user account the program should run under. Note that if the request is redirected (`target=` parameter), policy action remains the same - even if there is another rule which would otherwise deny such request. From 5ea1118fa1e6ba48829f86edd173262111b01211 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Mon, 12 Aug 2019 11:36:20 -0500 Subject: [PATCH 18/24] Reorder steps in RPC service example Also add a shell prompt character --- developer/services/qrexec.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index c0200566..29a2859f 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -237,20 +237,23 @@ The code for this file is: echo $(($arg1+$arg2)) # print to stdout, which is passed to the RPC client We'll need to create a service called `test.Add` with its own definition and policy file in dom0. -In dom0 add the following test to `/etc/qubes-rpc/policy/test.Add`: - - $anyvm $anyvm ask - Now we need to define what the service does. -In this case, it should call our additing script. +In this case, it should call our addition script. We define the service with another one-line file, `/etc/qubes-rpc/test.Add`: - /usr/bin/our_test_add_server + /usr/bin/our_test_add_server + +The adminstrative domain will direct traffic based on the current RPC policies. +In dom0, create a file at `/etc/qubes-rpc/policy/test.Add` containing the following: + + $anyvm $anyvm ask + +This will allow our client and server to communicate. Before we make the call, ensure that the client and server scripts have executable permissions. -Now, invoke the RPC service! +Finally, invoke the RPC service. - qrexec-client-vm anotherVM test.Add /usr/bin/our_test_add_client 1 2 + $ qrexec-client-vm anotherVM test.Add /usr/bin/our_test_add_client 1 2 We should get "3" as answer. (dom0 will ask for confirmation first.) From 6bf95dab39293adf1f758e03c158939060cdd1e6 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Mon, 12 Aug 2019 11:40:43 -0500 Subject: [PATCH 19/24] Edit qrexec-client command line examples - remove absolute paths from qrexec-client calls - add shell prompt characters --- developer/services/qrexec.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 29a2859f..8f1a2b5b 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -42,7 +42,7 @@ Once this channel is established, stdin/stdout/stderr from the VMprocess is pass The `qrexec-client` command is used to make connections to VMs from dom0. For example, the following command - qrexec-client -e -d someVM user:'touch hello-world.txt' + $ qrexec-client -e -d someVM user:'touch hello-world.txt' creates an empty file called `hello-world.txt` in the home folder of `someVM`. @@ -51,7 +51,7 @@ The `-e` flag tells `qrexec-client` to exit immediately after sending the execut With this option, no further data is passed between the domains. By contrast, the following command demonstrates an open channel between dom0 and someVM (in this case, a remote shell): - qrexec-client -d someVM user:bash + $ qrexec-client -d someVM user:bash The `qvm-run` command is heavily based on `qrexec-client`. It also takes care of additional activities, e.g. starting the domain if it is not up yet and starting the GUI daemon. @@ -121,14 +121,14 @@ In the target VM, the `/etc/qubes-rpc/RPC_ACTION_NAME` must exist, containing th In the src VM, one should invoke the client via: - /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME rpc_client_path client arguments + $ qrexec-client-vm target_vm_name RPC_ACTION_NAME rpc_client_path client arguments Note that only stdin/stdout is passed between RPC server and client -- notably, no command line arguments are passed. Source VM name is specified by `QREXEC_REMOTE_DOMAIN` environment variable. By default, stderr of client and server is logged to respective `/var/log/qubes/qrexec.XID` files. It is also possible to call service without specific client program - in which case server stdin/out will be connected with the terminal: - /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME + $ qrexec-client-vm target_vm_name RPC_ACTION_NAME Target VM can be specified also as `@dispvm:DISP_VM`, which is very similar to `@dispvm` but forces using a particular VM (`DISP_VM`) as a base VM to be started as DisposableVM. For example: @@ -203,7 +203,7 @@ And generally the less choices the user must make, the lower the chance to make The syntax is simple: when calling a service, add an argument to the service name separated with `+` sign, for example: - /usr/lib/qubes/qrexec-client-vm target_vm_name RPC_ACTION_NAME+ARGUMENT + $ qrexec-client-vm target_vm_name RPC_ACTION_NAME+ARGUMENT Then create a policy as usual, including the argument (`/etc/qubes-rpc/policy/RPC_ACTION_NAME+ARGUMENT`). If the policy for the specific argument is not set (file does not exist), then the default policy for this service is loaded (`/etc/qubes-rpc/policy/RPC_ACTION_NAME`). From b8a53197d3a558334cd371e317b230c9192d0455 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Fri, 16 Aug 2019 12:49:44 -0500 Subject: [PATCH 20/24] Add qrexec diagram to qrexec.md --- developer/services/qrexec.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 8f1a2b5b..dcef3bd6 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -39,6 +39,8 @@ Typically, the first thing that a `qrexec-client` instance does is to send a req `qrexec-client` starts a vchan server, which `qrexec-agent` then connects to. Once this channel is established, stdin/stdout/stderr from the VMprocess is passed between `qrexec-agent` and the `qrexec-client` process. +![qrexec basics diagram](/attachment/wiki/qrexec3/qrexec3-basics.png) + The `qrexec-client` command is used to make connections to VMs from dom0. For example, the following command From 50b38d116108617fcace8e21482d466da52dc3ba Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Sun, 18 Aug 2019 18:58:47 -0500 Subject: [PATCH 21/24] Make more edits to qrexec docs - Several corrections and rewordings for accuracy - Fixes leftover outdated '$' keywords - Remove incomplete list of RPC services --- developer/services/qrexec.md | 38 +++++++++--------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index dcef3bd6..2e3cf7c8 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -30,7 +30,7 @@ It allows users and developers to use and design secure inter-VM tools. ## Qrexec basics: architecture and examples Qrexec is built on top of *vchan*, a Xen library providing data links between VMs. -During domain creation, a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. +During domain startup , a process named `qrexec-daemon` is started in dom0, and a process named `qrexec-agent` is started in the VM. They are connected over a **vchan** channel. `qrexec-daemon` listens for connections from a dom0 utility named `qrexec-client`. Let's say we want to start a process (call it `VMprocess`) in a VM (`someVM`). @@ -59,8 +59,8 @@ The `qvm-run` command is heavily based on `qrexec-client`. It also takes care of additional activities, e.g. starting the domain if it is not up yet and starting the GUI daemon. Thus, it is usually more convenient to use `qvm-run`. -There can be almost arbitrary number of `qrexec-client` processes for a domain (so, connected to the same `qrexec-daemon`, same domain) -- their data is multiplexed independently. -Number of available vchan channels is the limiting factor here, it depends on the underlying hypervisor. +There can be an almost arbitrary number of `qrexec-client` processes for a given domain. +The limiting factor is the number of available vchan channels, which depends on the underlying hypervisor, as well the domain's OS. ## Qubes RPC services @@ -77,7 +77,7 @@ Thus, the framework allows dom0 to be the RPC target as well. Thanks to the framework, RPC programs are very simple -- both RPC client and server just use their stdin/stdout to pass data. The framework does all the inner work to connect these processes to each other via `qrexec-daemon` and `qrexec-agent`. -Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is identical to RPC to a normal domain, all one needs is to pass `$dispvm` as the remote domain name. +Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is identical to RPC to a normal domain, all one needs is to pass `@dispvm` as the remote domain name. ## Qubes RPC administration @@ -85,37 +85,17 @@ Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is The dom0 directory `/etc/qubes-rpc/policy/` contains files for each available RPC action. Together their contents make up the RPC access policy database. -Currently, the defined actions are: - - qubes.ClipboardPaste - qubes.Filecopy - qubes.GetImageRGBA - qubes.GetRandomizedTime - qubes.Gpg - qubes.GpgImportKey - qubes.InputKeyboard - qubes.InputMouse - qubes.NotifyTools - qubes.NotifyUpdates - qubes.OpenInVM - qubes.OpenURL - qubes.PdfConvert - qubes.ReceiveUpdates - qubes.SyncAppMenus - qubes.USB - qubes.VMShell - qubes.WindowIconUpdater - These files contain lines with the following format: srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to] -You can specify srcvm and destvm by name or by one of three reserved keywords: `@anyvm`, `@dispvm`, and `dom0` (without the `@`). +You can specify srcvm and destvm by name or by one of the reserved keywords such as `@anyvm`, `@dispvm`, or `dom0`. Only `@anyvm` keyword makes sense in srcvm field. (Service calls from dom0 are currently always allowed, `@dispvm` means "new VM created for this particular request," so it is never a source of request.) Whenever a RPC request for an action is received, the domain checks the first matching line of the relevant file in `/etc/qubes-rpc/policy/` to determine access: whether to allow the request, what VM to redirect the execution to, and what user account the program should run under. Note that if the request is redirected (`target=` parameter), policy action remains the same - even if there is another rule which would otherwise deny such request. +If no policy rule is matched, the action is denied. If the policy file does not exist, the user is prompted to create one. If still there is no policy file after prompting, the action is denied. @@ -127,7 +107,7 @@ In the src VM, one should invoke the client via: Note that only stdin/stdout is passed between RPC server and client -- notably, no command line arguments are passed. Source VM name is specified by `QREXEC_REMOTE_DOMAIN` environment variable. -By default, stderr of client and server is logged to respective `/var/log/qubes/qrexec.XID` files. +By default, stderr of client and server is logged in the syslog/journald of the VM where the process is running. It is also possible to call service without specific client program - in which case server stdin/out will be connected with the terminal: $ qrexec-client-vm target_vm_name RPC_ACTION_NAME @@ -248,7 +228,7 @@ We define the service with another one-line file, `/etc/qubes-rpc/test.Add`: The adminstrative domain will direct traffic based on the current RPC policies. In dom0, create a file at `/etc/qubes-rpc/policy/test.Add` containing the following: - $anyvm $anyvm ask + @anyvm @anyvm ask This will allow our client and server to communicate. @@ -292,7 +272,7 @@ And no separate client script will be used. * default policy file in dom0 (*/etc/qubes-rpc/policy/test.File* ) - $anyvm $anyvm deny + @anyvm @anyvm deny * invoke RPC from `source_vm1` via From c0a91ca4a92ba2d2c798cfe78551fe83a0d328e3 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Thu, 22 Aug 2019 16:02:14 -0500 Subject: [PATCH 22/24] Revise and edit "Qubes RPC administration" section in qrexec docs Sorts material under three headings. --- developer/services/qrexec.md | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 2e3cf7c8..0a74ceeb 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -83,36 +83,54 @@ Additionally, disposable VMs are tightly integrated -- RPC to a DisposableVM is -The dom0 directory `/etc/qubes-rpc/policy/` contains files for each available RPC action. -Together their contents make up the RPC access policy database. -These files contain lines with the following format: +### Policy files - srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to] +The dom0 directory `/etc/qubes-rpc/policy/` contains a file for each available RPC action that a VM might call. +Together the contents of these files make up the RPC access policy database. +Policies are defined in lines with the following format: + + srcvm destvm (allow|deny|ask[,default_target=default_target_VM])[,user=user_to_run_as][,target=VM_to_redirect_to] You can specify srcvm and destvm by name or by one of the reserved keywords such as `@anyvm`, `@dispvm`, or `dom0`. -Only `@anyvm` keyword makes sense in srcvm field. -(Service calls from dom0 are currently always allowed, `@dispvm` means "new VM created for this particular request," so it is never a source of request.) +(Of these three, only `@anyvm` keyword makes sense in the srcvm field. +Service calls from dom0 are currently always allowed, and `@dispvm` means "new VM created for this particular request," so it is never a source of request.) +Other methods using *tags* and *types* are also available (and discussed below). + Whenever a RPC request for an action is received, the domain checks the first matching line of the relevant file in `/etc/qubes-rpc/policy/` to determine access: whether to allow the request, what VM to redirect the execution to, and what user account the program should run under. -Note that if the request is redirected (`target=` parameter), policy action remains the same - even if there is another rule which would otherwise deny such request. +Note that if the request is redirected (`target=` parameter), policy action remains the same -- even if there is another rule which would otherwise deny such request. If no policy rule is matched, the action is denied. If the policy file does not exist, the user is prompted to create one. -If still there is no policy file after prompting, the action is denied. +If there is still no policy file after prompting, the action is denied. -In the target VM, the `/etc/qubes-rpc/RPC_ACTION_NAME` must exist, containing the file name of the program that will be invoked, or being that program itself - in which case it must have executable permission set (`chmod +x`). +In the target VM, the file `/etc/qubes-rpc/RPC_ACTION_NAME` must exist, containing the file name of the program that will be invoked, or being that program itself -- in which case it must have executable permission set (`chmod +x`). -In the src VM, one should invoke the client via: +### Making an RPC call + +From outside of dom0, RPC calls take the following form: $ qrexec-client-vm target_vm_name RPC_ACTION_NAME rpc_client_path client arguments +For example: + + $ qrexec-client-vm work qubes.StartApp+firefox + Note that only stdin/stdout is passed between RPC server and client -- notably, no command line arguments are passed. -Source VM name is specified by `QREXEC_REMOTE_DOMAIN` environment variable. By default, stderr of client and server is logged in the syslog/journald of the VM where the process is running. -It is also possible to call service without specific client program - in which case server stdin/out will be connected with the terminal: + +It is also possible to call service without specific client program -- in which case server stdin/out will be connected with the terminal: $ qrexec-client-vm target_vm_name RPC_ACTION_NAME -Target VM can be specified also as `@dispvm:DISP_VM`, which is very similar to `@dispvm` but forces using a particular VM (`DISP_VM`) as a base VM to be started as DisposableVM. +### Specifying VMs: tags, types, targets, etc. + +There are severals methods for specifying source/target VMs in RPC policies. + + * `@tag:some-tag` - meaning a VM with tag `some-tag` + * `@type:type` - meaning a VM of `type` (like `AppVM`, `TemplateVM` etc) + +Target VM can be also specified as `@default`, which matches the case when calling VM didn't specified any particular target (either by using `@default` target, or empty target). +For DisposableVMs, `@dispvm:DISP_VM` is very similar to `@dispvm` but forces using a particular VM (`DISP_VM`) as a base VM to be started as DisposableVM. For example: anon-whonix @dispvm:anon-whonix-dvm allow @@ -126,13 +144,6 @@ Note that without redirection, this rule would allow using default Disposable VM Also note that the request will be allowed (`allow` action) even if there is no second rule allowing calls to `@dispvm:anon-whonix-dvm`, or even if there is a rule explicitly denying it. This is because the redirection happens _after_ considering the action. -There are also additional methods to specify source/target VM: - - * `@tag:some-tag` - meaning a VM with tag `some-tag` - * `@type:type` - meaning a VM of `type` (like `AppVM`, `TemplateVM` etc) - -Target VM can be also specified as `@default`, which matches the case when calling VM didn't specified any particular target (either by using `@default` target, or empty target). - The policy confirmation dialog (`ask` action) allows the user to specify target VM. User can choose from VMs that, according to policy, would lead to `ask` or `allow` actions. It is not possible to select VM that policy would deny. From a4b7b754bb5a3b0289a5b2a87dd7dabb8efb80c9 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Fri, 23 Aug 2019 10:13:40 -0500 Subject: [PATCH 23/24] Fix links to qrexec docs --- developer/releases/3.2/release-notes.md | 2 +- developer/releases/4.0/release-notes.md | 2 +- developer/services/qrexec2.md | 2 +- user/advanced-configuration/disposablevm-customization.md | 2 +- user/common-tasks/copying-files.md | 3 --- user/common-tasks/disposablevm.md | 4 ++-- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/developer/releases/3.2/release-notes.md b/developer/releases/3.2/release-notes.md index f7b66dee..3fc0ef02 100644 --- a/developer/releases/3.2/release-notes.md +++ b/developer/releases/3.2/release-notes.md @@ -73,5 +73,5 @@ the instructions above. This will be time consuming process. [upgrade-r3.1]: /doc/releases/3.1/release-notes/#upgrading [backup]: /doc/backup-restore/ [qrexec-argument]: https://github.com/QubesOS/qubes-issues/issues/1876 -[qrexec-doc]: /doc/qrexec3/#service-argument-in-policy +[qrexec-doc]: /doc/qrexec/#service-argument-in-policy [github-release-notes]: https://github.com/QubesOS/qubes-issues/issues?q=is%3Aissue+sort%3Aupdated-desc+milestone%3A%22Release+3.2%22+label%3Arelease-notes+is%3Aclosed diff --git a/developer/releases/4.0/release-notes.md b/developer/releases/4.0/release-notes.md index 38a47501..30336924 100644 --- a/developer/releases/4.0/release-notes.md +++ b/developer/releases/4.0/release-notes.md @@ -115,7 +115,7 @@ We also provide [detailed instruction][upgrade-to-r4.0] for this procedure. [qrexec-proxy]: https://github.com/QubesOS/qubes-issues/issues/1854 [qrexec-policy-keywords]: https://github.com/QubesOS/qubes-issues/issues/865 [qrexec-confirm]: https://github.com/QubesOS/qubes-issues/issues/910 -[qrexec-doc]: /doc/qrexec3/#extra-keywords-available-in-qubes-40-and-later +[qrexec-doc]: /doc/qrexec/#specifying-vms-tags-types-targets-etc [storage]: https://github.com/QubesOS/qubes-issues/issues/1842 [vm-interface]: /doc/vm-interface/ [admin-api]: /news/2017/06/27/qubes-admin-api/ diff --git a/developer/services/qrexec2.md b/developer/services/qrexec2.md index 79ea7d14..1f5f3c48 100644 --- a/developer/services/qrexec2.md +++ b/developer/services/qrexec2.md @@ -231,7 +231,7 @@ surfaces that are exposed to untrusted or less trusted VMs in that case. # Qubes RPC internals # (*This is about the implementation of qrexec v2. For the implementation of -qrexec v3, see [here](/doc/qrexec3/#qubes-rpc-internals). Note that the user +qrexec v3, see [here](/doc/qrexec-internals/). Note that the user API in v3 is backward compatible: qrexec apps written for Qubes R2 should run without modification on Qubes R3.*) diff --git a/user/advanced-configuration/disposablevm-customization.md b/user/advanced-configuration/disposablevm-customization.md index 581da8b3..b4e9596b 100644 --- a/user/advanced-configuration/disposablevm-customization.md +++ b/user/advanced-configuration/disposablevm-customization.md @@ -48,7 +48,7 @@ Additionally you may want to set it as default DisposableVM Template: [user@dom0 ~]$ qubes-prefs default_dispvm custom-disposablevm-template -The above default is used whenever a qube request starting a new DisposableVM and do not specify which one (for example `qvm-open-in-dvm` tool). This can be also set in qube settings and will affect service calls from that qube. See [qrexec documentation](/doc/qrexec3/#extra-keywords-available-in-qubes-40-and-later) for details. +The above default is used whenever a qube request starting a new DisposableVM and do not specify which one (for example `qvm-open-in-dvm` tool). This can be also set in qube settings and will affect service calls from that qube. See [qrexec documentation](/doc/qrexec/#specifying-vms-tags-types-targets-etc) for details. If you wish to use the `fedora-minimal` template as a DisposableVM Template, see the "DisposableVM Template" use case under [fedora-minimal customization](/doc/templates/fedora-minimal/#customization). diff --git a/user/common-tasks/copying-files.md b/user/common-tasks/copying-files.md index ea8ddb49..21688795 100644 --- a/user/common-tasks/copying-files.md +++ b/user/common-tasks/copying-files.md @@ -57,6 +57,3 @@ It even applies to the scenario of copying files between air-gapped machines. So, you should always copy data only from *more trusted* to *less trusted* qubes. See also [this article](https://blog.invisiblethings.org/2011/03/13/partitioning-my-digital-life-into.html) for more information on this topic, and some ideas of how we might solve this problem in some future version of Qubes. - -You may also want to read how to [revoke "Yes to All" authorization](/doc/qrexec3/#revoking-yes-to-all-authorization) - diff --git a/user/common-tasks/disposablevm.md b/user/common-tasks/disposablevm.md index 981144f5..4e211367 100644 --- a/user/common-tasks/disposablevm.md +++ b/user/common-tasks/disposablevm.md @@ -168,7 +168,7 @@ This line means: In other words, any VM will be allowed to create a new DisposableVM based on `online-dvm-template` and open a URL inside of that DisposableVM. -More information about RPC policies for DisposableVMs can be found [here][qrexec3-4.0]. +More information about RPC policies for DisposableVMs can be found [here][qrexec]. ## Customizing DisposableVMs ## @@ -179,4 +179,4 @@ Full instructions can be found [here](/doc/disposablevm-customization/). [DisposableVM Template]: /doc/glossary/#disposablevm-template -[qrexec3-4.0]: /doc/qrexec3/#extra-keywords-available-in-qubes-40-and-later +[qrexec]: /doc/qrexec/#qubes-rpc-administration From 922f84f9ddd6ba8fc1d729bc9541a5add9d92132 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Fri, 23 Aug 2019 17:06:58 -0500 Subject: [PATCH 24/24] Fix two typos in qrexec.md --- developer/services/qrexec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/developer/services/qrexec.md b/developer/services/qrexec.md index 0a74ceeb..86452c7b 100644 --- a/developer/services/qrexec.md +++ b/developer/services/qrexec.md @@ -65,7 +65,7 @@ The limiting factor is the number of available vchan channels, which depends on ## Qubes RPC services Some common tasks (like copying files between VMs) have an RPC-like structure: a process in one VM (say, the file sender) needs to invoke and send/receive data to some process in other VM (say, the file receiver). -The Qubes RPC framework was created to securely facilite a range of such actions. +The Qubes RPC framework was created to securely facilitate a range of such actions. Obviously, inter-VM communication must be tightly controlled to prevent one VM from taking control of another, possibly more privileged, VM. Therefore the design decision was made to pass all control communication via dom0, that can enforce proper authorization. @@ -236,7 +236,7 @@ We define the service with another one-line file, `/etc/qubes-rpc/test.Add`: /usr/bin/our_test_add_server -The adminstrative domain will direct traffic based on the current RPC policies. +The administrative domain will direct traffic based on the current RPC policies. In dom0, create a file at `/etc/qubes-rpc/policy/test.Add` containing the following: @anyvm @anyvm ask