diff --git a/Makefile b/Makefile index 066c0044..cbaa5c92 100644 --- a/Makefile +++ b/Makefile @@ -176,10 +176,12 @@ endif cp qubes-rpc-policy/qubes.VMShell.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMShell cp qubes-rpc-policy/qubes.UpdatesProxy.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.UpdatesProxy cp qubes-rpc-policy/qubes.GetDate.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.GetDate + cp qubes-rpc-policy/policy.RegisterArgument.policy $(DESTDIR)/etc/qubes-rpc/policy/policy.RegisterArgument cp qubes-rpc/qubes.FeaturesRequest $(DESTDIR)/etc/qubes-rpc/ cp qubes-rpc/qubes.GetRandomizedTime $(DESTDIR)/etc/qubes-rpc/ cp qubes-rpc/qubes.NotifyTools $(DESTDIR)/etc/qubes-rpc/ cp qubes-rpc/qubes.NotifyUpdates $(DESTDIR)/etc/qubes-rpc/ + cp qubes-rpc/policy.RegisterArgument $(DESTDIR)/etc/qubes-rpc/ install qubes-rpc/qubesd-query-fast $(DESTDIR)/usr/libexec/qubes/ install -m 0755 qvm-tools/qubes-bug-report $(DESTDIR)/usr/bin/qubes-bug-report install -m 0755 qvm-tools/qubes-hcl-report $(DESTDIR)/usr/bin/qubes-hcl-report diff --git a/qubes-rpc-policy/policy.RegisterArgument.policy b/qubes-rpc-policy/policy.RegisterArgument.policy new file mode 100644 index 00000000..4556c721 --- /dev/null +++ b/qubes-rpc-policy/policy.RegisterArgument.policy @@ -0,0 +1 @@ +# DO NOT USE THIS FILE. Instead, create a policy for the particular argument. diff --git a/qubes-rpc/policy.RegisterArgument b/qubes-rpc/policy.RegisterArgument new file mode 100755 index 00000000..cd106b4c --- /dev/null +++ b/qubes-rpc/policy.RegisterArgument @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# The Qubes OS Project, https://www.qubes-os.org/ +# +# Copyright (C) 2017 Wojtek Porczyk +# +# 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 . +# + +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') + + filename = '{}+{}'.format(rpcname, argument) + logging.debug('%s %s → %s argument %s filename %s', + rpcname, frontend, backend, argument, filename) + + try: + # the 'x' is critical + 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() diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 1c2d498d..e8d6ba62 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -432,11 +432,13 @@ fi %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.VMRootShell %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.UpdatesProxy %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.GetDate +%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/policy.RegisterArgument /etc/qubes-rpc/admin.* /etc/qubes-rpc/qubes.FeaturesRequest /etc/qubes-rpc/qubes.GetRandomizedTime /etc/qubes-rpc/qubes.NotifyTools /etc/qubes-rpc/qubes.NotifyUpdates +/etc/qubes-rpc/policy.RegisterArgument %attr(2770,root,qubes) %dir /var/log/qubes %attr(0770,root,qubes) %dir /var/run/qubes /etc/xdg/autostart/qrexec-policy-agent.desktop