diff --git a/Makefile b/Makefile index 8816a45f..8c692f9f 100644 --- a/Makefile +++ b/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" diff --git a/qubes-rpc-policy/90-admin-default.policy.header b/qubes-rpc-policy/90-admin-default.policy.header new file mode 100644 index 00000000..bd08451e --- /dev/null +++ b/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 + diff --git a/qubes-rpc-policy/90-default.policy b/qubes-rpc-policy/90-default.policy new file mode 100644 index 00000000..1bd30fd8 --- /dev/null +++ b/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 diff --git a/qubes-rpc-policy/admin-global-ro b/qubes-rpc-policy/admin-global-ro index 48cf561a..d1b136ab 100644 --- a/qubes-rpc-policy/admin-global-ro +++ b/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 diff --git a/qubes-rpc-policy/admin-global-rwx b/qubes-rpc-policy/admin-global-rwx index 02c0a8d2..22f7c739 100644 --- a/qubes-rpc-policy/admin-global-rwx +++ b/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 diff --git a/qubes-rpc-policy/admin-local-ro b/qubes-rpc-policy/admin-local-ro index b16e9f5e..8dbfec54 100644 --- a/qubes-rpc-policy/admin-local-ro +++ b/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 diff --git a/qubes-rpc-policy/admin-local-rwx b/qubes-rpc-policy/admin-local-rwx index 8c827af3..2fa944f8 100644 --- a/qubes-rpc-policy/admin-local-rwx +++ b/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 diff --git a/qubes-rpc-policy/generate-admin-policy b/qubes-rpc-policy/generate-admin-policy index f2805d81..a724a375 100755 --- a/qubes-rpc-policy/generate-admin-policy +++ b/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__': diff --git a/rpm_spec/core-dom0.spec.in b/rpm_spec/core-dom0.spec.in index 64dbe2a5..09a8a767 100644 --- a/rpm_spec/core-dom0.spec.in +++ b/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