Browse Source

Convert default qrexec policy to the new format

QubesOS/qubes-issues#4370
Marek Marczykowski-Górecki 4 years ago
parent
commit
9cc6050e7c

+ 8 - 31
Makefile

@@ -170,31 +170,11 @@ ifeq ($(BACKEND_VMM),xen)
 	# Currently supported only on xen
 	cp etc/qmemman.conf $(DESTDIR)/etc/qubes/
 endif
-	mkdir -p $(DESTDIR)/etc/qubes-rpc/policy
+	mkdir -p $(DESTDIR)/etc/qubes-rpc
+	mkdir -p $(DESTDIR)/etc/qubes/policy.d
 	mkdir -p $(DESTDIR)/usr/libexec/qubes
-	cp qubes-rpc-policy/qubes.FeaturesRequest.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.FeaturesRequest
-	cp qubes-rpc-policy/qubes.Filecopy.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.Filecopy
-	cp qubes-rpc-policy/qubes.OpenInVM.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.OpenInVM
-	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.VMExecGUI.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.VMExecGUI
-	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
-	cp qubes-rpc-policy/qubes.GetRandomizedTime.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.GetRandomizedTime
-	cp qubes-rpc-policy/qubes.NotifyTools.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.NotifyTools
-	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.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
-	cp qubes-rpc-policy/admin.vm.Console.policy $(DESTDIR)/etc/qubes-rpc/policy/admin.vm.Console
-	cp qubes-rpc-policy/admin.vm.volume.Import.policy $(DESTDIR)/etc/qubes-rpc/policy/admin.vm.volume.Import
-	cp qubes-rpc-policy/admin.vm.volume.ImportWithSize.policy $(DESTDIR)/etc/qubes-rpc/policy/admin.vm.volume.ImportWithSize
-	cp qubes-rpc-policy/policy.RegisterArgument.policy $(DESTDIR)/etc/qubes-rpc/policy/policy.RegisterArgument
+	install -m 0644 qubes-rpc-policy/90-default.policy \
+		$(DESTDIR)/etc/qubes/policy.d/90-default.policy
 	cp qubes-rpc/qubes.FeaturesRequest $(DESTDIR)/etc/qubes-rpc/
 	cp qubes-rpc/qubes.GetDate $(DESTDIR)/etc/qubes-rpc/
 	cp qubes-rpc/qubes.GetRandomizedTime $(DESTDIR)/etc/qubes-rpc/
@@ -214,7 +194,8 @@ endif
 	ln -s admin.vm.volume.Import $(DESTDIR)/etc/qubes-rpc/admin.vm.volume.ImportWithSize
 	install qubes-rpc/admin.vm.Console $(DESTDIR)/etc/qubes-rpc/
 	PYTHONPATH=.:test-packages qubes-rpc-policy/generate-admin-policy \
-		--destdir=$(DESTDIR)/etc/qubes-rpc/policy \
+		--dest=$(DESTDIR)/etc/qubes/policy.d/90-admin-default.policy \
+		--header=qubes-rpc-policy/90-admin-default.policy.header \
 		--exclude admin.vm.Create.AdminVM \
 				  admin.vm.CreateInPool.AdminVM \
 		          admin.vm.device.testclass.Attach \
@@ -222,16 +203,12 @@ endif
 				  admin.vm.device.testclass.List \
 				  admin.vm.device.testclass.Set.persistent \
 				  admin.vm.device.testclass.Available
-	# sanity check
-	for method in $(DESTDIR)/etc/qubes-rpc/policy/admin.*; do \
-		ls $(DESTDIR)/etc/qubes-rpc/$$(basename $$method) >/dev/null || exit 1; \
-	done
-	install -d $(DESTDIR)/etc/qubes-rpc/policy/include
+	install -d $(DESTDIR)/etc/qubes/policy.d/include
 	install -m 0644 qubes-rpc-policy/admin-local-ro \
 		qubes-rpc-policy/admin-local-rwx \
 		qubes-rpc-policy/admin-global-ro \
 		qubes-rpc-policy/admin-global-rwx \
