Browse Source

Support qubes.VMExec call

The feature is advertised by core-agent so that it can be used
instead of VMShell.

See QubesOS/qubes-issues#4850.
Pawel Marczewski 4 years ago
parent
commit
08d83fb241
5 changed files with 50 additions and 13 deletions
  1. 1 1
      Makefile
  2. 31 0
      qubes-rpc-policy/qubes.VMExec.policy
  3. 4 3
      qubes/ext/core_features.py
  4. 13 9
      qubes/tests/ext.py
  5. 1 0
      rpm_spec/core-dom0.spec.in

+ 1 - 1
Makefile

@@ -178,6 +178,7 @@ endif
 	cp qubes-rpc-policy/qubes.OpenURL.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.OpenURL
 	cp qubes-rpc-policy/qubes.VMShell.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMShell
 	cp qubes-rpc-policy/qubes.VMRootShell.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMRootShell
+	cp qubes-rpc-policy/qubes.VMExec.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMExec
 	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
@@ -186,7 +187,6 @@ endif
 	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.StartApp.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.StartApp
-	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/qubes.ConnectTCP.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.ConnectTCP

+ 31 - 0
qubes-rpc-policy/qubes.VMExec.policy

@@ -0,0 +1,31 @@
+## 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	$dispvm	allow
+$anyvm	$anyvm	deny
+
+# WARNING: The qubes.VMExec service is dangerous and there are really few
+# cases when it could be safely used. Especially when policy set to "ask" you
+# have no way to know for sure what command(s) will be called. Compromissed
+# source VM can substitute the command. Allowing one VM to execute
+# qubes.VMExec over the other VM allows the former to TAKE FULL CONTROL over
+# the later. In most cases this is not what we want!
+#
+# Instead we should be using task-specific qrexec services which provide
+# assurance as to what program will be responding to the (untrusted) VM
+# requests.
+#
+# It is, however, safe, in most cases, to allow ultimate control of the
+# creating AppVM over the DisposableVM it creates as part of the qrexec service
+# invocation. That's why by default we have "$anyvm  $dispvm allow" rule. Note
+# that it does _not_ allow any AppVM to execute qubes.VMExec service over any
+# DispVM created in the system -- that would obviously be wrong. It only allows
+# qubes.VMExec service access to the AppVM which creates the DispVM as part of
+# this very service invocation.
+#
+# See e.g. this thread for some discussion:
+# https://groups.google.com/d/msg/qubes-users/xnAByaL_bjI/3PjYdiTDW-0J
+#
+#

+ 4 - 3
qubes/ext/core_features.py

@@ -1,4 +1,4 @@
-# -*- encoding: utf8 -*-
+# -*- encoding: utf-8 -*-
 #
 # The Qubes OS Project, http://www.qubes-os.org
 #
@@ -34,7 +34,8 @@ class CoreFeatures(qubes.ext.Extension):
             return
 
         requested_features = {}
-        for feature in ('qrexec', 'gui', 'gui-emulated', 'qubes-firewall'):
+        for feature in (
+                'qrexec', 'gui', 'gui-emulated', 'qubes-firewall', 'vmexec'):
             untrusted_value = untrusted_features.get(feature, None)
             if untrusted_value in ('1', '0'):
                 requested_features[feature] = bool(int(untrusted_value))
@@ -53,7 +54,7 @@ class CoreFeatures(qubes.ext.Extension):
                 vm.features[feature] = requested_features[feature]
 
         # those features can be freely enabled or disabled by template
-        for feature in ('qubes-firewall',):
+        for feature in ('qubes-firewall', 'vmexec'):
             if feature in requested_features:
                 vm.features[feature] = requested_features[feature]
 

+ 13 - 9
qubes/tests/ext.py

@@ -1,4 +1,4 @@
-# -*- encoding: utf8 -*-
+# -*- encoding: utf-8 -*-
 #
 # The Qubes OS Project, http://www.qubes-os.org
 #
@@ -46,13 +46,15 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
                     'gui': '1',
                     'version': '1',
                     'default-user': 'user',
-                    'qrexec': '1'}))
-        self.assertEqual(self.vm.mock_calls, [
+                    'qrexec': '1',
+                    'vmexec': '1'}))
+        self.assertListEqual(self.vm.mock_calls, [
             ('features.get', ('qrexec', False), {}),
             ('features.__contains__', ('qrexec',), {}),
             ('features.__setitem__', ('qrexec', True), {}),
             ('features.__contains__', ('gui',), {}),
             ('features.__setitem__', ('gui', True), {}),
+            ('features.__setitem__', ('vmexec', True), {}),
             ('features.get', ('qrexec', False), {}),
             ('fire_event_async', ('template-postinstall',), {}),
             ('fire_event_async().__iter__', (), {}),
@@ -66,13 +68,15 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
                     'gui': '0',
                     'version': '1',
                     'default-user': 'user',
-                    'qrexec': '0'}))
-        self.assertEqual(self.vm.mock_calls, [
+                    'qrexec': '0',
+                    'vmexec': '0'}))
+        self.assertListEqual(self.vm.mock_calls, [
             ('features.get', ('qrexec', False), {}),
             ('features.__contains__', ('qrexec',), {}),
             ('features.__setitem__', ('qrexec', False), {}),
             ('features.__contains__', ('gui',), {}),
             ('features.__setitem__', ('gui', False), {}),
+            ('features.__setitem__', ('vmexec', False), {}),
             ('features.get', ('qrexec', False), {}),
         ])
 
@@ -84,7 +88,7 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
                     'version': '1',
                     'default-user': 'user',
                 }))
-        self.assertEqual(self.vm.mock_calls, [
+        self.assertListEqual(self.vm.mock_calls, [
             ('features.get', ('qrexec', False), {}),
             ('features.get', ('qrexec', False), {}),
         ])
@@ -98,7 +102,7 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
                     'gui': '1',
                     'default-user': 'user',
                 }))
-        self.assertEqual(self.vm.mock_calls, [
+        self.assertListEqual(self.vm.mock_calls, [
             ('features.get', ('qrexec', False), {}),
             ('features.__contains__', ('qrexec',), {}),
             ('features.__setitem__', ('qrexec', True), {}),
@@ -136,7 +140,7 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
                     'gui': 'invalid',
                     'default-user': 'user',
                 }))
-        self.assertEqual(self.vm.mock_calls, [
+        self.assertListEqual(self.vm.mock_calls, [
             ('features.get', ('qrexec', False), {}),
             ('features.__contains__', ('qrexec',), {}),
             ('features.__setitem__', ('qrexec', True), {}),
@@ -171,7 +175,7 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
                     'version': '1',
                     'default-user': 'user',
                     'qrexec': '1'}))
-        self.assertEqual(self.vm.mock_calls, [
+        self.assertListEqual(self.vm.mock_calls, [
             ('features.get', ('qrexec', False), {}),
             ('features.__contains__', ('qrexec',), {}),
             ('features.__contains__', ('gui',), {}),

+ 1 - 0
rpm_spec/core-dom0.spec.in

@@ -402,6 +402,7 @@ fi
 %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.StartApp
 %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.VMShell
 %attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.VMRootShell
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.VMExec
 %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