Implementation of qrexec in Qubes R3
This page describes implementation of the qrexec framework in Qubes OS R3.
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 use pipes as the underlying transport medium, while components in separate domains use vchan link.
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, thenqrexec-agent).
 
- Handling execution and service requests from dom0 (source: 
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-daemonuses 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-nameSpecifies the target for the execution/service request.
-l local-programOptional. If present,local-programis executed and its stdout/stdin are used when sending/receiving data to/from the remote peer.
-eOptional. If present, stdout/stdin are not connected to the remote peer. Only process creation status code is received.
-c <request-id,src-domain-name,src-domain-id>Used for connecting a VM-VM service request byqrexec-policy. Details described below in the service example.
cmdlineCommand line to pass toqrexec-daemonas 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-vmand passing them to connectedqrexec-daemonin dom0.
- Executing associated qrexec-daemonexecution/service requests.
 
- Handling service requests from 
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-nameTarget domain for the service request. Source is the current domain.
service-nameRequested service name.
local-programlocal-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. If protocol versions don’t match, the connection is closed. TODO: fallback for backwards compatibility, don’t do handshake in the same domain?.
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-clientis invoked in dom0 as follows:
qrexec-client -d domX [-l local_program] user:some_command
usermay be substituted with the literalDEFAULT. In that case, default Qubes user will be used to executesome_command.
- dom0: qrexec-clientsetsQREXEC_REMOTE_DOMAINenvironment variable to domX.
- dom0: If local_programis set,qrexec-clientexecutes it and uses that child’s stdin/stdout in place of its own when exchanging data withqrexec-agentlater.
- dom0: qrexec-clientconnects to domX’sqrexec-daemon.
- dom0: qrexec-daemonsendsMSG_HELLOheader followed bypeer_infotoqrexec-client.
- dom0: qrexec-clientreplies withMSG_HELLOheader followed bypeer_infotoqrexec-daemon.
- dom0: qrexec-clientsendsMSG_EXEC_CMDLINEheader followed byexec_paramstoqrexec-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-daemonreplies toqrexec-clientwithMSG_EXEC_CMDLINEheader followed byexec_params, but with emptycmdlinefield.connect_domainis set to Qubes ID of domX andconnect_portis set to a vchan port allocated byqrexec-daemon.
- dom0: qrexec-daemonsendsMSG_EXEC_CMDLINEheader followed byexec_paramsto the associated domXqrexec-agentover vchan.connect_domainis set to 0 (dom0),connect_portis the same as sent toqrexec-client.cmdlineis unchanged except that the literalDEFAULTis replaced with actual user name, if present.
- dom0: qrexec-clientdisconnects fromqrexec-daemon.
- dom0: qrexec-clientstarts a vchan server using the details received fromqrexec-daemonand waits for connection from domX’sqrexec-agent.
- domX: qrexec-agentreceivesMSG_EXEC_CMDLINEheader followed byexec_paramsfromqrexec-daemonover vchan.
- domX: qrexec-agentconnects toqrexec-clientover vchan using the details fromexec_params.
- domX: qrexec-agentexecutessome_commandin domX and connects the child’s stdin/stdout to the data vchan. If the process creation fails,qrexec-agentsendsMSG_DATA_EXIT_CODEtoqrexec-clientfollowed by the status code (int) and disconnects from the data vchan.
- Data read from some_command’s stdout is sent to the data vchan usingMSG_DATA_STDOUTbyqrexec-agent.qrexec-clientpasses data received asMSG_DATA_STDOUTto its own stdout (or tolocal_program’s stdin if used).
- qrexec-clientsends data read from local stdin (or- local_program’s stdout if used) to- qrexec-agentover the data vchan using- MSG_DATA_STDIN.- qrexec-agentpasses data received as- MSG_DATA_STDINto- some_command’s stdin.
- MSG_DATA_STDOUTor- MSG_DATA_STDINwith data- lenfield set to 0 in- msg_headeris an EOF marker. Peer receiving such message should close the associated input/output pipe.
- When some_commandterminates, domX’sqrexec-agentsendsMSG_DATA_EXIT_CODEheader toqrexec-clientfollowed by the exit code (int).qrexec-agentthen disconnects from the data vchan.
domY: invoke execution of qubes service qubes.SomeRpc? in domX and pass stdin/stdout
- domY: qrexec-client-vmis invoked as follows:
qrexec-client-vm domX qubes.SomeRpc local_program [params]
- domY: qrexec-client-vmconnects toqrexec-agent(via local socket/named pipe).
- domY: qrexec-client-vmsendstrigger_service_paramsdata toqrexec-agent(without filling therequest_idfield):
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-agentallocates a locally-unique (for this domain)request_id(let’s say13) and fills it in thetrigger_service_paramsstruct received fromqrexec-client-vm.
- domY: qrexec-agentsendsMSG_TRIGGER_SERVICEheader followed bytrigger_service_paramstoqrexec-daemonin dom0 via vchan.
- dom0: domY’s qrexec-daemonexecutesqrexec-policy:qrexec-policy domY_id domY domX qubes.SomeRpc 13.
- dom0: qrexec-policyevaluates if the RPC should be allowed or denied. If the action is allowed it returns0, if the action is denied it returns1.
- dom0: domY’s qrexec-daemonchecks the exit code ofqrexec-policy.- If qrexec-policyreturned not0: domY’sqrexec-daemonsendsMSG_SERVICE_REFUSEDheader followed byservice_paramsto domY’sqrexec-agent.service_params.identis identical to the one received. domY’sqrexec-agentdisconnects itsqrexec-client-vmand RPC processing is finished.
- If qrexec-policyreturned0, RPC processing continues.
 
- If 
- dom0: if qrexec-policyallowed the RPC, it executedqrexec-client -d domX -c 13,domY,domY_id user:QUBESRPC qubes.SomeRpc domY.
- dom0: qrexec-clientsetsQREXEC_REMOTE_DOMAINenvironment variable to domX.
- dom0: qrexec-clientconnects to domX’sqrexec-daemon.
- dom0: domX’s qrexec-daemonsendsMSG_HELLOheader followed bypeer_infotoqrexec-client.
- dom0: qrexec-clientreplies withMSG_HELLOheader followed bypeer_infoto domX’sqrexec-daemon.
- dom0: qrexec-clientsendsMSG_EXEC_CMDLINEheader followed byexec_paramsto domX’sqrexec-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-daemonreplies toqrexec-clientwithMSG_EXEC_CMDLINEheader followed byexec_params, but with emptycmdlinefield.connect_domainis set to Qubes ID of domX andconnect_portis set to a vchan port allocated by domX’sqrexec-daemon.
- dom0: domX’s qrexec-daemonsendsMSG_EXEC_CMDLINEheader followed byexec_paramsto domX’sqrexec-agent.connect_domainandconnect_portfields are the same as in the step above.cmdlineis set to the one received fromqrexec-client, in this caseuser:QUBESRPC qubes.SomeRpc domY.
- dom0: qrexec-clientdisconnects from domX’sqrexec-daemonafter receiving connection details.
- dom0: qrexec-clientconnects to domY’sqrexec-daemonand exchanges MSG_HELLO as usual.
- dom0: qrexec-clientsendsMSG_SERVICE_CONNECTheader followed byexec_paramsto domY’sqrexec-daemon.connect_domainis set to ID of domX (received from domX’sqrexec-daemon) andconnect_portis the one received as well.cmdlineis set to request ID (13in this case).
- dom0: domY’s qrexec-daemonsendsMSG_SERVICE_CONNECTheader followed byexec_paramsto domY’sqrexec-agent. Data fields are unchanged from the step above.
- domY: qrexec-agentstarts a vchan server on the port received in the step above. It acts as aqrexec-clientin this case because this is a VM-VM connection.
- domX: qrexec-agentconnects to the vchan server of domY’sqrexec-agent(connection details were received before from domX’sqrexec-daemon).
- After that, connection follows the flow of the previous example (dom0-VM).
 Qubes OS Project
Qubes OS Project