-		$(DESTDIR)/etc/qubes-rpc/policy/include/
+		$(DESTDIR)/etc/qubes/policy.d/include/
 
 	mkdir -p "$(DESTDIR)$(FILESDIR)"
 	cp -r templates "$(DESTDIR)$(FILESDIR)/templates"

+ 22 - 0
qubes-rpc-policy/90-admin-default.policy.header

@@ -0,0 +1,22 @@
+## Do not modify this file, create a new policy file with a lower number in the
+## filename instead. For example `30-admin-user.policy`.
+
+###
+### Default Admin API qrexec policy
+###
+
+## WARNING: most qrexec services here allows a qube to control system
+## configuration. It should be allowed only to trusted qubes.
+
+## Remember to add "target=dom0" option to any (allow/ask) entry you create.
+
+## For convenience of maintaining this policy, all services include one of:
+##  - include/admin-local-rwx (services to modify a specific qube configuration)
+##  - include/admin-local-ro (services to read a specific qube configuration)
+##  - include/admin-global-rwx (services to modify global parameters)
+##  - include/admin-global-ro (services to read global parameters)
+
+!include-service admin.vm.Console       * include/admin-local-rwx
+!include-service admin.vm.volume.Import * include/admin-local-rwx
+!include-service admin.vm.volume.ImportWithSize * include/admin-local-rwx
+

+ 127 - 0
qubes-rpc-policy/90-default.policy

@@ -0,0 +1,127 @@
+## Do not modify this file, create a new policy file with a lower number in the
+## filename instead. For example `30-user.policy`.
+
+###
+### Default qrexec policy
+###
+
+## File format:
+## service-name|*       +argument|* source          destination action  [options]
+
+## Note that policy parsing stops at the first match.
+
+# policy.RegisterArgument should be allowed only for specific arguments.
+policy.RegisterArgument *           @anyvm          dom0        deny
+
+# WARNING: The qubes.ConnectTCP service is dangerous and allows any
+# qube to access any other qube TCP port. It should be restricted
+# only to restricted qubes. This is why the default policy is 'deny'
+
+# Example of policy: qubes.ConnectTCP +22 mytcp-client @default allow,target=mytcp-server
+qubes.ConnectTCP        *           @anyvm          @anyvm      deny
+
+# VM advertise its supported features
+qubes.FeaturesRequest   *           @anyvm	        dom0	    allow
+
+# Windows VM advertise installed Qubes Windows Tools
+qubes.NotifyTools       *           @anyvm          dom0        allow
+
+# File copy/move
+qubes.Filecopy          *           @anyvm          @anyvm      ask
+
+# Get current date/time
+qubes.GetDate           *           @tag:anon-vm    @anyvm      deny
+qubes.GetDate           *           @anyvm          @anyvm      allow target=dom0
+
+# Get slightly randomized date/time
+qubes.GetRandomizedTime *           @anyvm          dom0        allow
+
+# Convert image to a safe format, also, allows to get an image (icon) file from a VM
+qubes.GetImageRGBA      *           @anyvm          @dispvm     allow
+qubes.GetImageRGBA      *           @anyvm          @anyvm      ask
+
+# Notify about available updates
+qubes.NotifyUpdates     *           @anyvm          dom0        allow
+
+# Open a file in a VM
+qubes.OpenInVM          *           @anyvm          @dispvm     allow
+qubes.OpenInVM          *           @anyvm          @anyvm      ask
+
+# Open URL in a VM
+qubes.OpenURL           *           @anyvm          @dispvm     allow
+qubes.OpenURL           *           @anyvm          @anyvm      ask
+
+# Start application using its menu entry (only applications with menu entries
+# are allowed, no arbitrary command). Argument is an application name (in case
+# of Linux, basename of .desktop file from /usr/share/applications or similar
+# location).
+qubes.StartApp          *           @anyvm          @dispvm     allow
+qubes.StartApp          *           @anyvm          @anyvm      ask
+
+# HTTP proxy for downloading updates
+# Upgrade all TemplateVMs through sys-whonix.
+#qubes.UpdatesProxy * @type:TemplateVM @default allow,target=sys-whonix
+# Upgrade Whonix TemplateVMs through sys-whonix.
+qubes.UpdatesProxy      *   @tag:whonix-updatevm    @default    allow target=sys-whonix
+# Deny Whonix TemplateVMs using UpdatesProxy of any other VM.
+qubes.UpdatesProxy      *   @tag:whonix-updatevm    @anyvm      deny
+# Default rule for all TemplateVMs - direct the connection to sys-net
+qubes.UpdatesProxy      *   @type:TemplateVM        @default    allow target=sys-net
+qubes.UpdatesProxy      *   @anyvm                  @anyvm      deny
+
+# WARNING: The qubes.VMShell 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.VMShell 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.VMShell service over any
+# DispVM created in the system -- that would obviously be wrong. It only allows
+# qubes.VMShell 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
+qubes.VMShell           *           @anyvm          @dispvm     allow
+qubes.VMShell           *           @anyvm          @anyvm      deny
+
+# WARNING: qubes.VMRootShell has similar risks as qubes.VMExec
+# Add "user=root" option to any ask or allow rules.
+qubes.VMRootShell       *           @anyvm          @anyvm      deny
+
+# WARNING: The qubes.VMExec service is dangerous and there are really few
+# cases when it could be safely used. Contrary to qubes.VMShell, when policy is
+# set to "ask", the command to be executed is visible in the confirmation
+# prompt. But once allowed, the source VM have full control over the command
+# standard input/output. 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
+qubes.VMExec            *           @anyvm          @dispvm     allow
+qubes.VMExec            *           @anyvm          @anyvm      deny
+
+# WARNING: qubes.VMExecGUI has similar risks as qubes.VMExec
+qubes.VMExecGUI         *           @anyvm          @dispvm     allow
+qubes.VMExecGUI         *           @anyvm          @anyvm      deny

