123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- #!/usr/bin/env python3
- #
- # The Qubes OS Project, https://www.qubes-os.org/
- #
- # Copyright (C) 2017 Wojtek Porczyk <woju@invisiblethingslab.com>
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
- # License as published by the Free Software Foundation; either
- # version 2.1 of the License, or (at your option) any later version.
- #
- # This library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public
- # License along with this library; if not, see <https://www.gnu.org/licenses/>.
- #
- '''policy.RegisterArgument
- This qrexec is meant for services, which require some kind of "registering"
- before use (say ``example.Register`` and ``example.Perform+ARGUMENT``). After
- registering, the backend should invoke this call with frontend as the intended
- destination, with the actual service in argument of this call and the argument
- as the payload. The policy generated will be a single line with explicit
- frontend and backend domain names, and a plain "allow", without further
- qualifiers.
- The call allows for registering an argument only once, for one frontend domain.
- There is not possibility of deregistering or reregistering for another frontend.
- The backend can always register another argument for any frontend, including
- one that is already registered for some other argument.
- By default this qrexec is disabled by policy. To actually use it you should
- drop a policy for an exact call you want to register which will redirect the
- call to dom0.
- .. code-block:: none
- :caption: /etc/qubes-rpc/policy/policy.RegisterArgument+example.Perform
- backendvm $anyvm allow,target=dom0
- It will generate, for argument ``EXAMPLE``:
- .. code-bloc:: none
- :caption: /etc/qubes-rpc/policy/example.Perform+EXAMPLE
- frontendvm backendvm allow
- '''
- import logging
- import os
- import string
- import sys
- import pathlib
- POLICY_PATH = pathlib.Path('/etc/qubes-rpc/policy')
- POLICY_RULE = '{frontend} {backend} allow\n'
- # linux-utils/qrexec-lib/qrexec.h
- MAX_ARGUMENT_LEN = 64
- # core-admin-linux/qrexec/qrexec-daemon.c
- VALID_CHARS = set(map(ord, string.ascii_letters + string.digits + '-._'))
- def die(*args, **kwargs):
- logging.error(*args, **kwargs)
- sys.exit(1)
- def main():
- # pylint: disable=missing-docstring
- logging.basicConfig(
- level=logging.WARNING,
- filename='/var/log/qubes/policy-register.log',
- format='%(asctime)s %(message)s')
- backend = os.environ['QREXEC_REMOTE_DOMAIN']
- frontend = os.environ['QREXEC_REQUESTED_TARGET']
- rpcname = os.environ['QREXEC_SERVICE_ARGUMENT']
- logging.debug('%s %s → %s request, reading argument',
- rpcname, frontend, backend)
- untrusted_argument = sys.stdin.buffer.read(MAX_ARGUMENT_LEN)
- untrusted_overflow = sys.stdin.buffer.read(1)
- sys.stdin.buffer.close()
- if untrusted_overflow:
- die('%s: %s → %s request refused: argument too long',
- rpcname, frontend, backend)
- if not untrusted_argument:
- die('%s: %s → %s request refused: empty argument',
- rpcname, frontend, backend)
- if any(c not in VALID_CHARS for c in untrusted_argument):
- die('%s: %s → %s request refused: invalid argument',
- rpcname, frontend, backend)
- # argument may also be too long, so that length of rpcname, separator and
- # argument exceed 64 bytes, but that's fine, the call just wont work
- argument = untrusted_argument
- del untrusted_argument
- argument = argument.decode('ascii', errors='strict')
- filename = '{}+{}'.format(rpcname, argument)
- logging.debug('%s %s → %s argument %s filename %s',
- rpcname, frontend, backend, argument, filename)
- try:
- # the 'x' enforces that argument cannot be registered twice
- with open(str(POLICY_PATH / filename), 'x') as file:
- rule = POLICY_RULE.format(frontend=frontend, backend=backend)
- logging.warning('%s: %s → %s %s argument allowed',
- rpcname, frontend, backend, argument)
- logging.debug('%s: %s → %s %s adding rule %r',
- rpcname, frontend, backend, rule)
- file.write(rule)
- except FileExistsError:
- die('%s: %s → %s %s argument failed: file exists')
- if __name__ == '__main__':
- main()
|