浏览代码

qubespolicy: add $adminvm keyword for specifying dom0 aka AdminVM

Fixes QubesOS/qubes-issues#2872
Marek Marczykowski-Górecki 7 年之前
父节点
当前提交
26ea836f67
共有 3 个文件被更改,包括 68 次插入5 次删除
  1. 4 0
      doc/qubes-policy.rst
  2. 26 4
      qubespolicy/__init__.py
  3. 38 1
      qubespolicy/tests/__init__.py

+ 4 - 0
doc/qubes-policy.rst

@@ -30,6 +30,10 @@ Each line consist of three values separated by white characters (space(s), tab(s
   - `$dispvm:vm-name` - _new_ Disposable VM created from AppVM `vm-name`
   - `$dispvm` - _new_ Disposable VM created from AppVM pointed by caller
     property `default_dispvm`, which defaults to global property `default_dispvm`
+  - `$adminvm` - Admin VM aka dom0
+
+  Dom0 can only be matched explicitly - either as `dom0` or `$adminvm` keyword.
+  None of `$anyvm`, `$tag:some-tag`, `$type:AdminVM` will match.
 
 3. Action and optional action parameters, one of:
 

+ 26 - 4
qubespolicy/__init__.py

@@ -66,6 +66,8 @@ def verify_target_value(system_info, value):
     '''
     if value == '$dispvm':
         return True
+    elif value == '$adminvm':
+        return True
     elif value.startswith('$dispvm:'):
         dispvm_base = value.split(':', 1)[1]
         if dispvm_base not in system_info['domains']:
@@ -93,6 +95,8 @@ def verify_special_value(value, for_target=True):
         return True
     elif value == '$anyvm':
         return True
+    elif value == '$adminvm':
+        return True
     elif value.startswith('$dispvm:') and for_target:
         return True
     elif value == '$dispvm' and for_target:
@@ -184,8 +188,9 @@ class PolicyRule(object):
                 'allow action for $default rule must specify target= option')
 
         if self.override_target is not None:
-            if self.override_target.startswith('$') and not \
-                    self.override_target.startswith('$dispvm'):
+            if self.override_target.startswith('$') and \
+                    not self.override_target.startswith('$dispvm') and \
+                    self.override_target != '$adminvm':
                 raise PolicySyntaxError(filename, lineno,
                     'target= option needs to name specific target')
 
@@ -216,11 +221,19 @@ class PolicyRule(object):
         if not verify_target_value(system_info, value):
             return False
 
+        # handle $adminvm keyword
+        if policy_value == 'dom0':
+            # TODO: log a warning in Qubes 4.1
+            policy_value = '$adminvm'
+
+        if value == 'dom0':
+            value = '$adminvm'
+
         # allow any _valid_, non-dom0 target
         if policy_value == '$anyvm':
-            return value != 'dom0'
+            return value != '$adminvm'
 
-        # exact match, including $dispvm*
+        # exact match, including $dispvm* and $adminvm
         if value == policy_value:
             return True
 
@@ -229,6 +242,11 @@ class PolicyRule(object):
         if value.startswith('$dispvm'):
             return False
 
+        # require $adminvm to be matched explicitly (not through $tag or $type)
+        # - if not matched already, reject it
+        if value == '$adminvm':
+            return False
+
         # at this point, value name a specific target
         domain_info = system_info['domains'][value]
 
@@ -293,6 +311,8 @@ class PolicyRule(object):
             except KeyError:
                 # TODO log a warning?
                 pass
+        elif self.target == '$adminvm':
+            yield self.target
         elif self.target == '$dispvm':
             yield self.target
         else:
@@ -378,6 +398,8 @@ class PolicyAction(object):
         assert self.action == Action.allow
         assert self.target is not None
 
+        if self.target == '$adminvm':
+            self.target = 'dom0'
         if self.target == 'dom0':
             cmd = '{multiplexer} {service} {source} {original_target}'.format(
                 multiplexer=QUBES_RPC_MULTIPLEXER_PATH,

+ 38 - 1
qubespolicy/tests/__init__.py

@@ -31,7 +31,7 @@ tmp_policy_dir = '/tmp/policy'
 system_info = {
     'domains': {
         'dom0': {
-            'tags': [],
+            'tags': ['dom0-tag'],
             'type': 'AdminVM',
             'default_dispvm': 'default-dvm',
             'dispvm_allowed': False,
@@ -102,6 +102,8 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
             qubespolicy.verify_target_value(system_info, 'test-template'))
         self.assertTrue(
             qubespolicy.verify_target_value(system_info, 'test-standalone'))
+        self.assertTrue(
+            qubespolicy.verify_target_value(system_info, '$adminvm'))
         self.assertFalse(
             qubespolicy.verify_target_value(system_info, 'no-such-vm'))
         self.assertFalse(
@@ -127,6 +129,8 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
             for_target=False))
         self.assertTrue(qubespolicy.verify_special_value('$type:AppVM',
             for_target=False))
+        self.assertTrue(qubespolicy.verify_special_value('$adminvm',
+            for_target=False))
         self.assertFalse(qubespolicy.verify_special_value('$default',
             for_target=False))
         self.assertFalse(qubespolicy.verify_special_value('$dispvm',
@@ -197,6 +201,20 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
         self.assertIsNone(line.override_user)
         self.assertEqual(line.default_target, 'test-vm1')
 
+    def test_024_line_simple(self):
+        line = qubespolicy.PolicyRule(
+            '$anyvm $adminvm ask,default_target=$adminvm',
+            'filename', 12)
+        self.assertEqual(line.filename, 'filename')
+        self.assertEqual(line.lineno, 12)
+        self.assertEqual(line.action, qubespolicy.Action.ask)
+        self.assertEqual(line.source, '$anyvm')
+        self.assertEqual(line.target, '$adminvm')
+        self.assertEqual(line.full_action, 'ask,default_target=$adminvm')
+        self.assertIsNone(line.override_target)
+        self.assertIsNone(line.override_user)
+        self.assertEqual(line.default_target, '$adminvm')
+
     def test_030_line_invalid(self):
         invalid_lines = [
             '$dispvm $default allow',  # $dispvm can't be a source
@@ -236,6 +254,9 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
         self.assertTrue(is_match_single(system_info,
             '$anyvm', '$dispvm:default-dvm'))
         self.assertTrue(is_match_single(system_info, '$dispvm', '$dispvm'))
+        self.assertTrue(is_match_single(system_info, '$adminvm', '$adminvm'))
+        self.assertTrue(is_match_single(system_info, '$adminvm', 'dom0'))
+        self.assertTrue(is_match_single(system_info, 'dom0', '$adminvm'))
         self.assertTrue(is_match_single(system_info, 'dom0', 'dom0'))
         self.assertTrue(is_match_single(system_info,
             '$dispvm:default-dvm', '$dispvm:default-dvm'))
@@ -254,6 +275,15 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
         self.assertFalse(is_match_single(system_info,
             '$dispvm:test-vm1', '$dispvm:test-vm1'))
         self.assertFalse(is_match_single(system_info, '$anyvm', 'dom0'))
+        self.assertFalse(is_match_single(system_info, '$anyvm', '$adminvm'))
+        self.assertFalse(is_match_single(system_info,
+            '$tag:dom0-tag', '$adminvm'))
+        self.assertFalse(is_match_single(system_info,
+            '$type:AdminVM', '$adminvm'))
+        self.assertFalse(is_match_single(system_info,
+            '$tag:dom0-tag', 'dom0'))
+        self.assertFalse(is_match_single(system_info,
+            '$type:AdminVM', 'dom0'))
         self.assertFalse(is_match_single(system_info, '$tag:tag1', 'dom0'))
         self.assertFalse(is_match_single(system_info, '$anyvm', '$tag:tag1'))
         self.assertFalse(is_match_single(system_info, '$anyvm', '$type:AppVM'))
@@ -339,6 +369,13 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
             line.expand_override_target(system_info, 'test-no-dvm'),
             'dom0')
 
+    def test_075_expand_override_target_dom0(self):
+        line = qubespolicy.PolicyRule(
+            '$anyvm $anyvm allow,target=$adminvm')
+        self.assertEqual(
+            line.expand_override_target(system_info, 'test-no-dvm'),
+            '$adminvm')
+
 
 class TC_10_PolicyAction(qubes.tests.QubesTestCase):
     def test_000_init(self):