+ 3 - 3
qubes-rpc-policy/admin-global-ro

@@ -2,12 +2,12 @@
 ## _in default configuration_. To allow only specific action,
 ## edit specific policy file.
 
-## Note that policy parsing stops at the first match,
+## Note that policy parsing stops at the first match.
 
 ## Please use a single # to start your custom comments
 
 ## Include all already having write access
-$include:include/admin-global-rwx
+!include include/admin-global-rwx
 
-## Add your entries here, make sure to append ",target=dom0" to all allow/ask actions
+## Add your entries here, make sure to append "target=dom0" to all allow/ask actions
 

+ 2 - 3
qubes-rpc-policy/admin-global-rwx

@@ -2,10 +2,9 @@
 ## _in default configuration_. To allow only specific action,
 ## edit specific policy file.
 
-## Note that policy parsing stops at the first match,
-## so adding anything below "$anyvm $anyvm action" line will have no effect
+## Note that policy parsing stops at the first match.
 
 ## Please use a single # to start your custom comments
 
-## Add your entries here, make sure to append ",target=dom0" to all allow/ask actions
+## Add your entries here, make sure to append "target=dom0" to all allow/ask actions
 

+ 3 - 4
qubes-rpc-policy/admin-local-ro

@@ -2,13 +2,12 @@
 ## _in default configuration_. To allow only specific action,
 ## edit specific policy file.
 
-## Note that policy parsing stops at the first match,
-## so adding anything below "$anyvm $anyvm action" line will have no effect
+## Note that policy parsing stops at the first match.
 
 ## Please use a single # to start your custom comments
 
 ## Include all already having write access
-$include:include/admin-local-rwx
+!include include/admin-local-rwx
 
-## Add your entries here, make sure to append ",target=dom0" to all allow/ask actions
+## Add your entries here, make sure to append "target=dom0" to all allow/ask actions
 

+ 2 - 3
qubes-rpc-policy/admin-local-rwx

@@ -2,10 +2,9 @@
 ## _in default configuration_. To allow only specific action,
 ## edit specific policy file.
 
-## Note that policy parsing stops at the first match,
-## so adding anything below "$anyvm $anyvm action" line will have no effect
+## Note that policy parsing stops at the first match.
 
 ## Please use a single # to start your custom comments
 
-## Add your entries here, make sure to append ",target=dom0" to all allow/ask actions
+## Add your entries here, make sure to append "target=dom0" to all allow/ask actions
 

+ 19 - 29
qubes-rpc-policy/generate-admin-policy

