From 12661dccf88a10077218ba45ba132e5ca0e8229e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 13 Aug 2017 02:36:26 +0200 Subject: [PATCH] qubespolicy: ask to create default policy if none is found Fixes QubesOS/qubes-issues#3005 --- qubespolicy/__init__.py | 8 + qubespolicy/agent.py | 13 ++ qubespolicy/cli.py | 32 +++- .../PolicyCreateConfirmationWindow.glade | 141 ++++++++++++++++++ qubespolicy/policycreateconfirmation.py | 82 ++++++++++ rpm_spec/core-dom0.spec | 3 + 6 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 qubespolicy/glade/PolicyCreateConfirmationWindow.glade create mode 100644 qubespolicy/policycreateconfirmation.py diff --git a/qubespolicy/__init__.py b/qubespolicy/__init__.py index 37532d2c..c3b5da10 100755 --- a/qubespolicy/__init__.py +++ b/qubespolicy/__init__.py @@ -47,6 +47,12 @@ class PolicySyntaxError(AccessDenied): super(PolicySyntaxError, self).__init__( '{}:{}: {}'.format(filename, lineno, msg)) +class PolicyNotFound(AccessDenied): + ''' Policy was not found for this service ''' + def __init__(self, service_name): + super(PolicyNotFound, self).__init__( + 'Policy not found for service {}'.format(service_name)) + class Action(enum.Enum): ''' Action as defined by policy ''' @@ -486,6 +492,8 @@ class Policy(object): if not os.path.exists(policy_file): # fallback to policy without specific argument set (if any) policy_file = os.path.join(policy_dir, service.split('+')[0]) + if not os.path.exists(policy_file): + raise PolicyNotFound(service) #: policy storage directory self.policy_dir = policy_dir diff --git a/qubespolicy/agent.py b/qubespolicy/agent.py index f0bf4df3..704312c1 100644 --- a/qubespolicy/agent.py +++ b/qubespolicy/agent.py @@ -30,6 +30,7 @@ from gi.repository import GLib # pylint: enable=import-error import qubespolicy.rpcconfirmation +import qubespolicy.policycreateconfirmation # pylint: enable=wrong-import-position class PolicyAgent(object): @@ -45,6 +46,11 @@ class PolicyAgent(object): + + + + + """ @@ -63,6 +69,13 @@ class PolicyAgent(object): targets, default_target or None) return response or '' + @staticmethod + def ConfirmPolicyCreate(source, service_name): + # pylint: disable=invalid-name + + response = qubespolicy.policycreateconfirmation.confirm( + source, service_name) + return response def main(): loop = GLib.MainLoop() diff --git a/qubespolicy/cli.py b/qubespolicy/cli.py index 9fcd4c15..c6f5fc16 100644 --- a/qubespolicy/cli.py +++ b/qubespolicy/cli.py @@ -20,6 +20,7 @@ import argparse import logging import logging.handlers +import os import sys @@ -46,6 +47,20 @@ parser.add_argument('process_ident', metavar='process-ident', help='Qrexec process identifier - for connecting data channel') +def create_default_policy(service_name): + policy_file = os.path.join(qubespolicy.POLICY_DIR, service_name) + with open(policy_file, "w") as policy: + policy.write( + "## Policy file automatically created on first service call.\n") + policy.write( + "## Fill free to edit.\n") + policy.write("## Note that policy parsing stops at the first match\n") + policy.write("\n") + policy.write("## Please use a single # to start your custom comments\n") + policy.write("\n") + policy.write("$anyvm $anyvm ask\n") + + def main(args=None): args = parser.parse_args(args) @@ -64,7 +79,22 @@ def main(args=None): log.error(log_prefix + 'error getting system info: ' + str(e)) return 1 try: - policy = qubespolicy.Policy(args.service_name) + try: + policy = qubespolicy.Policy(args.service_name) + except qubespolicy.PolicyNotFound: + service_name = args.service_name.split('+')[0] + import pydbus + bus = pydbus.SystemBus() + proxy = bus.get('org.qubesos.PolicyAgent', + '/org/qubesos/PolicyAgent') + create_policy = proxy.ConfirmPolicyCreate( + args.domain, service_name) + if create_policy: + create_default_policy(service_name) + policy = qubespolicy.Policy(args.service_name) + else: + raise + action = policy.evaluate(system_info, args.domain, args.target) if args.assume_yes_for_ask and action.action == qubespolicy.Action.ask: action.action = qubespolicy.Action.allow diff --git a/qubespolicy/glade/PolicyCreateConfirmationWindow.glade b/qubespolicy/glade/PolicyCreateConfirmationWindow.glade new file mode 100644 index 00000000..14e1994f --- /dev/null +++ b/qubespolicy/glade/PolicyCreateConfirmationWindow.glade @@ -0,0 +1,141 @@ + + + + + + False + Default service policy + dialog-warning + dialog + question + ok-cancel + + + False + vertical + + + False + end + + + False + True + 0 + + + + + True + False + vertical + 2 + + + True + False + Policy for requested service does not exist. +Do you want to create default one (ask for everything)? + + + False + True + 2 + 0 + + + + + True + False + 2 + 2 + + + True + False + 4 + Source: + + + 0 + 0 + + + + + True + False + 4 + Service: + + + 0 + 1 + + + + + True + False + True + False + + + 1 + 0 + + + + + True + False + True + False + + + 1 + 1 + + + + + True + False + Type capital YES to confirm: + + + 0 + 2 + + + + + True + True + True + False + + + 1 + 2 + + + + + False + False + 1 + + + + + False + True + 1 + + + + + + diff --git a/qubespolicy/policycreateconfirmation.py b/qubespolicy/policycreateconfirmation.py new file mode 100644 index 00000000..85c633dd --- /dev/null +++ b/qubespolicy/policycreateconfirmation.py @@ -0,0 +1,82 @@ +# -*- encoding: utf-8 -*- +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# +# 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, see . + +import os + +import pkg_resources + +# pylint: disable=import-error,wrong-import-position +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +# pylint: enable=import-error + +class PolicyCreateConfirmationWindow(object): + # pylint: disable=too-few-public-methods + _source_file = pkg_resources.resource_filename('qubespolicy', + os.path.join('glade', "PolicyCreateConfirmationWindow.glade")) + _source_id = {'window': "PolicyCreateConfirmationWindow", + 'ok': "okButton", + 'cancel': "cancelButton", + 'source': "sourceEntry", + 'service': "serviceEntry", + 'confirm': "confirmEntry", + } + + def __init__(self, source, service): + self._gtk_builder = Gtk.Builder() + self._gtk_builder.add_from_file(self._source_file) + self._window = self._gtk_builder.get_object( + self._source_id['window']) + self._rpc_ok_button = self._gtk_builder.get_object( + self._source_id['ok']) + self._rpc_cancel_button = self._gtk_builder.get_object( + self._source_id['cancel']) + self._service_entry = self._gtk_builder.get_object( + self._source_id['service']) + self._source_entry = self._gtk_builder.get_object( + self._source_id['source']) + self._confirm_entry = self._gtk_builder.get_object( + self._source_id['confirm']) + + self._source_entry.set_text(source) + self._service_entry.set_text(service) + + # make OK button the default + ok_button = self._window.get_widget_for_response(Gtk.ResponseType.OK) + ok_button.set_can_default(True) + ok_button.grab_default() + + def run(self): + self._window.set_keep_above(True) + self._window.connect("delete-event", Gtk.main_quit) + self._window.show_all() + + response = self._window.run() + + self._window.hide() + if response == Gtk.ResponseType.OK: + return self._confirm_entry.get_text() == 'YES' + return False + +def confirm(source, service): + window = PolicyCreateConfirmationWindow(source, service) + + return window.run() diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index e0e6b00d..f2e6d326 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -374,6 +374,7 @@ fi %{python3_sitelib}/qubespolicy/cli.py %{python3_sitelib}/qubespolicy/agent.py %{python3_sitelib}/qubespolicy/gtkhelpers.py +%{python3_sitelib}/qubespolicy/policycreateconfirmation.py %{python3_sitelib}/qubespolicy/rpcconfirmation.py %{python3_sitelib}/qubespolicy/utils.py %{python3_sitelib}/qubespolicy/graph.py @@ -386,6 +387,7 @@ fi %{python3_sitelib}/qubespolicy/tests/rpcconfirmation.py %dir %{python3_sitelib}/qubespolicy/glade +%{python3_sitelib}/qubespolicy/glade/PolicyCreateConfirmationWindow.glade %{python3_sitelib}/qubespolicy/glade/RPCConfirmationWindow.glade /usr/lib/qubes/cleanup-dispvms @@ -413,6 +415,7 @@ fi /etc/xen/scripts/block-snapshot /etc/xen/scripts/block-origin /etc/xen/scripts/vif-route-qubes +%attr(2775,root,qubes) %dir /etc/qubes-rpc/policy %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/admin.* %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/include/admin-local-ro %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/include/admin-local-rwx