mirror of
https://github.com/QubesOS/qubes-doc.git
synced 2024-12-17 19:54:39 -05:00
bbd0337e91
Manual fixes after the conversion tool.
417 lines
14 KiB
ReStructuredText
417 lines
14 KiB
ReStructuredText
===========================
|
||
Qrexec: Qubes RPC internals
|
||
===========================
|
||
|
||
|
||
(*This page details the current implementation of qrexec (qrexec3). A*
|
||
:doc:`general introduction </developer/services/qrexec>` *to qrexec is also available. For the implementation of qrexec2, see*
|
||
:ref:`here <developer/services/qrexec2:qubes rpc internals>` *.*)
|
||
|
||
The qrexec framework consists of a number of processes communicating
|
||
with each other using a 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 local
|
||
sockets as the underlying transport medium. Components in separate
|
||
domains (``qrexec-daemon`` to ``qrexec-agent``, data channel between
|
||
``qrexec-agent``) use vchan links. 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
|
||
-------------------------
|
||
|
||
|
||
The following programs handle parts of the framework: sending and
|
||
receiving requests, verifying permissions, and administering
|
||
connections. These tools are not designed to be used by users directly.
|
||
|
||
qrexec-daemon
|
||
^^^^^^^^^^^^^
|
||
|
||
|
||
``/usr/sbin/qrexec-daemon``
|
||
|
||
One instance is required for every active domain. ``qrexec-daemon`` is
|
||
responsible for both:
|
||
|
||
- handling execution and service requests from **dom0** (source:
|
||
``qrexec-client``); and
|
||
|
||
- handling service requests from the associated domain (source:
|
||
``qrexec-client-vm``, then ``qrexec-agent``).
|
||
|
||
|
||
|
||
Command line usage:
|
||
|
||
``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.
|
||
|
||
|
||
|
||
qrexec-client
|
||
^^^^^^^^^^^^^
|
||
|
||
|
||
``/usr/bin/qrexec-client``
|
||
|
||
Used to pass execution and service requests to ``qrexec-daemon``.
|
||
|
||
Command line usage:
|
||
|
||
- ``-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 <request-id,src-domain-name,src-domain-id>``: 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.
|
||
|
||
|
||
|
||
VM tools implementation
|
||
-----------------------
|
||
|
||
|
||
qrexec-agent
|
||
^^^^^^^^^^^^
|
||
|
||
|
||
``/usr/lib/qubes/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.
|
||
|
||
|
||
|
||
The ``qrexec-agent`` command takes no parameters.
|
||
|
||
qrexec-client-vm
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
|
||
``/usr/bin/qrexec-client-vm``
|
||
|
||
Runs in an active domain. Used to pass service requests to
|
||
``qrexec-agent``.
|
||
|
||
Command line usage:
|
||
|
||
``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
|
||
-----------------------
|
||
|
||
|
||
The qrexec protocol is message-based. All messages share a common header
|
||
followed by an optional data packet.
|
||
|
||
.. code:: c
|
||
|
||
/* 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:
|
||
|
||
.. code:: c
|
||
|
||
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 ``cmd`` in domX
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
.. figure:: /attachment/doc/qrexec-dom0-vm.png
|
||
:alt: qrexec internals diagram dom0-vm
|
||
|
||
qrexec internals diagram dom0-vm
|
||
|
||
- **dom0**: ``qrexec-client`` is invoked in **dom0** as follows:
|
||
|
||
.. code:: bash
|
||
|
||
qrexec-client -d domX [-l local_program] user:cmd
|
||
|
||
|
||
(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.)
|
||
``qrexec-client`` translates that request into a ``MSG_EXEC_CMDLINE``
|
||
message sent to ``qrexec-daemon``, with ``connect_domain`` set to 0
|
||
(connect to **dom0**) and ``connect_port`` also set to 0 (allocate a
|
||
port).
|
||
|
||
- **dom0**: ``qrexec-daemon`` allocates a free port (in this case 513),
|
||
and sends a ``MSG_EXEC_CMDLINE`` back to the client with connection
|
||
parameters (**domX** and 513) and with command field empty.
|
||
``qrexec-client`` disconnects from the daemon, starts a vchan server
|
||
on port 513 and awaits connection.
|
||
Then, ``qrexec-daemon`` passes on the request as ``MSG_EXEC_CMDLINE``
|
||
message to the ``qrexec-agent`` running in **domX**. In this case,
|
||
the connection parameters are **dom0** and 513.
|
||
|
||
- **domX**: ``qrexec-agent`` receives ``MSG_EXEC_CMDLINE``, and starts
|
||
the command (``user:cmd``, or ``cmd`` as user ``user``). If possible,
|
||
this is actually delegated to a separate server
|
||
(``qrexec-fork-server``) also running on domX.
|
||
After starting the command, ``qrexec-fork-server`` connects to
|
||
``qrexec-client`` in **dom0** over the provided vchan port 513.
|
||
|
||
- Data is forwarded between the ``qrexec-client`` in **dom0** and the
|
||
command executed in **domX** using ``MSG_DATA_STDIN``,
|
||
``MSG_DATA_STDOUT`` and ``MSG_DATA_STDERR``.
|
||
Empty messages (with data ``len`` field set to 0 in ``msg_header``)
|
||
are an EOF marker. Peer receiving such message should close the
|
||
associated input/output pipe.
|
||
When ``cmd`` terminates, **domX**’s ``qrexec-fork-server`` sends
|
||
``MSG_DATA_EXIT_CODE`` header to ``qrexec-client`` followed by the
|
||
exit code (**int**).
|
||
|
||
|
||
|
||
domX: request execution of service ``admin.Service`` in dom0
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
.. figure:: /attachment/doc/qrexec-vm-dom0.png
|
||
:alt: qrexec internals diagram vm-dom0
|
||
|
||
qrexec internals diagram vm-dom0
|
||
|
||
- **domX**: ``qrexec-client-vm`` is invoked as follows:
|
||
|
||
.. code:: bash
|
||
|
||
qrexec-client-vm dom0 admin.Service [local_program] [params]
|
||
|
||
|
||
(If ``local_program`` is set, it will be executed in **domX** and
|
||
connected to the remote command’s stdin/stdout).
|
||
``qrexec-client-vm`` connects to ``qrexec-agent`` and requests
|
||
service execution (``admin.Service``) in **dom0**.
|
||
``qrexec-agent`` assigns an internal identifier to the request. It’s
|
||
based on a file descriptor of the connected ``qrexec-client-vm``: in
|
||
this case, ``SOCKET11``.
|
||
``qrexec-agent`` forwards the request (``MSG_TRIGGER_SERVICE3``) to
|
||
its corresponding ``qrexec-daemon`` running in dom0.
|
||
|
||
- **dom0**: ``qrexec-daemon`` receives the request and triggers
|
||
``qrexec-policy`` program, passing all necessary parameters: source
|
||
domain **domX**, target domain **dom0**, service ``admin.Service``
|
||
and identifier ``SOCKET11``.
|
||
``qrexec-policy`` evaluates if the RPC should be allowed or denied,
|
||
possibly also launching a GUI confirmation prompt.
|
||
(If the RPC is denied, it returns with exit code 1, in which case
|
||
``qrexec-daemon`` sends a ``MSG_SERVICE_REFUSED`` back).
|
||
|
||
- **dom0**: If the RPC is allowed, ``qrexec-policy`` will launch a
|
||
``qrexec-client`` with the right command:
|
||
|
||
.. code:: bash
|
||
|
||
qrexec-client -d dom0 -c domX,X,SOCKET11 "QUBESRPC admin.Service domX name dom0"
|
||
|
||
|
||
The ``-c domX,X,SOCKET11`` are parameters indicating how connect back
|
||
to **domX** and pass its input/output.
|
||
The command parameter describes the RPC call: it contains service
|
||
name (``admin.Service``), source domain (``domX``) and target
|
||
description (``name dom0``, could also be e.g. ``keyword @dispvm``).
|
||
The target description is important in case the original target
|
||
wasn’t dom0, but the service is executing in dom0.
|
||
``qrexec-client`` connects to a ``qrexec-daemon`` for **domX** and
|
||
sends a ``MSG_SERVICE_CONNECT`` with connection parameters (**dom0**,
|
||
and port 0, indicating a port should be allocated) and request
|
||
identifier (``SOCKET11``).
|
||
``qrexec-daemon`` allocates a free port (513) and sends back
|
||
connection parameters to ``qrexec-client`` (**domX** port 513).
|
||
``qrexec-client`` starts the command, and tries to connect to
|
||
**domX** over the provided port 513.
|
||
Then, ``qrexec-daemon`` forwards the connection request
|
||
(``MSG_SERVICE_CONNECT``) to ``qrexec-agent`` running in **domX**,
|
||
with the right parameters (**dom0** port 513, request ``SOCKET11``).
|
||
|
||
- **dom0**: Because the command has the form ``QUBESRPC: ...``, it is
|
||
started through the ``qubes-rpc-multiplexer`` program with the
|
||
provided parameters (``admin.Service domX name dom0``). That program
|
||
finds and executes the necessary script in ``/etc/qubes-rpc/``.
|
||
|
||
- **domX**: ``qrexec-agent`` receives the ``MSG_SERVICE_CONNECT`` and
|
||
passes the connection parameters back to the connected
|
||
``qrexec-client-vm``. It identifies the ``qrexec-client-vm`` by the
|
||
request identifier (``SOCKET11`` means file descriptor 11).
|
||
``qrexec-client-vm`` starts a vchan server on 513 and receives a
|
||
connection from ``qrexec-client``.
|
||
|
||
- Data is forwarded between **dom0** and **domX** as in the previous
|
||
example (dom0-VM).
|
||
|
||
|
||
|
||
domX: invoke execution of qubes service ``qubes.Service`` in domY
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
.. figure:: /attachment/doc/qrexec-vm-vm.png
|
||
:alt: qrexec internals diagram vm-vm
|
||
|
||
qrexec internals diagram vm-vm
|
||
|
||
- **domX**: ``qrexec-client-vm`` is invoked as follows:
|
||
|
||
.. code:: bash
|
||
|
||
qrexec-client-vm domY qubes.Service [local_program] [params]
|
||
|
||
|
||
(If ``local_program`` is set, it will be executed in **domX** and
|
||
connected to the remote command’s stdin/stdout).
|
||
|
||
- The request is forwarded as ``MSG_TRIGGER_SERVICE3`` to
|
||
``qrexec-daemon`` running in **dom0**, then to ``qrexec-policy``,
|
||
then (if allowed) to ``qrexec-client``.
|
||
This is the same as in the previous example (VM-dom0).
|
||
|
||
- **dom0**: If the RPC is allowed, ``qrexec-policy`` will launch a
|
||
``qrexec-client`` with the right command:
|
||
|
||
.. code:: bash
|
||
|
||
qrexec-client -d domY -c domX,X,SOCKET11 user:cmd "DEFAULT:QUBESRPC qubes.Service domX"
|
||
|
||
|
||
The ``-c domX,X,SOCKET11`` are parameters indicating how connect back
|
||
to **domX** and pass its input/output.
|
||
The command parameter describes the service call: it contains the
|
||
username (or ``DEFAULT``), service name (``qubes.Service``) and
|
||
source domain (``domX``).
|
||
``qrexec-client`` will then send a ``MSG_EXEC_CMDLINE`` message to
|
||
``qrexec-daemon`` for **domY**. The message will be with port number
|
||
0, requesting port allocation.
|
||
``qrexec-daemon`` for **domY** will allocate a port (513) and send it
|
||
back. It will also send a ``MSG_EXEC_CMDLINE`` to its corresponding
|
||
agent. (It will also translate ``DEFAULT`` to the configured default
|
||
username).
|
||
Then, ``qrexec-client`` will also send ``MSG_SERVICE_CONNECT``
|
||
message to **domX**’s agent, indicating that it should connect to
|
||
**domY** over port 513.
|
||
Having notified both domains about a connection, ``qrexec-client``
|
||
now exits.
|
||
|
||
- **domX**: ``qrexec-agent`` receives a ``MSG_SERVICE_CONNECT`` with
|
||
connection parameters (**domY** port 513) and request identifier
|
||
(``SOCKET11``). It sends the connection parameters back to the right
|
||
``qrexec-client-vm``.
|
||
``qrexec-client-vm`` starts a vchan server on port 513. note that
|
||
this is different than in the other examples: ``MSG_SERVICE_CONNECT``
|
||
means you should start a server, ``MSG_EXEC_CMDLINE`` means you
|
||
should start a client.
|
||
|
||
- **domY**: ``qrexec-agent`` receives a ``MSG_EXEC_CMDLINE`` with the
|
||
command to execute (``user:QUBESRPC...``) and connection parameters
|
||
(**domX** port 513).
|
||
It forwards the request to ``qrexec-fork-server``, which handles the
|
||
command and connects to **domX** over the provided port.
|
||
Because the command is of the form ``QUBESRPC ...``,
|
||
``qrexec-fork-server`` starts it using ``qubes-rpc-multiplexer``
|
||
program, which finds and executes the necessary script in
|
||
``/etc/qubes-rpc/``.
|
||
|
||
- After that, the data is passed between **domX** and **domY** as in
|
||
the previous examples (dom0-VM, VM-dom0).
|
||
|
||
|
||
|
||
``qrexec-policy`` implementation
|
||
--------------------------------
|
||
|
||
|
||
``qrexec-policy`` is a mechanism for evaluating whether an RPC call
|
||
should be allowed. For introduction, see :ref:`Qubes RPC administration <developer/services/qrexec:qubes rpc administration>`.
|
||
|
||
``qrexec-policy-daemon``
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
This is a service running in dom0. It is called by ``qrexec-daemon`` and
|
||
is responsible for evaluating the request and possibly launching an
|
||
action.
|
||
|
||
The daemon listens on a socket (``/var/run/qubes/policy.sock``). It
|
||
accepts requests in the format described in
|
||
`qrexec-policy-daemon.rst <https://github.com/QubesOS/qubes-core-qrexec/blob/master/doc/qrexec-policy-daemon.rst>`__
|
||
and replies with ``result=allow/deny``.
|
||
|
||
A standalone version is called ``qrexec-policy-exec`` and is available
|
||
as a fallback.
|
||
|
||
``qrexec-policy-agent``
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
This is a service running in the GuiVM. It is called by
|
||
``qrexec-policy-daemon`` in order to display prompts and notifications
|
||
to the user.
|
||
|
||
It is a :doc:`socket-based Qubes RPC service </developer/services/qrexec-socket-services>`. Requests are in JSON format,
|
||
and response is simple ASCII.
|
||
|
||
There are two endpoints:
|
||
|
||
- ``policy.Ask`` - ask the user about whether to execute a given action
|
||
|
||
- ``policy.Notify`` - notify the user about an action.
|
||
|
||
|
||
|
||
See
|
||
`qrexec-policy-agent.rst <https://github.com/QubesOS/qubes-core-qrexec/blob/master/Documentation/qrexec-policy-agent.rst>`__
|
||
for protocol details.
|