Skip to content

Commit

Permalink
qubespolicy: add $adminvm keyword for specifying dom0 aka AdminVM
Browse files Browse the repository at this point in the history
  • Loading branch information
marmarek committed Jul 4, 2017
1 parent a937bb1 commit 26ea836
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 5 deletions.
4 changes: 4 additions & 0 deletions doc/qubes-policy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
30 changes: 26 additions & 4 deletions qubespolicy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -184,8 +188,9 @@ def __init__(self, line, filename=None, lineno=None):
'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')

Expand Down Expand Up @@ -216,11 +221,19 @@ def is_match_single(system_info, policy_value, value):
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

Expand All @@ -229,6 +242,11 @@ def is_match_single(system_info, policy_value, value):
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]

Expand Down Expand Up @@ -293,6 +311,8 @@ def expand_target(self, system_info):
except KeyError:
# TODO log a warning?
pass
elif self.target == '$adminvm':
yield self.target
elif self.target == '$dispvm':
yield self.target
else:
Expand Down Expand Up @@ -378,6 +398,8 @@ def execute(self, caller_ident):
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,
Expand Down
39 changes: 38 additions & 1 deletion qubespolicy/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
system_info = {
'domains': {
'dom0': {
'tags': [],
'tags': ['dom0-tag'],
'type': 'AdminVM',
'default_dispvm': 'default-dvm',
'dispvm_allowed': False,
Expand Down Expand Up @@ -102,6 +102,8 @@ def test_000_verify_target_value(self):
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(
Expand All @@ -127,6 +129,8 @@ def test_010_verify_special_value(self):
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',
Expand Down Expand Up @@ -197,6 +201,20 @@ def test_023_line_simple(self):
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
Expand Down Expand Up @@ -236,6 +254,9 @@ def test_040_match_single(self):
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'))
Expand All @@ -254,6 +275,15 @@ def test_040_match_single(self):
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'))
Expand Down Expand Up @@ -339,6 +369,13 @@ def test_074_expand_override_target_dom0(self):
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):
Expand Down

0 comments on commit 26ea836

Please sign in to comment.