From cb39325e6f06a659ab27468ffee4ba2fc4ccd730 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 10 Aug 2012 11:06:58 +0200 Subject: [PATCH 01/17] vm: ignore additional actions in *.desktop files (#631) --- qubes_rpc/qubes.GetAppmenus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubes_rpc/qubes.GetAppmenus b/qubes_rpc/qubes.GetAppmenus index cada68ca..59027828 100644 --- a/qubes_rpc/qubes.GetAppmenus +++ b/qubes_rpc/qubes.GetAppmenus @@ -1,2 +1,2 @@ shopt -s nullglob -/bin/grep -H = /usr/share/applications/*.desktop /usr/local/share/applications/*.desktop 2> /dev/null +awk '/^\[/ { if (tolower($0) != "\[desktop entry\]") nextfile } /=/ {print FILENAME ":" $0 }' /usr/share/applications/*.desktop /usr/local/share/applications/*.desktop 2> /dev/null From f41759a8b771873f6d3b2fb805f9aee25b7620f8 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 10 Aug 2012 11:06:58 +0200 Subject: [PATCH 02/17] vm: ignore additional actions in *.desktop files (#631) --- qubes_rpc/qubes.GetAppmenus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubes_rpc/qubes.GetAppmenus b/qubes_rpc/qubes.GetAppmenus index cada68ca..59027828 100644 --- a/qubes_rpc/qubes.GetAppmenus +++ b/qubes_rpc/qubes.GetAppmenus @@ -1,2 +1,2 @@ shopt -s nullglob -/bin/grep -H = /usr/share/applications/*.desktop /usr/local/share/applications/*.desktop 2> /dev/null +awk '/^\[/ { if (tolower($0) != "\[desktop entry\]") nextfile } /=/ {print FILENAME ":" $0 }' /usr/share/applications/*.desktop /usr/local/share/applications/*.desktop 2> /dev/null From ba85ca2df2923fe762d68bb34aa1682286b5c1e2 Mon Sep 17 00:00:00 2001 From: Joanna Rutkowska Date: Fri, 10 Aug 2012 14:45:19 +0200 Subject: [PATCH 03/17] version 1.7.43 --- version_dom0 | 2 +- version_vm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/version_dom0 b/version_dom0 index 2d29ee14..4651b88e 100644 --- a/version_dom0 +++ b/version_dom0 @@ -1 +1 @@ -1.7.42 +1.7.43 diff --git a/version_vm b/version_vm index 2d29ee14..4651b88e 100644 --- a/version_vm +++ b/version_vm @@ -1 +1 @@ -1.7.42 +1.7.43 From 9b3a77bc1deb6ccfbf1cc1c1fe0231a66253382d Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 15 Aug 2012 04:51:42 +0200 Subject: [PATCH 04/17] dom0: move RPC services to separate directory (#654) This makes more clear which code have contact with untrusted data from VM. --- dom0/qubes.SyncAppMenus | 1 - dom0/{aux-tools => qubes_rpc}/Makefile | 0 .../qfile-dom0-unpacker.c | 0 .../qubes-notify-updates | 0 dom0/qubes_rpc/qubes-receive-appmenus | 225 +++++++++++++++++ .../qubes-receive-updates | 0 .../qubes.NotifyUpdates | 0 .../qubes.NotifyUpdates.policy | 0 .../qubes.ReceiveUpdates | 0 .../qubes.ReceiveUpdates.policy | 0 dom0/qubes_rpc/qubes.SyncAppMenus | 1 + .../{ => qubes_rpc}/qubes.SyncAppMenus.policy | 0 dom0/qvm-tools/qvm-sync-appmenus | 227 +----------------- rpm_spec/core-dom0.spec | 22 +- 14 files changed, 240 insertions(+), 236 deletions(-) delete mode 100644 dom0/qubes.SyncAppMenus rename dom0/{aux-tools => qubes_rpc}/Makefile (100%) rename dom0/{aux-tools => qubes_rpc}/qfile-dom0-unpacker.c (100%) rename dom0/{aux-tools => qubes_rpc}/qubes-notify-updates (100%) create mode 100755 dom0/qubes_rpc/qubes-receive-appmenus rename dom0/{aux-tools => qubes_rpc}/qubes-receive-updates (100%) rename dom0/{aux-tools => qubes_rpc}/qubes.NotifyUpdates (100%) rename dom0/{aux-tools => qubes_rpc}/qubes.NotifyUpdates.policy (100%) rename dom0/{aux-tools => qubes_rpc}/qubes.ReceiveUpdates (100%) rename dom0/{aux-tools => qubes_rpc}/qubes.ReceiveUpdates.policy (100%) create mode 100644 dom0/qubes_rpc/qubes.SyncAppMenus rename dom0/{ => qubes_rpc}/qubes.SyncAppMenus.policy (100%) diff --git a/dom0/qubes.SyncAppMenus b/dom0/qubes.SyncAppMenus deleted file mode 100644 index 461fd519..00000000 --- a/dom0/qubes.SyncAppMenus +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/qvm-sync-appmenus diff --git a/dom0/aux-tools/Makefile b/dom0/qubes_rpc/Makefile similarity index 100% rename from dom0/aux-tools/Makefile rename to dom0/qubes_rpc/Makefile diff --git a/dom0/aux-tools/qfile-dom0-unpacker.c b/dom0/qubes_rpc/qfile-dom0-unpacker.c similarity index 100% rename from dom0/aux-tools/qfile-dom0-unpacker.c rename to dom0/qubes_rpc/qfile-dom0-unpacker.c diff --git a/dom0/aux-tools/qubes-notify-updates b/dom0/qubes_rpc/qubes-notify-updates similarity index 100% rename from dom0/aux-tools/qubes-notify-updates rename to dom0/qubes_rpc/qubes-notify-updates diff --git a/dom0/qubes_rpc/qubes-receive-appmenus b/dom0/qubes_rpc/qubes-receive-appmenus new file mode 100755 index 00000000..fef07274 --- /dev/null +++ b/dom0/qubes_rpc/qubes-receive-appmenus @@ -0,0 +1,225 @@ +#!/usr/bin/python +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2011 Marek Marczykowski +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +import subprocess +import re +import os +import sys +import fnmatch +import shutil +from optparse import OptionParser +from qubes.qubes import QubesVmCollection,QubesException +from qubes.qubes import qrexec_client_path + +# fields required to be present (and verified) in retrieved desktop file +required_fields = [ "Name", "Exec" ] + +#limits +appmenus_line_size = 1024 +appmenus_line_count = 100000 + +# regexps for sanitization of retrieved values +std_re = re.compile(r"^[/a-zA-Z0-9.,&() -]*$") +fields_regexp = { + "Name": std_re, + "GenericName": std_re, + "Comment": std_re, + "Categories": re.compile(r"^[a-zA-Z0-9/.; -]*$"), + "Exec": re.compile(r"^[a-zA-Z0-9%>/:.= -]*$"), +} + +def get_appmenus(xid): + global appmenus_line_count + global appmenus_line_size + untrusted_appmenulist = [] + if xid == -1: + while appmenus_line_count > 0: + line = sys.stdin.readline(appmenus_line_size) + if line == "": + break; + untrusted_appmenulist.append(line.strip()) + appmenus_line_count -= 1 + if appmenus_line_count == 0: + raise QubesException("Line count limit exceeded") + else: + p = subprocess.Popen ([qrexec_client_path, '-d', str(xid), + 'user:QUBESRPC qubes.GetAppmenus dom0'], stdout=subprocess.PIPE) + while appmenus_line_count > 0: + line = p.stdout.readline(appmenus_line_size) + if line == "": + break; + untrusted_appmenulist.append(line.strip()) + appmenus_line_count -= 1 + p.wait() + if p.returncode != 0: + raise QubesException("Error getting application list") + if appmenus_line_count == 0: + raise QubesException("Line count limit exceeded") + + row_no = 0 + appmenus = {} + line_rx = re.compile(r"([a-zA-Z0-9-.]+.desktop):([a-zA-Z0-9-]+(?:\[[a-zA-Z@_]+\])?)=(.*)") + ignore_rx = re.compile(r".*([a-zA-Z0-9-.]+.desktop):(#.*|\s+)$") + for untrusted_line in untrusted_appmenulist: + # Ignore blank lines and comments + if len(untrusted_line) == 0 or ignore_rx.match(untrusted_line): + continue + # use search instead of match to skip file path + untrusted_m = line_rx.search(untrusted_line) + if untrusted_m: + untrusted_key = untrusted_m.group(2) + untrusted_value = untrusted_m.group(3) + if fields_regexp.has_key(untrusted_key): + if fields_regexp[untrusted_key].match(untrusted_value): + # now values are sanitized + key = untrusted_key + value = untrusted_value + filename = untrusted_m.group(1) + + if not appmenus.has_key(filename): + appmenus[filename] = {} + + appmenus[filename][key]=value + else: + print >>sys.stderr, "Warning: ignoring key %s: %s" % (untrusted_key, untrusted_value) + # else: ignore this key + else: + print >>sys.stderr, "Warning: ignoring line: %s" % (untrusted_line); + + return appmenus + + +def create_template(path, values): + + # check if all required fields are present + for key in required_fields: + if not values.has_key(key): + print >>sys.stderr, "Warning: not creating/updating '%s' because of missing '%s' key" % (path, key) + return + + desktop_file = open(path, "w") + desktop_file.write("[Desktop Entry]\n") + desktop_file.write("Version=1.0\n") + desktop_file.write("Type=Application\n") + desktop_file.write("Terminal=false\n") + desktop_file.write("X-Qubes-VmName=%VMNAME%\n") + desktop_file.write("Icon=%VMDIR%/icon.png\n") + for key in ["Name", "GenericName" ]: + if values.has_key(key): + desktop_file.write("{0}=%VMNAME%: {1}\n".format(key, values[key])) + + for key in [ "Comment", "Categories" ]: + if values.has_key(key): + desktop_file.write("{0}={1}\n".format(key, values[key])) + + desktop_file.write("Exec=qvm-run -q --tray -a %VMNAME% '{0}'\n".format(values['Exec'])) + desktop_file.close() + + +def main(): + + env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN") + usage = "usage: %prog [options] \n"\ + "Updates desktop file templates for given StandaloneVM or TemplateVM" + + parser = OptionParser (usage) + parser.add_option ("-v", "--verbose", action="store_true", dest="verbose", default=False) + parser.add_option ("--force-root", action="store_true", dest="force_root", default=False, + help="Force to run, even with root privileges") + + (options, args) = parser.parse_args () + if (len (args) != 1) and env_vmname is None: + parser.error ("You must specify at least the VM name!") + + if env_vmname: + vmname=env_vmname + else: + vmname=args[0] + + if os.geteuid() == 0: + if not options.force_root: + print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems." + print >> sys.stderr, "Retry as unprivileged user." + print >> sys.stderr, "... or use --force-root to continue anyway." + exit(1) + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vm = qvm_collection.get_vm_by_name(vmname) + + if vm is None: + print >>sys.stderr, "ERROR: A VM with the name '{0}' does not exist in the system.".format(vmname) + exit(1) + + if vm.template is not None: + print >>sys.stderr, "ERROR: To sync appmenus for template based VM, do it on template instead" + exit(1) + + if not vm.is_running(): + print >>sys.stderr, "ERROR: Appmenus can be retrieved only from running VM - start it first" + exit(1) + + new_appmenus = {} + if env_vmname is None: + # Get appmenus from VM + xid = vm.get_xid() + assert xid > 0 + + new_appmenus = get_appmenus(xid) + else: + options.verbose = False + new_appmenus = get_appmenus(-1) + + if len(new_appmenus) == 0: + print >>sys.stderr, "ERROR: No appmenus received, terminating" + exit(1) + + if not os.path.exists(vm.appmenus_templates_dir): + os.mkdir(vm.appmenus_templates_dir) + + # Create new/update existing templates + if options.verbose: + print >> sys.stderr, "--> Got {0} appmenus, storing to disk".format(str(len(new_appmenus))) + for appmenu_file in new_appmenus.keys(): + if options.verbose: + if os.path.exists(vm.appmenus_templates_dir + '/' + appmenu_file): + print >> sys.stderr, "---> Updating {0}".format(appmenu_file) + else: + print >> sys.stderr, "---> Creating {0}".format(appmenu_file) + create_template(vm.appmenus_templates_dir + '/' + appmenu_file, new_appmenus[appmenu_file]) + + # Delete appmenus of remove applications + if options.verbose: + print >> sys.stderr, "--> Cleaning old files" + for appmenu_file in os.listdir(vm.appmenus_templates_dir): + if not fnmatch.fnmatch(appmenu_file, '*.desktop'): + continue + + if not new_appmenus.has_key(appmenu_file): + if options.verbose: + print >> sys.stderr, "---> Removing {0}".format(appmenu_file) + os.unlink(vm.appmenus_templates_dir + '/' + appmenu_file) + +main() diff --git a/dom0/aux-tools/qubes-receive-updates b/dom0/qubes_rpc/qubes-receive-updates similarity index 100% rename from dom0/aux-tools/qubes-receive-updates rename to dom0/qubes_rpc/qubes-receive-updates diff --git a/dom0/aux-tools/qubes.NotifyUpdates b/dom0/qubes_rpc/qubes.NotifyUpdates similarity index 100% rename from dom0/aux-tools/qubes.NotifyUpdates rename to dom0/qubes_rpc/qubes.NotifyUpdates diff --git a/dom0/aux-tools/qubes.NotifyUpdates.policy b/dom0/qubes_rpc/qubes.NotifyUpdates.policy similarity index 100% rename from dom0/aux-tools/qubes.NotifyUpdates.policy rename to dom0/qubes_rpc/qubes.NotifyUpdates.policy diff --git a/dom0/aux-tools/qubes.ReceiveUpdates b/dom0/qubes_rpc/qubes.ReceiveUpdates similarity index 100% rename from dom0/aux-tools/qubes.ReceiveUpdates rename to dom0/qubes_rpc/qubes.ReceiveUpdates diff --git a/dom0/aux-tools/qubes.ReceiveUpdates.policy b/dom0/qubes_rpc/qubes.ReceiveUpdates.policy similarity index 100% rename from dom0/aux-tools/qubes.ReceiveUpdates.policy rename to dom0/qubes_rpc/qubes.ReceiveUpdates.policy diff --git a/dom0/qubes_rpc/qubes.SyncAppMenus b/dom0/qubes_rpc/qubes.SyncAppMenus new file mode 100644 index 00000000..bbb55400 --- /dev/null +++ b/dom0/qubes_rpc/qubes.SyncAppMenus @@ -0,0 +1 @@ +/usr/lib/qubes/qubes-receive-appmenus diff --git a/dom0/qubes.SyncAppMenus.policy b/dom0/qubes_rpc/qubes.SyncAppMenus.policy similarity index 100% rename from dom0/qubes.SyncAppMenus.policy rename to dom0/qubes_rpc/qubes.SyncAppMenus.policy diff --git a/dom0/qvm-tools/qvm-sync-appmenus b/dom0/qvm-tools/qvm-sync-appmenus index fef07274..235244ed 100755 --- a/dom0/qvm-tools/qvm-sync-appmenus +++ b/dom0/qvm-tools/qvm-sync-appmenus @@ -1,225 +1,2 @@ -#!/usr/bin/python -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2011 Marek Marczykowski -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# - -import subprocess -import re -import os -import sys -import fnmatch -import shutil -from optparse import OptionParser -from qubes.qubes import QubesVmCollection,QubesException -from qubes.qubes import qrexec_client_path - -# fields required to be present (and verified) in retrieved desktop file -required_fields = [ "Name", "Exec" ] - -#limits -appmenus_line_size = 1024 -appmenus_line_count = 100000 - -# regexps for sanitization of retrieved values -std_re = re.compile(r"^[/a-zA-Z0-9.,&() -]*$") -fields_regexp = { - "Name": std_re, - "GenericName": std_re, - "Comment": std_re, - "Categories": re.compile(r"^[a-zA-Z0-9/.; -]*$"), - "Exec": re.compile(r"^[a-zA-Z0-9%>/:.= -]*$"), -} - -def get_appmenus(xid): - global appmenus_line_count - global appmenus_line_size - untrusted_appmenulist = [] - if xid == -1: - while appmenus_line_count > 0: - line = sys.stdin.readline(appmenus_line_size) - if line == "": - break; - untrusted_appmenulist.append(line.strip()) - appmenus_line_count -= 1 - if appmenus_line_count == 0: - raise QubesException("Line count limit exceeded") - else: - p = subprocess.Popen ([qrexec_client_path, '-d', str(xid), - 'user:QUBESRPC qubes.GetAppmenus dom0'], stdout=subprocess.PIPE) - while appmenus_line_count > 0: - line = p.stdout.readline(appmenus_line_size) - if line == "": - break; - untrusted_appmenulist.append(line.strip()) - appmenus_line_count -= 1 - p.wait() - if p.returncode != 0: - raise QubesException("Error getting application list") - if appmenus_line_count == 0: - raise QubesException("Line count limit exceeded") - - row_no = 0 - appmenus = {} - line_rx = re.compile(r"([a-zA-Z0-9-.]+.desktop):([a-zA-Z0-9-]+(?:\[[a-zA-Z@_]+\])?)=(.*)") - ignore_rx = re.compile(r".*([a-zA-Z0-9-.]+.desktop):(#.*|\s+)$") - for untrusted_line in untrusted_appmenulist: - # Ignore blank lines and comments - if len(untrusted_line) == 0 or ignore_rx.match(untrusted_line): - continue - # use search instead of match to skip file path - untrusted_m = line_rx.search(untrusted_line) - if untrusted_m: - untrusted_key = untrusted_m.group(2) - untrusted_value = untrusted_m.group(3) - if fields_regexp.has_key(untrusted_key): - if fields_regexp[untrusted_key].match(untrusted_value): - # now values are sanitized - key = untrusted_key - value = untrusted_value - filename = untrusted_m.group(1) - - if not appmenus.has_key(filename): - appmenus[filename] = {} - - appmenus[filename][key]=value - else: - print >>sys.stderr, "Warning: ignoring key %s: %s" % (untrusted_key, untrusted_value) - # else: ignore this key - else: - print >>sys.stderr, "Warning: ignoring line: %s" % (untrusted_line); - - return appmenus - - -def create_template(path, values): - - # check if all required fields are present - for key in required_fields: - if not values.has_key(key): - print >>sys.stderr, "Warning: not creating/updating '%s' because of missing '%s' key" % (path, key) - return - - desktop_file = open(path, "w") - desktop_file.write("[Desktop Entry]\n") - desktop_file.write("Version=1.0\n") - desktop_file.write("Type=Application\n") - desktop_file.write("Terminal=false\n") - desktop_file.write("X-Qubes-VmName=%VMNAME%\n") - desktop_file.write("Icon=%VMDIR%/icon.png\n") - for key in ["Name", "GenericName" ]: - if values.has_key(key): - desktop_file.write("{0}=%VMNAME%: {1}\n".format(key, values[key])) - - for key in [ "Comment", "Categories" ]: - if values.has_key(key): - desktop_file.write("{0}={1}\n".format(key, values[key])) - - desktop_file.write("Exec=qvm-run -q --tray -a %VMNAME% '{0}'\n".format(values['Exec'])) - desktop_file.close() - - -def main(): - - env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN") - usage = "usage: %prog [options] \n"\ - "Updates desktop file templates for given StandaloneVM or TemplateVM" - - parser = OptionParser (usage) - parser.add_option ("-v", "--verbose", action="store_true", dest="verbose", default=False) - parser.add_option ("--force-root", action="store_true", dest="force_root", default=False, - help="Force to run, even with root privileges") - - (options, args) = parser.parse_args () - if (len (args) != 1) and env_vmname is None: - parser.error ("You must specify at least the VM name!") - - if env_vmname: - vmname=env_vmname - else: - vmname=args[0] - - if os.geteuid() == 0: - if not options.force_root: - print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems." - print >> sys.stderr, "Retry as unprivileged user." - print >> sys.stderr, "... or use --force-root to continue anyway." - exit(1) - - qvm_collection = QubesVmCollection() - qvm_collection.lock_db_for_reading() - qvm_collection.load() - qvm_collection.unlock_db() - - vm = qvm_collection.get_vm_by_name(vmname) - - if vm is None: - print >>sys.stderr, "ERROR: A VM with the name '{0}' does not exist in the system.".format(vmname) - exit(1) - - if vm.template is not None: - print >>sys.stderr, "ERROR: To sync appmenus for template based VM, do it on template instead" - exit(1) - - if not vm.is_running(): - print >>sys.stderr, "ERROR: Appmenus can be retrieved only from running VM - start it first" - exit(1) - - new_appmenus = {} - if env_vmname is None: - # Get appmenus from VM - xid = vm.get_xid() - assert xid > 0 - - new_appmenus = get_appmenus(xid) - else: - options.verbose = False - new_appmenus = get_appmenus(-1) - - if len(new_appmenus) == 0: - print >>sys.stderr, "ERROR: No appmenus received, terminating" - exit(1) - - if not os.path.exists(vm.appmenus_templates_dir): - os.mkdir(vm.appmenus_templates_dir) - - # Create new/update existing templates - if options.verbose: - print >> sys.stderr, "--> Got {0} appmenus, storing to disk".format(str(len(new_appmenus))) - for appmenu_file in new_appmenus.keys(): - if options.verbose: - if os.path.exists(vm.appmenus_templates_dir + '/' + appmenu_file): - print >> sys.stderr, "---> Updating {0}".format(appmenu_file) - else: - print >> sys.stderr, "---> Creating {0}".format(appmenu_file) - create_template(vm.appmenus_templates_dir + '/' + appmenu_file, new_appmenus[appmenu_file]) - - # Delete appmenus of remove applications - if options.verbose: - print >> sys.stderr, "--> Cleaning old files" - for appmenu_file in os.listdir(vm.appmenus_templates_dir): - if not fnmatch.fnmatch(appmenu_file, '*.desktop'): - continue - - if not new_appmenus.has_key(appmenu_file): - if options.verbose: - print >> sys.stderr, "---> Removing {0}".format(appmenu_file) - os.unlink(vm.appmenus_templates_dir + '/' + appmenu_file) - -main() +#!/bin/sh +exec /usr/lib/qubes/qubes-receive-appmenus $@ diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 8c60a54d..26c0c0d1 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -52,7 +52,7 @@ The Qubes core files for installation on Dom0. python -m compileall qvm-core qmemman python -O -m compileall qvm-core qmemman make -C restore -make -C aux-tools +make -C qubes_rpc make -C ../qubes_rpc make -C ../vchan -f Makefile.linux make -C ../u2mfn @@ -108,9 +108,10 @@ cp ../misc/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes/ cp ../qrexec/qrexec_daemon $RPM_BUILD_ROOT/usr/lib/qubes/ cp ../qrexec/qrexec_client $RPM_BUILD_ROOT/usr/lib/qubes/ cp ../qrexec/qrexec_policy $RPM_BUILD_ROOT/usr/lib/qubes/ -cp aux-tools/qfile-dom0-unpacker $RPM_BUILD_ROOT/usr/lib/qubes/ -cp aux-tools/qubes-notify-updates $RPM_BUILD_ROOT/usr/lib/qubes/ -cp aux-tools/qubes-receive-updates $RPM_BUILD_ROOT/usr/lib/qubes/ +cp qubes_rpc/qfile-dom0-unpacker $RPM_BUILD_ROOT/usr/lib/qubes/ +cp qubes_rpc/qubes-notify-updates $RPM_BUILD_ROOT/usr/lib/qubes/ +cp qubes_rpc/qubes-receive-appmenus $RPM_BUILD_ROOT/usr/lib/qubes/ +cp qubes_rpc/qubes-receive-updates $RPM_BUILD_ROOT/usr/lib/qubes/ cp ../misc/block_add_change $RPM_BUILD_ROOT/usr/lib/qubes/ cp ../misc/block_remove $RPM_BUILD_ROOT/usr/lib/qubes/ cp ../misc/block_cleanup $RPM_BUILD_ROOT/usr/lib/qubes/ @@ -120,13 +121,13 @@ mkdir -p $RPM_BUILD_ROOT/etc/qubes_rpc/policy cp ../qubes_rpc/qubes.Filecopy.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.Filecopy cp ../qubes_rpc/qubes.OpenInVM.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.OpenInVM cp ../qubes_rpc/qubes.VMShell.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.VMShell -cp qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.SyncAppMenus -cp qubes.SyncAppMenus $RPM_BUILD_ROOT/etc/qubes_rpc/ +cp qubes_rpc/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.SyncAppMenus +cp qubes_rpc/qubes.SyncAppMenus $RPM_BUILD_ROOT/etc/qubes_rpc/ cp ../qrexec/qubes_rpc_multiplexer $RPM_BUILD_ROOT/usr/lib/qubes -cp aux-tools/qubes.NotifyUpdates.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.NotifyUpdates -cp aux-tools/qubes.NotifyUpdates $RPM_BUILD_ROOT/etc/qubes_rpc/ -cp aux-tools/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.ReceiveUpdates -cp aux-tools/qubes.ReceiveUpdates $RPM_BUILD_ROOT/etc/qubes_rpc/ +cp qubes_rpc/qubes.NotifyUpdates.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.NotifyUpdates +cp qubes_rpc/qubes.NotifyUpdates $RPM_BUILD_ROOT/etc/qubes_rpc/ +cp qubes_rpc/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.ReceiveUpdates +cp qubes_rpc/qubes.ReceiveUpdates $RPM_BUILD_ROOT/etc/qubes_rpc/ install -D aux-tools/qubes-dom0.modules $RPM_BUILD_ROOT/etc/sysconfig/modules/qubes-dom0.modules install -D aux-tools/cpufreq-xen.modules $RPM_BUILD_ROOT/etc/sysconfig/modules/cpufreq-xen.modules install -D aux-tools/qubes-dom0-updates.cron $RPM_BUILD_ROOT/etc/cron.daily/qubes-dom0-updates.cron @@ -354,6 +355,7 @@ fi /usr/lib/qubes/meminfo-writer /usr/lib/qubes/qfile-daemon-dvm* /usr/lib/qubes/qubes-notify-updates +/usr/lib/qubes/qubes-receive-appmenus /usr/lib/qubes/qubes-receive-updates /usr/lib/qubes/block_add_change /usr/lib/qubes/block_remove From 26fca20d4566f740e2075c271313d61fcd4dad05 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 16 Aug 2012 01:53:49 +0200 Subject: [PATCH 05/17] dom0/qmemman: fix reporting to qubes-manager When VM is shutting down, xenstore entries (especially 'name') can be deleted before qmemman remove VM from its list. So check if name is defined before reporting to qubes-manager. --- dom0/qmemman/qmemman.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dom0/qmemman/qmemman.py b/dom0/qmemman/qmemman.py index 7b6f687e..d88d75e3 100755 --- a/dom0/qmemman/qmemman.py +++ b/dom0/qmemman/qmemman.py @@ -214,12 +214,14 @@ class SystemState: print 'dom %s didnt react to memory request (holds %d, requested balloon down to %d)' % (dom2, self.domdict[dom2].memory_actual, mem2) self.domdict[dom2].no_progress = True dom_name = self.xs.read('', '/local/domain/%s/name' % str(dom2)) - notify_error_qubes_manager(dom_name, no_progress_msg) + if dom_name is not None: + notify_error_qubes_manager(str(dom_name), no_progress_msg) else: print 'dom %s still hold more memory than have assigned (%d > %d)' % (dom2, self.domdict[dom2].memory_actual, mem2) self.domdict[dom2].slow_memset_react = True dom_name = self.xs.read('', '/local/domain/%s/name' % str(dom2)) - notify_error_qubes_manager(dom_name, slow_memset_react_msg) + if dom_name is not None: + notify_error_qubes_manager(str(dom_name), slow_memset_react_msg) self.mem_set(dom, self.get_free_xen_memory() + self.domdict[dom].memory_actual - self.XEN_FREE_MEM_LEFT) return From cc23d3cb3dffe8272a48fe4ca2d7929d211d6ca0 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 16 Aug 2012 16:37:23 +0200 Subject: [PATCH 06/17] dom0/qubes_rpc: mark untrusted variables (#654) --- dom0/qubes_rpc/qubes-notify-updates | 8 +++++--- dom0/qubes_rpc/qubes-receive-appmenus | 13 +++++++------ dom0/qubes_rpc/qubes-receive-updates | 11 ++++++----- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/dom0/qubes_rpc/qubes-notify-updates b/dom0/qubes_rpc/qubes-notify-updates index d34de46c..a6ece1d6 100755 --- a/dom0/qubes_rpc/qubes-notify-updates +++ b/dom0/qubes_rpc/qubes-notify-updates @@ -50,10 +50,12 @@ def main(): os.umask(0002) qubes_gid = grp.getgrnam('qubes').gr_gid - update_count = sys.stdin.readline(128).strip() - if not update_count.isdigit(): - print >> sys.stderr, 'Domain ' + source + ' sent invalid number of updates: ' + update_count + untrusted_update_count = sys.stdin.readline(128).strip() + if not untrusted_update_count.isdigit(): + print >> sys.stderr, 'Domain ' + source + ' sent invalid number of updates: %s' % untrusted_update_count exit(1) + # now sanitized + update_count = untrusted_update_count if source_vm.updateable: # Just trust information from VM itself update_f = open(source_vm.dir_path + '/' + updates_stat_file, "w") diff --git a/dom0/qubes_rpc/qubes-receive-appmenus b/dom0/qubes_rpc/qubes-receive-appmenus index fef07274..14515b63 100755 --- a/dom0/qubes_rpc/qubes-receive-appmenus +++ b/dom0/qubes_rpc/qubes-receive-appmenus @@ -53,10 +53,10 @@ def get_appmenus(xid): untrusted_appmenulist = [] if xid == -1: while appmenus_line_count > 0: - line = sys.stdin.readline(appmenus_line_size) - if line == "": + untrusted_line = sys.stdin.readline(appmenus_line_size) + if untrusted_line == "": break; - untrusted_appmenulist.append(line.strip()) + untrusted_appmenulist.append(untrusted_line.strip()) appmenus_line_count -= 1 if appmenus_line_count == 0: raise QubesException("Line count limit exceeded") @@ -64,10 +64,10 @@ def get_appmenus(xid): p = subprocess.Popen ([qrexec_client_path, '-d', str(xid), 'user:QUBESRPC qubes.GetAppmenus dom0'], stdout=subprocess.PIPE) while appmenus_line_count > 0: - line = p.stdout.readline(appmenus_line_size) - if line == "": + untrusted_line = p.stdout.readline(appmenus_line_size) + if untrusted_line == "": break; - untrusted_appmenulist.append(line.strip()) + untrusted_appmenulist.append(untrusted_line.strip()) appmenus_line_count -= 1 p.wait() if p.returncode != 0: @@ -88,6 +88,7 @@ def get_appmenus(xid): if untrusted_m: untrusted_key = untrusted_m.group(2) untrusted_value = untrusted_m.group(3) + # Look only at predefined keys if fields_regexp.has_key(untrusted_key): if fields_regexp[untrusted_key].match(untrusted_value): # now values are sanitized diff --git a/dom0/qubes_rpc/qubes-receive-updates b/dom0/qubes_rpc/qubes-receive-updates index 78d9027c..e076900b 100755 --- a/dom0/qubes_rpc/qubes-receive-updates +++ b/dom0/qubes_rpc/qubes-receive-updates @@ -66,9 +66,12 @@ def handle_dom0updates(updatevm): os.chmod(updates_rpm_dir, 0775) subprocess.check_call(["/usr/lib/qubes/qfile-dom0-unpacker", str(os.getuid()), updates_rpm_dir]) # Verify received files - for f in os.listdir(updates_rpm_dir): - full_path = updates_rpm_dir + "/" + f - if package_regex.match(f): + for untrusted_f in os.listdir(updates_rpm_dir): + if not package_regex.match(untrusted_f): + dom0updates_fatal(untrusted_f, 'Domain ' + source + ' sent unexpected file: ' + untrusted_f) + else: + f = untrusted_f + full_path = updates_rpm_dir + "/" + f if os.path.islink(full_path) or not os.path.isfile(full_path): dom0updates_fatal(f, 'Domain ' + source + ' sent not regular file') p = subprocess.Popen (["/bin/rpm", "-K", full_path], @@ -78,8 +81,6 @@ def handle_dom0updates(updatevm): dom0updates_fatal(f, 'Error while verifing %s signature: %s' % (f, output)) if not gpg_ok_regex.search(output.strip()): dom0updates_fatal(f, 'Domain ' + source + ' sent not signed rpm: ' + f) - else: - dom0updates_fatal(f, 'Domain ' + source + ' sent unexpected file: ' + f) if updates_error_file_handle is not None: updates_error_file_handle.close() # After updates received - create repo metadata From a98020eca7f738fa18dff2f3c506e7dea3cc198c Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 18 Aug 2012 21:17:07 +0200 Subject: [PATCH 07/17] dom0+vm/qfile-copy: use setuid instead of policy setting to allow chroot This will allow to not hardcode "root" username in policy, which can be useful for non-Linux systems. --- qubes_rpc/qubes.Filecopy.policy | 2 +- rpm_spec/core-vm.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qubes_rpc/qubes.Filecopy.policy b/qubes_rpc/qubes.Filecopy.policy index 0a0d7352..e0bef3ef 100644 --- a/qubes_rpc/qubes.Filecopy.policy +++ b/qubes_rpc/qubes.Filecopy.policy @@ -3,4 +3,4 @@ ## Please use a single # to start your custom comments -$anyvm $anyvm ask,user=root +$anyvm $anyvm ask diff --git a/rpm_spec/core-vm.spec b/rpm_spec/core-vm.spec index f9ba7743..6c84ae0b 100644 --- a/rpm_spec/core-vm.spec +++ b/rpm_spec/core-vm.spec @@ -398,7 +398,7 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/meminfo-writer /usr/lib/qubes/network-manager-prepare-conf-dir /usr/lib/qubes/qfile-agent -/usr/lib/qubes/qfile-unpacker +%attr(4755,root,root) /usr/lib/qubes/qfile-unpacker /usr/lib/qubes/qopen-in-vm /usr/lib/qubes/qrexec_agent /usr/lib/qubes/qrexec_client_vm From 7745e23137f85eadd214df67af3ba1ae18ea8efc Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 18 Aug 2012 21:21:20 +0200 Subject: [PATCH 08/17] dom0/qrexec: use QUBESRPC instead of direct multiplexer path --- qrexec/qrexec_policy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qrexec/qrexec_policy b/qrexec/qrexec_policy index 7d418c8d..69fed14b 100755 --- a/qrexec/qrexec_policy +++ b/qrexec/qrexec_policy @@ -96,7 +96,7 @@ def do_execute(domain, target, user, exec_index, process_ident): # also, dangling "xl" would keep stderr open and may prevent closing connection spawn_target_if_necessary(target) cmd= QREXEC_CLIENT + " -d " + target + " '" + user - cmd+=":/usr/lib/qubes/qubes_rpc_multiplexer "+ exec_index + " " + domain + "'" + cmd+=":QUBESRPC "+ exec_index + " " + domain + "'" os.execl(QREXEC_CLIENT, "qrexec_client", "-d", domain, "-l", cmd, "-c", process_ident) def confirm_execution(domain, target, exec_index): From 11e142adb36d4d190a867ad6dd680973e61d6d6e Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 18 Aug 2012 21:42:54 +0200 Subject: [PATCH 09/17] dom0/qrexec: use default user from VM settings --- qrexec/qrexec_policy | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/qrexec/qrexec_policy b/qrexec/qrexec_policy index 69fed14b..75dd136a 100755 --- a/qrexec/qrexec_policy +++ b/qrexec/qrexec_policy @@ -5,6 +5,7 @@ import os.path import subprocess import xen.lowlevel.xl import qubes.guihelpers +from qubes.qubes import QubesVmCollection import fcntl POLICY_FILE_DIR="/etc/qubes_rpc/policy" @@ -156,7 +157,16 @@ def main(): if policy_dict.has_key("action.user"): user=policy_dict["action.user"] else: - user="user" + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + vm = qvm_collection.get_vm_by_name(target) + if vm is None: + print >> sys.stderr, "Cannot find settings of VM '%s', assuming default user 'user'" % target + user = "user" + else: + user = vm.default_user do_execute(domain, target, user, exec_index, process_ident) print >> sys.stderr, "Rpc denied:", domain, target, exec_index From 7c1dfe9266b65e76bd87d60c056e3440dee68e48 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 18 Aug 2012 22:08:26 +0200 Subject: [PATCH 10/17] dom0/qrexec: implement standalone policy evaluation (#12 pro) This change will allow to use the same policy mechanism to control clipboard copy between domains. --- qrexec/qrexec_policy | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/qrexec/qrexec_policy b/qrexec/qrexec_policy index 75dd136a..d25e9d08 100755 --- a/qrexec/qrexec_policy +++ b/qrexec/qrexec_policy @@ -5,6 +5,7 @@ import os.path import subprocess import xen.lowlevel.xl import qubes.guihelpers +from optparse import OptionParser from qubes.qubes import QubesVmCollection import fcntl @@ -127,10 +128,18 @@ def policy_editor(domain, target, exec_index): subprocess.call(["/usr/bin/zenity", "--info", "--text", text]) def main(): - domain=sys.argv[1] - target=sys.argv[2] - exec_index=sys.argv[3] - process_ident=sys.argv[4] + usage = "usage: %prog [options] " + parser = OptionParser (usage) + parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False, + help="Allow run of service without confirmation if policy say 'ask'") + parser.add_option ("--just-evaluate", action="store_true", dest="just_evaluate", default=False, + help="Do not run the service, only evaluate policy; retcode=0 means 'allow'") + + (options, args) = parser.parse_args () + domain=args[0] + target=args[1] + exec_index=args[2] + process_ident=args[3] policy_list=read_policy_file(exec_index) if policy_list==None: @@ -140,6 +149,9 @@ def main(): policy_list=list() policy_dict=find_policy(policy_list, domain, target) + + if policy_dict["action"] == "ask" and options.assume_yes_for_ask: + policy_dict["action"] = "allow" if policy_dict["action"] == "ask": user_choice = confirm_execution(domain, target, exec_index) @@ -150,7 +162,13 @@ def main(): policy_dict["action"] = "allow" else: policy_dict["action"] = "deny" - + + if options.just_evaluate: + if policy_dict["action"] == "allow": + exit(0) + else: + exit(1) + if policy_dict["action"] == "allow": if policy_dict.has_key("action.target"): target=policy_dict["action.target"] From 4fcaf3e7b718b44d00ee4e2a3f6852a71e472abe Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 22 Aug 2012 21:55:03 +0200 Subject: [PATCH 11/17] Revert "win: unmask evtchn before checking if libvchan_wait is needed" This reverts commit f7ce10db8f76f752cf9d65b420de352e70bdb048. This should be fixed by doing 0-length reads in qrexec main loop (instead of real read of fired port number). --- vchan/io.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/vchan/io.c b/vchan/io.c index 17a3abe3..e735c86a 100644 --- a/vchan/io.c +++ b/vchan/io.c @@ -125,12 +125,6 @@ int libvchan_write(struct libvchan *ctrl, char *data, int size) { int avail, avail_contig; int real_idx; -#ifdef WINNT - // because of mask-on-fire and do_notify called previously, evtchn must be - // unmasked before libvchan_wait. Do it before checking if data is - // available to prevent race - libvchan_prepare_to_select(ctrl); -#endif while ((avail = libvchan_buffer_space(ctrl)) == 0) if (libvchan_wait(ctrl) < 0) return -1; @@ -156,12 +150,6 @@ int libvchan_read(struct libvchan *ctrl, char *data, int size) { int avail, avail_contig; int real_idx; -#ifdef WINNT - // because of mask-on-fire and do_notify called previously, evtchn must be - // unmasked before libvchan_wait. Do it before checking if data is - // available to prevent race - libvchan_prepare_to_select(ctrl); -#endif while ((avail = libvchan_data_ready(ctrl)) == 0) if (libvchan_wait(ctrl) < 0) return -1; From 85565af560fd94b358352c643dfec78a021a5b26 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 23 Aug 2012 02:50:18 +0200 Subject: [PATCH 12/17] dom0/core: verify VM name for not-allowed characters (#656) --- dom0/qvm-core/qubes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 8aa96634..687d1d36 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -324,6 +324,9 @@ class QubesVm(object): assert self.__qid < qubes_max_qid, "VM id out of bounds!" assert self.name is not None + if not self.verify_name(self.name): + raise QubesException("Invalid characters in VM name") + if self.netvm is not None: self.netvm.connected_vms[self.qid] = self @@ -491,6 +494,9 @@ class QubesVm(object): else: return False + def verify_name(self, name): + return re.match(r"^[a-zA-Z0-9-]*$", name) is not None + def pre_rename(self, new_name): pass @@ -498,6 +504,9 @@ class QubesVm(object): if self.is_running(): raise QubesException("Cannot change name of running VM!") + if not self.verify_name(name): + raise QubesException("Invalid characters in VM name") + self.pre_rename(name) new_conf = "%s/%s.conf" % (self.dir_path, name) From b7f5c6ac0bf143dd6c7986effa43ec974842538c Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 23 Aug 2012 03:48:03 +0200 Subject: [PATCH 13/17] dom0/qvm-tools: catch exceptions while settting VM name (#656) --- dom0/qvm-tools/qvm-create | 21 +++++++++++++-------- dom0/qvm-tools/qvm-prefs | 6 +++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index 1f8c47b1..d1c4b5ee 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -22,6 +22,7 @@ from qubes.qubes import QubesVmCollection from qubes.qubes import QubesVmLabels +from qubes.qubes import QubesException from optparse import OptionParser; import subprocess import re @@ -133,14 +134,18 @@ def main(): new_vm_template = template vm = None - if options.netvm: - vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label) - elif options.proxyvm: - vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label) - elif options.hvm: - vm = qvm_collection.add_new_hvm(vmname, label = label) - else: - vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label) + try: + if options.netvm: + vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label) + elif options.proxyvm: + vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label) + elif options.hvm: + vm = qvm_collection.add_new_hvm(vmname, label = label) + else: + vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label) + except QubesException as err: + print >> sys.stderr, "ERROR: {0}".format(err) + exit (1) if options.internal: vm.internal = True diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index d192f73f..2d690d30 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -375,7 +375,11 @@ def do_set(vms, vm, property, args): print >> sys.stderr, "ERROR: Property '{0}' not available for this VM".format(property) return False - return properties[property](vms, vm, args) + try: + return properties[property](vms, vm, args) + except Exception as err: + print >> sys.stderr, "ERROR: %s" % str(err) + return False def main(): From dde1b5b2f51c880ea7ce6aed2f00e1da8dfc0b35 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 18 Aug 2012 21:21:20 +0200 Subject: [PATCH 14/17] dom0/qrexec: use QUBESRPC instead of direct multiplexer path --- qrexec/qrexec_policy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qrexec/qrexec_policy b/qrexec/qrexec_policy index 7d418c8d..69fed14b 100755 --- a/qrexec/qrexec_policy +++ b/qrexec/qrexec_policy @@ -96,7 +96,7 @@ def do_execute(domain, target, user, exec_index, process_ident): # also, dangling "xl" would keep stderr open and may prevent closing connection spawn_target_if_necessary(target) cmd= QREXEC_CLIENT + " -d " + target + " '" + user - cmd+=":/usr/lib/qubes/qubes_rpc_multiplexer "+ exec_index + " " + domain + "'" + cmd+=":QUBESRPC "+ exec_index + " " + domain + "'" os.execl(QREXEC_CLIENT, "qrexec_client", "-d", domain, "-l", cmd, "-c", process_ident) def confirm_execution(domain, target, exec_index): From 536eb00b9c24ad115d52a49e63af637112381af4 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 23 Aug 2012 02:50:18 +0200 Subject: [PATCH 15/17] dom0/core: verify VM name for not-allowed characters (#656) --- dom0/qvm-core/qubes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 26cd3972..f52b5fca 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -315,6 +315,9 @@ class QubesVm(object): assert self.__qid < qubes_max_qid, "VM id out of bounds!" assert self.name is not None + if not self.verify_name(self.name): + raise QubesException("Invalid characters in VM name") + if self.netvm is not None: self.netvm.connected_vms[self.qid] = self @@ -482,6 +485,9 @@ class QubesVm(object): else: return False + def verify_name(self, name): + return re.match(r"^[a-zA-Z0-9-]*$", name) is not None + def pre_rename(self, new_name): pass @@ -489,6 +495,9 @@ class QubesVm(object): if self.is_running(): raise QubesException("Cannot change name of running VM!") + if not self.verify_name(name): + raise QubesException("Invalid characters in VM name") + self.pre_rename(name) new_conf = "%s/%s.conf" % (self.dir_path, name) From 1cd3ef3456d3da65eb6c71076281e7399d52656b Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 23 Aug 2012 03:48:03 +0200 Subject: [PATCH 16/17] dom0/qvm-tools: catch exceptions while settting VM name (#656) --- dom0/qvm-tools/qvm-create | 17 +++++++++++------ dom0/qvm-tools/qvm-prefs | 6 +++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index 595442fd..0bc22a8d 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -22,6 +22,7 @@ from qubes.qubes import QubesVmCollection from qubes.qubes import QubesVmLabels +from qubes.qubes import QubesException from optparse import OptionParser; import subprocess import re @@ -116,12 +117,16 @@ def main(): new_vm_template = template vm = None - if options.netvm: - vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label) - elif options.proxyvm: - vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label) - else: - vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label) + try: + if options.netvm: + vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label) + elif options.proxyvm: + vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label) + else: + vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label) + except QubesException as err: + print >> sys.stderr, "ERROR: {0}".format(err) + exit (1) if options.internal: vm.internal = True diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index 790ca1ed..65bd1973 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -331,7 +331,11 @@ def do_set(vms, vm, property, args): print >> sys.stderr, "ERROR: Property '{0}' not available for this VM".format(property) return False - return properties[property](vms, vm, args) + try: + return properties[property](vms, vm, args) + except Exception as err: + print >> sys.stderr, "ERROR: %s" % str(err) + return False def main(): From ab52153ab292772f3501a28b26a50515108a54a7 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 23 Aug 2012 21:39:41 +0200 Subject: [PATCH 17/17] win/vchan: remove outstanding evtchn events on libvchan_wait Windows evtchn driver can double fire on one event (details in xenpci/evtchn_device_interface.c:EvtChn_EvtIoWrite). Less intrusive way of fixing it is just remove all events from queue on libvchan_wait (to prevent queue fill). This won't hurt because we are interested in events only sent after xc_evtchn_unmask call which is one line below. --- vchan/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vchan/io.c b/vchan/io.c index e735c86a..9709109e 100644 --- a/vchan/io.c +++ b/vchan/io.c @@ -77,7 +77,7 @@ int libvchan_wait(struct libvchan *ctrl) { int ret; - ret = xc_evtchn_pending(ctrl->evfd); + ret = xc_evtchn_pending_with_flush(ctrl->evfd); if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport)) return -1; if (ret!=-1 && libvchan_is_eof(ctrl))