From e757444c352fccc5c639fad7d44cc929a2d1498c Mon Sep 17 00:00:00 2001 From: Wojtek Porczyk Date: Tue, 17 May 2016 18:12:24 +0200 Subject: [PATCH] qubes/tools/qvm-features: add tool for managing qvm-features QubesOS/qubes-issues#1637 --- Makefile | 14 +-- doc/manpages/qvm-features.rst | 53 +++++++++ qubes-rpc-policy/qubes.FeaturesRequest.policy | 6 ++ qubes-rpc/qubes.FeaturesRequest | 13 +++ qubes/tools/qvm_features.py | 101 ++++++++++++++++++ rpm_spec/core-dom0.spec | 9 +- 6 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 doc/manpages/qvm-features.rst create mode 100644 qubes-rpc-policy/qubes.FeaturesRequest.policy create mode 100755 qubes-rpc/qubes.FeaturesRequest create mode 100644 qubes/tools/qvm_features.py diff --git a/Makefile b/Makefile index 99d1491d..db1dcb19 100644 --- a/Makefile +++ b/Makefile @@ -67,16 +67,18 @@ endif $(MAKE) install -C dispvm mkdir -p $(DESTDIR)/etc/qubes-rpc/policy mkdir -p $(DESTDIR)/usr/libexec/qubes + cp qubes-rpc-policy/qubes.FeaturesRequest.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.FeaturesRequest cp qubes-rpc-policy/qubes.Filecopy.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.Filecopy - cp qubes-rpc-policy/qubes.OpenInVM.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.OpenInVM - cp qubes-rpc-policy/qubes.VMShell.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMShell - cp qubes-rpc-policy/qubes.NotifyUpdates.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.NotifyUpdates - cp qubes-rpc-policy/qubes.NotifyTools.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.NotifyTools cp qubes-rpc-policy/qubes.GetImageRGBA.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.GetImageRGBA cp qubes-rpc-policy/qubes.GetRandomizedTime.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.GetRandomizedTime - cp qubes-rpc/qubes.NotifyUpdates $(DESTDIR)/etc/qubes-rpc/ - cp qubes-rpc/qubes.NotifyTools $(DESTDIR)/etc/qubes-rpc/ + cp qubes-rpc-policy/qubes.NotifyTools.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.NotifyTools + cp qubes-rpc-policy/qubes.NotifyUpdates.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.NotifyUpdates + cp qubes-rpc-policy/qubes.OpenInVM.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.OpenInVM + cp qubes-rpc-policy/qubes.VMShell.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMShell + 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/qubes-notify-updates $(DESTDIR)/usr/libexec/qubes/ cp qubes-rpc/qubes-notify-tools $(DESTDIR)/usr/libexec/qubes/ diff --git a/doc/manpages/qvm-features.rst b/doc/manpages/qvm-features.rst new file mode 100644 index 00000000..efbdce0c --- /dev/null +++ b/doc/manpages/qvm-features.rst @@ -0,0 +1,53 @@ +.. program:: qvm-features + +:program:`qvm-features` -- manage domain's features +=================================================== + +Synopsis +-------- + +:command:`qvm-features` [-h] [--verbose] [--quiet] *VMNAME* [*FEATURE* [*VALUE*]] + +Options +------- + +.. option:: --help, -h + + show this help message and exit + +.. option:: --verbose, -v + + increase verbosity + +.. option:: --quiet, -q + + decrease verbosity + +Description +----------- + +This command is used to manually manage the *features* of the domain. The +features are key-value pairs with both key and value being strings. They are +used by extensions to store information about the domain and make policy +decisions based on them. For example, they may indicate that some specific +software package was installed inside the template and the domains based on it +have some specific capability. + +.. warning:: + + The features are normally managed by the extensions themselves and you should + not change them directly. Strange things might happen otherwise. + +Some extensions interpret the values as boolean. In this case, the empty string +means :py:obj:`False` and non-empty string (commonly ``'1'``) means +:py:obj:`True`. An absence of the feature means "default", which is +extension-dependent. + +Authors +------- + +| Joanna Rutkowska +| Marek Marczykowski +| Wojtek Porczyk + +.. vim: ts=3 sw=3 et tw=80 diff --git a/qubes-rpc-policy/qubes.FeaturesRequest.policy b/qubes-rpc-policy/qubes.FeaturesRequest.policy new file mode 100644 index 00000000..0f00b0b6 --- /dev/null +++ b/qubes-rpc-policy/qubes.FeaturesRequest.policy @@ -0,0 +1,6 @@ +## Note that policy parsing stops at the first match, +## so adding anything below "$anyvm $anyvm action" line will have no effect + +## Please use a single # to start your custom comments + +$anyvm dom0 allow diff --git a/qubes-rpc/qubes.FeaturesRequest b/qubes-rpc/qubes.FeaturesRequest new file mode 100755 index 00000000..b0ec5c19 --- /dev/null +++ b/qubes-rpc/qubes.FeaturesRequest @@ -0,0 +1,13 @@ +#!/usr/bin/env python2 + +import os +import qubes + +PREFIX = '/features-request/' + +app = qubes.Qubes() +vm = app.domains[os.environ['QREXEC_REMOTE_DOMAIN']] +vm.fire_event('features-request', + untrusted_features={key[len(PREFIX):]: vm.qdb.read(key) + for key in vm.qdb.list(PREFIX)}) +app.save() diff --git a/qubes/tools/qvm_features.py b/qubes/tools/qvm_features.py new file mode 100644 index 00000000..11a8109f --- /dev/null +++ b/qubes/tools/qvm_features.py @@ -0,0 +1,101 @@ +#!/usr/bin/python2 -O +# vim: fileencoding=utf-8 + +# +# The Qubes OS Project, https://www.qubes-os.org/ +# +# Copyright (C) 2010-2016 Joanna Rutkowska +# Copyright (C) 2016 Wojtek Porczyk +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +'''qvm-features - Manage domain's features''' + +import argparse +import qubes + +parser = qubes.tools.QubesArgumentParser( + want_vm=True, + description='manage domain\'s features') + +parser.add_argument('--request', + action='store_true', default=False, + help=argparse.SUPPRESS) + +parser.add_argument('feature', metavar='FEATURE', + action='store', nargs='?', + help='name of the feature') + +parser.add_argument('value', metavar='VALUE', + action='store', nargs='?', + help='new value of the feature') + +parser.add_argument('--unset', '--default', '--delete', '-D', + dest='delete', + action='store_true', + help='unset the feature') + + +def main(args=None): + '''Main routine of :program:`qvm-features`. + + :param list args: Optional arguments to override those delivered from \ + command line. + ''' + + args = parser.parse_args(args) + + if args.request: + # Request mode: instead of setting the features directly, + # let the extensions handle them first. + args.vm.fire_event('feature-request', untrusted_features=args.features) + return 0 + + if args.feature is None: + if args.delete: + parser.error('--unset requires a feature') + + width = max(len(feature) for feature in args.vm.features) + for feature in sorted(args.vm.features): + print('{name:{width}s} {value}'.format( + name=feature, value=args.vm.features[feature], width=width)) + + return 0 + + if args.delete: + if args.value is not None: + parser.error('cannot both set and unset a value') + try: + del args.vm.features[args.feature] + args.app.save() + except KeyError: + pass + return 0 + + if args.value is None: + try: + print(args.vm.features[args.feature]) + return 0 + except KeyError: + return 1 + + args.vm.features[args.feature] = args.value + args.app.save() + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index ebf90d11..9bedef4a 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -243,6 +243,7 @@ fi %{python_sitelib}/qubes/tools/qubes_monitor_layout_notify.py* %{python_sitelib}/qubes/tools/qubes_prefs.py* %{python_sitelib}/qubes/tools/qvm_create.py* +%{python_sitelib}/qubes/tools/qvm_features.py* %{python_sitelib}/qubes/tools/qvm_kill.py* %{python_sitelib}/qubes/tools/qvm_ls.py* %{python_sitelib}/qubes/tools/qvm_pause.py* @@ -340,16 +341,18 @@ fi /etc/xen/scripts/block-snapshot /etc/xen/scripts/block-origin /etc/xen/scripts/vif-route-qubes +%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.FeaturesRequest %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.Filecopy %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.GetImageRGBA -%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.OpenInVM +%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.GetRandomizedTime %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.NotifyTools %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.NotifyUpdates +%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.OpenInVM %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.VMShell -%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.GetRandomizedTime +/etc/qubes-rpc/qubes.FeaturesRequest +/etc/qubes-rpc/qubes.GetRandomizedTime /etc/qubes-rpc/qubes.NotifyTools /etc/qubes-rpc/qubes.NotifyUpdates -/etc/qubes-rpc/qubes.GetRandomizedTime %attr(2770,root,qubes) %dir /var/log/qubes %attr(0770,root,qubes) %dir /var/run/qubes /etc/xdg/autostart/qubes-guid.desktop