@@ -30,9 +30,12 @@ parser = argparse.ArgumentParser(
 parser.add_argument('--include-base', action='store',
     default='include',
     help='Base path for included paths (default: %(default)s)')
-parser.add_argument('--destdir', action='store',
-    default='/etc/qubes-rpc/policy',
-    help='Directory where write output files to (default: %(default)s)')
+parser.add_argument('--dest', action='store',
+    default='/etc/qubes/policy.d/90-admin-default.policy',
+    help='Path where write output file to (default: %(default)s)')
+parser.add_argument('--header', action='store',
+    default='90-admin-default.policy.header',
+    help='File to prepend to the policy (default: %(default)s)')
 parser.add_argument('--verbose', action='store_true', default=False,
     help='Be verbose')
 parser.add_argument('--exclude', action='store', nargs='*',
@@ -40,22 +43,7 @@ parser.add_argument('--exclude', action='store', nargs='*',
 parser.add_argument('service', nargs='*', action='store',
     help='Generate policy for those services (default: all)')
 
-default_policy_header = '''\
-## Note that policy parsing stops at the first match.
-## Anything not specifically allowed here (or in included file) will be denied.
-
-## Please use a single # to start your custom comments
-
-## Add your entries here, make sure to append ",target=dom0" to all allow/ask actions
-
-## Include a common file for all admin.* methods to ease setting up
-## Management VM.
-## To allow only specific actions, edit specific policy file, like this one. To
-## allow all of them, edit appropriate /etc/qubes-rpc/include/admin-*.
-
-'''
-
-def write_default_policy(args, apiname, clasifiers):
+def write_default_policy(args, apiname, clasifiers, f):
     ''' Write single default policy for given API call '''
     assert 'scope' in clasifiers, \
         'Method {} lack scope classifier'.format(apiname)
@@ -73,22 +61,24 @@ def write_default_policy(args, apiname, clasifiers):
     if args.verbose:
         print('Service {}: include {}'.format(apiname, file_to_include),
             file=sys.stderr)
-    with open(os.path.join(args.destdir, apiname), 'w') as f:
-        f.write(default_policy_header)
-        f.write('$include:{}\n'.format(
-            os.path.join(args.include_base, file_to_include)))
+    f.write('!include-service {} * {}\n'.format(
+        apiname,
+        os.path.join(args.include_base, file_to_include)))
 
 
 def main(args=None):
     ''' Main function of default-admin-policy tool'''
     args = parser.parse_args(args)
 
-    for func, apiname, _ in qubes.api.admin.QubesAdminAPI.list_methods():
-        if args.service and apiname not in args.service:
-            continue
-        if args.exclude and apiname in args.exclude:
-            continue
-        write_default_policy(args, apiname, func.classifiers)
+    with open(os.path.join(args.dest), 'w') as f:
+        with open(args.header) as header_f:
+            f.write(header_f.read())
+        for func, apiname, _ in qubes.api.admin.QubesAdminAPI.list_methods():
+            if args.service and apiname not in args.service:
+                continue
+            if args.exclude and apiname in args.exclude:
+                continue
+            write_default_policy(args, apiname, func.classifiers, f)
 
 
 if __name__ == '__main__':

+ 6 - 23
rpm_spec/core-dom0.spec.in

@@ -383,29 +383,12 @@ 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
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/include/admin-global-ro
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/include/admin-global-rwx
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.ConnectTCP
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.FeaturesRequest
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.Filecopy
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.GetImageRGBA
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.GetRandomizedTime
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.NotifyTools
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.NotifyUpdates
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.OpenInVM
-%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.OpenURL
-%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.VMExecGUI
-%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
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes/policy.d/90-admin-default.policy
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes/policy.d/90-default.policy
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes/policy.d/include/admin-global-ro
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes/policy.d/include/admin-global-rwx
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes/policy.d/include/admin-local-ro
+%attr(0664,root,qubes) %config(noreplace) /etc/qubes/policy.d/include/admin-local-rwx
 /etc/qubes-rpc/admin.*
 /etc/qubes-rpc/qubes.FeaturesRequest
 /etc/qubes-rpc/qubes.GetDate