Update windows-related feature requests
Handle 'os' feature - if it's Windows, then set rpc-clipboard feature. Handle 'gui-emulated' feature - request for specifically stubdomain GUI. With 'gui' feature it is only possible to enable gui-agent based on, or disable GUI completely. Handle 'default-user' - verify it for weird characters and set 'default_user' property (if wasn't already set). QubesOS/qubes-issues#3585
This commit is contained in:
parent
ce87451c73
commit
af7d54d388
@ -76,7 +76,8 @@ class QubesMiscAPI(qubes.api.AbstractQubesAPI):
|
||||
|
||||
untrusted_features = {}
|
||||
safe_set = string.ascii_letters + string.digits
|
||||
expected_features = ('qrexec', 'gui', 'default-user')
|
||||
expected_features = ('qrexec', 'gui', 'gui-emulated', 'default-user',
|
||||
'os')
|
||||
for feature in expected_features:
|
||||
untrusted_value = self.src.untrusted_qdb.read(
|
||||
'/qubes-tools/' + feature)
|
||||
|
@ -32,7 +32,7 @@ class CoreFeatures(qubes.ext.Extension):
|
||||
return
|
||||
|
||||
requested_features = {}
|
||||
for feature in ('qrexec', 'gui', 'qubes-firewall'):
|
||||
for feature in ('qrexec', 'gui', 'gui-emulated', 'qubes-firewall'):
|
||||
untrusted_value = untrusted_features.get(feature, None)
|
||||
if untrusted_value in ('1', '0'):
|
||||
requested_features[feature] = bool(int(untrusted_value))
|
||||
@ -44,7 +44,7 @@ class CoreFeatures(qubes.ext.Extension):
|
||||
# gui agent presence (0 or 1)
|
||||
|
||||
qrexec_before = vm.features.get('qrexec', False)
|
||||
for feature in ('qrexec', 'gui'):
|
||||
for feature in ('qrexec', 'gui', 'gui-emulated'):
|
||||
# do not allow (Template)VM to override setting if already set
|
||||
# some other way
|
||||
if feature in requested_features and feature not in vm.features:
|
||||
|
64
qubes/ext/windows.py
Normal file
64
qubes/ext/windows.py
Normal file
@ -0,0 +1,64 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2017 Marek Marczykowski-Górecki
|
||||
# <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import qubes.ext
|
||||
|
||||
class WindowsFeatures(qubes.ext.Extension):
|
||||
# pylint: disable=too-few-public-methods
|
||||
@qubes.ext.handler('features-request')
|
||||
def qubes_features_request(self, vm, event, untrusted_features):
|
||||
'''Handle features provided requested by Qubes Windows Tools'''
|
||||
# pylint: disable=no-self-use,unused-argument
|
||||
if getattr(vm, 'template', None):
|
||||
vm.log.warning(
|
||||
'Ignoring qubes.NotifyTools for template-based VM')
|
||||
return
|
||||
|
||||
guest_os = None
|
||||
if 'os' in untrusted_features:
|
||||
if untrusted_features['os'] in ['Windows']:
|
||||
guest_os = untrusted_features['os']
|
||||
|
||||
qrexec = None
|
||||
if 'qrexec' in untrusted_features:
|
||||
if untrusted_features['qrexec'] == '1':
|
||||
# qrexec feature is set by CoreFeatures extension
|
||||
qrexec = True
|
||||
|
||||
del untrusted_features
|
||||
|
||||
if guest_os:
|
||||
vm.features['os'] = guest_os
|
||||
if guest_os == 'Windows' and qrexec:
|
||||
vm.features['rpc-clipboard'] = True
|
||||
|
||||
@qubes.ext.handler('domain-add', system=True)
|
||||
def on_domain_add(self, app, _event, vm, **kwargs):
|
||||
# pylint: disable=no-self-use,unused-argument
|
||||
if getattr(vm, 'template', None) is None:
|
||||
# handle only template-based vms
|
||||
return
|
||||
|
||||
template = vm.template
|
||||
if template.features.check_with_template('os', None) != 'Windows':
|
||||
# ignore non-windows templates
|
||||
return
|
||||
|
||||
# TODO: consider copying template's root volume here
|
@ -131,11 +131,14 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui-emulated'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/default-user'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/os'),
|
||||
mock.call.fire_event_async('features-request', untrusted_features={
|
||||
'gui': '1',
|
||||
'default-user': 'user',
|
||||
'qrexec': '1'}),
|
||||
'qrexec': '1',
|
||||
'os': 'Linux'}),
|
||||
('fire_event_async().__iter__', (), {}),
|
||||
])
|
||||
self.assertEqual(self.app.mock_calls, [mock.call.save()])
|
||||
@ -153,11 +156,14 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui-emulated'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/default-user'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/os'),
|
||||
mock.call.fire_event_async('features-request', untrusted_features={
|
||||
'gui': '1',
|
||||
'default-user': 'user',
|
||||
'qrexec': '1'}),
|
||||
'qrexec': '1',
|
||||
'os': 'Linux'}),
|
||||
('fire_event_async().__iter__', (), {}),
|
||||
])
|
||||
self.assertEqual(self.app.mock_calls, [mock.call.save()])
|
||||
|
@ -21,6 +21,7 @@
|
||||
from unittest import mock
|
||||
|
||||
import qubes.ext.core_features
|
||||
import qubes.ext.windows
|
||||
import qubes.tests
|
||||
|
||||
|
||||
@ -163,3 +164,53 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
||||
('features.__contains__', ('qrexec',), {}),
|
||||
('features.__contains__', ('gui',), {}),
|
||||
])
|
||||
|
||||
class TC_10_WindowsFeatures(qubes.tests.QubesTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.ext = qubes.ext.windows.WindowsFeatures()
|
||||
self.vm = mock.MagicMock()
|
||||
self.features = {}
|
||||
self.vm.configure_mock(**{
|
||||
'features.get.side_effect': self.features.get,
|
||||
'features.__contains__.side_effect': self.features.__contains__,
|
||||
'features.__setitem__.side_effect': self.features.__setitem__,
|
||||
})
|
||||
|
||||
def test_000_notify_tools_full(self):
|
||||
del self.vm.template
|
||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||
untrusted_features={
|
||||
'gui': '1',
|
||||
'version': '1',
|
||||
'default-user': 'user',
|
||||
'qrexec': '1',
|
||||
'os': 'Windows'})
|
||||
self.assertEqual(self.vm.mock_calls, [
|
||||
('features.__setitem__', ('os', 'Windows'), {}),
|
||||
('features.__setitem__', ('rpc-clipboard', True), {}),
|
||||
])
|
||||
|
||||
def test_001_notify_tools_no_qrexec(self):
|
||||
del self.vm.template
|
||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||
untrusted_features={
|
||||
'gui': '1',
|
||||
'version': '1',
|
||||
'default-user': 'user',
|
||||
'qrexec': '0',
|
||||
'os': 'Windows'})
|
||||
self.assertEqual(self.vm.mock_calls, [
|
||||
('features.__setitem__', ('os', 'Windows'), {}),
|
||||
])
|
||||
|
||||
def test_002_notify_tools_other_os(self):
|
||||
del self.vm.template
|
||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||
untrusted_features={
|
||||
'gui': '1',
|
||||
'version': '1',
|
||||
'default-user': 'user',
|
||||
'qrexec': '1',
|
||||
'os': 'Linux'})
|
||||
self.assertEqual(self.vm.mock_calls, [])
|
||||
|
@ -284,6 +284,7 @@ fi
|
||||
%{python3_sitelib}/qubes/ext/qubesmanager.py
|
||||
%{python3_sitelib}/qubes/ext/r3compatibility.py
|
||||
%{python3_sitelib}/qubes/ext/services.py
|
||||
%{python3_sitelib}/qubes/ext/windows.py
|
||||
|
||||
%dir %{python3_sitelib}/qubes/tests
|
||||
%dir %{python3_sitelib}/qubes/tests/__pycache__
|
||||
|
1
setup.py
1
setup.py
@ -74,6 +74,7 @@ if __name__ == '__main__':
|
||||
'qubes.ext.pci = qubes.ext.pci:PCIDeviceExtension',
|
||||
'qubes.ext.block = qubes.ext.block:BlockDeviceExtension',
|
||||
'qubes.ext.services = qubes.ext.services:ServicesExtension',
|
||||
'qubes.ext.windows = qubes.ext.windows:WindowsFeatures',
|
||||
],
|
||||
'qubes.devices': [
|
||||
'pci = qubes.ext.pci:PCIDevice',
|
||||
|
Loading…
Reference in New Issue
Block a user