qubes-doc/developer/services/qrexec-internals.rst
Marek Marczykowski-Górecki bbd0337e91
manual fixes
Manual fixes after the conversion tool.
2024-05-21 22:04:12 +02:00

417 lines
14 KiB
ReStructuredText
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

===========================
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 dont 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 childs 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 commands 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. Its
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
wasnt 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 commands 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.